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

221 lines
6.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@Tags(['shots'])
library;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_test/flutter_test.dart';
import 'support/golden_fonts.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:miler/data/service_profile.dart';
import 'package:miler/views/Dashboard/home/trip.dart';
import 'package:miler/views/Dashboard/home/trip_progress_rail.dart';
// `card.dart` is a `part of` this library, so [PickupCard] comes through it.
import 'package:miler/views/Dashboard/pickups/pickups.dart';
import 'package:miler/views/helpers/constants/Colorconstants.dart';
import 'package:miler/views/helpers/constants/Font_constant.dart';
/// Visual harness for the Deliveries tab's two public pieces.
///
/// `_MyPickupsState` cannot be pumped — Get, SharedPreferences, Geolocator and
/// a 3s poll — and the queue's three quieter tiers (`_NextCard`, `_LaterRow`,
/// `_SkippedRow`) are private to `card.dart`. What is reachable is exactly the
/// pair the redesign is about: [TripProgressRail], which is the head of the
/// screen, and [PickupCard], which is the one live card under it.
///
/// The rail is shot at four route lengths because the documented gap against it
/// is that "a 21-stop route overflows it" — a claim about layout, which is
/// answerable by looking. Run with `--update-goldens`.
void main() {
setUpAll(() async {
// Same reasoning as `home_shots_test`: a golden rendered without the app's
// own family is a picture of the fallback, and Poppins ships one file per
// weight so each has to be registered under the family by hand.
await loadGoldenFonts();
});
tearDown(() {
Get.reset();
ServiceProfile.setActive(ServiceProfile.parcel);
});
Map<String, dynamic> drop(
String id,
String customer, {
String status = 'picked',
String bag = '',
}) => {
'orderid': id,
'tripid': 'T1',
'type': 'delivery',
'pickupcustomer': customer,
'name': customer,
'sourcename': 'DailyGrubs RS Puram Kitchen',
'kitchenid': 'K1',
'kitchenname': 'DailyGrubs RS Puram Kitchen',
'pickupaddress': '4 Cross, RS Puram, Coimbatore 641002',
'dropaddress': '12, SNS Colony, Peelamedu, Coimbatore 641004',
'deliveryaddress': '12, SNS Colony, Peelamedu, Coimbatore 641004',
'orderstatus': status,
'pickupcontactno': '9787698259',
'deliverycontactno': '9787698259',
if (bag.isNotEmpty) 'baglabel': bag,
'Quantity': 1,
'pickuplat': 11.0168,
'pickuplon': 76.9558,
'droplat': 11.0208,
'droplon': 76.9598,
};
Future<void> frame(
WidgetTester tester,
String name,
Widget child, {
double height = 400,
double width = 390,
}) async {
ServiceProfile.setActive(ServiceProfile.milkMan);
SharedPreferences.setMockInitialValues({'userid': 1});
tester.view.physicalSize = Size(width * 3, height * 3);
tester.view.devicePixelRatio = 3.0;
addTearDown(tester.view.reset);
await tester.pumpWidget(
ScreenUtilInit(
// Design size == surface, so `.w`/`.h` are both exactly 1 and the shot
// does not lie about spacing. See the note in `home_shots_test`.
designSize: Size(width, height),
builder: (_, _) => MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
backgroundColor: ColorConstants.daylightSurface,
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: child,
),
),
),
),
),
);
await tester.pumpAndSettle();
await expectLater(
find.byType(MaterialApp),
matchesGoldenFile('shots/deliveries_$name.png'),
);
}
/// A route of [total] stops with [done] delivered and the rider standing at
/// the next one — the shape the rail is actually asked to draw mid-round.
List<StopProgress> route(int total, {int done = 0}) => [
for (var i = 0; i < total; i++)
if (i < done)
StopProgress.done
else if (i == done)
StopProgress.current
else
StopProgress.pending,
];
group('the route rail', () {
testWidgets('a short round lays out across the full width', (tester) async {
await frame(
tester,
'rail_short',
TripProgressRail(
progress: route(5, done: 2),
slotLabel: 'Trip 1 · 10:00 AM – 1:00 PM',
remaining: const Duration(minutes: 74),
),
height: 150,
);
});
testWidgets('the documented 21-stop round', (tester) async {
await frame(
tester,
'rail_21',
TripProgressRail(
progress: route(21, done: 6),
slotLabel: 'Trip 1 · 10:00 AM – 1:00 PM',
remaining: const Duration(minutes: 110),
),
height: 150,
);
});
testWidgets('nothing delivered yet — the whole load still aboard', (
tester,
) async {
await frame(
tester,
'rail_21_fresh',
TripProgressRail(
progress: route(21),
slotLabel: 'Trip 1 · 10:00 AM – 1:00 PM',
remaining: const Duration(minutes: 130),
),
height: 150,
);
});
testWidgets('a 320pt phone at 1.3x text scale', (tester) async {
await frame(
tester,
'rail_21_small',
MediaQuery(
data: const MediaQueryData(textScaler: TextScaler.linear(1.3)),
child: TripProgressRail(
progress: route(21, done: 6),
slotLabel: 'Trip 1 · 10:00 AM – 1:00 PM',
remaining: const Duration(minutes: 110),
),
),
height: 170,
width: 320,
);
});
});
group('the live card', () {
testWidgets('picked, waiting to be taken out', (tester) async {
await frame(
tester,
'card_picked',
PickupCard(
item: drop('D1', 'Joe Mathew', bag: 'Bag 8'),
displayStep: 7,
distanceMeters: 1100,
bag: 'Bag 8',
collectedIds: const {'D1'},
),
height: 320,
);
});
testWidgets('active — the rider has set off', (tester) async {
await frame(
tester,
'card_active',
PickupCard(
item: drop('D1', 'Joe Mathew', bag: 'Bag 8'),
displayStep: 7,
distanceMeters: 1100,
bag: 'Bag 8',
collectedIds: const {'D1'},
outForDeliveryIds: const {'D1'},
),
height: 320,
);
});
});
}