diff --git a/apps/lib/features/auth/presentation/cubits/login_cubit.dart b/apps/lib/features/auth/presentation/cubits/login_cubit.dart new file mode 100644 index 0000000..817656e --- /dev/null +++ b/apps/lib/features/auth/presentation/cubits/login_cubit.dart @@ -0,0 +1,76 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:formz/formz.dart'; +import 'package:equatable/equatable.dart'; +import '../../data/auth_repository.dart'; +import '../../data/models/login_request.dart'; +import '../../data/models/auth_response.dart'; +import 'register_cubit.dart' show Email, Password; + +class LoginState extends Equatable { + final Email email; + final Password password; + final FormzSubmissionStatus status; + final String? errorMessage; + + const LoginState({ + this.email = const Email.pure(), + this.password = const Password.pure(), + this.status = FormzSubmissionStatus.initial, + this.errorMessage, + }); + + bool get isValid => email.isValid && password.isValid; + + LoginState copyWith({ + Email? email, + Password? password, + FormzSubmissionStatus? status, + String? errorMessage, + }) { + return LoginState( + email: email ?? this.email, + password: password ?? this.password, + status: status ?? this.status, + errorMessage: errorMessage, + ); + } + + @override + List get props => [email, password, status, errorMessage]; +} + +class LoginCubit extends Cubit { + final AuthRepository _repository; + + LoginCubit(this._repository) : super(const LoginState()); + + void emailChanged(String value) { + emit(state.copyWith(email: Email.dirty(value))); + } + + void passwordChanged(String value) { + emit(state.copyWith(password: Password.dirty(value))); + } + + Future submit() async { + if (!state.isValid) return null; + + emit(state.copyWith(status: FormzSubmissionStatus.inProgress)); + + try { + final response = await _repository.login( + LoginRequest(email: state.email.value, password: state.password.value), + ); + emit(state.copyWith(status: FormzSubmissionStatus.success)); + return response; + } catch (e) { + emit( + state.copyWith( + status: FormzSubmissionStatus.failure, + errorMessage: e.toString(), + ), + ); + return null; + } + } +} diff --git a/apps/test/features/auth/presentation/cubits/login_cubit_test.dart b/apps/test/features/auth/presentation/cubits/login_cubit_test.dart new file mode 100644 index 0000000..3403ff7 --- /dev/null +++ b/apps/test/features/auth/presentation/cubits/login_cubit_test.dart @@ -0,0 +1,42 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:formz/formz.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:social_app/features/auth/data/auth_repository.dart'; +import 'package:social_app/features/auth/presentation/cubits/login_cubit.dart'; + +class MockAuthRepository extends Mock implements AuthRepository {} + +void main() { + late LoginCubit cubit; + late MockAuthRepository mockRepository; + + setUp(() { + mockRepository = MockAuthRepository(); + cubit = LoginCubit(mockRepository); + }); + + tearDown(() { + cubit.close(); + }); + + group('LoginCubit', () { + test('initial state has pure status', () { + expect(cubit.state.status, FormzSubmissionStatus.initial); + }); + + blocTest( + 'emailChanged updates email', + build: () => cubit, + act: (c) => c.emailChanged('test@example.com'), + expect: () => [isA()], + ); + + blocTest( + 'passwordChanged updates password', + build: () => cubit, + act: (c) => c.passwordChanged('password123'), + expect: () => [isA()], + ); + }); +}