Files
doormile_milderapp/test/page_routing_test.dart
Thiru-tenext d7348e253f Miler rider app: surface system, visible design language, backend lifecycle
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>
2026-08-22 05:40:35 +05:30

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));
}
});
});
}