From ea50021c7b3a694d7f6d53e30d68521fd134c8d5 Mon Sep 17 00:00:00 2001 From: Isaac Obella Date: Sat, 28 Jan 2023 23:47:32 +0300 Subject: [PATCH] ft: Launch password dialog from details Signed-off-by: Isaac Obella --- .../crowdaction_details_screen.dart | 10 ++ .../home/widgets/password_modal.dart | 28 +++- .../profile_tab/profile_tab_bloc.mocks.dart | 5 + .../crowdaction_details_screen_test.dart | 150 ++++++++++++++++++ 4 files changed, 188 insertions(+), 5 deletions(-) create mode 100644 test/application/user/profile_tab/profile_tab_bloc.mocks.dart create mode 100644 test/presentation/crowdaction/crowdaction_details/crowdaction_details_screen_test.dart diff --git a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart index 739997eb..25da85d8 100644 --- a/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart +++ b/lib/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart @@ -9,6 +9,7 @@ import '../../../application/auth/auth_bloc.dart'; import '../../../application/crowdaction/crowdaction_details/crowdaction_details_bloc.dart'; import '../../../application/participation/participation_bloc.dart'; import '../../../application/user/profile_tab/profile_tab_bloc.dart'; +import '../../home/widgets/password_modal.dart'; import '../../routes/app_routes.gr.dart'; import '../../shared_widgets/commitments/commitment_card_list.dart'; import '../../shared_widgets/expandable_text.dart'; @@ -118,6 +119,15 @@ class CrowdActionDetailsPageState extends State { state.maybeMap( loadSuccess: (state) { crowdAction = state.crowdAction; + showPasswordModal( + context, + state.crowdAction, + onPasswordValid: (isValidated) { + if (isValidated) { + Navigator.of(context).pop(); + } + }, + ); }, orElse: () {}, ); diff --git a/lib/presentation/home/widgets/password_modal.dart b/lib/presentation/home/widgets/password_modal.dart index 94cbd3c5..5df5de2d 100644 --- a/lib/presentation/home/widgets/password_modal.dart +++ b/lib/presentation/home/widgets/password_modal.dart @@ -10,10 +10,12 @@ import '../../../presentation/themes/constants.dart'; class PasswordModal extends StatefulWidget { final CrowdAction crowdAction; + final Function(bool)? onPasswordValid; const PasswordModal({ super.key, required this.crowdAction, + this.onPasswordValid, }); @override @@ -147,6 +149,11 @@ class _PasswordModalState extends State { addCrowdActionAccess(); + if (widget.onPasswordValid != null) { + widget.onPasswordValid?.call(true); + return; + } + context.router.replace( CrowdActionDetailsRoute( crowdAction: widget.crowdAction, @@ -173,21 +180,32 @@ class _PasswordModalState extends State { Future showPasswordModal( BuildContext context, - CrowdAction crowdAction, -) async { + CrowdAction crowdAction, { + Function(bool)? onPasswordValid, +}) async { final settingsRepository = getIt(); final accessList = await settingsRepository.getCrowdActionAccessList(); if (accessList.contains(crowdAction.id)) { - context.router.push( - CrowdActionDetailsRoute(crowdAction: crowdAction), + if (onPasswordValid != null) { + onPasswordValid(false); + return; + } + + context.router.replace( + CrowdActionDetailsRoute( + crowdAction: crowdAction, + ), ); } else { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, constraints: const BoxConstraints(maxHeight: 350), - builder: (context) => PasswordModal(crowdAction: crowdAction), + builder: (context) => PasswordModal( + crowdAction: crowdAction, + onPasswordValid: onPasswordValid, + ), ); } } diff --git a/test/application/user/profile_tab/profile_tab_bloc.mocks.dart b/test/application/user/profile_tab/profile_tab_bloc.mocks.dart new file mode 100644 index 00000000..374c22e9 --- /dev/null +++ b/test/application/user/profile_tab/profile_tab_bloc.mocks.dart @@ -0,0 +1,5 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:collaction_app/application/user/profile_tab/profile_tab_bloc.dart'; + +class MockProfileTabBloc extends MockBloc + implements ProfileTabBloc {} diff --git a/test/presentation/crowdaction/crowdaction_details/crowdaction_details_screen_test.dart b/test/presentation/crowdaction/crowdaction_details/crowdaction_details_screen_test.dart new file mode 100644 index 00000000..bd187285 --- /dev/null +++ b/test/presentation/crowdaction/crowdaction_details/crowdaction_details_screen_test.dart @@ -0,0 +1,150 @@ +import 'dart:async'; + +import 'package:bloc_test/bloc_test.dart'; +import 'package:collaction_app/application/auth/auth_bloc.dart'; +import 'package:collaction_app/application/crowdaction/crowdaction_details/crowdaction_details_bloc.dart'; +import 'package:collaction_app/application/participation/participation_bloc.dart'; +import 'package:collaction_app/application/participation/top_participants/top_participants_bloc.dart'; +import 'package:collaction_app/application/user/profile_tab/profile_tab_bloc.dart'; +import 'package:collaction_app/domain/core/i_settings_repository.dart'; +import 'package:collaction_app/domain/crowdaction/crowdaction.dart'; +import 'package:collaction_app/domain/user/user.dart'; +import 'package:collaction_app/presentation/crowdaction/crowdaction_details/crowdaction_details_screen.dart'; +import 'package:collaction_app/presentation/home/widgets/password_modal.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mocktail/mocktail.dart'; + +import '../../../application/auth/auth_bloc.mocks.dart'; +import '../../../application/crowdaction/crowdaction_details/crowdaction_details_bloc.mocks.dart'; +import '../../../application/participation/participation_bloc.mock.dart'; +import '../../../application/participation/top_participants/top_participants_bloc.mocks.dart'; +import '../../../application/user/profile_tab/profile_tab_bloc.mocks.dart'; +import '../../../test_utilities.dart'; + +void main() { + late final CrowdActionDetailsBloc crowdActionDetailsBloc; + late final ProfileTabBloc profileTabBloc; + late final ParticipationBloc participationBloc; + late final TopParticipantsBloc topParticipantsBloc; + late final AuthBloc authBloc; + late final ISettingsRepository settingsRepository; + + final crowdAction = tCrowdaction; + + setUpAll(() { + dotenv.testLoad(fileInput: tDotEnv); + + crowdActionDetailsBloc = MockCrowdActionDetailsBloc(); + GetIt.I.registerSingleton(crowdActionDetailsBloc); + when(() => crowdActionDetailsBloc.state).thenAnswer( + (_) => CrowdActionDetailsState.initial(), + ); + whenListen( + crowdActionDetailsBloc, + Stream.fromIterable( + [ + CrowdActionDetailsState.initial(), + CrowdActionDetailsState.loadSuccess(crowdAction), + ], + ), + ); + + profileTabBloc = MockProfileTabBloc(); + when(() => profileTabBloc.state).thenAnswer( + (_) => ProfileTabState(crowdActions: [crowdAction]), + ); + + participationBloc = MockParticipationBloc(); + GetIt.I.registerSingleton(participationBloc); + when(() => participationBloc.state).thenAnswer( + (_) => ParticipationState.notParticipating(), + ); + + topParticipantsBloc = MockTopParticipantsBloc(); + GetIt.I.registerSingleton(topParticipantsBloc); + when(() => topParticipantsBloc.state).thenAnswer( + (_) => TopParticipantsState.initial(), + ); + + authBloc = MockAuthBloc(); + GetIt.I.registerSingleton(authBloc); + when(() => authBloc.state).thenAnswer( + (_) => AuthState.authenticated(User.anonymous), + ); + + settingsRepository = MockSettingsRepository(); + GetIt.I.registerSingleton(settingsRepository); + }); + + tearDownAll(() { + GetIt.I.unregister(); + GetIt.I.unregister(); + GetIt.I.unregister(); + GetIt.I.unregister(); + GetIt.I.unregister(); + }); + + testWidgets( + 'should launch [PasswordModal] ' + 'when crowdaction is not in access list', (tester) async { + when(() => settingsRepository.getCrowdActionAccessList()).thenAnswer( + (_) async => [], + ); + when( + () => settingsRepository.addCrowdActionAccess( + crowdActionId: crowdAction.id, + ), + ).thenAnswer((_) async {}); + + await tester.pumpCrowdActionsDetailPage( + crowdAction: crowdAction, + profileTabBloc: profileTabBloc, + ); + + await tester.pump(); + + final findPasswordModal = find.byType(PasswordModal); + expect(findPasswordModal, findsOneWidget); + }); + + testWidgets( + 'should not launch [PasswordModal] ' + 'when crowdaction is in access list', (tester) async { + when(() => settingsRepository.getCrowdActionAccessList()).thenAnswer( + (_) async => [crowdAction.id], + ); + + await tester.pumpCrowdActionsDetailPage( + crowdAction: crowdAction, + profileTabBloc: profileTabBloc, + ); + + await tester.pump(); + + expect(find.byType(PasswordModal), findsNothing); + }); +} + +extension WidgetTesterX on WidgetTester { + Future pumpCrowdActionsDetailPage({ + required CrowdAction crowdAction, + required ProfileTabBloc profileTabBloc, + }) async { + await pumpWidget( + BlocProvider.value( + value: profileTabBloc, + child: MaterialApp( + home: Scaffold( + body: CrowdActionDetailsPage( + crowdAction: crowdAction, + ), + ), + ), + ), + ); + } +}