import 'package:doormile_cx/data/dev_doormile_api.dart'; import 'package:doormile_cx/data/models.dart'; import 'package:doormile_cx/main.dart'; import 'package:doormile_cx/state/app_state.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; /// The login screen must not walk a customer to the code screen when no code /// was sent. /// /// `sent: false` is the server saying it accepted the request and delivered /// nothing — the SMS gateway or the mail relay refused it. Advancing anyway /// puts four empty boxes in front of someone for a code that does not exist, /// and every digit they try is wrong. The failure then reads as "I typed it /// wrong" instead of "nothing arrived", which is the wrong problem to hand /// somebody. /// /// The resend control on the code screen has always honoured this flag. The /// login screen did not, which is the asymmetry these tests pin down. /// The app runs continuous animations, so `pumpAndSettle` never returns. Future settle(WidgetTester tester, [int ms = 800]) async { await tester.pump(); await tester.pump(Duration(milliseconds: ms)); await tester.pump(const Duration(milliseconds: 400)); } class _SendFails extends DevDoormileApi { @override Future sendOtp(String identifier) async => const OtpChallenge(sent: false); } class _SendSucceeds extends DevDoormileApi { var calls = 0; @override Future sendOtp(String identifier) async { calls++; return const OtpChallenge(sent: true); } } Future _tapContinue(WidgetTester tester, AppState state) async { tester.view.physicalSize = const Size(1230, 9000); tester.view.devicePixelRatio = 3.0; addTearDown(tester.view.reset); await tester.pumpWidget(DoormileApp(initialState: state)); await settle(tester); await tester.enterText(find.byType(TextField).first, '9876543210'); await settle(tester, 200); await tester.tap(find.text('Continue')); await settle(tester); } void main() { testWidgets('a code that was never sent does not open the code screen', (tester) async { await _tapContinue(tester, AppState(api: _SendFails())); expect( find.text('Verify your number'), findsNothing, reason: 'no code was delivered, so there is nothing to verify', ); expect( // The entry screen's own headline, which now sits on the brand field // rather than as a heading inside the sheet. find.text('Book a pickup from your door.'), findsOneWidget, reason: 'the customer stays where they can try again', ); // Drain the toast timer so the test ends with nothing pending. await tester.pump(const Duration(seconds: 4)); }); testWidgets('a delivered code does open the code screen', (tester) async { final api = _SendSucceeds(); await _tapContinue(tester, AppState(api: api)); expect(find.text('Verify your number'), findsOneWidget); expect(api.calls, 1); await tester.pump(const Duration(seconds: 4)); }); }