From c81fc07af730583f4a2f09e6b1f0af8b7f3ea9bf Mon Sep 17 00:00:00 2001 From: januszjanus Date: Mon, 23 Dec 2024 16:23:35 +0100 Subject: [PATCH 1/3] wip --- .../integration_test/onboarding_test.dart | 41 +++++++++++++++++++ .../pageobject/app_bar_page.dart | 2 + .../pageobject/common_page.dart | 10 +++++ .../pageobject/onboarding_page.dart | 35 ++++++++++++++++ .../registration/registration_info_panel.dart | 1 + .../widgets/information_panel.dart | 5 ++- .../lib/widgets/buttons/voices_buttons.dart | 1 + .../lib/widgets/common/affix_decorator.dart | 4 +- 8 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 catalyst_voices/apps/voices/integration_test/onboarding_test.dart create mode 100644 catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart create mode 100644 catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart diff --git a/catalyst_voices/apps/voices/integration_test/onboarding_test.dart b/catalyst_voices/apps/voices/integration_test/onboarding_test.dart new file mode 100644 index 00000000000..1f9a72cb82d --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/onboarding_test.dart @@ -0,0 +1,41 @@ +import 'package:catalyst_voices/app/view/app.dart'; +import 'package:catalyst_voices/configs/bootstrap.dart'; +import 'package:catalyst_voices/routes/routes.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:patrol_finders/patrol_finders.dart'; + +import 'pageobject/app_bar_page.dart'; +import 'pageobject/onboarding_page.dart'; + +void main() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + late final GoRouter router; + + setUpAll(() async { + router = buildAppRouter(); + await bootstrap(router: router); + }); + + setUp(() async { + await registerDependencies(); + router.go(const DiscoveryRoute().location); + }); + + tearDown(() async { + await restartDependencies(); + }); + + patrolWidgetTest( + 'Onboarding - visitor - get started button works', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: const Duration(seconds: 10)); + expect(await OnboardingPage.infoLearnMoreButtonText($), 'Learn More'); + // await Future.delayed(const Duration(seconds: 5)); + }, + // skip: true, + ); +} diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/app_bar_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/app_bar_page.dart index 32e7018cb77..ba80fdd9624 100644 --- a/catalyst_voices/apps/voices/integration_test/pageobject/app_bar_page.dart +++ b/catalyst_voices/apps/voices/integration_test/pageobject/app_bar_page.dart @@ -4,4 +4,6 @@ import 'package:flutter/material.dart'; class AppBarPage { static const spacesDrawerButton = Key('DrawerButton'); + static const getStartedBtn = Key('GetStartedButton'); + } diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart new file mode 100644 index 00000000000..370e38544a5 --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart @@ -0,0 +1,10 @@ +library dashboard_page; + +import 'package:flutter/material.dart'; + +class CommonPage { + static const decoratorData = Key('DecoratorData'); + static const decoratorIconBefore = Key('DecoratorIconBefore'); + static const decoratorIconAfter = Key('DecoratorIconAfter'); + +} diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart new file mode 100644 index 00000000000..cafe555366e --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart @@ -0,0 +1,35 @@ +library dashboard_page; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:patrol_finders/patrol_finders.dart'; + +import 'common_page.dart'; + +class OnboardingPage { + static const registrationInfoPanel = Key('RegistrationInfoPanel'); + static const registrationInfoLearnMoreButton = Key('LearnMoreButton'); + static const headerTitle = Key('HeaderTitle'); + static const headerSubtitle = Key('HeaderSubtitle'); + static const headerBody = Key('HeaderBody'); + + static Future infoHeaderTitleText(PatrolTester $) async { + return $(registrationInfoPanel).$(headerTitle).text; + } + + static Future infoHeaderSubtitleText(PatrolTester $) async { + return $(registrationInfoPanel).$(headerSubtitle).text; + } + + static Future infoHeaderBodyText(PatrolTester $) async { + return $(registrationInfoPanel).$(headerBody).text; + } + + static Future infoLearnMoreButtonText(PatrolTester $) async { + final child = find.descendant( + of: $(registrationInfoPanel).$(CommonPage.decoratorData), + matching: find.byType(Text), + ); + return $(child).text; + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/registration/registration_info_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/registration_info_panel.dart index 5d320a93ef9..8c2928cb06d 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/registration_info_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/registration_info_panel.dart @@ -50,6 +50,7 @@ class RegistrationInfoPanel extends StatelessWidget { ); return InformationPanel( + key: const Key('RegistrationInfoPanel'), title: headerStrings.title, subtitle: headerStrings.subtitle, body: headerStrings.body, diff --git a/catalyst_voices/apps/voices/lib/pages/registration/widgets/information_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/widgets/information_panel.dart index 9f4104b4333..9f270fe665f 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/widgets/information_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/widgets/information_panel.dart @@ -30,7 +30,7 @@ class InformationPanel extends StatelessWidget { body: body, ), const SizedBox(height: 12), - Expanded(child: Center(child: picture)), + Expanded(key: const Key('PictureContainer'), child: Center(child: picture)), const SizedBox(height: 12), _Footer( progress: progress, @@ -64,16 +64,19 @@ class _Header extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( + key: const Key('HeaderTitle'), title, style: theme.textTheme.titleLarge?.copyWith(color: textColor), ), if (subtitle != null) Text( + key: const Key('HeaderSubtitle'), subtitle, style: theme.textTheme.titleMedium?.copyWith(color: textColor), ), if (body != null) Text( + key: const Key('HeaderBody'), body, style: theme.textTheme.bodyMedium?.copyWith(color: textColor), ), diff --git a/catalyst_voices/apps/voices/lib/widgets/buttons/voices_buttons.dart b/catalyst_voices/apps/voices/lib/widgets/buttons/voices_buttons.dart index 27d91ad462b..a5768ea08a2 100644 --- a/catalyst_voices/apps/voices/lib/widgets/buttons/voices_buttons.dart +++ b/catalyst_voices/apps/voices/lib/widgets/buttons/voices_buttons.dart @@ -166,6 +166,7 @@ class VoicesLearnMoreButton extends StatelessWidget { @override Widget build(BuildContext context) { return VoicesTextButton( + key: const Key('LearnMoreButton'), trailing: VoicesAssets.icons.externalLink.buildIcon(), onTap: onTap, child: Text(context.l10n.learnMore), diff --git a/catalyst_voices/apps/voices/lib/widgets/common/affix_decorator.dart b/catalyst_voices/apps/voices/lib/widgets/common/affix_decorator.dart index b9ff8621fc9..40c45517ba1 100644 --- a/catalyst_voices/apps/voices/lib/widgets/common/affix_decorator.dart +++ b/catalyst_voices/apps/voices/lib/widgets/common/affix_decorator.dart @@ -57,15 +57,17 @@ class AffixDecorator extends StatelessWidget { children: [ if (prefix != null) ...[ IconTheme( + key: const Key('DecoratorIconBefore'), data: iconTheme ?? IconTheme.of(context), child: prefix, ), SizedBox(width: gap), ], - Flexible(child: child), + Flexible(key: const Key('DecoratorData'),child: child,), if (suffix != null) ...[ SizedBox(width: gap), IconTheme( + key: const Key('DecoratorIconAfter'), data: iconTheme ?? IconTheme.of(context), child: suffix, ), From 01f17f955a6df59e855a5c8a8d0ce91ac61f2837 Mon Sep 17 00:00:00 2001 From: januszjanus Date: Tue, 24 Dec 2024 15:16:26 +0100 Subject: [PATCH 2/3] wip --- .../integration_test/onboarding_test.dart | 15 +- .../pageobject/common_page.dart | 2 +- .../pageobject/onboarding_page.dart | 141 +++++++++++++++++- .../types/registration_state.dart | 15 ++ .../utils/translations_utils.dart | 8 + .../get_started/get_started_panel.dart | 3 +- .../registration_details_panel.dart | 1 + .../widgets/registration_stage_message.dart | 2 + .../widgets/modals/voices_desktop_dialog.dart | 1 + 9 files changed, 179 insertions(+), 9 deletions(-) create mode 100644 catalyst_voices/apps/voices/integration_test/types/registration_state.dart create mode 100644 catalyst_voices/apps/voices/integration_test/utils/translations_utils.dart diff --git a/catalyst_voices/apps/voices/integration_test/onboarding_test.dart b/catalyst_voices/apps/voices/integration_test/onboarding_test.dart index 1f9a72cb82d..55c15876d7e 100644 --- a/catalyst_voices/apps/voices/integration_test/onboarding_test.dart +++ b/catalyst_voices/apps/voices/integration_test/onboarding_test.dart @@ -33,9 +33,18 @@ void main() async { await $.pumpWidgetAndSettle(App(routerConfig: router)); await $(AppBarPage.getStartedBtn) .tap(settleTimeout: const Duration(seconds: 10)); - expect(await OnboardingPage.infoLearnMoreButtonText($), 'Learn More'); - // await Future.delayed(const Duration(seconds: 5)); + expect($(OnboardingPage.registrationInfoPanel), findsOneWidget); + expect($(OnboardingPage.registrationDetailsPanel), findsOneWidget); + }, + ); + + patrolWidgetTest( + 'Onboarding - visitor - get started screen looks as expected', + (PatrolTester $) async { + await $.pumpWidgetAndSettle(App(routerConfig: router)); + await $(AppBarPage.getStartedBtn) + .tap(settleTimeout: const Duration(seconds: 10)); + await OnboardingPage.getStartedScreenLooksAsExpected($); }, - // skip: true, ); } diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart index 370e38544a5..8581d62e1a4 100644 --- a/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart +++ b/catalyst_voices/apps/voices/integration_test/pageobject/common_page.dart @@ -6,5 +6,5 @@ class CommonPage { static const decoratorData = Key('DecoratorData'); static const decoratorIconBefore = Key('DecoratorIconBefore'); static const decoratorIconAfter = Key('DecoratorIconAfter'); - + static const dialogCloseButton = Key('DialogCloseButton'); } diff --git a/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart b/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart index cafe555366e..1c7e3acf455 100644 --- a/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart +++ b/catalyst_voices/apps/voices/integration_test/pageobject/onboarding_page.dart @@ -4,32 +4,165 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:patrol_finders/patrol_finders.dart'; +import '../types/registration_state.dart'; +import '../utils/translations_utils.dart'; import 'common_page.dart'; class OnboardingPage { static const registrationInfoPanel = Key('RegistrationInfoPanel'); + static const registrationDetailsPanel = Key('RegistrationDetailsPanel'); static const registrationInfoLearnMoreButton = Key('LearnMoreButton'); static const headerTitle = Key('HeaderTitle'); static const headerSubtitle = Key('HeaderSubtitle'); static const headerBody = Key('HeaderBody'); + static const registrationInfoPictureContainer = Key('PictureContainer'); + static const registrationInfoTaskPicture = Key('TaskPictureIconBox'); + static const registrationDetailsTitle = Key('RegistrationDetailsTitle'); + static const registrationDetailsBody = Key('RegistrationDetailsBody'); - static Future infoHeaderTitleText(PatrolTester $) async { + static Future infoPartHeaderTitleText(PatrolTester $) async { return $(registrationInfoPanel).$(headerTitle).text; } - static Future infoHeaderSubtitleText(PatrolTester $) async { + static Future infoPartHeaderSubtitleText(PatrolTester $) async { return $(registrationInfoPanel).$(headerSubtitle).text; } - static Future infoHeaderBodyText(PatrolTester $) async { + static Future infoPartHeaderBodyText(PatrolTester $) async { return $(registrationInfoPanel).$(headerBody).text; } - static Future infoLearnMoreButtonText(PatrolTester $) async { + static Future infoPartLearnMoreButtonText(PatrolTester $) async { final child = find.descendant( of: $(registrationInfoPanel).$(CommonPage.decoratorData), matching: find.byType(Text), ); return $(child).text; } + + static Finder infoPartTaskPicture(PatrolTester $) { + final child = find.descendant( + of: $(registrationInfoPanel).$(registrationInfoPictureContainer), + matching: find.byType(IconTheme), + ); + return child; + } + + static String? detailsPartGetStartedTitle(PatrolTester $) { + final child = find.descendant( + of: $(registrationDetailsPanel).$(registrationDetailsTitle), + matching: find.byType(Text), + ); + return $(child).text; + } + + static String? detailsPartGetStartedBody(PatrolTester $) { + final child = find.descendant( + of: $(registrationDetailsPanel).$(registrationDetailsBody), + matching: find.byType(Text), + ); + return $(child).text; + } + + static String? detailsPartGetStartedQuestionText(PatrolTester $) { + return $(registrationDetailsPanel).$(const Key('GetStartedQuestion')).text; + } + + static Future detailsPartGetStartedCreateNewBtn( + PatrolTester $, + ) async { + return $(registrationDetailsPanel) + .$(const Key('CreateAccountType.createNew')); + } + + static Future detailsPartGetStartedRecoverBtn( + PatrolTester $, + ) async { + return $(registrationDetailsPanel) + .$(const Key('CreateAccountType.recover')); + } + + static Future getStartedScreenLooksAsExpected(PatrolTester $) async { + await registrationInfoPanelLooksAsExpected($, RegistrationState.getStarted); + await registrationDetailsPanelLooksAsExpected( + $, + RegistrationState.getStarted, + ); + } + + static Future registrationInfoPanelLooksAsExpected( + PatrolTester $, + RegistrationState step, + ) async { + switch (step) { + case RegistrationState.getStarted: + expect(await infoPartHeaderTitleText($), T.get('Get Started')); + expect(await infoPartLearnMoreButtonText($), T.get('Learn More')); + expect(infoPartTaskPicture($), findsOneWidget); + break; + case RegistrationState.checkYourKeychain: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.createKeychain: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.keychainCreated: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.keychainRestoreInfo: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.keychainRestoreInput: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.keychainRestoreStart: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.keychainRestoreSuccess: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.mnemonicInput: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.mnemonicVerified: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.mnemonicWritedown: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.passwordInfo: + // TODO: Handle this case. + throw UnimplementedError(); + case RegistrationState.passwordInput: + // TODO: Handle this case. + throw UnimplementedError(); + } + } + + static Future registrationDetailsPanelLooksAsExpected( + PatrolTester $, RegistrationState getStarted,) async { + expect( + detailsPartGetStartedTitle($), + T.get('Welcome to Catalyst'), + ); + expect( + detailsPartGetStartedBody($), isNotEmpty, + ); + expect( + detailsPartGetStartedQuestionText($), + T.get('What do you want to do?'), + ); + expect( + await detailsPartGetStartedCreateNewBtn($), + findsOneWidget, + ); + expect( + await detailsPartGetStartedRecoverBtn($), + findsOneWidget, + ); + expect( + $(CommonPage.dialogCloseButton), + findsOneWidget, + ); + } } diff --git a/catalyst_voices/apps/voices/integration_test/types/registration_state.dart b/catalyst_voices/apps/voices/integration_test/types/registration_state.dart new file mode 100644 index 00000000000..c40c7ddd5b6 --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/types/registration_state.dart @@ -0,0 +1,15 @@ +enum RegistrationState { + checkYourKeychain, + createKeychain, + getStarted, + keychainCreated, + keychainRestoreInfo, + keychainRestoreInput, + keychainRestoreStart, + keychainRestoreSuccess, + mnemonicInput, + mnemonicVerified, + mnemonicWritedown, + passwordInfo, + passwordInput; +} diff --git a/catalyst_voices/apps/voices/integration_test/utils/translations_utils.dart b/catalyst_voices/apps/voices/integration_test/utils/translations_utils.dart new file mode 100644 index 00000000000..f3064792d01 --- /dev/null +++ b/catalyst_voices/apps/voices/integration_test/utils/translations_utils.dart @@ -0,0 +1,8 @@ +//wrapper that we should adapt to read actual i18n translations we use in the app +//it will also support different locales once we have it +//right now this class is here so we can easily replace this implementation and know where +class T { + static String get(String key, {String? locale}) { + return key; + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/registration/get_started/get_started_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/get_started/get_started_panel.dart index 7add0d58cd5..cd5a83ad0c6 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/get_started/get_started_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/get_started/get_started_panel.dart @@ -28,6 +28,7 @@ class GetStartedPanel extends StatelessWidget { ), const SizedBox(height: 32), Text( + key: const Key('GetStartedQuestion'), context.l10n.accountCreationGetStatedWhatNext, style: theme.textTheme.titleSmall?.copyWith( color: theme.colors.textOnPrimaryLevel0, @@ -40,7 +41,7 @@ class GetStartedPanel extends StatelessWidget { children: CreateAccountType.values .map((type) { return RegistrationTile( - key: ValueKey(type), + key: Key(type.toString()), icon: type._icon, title: type._getTitle(context.l10n), subtitle: type._getSubtitle(context.l10n), diff --git a/catalyst_voices/apps/voices/lib/pages/registration/registration_details_panel.dart b/catalyst_voices/apps/voices/lib/pages/registration/registration_details_panel.dart index 6fe0c6023bf..d7a29b8a769 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/registration_details_panel.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/registration_details_panel.dart @@ -16,6 +16,7 @@ class RegistrationDetailsPanel extends StatelessWidget { @override Widget build(BuildContext context) { return BlocSelector( + key: const Key('RegistrationDetailsPanel'), selector: (state) => state.step, builder: (context, state) { return switch (state) { diff --git a/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_stage_message.dart b/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_stage_message.dart index 52cc5f6e92f..ccdf1d2fe45 100644 --- a/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_stage_message.dart +++ b/catalyst_voices/apps/voices/lib/pages/registration/widgets/registration_stage_message.dart @@ -25,11 +25,13 @@ class RegistrationStageMessage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ DefaultTextStyle( + key: const Key('RegistrationDetailsTitle'), style: theme.textTheme.titleMedium!.copyWith(color: textColor), child: title, ), SizedBox(height: spacing), DefaultTextStyle( + key: const Key('RegistrationDetailsBody'), style: theme.textTheme.bodyMedium!.copyWith(color: textColor), child: subtitle, ), diff --git a/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart b/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart index c3677d45f03..1fb74349024 100644 --- a/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart +++ b/catalyst_voices/apps/voices/lib/widgets/modals/voices_desktop_dialog.dart @@ -142,6 +142,7 @@ class _DialogCloseButton extends StatelessWidget { ); return Align( + key: const Key('DialogCloseButton'), alignment: Alignment.topRight, child: IconButtonTheme( data: const IconButtonThemeData(style: buttonStyle), From f8944442821702c5b8694869cf4348489efc23c8 Mon Sep 17 00:00:00 2001 From: januszjanus Date: Tue, 24 Dec 2024 15:22:30 +0100 Subject: [PATCH 3/3] dic fix --- .config/dictionaries/project.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/dictionaries/project.dic b/.config/dictionaries/project.dic index 2f33d343d5e..1b2bb638c44 100644 --- a/.config/dictionaries/project.dic +++ b/.config/dictionaries/project.dic @@ -322,6 +322,7 @@ Wireframes Wmissing Wnullable Woverlength +Writedown xcassets xcconfig xcfilelist