diff --git a/lib/feature/login/controllers/login_controller.dart b/lib/feature/login/controllers/login_controller.dart index 2dd8f6b..7b4da7f 100644 --- a/lib/feature/login/controllers/login_controller.dart +++ b/lib/feature/login/controllers/login_controller.dart @@ -34,11 +34,11 @@ class LoginController extends GetxController { @override void onInit() { super.onInit(); - emailController.addListener(_validateFields); - passwordController.addListener(_validateFields); + emailController.addListener(validateFields); + passwordController.addListener(validateFields); } - void _validateFields() { + void validateFields() { final isEmailNotEmpty = emailController.text.trim().isNotEmpty; final isPasswordNotEmpty = passwordController.text.trim().isNotEmpty; isButtonEnabled.value = (isEmailNotEmpty && isPasswordNotEmpty) ? ButtonType.primary : ButtonType.disabled; diff --git a/pubspec.lock b/pubspec.lock index 2e87f83..f409f2b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f + url: "https://pub.dev" + source: hosted + version: "82.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0" + url: "https://pub.dev" + source: hosted + version: "7.4.5" args: dependency: transitive description: @@ -25,6 +41,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4 + url: "https://pub.dev" + source: hosted + version: "8.9.5" characters: dependency: transitive description: @@ -41,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + url: "https://pub.dev" + source: hosted + version: "4.10.1" collection: dependency: transitive description: @@ -49,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" crypto: dependency: transitive description: @@ -65,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + url: "https://pub.dev" + source: hosted + version: "3.0.1" dio: dependency: "direct main" description: @@ -121,6 +185,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -165,6 +237,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.6.6" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" google_fonts: dependency: "direct main" description: @@ -325,6 +405,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + mockito: + dependency: "direct dev" + description: + name: mockito + sha256: f99d8d072e249f719a5531735d146d8cf04c580d93920b04de75bef6dfb2daf6 + url: "https://pub.dev" + source: hosted + version: "5.4.5" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" path: dependency: transitive description: @@ -397,6 +493,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" shared_preferences: dependency: "direct main" description: @@ -474,6 +578,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + url: "https://pub.dev" + source: hosted + version: "2.0.0" source_span: dependency: transitive description: @@ -546,6 +658,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.3.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + url: "https://pub.dev" + source: hosted + version: "1.1.1" web: dependency: transitive description: @@ -562,6 +682,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" sdks: dart: ">=3.6.0 <4.0.0" flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 823951f..c5c13c3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + mockito: ^5.4.4 # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is diff --git a/test/controller/login_controller_test.dart b/test/controller/login_controller_test.dart new file mode 100644 index 0000000..55874cf --- /dev/null +++ b/test/controller/login_controller_test.dart @@ -0,0 +1,135 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:get/get.dart'; +// import 'package:mockito/mockito.dart'; +// import 'package:quiz_app/app/routes/app_pages.dart'; +// import 'package:quiz_app/component/global_button.dart'; +// import 'package:quiz_app/data/controllers/user_controller.dart'; +// import 'package:quiz_app/data/entity/user/user_entity.dart'; +// import 'package:quiz_app/data/models/login/login_request_model.dart'; +// import 'package:quiz_app/data/models/login/login_response_model.dart'; +// import 'package:quiz_app/data/services/auth_service.dart'; +// import 'package:quiz_app/data/services/google_auth_service.dart'; +// import 'package:quiz_app/data/services/user_storage_service.dart'; +// import 'package:quiz_app/feature/login/controllers/login_controller.dart'; + +// class MockAuthService extends Mock implements AuthService {} + +// class MockUserStorageService extends Mock implements UserStorageService {} + +// class MockUserController extends Mock implements UserController {} + +// class MockGoogleAuthService extends Mock implements GoogleAuthService {} + +// void main() { +// late LoginController loginController; +// late MockAuthService mockAuthService; +// late MockUserStorageService mockUserStorageService; +// late MockUserController mockUserController; +// late MockGoogleAuthService mockGoogleAuthService; + +// setUp(() { +// mockAuthService = MockAuthService(); +// mockUserStorageService = MockUserStorageService(); +// mockUserController = MockUserController(); +// mockGoogleAuthService = MockGoogleAuthService(); + +// loginController = LoginController( +// mockAuthService, +// mockUserStorageService, +// mockUserController, +// mockGoogleAuthService, +// ); +// }); + +// test('Initial state should have button disabled and password hidden', () { +// expect(loginController.isButtonEnabled.value, ButtonType.disabled); +// expect(loginController.isPasswordHidden.value, true); +// }); + +// test('Button should enable when both fields are not empty', () { +// loginController.emailController.text = 'test@example.com'; +// loginController.passwordController.text = 'password123'; + +// loginController.validateFields(); + +// expect(loginController.isButtonEnabled.value, ButtonType.primary); +// }); + +// test('Button should disable if email or password is empty', () { +// loginController.emailController.text = ''; +// loginController.passwordController.text = 'password123'; + +// loginController.validateFields(); + +// expect(loginController.isButtonEnabled.value, ButtonType.disabled); +// }); + +// test('Toggle password visibility works', () { +// final initial = loginController.isPasswordHidden.value; +// loginController.togglePasswordVisibility(); +// expect(loginController.isPasswordHidden.value, !initial); +// }); + +// test('Successful email login navigates to main page', () async { +// final response = LoginResponseModel(id: '1', name: 'John', email: 'john@example.com'); + +// when(mockAuthService.loginWithEmail(any)).thenAnswer((_) async => response); +// when(mockUserStorageService.saveUser(any)).thenAnswer((_) async => {}); +// when(mockUserStorageService.isLogged = true).thenReturn(true); + +// loginController.emailController.text = 'john@example.com'; +// loginController.passwordController.text = 'password123'; + +// await loginController.loginWithEmail(); + +// verify(mockAuthService.loginWithEmail(any)).called(1); +// verify(mockUserStorageService.saveUser(any)).called(1); +// verify(mockUserController.setUserFromEntity(any)).called(1); +// }); + +// test('Login with empty fields should show error', () async { +// loginController.emailController.text = ''; +// loginController.passwordController.text = ''; + +// await loginController.loginWithEmail(); + +// expect(loginController.isLoading.value, false); +// }); + +// test('Google login canceled should show error', () async { +// when(mockGoogleAuthService.signIn()).thenAnswer((_) async => null); + +// await loginController.loginWithGoogle(); + +// verifyNever(mockAuthService.loginWithGoogle(any)); +// }); + +// test('Successful Google login navigates to main page', () async { +// final fakeGoogleUser = FakeGoogleUser(); +// final response = LoginResponseModel(id: '1', name: 'John', email: 'john@example.com'); + +// when(mockGoogleAuthService.signIn()).thenAnswer((_) async => fakeGoogleUser); +// when(fakeGoogleUser.authentication).thenAnswer((_) async => FakeGoogleAuth()); +// when(mockAuthService.loginWithGoogle(any)).thenAnswer((_) async => response); +// when(mockUserStorageService.saveUser(any)).thenAnswer((_) async => {}); +// when(mockUserStorageService.isLogged = true).thenReturn(true); + +// await loginController.loginWithGoogle(); + +// verify(mockAuthService.loginWithGoogle(any)).called(1); +// verify(mockUserStorageService.saveUser(any)).called(1); +// verify(mockUserController.setUserFromEntity(any)).called(1); +// }); +// } + +// /// Fakes for Google Sign-In +// class FakeGoogleUser extends Mock { +// @override +// Future get authentication async => FakeGoogleAuth(); +// } + +// class FakeGoogleAuth extends Mock { +// @override +// Future get idToken async => 'fake_id_token'; +// } diff --git a/test/controller/register_controller_test.dart b/test/controller/register_controller_test.dart new file mode 100644 index 0000000..6ef2ef3 --- /dev/null +++ b/test/controller/register_controller_test.dart @@ -0,0 +1,115 @@ +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:mockito/mockito.dart'; +// import 'package:quiz_app/data/models/register/register_request.dart'; +// import 'package:quiz_app/data/services/auth_service.dart'; +// import 'package:quiz_app/feature/register/controller/register_controller.dart'; + +// // Mock class for AuthService +// class MockAuthService extends Mock implements AuthService {} + +// void main() { +// late RegisterController registerController; +// late MockAuthService mockAuthService; + +// setUp(() { +// mockAuthService = MockAuthService(); +// registerController = RegisterController(mockAuthService); +// }); + +// test('Initial state should have password and confirm password hidden', () { +// expect(registerController.isPasswordHidden.value, true); +// expect(registerController.isConfirmPasswordHidden.value, true); +// }); + +// test('Toggle password visibility works correctly', () { +// final initial = registerController.isPasswordHidden.value; +// registerController.togglePasswordVisibility(); +// expect(registerController.isPasswordHidden.value, !initial); +// }); + +// test('Toggle confirm password visibility works correctly', () { +// final initial = registerController.isConfirmPasswordHidden.value; +// registerController.toggleConfirmPasswordVisibility(); +// expect(registerController.isConfirmPasswordHidden.value, !initial); +// }); + +// test('Invalid email format should show error', () async { +// registerController.emailController.text = 'invalid-email'; +// registerController.nameController.text = 'John Doe'; +// registerController.bDateController.text = '12-12-2000'; +// registerController.passwordController.text = 'password123'; +// registerController.confirmPasswordController.text = 'password123'; + +// await registerController.onRegister(); + + + +// // Verify no call to register method due to invalid email +// verifyNever(() => mockAuthService.register(any())); +// }); + +// // test('Invalid date format should show error', () async { +// // registerController.emailController.text = 'john@example.com'; +// // registerController.nameController.text = 'John Doe'; +// // registerController.bDateController.text = '12/12/2000'; // Invalid format +// // registerController.passwordController.text = 'password123'; +// // registerController.confirmPasswordController.text = 'password123'; + +// // await registerController.onRegister(); + +// // verifyNever(mockAuthService.register(any)); +// // }); + +// // test('Passwords do not match should show error', () async { +// // registerController.emailController.text = 'john@example.com'; +// // registerController.nameController.text = 'John Doe'; +// // registerController.bDateController.text = '12-12-2000'; +// // registerController.passwordController.text = 'password123'; +// // registerController.confirmPasswordController.text = 'password456'; + +// // await registerController.onRegister(); + +// // verifyNever(mockAuthService.register(any)); +// // }); + +// // test('Phone number invalid length should show error', () async { +// // registerController.emailController.text = 'john@example.com'; +// // registerController.nameController.text = 'John Doe'; +// // registerController.bDateController.text = '12-12-2000'; +// // registerController.passwordController.text = 'password123'; +// // registerController.confirmPasswordController.text = 'password123'; +// // registerController.phoneController.text = '123'; // Too short + +// // await registerController.onRegister(); + +// // verifyNever(mockAuthService.register(any)); +// // }); + +// // test('Successful registration calls AuthService and navigates back', () async { +// // registerController.emailController.text = 'john@example.com'; +// // registerController.nameController.text = 'John Doe'; +// // registerController.bDateController.text = '12-12-2000'; +// // registerController.passwordController.text = 'password123'; +// // registerController.confirmPasswordController.text = 'password123'; +// // registerController.phoneController.text = '081234567890'; + +// // when(mockAuthService.register(any)).thenAnswer((_) async => {}); + +// // await registerController.onRegister(); + +// // verify(mockAuthService.register(any)).called(1); +// // }); + +// // test('_isValidDateFormat should return correct results', () { +// // expect(registerController._isValidDateFormat('12-12-2000'), true); +// // expect(registerController._isValidDateFormat('31-02-2000'), true); // Format correct, logic not checked +// // expect(registerController._isValidDateFormat('12/12/2000'), false); +// // expect(registerController._isValidDateFormat('2000-12-12'), false); +// // }); + +// // test('_isValidEmail should return correct results', () { +// // expect(registerController._isValidEmail('test@example.com'), true); +// // expect(registerController._isValidEmail('test.example.com'), false); +// // expect(registerController._isValidEmail('test@com'), false); +// // }); +// }