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

85 lines
3.6 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:miler/data/service_profile.dart';
import 'package:miler/views/Dashboard/activity/activity_format.dart';
/// ─────────────────────────────────────────────────────────────────────────
/// READING A DOORMILE TIMESTAMP
///
/// Doormile timestamps are IST wall-clock in Postgres `timestamp without time
/// zone` columns. Some responses come back with a trailing `Z` anyway — a Go +
/// pgx footgun where a naive DB timestamp loads into `time.Time` under the UTC
/// location and marshals with a zone marker it never had. The console strips it
/// the same way; see `src/utils/doormileTimestamp.js`.
///
/// `DateTime.tryParse` believes the `Z`, and that broke the tracking rail in the
/// one way it cannot survive: on a real phone an event stamped `11:43Z` sorted
/// *below* two 11:57 events, because as an absolute instant it is 17:13 IST —
/// while its own rung still printed `11:43`, so nothing on screen explained the
/// order.
/// ─────────────────────────────────────────────────────────────────────────
void main() {
group('parseStamp', () {
test('a false Z is stripped, not believed', () {
final withZ = parseStamp('2026-08-20T11:43:00.000Z')!;
final naive = parseStamp('2026-08-20T11:43:00.000')!;
// Same wall-clock, and therefore the same instant on this device.
expect(withZ, naive);
expect(withZ.hour, 11);
expect(withZ.minute, 43);
expect(clockOf(withZ), '11:43 AM');
});
test('an offset marker is stripped too', () {
expect(
parseStamp('2026-08-20T11:43:00+05:30'),
parseStamp('2026-08-20T11:43:00'),
);
});
test('it sorts by the clock it prints', () {
// The exact shape of the device bug: a backend stamp and two local ones.
final placed = parseStamp('2026-08-20T11:43:00.000Z')!;
final setOff = parseStamp('2026-08-20T11:57:00.000')!;
expect(placed.isBefore(setOff), isTrue);
});
test('empty and unparseable are null, never a fabricated moment', () {
expect(parseStamp(null), isNull);
expect(parseStamp(''), isNull);
expect(parseStamp(' '), isNull);
expect(parseStamp('not a date'), isNull);
});
});
group('happenedAt', () {
test('reads through parseStamp, so the list and the record agree', () {
final row = {'completedat': '2026-08-20T11:43:00.000Z'};
expect(happenedAt(row), parseStamp('2026-08-20T11:43:00')!);
});
test('a record with no usable stamp sinks rather than jumping the day', () {
expect(happenedAt(const {}).millisecondsSinceEpoch, 0);
});
});
group('routeLineOf', () {
test('reads as speech, and carries no arrow character', () {
// Poppins has no U+2192, so the app drew the most-read line on the record
// as `Vidhya Kitchen ▯ Joe Mathew`. The arrow is a Material glyph on
// screen now; this string is for screen readers and logs, where the word
// is what a listener needs anyway.
const stop = {
'pickupcustomer': 'Joe Mathew',
'sourcename': 'Vidhya Kitchen',
};
ServiceProfile.setActive(ServiceProfile.milkMan);
addTearDown(() => ServiceProfile.setActive(ServiceProfile.parcel));
expect(routeLineOf(stop), 'Vidhya Kitchen to Joe Mathew');
expect(routeLineOf(stop), isNot(contains('→')));
});
});
}