134 lines
5.3 KiB
Dart
134 lines
5.3 KiB
Dart
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<String, dynamic> 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);
|
|
});
|
|
});
|
|
}
|