diff --git a/lib/core/di/locator.dart b/lib/core/di/locator.dart index f0508ba..15527e8 100644 --- a/lib/core/di/locator.dart +++ b/lib/core/di/locator.dart @@ -1,3 +1,4 @@ +import 'package:aranduapp/ui/edit_password/di/di.dart'; import 'package:aranduapp/ui/edit_profile/di/di.dart'; import 'package:aranduapp/ui/login/di/di.dart'; import 'package:aranduapp/ui/register_account/di/di.dart'; @@ -9,4 +10,5 @@ void setupLocator() { setupLoginDI(); setupRegisterDI(); setupEditProfileDI(); + setupPasswordDI(); } diff --git a/lib/ui/edit_password/di/di.dart b/lib/ui/edit_password/di/di.dart new file mode 100644 index 0000000..945389c --- /dev/null +++ b/lib/ui/edit_password/di/di.dart @@ -0,0 +1,9 @@ +import 'package:aranduapp/ui/edit_password/service/edit_password_service.dart'; +import 'package:aranduapp/ui/edit_password/viewmodel/edit_password_viewmodel.dart'; +import 'package:get_it/get_it.dart'; + +void setupPasswordDI(){ + + GetIt.instance.registerFactory(() => EditPasswordViewModel()); + GetIt.instance.registerSingleton(() => EditPasswordService()); +} diff --git a/lib/ui/edit_password/service/edit_password_service.dart b/lib/ui/edit_password/service/edit_password_service.dart index 9cd8145..6d940d3 100644 --- a/lib/ui/edit_password/service/edit_password_service.dart +++ b/lib/ui/edit_password/service/edit_password_service.dart @@ -3,7 +3,7 @@ import 'package:aranduapp/ui/edit_password/model/edit_password_request.dart'; class EditPasswordService { - static Future edit(EditPasswordRequest editPasswordRequest) async { + Future edit(EditPasswordRequest editPasswordRequest) async { await BaseApi.getInstance(auth: true) .put(path: '/auth/change-password', data: editPasswordRequest.toJson()); } diff --git a/lib/ui/edit_password/view/edit_password_view.dart b/lib/ui/edit_password/view/edit_password_view.dart index 1351f8c..95ba203 100644 --- a/lib/ui/edit_password/view/edit_password_view.dart +++ b/lib/ui/edit_password/view/edit_password_view.dart @@ -1,6 +1,8 @@ +import 'package:aranduapp/ui/edit_password/model/edit_password_request.dart'; import 'package:aranduapp/ui/edit_password/viewmodel/edit_password_viewmodel.dart'; import 'package:aranduapp/ui/shared/command_button.dart'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:provider/provider.dart'; import 'package:aranduapp/ui/shared/text_password.dart'; @@ -10,15 +12,19 @@ class EditPassword extends StatelessWidget { @override Widget build(BuildContext context) { - return ChangeNotifierProvider( - create: (context) => EditPasswordViewModel(), - child: const EditPasswordScreen(), + return ChangeNotifierProvider.value( + value: GetIt.instance(), + child: EditPasswordScreen(), ); } } class EditPasswordScreen extends StatelessWidget { - const EditPasswordScreen({super.key}); + final GlobalKey formKey = GlobalKey(); + final TextEditingController newPasswordController = TextEditingController(); + final TextEditingController oldPasswordController = TextEditingController(); + + EditPasswordScreen({super.key}); @override Widget build(BuildContext context) { @@ -63,16 +69,18 @@ class EditPasswordScreen extends StatelessWidget { Provider.of(context); return Form( - key: viewModel.formKey, + key: formKey, child: Column(children: [ TextPassWord( + key: const Key('old_password') , label: "Senha Antiga", padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), - controller: viewModel.oldPasswordController), + controller: oldPasswordController), TextPassWord( + key: const Key('new_password') , label: "Senha Nova", padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), - controller: viewModel.newPasswordController), + controller: newPasswordController), const SizedBox(height: 80), _button(context, viewModel) ]), @@ -81,7 +89,13 @@ class EditPasswordScreen extends StatelessWidget { Widget _button(BuildContext context, EditPasswordViewModel viewModel) { return CommandButton( - tap: viewModel.editCommand.execute , + tap: () { + if (formKey.currentState!.validate()) { + viewModel.editCommand.execute(EditPasswordRequest( + oldPassword: oldPasswordController.text, + newPassword: newPasswordController.text)); + } + }, command: viewModel.editCommand, nameButton: "Enviar", onErrorCallback: (e) { diff --git a/lib/ui/edit_password/viewmodel/edit_password_viewmodel.dart b/lib/ui/edit_password/viewmodel/edit_password_viewmodel.dart index b9a80a5..7317e31 100644 --- a/lib/ui/edit_password/viewmodel/edit_password_viewmodel.dart +++ b/lib/ui/edit_password/viewmodel/edit_password_viewmodel.dart @@ -3,32 +3,17 @@ import 'package:aranduapp/ui/edit_password/model/edit_password_request.dart'; import 'package:aranduapp/ui/edit_password/service/edit_password_service.dart'; import 'package:async/async.dart'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; class EditPasswordViewModel extends ChangeNotifier { - final GlobalKey formKey; - final TextEditingController newPasswordController; - final TextEditingController oldPasswordController; + late Command1 editCommand; - late Command0 editCommand; - - EditPasswordViewModel() - : formKey = GlobalKey(), - newPasswordController = TextEditingController(), - oldPasswordController = TextEditingController() { - editCommand = Command0(editPassword); + EditPasswordViewModel() { + editCommand = Command1(editPassword); } - Future> editPassword() async { - if (!formKey.currentState!.validate()) { - return Result.error(Exception('Valores inválidos')); - } - - EditPasswordRequest request = EditPasswordRequest( - oldPassword: oldPasswordController.text, - newPassword: newPasswordController.text); - - await EditPasswordService.edit(request); - + Future> editPassword(EditPasswordRequest request) async { + await GetIt.instance().edit(request); return Result.value(null); } } diff --git a/test/ui/edit_password/view/edit_password_view_test.dart b/test/ui/edit_password/view/edit_password_view_test.dart new file mode 100644 index 0000000..33bcae2 --- /dev/null +++ b/test/ui/edit_password/view/edit_password_view_test.dart @@ -0,0 +1,84 @@ +import 'package:aranduapp/core/state/command.dart'; +import 'package:aranduapp/ui/edit_password/model/edit_password_request.dart'; +import 'package:aranduapp/ui/edit_password/view/edit_password_view.dart'; +import 'package:aranduapp/ui/edit_password/viewmodel/edit_password_viewmodel.dart'; +import 'package:aranduapp/ui/shared/command_button.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'edit_password_view_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec(), MockSpec()]) +void main() { + late MockEditPasswordViewModel mockEditPasswordViewModel; + late MockCommand1 mockEditPasswordCommand1; + + setUp(() async { + mockEditPasswordViewModel = MockEditPasswordViewModel(); + + mockEditPasswordCommand1 = MockCommand1(); + + when(mockEditPasswordViewModel.editCommand) + .thenReturn(mockEditPasswordCommand1); + + when(mockEditPasswordCommand1.running).thenReturn(false); + when(mockEditPasswordCommand1.isError).thenReturn(false); + when(mockEditPasswordCommand1.isOk).thenReturn(false); + + await GetIt.instance.reset(); + GetIt.I.registerLazySingleton( + () => mockEditPasswordViewModel); + }); + + Widget createScreen() { + return const MaterialApp( + home: EditPassword(), + ); + } + + testWidgets('edit password screen displays', (WidgetTester tester) async { + await tester.pumpWidget(createScreen()); + await tester.pumpAndSettle(); + + expect(find.byType(AppBar), findsOneWidget); + expect(find.byKey(const ValueKey('old_password')), findsOneWidget); + expect(find.byKey(const ValueKey('new_password')), findsOneWidget); + expect(find.byType(CommandButton), findsOneWidget); + }); + + testWidgets('edit password right values', (WidgetTester tester) async { + await tester.pumpWidget(createScreen()); + + const oldPassword = 'test@example.com'; + const newPassword = 'password123'; + + await tester.enterText(find.byKey(const Key('old_password')), oldPassword); + await tester.enterText(find.byKey(const Key('new_password')), newPassword); + + await tester.tap(find.byKey(const Key('elevated_button_key'))); + await tester.pumpAndSettle(); + + verify(mockEditPasswordCommand1.execute(argThat( + predicate((req) => + req.oldPassword == oldPassword && + req.newPassword == newPassword)))).called(1); + }); + + testWidgets('edit password wrong values', (WidgetTester tester) async { + await tester.pumpWidget(createScreen()); + + const oldPassword = 'test'; + const newPassword = 'password123'; + + await tester.enterText(find.byKey(const Key('old_password')), oldPassword); + await tester.enterText(find.byKey(const Key('new_password')), newPassword); + + await tester.tap(find.byKey(const Key('elevated_button_key'))); + await tester.pumpAndSettle(); + + verifyNever(mockEditPasswordCommand1.execute(argThat(isNotNull))); + }); +} diff --git a/test/ui/edit_password/view/edit_password_view_test.mocks.dart b/test/ui/edit_password/view/edit_password_view_test.mocks.dart new file mode 100644 index 0000000..c7d4b56 --- /dev/null +++ b/test/ui/edit_password/view/edit_password_view_test.mocks.dart @@ -0,0 +1,253 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in aranduapp/test/ui/edit_password/view/edit_password_view_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i6; + +import 'package:aranduapp/core/state/command.dart' as _i2; +import 'package:aranduapp/ui/edit_password/model/edit_password_request.dart' + as _i5; +import 'package:aranduapp/ui/edit_password/viewmodel/edit_password_viewmodel.dart' + as _i4; +import 'package:async/async.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeCommand1_0 extends _i1.SmartFake + implements _i2.Command1 { + _FakeCommand1_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeResult_1 extends _i1.SmartFake implements _i3.Result { + _FakeResult_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [EditPasswordViewModel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockEditPasswordViewModel extends _i1.Mock + implements _i4.EditPasswordViewModel { + @override + _i2.Command1 get editCommand => + (super.noSuchMethod( + Invocation.getter(#editCommand), + returnValue: _FakeCommand1_0( + this, + Invocation.getter(#editCommand), + ), + returnValueForMissingStub: + _FakeCommand1_0( + this, + Invocation.getter(#editCommand), + ), + ) as _i2.Command1); + + @override + set editCommand(_i2.Command1? _editCommand) => + super.noSuchMethod( + Invocation.setter( + #editCommand, + _editCommand, + ), + returnValueForMissingStub: null, + ); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i6.Future<_i3.Result> editPassword(_i5.EditPasswordRequest? request) => + (super.noSuchMethod( + Invocation.method( + #editPassword, + [request], + ), + returnValue: _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #editPassword, + [request], + ), + )), + returnValueForMissingStub: + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #editPassword, + [request], + ), + )), + ) as _i6.Future<_i3.Result>); + + @override + void addListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [Command1]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommand1 extends _i1.Mock implements _i2.Command1 { + @override + _i6.Future<_i3.Result> Function(A) get action => (super.noSuchMethod( + Invocation.getter(#action), + returnValue: (A __p0) => + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.getter(#action), + )), + returnValueForMissingStub: (A __p0) => + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.getter(#action), + )), + ) as _i6.Future<_i3.Result> Function(A)); + + @override + bool get isError => (super.noSuchMethod( + Invocation.getter(#isError), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get isOk => (super.noSuchMethod( + Invocation.getter(#isOk), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get running => (super.noSuchMethod( + Invocation.getter(#running), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i6.Future<_i3.Result> execute(A? arg1) => (super.noSuchMethod( + Invocation.method( + #execute, + [arg1], + ), + returnValue: _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #execute, + [arg1], + ), + )), + returnValueForMissingStub: + _i6.Future<_i3.Result>.value(_FakeResult_1( + this, + Invocation.method( + #execute, + [arg1], + ), + )), + ) as _i6.Future<_i3.Result>); + + @override + void addListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(dynamic listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +}