Design system - MilerSurface ladder (canvas → working → raised → floating) with MilerPanel as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1. - Visible vocabulary applied across Home, Deliveries, Activity, Account and the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x), canvas wells for anything that opens, small filled tags for shelf labels, demoted placeholders. Recorded in DESIGN_SYSTEM.md §6. - One icon family: 222 Material glyphs migrated to Lucide; none left outside lib/xpress. - Colour semantics corrected: amber only for what is genuinely owed, brand red reserved for the live stop, disabled primaries go neutral rather than pale. Data and lifecycle - lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart makes admin sequence the single ordering authority; service_day.dart, and stop_area.dart rewritten against live Coimbatore addresses (digit-token stripping, city stoplist, street suffixes, stammer collapse). - countLabel states the load once, in bags. Testing - 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity, sheets and verify, with test/failures/ now gitignored (diff debris). - New pins: home_gutter_test, stop_area_test, plus updated structural bounds. Note: this commit also carries pre-existing working-tree deletions that were present before this work (API_SPEC.md, README.md, demo test fixtures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
97 lines
4.0 KiB
Dart
97 lines
4.0 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:miler/Models/stop_status.dart';
|
|
import 'package:miler/data/service_profile.dart';
|
|
|
|
/// Which of the three tabs a stop belongs on, at each rung of the ladder.
|
|
///
|
|
/// Home = decide and go · Deliveries = the work in hand · Activity = history.
|
|
/// The pages all read the same two predicates, so this is the whole contract.
|
|
String pageFor(StopStatus s) {
|
|
if (s.isWorkComplete || s.isSkipped) return 'Activity';
|
|
if (s.isPending) return 'Home';
|
|
return 'Deliveries';
|
|
}
|
|
|
|
void main() {
|
|
endOfDay();
|
|
tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel));
|
|
|
|
group('milk man — collect at a kitchen, then a round of drops', () {
|
|
setUp(() => ServiceProfile.setActive(ServiceProfile.milkMan));
|
|
|
|
test('picked up moves it off Home and onto Deliveries', () {
|
|
expect(pageFor(StopStatus.assigned), 'Home');
|
|
expect(pageFor(StopStatus.accepted), 'Deliveries');
|
|
expect(pageFor(StopStatus.picked), 'Deliveries');
|
|
expect(pageFor(StopStatus.outForDelivery), 'Deliveries');
|
|
expect(pageFor(StopStatus.deliveryArrived), 'Deliveries');
|
|
});
|
|
|
|
test('delivered moves it to Activity', () {
|
|
expect(pageFor(StopStatus.delivered), 'Activity');
|
|
expect(pageFor(StopStatus.skipped), 'Activity');
|
|
});
|
|
});
|
|
|
|
group('logistics — first mile, and the hub delivers it', () {
|
|
setUp(() => ServiceProfile.setActive(ServiceProfile.parcel));
|
|
|
|
// ── This is the line that does not match the request ──
|
|
//
|
|
// A logistics rider collects at the customer's door and carries the
|
|
// shipment to the hub; somebody else delivers it, on another day, and this
|
|
// app never sees that. So `picked` IS the end of his work, and there is no
|
|
// `delivered` event for him to reach.
|
|
//
|
|
// Making `picked` land on Deliveries instead would need a rung that does
|
|
// not exist yet — a hand-over-at-hub step — or every collected parcel would
|
|
// sit on that tab forever and never reach Activity.
|
|
test('picked up is the end of the rider work, so it files to Activity', () {
|
|
expect(pageFor(StopStatus.assigned), 'Home');
|
|
expect(pageFor(StopStatus.accepted), 'Deliveries');
|
|
expect(pageFor(StopStatus.arrived), 'Deliveries');
|
|
expect(pageFor(StopStatus.picked), 'Activity');
|
|
});
|
|
|
|
test('the profile says so explicitly', () {
|
|
expect(ServiceProfile.active.deliversToCustomer, isFalse);
|
|
expect(ServiceProfile.milkMan.deliversToCustomer, isTrue);
|
|
});
|
|
});
|
|
}
|
|
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
/// WHERE THE DAY ENDS
|
|
///
|
|
/// A logistics rider collects at customers' doors and carries the shipments to
|
|
/// the hub, so his last leg is a building. A milk-man round is the other way
|
|
/// up: he loads at the kitchen and his last address is a customer's door, so
|
|
/// there is nothing to return and nowhere to return it to.
|
|
///
|
|
/// The screens read [ServiceProfile.endsAtHub], never the line.
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
void endOfDay() {
|
|
group('end of day', () {
|
|
test('a milk run finishes where it stands', () {
|
|
expect(ServiceProfile.milkMan.endsAtHub, isFalse);
|
|
expect(ServiceProfile.milkMan.deliversToCustomer, isTrue);
|
|
});
|
|
|
|
test('a parcel route goes back to the depot', () {
|
|
expect(ServiceProfile.parcel.endsAtHub, isTrue);
|
|
expect(ServiceProfile.parcel.deliversToCustomer, isFalse);
|
|
});
|
|
|
|
test('the two are one fact, not two flags that can disagree', () {
|
|
for (final p in [
|
|
ServiceProfile.parcel,
|
|
ServiceProfile.milkMan,
|
|
ServiceProfile.delivery,
|
|
]) {
|
|
expect(p.endsAtHub, isNot(p.deliversToCustomer));
|
|
}
|
|
});
|
|
});
|
|
}
|