first commit
This commit is contained in:
128
test/demo_bookings_test.dart
Normal file
128
test/demo_bookings_test.dart
Normal file
@@ -0,0 +1,128 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:miler/Models/stop_status.dart';
|
||||
import 'fixtures/demo_stops.dart';
|
||||
|
||||
/// Injecting demo stops into Bookings is only half the fix. The tab drops
|
||||
/// anything still pending — it shows work the rider has *taken on* — so demo
|
||||
/// stops that arrive as `status: 'new'` are filtered out silently and the tab
|
||||
/// stays empty anyway. These tests pin the states, because a regression there
|
||||
/// looks exactly like "the mock data doesn't work" with nothing in the logs.
|
||||
void main() {
|
||||
/// The predicate Bookings uses to decide a stop belongs to it at all
|
||||
/// (pickups.dart — anything still pending lives on Home instead).
|
||||
bool survivesBookingsFilter(Map<String, dynamic> o) {
|
||||
final status = (o['orderstatus']?.toString().toLowerCase() ?? '').trim();
|
||||
const pending = {'', 'assigned', 'miler_assigned', 'pending', 'new'};
|
||||
return !pending.contains(status);
|
||||
}
|
||||
|
||||
test('every demo booking survives the accepted-only filter', () {
|
||||
final bookings = getDemoBookings(riderId: 4021);
|
||||
expect(bookings, isNotEmpty);
|
||||
expect(
|
||||
bookings.where(survivesBookingsFilter).length,
|
||||
bookings.length,
|
||||
reason: 'a demo stop left as "new" is dropped and never reaches the list',
|
||||
);
|
||||
});
|
||||
|
||||
test('covers every state the tab can render', () {
|
||||
final statuses = getDemoBookings(riderId: 4021)
|
||||
.map((b) => stopStatusFromRaw(b['orderstatus']))
|
||||
.toSet();
|
||||
|
||||
// Two finished ones give the progress bar something green; the active one
|
||||
// drives the banner and blocks the rest; the skipped one is the amber
|
||||
// return-visit case.
|
||||
expect(statuses, contains(StopStatus.picked));
|
||||
expect(statuses, contains(StopStatus.active));
|
||||
expect(statuses, contains(StopStatus.skipped));
|
||||
expect(statuses, contains(StopStatus.accepted));
|
||||
});
|
||||
|
||||
test('leaves enough open stops to fill the list', () {
|
||||
// Bookings excludes active and finished stops from the card list — those
|
||||
// go to the banner and the progress count. What is left has to be more
|
||||
// than a couple of cards or the tab still looks broken.
|
||||
final open = getDemoBookings(riderId: 4021).where((b) {
|
||||
final st = stopStatusFromRaw(b['orderstatus']);
|
||||
return st != StopStatus.active &&
|
||||
st != StopStatus.picked &&
|
||||
st != StopStatus.cancelled;
|
||||
}).length;
|
||||
expect(open, greaterThanOrEqualTo(8));
|
||||
});
|
||||
|
||||
test('is the same route Home gave the miler', () {
|
||||
final home = getDemoTripStops(riderId: 4021).take(12);
|
||||
final bookings = getDemoBookings(riderId: 4021);
|
||||
expect(
|
||||
bookings.map((b) => b['orderid']),
|
||||
home.map((b) => b['orderid']),
|
||||
reason: 'Bookings must not invent a second, unrelated route',
|
||||
);
|
||||
});
|
||||
|
||||
pipelineTests();
|
||||
|
||||
test('two milers get different bookings', () {
|
||||
final a = getDemoBookings(riderId: 4021).map((b) => b['orderid']).toSet();
|
||||
final b = getDemoBookings(riderId: 4022).map((b) => b['orderid']).toSet();
|
||||
expect(a.intersection(b), isEmpty);
|
||||
});
|
||||
}
|
||||
|
||||
/// The first fix for the empty Bookings tab was gated on the RAW api response
|
||||
/// (`items.isEmpty`), but the tab renders a list that survives two more
|
||||
/// filters. A signed-in miler's backend answer is rarely empty — it is full of
|
||||
/// ASSIGNED bookings, which belong on Home and are filtered out here — so the
|
||||
/// fallback never fired and the tab stayed blank. These tests pin the end of
|
||||
/// the pipeline, using the real production helpers, not a paraphrase of them.
|
||||
void pipelineTests() {
|
||||
/// Bookings' accepted-only predicate.
|
||||
List<Map<String, dynamic>> keepWorkable(
|
||||
List<Map<String, dynamic>> items,
|
||||
Set<String> acceptedIds,
|
||||
) =>
|
||||
items.where((o) {
|
||||
final id = (o['orderid'] ?? '').toString();
|
||||
if (id.isNotEmpty && acceptedIds.contains(id)) return true;
|
||||
final status =
|
||||
(o['orderstatus']?.toString().toLowerCase() ?? '').trim();
|
||||
const pending = {'', 'assigned', 'miler_assigned', 'pending', 'new'};
|
||||
return !pending.contains(status);
|
||||
}).toList();
|
||||
|
||||
test('a backend full of ASSIGNED bookings still leaves the tab empty', () {
|
||||
// What the live backend actually returns for a seeded miler.
|
||||
final fromApi = [
|
||||
for (var i = 0; i < 6; i++)
|
||||
<String, dynamic>{'orderid': 'API-$i', 'orderstatus': 'miler_assigned'},
|
||||
];
|
||||
expect(fromApi, isNotEmpty, reason: 'the raw response is NOT empty');
|
||||
expect(keepWorkable(fromApi, {}), isEmpty,
|
||||
reason: 'yet nothing survives — which is why the old gate never fired');
|
||||
});
|
||||
|
||||
test('the fallback keyed on the filtered list produces visible cards', () {
|
||||
final fromApi = [
|
||||
for (var i = 0; i < 6; i++)
|
||||
<String, dynamic>{'orderid': 'API-$i', 'orderstatus': 'miler_assigned'},
|
||||
];
|
||||
|
||||
var workable = keepWorkable(fromApi, {});
|
||||
if (workable.isEmpty) workable = getDemoBookings(riderId: 4021);
|
||||
|
||||
// Bookings' list excludes active and finished stops — those go to the
|
||||
// banner and the progress count.
|
||||
final listForTop = workable.where((o) {
|
||||
final st = stopStatusFromRaw(o['orderstatus']);
|
||||
return st != StopStatus.active &&
|
||||
st != StopStatus.picked &&
|
||||
st != StopStatus.cancelled;
|
||||
}).toList();
|
||||
|
||||
expect(listForTop, isNotEmpty, reason: 'the rider must see cards');
|
||||
expect(listForTop.length, greaterThanOrEqualTo(8));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user