import 'package:flutter_test/flutter_test.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:mockito/mockito.dart'; import 'package:mockito/annotations.dart'; import 'package:travel_mate/blocs/auth/auth_bloc.dart'; import 'package:travel_mate/blocs/auth/auth_event.dart'; import 'package:travel_mate/blocs/auth/auth_state.dart'; import 'package:travel_mate/repositories/auth_repository.dart'; import 'package:travel_mate/models/user.dart'; import 'package:firebase_auth/firebase_auth.dart' as firebase_auth; import 'package:travel_mate/services/notification_service.dart'; import 'auth_bloc_test.mocks.dart'; @GenerateMocks([AuthRepository, NotificationService]) void main() { group('AuthBloc', () { late MockAuthRepository mockAuthRepository; late MockNotificationService mockNotificationService; late AuthBloc authBloc; final user = User( id: '123', nom: 'Doe', prenom: 'John', email: 'test@example.com', platform: 'email', ); setUp(() { mockAuthRepository = MockAuthRepository(); mockNotificationService = MockNotificationService(); authBloc = AuthBloc( authRepository: mockAuthRepository, notificationService: mockNotificationService, ); // Default stub for saveTokenToFirestore to avoid strict mock errors when( mockNotificationService.saveTokenToFirestore(any), ).thenAnswer((_) async {}); }); tearDown(() { authBloc.close(); }); test('initial state is AuthUninitialized', () { expect(authBloc.state, AuthInitial()); }); blocTest( 'emits [AuthAuthenticated] when AuthCheckRequested is added and user is logged in', build: () { when( mockAuthRepository.currentUser, ).thenReturn(MockFirebaseUser(uid: '123', email: 'test@example.com')); when( mockAuthRepository.getUserFromFirestore('123'), ).thenAnswer((_) async => user); return authBloc; }, act: (bloc) => bloc.add(AuthCheckRequested()), expect: () => [AuthLoading(), AuthAuthenticated(user: user)], ); blocTest( 'emits [AuthUnauthenticated] when AuthCheckRequested is added and user is not logged in', build: () { when(mockAuthRepository.currentUser).thenReturn(null); return authBloc; }, act: (bloc) => bloc.add(AuthCheckRequested()), expect: () => [AuthLoading(), AuthUnauthenticated()], ); blocTest( 'emits [AuthAuthenticated] when AuthSignInRequested is added', build: () { when( mockAuthRepository.signInWithEmailAndPassword( email: 'test@example.com', password: 'password', ), ).thenAnswer((_) async => user); return authBloc; }, act: (bloc) => bloc.add( const AuthSignInRequested( email: 'test@example.com', password: 'password', ), ), expect: () => [AuthLoading(), AuthAuthenticated(user: user)], ); blocTest( 'emits [AuthUnauthenticated] when AuthSignOutRequested is added', build: () { when(mockAuthRepository.signOut()).thenAnswer((_) async {}); return authBloc; }, act: (bloc) => bloc.add(AuthSignOutRequested()), expect: () => [AuthUnauthenticated()], verify: (_) { verify(mockAuthRepository.signOut()).called(1); }, ); }); } // Simple Mock for FirebaseUser since we can't easily mock the real one without more boilerplate // or using firebase_auth_mocks package which we didn't add. // However, AuthRepository.currentUser returns firebase_auth.User. // Attempting to mock it via extends might be tricky due to private constructors. // Let's rely on Mockito to generate a mock for firebase_auth.User if needed, // or adjusting the test to not depend on the return value of currentUser being a complex object // if the Bloc only checks for null. // // Looking at AuthBloc source (I haven't read it yet), it probably checks `authRepository.currentUser`. // I'll read AuthBloc code in the next step to be sure how to mock the return value. class MockFirebaseUser extends Mock implements firebase_auth.User { @override final String uid; @override final String? email; MockFirebaseUser({required this.uid, this.email}); }