import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; import 'package:sxchnge_auth/sxchnge_auth.dart'; import 'package:sxchnge_app/main.dart'; import 'package:sxchnge_app/providers/locale.dart'; class RecoveringService implements VerificationService { int sends = 0; int confirmations = 0; @override Future send({ required String email, required String locale, }) async { if (++sends == 1) throw StateError('private error'); return const VerificationResult(isSimulated: true); } @override Future confirm({required String token}) async { if (++confirmations == 1) throw StateError('private error'); return const VerificationResult(isSimulated: true); } } Future openVerification( WidgetTester tester, String route, { VerificationService service = const DummyVerificationService( delay: Duration.zero, ), }) async { tester.platformDispatcher.defaultRouteNameTestValue = route; addTearDown(tester.platformDispatcher.clearDefaultRouteNameTestValue); await tester.pumpWidget(SxchngeApp(verificationService: service)); await tester.pumpAndSettle(); } void main() { for (final scenario in { 'demo-valid': 'verified', 'demo-expired': 'expired', 'wrong': 'invalid', '': 'invalid', 'demo-internal': 'error', }.entries) { testWidgets( 'confirmation link resolves ${scenario.key} as ${scenario.value}', (tester) async { await openVerification(tester, '/verify-email?token=${scenario.key}'); expect( find.byKey(ValueKey('verification-${scenario.value}')), findsOneWidget, ); expect(find.textContaining('DEMO'), findsNothing); expect(tester.takeException(), isNull); }, ); } testWidgets('internal confirmation error can retry without entering email', ( tester, ) async { final service = RecoveringService(); await openVerification( tester, '/verify-email?token=opaque', service: service, ); expect(find.text('private error'), findsNothing); final retry = find.byKey(const ValueKey('verification-retry')); await tester.ensureVisible(retry); await tester.tap(retry); await tester.pumpAndSettle(); expect(find.byKey(const ValueKey('verification-verified')), findsOneWidget); expect(service.confirmations, 2); }); testWidgets( 'request validates email, reports send error and resends successfully', (tester) async { final service = RecoveringService(); await openVerification(tester, '/verification-sent', service: service); final resend = find.byKey(const ValueKey('verification-resend')); await tester.ensureVisible(resend); await tester.tap(resend); await tester.pumpAndSettle(); expect(find.text('Enter your email address.'), findsOneWidget); expect(service.sends, 0); await tester.enterText( find.byKey(const ValueKey('verification-email')), 'alex@example.com', ); await tester.ensureVisible(resend); await tester.tap(resend); await tester.pumpAndSettle(); expect(find.byKey(const ValueKey('verification-error')), findsOneWidget); await tester.ensureVisible(resend); await tester.tap(resend); await tester.pumpAndSettle(); expect(find.byKey(const ValueKey('verification-sent')), findsOneWidget); expect(service.sends, 2); }, ); for (final route in [ '/verification-sent', '/verify-email?token=demo-valid', '/verify-email?token=demo-expired', '/verify-email?token=bad', '/verify-email?token=demo-internal', ]) { testWidgets('$route fits narrow viewport at 200% in EN and RU', ( tester, ) async { tester.view.physicalSize = const Size(320, 740); tester.view.devicePixelRatio = 1; tester.platformDispatcher.textScaleFactorTestValue = 2; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); addTearDown(tester.platformDispatcher.clearTextScaleFactorTestValue); await openVerification(tester, route); expect(tester.takeException(), isNull); final element = tester.element(find.byType(Scaffold).first); await element.read().setLocale(const Locale('ru')); await tester.pumpAndSettle(); expect(find.byKey(const ValueKey('locale-selector')), findsNothing); expect(tester.takeException(), isNull); }); } }