diff --git a/mobile-app/lib/shared/constants/e2e_keys.dart b/mobile-app/lib/shared/constants/e2e_keys.dart index d626eb36..e8b0ab04 100644 --- a/mobile-app/lib/shared/constants/e2e_keys.dart +++ b/mobile-app/lib/shared/constants/e2e_keys.dart @@ -22,8 +22,21 @@ class E2EKeys { static const String homeScreen = 'home_screen'; static const String homeSendButton = 'home_send_button'; + static const String homeSettingsButton = 'home_settings_button'; static const String homePendingSendActivityItem = 'home_pending_send_activity_item'; + static const String settingsScreen = 'settings_screen'; + static const String settingsWalletMenuRow = 'settings_wallet_menu_row'; + + static const String walletSettingsScreen = 'wallet_settings_screen'; + static const String walletSettingsRecoveryPhraseRow = 'wallet_settings_recovery_phrase_row'; + + static const String recoveryPhraseConfirmScreen = 'recovery_phrase_confirm_screen'; + static const String recoveryPhraseConfirmContinueButton = 'recovery_phrase_confirm_continue_button'; + static const String recoveryPhraseScreen = 'recovery_phrase_screen'; + static const String recoveryPhraseRevealArea = 'recovery_phrase_reveal_area'; + static const String recoveryPhraseRevealed = 'recovery_phrase_revealed'; + static const String sendSelectRecipientScreen = 'send_select_recipient_screen'; static const String sendRecipientField = 'send_recipient_field'; static const String sendContinueButton = 'send_continue_button'; diff --git a/mobile-app/lib/v2/components/recovery_phrase_body.dart b/mobile-app/lib/v2/components/recovery_phrase_body.dart index 45d142cf..615e3f69 100644 --- a/mobile-app/lib/v2/components/recovery_phrase_body.dart +++ b/mobile-app/lib/v2/components/recovery_phrase_body.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:resonance_network_wallet/features/components/mnemonic_grid.dart'; import 'package:resonance_network_wallet/l10n/app_localizations.dart'; import 'package:resonance_network_wallet/providers/l10n_provider.dart'; +import 'package:resonance_network_wallet/shared/constants/e2e_keys.dart'; import 'package:resonance_network_wallet/shared/extensions/clipboard_extensions.dart'; import 'package:resonance_network_wallet/v2/components/loader.dart'; import 'package:resonance_network_wallet/v2/components/quantus_button.dart'; @@ -87,6 +88,7 @@ class _RecoveryPhraseBodyState extends ConsumerState { Widget _grid(AppLocalizations l10n, AppColorsV2 colors, AppTextTheme text) { return GestureDetector( + key: const Key(E2EKeys.recoveryPhraseRevealArea), onTap: _toggleRevealed, behavior: HitTestBehavior.opaque, child: Column( @@ -101,15 +103,22 @@ class _RecoveryPhraseBodyState extends ConsumerState { ), if (_isRevealed) ...[ const SizedBox(height: 16), - _tapHint(Icons.visibility_off_outlined, l10n.recoveryPhraseBodyTapToHide, colors.textSecondary, text), + _tapHint( + Icons.visibility_off_outlined, + l10n.recoveryPhraseBodyTapToHide, + colors.textSecondary, + text, + key: const Key(E2EKeys.recoveryPhraseRevealed), + ), ], ], ), ); } - Widget _tapHint(IconData icon, String label, Color color, AppTextTheme text) { + Widget _tapHint(IconData icon, String label, Color color, AppTextTheme text, {Key? key}) { return Row( + key: key, mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 18, color: color), diff --git a/mobile-app/lib/v2/screens/home/home_screen.dart b/mobile-app/lib/v2/screens/home/home_screen.dart index 2f4f18f5..bb95ab41 100644 --- a/mobile-app/lib/v2/screens/home/home_screen.dart +++ b/mobile-app/lib/v2/screens/home/home_screen.dart @@ -277,6 +277,7 @@ class _HomeScreenState extends ConsumerState { ), const SizedBox(width: 12), QuantusIconButton.circular( + key: const Key(E2EKeys.homeSettingsButton), style: IconButtonStyle.glass, icon: Icons.settings_outlined, onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SettingsScreenV2())), diff --git a/mobile-app/lib/v2/screens/settings/recovery_phrase_confirmation_screen.dart b/mobile-app/lib/v2/screens/settings/recovery_phrase_confirmation_screen.dart index cfa6b1cb..c88d1840 100644 --- a/mobile-app/lib/v2/screens/settings/recovery_phrase_confirmation_screen.dart +++ b/mobile-app/lib/v2/screens/settings/recovery_phrase_confirmation_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:resonance_network_wallet/providers/l10n_provider.dart'; import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/services/local_auth_service.dart'; +import 'package:resonance_network_wallet/shared/constants/e2e_keys.dart'; import 'package:resonance_network_wallet/shared/extensions/toaster_extensions.dart'; import 'package:resonance_network_wallet/v2/screens/settings/recovery_phrase_screen.dart'; import 'package:resonance_network_wallet/v2/screens/settings/settings_caution_scaffold.dart'; @@ -46,13 +47,17 @@ class _RecoveryPhraseConfirmationScreenState extends ConsumerState { Widget build(BuildContext context) { final l10n = ref.watch(l10nProvider); - return RecoveryPhraseBody( - appBarTitle: l10n.settingsRecoveryPhraseTitle, - words: _words, - primaryButtonLabel: l10n.settingsRecoveryPhraseDone, - onPrimary: () => Navigator.of(context).pop(), - onPhraseExposed: _onPhraseExposed, + return KeyedSubtree( + key: const Key(E2EKeys.recoveryPhraseScreen), + child: RecoveryPhraseBody( + appBarTitle: l10n.settingsRecoveryPhraseTitle, + words: _words, + primaryButtonLabel: l10n.settingsRecoveryPhraseDone, + onPrimary: () => Navigator.of(context).pop(), + onPhraseExposed: _onPhraseExposed, + ), ); } } diff --git a/mobile-app/lib/v2/screens/settings/settings_caution_scaffold.dart b/mobile-app/lib/v2/screens/settings/settings_caution_scaffold.dart index 977392ae..c561ca9e 100644 --- a/mobile-app/lib/v2/screens/settings/settings_caution_scaffold.dart +++ b/mobile-app/lib/v2/screens/settings/settings_caution_scaffold.dart @@ -48,6 +48,7 @@ class SettingsCautionScaffold extends StatelessWidget { final bool continueButtonLoading; final String? secondaryLabel; final VoidCallback? onSecondary; + final Key? continueButtonKey; const SettingsCautionScaffold({ super.key, @@ -61,6 +62,7 @@ class SettingsCautionScaffold extends StatelessWidget { this.continueButtonLoading = false, this.secondaryLabel, this.onSecondary, + this.continueButtonKey, }); @override @@ -96,6 +98,7 @@ class SettingsCautionScaffold extends StatelessWidget { continueButtonLoading: continueButtonLoading, secondaryLabel: secondaryLabel, onSecondary: onSecondary, + continueButtonKey: continueButtonKey, ), ); } @@ -111,6 +114,7 @@ class _SettingsCautionBottom extends StatelessWidget { required this.continueButtonLoading, required this.secondaryLabel, required this.onSecondary, + this.continueButtonKey, }); final String? checkboxLabel; @@ -121,6 +125,7 @@ class _SettingsCautionBottom extends StatelessWidget { final bool continueButtonLoading; final String? secondaryLabel; final VoidCallback? onSecondary; + final Key? continueButtonKey; @override Widget build(BuildContext context) { @@ -133,6 +138,7 @@ class _SettingsCautionBottom extends StatelessWidget { const SizedBox(height: 32), ], QuantusButton.simple( + key: continueButtonKey, label: continueLabel, onTap: onContinue, variant: ButtonVariant.primary, diff --git a/mobile-app/lib/v2/screens/settings/settings_screen.dart b/mobile-app/lib/v2/screens/settings/settings_screen.dart index f93172c8..12a7ae28 100644 --- a/mobile-app/lib/v2/screens/settings/settings_screen.dart +++ b/mobile-app/lib/v2/screens/settings/settings_screen.dart @@ -5,6 +5,7 @@ import 'package:resonance_network_wallet/generated/version.g.dart'; import 'package:resonance_network_wallet/l10n/app_localizations.dart'; import 'package:resonance_network_wallet/providers/l10n_provider.dart'; import 'package:resonance_network_wallet/providers/mining_rewards_provider.dart'; +import 'package:resonance_network_wallet/shared/constants/e2e_keys.dart'; import 'package:resonance_network_wallet/v2/components/scaffold_base.dart'; import 'package:resonance_network_wallet/v2/components/v2_app_bar.dart'; import 'package:resonance_network_wallet/v2/screens/settings/about_quantus_screen.dart'; @@ -35,6 +36,7 @@ class _SettingsScreenV2State extends ConsumerState { final entries = _settingsHubItems(colors, l10n); return ScaffoldBase( + key: const Key(E2EKeys.settingsScreen), appBar: V2AppBar(title: l10n.settingsTitle), mainContent: ListView( children: [ @@ -64,6 +66,7 @@ class _SettingsScreenV2State extends ConsumerState { } Widget _buildTappableRow(_SettingsHubItem item, {required Widget trailing, String? subtitle}) => SettingsTappableRow( + key: item.rowKey, leading: item.leading, title: item.title, subtitle: subtitle ?? item.subtitle, @@ -79,6 +82,7 @@ class _SettingsHubItem { required this.subtitle, required this.page, this.isMiningRewards = false, + this.rowKey, }); final Widget leading; @@ -86,6 +90,7 @@ class _SettingsHubItem { final String subtitle; final Widget page; final bool isMiningRewards; + final Key? rowKey; } List<_SettingsHubItem> _settingsHubItems(AppColorsV2 colors, AppLocalizations l10n) { @@ -95,6 +100,7 @@ List<_SettingsHubItem> _settingsHubItems(AppColorsV2 colors, AppLocalizations l1 title: l10n.settingsWalletTitle, subtitle: l10n.settingsWalletSubtitle, page: const WalletSettingsScreenV2(), + rowKey: const Key(E2EKeys.settingsWalletMenuRow), ), _SettingsHubItem( leading: _settingsHubIcon(colors, icon: Icons.tune), diff --git a/mobile-app/lib/v2/screens/settings/wallet_settings_screen.dart b/mobile-app/lib/v2/screens/settings/wallet_settings_screen.dart index 2b65a698..fa38529b 100644 --- a/mobile-app/lib/v2/screens/settings/wallet_settings_screen.dart +++ b/mobile-app/lib/v2/screens/settings/wallet_settings_screen.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/providers/account_providers.dart'; import 'package:resonance_network_wallet/providers/l10n_provider.dart'; +import 'package:resonance_network_wallet/shared/constants/e2e_keys.dart'; import 'package:resonance_network_wallet/shared/extensions/toaster_extensions.dart'; import 'package:resonance_network_wallet/shared/utils/account_utils.dart'; import 'package:resonance_network_wallet/v2/components/loader.dart'; @@ -57,6 +58,7 @@ class _WalletSettingsScreenV2State extends ConsumerState final accountsAsync = ref.watch(accountsProvider); return ScaffoldBase( + key: const Key(E2EKeys.walletSettingsScreen), appBar: V2AppBar(title: l10n.settingsWalletTitle), mainContent: accountsAsync.when( loading: () => const Center(child: Loader()), @@ -66,6 +68,7 @@ class _WalletSettingsScreenV2State extends ConsumerState data: (accounts) => ListView( children: [ SettingsTappableRow( + key: const Key(E2EKeys.walletSettingsRecoveryPhraseRow), title: l10n.settingsWalletRecoveryPhrase, subtitle: l10n.settingsWalletRecoveryPhraseSubtitle, onTap: () => _navigateToRecoveryPhrase(accounts), diff --git a/mobile-app/patrol_test/flows/create_wallet_test.dart b/mobile-app/patrol_test/flows/create_wallet_test.dart index 7b659a17..18e33f7c 100644 --- a/mobile-app/patrol_test/flows/create_wallet_test.dart +++ b/mobile-app/patrol_test/flows/create_wallet_test.dart @@ -1,23 +1,11 @@ -import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/patrol.dart'; import '../support/app_launcher.dart'; -import '../support/patrol_timeouts.dart'; -import '../support/selectors.dart'; +import '../support/wallet_flows.dart'; void main() { patrolTest('create new wallet from welcome lands on home', ($) async { await AppLauncher.launchFresh($); - - expect($(Selectors.welcomeScreen), findsOneWidget); - - await $(Selectors.welcomeCreateWalletButton).tap(); - - await $(Selectors.accountReadyDoneButton).waitUntilVisible(timeout: PatrolTimeouts.network); - expect($('Account 1'), findsOneWidget); - - await $(Selectors.accountReadyDoneButton).tap(); - - await $(Selectors.homeScreen).waitUntilVisible(timeout: PatrolTimeouts.network); + await WalletFlows.createFromWelcome($); }); } diff --git a/mobile-app/patrol_test/flows/view_recovery_phrase_test.dart b/mobile-app/patrol_test/flows/view_recovery_phrase_test.dart new file mode 100644 index 00000000..9eecbb82 --- /dev/null +++ b/mobile-app/patrol_test/flows/view_recovery_phrase_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:patrol/patrol.dart'; + +import '../support/app_launcher.dart'; +import '../support/patrol_timeouts.dart'; +import '../support/selectors.dart'; +import '../support/wallet_flows.dart'; + +void main() { + patrolTest('create wallet and view recovery phrase from settings', ($) async { + await AppLauncher.launchFresh($); + await WalletFlows.createFromWelcome($); + + await $(Selectors.homeSettingsButton).tap(); + await $(Selectors.settingsScreen).waitUntilVisible(timeout: PatrolTimeouts.visible); + + await $(Selectors.settingsWalletMenuRow).tap(); + await $(Selectors.walletSettingsScreen).waitUntilVisible(timeout: PatrolTimeouts.visible); + + await $(Selectors.walletSettingsRecoveryPhraseRow).tap(); + await $(Selectors.recoveryPhraseConfirmScreen).waitUntilVisible(timeout: PatrolTimeouts.visible); + + await $(Selectors.recoveryPhraseConfirmContinueButton).tap(); + await $(Selectors.recoveryPhraseScreen).waitUntilVisible(timeout: PatrolTimeouts.visible); + + await $(Selectors.recoveryPhraseRevealArea).waitUntilVisible(timeout: PatrolTimeouts.network); + await $(Selectors.recoveryPhraseRevealArea).tap(); + + expect($(Selectors.recoveryPhraseRevealed), findsOneWidget); + }); +} diff --git a/mobile-app/patrol_test/support/selectors.dart b/mobile-app/patrol_test/support/selectors.dart index 8d627fde..5e50647e 100644 --- a/mobile-app/patrol_test/support/selectors.dart +++ b/mobile-app/patrol_test/support/selectors.dart @@ -21,8 +21,21 @@ class Selectors { static const Key homeScreen = Key(E2EKeys.homeScreen); static const Key homeSendButton = Key(E2EKeys.homeSendButton); + static const Key homeSettingsButton = Key(E2EKeys.homeSettingsButton); static const Key homePendingSendActivityItem = Key(E2EKeys.homePendingSendActivityItem); + static const Key settingsScreen = Key(E2EKeys.settingsScreen); + static const Key settingsWalletMenuRow = Key(E2EKeys.settingsWalletMenuRow); + + static const Key walletSettingsScreen = Key(E2EKeys.walletSettingsScreen); + static const Key walletSettingsRecoveryPhraseRow = Key(E2EKeys.walletSettingsRecoveryPhraseRow); + + static const Key recoveryPhraseConfirmScreen = Key(E2EKeys.recoveryPhraseConfirmScreen); + static const Key recoveryPhraseConfirmContinueButton = Key(E2EKeys.recoveryPhraseConfirmContinueButton); + static const Key recoveryPhraseScreen = Key(E2EKeys.recoveryPhraseScreen); + static const Key recoveryPhraseRevealArea = Key(E2EKeys.recoveryPhraseRevealArea); + static const Key recoveryPhraseRevealed = Key(E2EKeys.recoveryPhraseRevealed); + static const Key sendSelectRecipientScreen = Key(E2EKeys.sendSelectRecipientScreen); static const Key sendRecipientField = Key(E2EKeys.sendRecipientField); static const Key sendContinueButton = Key(E2EKeys.sendContinueButton); diff --git a/mobile-app/patrol_test/support/wallet_flows.dart b/mobile-app/patrol_test/support/wallet_flows.dart index d01ec550..e12e807c 100644 --- a/mobile-app/patrol_test/support/wallet_flows.dart +++ b/mobile-app/patrol_test/support/wallet_flows.dart @@ -9,6 +9,18 @@ import 'test_env.dart'; class WalletFlows { WalletFlows._(); + /// Creates a new wallet from the welcome screen and waits for home. + static Future createFromWelcome(PatrolIntegrationTester $) async { + expect($(Selectors.welcomeScreen), findsOneWidget); + + await $(Selectors.welcomeCreateWalletButton).tap(); + + await $(Selectors.accountReadyDoneButton).waitUntilVisible(timeout: PatrolTimeouts.network); + await $(Selectors.accountReadyDoneButton).tap(); + + await $(Selectors.homeScreen).waitUntilVisible(timeout: PatrolTimeouts.network); + } + /// Imports a wallet from the welcome screen and waits for home. static Future importFromWelcome(PatrolIntegrationTester $) async { expect($(Selectors.welcomeScreen), findsOneWidget);