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>
171 lines
6.3 KiB
Dart
171 lines
6.3 KiB
Dart
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);
|
||
});
|
||
}
|