Files
doormile_milderapp/test/bookings_trip_label_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

81 lines
2.9 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:miler/views/Dashboard/home/trip.dart';
/// Bookings shows one flat list — no trip tabs, deliberately — so the only
/// thing telling the rider which of the day's runs he is on is the label above
/// it. The number has to mean what the hub means by it, and that is the part
/// worth pinning down: the numbering is against the WHOLE day, not against the
/// subset of it the rider happened to accept.
///
/// This exercises the same `groupIntoTrips` + positional numbering the screen
/// uses, which is the piece that can silently go wrong.
void main() {
Map<String, dynamic> stop(String id, String slotFrom, String slotTo) =>
<String, dynamic>{
'orderid': id,
'pickupcustomer': 'Shop $id',
'slotstarttime': slotFrom,
'slotendtime': slotTo,
};
/// Order id → 1-based trip number, the way `_numberTrips` builds it.
Map<String, int> numberTrips(List<Map<String, dynamic>> allToday) {
final byId = <String, Map<String, dynamic>>{};
for (final s in allToday) {
final id = (s['orderid'] ?? '').toString();
if (id.isEmpty) continue;
byId.putIfAbsent(id, () => s);
}
final trips = Trip.groupIntoTrips(byId.values.toList());
return <String, int>{
for (var i = 0; i < trips.length; i++)
for (final id in trips[i].orderIds) id: i + 1,
};
}
final morning = [
stop('A', '2026-08-06T09:00:00', '2026-08-06T12:00:00'),
stop('B', '2026-08-06T09:00:00', '2026-08-06T12:00:00'),
];
final afternoon = [stop('C', '2026-08-06T14:00:00', '2026-08-06T17:00:00')];
test('trips are numbered by their slot, earliest first', () {
final numbers = numberTrips([...afternoon, ...morning]);
expect(numbers['A'], 1);
expect(numbers['B'], 1);
expect(numbers['C'], 2);
});
test('a rider who took only the afternoon is still on Trip 2', () {
// ── The reason this is computed from the day and not from the queue ──
//
// Numbering only the accepted stops would call this run "Trip 1", while the
// hub, Home and the rider's own supervisor all call it Trip 2. The label is
// a shared name or it is worse than nothing.
final dayNumbers = numberTrips([...morning, ...afternoon]);
expect(dayNumbers['C'], 2);
final queueOnly = numberTrips(afternoon);
expect(
queueOnly['C'],
1,
reason: 'this is exactly the wrong answer the screen must not give',
);
});
test('a stop appearing twice in one fetch does not split its trip', () {
// The same stop arrives from up to three sources per fetch — v1, v3 and the
// local accepted store — and each copy carries the same slot.
final numbers = numberTrips([...morning, ...morning, ...afternoon]);
expect(numbers['A'], 1);
expect(numbers['C'], 2);
});
test('a day with one slot is Trip 1, not "your route"', () {
expect(numberTrips(morning)['A'], 1);
});
}