From 6235ca342f13baca35d83db0bc0a08b6a032cf24 Mon Sep 17 00:00:00 2001 From: Jeffrey Kengne Date: Mon, 27 Mar 2023 03:46:51 +0100 Subject: [PATCH] added more helper methods for widget and integration tests --- integration_test/app_test.dart | 12 ++ integration_test/bottom_nav_bar_test.dart | 85 ++++++++++ lib/app/view/app.dart | 3 +- .../home_view_bloc/home_view_bloc.dart | 34 ++-- lib/home/view/dashboard.dart | 1 - lib/home/view/widgets/bottom_nav_bar.dart | 5 + lib/injection.config.dart | 111 ++++++------- lib/router/app_route.gr.dart | 145 ++++++++++------- lib/sign_in/view/sign_in_page.dart | 23 ++- pubspec.lock | 153 +++++++++++++++++- pubspec.yaml | 2 + test/app/view/app_test.dart | 21 --- .../application/facebook_auth_bloc_test.dart | 40 +---- .../application/bloc/auth_bloc_test.dart | 14 +- .../application/google_sign_in_bloc_test.dart | 32 +--- .../email_password_bloc_test.dart | 26 +-- test/helpers/arrange_auth_repository.dart | 91 +++++++++++ test/helpers/arrange_cached.dart | 11 ++ test/helpers/arrange_facebook_repository.dart | 50 ++++++ test/helpers/arrange_google_repository.dart | 50 ++++++ test/helpers/helpers.dart | 4 + test/helpers/mocks.dart | 3 + test/helpers/pump_app.dart | 74 ++++++++- test/helpers/test_data.dart | 9 +- .../home_view_bloc/home_view_bloc_test.dart | 65 ++++++++ test/sign_in/view/sign_in_screen_test.dart | 35 ++++ test/widget_test.dart | 17 -- 27 files changed, 823 insertions(+), 293 deletions(-) create mode 100644 integration_test/app_test.dart create mode 100644 integration_test/bottom_nav_bar_test.dart delete mode 100644 test/app/view/app_test.dart create mode 100644 test/helpers/arrange_auth_repository.dart create mode 100644 test/helpers/arrange_cached.dart create mode 100644 test/helpers/arrange_facebook_repository.dart create mode 100644 test/helpers/arrange_google_repository.dart create mode 100644 test/home/application/home_view_bloc/home_view_bloc_test.dart create mode 100644 test/sign_in/view/sign_in_screen_test.dart delete mode 100644 test/widget_test.dart diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart new file mode 100644 index 0000000..f303140 --- /dev/null +++ b/integration_test/app_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import 'bottom_nav_bar_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('Development App', () { + bottomNavBarTest(); + }); +} diff --git a/integration_test/bottom_nav_bar_test.dart b/integration_test/bottom_nav_bar_test.dart new file mode 100644 index 0000000..e917694 --- /dev/null +++ b/integration_test/bottom_nav_bar_test.dart @@ -0,0 +1,85 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:fpb/core/domain/user.dart'; +import 'package:fpb/core/shared/helpers/value_injector.dart'; +import 'package:fpb/home/home_screen.dart'; +import 'package:fpb/home/view/budget_screen.dart'; +import 'package:fpb/home/view/dashboard.dart'; +import 'package:fpb/home/view/home_container.dart'; +import 'package:fpb/home/view/widgets/bottom_nav_bar.dart'; +import 'package:fpb/savings/view/savings_page.dart'; + +import '../test/helpers/helpers.dart'; + +void bottomNavBarTest() { + late MockCached cached; + late MockIFacebookRepositoryFacade mockIFacebookRepositoryFacade; + late MockIGoogleRepositoryFacade mockIGoogleRepositoryFacade; + late MockIAuthFacade mockIAuthFacade; + + setUp(() { + cached = MockCached(); + mockIAuthFacade = MockIAuthFacade(); + mockIFacebookRepositoryFacade = MockIFacebookRepositoryFacade(); + mockIGoogleRepositoryFacade = MockIGoogleRepositoryFacade(); + }); + + testWidgets('renders home bottom nav bar buttons', (tester) async { + arrangeAuthRepositoryReturnsStreamWithUser(mockIAuthFacade); + arrangeAuthRepositoryReturnsCurrentUser(mockIAuthFacade); + arrangeCachedReturnsLastView(cached, 0); + + await tester.pumpApp( + ValueInjector( + value: testUser, + child: HomeBody( + user: testUser, + ), + ), + mockCachedForHomeBloc: cached, + mockIAuthFacadeForAuthBloc: mockIAuthFacade, + ); + + await tester.pumpAndSettle(); + + // await tester.pump(); + + expect(find.byType(HomeContainer), findsOneWidget); + expect(find.byType(BottomNavBar), findsOneWidget); + expect(find.byType(DashBoard), findsOneWidget); + + await tester.tap( + find.byKey( + Key("BNB-savings-page-button"), + ), + ); + + await tester.pumpAndSettle(); + + expect(find.byType(SavingsPage), findsOneWidget); + + await tester.tap( + find.byKey( + Key("BNB-budget-page-button"), + ), + ); + + await tester.pumpAndSettle(); + + expect(find.byType(BudgetScreen), findsOneWidget); + + // await tester.tap( + // find.byKey( + // Key("BNB-userSearch-page-button"), + // ), + // ); + + // await tester.pumpAndSettle(); + + // expect(find.byType(UserSearchScreen), findsOneWidget); + + await Future.delayed( + const Duration(seconds: 3), + ); + }); +} diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index e4938d9..ed5cbbe 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:fpb/core/presentation/theming/themes/theme.dart'; - import 'package:fpb/authentication_mock_without_backend/application/bloc/authentication_bloc.dart'; import 'package:fpb/authentication_with_firebase/application/bloc/auth_bloc.dart'; import 'package:fpb/core/application/internet_and_time_bloc/internet_and_time_bloc.dart'; +import 'package:fpb/core/presentation/theming/themes/theme.dart'; import 'package:fpb/injection.dart'; import 'package:fpb/l10n/l10n.dart'; import 'package:fpb/router/app_route.gr.dart'; diff --git a/lib/home/application/home_view_bloc/home_view_bloc.dart b/lib/home/application/home_view_bloc/home_view_bloc.dart index c1ca297..5bbdc8e 100644 --- a/lib/home/application/home_view_bloc/home_view_bloc.dart +++ b/lib/home/application/home_view_bloc/home_view_bloc.dart @@ -1,18 +1,18 @@ // ignore_for_file: inference_failure_on_untyped_parameter import 'package:bloc/bloc.dart'; -import 'package:fpb/core/settings/app_settings_helper.dart'; +import 'package:fpb/core/settings/cached.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:injectable/injectable.dart'; +part 'home_view_bloc.freezed.dart'; part 'home_view_event.dart'; part 'home_view_state.dart'; -part 'home_view_bloc.freezed.dart'; @injectable class HomeViewBloc extends Bloc { - final AppSettingsHelper _appSettingsHelper; - HomeViewBloc(this._appSettingsHelper) : super(HomeViewState.home()) { + final Cached _cached; + HomeViewBloc(this._cached) : super(HomeViewState.home()) { on<_HomeE>(_home); on<_SavingsE>(_savings); on<_QuickCashE>(_quickCash); @@ -21,33 +21,41 @@ class HomeViewBloc extends Bloc { on<_SearchE>(_search); } + int? lastView; + void _search(event, Emitter emit) { emit(HomeViewState.search()); - _appSettingsHelper.appCache.setLastView = 4; + lastView = 4; } void _home(event, Emitter emit) { emit(HomeViewState.home()); - _appSettingsHelper.appCache.setLastView = 0; + lastView = 0; } void _savings(event, Emitter emit) { emit(HomeViewState.savings()); - _appSettingsHelper.appCache.setLastView = 1; + lastView = 1; } void _quickCash(event, Emitter emit) { emit(HomeViewState.quickCash()); - _appSettingsHelper.appCache.setLastView = 2; + lastView = 2; } void _budget(event, Emitter emit) { emit(HomeViewState.budget()); - _appSettingsHelper.appCache.setLastView = 3; + lastView = 3; } void _lastState(event, Emitter emit) { - final lastState = _appSettingsHelper.appCache.getLastView; + late int lastState; + if (lastView != null) { + lastState = lastView!; + } else { + lastState = _cached.getLastView; + } + switch (lastState) { case 0: _home(event, emit); @@ -69,4 +77,10 @@ class HomeViewBloc extends Bloc { break; } } + + @override + Future close() { + if (lastView != null) _cached.setLastView = lastView!; + return super.close(); + } } diff --git a/lib/home/view/dashboard.dart b/lib/home/view/dashboard.dart index 96c0ab0..b9d5bba 100644 --- a/lib/home/view/dashboard.dart +++ b/lib/home/view/dashboard.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:fpb/authentication_with_firebase/application/bloc/auth_bloc.dart'; import 'package:fpb/core/domain/user.dart'; import 'package:fpb/core/presentation/animations/slide_up_route_transition.dart'; diff --git a/lib/home/view/widgets/bottom_nav_bar.dart b/lib/home/view/widgets/bottom_nav_bar.dart index 5fe8b44..0d31b69 100644 --- a/lib/home/view/widgets/bottom_nav_bar.dart +++ b/lib/home/view/widgets/bottom_nav_bar.dart @@ -25,6 +25,7 @@ class _BottomNavBarState extends State { IconButton( enableFeedback: false, padding: EdgeInsets.zero, + key: Key("BNB-home-page-button"), onPressed: () { setState(() { pageIndex = 0; @@ -56,6 +57,7 @@ class _BottomNavBarState extends State { IconButton( enableFeedback: false, padding: EdgeInsets.zero, + key: Key("BNB-savings-page-button"), onPressed: () { setState(() { pageIndex = 1; @@ -86,6 +88,7 @@ class _BottomNavBarState extends State { IconButton( enableFeedback: false, padding: EdgeInsets.zero, + key: Key("BNB-quickCash-page-button"), onPressed: () { setState(() { pageIndex = 2; @@ -116,6 +119,7 @@ class _BottomNavBarState extends State { IconButton( enableFeedback: false, padding: EdgeInsets.zero, + key: Key("BNB-budget-page-button"), onPressed: () { setState(() { pageIndex = 3; @@ -147,6 +151,7 @@ class _BottomNavBarState extends State { IconButton( enableFeedback: false, padding: EdgeInsets.zero, + key: Key("BNB-userSearch-page-button"), onPressed: () { setState(() { pageIndex = 4; diff --git a/lib/injection.config.dart b/lib/injection.config.dart index 702ab0c..36981d4 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -13,55 +13,49 @@ import 'package:firebase_auth/firebase_auth.dart' as _i7; import 'package:firebase_core/firebase_core.dart' as _i6; import 'package:flutter_facebook_auth/flutter_facebook_auth.dart' as _i5; import 'package:fpb/authentication_mock_without_backend/application/bloc/authentication_bloc.dart' - as _i19; + as _i17; import 'package:fpb/authentication_mock_without_backend/infrastructure/authentication_mock_module_injection.dart' - as _i33; + as _i30; import 'package:fpb/authentication_with_facebook/application/facebook_auth_bloc.dart' - as _i21; + as _i19; import 'package:fpb/authentication_with_facebook/domain/i_facebook_repository_facade.dart' as _i10; import 'package:fpb/authentication_with_facebook/infrastructure/facebook_auth_repository.dart' as _i11; import 'package:fpb/authentication_with_facebook/infrastructure/facebook_authentication_injectable_module.dart' - as _i35; + as _i32; import 'package:fpb/authentication_with_firebase/application/bloc/auth_bloc.dart' - as _i27; + as _i25; import 'package:fpb/authentication_with_firebase/domain/i_auth_facade.dart' - as _i24; + as _i22; import 'package:fpb/authentication_with_firebase/infrastructure/firebase_auth_facade_impl.dart' - as _i25; + as _i23; import 'package:fpb/authentication_with_firebase/infrastructure/firebase_auth_injectable_module.dart' - as _i34; + as _i31; import 'package:fpb/authentication_with_google/application/google_auth_bloc/google_sign_in_bloc.dart' - as _i23; + as _i20; import 'package:fpb/authentication_with_google/domain/i_google_repository_facade.dart' - as _i14; + as _i12; import 'package:fpb/authentication_with_google/infrastructure/google_authentication_injectable_module.dart' - as _i31; + as _i28; import 'package:fpb/authentication_with_google/infrastructure/google_authentication_repository.dart' - as _i15; + as _i13; import 'package:fpb/core/application/email_password_bloc/email_password_bloc.dart' - as _i28; + as _i26; import 'package:fpb/core/application/internet_and_time_bloc/internet_and_time_bloc.dart' - as _i30; -import 'package:fpb/core/infrastructure/core_injectable_module.dart' as _i32; -import 'package:fpb/core/settings/app_settings_helper.dart' as _i26; -import 'package:fpb/core/settings/cached.dart' as _i20; -import 'package:fpb/forgot_password_flow/application/bloc/forgot_password_bloc.dart' - as _i22; -import 'package:fpb/forgot_password_flow/domain/i_forgot_password_facade.dart' - as _i12; -import 'package:fpb/forgot_password_flow/infrastructure/forgot_password_repository.dart' - as _i13; + as _i27; +import 'package:fpb/core/infrastructure/core_injectable_module.dart' as _i29; +import 'package:fpb/core/settings/app_settings_helper.dart' as _i24; +import 'package:fpb/core/settings/cached.dart' as _i18; import 'package:fpb/home/application/home_view_bloc/home_view_bloc.dart' - as _i29; + as _i21; import 'package:get_it/get_it.dart' as _i1; import 'package:google_sign_in/google_sign_in.dart' as _i9; import 'package:injectable/injectable.dart' as _i2; -import 'package:ntp/ntp.dart' as _i16; -import 'package:shared_preferences/shared_preferences.dart' as _i17; +import 'package:ntp/ntp.dart' as _i14; +import 'package:shared_preferences/shared_preferences.dart' as _i15; import 'package:user_repository/user_repository.dart' - as _i18; // ignore_for_file: unnecessary_lambdas + as _i16; // ignore_for_file: unnecessary_lambdas // ignore_for_file: lines_longer_than_80_chars extension GetItInjectableX on _i1.GetIt { @@ -104,66 +98,55 @@ extension GetItInjectableX on _i1.GetIt { gh<_i5.FacebookAuth>(), gh<_i7.FirebaseAuth>(), )); - gh.lazySingleton<_i12.IForgotPasswordRepositoryFacade>( - () => _i13.ForgotPasswordRepository( - gh<_i7.FirebaseAuth>(), - gh(), - )); - gh.lazySingleton<_i14.IGoogleRepositoryFacade>( - () => _i15.GoogleAuthenticationRepository( + gh.lazySingleton<_i12.IGoogleRepositoryFacade>( + () => _i13.GoogleAuthenticationRepository( gh<_i9.GoogleSignIn>(), gh<_i7.FirebaseAuth>(), )); - gh.lazySingleton<_i16.NTP>(() => coreInjectableModule.ntp); - await gh.factoryAsync<_i17.SharedPreferences>( + gh.lazySingleton<_i14.NTP>(() => coreInjectableModule.ntp); + await gh.factoryAsync<_i15.SharedPreferences>( () => firebaseAuthInjectableModule.sharePreferences, preResolve: true, ); - gh.singleton<_i18.UserRepository>( + gh.singleton<_i16.UserRepository>( authenticationMockModuleInjection.userRepository); - gh.factory<_i19.AuthenticationBloc>(() => _i19.AuthenticationBloc( + gh.factory<_i17.AuthenticationBloc>(() => _i17.AuthenticationBloc( authenticationRepository: gh<_i3.AuthenticationRepository>(), - userRepository: gh<_i18.UserRepository>(), + userRepository: gh<_i16.UserRepository>(), )); - gh.singleton<_i20.Cached>(_i20.Cached(gh<_i17.SharedPreferences>())); - gh.factory<_i21.FacebookAuthBloc>(() => _i21.FacebookAuthBloc( + gh.singleton<_i18.Cached>(_i18.Cached(gh<_i15.SharedPreferences>())); + gh.factory<_i19.FacebookAuthBloc>(() => _i19.FacebookAuthBloc( authenticationRepository: gh<_i10.IFacebookRepositoryFacade>())); - gh.factory<_i22.ForgotPasswordBloc>(() => _i22.ForgotPasswordBloc( - gh<_i22.ForgotPasswordState>(), - forgotPasswordRepositoryFacade: - gh<_i12.IForgotPasswordRepositoryFacade>(), - )); - gh.factory<_i23.GoogleSignInBloc>(() => _i23.GoogleSignInBloc( - authenticationRepository: gh<_i14.IGoogleRepositoryFacade>())); - gh.lazySingleton<_i24.IAuthFacade>(() => _i25.FirebaseAuthFacade( + gh.factory<_i20.GoogleSignInBloc>(() => _i20.GoogleSignInBloc( + authenticationRepository: gh<_i12.IGoogleRepositoryFacade>())); + gh.factory<_i21.HomeViewBloc>(() => _i21.HomeViewBloc(gh<_i18.Cached>())); + gh.lazySingleton<_i22.IAuthFacade>(() => _i23.FirebaseAuthFacade( gh<_i7.FirebaseAuth>(), - gh<_i20.Cached>(), + gh<_i18.Cached>(), )); - gh.lazySingleton<_i26.AppSettingsHelper>(() => _i26.AppSettingsHelper( - gh<_i20.Cached>(), + gh.lazySingleton<_i24.AppSettingsHelper>(() => _i24.AppSettingsHelper( + gh<_i18.Cached>(), gh<_i4.Connectivity>(), )); - gh.factory<_i27.AuthBloc>(() => _i27.AuthBloc(gh<_i24.IAuthFacade>())); - gh.singleton<_i28.EmailPasswordBloc>(_i28.EmailPasswordBloc( - authenticationRepository: gh<_i24.IAuthFacade>())); - gh.factory<_i29.HomeViewBloc>( - () => _i29.HomeViewBloc(gh<_i26.AppSettingsHelper>())); - gh.factory<_i30.InternetAndTimeBloc>( - () => _i30.InternetAndTimeBloc(gh<_i26.AppSettingsHelper>())); + gh.factory<_i25.AuthBloc>(() => _i25.AuthBloc(gh<_i22.IAuthFacade>())); + gh.singleton<_i26.EmailPasswordBloc>(_i26.EmailPasswordBloc( + authenticationRepository: gh<_i22.IAuthFacade>())); + gh.factory<_i27.InternetAndTimeBloc>( + () => _i27.InternetAndTimeBloc(gh<_i24.AppSettingsHelper>())); return this; } } class _$GoogleAuthenticationInjectableModule - extends _i31.GoogleAuthenticationInjectableModule {} + extends _i28.GoogleAuthenticationInjectableModule {} -class _$CoreInjectableModule extends _i32.CoreInjectableModule {} +class _$CoreInjectableModule extends _i29.CoreInjectableModule {} class _$AuthenticationMockModuleInjection - extends _i33.AuthenticationMockModuleInjection {} + extends _i30.AuthenticationMockModuleInjection {} class _$FirebaseAuthInjectableModule - extends _i34.FirebaseAuthInjectableModule {} + extends _i31.FirebaseAuthInjectableModule {} class _$FacebookAuthenticationInjectableModule - extends _i35.FacebookAuthenticationInjectableModule {} + extends _i32.FacebookAuthenticationInjectableModule {} diff --git a/lib/router/app_route.gr.dart b/lib/router/app_route.gr.dart index 7a5a657..c50a83f 100644 --- a/lib/router/app_route.gr.dart +++ b/lib/router/app_route.gr.dart @@ -11,11 +11,12 @@ // ignore_for_file: type=lint // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i13; -import 'package:flutter/material.dart' as _i14; +import 'package:auto_route/auto_route.dart' as _i15; +import 'package:flutter/material.dart' as _i16; -import '../contact_us/contact_us_page.dart' as _i11; -import '../core/domain/user.dart' as _i15; +import '../contact_us/view/contact_us_screen.dart' as _i11; +import '../contact_us/view/contact_us_success_screen.dart' as _i12; +import '../core/domain/user.dart' as _i17; import '../email_confirmation/email_confirmation.dart' as _i4; import '../home/view/home_screen.dart' as _i7; import '../latest_activities/view/latest_activities_screen.dart' as _i8; @@ -23,57 +24,58 @@ import '../onboarding/view/onboarding_screens.dart' as _i10; import '../onboarding/view/splash_screen.dart' as _i1; import '../phone_number_confirmation/view/phone_number_confirmation.dart' as _i3; -import '../profile/view/profile_page.dart' as _i12; +import '../profile/view/profile_page.dart' as _i14; import '../qr_code_screen/view/qr_code_screen.dart' as _i9; import '../savings/save_money_with_bucket/save_money_with_bucket.dart' as _i6; +import '../savings/view/savings_page.dart' as _i13; import '../sign_in/view/sign_in_page.dart' as _i2; import '../sign_up/view/signup_page.dart' as _i5; -class AppRoute extends _i13.RootStackRouter { - AppRoute([_i14.GlobalKey<_i14.NavigatorState>? navigatorKey]) +class AppRoute extends _i15.RootStackRouter { + AppRoute([_i16.GlobalKey<_i16.NavigatorState>? navigatorKey]) : super(navigatorKey); @override - final Map pagesMap = { + final Map pagesMap = { SplashRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i1.SplashScreen(), ); }, SignInRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i2.SignInScreen(), ); }, PhoneNumberConfirmationRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i3.PhoneNumberConfirmationScreen(), ); }, EmailConfirmationRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i4.EmailConfirmationScreen(), ); }, SignUpRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i5.SignUpScreen(), ); }, SaveMoneyRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i6.SaveMoneyScreen(), ); }, HomeRouter.name: (routeData) { final args = routeData.argsAs(); - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: _i7.HomeScreen( key: args.key, @@ -82,13 +84,13 @@ class AppRoute extends _i13.RootStackRouter { ); }, LatestActivitiesPage.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i8.LatestActivitiesPage(), ); }, QrCodeRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i9.QrCodeScreen(), ); @@ -96,7 +98,7 @@ class AppRoute extends _i13.RootStackRouter { OnboardingRoute.name: (routeData) { final args = routeData.argsAs( orElse: () => const OnboardingRouteArgs()); - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: _i10.OnboardingScreen( onGetStartedPressed: args.onGetStartedPressed, @@ -105,79 +107,90 @@ class AppRoute extends _i13.RootStackRouter { ); }, ContactUsRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, child: const _i11.ContactUsScreen(), ); }, ContactUsSuccessRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, - child: const _i11.ContactUsSuccessScreen(), + child: const _i12.ContactUsSuccessScreen(), + ); + }, + SavingsPage.name: (routeData) { + return _i15.MaterialPageX( + routeData: routeData, + child: const _i13.SavingsPage(), ); }, ProfileRoute.name: (routeData) { - return _i13.MaterialPageX( + return _i15.MaterialPageX( routeData: routeData, - child: const _i12.ProfileScreen(), + child: const _i14.ProfileScreen(), ); }, }; @override - List<_i13.RouteConfig> get routes => [ - _i13.RouteConfig( + List<_i15.RouteConfig> get routes => [ + _i15.RouteConfig( SplashRoute.name, path: '/', ), - _i13.RouteConfig( + _i15.RouteConfig( SignInRoute.name, path: '/sign-in-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( PhoneNumberConfirmationRoute.name, path: '/phone-number-confirmation-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( EmailConfirmationRoute.name, path: '/email-confirmation-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( SignUpRoute.name, path: '/sign-up-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( SaveMoneyRoute.name, path: '/save-money-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( HomeRouter.name, path: '/home-screen', children: [ - _i13.RouteConfig( + _i15.RouteConfig( + SavingsPage.name, + path: 'savings', + parent: HomeRouter.name, + ), + _i15.RouteConfig( ProfileRoute.name, path: 'profile', parent: HomeRouter.name, - ) + ), ], ), - _i13.RouteConfig( + _i15.RouteConfig( LatestActivitiesPage.name, path: 'latestActivities', ), - _i13.RouteConfig( + _i15.RouteConfig( QrCodeRoute.name, path: '/qr-code-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( OnboardingRoute.name, path: '/onboarding-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( ContactUsRoute.name, path: '/contact-us-screen', ), - _i13.RouteConfig( + _i15.RouteConfig( ContactUsSuccessRoute.name, path: '/contact-us-success-screen', ), @@ -186,7 +199,7 @@ class AppRoute extends _i13.RootStackRouter { /// generated route for /// [_i1.SplashScreen] -class SplashRoute extends _i13.PageRouteInfo { +class SplashRoute extends _i15.PageRouteInfo { const SplashRoute() : super( SplashRoute.name, @@ -198,7 +211,7 @@ class SplashRoute extends _i13.PageRouteInfo { /// generated route for /// [_i2.SignInScreen] -class SignInRoute extends _i13.PageRouteInfo { +class SignInRoute extends _i15.PageRouteInfo { const SignInRoute() : super( SignInRoute.name, @@ -210,7 +223,7 @@ class SignInRoute extends _i13.PageRouteInfo { /// generated route for /// [_i3.PhoneNumberConfirmationScreen] -class PhoneNumberConfirmationRoute extends _i13.PageRouteInfo { +class PhoneNumberConfirmationRoute extends _i15.PageRouteInfo { const PhoneNumberConfirmationRoute() : super( PhoneNumberConfirmationRoute.name, @@ -222,7 +235,7 @@ class PhoneNumberConfirmationRoute extends _i13.PageRouteInfo { /// generated route for /// [_i4.EmailConfirmationScreen] -class EmailConfirmationRoute extends _i13.PageRouteInfo { +class EmailConfirmationRoute extends _i15.PageRouteInfo { const EmailConfirmationRoute() : super( EmailConfirmationRoute.name, @@ -234,7 +247,7 @@ class EmailConfirmationRoute extends _i13.PageRouteInfo { /// generated route for /// [_i5.SignUpScreen] -class SignUpRoute extends _i13.PageRouteInfo { +class SignUpRoute extends _i15.PageRouteInfo { const SignUpRoute() : super( SignUpRoute.name, @@ -246,7 +259,7 @@ class SignUpRoute extends _i13.PageRouteInfo { /// generated route for /// [_i6.SaveMoneyScreen] -class SaveMoneyRoute extends _i13.PageRouteInfo { +class SaveMoneyRoute extends _i15.PageRouteInfo { const SaveMoneyRoute() : super( SaveMoneyRoute.name, @@ -258,11 +271,11 @@ class SaveMoneyRoute extends _i13.PageRouteInfo { /// generated route for /// [_i7.HomeScreen] -class HomeRouter extends _i13.PageRouteInfo { +class HomeRouter extends _i15.PageRouteInfo { HomeRouter({ - _i14.Key? key, - required _i15.User user, - List<_i13.PageRouteInfo>? children, + _i16.Key? key, + required _i17.User user, + List<_i15.PageRouteInfo>? children, }) : super( HomeRouter.name, path: '/home-screen', @@ -282,9 +295,9 @@ class HomeRouterArgs { required this.user, }); - final _i14.Key? key; + final _i16.Key? key; - final _i15.User user; + final _i17.User user; @override String toString() { @@ -294,7 +307,7 @@ class HomeRouterArgs { /// generated route for /// [_i8.LatestActivitiesPage] -class LatestActivitiesPage extends _i13.PageRouteInfo { +class LatestActivitiesPage extends _i15.PageRouteInfo { const LatestActivitiesPage() : super( LatestActivitiesPage.name, @@ -306,7 +319,7 @@ class LatestActivitiesPage extends _i13.PageRouteInfo { /// generated route for /// [_i9.QrCodeScreen] -class QrCodeRoute extends _i13.PageRouteInfo { +class QrCodeRoute extends _i15.PageRouteInfo { const QrCodeRoute() : super( QrCodeRoute.name, @@ -318,10 +331,10 @@ class QrCodeRoute extends _i13.PageRouteInfo { /// generated route for /// [_i10.OnboardingScreen] -class OnboardingRoute extends _i13.PageRouteInfo { +class OnboardingRoute extends _i15.PageRouteInfo { OnboardingRoute({ void Function()? onGetStartedPressed, - _i14.Key? key, + _i16.Key? key, }) : super( OnboardingRoute.name, path: '/onboarding-screen', @@ -342,7 +355,7 @@ class OnboardingRouteArgs { final void Function()? onGetStartedPressed; - final _i14.Key? key; + final _i16.Key? key; @override String toString() { @@ -352,7 +365,7 @@ class OnboardingRouteArgs { /// generated route for /// [_i11.ContactUsScreen] -class ContactUsRoute extends _i13.PageRouteInfo { +class ContactUsRoute extends _i15.PageRouteInfo { const ContactUsRoute() : super( ContactUsRoute.name, @@ -363,8 +376,8 @@ class ContactUsRoute extends _i13.PageRouteInfo { } /// generated route for -/// [_i11.ContactUsSuccessScreen] -class ContactUsSuccessRoute extends _i13.PageRouteInfo { +/// [_i12.ContactUsSuccessScreen] +class ContactUsSuccessRoute extends _i15.PageRouteInfo { const ContactUsSuccessRoute() : super( ContactUsSuccessRoute.name, @@ -375,8 +388,20 @@ class ContactUsSuccessRoute extends _i13.PageRouteInfo { } /// generated route for -/// [_i12.ProfileScreen] -class ProfileRoute extends _i13.PageRouteInfo { +/// [_i13.SavingsPage] +class SavingsPage extends _i15.PageRouteInfo { + const SavingsPage() + : super( + SavingsPage.name, + path: 'savings', + ); + + static const String name = 'SavingsPage'; +} + +/// generated route for +/// [_i14.ProfileScreen] +class ProfileRoute extends _i15.PageRouteInfo { const ProfileRoute() : super( ProfileRoute.name, diff --git a/lib/sign_in/view/sign_in_page.dart b/lib/sign_in/view/sign_in_page.dart index ca8c867..055b805 100644 --- a/lib/sign_in/view/sign_in_page.dart +++ b/lib/sign_in/view/sign_in_page.dart @@ -1,9 +1,7 @@ import 'package:auto_route/auto_route.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; - import 'package:fpb/assets/fpb_icons/fpb_icons_icons.dart'; import 'package:fpb/assets/fpb_svg.dart'; import 'package:fpb/authenticate_with_biometrics/application/bloc/biometric_auth_bloc.dart'; @@ -60,7 +58,6 @@ class SignInBody extends StatefulWidget { class _SignInBodyState extends State with SingleTickerProviderStateMixin { late TabController tabController; - final user = FirebaseAuth.instance.currentUser; @override void initState() { @@ -163,15 +160,17 @@ class _SignInBodyState extends State physics: const BouncingScrollPhysics(), controller: tabController, children: [ - Column( - crossAxisAlignment: - CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - EmailInput(box: cts), - PasswordInput(box: cts), - Text(l10n.signInForgotPasswordText), - ], + SingleChildScrollView( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + EmailInput(box: cts), + PasswordInput(box: cts), + Text(l10n.signInForgotPasswordText), + ], + ), ), Column( mainAxisAlignment: diff --git a/pubspec.lock b/pubspec.lock index 5f4fd57..7575968 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,6 +25,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.7.0" + archive: + dependency: transitive + description: + name: archive + sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb" + url: "https://pub.dev" + source: hosted + version: "3.3.2" args: dependency: transitive description: @@ -248,6 +256,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.6.3" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + url: "https://pub.dev" + source: hosted + version: "0.3.3+4" crypto: dependency: transitive description: @@ -413,6 +429,11 @@ packages: url: "https://pub.dev" source: hosted version: "8.1.2" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_facebook_auth: dependency: "direct main" description: @@ -556,6 +577,11 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" get_it: dependency: "direct main" description: @@ -668,6 +694,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" intl: dependency: "direct main" description: @@ -861,7 +892,7 @@ packages: source: hosted version: "1.0.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9" @@ -972,6 +1003,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + qr: + dependency: transitive + description: + name: qr + sha256: "5c4208b4dc0d55c3184d10d83ee0ded6212dc2b5e2ba17c5a0c0aab279128d21" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + qr_code_scanner: + dependency: "direct main" + description: + name: qr_code_scanner + sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd + url: "https://pub.dev" + source: hosted + version: "1.0.1" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: c5c121c54cb6dd837b9b9d57eb7bc7ec6df4aee741032060c8833a678c80b87e + url: "https://pub.dev" + source: hosted + version: "4.0.0" quiver: dependency: transitive description: @@ -988,6 +1043,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: "8c6892037b1824e2d7e8f59d54b3105932899008642e6372e5079c6939b4b625" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "82ddd4ab9260c295e6e39612d4ff00390b9a7a21f1bb1da771e2f232d80ab8a1" + url: "https://pub.dev" + source: hosted + version: "3.2.0" shared_preferences: dependency: "direct main" description: @@ -1153,6 +1224,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" term_glyph: dependency: transitive description: @@ -1201,6 +1280,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc" + url: "https://pub.dev" + source: hosted + version: "3.0.4" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa" + url: "https://pub.dev" + source: hosted + version: "2.0.16" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd + url: "https://pub.dev" + source: hosted + version: "3.0.5" user_repository: dependency: "direct main" description: @@ -1280,6 +1391,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: ef67178f0cc7e32c1494645b11639dd1335f1d18814aa8435113a92e9ef9d841 + url: "https://pub.dev" + source: hosted + version: "3.0.1" webkit_inspection_protocol: dependency: transitive description: @@ -1288,6 +1407,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: "47663d51a9061451aa3880a214ee9a65dcbb933b77bc44388e194279ab3ccaf6" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "34f83c2f0f64c75ad75c77a2ccfc8d2e531afbe8ad41af1fd787d6d33336aa90" + url: "https://pub.dev" + source: hosted + version: "3.4.3" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: "1939c39e2150fb4d30fd3cc59a891a49fed9935db53007df633ed83581b6117b" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: ab12479f7a0cf112b9420c36aaf206a1ca47cd60cd42de74a4be2e97a697587b + url: "https://pub.dev" + source: hosted + version: "3.2.1" widgetbook: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 73d449b..2b4e302 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,8 @@ dev_dependencies: bloc_test: ^9.1.1 flutter_test: sdk: flutter + integration_test: + sdk: flutter freezed: ^2.2.0 mocktail: ^0.3.0 very_good_analysis: ^4.0.0+1 diff --git a/test/app/view/app_test.dart b/test/app/view/app_test.dart deleted file mode 100644 index c15f5d9..0000000 --- a/test/app/view/app_test.dart +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2022, Very Good Ventures -// https://verygood.ventures -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. - -import 'package:flutter_test/flutter_test.dart'; -import 'package:fpb/app/app.dart'; -import 'package:fpb/onboarding/view/onboarding_screens.dart'; - -void main() { - group('App', () { - testWidgets('renders SignInPage', (tester) async { - await tester.pumpWidget( - App(), - ); - expect(find.byType(OnboardingScreen), findsOneWidget); - }); - }); -} diff --git a/test/authentication_with_facebook/application/facebook_auth_bloc_test.dart b/test/authentication_with_facebook/application/facebook_auth_bloc_test.dart index e7860c2..597aa49 100644 --- a/test/authentication_with_facebook/application/facebook_auth_bloc_test.dart +++ b/test/authentication_with_facebook/application/facebook_auth_bloc_test.dart @@ -2,16 +2,12 @@ import 'package:bloc_test/bloc_test.dart'; import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fpb/authentication_with_facebook/application/facebook_auth_bloc.dart'; -import 'package:fpb/authentication_with_facebook/facebook_auth.dart'; import 'package:fpb/core/domain/user.dart'; import 'package:fpb/core/failures/auth_failure.dart'; import 'package:mocktail/mocktail.dart'; import '../../helpers/helpers.dart'; -class MockIFacebookRepositoryFacade extends Mock - implements IFacebookRepositoryFacade {} - void main() { late FacebookAuthBloc bloc; late MockIFacebookRepositoryFacade facebookRepositoryFacade; @@ -34,13 +30,8 @@ void main() { blocTest( "should emit [loading, failed] with proper message for the error when facebook auth fails", build: () { - when( - () => facebookRepositoryFacade.signInWithFacebook(), - ).thenAnswer( - (invocation) async => left( - AuthFailure.cancelledByUser(), - ), - ); + arrangeFacebookRepositoryReturnsFailureOnSignin( + facebookRepositoryFacade); return bloc; }, act: (_) { @@ -67,13 +58,8 @@ void main() { blocTest( "should sign in the user with his facebook account", build: () { - when( - () => facebookRepositoryFacade.signInWithFacebook(), - ).thenAnswer( - (invocation) async => right( - testUser, - ), - ); + arrangeFacebookRepositoryReturnsSuccessOnSignin( + facebookRepositoryFacade); return bloc; }, act: (_) { @@ -102,13 +88,8 @@ void main() { blocTest( "should emit [loading, failed] with proper message for the error when facebook logout fails", build: () { - when( - () => facebookRepositoryFacade.signOut(), - ).thenAnswer( - (invocation) async => left( - AuthFailure.logoutFailed(), - ), - ); + arrangeFacebookRepositoryReturnsFailureOnSignout( + facebookRepositoryFacade); return bloc; }, act: (_) { @@ -135,13 +116,8 @@ void main() { blocTest( "should sign out the user with facebook", build: () { - when( - () => facebookRepositoryFacade.signOut(), - ).thenAnswer( - (invocation) async => right( - unit, - ), - ); + arrangeFacebookRepositoryReturnsSuccessOnSignout( + facebookRepositoryFacade); return bloc; }, act: (_) { diff --git a/test/authentication_with_firebase/application/bloc/auth_bloc_test.dart b/test/authentication_with_firebase/application/bloc/auth_bloc_test.dart index b64f3a0..3c5ceec 100644 --- a/test/authentication_with_firebase/application/bloc/auth_bloc_test.dart +++ b/test/authentication_with_firebase/application/bloc/auth_bloc_test.dart @@ -14,13 +14,7 @@ void main() { setUp(() { authFacade = MockIAuthFacade(); - when( - () => authFacade.user, - ).thenAnswer( - (_) async* { - yield User.empty; - }, - ); + arrangeAuthRepositoryReturnsStreamWithEmptyUser(authFacade); bloc = AuthBloc(authFacade); }); @@ -33,11 +27,7 @@ void main() { blocTest( "Trigger auth request", build: () { - when( - () => authFacade.currentUser, - ).thenReturn( - User.empty, - ); + arrangeAuthRepositoryReturnsCurrentUserAsEmpty(authFacade); return bloc; }, diff --git a/test/authentication_with_google/application/google_sign_in_bloc_test.dart b/test/authentication_with_google/application/google_sign_in_bloc_test.dart index 66c3521..ec0cf92 100644 --- a/test/authentication_with_google/application/google_sign_in_bloc_test.dart +++ b/test/authentication_with_google/application/google_sign_in_bloc_test.dart @@ -30,13 +30,7 @@ void main() { blocTest( "should emit [loading, failed] with proper message for the error when google auth fails", build: () { - when( - () => googleRepositoryFacade.signInWithGoogle(), - ).thenAnswer( - (invocation) async => left( - AuthFailure.cancelledByUser(), - ), - ); + arrangeGoogleRepositoryReturnsFailureOnSignin(googleRepositoryFacade); return bloc; }, act: (_) { @@ -63,13 +57,7 @@ void main() { blocTest( "should sign in the user with his google account", build: () { - when( - () => googleRepositoryFacade.signInWithGoogle(), - ).thenAnswer( - (invocation) async => right( - testUser, - ), - ); + arrangeGoogleRepositoryReturnsSuccessOnSignin(googleRepositoryFacade); return bloc; }, act: (_) { @@ -98,13 +86,7 @@ void main() { blocTest( "should emit [loading, failed] with proper message for the error when google logout fails", build: () { - when( - () => googleRepositoryFacade.signOut(), - ).thenAnswer( - (invocation) async => left( - AuthFailure.logoutFailed(), - ), - ); + arrangeGoogleRepositoryReturnsFailureOnSignout(googleRepositoryFacade); return bloc; }, act: (_) { @@ -131,13 +113,7 @@ void main() { blocTest( "should sign out the user with google", build: () { - when( - () => googleRepositoryFacade.signOut(), - ).thenAnswer( - (invocation) async => right( - unit, - ), - ); + arrangeGoogleRepositoryReturnsSuccessOnSignout(googleRepositoryFacade); return bloc; }, act: (_) { diff --git a/test/core/application/email_password_bloc/email_password_bloc_test.dart b/test/core/application/email_password_bloc/email_password_bloc_test.dart index b709cba..d8284e9 100644 --- a/test/core/application/email_password_bloc/email_password_bloc_test.dart +++ b/test/core/application/email_password_bloc/email_password_bloc_test.dart @@ -1,9 +1,7 @@ import 'package:bloc_test/bloc_test.dart'; -import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:formz/formz.dart'; import 'package:fpb/core/application/email_password_bloc/email_password_bloc.dart'; -import 'package:fpb/core/failures/auth_failure.dart'; import 'package:fpb/sign_in/domain/email.dart'; import 'package:fpb/sign_in/domain/password.dart'; import 'package:mocktail/mocktail.dart'; @@ -104,18 +102,7 @@ void main() { blocTest( "should emit [loading, failed] with proper message for the error when Email&Password password sign in fails", build: () { - when( - () => authFacade.signInWithEmailAndPassword( - email: validEmail, - password: validPassword, - ), - ).thenAnswer( - (invocation) async { - return left( - AuthFailure.invalidEmailAndPasswordCombination(), - ); - }, - ); + arrangeAuthRepositoryReturnsFailureOnSignin(authFacade); return bloc; }, act: (_) { @@ -147,16 +134,7 @@ void main() { blocTest( "should sign in the user with his email and password", build: () { - when( - () => authFacade.signInWithEmailAndPassword( - email: validEmail, - password: validPassword, - ), - ).thenAnswer( - (invocation) async => right( - testUser, - ), - ); + arrangeAuthRepositoryReturnsSuccessOnSignin(authFacade); return bloc; }, act: (_) { diff --git a/test/helpers/arrange_auth_repository.dart b/test/helpers/arrange_auth_repository.dart new file mode 100644 index 0000000..99df2f9 --- /dev/null +++ b/test/helpers/arrange_auth_repository.dart @@ -0,0 +1,91 @@ +import 'package:dartz/dartz.dart'; +import 'package:fpb/core/domain/user.dart'; +import 'package:fpb/core/failures/auth_failure.dart'; +import 'package:mocktail/mocktail.dart'; + +import 'mocks.dart'; +import 'test_data.dart'; + +void arrangeAuthRepositoryReturnsCurrentUser(MockIAuthFacade authFacade) { + when( + () => authFacade.currentUser, + ).thenReturn( + testUser, + ); +} + +void arrangeAuthRepositoryReturnsCurrentUserAsEmpty( + MockIAuthFacade authFacade) { + when( + () => authFacade.currentUser, + ).thenReturn( + User.empty, + ); +} + +void arrangeAuthRepositoryReturnsStreamWithEmptyUser( + MockIAuthFacade authFacade) { + when( + () => authFacade.user, + ).thenAnswer( + (_) async* { + yield User.empty; + }, + ); +} + +void arrangeAuthRepositoryReturnsStreamWithUser(MockIAuthFacade authFacade) { + when( + () => authFacade.user, + ).thenAnswer( + (_) async* { + yield testUser; + }, + ); +} + +void arrangeAuthRepositoryReturnsFailureOnSignin(MockIAuthFacade authFacade) { + when( + () => authFacade.signInWithEmailAndPassword( + email: validEmail, + password: validPassword, + ), + ).thenAnswer( + (invocation) async { + return left( + AuthFailure.invalidEmailAndPasswordCombination(), + ); + }, + ); +} + +void arrangeAuthRepositoryReturnsSuccessOnSignin(MockIAuthFacade authFacade) { + when( + () => authFacade.signInWithEmailAndPassword( + email: validEmail, + password: validPassword, + ), + ).thenAnswer( + (invocation) async => right( + testUser, + ), + ); +} + +void arrangeAuthRepositoryReturnsFailureOnLogoutRequest( + MockIAuthFacade authFacade) { + when(() => authFacade.signOut()).thenAnswer( + (invocation) async { + return left(AuthFailure.logoutFailed()); + }, + ); +} + +void arrangeAuthRepositoryReturnsSuccessOnLogoutRequest( + MockIAuthFacade authFacade) { + when(() => authFacade.signOut()).thenAnswer( + (invocation) async { + return right(unit); + }, + ); +} diff --git a/test/helpers/arrange_cached.dart b/test/helpers/arrange_cached.dart new file mode 100644 index 0000000..9f397f8 --- /dev/null +++ b/test/helpers/arrange_cached.dart @@ -0,0 +1,11 @@ +import 'package:mocktail/mocktail.dart'; + +import 'mocks.dart'; + +void arrangeCachedReturnsLastView(MockCached cached, int intendedValue) { + when( + () => cached.getLastView, + ).thenReturn( + intendedValue, + ); +} diff --git a/test/helpers/arrange_facebook_repository.dart b/test/helpers/arrange_facebook_repository.dart new file mode 100644 index 0000000..c24d6a6 --- /dev/null +++ b/test/helpers/arrange_facebook_repository.dart @@ -0,0 +1,50 @@ +import 'package:dartz/dartz.dart'; +import 'package:fpb/core/failures/auth_failure.dart'; +import 'package:mocktail/mocktail.dart'; + +import 'mocks.dart'; +import 'test_data.dart'; + +void arrangeFacebookRepositoryReturnsFailureOnSignin( + MockIFacebookRepositoryFacade facebookRepositoryFacade) { + when( + () => facebookRepositoryFacade.signInWithFacebook(), + ).thenAnswer( + (invocation) async => left( + AuthFailure.cancelledByUser(), + ), + ); +} + +void arrangeFacebookRepositoryReturnsSuccessOnSignin( + MockIFacebookRepositoryFacade facebookRepositoryFacade) { + when( + () => facebookRepositoryFacade.signInWithFacebook(), + ).thenAnswer( + (invocation) async => right( + testUser, + ), + ); +} + +void arrangeFacebookRepositoryReturnsFailureOnSignout( + MockIFacebookRepositoryFacade facebookRepositoryFacade) { + when( + () => facebookRepositoryFacade.signOut(), + ).thenAnswer( + (invocation) async => left( + AuthFailure.logoutFailed(), + ), + ); +} + +void arrangeFacebookRepositoryReturnsSuccessOnSignout( + MockIFacebookRepositoryFacade facebookRepositoryFacade) { + when( + () => facebookRepositoryFacade.signOut(), + ).thenAnswer( + (invocation) async => right( + unit, + ), + ); +} diff --git a/test/helpers/arrange_google_repository.dart b/test/helpers/arrange_google_repository.dart new file mode 100644 index 0000000..f9a7a79 --- /dev/null +++ b/test/helpers/arrange_google_repository.dart @@ -0,0 +1,50 @@ +import 'package:dartz/dartz.dart'; +import 'package:fpb/core/failures/auth_failure.dart'; +import 'package:mocktail/mocktail.dart'; + +import 'mocks.dart'; +import 'test_data.dart'; + +void arrangeGoogleRepositoryReturnsFailureOnSignin( + MockIGoogleRepositoryFacade googleRepositoryFacade) { + when( + () => googleRepositoryFacade.signInWithGoogle(), + ).thenAnswer( + (invocation) async => left( + AuthFailure.cancelledByUser(), + ), + ); +} + +void arrangeGoogleRepositoryReturnsSuccessOnSignin( + MockIGoogleRepositoryFacade googleRepositoryFacade) { + when( + () => googleRepositoryFacade.signInWithGoogle(), + ).thenAnswer( + (invocation) async => right( + testUser, + ), + ); +} + +void arrangeGoogleRepositoryReturnsFailureOnSignout( + MockIGoogleRepositoryFacade googleRepositoryFacade) { + when( + () => googleRepositoryFacade.signOut(), + ).thenAnswer( + (invocation) async => left( + AuthFailure.logoutFailed(), + ), + ); +} + +void arrangeGoogleRepositoryReturnsSuccessOnSignout( + MockIGoogleRepositoryFacade googleRepositoryFacade) { + when( + () => googleRepositoryFacade.signOut(), + ).thenAnswer( + (invocation) async => right( + unit, + ), + ); +} diff --git a/test/helpers/helpers.dart b/test/helpers/helpers.dart index 0ea84b8..a764a0e 100644 --- a/test/helpers/helpers.dart +++ b/test/helpers/helpers.dart @@ -5,6 +5,10 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +export 'arrange_auth_repository.dart'; +export 'arrange_cached.dart'; +export 'arrange_facebook_repository.dart'; +export 'arrange_google_repository.dart'; export 'mocks.dart'; export 'pump_app.dart'; export 'test_data.dart'; diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index 6f51f5f..c47bc76 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -2,6 +2,7 @@ import 'package:fpb/authentication_with_facebook/domain/i_facebook_repository_fa import 'package:fpb/authentication_with_firebase/domain/i_auth_facade.dart'; import 'package:fpb/authentication_with_google/domain/i_google_repository_facade.dart'; import 'package:fpb/core/settings/app_settings_helper.dart'; +import 'package:fpb/core/settings/cached.dart'; import 'package:mocktail/mocktail.dart'; class MockIGoogleRepositoryFacade extends Mock @@ -13,3 +14,5 @@ class MockIFacebookRepositoryFacade extends Mock implements IFacebookRepositoryFacade {} class MockAppSettingsHelper extends Mock implements AppSettingsHelper {} + +class MockCached extends Mock implements Cached {} diff --git a/test/helpers/pump_app.dart b/test/helpers/pump_app.dart index 40304c3..5e6da51 100644 --- a/test/helpers/pump_app.dart +++ b/test/helpers/pump_app.dart @@ -6,16 +6,80 @@ // https://opensource.org/licenses/MIT. import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:fpb/authentication_with_facebook/application/facebook_auth_bloc.dart'; +import 'package:fpb/authentication_with_firebase/application/bloc/auth_bloc.dart'; +import 'package:fpb/authentication_with_google/application/google_auth_bloc/google_sign_in_bloc.dart'; +import 'package:fpb/core/application/email_password_bloc/email_password_bloc.dart'; +import 'package:fpb/core/application/internet_and_time_bloc/internet_and_time_bloc.dart'; +import 'package:fpb/home/application/home_view_bloc/home_view_bloc.dart'; import 'package:fpb/l10n/l10n.dart'; +import 'helpers.dart'; + extension PumpApp on WidgetTester { - Future pumpApp(Widget widget) { + Future pumpApp( + Widget widget, { + MockAppSettingsHelper? mockAppSettingsHelper, + MockIAuthFacade? mockIAuthFacadeForAuthBloc, + MockIGoogleRepositoryFacade? mockIGoogleRepositoryFacade, + MockIFacebookRepositoryFacade? mockIFacebookRepositoryFacade, + MockIAuthFacade? mockIAuthFacadeFroEmailPasswordBloc, + MockCached? mockCachedForHomeBloc, + }) { return pumpWidget( - MaterialApp( - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - home: widget, + MultiBlocProvider( + providers: [ + // BlocProvider( + // create: (context) => getIt()), + if (mockAppSettingsHelper != null) + BlocProvider( + create: (context) => InternetAndTimeBloc( + mockAppSettingsHelper, + ), + ), + if (mockIAuthFacadeForAuthBloc != null) + BlocProvider( + create: (context) => AuthBloc( + mockIAuthFacadeForAuthBloc, + )..add(const AuthEvent.triggerAuthRequest()), + ), + if (mockIGoogleRepositoryFacade != null) + BlocProvider( + create: (context) => GoogleSignInBloc( + authenticationRepository: mockIGoogleRepositoryFacade, + ), + ), + if (mockIFacebookRepositoryFacade != null) + BlocProvider( + create: (context) => FacebookAuthBloc( + authenticationRepository: mockIFacebookRepositoryFacade, + ), + ), + if (mockIAuthFacadeFroEmailPasswordBloc != null) + BlocProvider( + create: (context) => EmailPasswordBloc( + authenticationRepository: mockIAuthFacadeFroEmailPasswordBloc, + ), + ), + if (mockCachedForHomeBloc != null) + BlocProvider( + create: (context) => HomeViewBloc(mockCachedForHomeBloc) + ..add( + HomeViewEvent.lastState(), + ), + ), + // BlocProvider( + // create: (context) => BiometricAuthBloc( + // authenticationRepository: LocalAuthentication()), + // ), + ], + child: MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: widget, + ), ), ); } diff --git a/test/helpers/test_data.dart b/test/helpers/test_data.dart index 2e80821..174788e 100644 --- a/test/helpers/test_data.dart +++ b/test/helpers/test_data.dart @@ -3,11 +3,12 @@ import 'package:fpb/core/domain/user.dart'; const User testUser = User( id: "dede", isNewUser: true, - photo: "photo", + photo: + "https://avatars.githubusercontent.com/u/52233891?s=400&u=e5da06a4923b3a269085775e6738f06b4f292607&v=4", providerId: "providerId", - name: "name", - email: "email", - phoneNumber: "phoneNumber", + name: "Kengne Jeffrey", + email: "darrelntangu@gmail.com", + phoneNumber: "+237652432246", ); const String invalidEmail = "dev.flutter@flu"; diff --git a/test/home/application/home_view_bloc/home_view_bloc_test.dart b/test/home/application/home_view_bloc/home_view_bloc_test.dart new file mode 100644 index 0000000..71983f2 --- /dev/null +++ b/test/home/application/home_view_bloc/home_view_bloc_test.dart @@ -0,0 +1,65 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:fpb/home/application/home_view_bloc/home_view_bloc.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + late HomeViewBloc bloc; + late MockCached cached; + + setUp(() { + cached = MockCached(); + + bloc = HomeViewBloc(cached); + }); + + test("Home View Bloc initial state should be _Home", () { + //assert + expect(bloc.state, equals(HomeViewState.home())); + }); + + blocTest( + "emits [_Home] when HomeViewEvent.home is added", + build: () { + when( + () => cached.getLastView, + ).thenReturn( + 0, + ); + return bloc; + }, + act: (_) { + _.add(HomeViewEvent.home()); + }, + expect: () => contains(HomeViewState.home()), + verify: (bloc) { + verify( + () => cached.setLastView = bloc.lastView ?? 0, + ).called(1); + }, + ); + + blocTest( + "emits [_Home, _Budget] when HomeViewEvent.home is added, after HomeViewEvent.budget is added, then HomeViewEvent.lastState is added", + build: () { + arrangeCachedReturnsLastView(cached, 0); + return bloc; + }, + act: (_) { + _.add(HomeViewEvent.home()); + _.add(HomeViewEvent.budget()); + _.add(HomeViewEvent.lastState()); + }, + expect: () => containsAllInOrder([ + HomeViewState.home(), + HomeViewState.budget(), + ]), + verify: (bloc) { + verify( + () => cached.setLastView = bloc.lastView ?? 0, + ).called(1); + }, + ); +} diff --git a/test/sign_in/view/sign_in_screen_test.dart b/test/sign_in/view/sign_in_screen_test.dart new file mode 100644 index 0000000..c69bc6a --- /dev/null +++ b/test/sign_in/view/sign_in_screen_test.dart @@ -0,0 +1,35 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:fpb/sign_in/sign_in.dart'; +import 'package:fpb/sign_in/view/widgets/login_button.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + late MockIFacebookRepositoryFacade mockIFacebookRepositoryFacade; + late MockIGoogleRepositoryFacade mockIGoogleRepositoryFacade; + late MockIAuthFacade mockIAuthFacade; + + setUp(() { + mockIAuthFacade = MockIAuthFacade(); + mockIFacebookRepositoryFacade = MockIFacebookRepositoryFacade(); + mockIGoogleRepositoryFacade = MockIGoogleRepositoryFacade(); + }); + + group("Sign in screen widget test", () { + testWidgets("check for login button", (widgetTester) async { + arrangeAuthRepositoryReturnsStreamWithEmptyUser(mockIAuthFacade); + arrangeAuthRepositoryReturnsCurrentUserAsEmpty(mockIAuthFacade); + + await widgetTester.pumpApp( + SignInBody(), + mockIAuthFacadeFroEmailPasswordBloc: mockIAuthFacade, + mockIFacebookRepositoryFacade: mockIFacebookRepositoryFacade, + mockIGoogleRepositoryFacade: mockIGoogleRepositoryFacade, + ); + + await widgetTester.pump(); + + expect(find.byType(LoginButton), findsOneWidget); + }); + }); +} diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 3d61c3e..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,17 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async {}); -} - -//commands to run after code -// flutter analyze lib test -// dart fix --dry-run -//flutter format lib test