import 'package:doormile_cx/data/app_config.dart'; import 'package:doormile_cx/data/location_service.dart'; import 'package:doormile_cx/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; /// Proves that a build carrying a real token reaches a real, authenticated /// booking screen — on a device, against the real backend. /// /// ── Why this file shrank ── /// /// It used to walk the whole booking: sign in with `9876543210` and four 1s, /// then pickup → destination → window → review → confirmation. Both halves of /// that stopped being true. /// /// * **The sign-in** only ever worked under the offline `DM_MOCK` build, /// deleted on 15 Sep 2026 after it cost two rounds of hunting for bookings /// in the admin console that had never left the phone. There is no longer a /// four-digit code that signs anybody in. /// * **The flow** was rebuilt into ONE screen plus two sheets. There is no /// "Where are you sending it?" page, no "When should we come?" page and no /// review page left to walk; the city is a horizontal strip and the window /// is a modal sheet. /// /// So every finder in it referred to something that no longer exists, and it /// could not pass. What replaces it is deliberately narrower than what it /// claimed to do, and everything in it has been checked against the current /// screens rather than remembered. /// /// **What this asserts:** the app adopts a server-issued token through the same /// session store a normal login writes to, asks the backend who that is, opens /// the real Home, and reaches a booking screen whose empty-draft state is /// rendered from live serviceability. Every call in that chain is a real call. /// /// **What it does not assert:** the booking itself. Completing it needs a city /// tapped out of a server-loaded strip and a window chosen from a sheet, and a /// test that guesses at those without ever having run is worth less than an /// honest gap. Do that part by hand on the device — the button tells you where /// you are, in this order: /// /// "Pick a city to see the price" → "Choose a pickup window" → "Book pickup" /// /// Run it: /// /// flutter test integration_test/book_one_test.dart \ /// -d \ /// --dart-define=DM_DEV_TOKEN=eyJ... void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('a real token opens a real, authenticated booking screen', (tester) async { // ── Skip rather than fail confusingly ── // // With no token the app correctly opens the login screen, and every finder // below would fail on a missing widget — which reads as "the app is broken" // when the truth is "this run was given no identity". Say the missing thing // by name, once. if (!AppConfig.authBypassed) { markTestSkipped( 'No DM_DEV_TOKEN (or DM_LOGIN_AS) — this test runs against the real ' 'backend and needs a real session. Pass ' '--dart-define=DM_DEV_TOKEN=.', ); return; } // Deterministic pin, so the pickup row never stops on an OS location dialog // and resolves the same way every run. LocationService.instance = FixedLocationService(); await tester.pumpWidget(const DoormileApp()); // The app runs continuous animations, so pumpAndSettle never returns. Pump // fixed slices instead. Future hold([int ms = 900]) async { await tester.pump(); await tester.pump(Duration(milliseconds: ms)); await tester.pump(const Duration(milliseconds: 400)); } // Longer than the other holds on purpose: this is two network calls — the // token is adopted into the session store, then `GET /customer/auth/me` is // asked who it belongs to — and against a real server they are not instant. await hold(4000); expect( find.text('Where should we pick up?'), findsOneWidget, reason: 'the app did not reach Home. A token that is expired or revoked ' 'lands on the login screen by design — that is the session being ' 'refused, not a bug in the app.', ); debugPrint('DRIVER: real session restored from DM_DEV_TOKEN, on Home'); await tester.tap(find.text('BOOK')); await hold(2500); expect(find.text('Send a parcel'), findsOneWidget); // The empty-draft state, in the button's own words. Reaching this proves // the screen built against live serviceability rather than a fixture: the // city strip is an async list off the server, and this is the label the // screen shows while nothing in it is selected. expect( find.text('Pick a city to see the price'), findsOneWidget, reason: 'the booking screen did not reach its empty-draft state', ); debugPrint('DRIVER: on the booking screen, awaiting a city — ' 'finish by hand from here'); }); }