import 'package:flutter_test/flutter_test.dart'; import 'package:miler/Models/stop_status.dart'; import 'package:miler/data/service_profile.dart'; import 'package:miler/views/Dashboard/home/trip.dart'; import 'package:miler/views/Dashboard/pickups/pickups.dart'; /// ───────────────────────────────────────────────────────────────────────── /// THE RAIL COUNTS THE ROUTE, NOT THE RECORD /// /// ── What the rider saw ── /// /// He accepted one order out of five, picked it up, and the progress rail at /// the top of Deliveries drew **five nodes**. One of them was his. /// /// ── Where the other four came from ── /// /// The rail reads the completed store, and that store is not a list of /// finished deliveries. It is where every order that leaves the rider's hands /// is filed, whatever ended it — and Home files a **declined** stop there too, /// with `terminalStatus: 'rejected'`, so the decision survives on Activity. /// That was harmless for as long as nothing read the store as a route. The rail /// reads it as a route. /// /// The count was the visible half. The colours were worse, and this is what /// these tests really pin: /// /// `rejected` → [StopProgress.pending] — four stops he had **refused**, /// drawn on the rail ahead of him as work still to come. /// `cancelled` → [StopStatusX.isWorkComplete] → [StopProgress.done] — /// drawn **green**, claiming a delivery that never happened. /// /// A skip is the case that must survive the fix: it is a door he went to and /// could not finish, the rail has an amber node for precisely that, and losing /// it would hide work he still owes. /// ───────────────────────────────────────────────────────────────────────── void main() { tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); /// A row as the completed store hands it back — `orderstatus` stamped with /// whatever ended it. See `addCompletedBookings`. Map filed(String id, String terminalStatus) => { 'orderid': id, 'orderstatus': terminalStatus, 'completedday': '2026-08-27', 'completedat': '2026-08-27T10:00:00', }; group('what belongs on a progress rail', () { test('a declined stop does not', () { // The four that made the rail read five. He said no to these. expect(wasRiddenTo(filed('r', 'rejected')), isFalse); }); test('a cancelled stop does not', () { // The office called it off. It was never a door he stood at, and on a // milk run it would have drawn green. expect(wasRiddenTo(filed('c', 'cancelled')), isFalse); }); test('a skipped stop does', () { // Unfinished business, and the one state the rail has an amber node for. // Dropping it with the others would hide a door he still owes a visit. expect(wasRiddenTo(filed('s', 'skipped')), isTrue); }); test('a delivered stop does', () { expect(wasRiddenTo(filed('d', 'delivered')), isTrue); }); test('a picked stop does', () { expect(wasRiddenTo(filed('p', 'picked')), isTrue); }); }); group('the states the excluded rows would have been drawn in', () { test('a declined stop reads as work still ahead', () { // Not a subtle wrong colour — [StopProgress.pending] is the same node a // stop he has yet to ride to gets. This is why exclusion is the fix and // recolouring is not: it is not on his route at all. expect( stopProgressFor(stopStatusFromRaw('rejected')), StopProgress.pending, ); }); test('a cancelled stop reads as done', () { ServiceProfile.setActive(ServiceProfile.milkMan); expect(stopStatusFromRaw('cancelled').isWorkComplete, isTrue); expect( stopProgressFor(stopStatusFromRaw('cancelled')), StopProgress.done, ); }); test('a skipped stop reads as skipped, on either line', () { for (final profile in [ServiceProfile.parcel, ServiceProfile.milkMan]) { ServiceProfile.setActive(profile); expect( stopProgressFor(stopStatusFromRaw('skipped')), StopProgress.skipped, reason: 'the amber node is the whole reason a skip stays', ); } }); }); group("the rider's own case: one of five", () { test('four declined and one picked leaves a rail of one', () { // The report, as arithmetic. The store holds all five because Home files // a decline; only one of them is a place he rode to. final store = [ filed('1', 'picked'), filed('2', 'rejected'), filed('3', 'rejected'), filed('4', 'rejected'), filed('5', 'rejected'), ]; expect(store.length, 5); expect(store.where(wasRiddenTo).length, 1); }); test('a day genuinely worked is not trimmed', () { // The fix must not cost a rider his progress. Four real completions and // one in hand is still a five-node rail, four of them green. final store = [ for (final id in ['1', '2', '3', '4']) filed(id, 'delivered'), ]; expect(store.where(wasRiddenTo).length, 4); }); }); }