import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:gametime/application/application.dart'; import 'package:gametime/domain/domain.dart'; import 'package:gametime/presentation/profile_screen.dart'; void main() { testWidgets('le profil déconnecté invite sans bloquer le reste', ( tester, ) async { final harness = _AuthHarness(); await tester.pumpWidget( MaterialApp(home: ProfileScreen(authUseCases: harness.useCases)), ); await tester.pump(); expect(find.text('Compte optionnel'), findsOneWidget); expect(find.text('Créer un compte'), findsOneWidget); expect(find.text('Se connecter'), findsOneWidget); expect(find.byType(AlertDialog), findsNothing); }); testWidgets('la connexion invalide affiche une erreur inline', ( tester, ) async { final harness = _AuthHarness() ..remote.loginFailure = RemoteAuthFailure.invalidCredentials; await tester.pumpWidget( MaterialApp(home: ProfileScreen(authUseCases: harness.useCases)), ); await tester.pump(); await tester.tap(find.text('Se connecter')); await tester.pumpAndSettle(); await tester.enterText( find.widgetWithText(TextFormField, 'Email'), 'a@b.fr', ); await tester.enterText( find.widgetWithText(TextFormField, 'Mot de passe'), 'password1', ); await tester.tap(find.widgetWithText(FilledButton, 'Se connecter')); await tester.pumpAndSettle(); expect(find.text('Email ou mot de passe incorrect.'), findsOneWidget); expect(find.byType(AlertDialog), findsNothing); }); testWidgets('la connexion réussie affiche le profil connecté', ( tester, ) async { final harness = _AuthHarness(); await tester.pumpWidget( MaterialApp(home: ProfileScreen(authUseCases: harness.useCases)), ); await tester.pump(); await tester.tap(find.text('Se connecter')); await tester.pumpAndSettle(); await tester.enterText( find.widgetWithText(TextFormField, 'Email'), 'alex@example.com', ); await tester.enterText( find.widgetWithText(TextFormField, 'Mot de passe'), 'password1', ); await tester.tap(find.widgetWithText(FilledButton, 'Se connecter')); await tester.pumpAndSettle(); expect(find.text('alex@example.com'), findsOneWidget); expect(find.text('Synchronisation'), findsOneWidget); expect(find.text('Partages reçus'), findsOneWidget); }); testWidgets('la déconnexion repasse au profil déconnecté', (tester) async { final harness = _AuthHarness( initialSession: UserAccountSession( id: 'account-1', serverUserId: 'server-user-1', email: 'alex@example.com', displayName: 'Alex', isLoggedIn: true, createdAt: DateTime.utc(2026, 7, 17), updatedAt: DateTime.utc(2026, 7, 17), ), initialToken: 'token-1', ); await tester.pumpWidget( MaterialApp(home: ProfileScreen(authUseCases: harness.useCases)), ); await tester.pump(); expect(find.text('Alex'), findsOneWidget); await tester.tap(find.text('Se déconnecter')); await tester.pumpAndSettle(); await tester.tap(find.widgetWithText(FilledButton, 'Se déconnecter')); await tester.pumpAndSettle(); expect(harness.tokenStore.token, isNull); expect(harness.accountRepository.session?.isLoggedIn, isFalse); expect(find.text('Compte optionnel'), findsOneWidget); }); } final class _AuthHarness { _AuthHarness({UserAccountSession? initialSession, String? initialToken}) : tokenStore = _FakeAuthTokenStore(initialToken), accountRepository = _FakeOnlineAccountRepository(initialSession), remote = _FakeRemoteAuthApi() { useCases = AuthUseCases( tokenStore: tokenStore, accountRepository: accountRepository, remoteAuthApi: remote, clock: const _FakeClock(), ids: _FakeIds(), ); } final _FakeAuthTokenStore tokenStore; final _FakeOnlineAccountRepository accountRepository; final _FakeRemoteAuthApi remote; late final AuthUseCases useCases; } final class _FakeClock implements Clock { const _FakeClock(); @override DateTime now() => DateTime.utc(2026, 7, 17, 12); } final class _FakeIds implements IdGenerator { var _next = 0; @override String newId() { _next += 1; return 'local-account-$_next'; } } final class _FakeAuthTokenStore implements AuthTokenStore { _FakeAuthTokenStore(this.token); String? token; @override Future clearToken() async { token = null; } @override Future readToken() async => token; @override Future saveToken(String token, DateTime expiresAt) async { this.token = token; } } final class _FakeOnlineAccountRepository implements OnlineAccountRepository { _FakeOnlineAccountRepository(this.session); UserAccountSession? session; @override Future clearSession() async { session = null; } @override Future currentSession() async => session; @override Future saveSession(UserAccountSession session) async { this.session = session; } } final class _FakeRemoteAuthApi implements RemoteAuthApi { RemoteAuthFailure? loginFailure; RemoteAuthFailure? registerFailure; @override Future login({ required String email, required String password, }) async { final failure = loginFailure; if (failure != null) { throw RemoteAuthException(failure); } return RemoteAuthResult( userId: 'server-user-1', email: email.trim(), token: 'token-1', expiresAt: DateTime.utc(2026, 7, 18), ); } @override Future logout(String token) async {} @override Future register({ required String email, required String password, String? displayName, }) async { final failure = registerFailure; if (failure != null) { throw RemoteAuthException(failure); } return RemoteAuthResult( userId: 'server-user-1', email: email.trim(), token: 'token-1', expiresAt: DateTime.utc(2026, 7, 18), ); } }