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'; import 'package:miler/views/helpers/constants/Colorconstants.dart'; import 'package:miler/views/helpers/widgets/app_widgets.dart'; /// ── The form is the screen; the boxes were not ── /// /// Every group on a verification leg used to be a filled, rounded, 16pt-padded /// panel — the stop summary, the parcel checklist, the recipient picker, the /// OTP, the photo — four to six of them stacked, each holding white controls. /// That put a *container* rank and a *control* rank on the page at the same /// weight, when only one of the two is something the rider touches at a door. /// /// They are named and ruled off instead. These tests pin the parts of that /// which a later change could quietly undo. void main() { Future pump( WidgetTester tester, Map stop, { double width = 390, }) async { tester.view.physicalSize = Size(width * 3, 844 * 3); tester.view.devicePixelRatio = 3.0; addTearDown(tester.view.reset); await tester.pumpWidget( ScreenUtilInit( designSize: const Size(390, 844), builder: (_, _) => MaterialApp(home: StopVerificationPage(pickup: stop)), ), ); await tester.pumpAndSettle(); } Map stopOf({ required String type, int deliveryQty = 0, int pickupQty = 0, }) => { 'orderid': 'T-1', 'type': type, 'pickupcustomer': 'Priya Sharma', 'pickupaddress': '45, Lake View Apartments, Race Course, Coimbatore', if (deliveryQty > 0) 'deliveryqty': '$deliveryQty', if (pickupQty > 0) 'pickupqty': '$pickupQty', 'Quantity': '${pickupQty > 0 ? pickupQty : deliveryQty}', }; /// The width of a chip, measured on the box that actually draws it. double chipWidth(WidgetTester tester, String label) => tester .getSize( find .ancestor( of: find.text(label), matching: find.byType(AnimatedContainer), ) .first, ) .width; testWidgets('a leg step draws no filled panels', (tester) async { await pump(tester, stopOf(type: 'combined', deliveryQty: 1, pickupQty: 2)); // `cardSurface` is the panel fill. It is still right for the review step — // one grey block per leg, on the screen the rider signs — but on a step he // is filling in, every one of them was a box around work rather than the // work itself. final fills = tester .widgetList( find.descendant( of: find.byType(SingleChildScrollView), matching: find.byType(Container), ), ) .map((c) => c.decoration) .whereType() .map((d) => d.color); expect(fills, isNot(contains(ColorConstants.cardSurface))); }); testWidgets('a section names itself, and stays under the stop', ( tester, ) async { await pump(tester, stopOf(type: 'delivery', deliveryQty: 1)); // ── This assertion changed with the design, deliberately ── // // It used to require a grey letterspaced eyebrow, from when the sections // were hairline-ruled groups on a white page: with no boundary of their // own, a loud title would have been the only thing separating them, and // five loud titles is a page of shouting. // // They are panels now. The boundary does the separating, so the title is // free to be a title — the line the rider reads to know what this step // asks of him — set in the same ink as the rest of the panel's content // rather than in the grey reserved for metadata. final label = tester.widget(find.text('Who received it?')); expect(label.style!.color, ColorConstants.slateText); expect(label.style!.fontWeight, FontWeight.w700); // What has not changed, and must not: it is still quieter than the stop it // belongs to. The customer's name is the subject of the screen; a step // inside it is not allowed to outrank it. final name = tester.widget(find.text('Priya Sharma')); expect(label.style!.fontSize, lessThan(name.style!.fontSize!)); }); testWidgets('the pickers are an even grid, with no half-width orphan', ( tester, ) async { await pump(tester, stopOf(type: 'delivery', deliveryQty: 1)); // Four recipients: two rows of two, all the same width. Natural-width chips // packed themselves three-then-one, which reads as a list that ran out // rather than as a set of four equal choices. final customer = chipWidth(tester, 'Customer'); for (final other in const ['Family', 'Security', 'Neighbour']) { expect(chipWidth(tester, other), closeTo(customer, 0.5)); } }); testWidgets('an odd picker gives its last option the full row', ( tester, ) async { await pump(tester, stopOf(type: 'pickup', pickupQty: 2)); // Three conditions. The third at half width on a line of its own is an // orphan; at full width it is a second row. expect( chipWidth(tester, 'Damaged'), greaterThan(chipWidth(tester, 'Sealed & intact') * 1.8), ); }); testWidgets('a blocked step gets a neutral bar, not a faded coloured one', ( tester, ) async { await pump(tester, stopOf(type: 'combined', deliveryQty: 1, pickupQty: 2)); // Nothing has been filled in, so the step is blocked. expect(find.text('1 parcel still to hand over'), findsOneWidget); // No accent button at all — a saturated pill at 45% still reads as a // button, so the rider taps it and nothing happens. expect(find.byType(MilerButton), findsNothing); expect(find.byIcon(LucideIcons.lock), findsOneWidget); // And the label stays readable: grey type on a neutral fill, not white // dropped to 45% over a wash of itself. // Named for where it goes, not for which step it is: the rider is on the // hand-over leg, so the button announces the collection that follows it. final label = tester.widget(find.text('Continue to collect')); expect(label.style!.color, ColorConstants.secondaryText); }); testWidgets('completing the step turns the bar a solid accent', ( tester, ) async { await pump(tester, stopOf(type: 'delivery', deliveryQty: 1)); await tester.tap(find.text('Parcel 1 of 1')); await tester.pumpAndSettle(); await tester.enterText(find.byType(TextField).first, '482916'); await tester.pumpAndSettle(); // Photo is the last gate and the camera cannot be reached here, so the bar // is still blocked — but on the name of what is missing, not the parcels. expect(find.text('Take a proof photo'), findsOneWidget); // The parcel row and the OTP now report themselves done, which is the // section-scoped tick: it speaks for its own step, never for the screen. expect(find.text('Handed over'), findsOneWidget); }); testWidgets('the app bar clears the status bar', (tester) async { await pump(tester, stopOf(type: 'delivery', deliveryQty: 1)); // The title used to sit hard against the clock, which is where a system // default puts it and nowhere near where the rest of the app puts a // heading. `toolbarHeight` alone only moves it half as far as it adds. final title = tester.getRect(find.text('Verify delivery')); expect(title.top, greaterThan(24.0)); }); }