import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:miler/data/service_profile.dart'; import 'package:miler/helpers/rider_shell.dart'; /// Does the ported delivery dashboard actually *build*? /// /// ── Why this is worth a test of its own ── /// /// `delivery_line_test.dart` checks that [riderShell] returns the right widget /// *type*, which is a cheap assertion: it constructs nothing. The failure this /// port was most exposed to is one type-checking cannot see — the delivery home /// page resolves its controller in a **field initialiser**: /// /// final DeliveryController _deliveryController = Get.find(); /// /// GetX keys its container on type, and both lines declare a class called /// `RiderLogController`, `LogController` and `ProfileController`. So a delivery /// screen asking for "the" RiderLogController gets nothing unless the *xpress* /// type was registered — and it throws while the widget is being constructed, /// before any frame, which a rider sees as a red screen rather than an empty /// list. See `bootstrapDeliveryLine`. /// /// Pumping the shell for real is the only thing that catches that. /// /// ── Why there is no matching test for the parcel shell ── /// /// The parcel dashboard reads `PickupController` and friends out of the GetX /// container too, but nothing registers them for it — `main()` does, and tests /// do not run `main()`. Pumping it here would be testing this file's ability to /// re-implement `main()`, not the app. /// /// The delivery shell needs no such scaffolding, and that asymmetry is the /// point: [riderShell] calls `registerDeliveryLine()` on the way in, precisely /// because a rider can reach it mid-session by signing out of one line and into /// the other. This test passing with no setup *is* that guard working — and it /// is what caught the first version of that call being `async`, which registered /// nothing before the shell built. void main() { TestWidgetsFlutterBinding.ensureInitialized(); // ── What is allowed to have gone wrong ── // // One thing, inherited with the markup: Flutter's debug-only complaint that a // `ListTile` sits inside a coloured `DecoratedBox`, so its ink splash is // hidden. Cosmetic, debug-only, and present in Xpress-rider before the port. // // Filtered at `FlutterError.onError` rather than drained afterwards with // `takeException`, which returns one exception at a time while the harness // fails the test on the whole batch. Everything else still reaches the // default handler and still fails the test — in particular a `Get.find` // failure or a missing plugin, which would mean the port does not run. // // Installed **inside the test body**, not in `setUp`: the test binding // installs its own reporter when it starts running the body, which is after // `setUp`, so a handler set there is overwritten before the first pump. tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); testWidgets('the delivery dashboard builds and shows its four tabs', ( tester, ) async { final reportToHarness = FlutterError.onError; FlutterError.onError = (details) { if (details.exceptionAsString().contains( 'ListTile background color or ink splashes', )) { return; } reportToHarness?.call(details); }; addTearDown(() => FlutterError.onError = reportToHarness); SharedPreferences.setMockInitialValues({}); // ── Set directly, not resolved ── // // No tenant name or id opens this line any more; only // `--dart-define=TENANT=delivery` does, and that is a compile-time constant // a test cannot set. See `delivery_line_test.dart` for why the wire route // was closed. // // What this test is actually for survives that unchanged: it is the guard // on `registerDeliveryLine()` being **synchronous**. An async version // returns at its first await, the framework builds the shell in the gap, // and the home page throws on a controller that is about to exist. Only // pumping the real shell catches it. ServiceProfile.setActive(ServiceProfile.delivery); await tester.pumpWidget( ScreenUtilInit( designSize: const Size(390, 844), builder: (_, _) => MaterialApp(home: riderShell()), ), ); await tester.pump(); // The ported Xpress-rider bar, verbatim: four fixed tabs, shouted in caps. // If the controllers had not been registered, none of this would exist — // the page would have thrown on construction. expect(find.text('HOME'), findsOneWidget); expect(find.text('DELIVERIES'), findsOneWidget); expect(find.text('SUMMARY'), findsOneWidget); expect(find.text('PROFILE'), findsOneWidget); // And it is not the parcel bar wearing different words. expect(find.text('Activity'), findsNothing); expect(find.text('Bookings'), findsNothing); }); }