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

171 lines
6.3 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.
import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:miler/views/Dashboard/pickups/stop_verify.dart';
/// The verification page is the only place the app records what actually
/// happened at a door, so the thing worth testing is the gate: which legs it
/// runs for each stop kind, and that it refuses to advance until the proof for
/// the current leg exists.
void main() {
/// Pumps the page on a phone-shaped surface. The default 800×600 test window
/// makes ScreenUtil scale width up and height down at once, which is not a
/// shape any phone has — layouts that are fine on device fail there for the
/// wrong reason.
Future<void> pumpStop(WidgetTester tester, Map<String, dynamic> stop) async {
tester.view.physicalSize = const Size(1170, 2532); // iPhone 13, @3x
tester.view.devicePixelRatio = 3.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(390, 844),
builder: (_, _) =>
MaterialApp(home: StopVerificationPage(pickup: stop)),
),
);
await tester.pumpAndSettle();
}
Map<String, dynamic> stopOf({
required String type,
int deliveryQty = 0,
int pickupQty = 0,
double cash = 0,
}) => <String, dynamic>{
'orderid': 'T-1',
'type': type,
'pickupcustomer': 'Priya Sharma',
'pickupaddress': '45, Lake View Apartments, Coimbatore',
if (deliveryQty > 0) 'deliveryqty': '$deliveryQty',
if (pickupQty > 0) 'pickupqty': '$pickupQty',
'Quantity': '${pickupQty > 0 ? pickupQty : deliveryQty}',
if (cash > 0) 'collectionamt': '$cash',
};
testWidgets('a pickup stop runs one collect leg', (tester) async {
await pumpStop(tester, stopOf(type: 'pickup', pickupQty: 3));
expect(find.text('Verify pickup'), findsOneWidget);
expect(find.text('Collect'), findsOneWidget); // stepper
expect(find.text('Confirm'), findsOneWidget);
expect(find.text('Hand over'), findsNothing);
// The collect leg's own controls.
expect(find.text('Parcels collected'), findsOneWidget);
expect(find.text('of 3 booked'), findsOneWidget);
expect(find.text('Pickup code'), findsOneWidget);
});
testWidgets(
'a delivery stop runs one hand-over leg with a parcel checklist',
(tester) async {
await pumpStop(tester, stopOf(type: 'delivery', deliveryQty: 2));
expect(find.text('Verify delivery'), findsOneWidget);
expect(find.text('Hand over'), findsOneWidget);
expect(find.text('Collect'), findsNothing);
// One tile per parcel — this is what stops a 2-parcel drop closing on 1.
expect(find.text('Parcel 1 of 2'), findsOneWidget);
expect(find.text('Parcel 2 of 2'), findsOneWidget);
expect(find.text('Who received it?'), findsOneWidget);
expect(find.text('Delivery OTP'), findsOneWidget);
// Nothing about weight or condition on a delivery.
expect(find.text('Condition'), findsNothing);
},
);
testWidgets('a combined stop runs hand over, then collect', (tester) async {
await pumpStop(
tester,
stopOf(type: 'combined', deliveryQty: 1, pickupQty: 2),
);
expect(find.text('Verify stop'), findsOneWidget);
// Three steps, delivery first — the rider walks up holding the outbound
// parcel, so he hands it over before taking anything back.
expect(find.text('Hand over'), findsOneWidget);
expect(find.text('Collect'), findsOneWidget);
expect(find.text('Confirm'), findsOneWidget);
// Both loads are on the summary.
expect(find.text('Hand over 1'), findsOneWidget);
expect(find.text('Collect 2'), findsOneWidget);
// Step 1 is the delivery leg.
expect(find.text('Hand over the parcels'), findsOneWidget);
expect(find.text('Parcels collected'), findsNothing);
});
testWidgets('the delivery leg will not advance without its proof', (
tester,
) async {
await pumpStop(tester, stopOf(type: 'delivery', deliveryQty: 1));
// Blocked from the start, and the bar says what is missing next.
expect(find.text('1 parcel still to hand over'), findsOneWidget);
await tester.tap(find.text('Parcel 1 of 1'));
await tester.pumpAndSettle();
expect(find.text('Enter the OTP from the customer'), findsOneWidget);
await tester.enterText(find.byType(TextField).first, '482156');
await tester.pumpAndSettle();
expect(find.text('Take a proof photo'), findsOneWidget);
// Still on step 1 — the camera is the last gate and cannot be faked here.
expect(find.text('Hand over the parcels'), findsOneWidget);
});
testWidgets('handing over to a third party demands their name', (
tester,
) async {
await pumpStop(tester, stopOf(type: 'delivery', deliveryQty: 1));
await tester.tap(find.text('Parcel 1 of 1'));
await tester.pumpAndSettle();
expect(find.text("Person's name"), findsNothing);
await tester.tap(find.text('Security'));
await tester.pumpAndSettle();
expect(find.text("Person's name"), findsOneWidget);
expect(
find.text('Enter the name of the person who took it'),
findsOneWidget,
);
});
testWidgets('a short count forces a written reason', (tester) async {
await pumpStop(tester, stopOf(type: 'pickup', pickupQty: 3));
// Pre-set to the booked count, so the common case needs no explanation.
expect(find.text('3'), findsOneWidget);
expect(find.text('What happened?'), findsNothing);
await tester.tap(find.byIcon(LucideIcons.minus));
await tester.pumpAndSettle();
expect(find.text('1 fewer than the parcels booked'), findsOneWidget);
expect(find.text('What happened?'), findsOneWidget);
});
testWidgets('damaged goods force a written reason too', (tester) async {
await pumpStop(tester, stopOf(type: 'pickup', pickupQty: 1));
expect(find.text('What happened?'), findsNothing);
await tester.tap(find.text('Damaged'));
await tester.pumpAndSettle();
expect(find.text('What happened?'), findsOneWidget);
});
testWidgets('COD is surfaced on the stop summary', (tester) async {
await pumpStop(tester, stopOf(type: 'pickup', pickupQty: 1, cash: 720));
expect(find.text('₹720 to collect'), findsOneWidget);
});
}