import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:miler/data/service_profile.dart'; import 'package:miler/views/Dashboard/home/trip.dart'; import 'package:miler/views/Dashboard/home/trip_card.dart'; /// ───────────────────────────────────────────────────────────────────────── /// A STOP HE IS STANDING AT MUST NOT LEAVE THE SCREEN /// /// Reported from a live run: the rider marked **Arrived**, the console updated /// correctly — and the stop vanished off Home. Deliveries did not have it /// either, and could not: arrival is a *pre-pickup* rung, and the delivery tab /// only takes work once it is collected. The stop existed in neither place. /// /// ── What caused it ── /// /// Nothing on the backend. [StopState.arrived] and [StopState.active] used to /// be one value, and `TripCard._showsOnHome` — like a dozen other sites — asked /// `state == StopState.active` to mean *is he at the counter*. Splitting the /// two apart so the row could say **Arrived** instead of **Active** left every /// one of those comparisons answering `false` for the new rung. /// /// The visibility filter was the one the rider could see. The others were just /// as broken and quieter: the selection bar offered *Accept* for a stop he was /// already at, the card stopped building its **Mark as picked** button, and the /// group he was working no longer opened by default. /// /// These pin the rung end to end — the predicate that now answers the question /// once ([StopStateX.isAtSource]), and the screen behaviour that regressed. /// ───────────────────────────────────────────────────────────────────────── void main() { tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); Map meal(String id, String who, {String? status}) => { 'orderid': id, 'type': 'delivery', 'step': int.parse(id), 'pickupcustomer': who, 'kitchenid': 'K1', 'kitchenname': 'Vidhya Kitchen', 'sourcename': 'Vidhya Kitchen', 'pickupcontactno': '9787698259', 'pickupaddress': '4 Cross, RS Puram, Coimbatore', 'dropaddress': '12 SNS Colony, Peelamedu, Coimbatore 641004', 'Quantity': '1', if (status != null) 'orderstatus': status, }; Future pumpHome( WidgetTester tester, List> stops, ) async { ServiceProfile.setActive(ServiceProfile.milkMan); tester.view.physicalSize = const Size(390 * 3, 2600 * 3); tester.view.devicePixelRatio = 3.0; addTearDown(tester.view.reset); await tester.pumpWidget( ScreenUtilInit( designSize: const Size(390, 844), builder: (_, _) => MaterialApp( home: Scaffold( body: SingleChildScrollView( child: TripCard( trip: Trip.fromStops(id: 'run', stops: stops), acceptedIds: const {}, selectedIds: const {}, onToggleSelect: (_) {}, onSelectAll: (_) {}, onStopTap: (_) {}, onCallStop: (_) {}, ), ), ), ), ), ); // Not `pumpAndSettle`: an arrived row is a **live** row, and [LiveMark] // pulses for as long as it is on screen — settling waits for an animation // that is never going to stop. Two pumps lay the frame out and let the // pulse start, which is all these assertions need. await tester.pump(); await tester.pump(const Duration(milliseconds: 300)); } group('1 · the predicate that answers it once', () { test('arrived and active both mean "he is at the source"', () { expect(StopState.arrived.isAtSource, isTrue); expect(StopState.active.isAtSource, isTrue); }); test('and nothing else does', () { for (final other in const [ StopState.pending, StopState.accepted, StopState.collected, StopState.done, StopState.skipped, StopState.rejected, ]) { expect( other.isAtSource, isFalse, reason: '$other must not read as standing at the counter', ); } }); test('an arrived stop is committed work, not an open offer', () { // It rode in on `active` and was dropped by the same split. A rider who // has reported an arrival has plainly committed to the stop. expect(StopState.arrived.isCommitted, isTrue); expect(StopState.pending.isCommitted, isFalse); }); }); group('2 · the screen behaviour that regressed', () { testWidgets('an arrived stop stays on Home', (tester) async { await pumpHome(tester, [ meal('1', 'Joe Mathew', status: 'arrived'), meal('2', 'Arun Prakash'), ]); expect( find.text('Joe Mathew'), findsOneWidget, reason: 'the rider is standing at this counter — it cannot leave Home, ' 'and Deliveries will not take it until it is collected', ); }); testWidgets('it is still there beside an untouched stop', (tester) async { // Guards the opposite overcorrection: making arrived visible must not // have cost the ordinary rows their place in the run. await pumpHome(tester, [ meal('1', 'Joe Mathew', status: 'arrived'), meal('2', 'Arun Prakash'), ]); expect(find.text('Joe Mathew'), findsOneWidget); expect(find.text('Arun Prakash'), findsOneWidget); }); testWidgets('a collected stop still leaves — that one is not a bug', ( tester, ) async { // The filter's real job, asserted so a fix for the disappearance cannot // quietly turn into "nothing ever leaves Home". await pumpHome(tester, [ meal('1', 'Joe Mathew', status: 'picked'), meal('2', 'Arun Prakash'), ]); expect(find.text('Joe Mathew'), findsNothing); expect(find.text('Arun Prakash'), findsOneWidget); }); }); }