From 989ecbb2de00fe7607b6dc104b12331d6696d6f9 Mon Sep 17 00:00:00 2001 From: Blazebrain Date: Wed, 31 Jul 2024 14:13:23 +0100 Subject: [PATCH] test: Test for Send flow scenario and minor restructuring for test folders and files --- .../workflows/automated_integration_test.yml | 3 +- cw_bitcoin/pubspec.lock | 12 +- cw_core/pubspec.lock | 4 +- cw_haven/pubspec.lock | 20 +- cw_monero/pubspec.lock | 20 +- cw_nano/pubspec.lock | 4 +- cw_wownero/pubspec.lock | 20 +- integration_test/app_test.dart | 140 ------- ...mon_checks.dart => common_test_cases.dart} | 8 +- .../components/common_test_constants.dart | 13 + .../components/common_test_flows.dart | 101 +++++ integration_test/funds_related_tests.dart | 93 +---- integration_test/robots/auth_page_robot.dart | 2 +- .../robots/dashboard_page_robot.dart | 4 +- .../robots/disclaimer_page_robot.dart | 13 +- .../robots/exchange_confirm_page_robot.dart | 4 +- .../robots/exchange_page_robot.dart | 4 +- .../robots/exchange_trade_page_robot.dart | 11 +- .../robots/new_wallet_type_page_robot.dart | 2 +- .../robots/pin_code_widget_robot.dart | 3 +- .../restore_from_seed_or_key_robot.dart | 22 +- .../robots/restore_options_page_robot.dart | 5 +- integration_test/robots/send_page_robot.dart | 349 ++++++++++++++++++ .../robots/setup_pin_code_robot.dart | 2 +- .../robots/welcome_page_robot.dart | 4 +- .../test_suites/exchange_flow_test.dart | 59 +++ .../test_suites/send_flow_test.dart | 41 ++ lib/main.dart | 9 +- .../exchange/widgets/exchange_card.dart | 2 +- .../wallet_restore_from_keys_form.dart | 3 + .../wallet_restore_from_seed_form.dart | 13 +- lib/src/screens/send/send_page.dart | 45 ++- lib/src/screens/send/widgets/send_card.dart | 9 + lib/src/widgets/address_text_field.dart | 45 +-- lib/src/widgets/alert_with_two_actions.dart | 15 + lib/src/widgets/base_alert_dialog.dart | 3 + lib/src/widgets/picker.dart | 27 +- lib/src/widgets/seed_widget.dart | 19 +- 38 files changed, 787 insertions(+), 366 deletions(-) delete mode 100644 integration_test/app_test.dart rename integration_test/components/{common_checks.dart => common_test_cases.dart} (88%) create mode 100644 integration_test/components/common_test_constants.dart create mode 100644 integration_test/components/common_test_flows.dart create mode 100644 integration_test/robots/send_page_robot.dart create mode 100644 integration_test/test_suites/exchange_flow_test.dart create mode 100644 integration_test/test_suites/send_flow_test.dart diff --git a/.github/workflows/automated_integration_test.yml b/.github/workflows/automated_integration_test.yml index e2d0fb89c..1c8e2f423 100644 --- a/.github/workflows/automated_integration_test.yml +++ b/.github/workflows/automated_integration_test.yml @@ -1,3 +1,4 @@ +# Commenting out for now, will bring back after I get the CI flow for Automated Testing stable name: Automated Integration Test on: @@ -215,7 +216,7 @@ jobs: script: | cd /opt/android/cake_wallet flutter pub get - flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart + flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites if [ $? -ne 0 ]; then echo "Tests failed" exit 1 diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 15f7cdb43..b3a9cc793 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -305,18 +305,18 @@ packages: dependency: transitive description: name: ffigen - sha256: d3e76c2ad48a4e7f93a29a162006f00eba46ce7c08194a77bb5c5e97d1b5ff0a + sha256: "3e12e80ccb6539bb3917217bb6f32709220efb737de0d0fa8736da0b7cb507da" url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "12.0.0" file: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: @@ -793,8 +793,8 @@ packages: dependency: "direct main" description: path: "." - ref: "sp_v2.0.0" - resolved-ref: "62c152b9086cd968019128845371072f7e1168de" + ref: "sp_v2.1.0" + resolved-ref: b4b8fbd8d832198d3cee853feb427c4ec482dd7d url: "https://github.com/cake-tech/sp_scanner" source: git version: "0.0.1" diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index 518c71b94..53946925c 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -205,10 +205,10 @@ packages: dependency: "direct main" description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index b8583d219..667eb1744 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -212,10 +212,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: @@ -254,10 +254,10 @@ packages: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" graphs: dependency: transitive description: @@ -514,14 +514,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" pub_semver: dependency: transitive description: @@ -707,10 +699,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.4" yaml: dependency: transitive description: diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index 011fed169..8a522875f 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -212,10 +212,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: @@ -254,10 +254,10 @@ packages: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" graphs: dependency: transitive description: @@ -555,14 +555,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" pub_semver: dependency: transitive description: @@ -748,10 +740,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.4" yaml: dependency: transitive description: diff --git a/cw_nano/pubspec.lock b/cw_nano/pubspec.lock index 70f2f6f0b..f2c9f3ccf 100644 --- a/cw_nano/pubspec.lock +++ b/cw_nano/pubspec.lock @@ -252,10 +252,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: diff --git a/cw_wownero/pubspec.lock b/cw_wownero/pubspec.lock index 011fed169..8a522875f 100644 --- a/cw_wownero/pubspec.lock +++ b/cw_wownero/pubspec.lock @@ -212,10 +212,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: @@ -254,10 +254,10 @@ packages: dependency: transitive description: name: glob - sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c" + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" graphs: dependency: transitive description: @@ -555,14 +555,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - process: - dependency: transitive - description: - name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" - url: "https://pub.dev" - source: hosted - version: "4.2.4" pub_semver: dependency: transitive description: @@ -748,10 +740,10 @@ packages: dependency: transitive description: name: xdg_directories - sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86 + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "0.2.0+3" + version: "1.0.4" yaml: dependency: transitive description: diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart deleted file mode 100644 index 0d8bdb386..000000000 --- a/integration_test/app_test.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'package:cake_wallet/main.dart' as app; -import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; - -import 'robots/auth_page_robot.dart'; -import 'robots/dashboard_page_robot.dart'; -import 'robots/disclaimer_page_robot.dart'; -import 'robots/exchange_confirm_page_robot.dart'; -import 'robots/exchange_page_robot.dart'; -import 'robots/exchange_trade_page_robot.dart'; -import 'robots/new_wallet_type_page_robot.dart'; -import 'robots/restore_from_seed_or_key_robot.dart'; -import 'robots/restore_options_page_robot.dart'; -import 'robots/setup_pin_code_robot.dart'; -import 'robots/welcome_page_robot.dart'; -import 'package:cake_wallet/.secrets.g.dart' as secrets; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - DisclaimerPageRobot disclaimerPageRobot; - WelcomePageRobot welcomePageRobot; - SetupPinCodeRobot setupPinCodeRobot; - RestoreOptionsPageRobot restoreOptionsPageRobot; - NewWalletTypePageRobot newWalletTypePageRobot; - RestoreFromSeedOrKeysPageRobot restoreFromSeedOrKeysPageRobot; - DashboardPageRobot dashboardPageRobot; - ExchangePageRobot exchangePageRobot; - ExchangeConfirmPageRobot exchangeConfirmPageRobot; - AuthPageRobot authPageRobot; - ExchangeTradePageRobot exchangeTradePageRobot; - - group('Startup Test', () { - testWidgets( - 'Test for Exchange flow using Restore Wallet, -Up to the point where the sending is to be triggered', - (tester) async { - authPageRobot = AuthPageRobot(tester); - welcomePageRobot = WelcomePageRobot(tester); - exchangePageRobot = ExchangePageRobot(tester); - setupPinCodeRobot = SetupPinCodeRobot(tester); - dashboardPageRobot = DashboardPageRobot(tester); - disclaimerPageRobot = DisclaimerPageRobot(tester); - exchangeTradePageRobot = ExchangeTradePageRobot(tester); - newWalletTypePageRobot = NewWalletTypePageRobot(tester); - restoreOptionsPageRobot = RestoreOptionsPageRobot(tester); - exchangeConfirmPageRobot = ExchangeConfirmPageRobot(tester); - restoreFromSeedOrKeysPageRobot = RestoreFromSeedOrKeysPageRobot(tester); - - final pin = [0, 8, 0, 1]; - - // String testAmount = '0.08'; - String testAmount = '8'; - CryptoCurrency testReceiveCurrency = CryptoCurrency.sol; - CryptoCurrency testDepositCurrency = CryptoCurrency.usdtSol; - - WalletType testWalletType = WalletType.solana; - String testWalletName = 'Integrated Testing Wallet'; - String testWalletAddress = 'An2Y2fsUYKfYvN1zF89GAqR1e6GUMBg3qA83Y5ZWDf8L'; - - await app.main(); - await tester.pumpAndSettle(); - - // --------- Disclaimer Page ------------ - // Tap checkbox to accept disclaimer - await disclaimerPageRobot.tapDisclaimerCheckbox(); - - // Tap accept button - await disclaimerPageRobot.tapAcceptButton(); - - // --------- Welcome Page --------------- - await welcomePageRobot.navigateToRestoreWalletPage(); - - // ----------- Restore Options Page ----------- - // Route to restore from seeds page to continue flow - await restoreOptionsPageRobot.navigateToRestoreFromSeedsPage(); - - // ----------- SetupPinCode Page ------------- - // Confirm initial defaults - Widgets to be displayed etc - await setupPinCodeRobot.isSetupPinCodePage(); - - await setupPinCodeRobot.enterPinCode(pin, true); - await setupPinCodeRobot.enterPinCode(pin, false); - await setupPinCodeRobot.tapSuccessButton(); - - // ----------- NewWalletType Page ------------- - // Confirm scroll behaviour works properly - await newWalletTypePageRobot.findParticularWalletTypeInScrollableList(WalletType.solana); - - // Select a wallet and route to next page - await newWalletTypePageRobot.selectWalletType(testWalletType); - await newWalletTypePageRobot.onNextButtonPressed(); - - // ----------- RestoreFromSeedOrKeys Page ------------- - await restoreFromSeedOrKeysPageRobot.enterWalletNameText(testWalletName); - await restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore(secrets.seeds); - await restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed(); - - // ----------- RestoreFromSeedOrKeys Page ------------- - await dashboardPageRobot.navigateToExchangePage(); - - // ----------- Exchange Page ------------- - await exchangePageRobot.isExchangePage(); - exchangePageRobot.hasResetButton(); - await exchangePageRobot.displayBothExchangeCards(); - exchangePageRobot.confirmRightComponentsDisplayOnDepositExchangeCards(); - exchangePageRobot.confirmRightComponentsDisplayOnReceiveExchangeCards(); - - await exchangePageRobot.selectDepositCurrency(testDepositCurrency); - await exchangePageRobot.selectReceiveCurrency(testReceiveCurrency); - - await exchangePageRobot.enterDepositAmount(testAmount); - await exchangePageRobot.enterDepositRefundAddress(depositAddress: testWalletAddress); - - await exchangePageRobot.enterReceiveAddress(testWalletAddress); - - await exchangePageRobot.onExchangeButtonPressed(); - - await exchangePageRobot.handleErrors(testAmount); - - final onAuthPage = authPageRobot.onAuthPage(); - if (onAuthPage) { - await authPageRobot.enterPinCode(pin, false); - } - - // ----------- Exchange Confirm Page ------------- - await exchangeConfirmPageRobot.isExchangeConfirmPage(); - - exchangeConfirmPageRobot.confirmComponentsOfTradeDisplayProperly(); - await exchangeConfirmPageRobot.confirmCopyTradeIdToClipBoardWorksProperly(); - await exchangeConfirmPageRobot.onSavedTradeIdButtonPressed(); - - // ----------- Exchange Trade Page ------------- - await exchangeTradePageRobot.isExchangeTradePage(); - exchangeTradePageRobot.hasInformationDialog(); - await exchangeTradePageRobot.onGotItButtonPressed(); - }); - }); -} diff --git a/integration_test/components/common_checks.dart b/integration_test/components/common_test_cases.dart similarity index 88% rename from integration_test/components/common_checks.dart rename to integration_test/components/common_test_cases.dart index eebad62e4..2e2991804 100644 --- a/integration_test/components/common_checks.dart +++ b/integration_test/components/common_test_cases.dart @@ -10,15 +10,15 @@ class CommonTestCases { hasType(); } - Future tapItemByKey(String key) async { + Future tapItemByKey(String key, {bool shouldPumpAndSettle = true}) async { final widget = find.byKey(ValueKey(key)); await tester.tap(widget); - await tester.pumpAndSettle(); + shouldPumpAndSettle ? await tester.pumpAndSettle() : await tester.pump(); } - Future tapItemByFinder(Finder finder) async { + Future tapItemByFinder(Finder finder, {bool shouldPumpAndSettle = true}) async { await tester.tap(finder); - await tester.pumpAndSettle(); + shouldPumpAndSettle ? await tester.pumpAndSettle() : await tester.pump(); } void hasText(String text, {bool hasWidget = true}) { diff --git a/integration_test/components/common_test_constants.dart b/integration_test/components/common_test_constants.dart new file mode 100644 index 000000000..d8381973e --- /dev/null +++ b/integration_test/components/common_test_constants.dart @@ -0,0 +1,13 @@ +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/wallet_type.dart'; + +class CommonTestConstants { + static final pin = [0, 8, 0, 1]; + static final String sendTestAmount = '0.00008'; + static final String exchangeTestAmount = '8'; + static final WalletType testWalletType = WalletType.solana; + static final String testWalletName = 'Integrated Testing Wallet'; + static final CryptoCurrency testReceiveCurrency = CryptoCurrency.sol; + static final CryptoCurrency testDepositCurrency = CryptoCurrency.usdtSol; + static final String testWalletAddress = 'An2Y2fsUYKfYvN1zF89GAqR1e6GUMBg3qA83Y5ZWDf8L'; +} diff --git a/integration_test/components/common_test_flows.dart b/integration_test/components/common_test_flows.dart new file mode 100644 index 000000000..a7eb1b17e --- /dev/null +++ b/integration_test/components/common_test_flows.dart @@ -0,0 +1,101 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cake_wallet/main.dart' as app; + +import '../robots/disclaimer_page_robot.dart'; +import '../robots/new_wallet_type_page_robot.dart'; +import '../robots/restore_from_seed_or_key_robot.dart'; +import '../robots/restore_options_page_robot.dart'; +import '../robots/setup_pin_code_robot.dart'; +import '../robots/welcome_page_robot.dart'; +import 'common_test_cases.dart'; +import 'common_test_constants.dart'; + +class CommonTestFlows { + CommonTestFlows(this._tester) + : _commonTestCases = CommonTestCases(_tester), + _welcomePageRobot = WelcomePageRobot(_tester), + _setupPinCodeRobot = SetupPinCodeRobot(_tester), + _disclaimerPageRobot = DisclaimerPageRobot(_tester), + _newWalletTypePageRobot = NewWalletTypePageRobot(_tester), + _restoreOptionsPageRobot = RestoreOptionsPageRobot(_tester), + _restoreFromSeedOrKeysPageRobot = RestoreFromSeedOrKeysPageRobot(_tester); + + final WidgetTester _tester; + final CommonTestCases _commonTestCases; + + final WelcomePageRobot _welcomePageRobot; + final SetupPinCodeRobot _setupPinCodeRobot; + final DisclaimerPageRobot _disclaimerPageRobot; + final NewWalletTypePageRobot _newWalletTypePageRobot; + final RestoreOptionsPageRobot _restoreOptionsPageRobot; + final RestoreFromSeedOrKeysPageRobot _restoreFromSeedOrKeysPageRobot; + + Future startAppFlow(Key key) async { + await app.main(topLevelKey: ValueKey('send_flow_test_app_key')); + + await _tester.pumpAndSettle(); + + // --------- Disclaimer Page ------------ + // Tap checkbox to accept disclaimer + await _disclaimerPageRobot.tapDisclaimerCheckbox(); + + // Tap accept button + await _disclaimerPageRobot.tapAcceptButton(); + } + + Future restoreWalletThroughSeedsFlow() async { + await _welcomeToRestoreFromSeedsPath(); + await _restoreFromSeeds(); + } + + Future restoreWalletThroughKeysFlow() async { + await _welcomeToRestoreFromSeedsPath(); + await _restoreFromKeys(); + } + + Future _welcomeToRestoreFromSeedsPath() async { + // --------- Welcome Page --------------- + await _welcomePageRobot.navigateToRestoreWalletPage(); + + // ----------- Restore Options Page ----------- + // Route to restore from seeds page to continue flow + await _restoreOptionsPageRobot.navigateToRestoreFromSeedsPage(); + + // ----------- SetupPinCode Page ------------- + // Confirm initial defaults - Widgets to be displayed etc + await _setupPinCodeRobot.isSetupPinCodePage(); + + await _setupPinCodeRobot.enterPinCode(CommonTestConstants.pin, true); + await _setupPinCodeRobot.enterPinCode(CommonTestConstants.pin, false); + await _setupPinCodeRobot.tapSuccessButton(); + + // ----------- NewWalletType Page ------------- + // Confirm scroll behaviour works properly + await _newWalletTypePageRobot + .findParticularWalletTypeInScrollableList(CommonTestConstants.testWalletType); + + // Select a wallet and route to next page + await _newWalletTypePageRobot.selectWalletType(CommonTestConstants.testWalletType); + await _newWalletTypePageRobot.onNextButtonPressed(); + } + + Future _restoreFromSeeds() async { + // ----------- RestoreFromSeedOrKeys Page ------------- + await _restoreFromSeedOrKeysPageRobot.enterWalletNameText(CommonTestConstants.testWalletName); + await _restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore(secrets.seeds); + await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed(); + } + + Future _restoreFromKeys() async { + await _commonTestCases.swipePage(); + await _commonTestCases.defaultSleepTime(); + + await _restoreFromSeedOrKeysPageRobot.enterWalletNameText(CommonTestConstants.testWalletName); + + await _restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore(''); + await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed(); + } +} diff --git a/integration_test/funds_related_tests.dart b/integration_test/funds_related_tests.dart index fb1cf7c40..9d97d47f8 100644 --- a/integration_test/funds_related_tests.dart +++ b/integration_test/funds_related_tests.dart @@ -1,100 +1,38 @@ -import 'package:cake_wallet/main.dart' as app; -import 'package:cw_core/crypto_currency.dart'; -import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'components/common_test_constants.dart'; +import 'components/common_test_flows.dart'; import 'robots/auth_page_robot.dart'; import 'robots/dashboard_page_robot.dart'; -import 'robots/disclaimer_page_robot.dart'; import 'robots/exchange_confirm_page_robot.dart'; import 'robots/exchange_page_robot.dart'; import 'robots/exchange_trade_page_robot.dart'; -import 'robots/new_wallet_type_page_robot.dart'; -import 'robots/restore_from_seed_or_key_robot.dart'; -import 'robots/restore_options_page_robot.dart'; -import 'robots/setup_pin_code_robot.dart'; -import 'robots/welcome_page_robot.dart'; -import 'package:cake_wallet/.secrets.g.dart' as secrets; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - DisclaimerPageRobot disclaimerPageRobot; - WelcomePageRobot welcomePageRobot; - SetupPinCodeRobot setupPinCodeRobot; - RestoreOptionsPageRobot restoreOptionsPageRobot; - NewWalletTypePageRobot newWalletTypePageRobot; - RestoreFromSeedOrKeysPageRobot restoreFromSeedOrKeysPageRobot; DashboardPageRobot dashboardPageRobot; ExchangePageRobot exchangePageRobot; ExchangeConfirmPageRobot exchangeConfirmPageRobot; AuthPageRobot authPageRobot; ExchangeTradePageRobot exchangeTradePageRobot; + CommonTestFlows commonTestFlows; group('Startup Test', () { testWidgets('Test for Exchange flow using Restore Wallet - Exchanging USDT(Sol) to SOL', (tester) async { authPageRobot = AuthPageRobot(tester); - welcomePageRobot = WelcomePageRobot(tester); exchangePageRobot = ExchangePageRobot(tester); - setupPinCodeRobot = SetupPinCodeRobot(tester); dashboardPageRobot = DashboardPageRobot(tester); - disclaimerPageRobot = DisclaimerPageRobot(tester); exchangeTradePageRobot = ExchangeTradePageRobot(tester); - newWalletTypePageRobot = NewWalletTypePageRobot(tester); - restoreOptionsPageRobot = RestoreOptionsPageRobot(tester); exchangeConfirmPageRobot = ExchangeConfirmPageRobot(tester); - restoreFromSeedOrKeysPageRobot = RestoreFromSeedOrKeysPageRobot(tester); + commonTestFlows = CommonTestFlows(tester); - final pin = [0, 8, 0, 1]; + await commonTestFlows.startAppFlow(ValueKey('funds_exchange_test_app_key')); - // String testAmount = '0.08'; - String testAmount = '8'; - CryptoCurrency testReceiveCurrency = CryptoCurrency.sol; - CryptoCurrency testDepositCurrency = CryptoCurrency.usdtSol; - - WalletType testWalletType = WalletType.solana; - String testWalletName = 'Integrated Testing Wallet'; - String testWalletAddress = 'An2Y2fsUYKfYvN1zF89GAqR1e6GUMBg3qA83Y5ZWDf8L'; - - await app.main(); - await tester.pumpAndSettle(); - - // --------- Disclaimer Page ------------ - // Tap checkbox to accept disclaimer - await disclaimerPageRobot.tapDisclaimerCheckbox(); - - // Tap accept button - await disclaimerPageRobot.tapAcceptButton(); - - // --------- Welcome Page --------------- - await welcomePageRobot.navigateToRestoreWalletPage(); - - // ----------- Restore Options Page ----------- - // Route to restore from seeds page to continue flow - await restoreOptionsPageRobot.navigateToRestoreFromSeedsPage(); - - // ----------- SetupPinCode Page ------------- - // Confirm initial defaults - Widgets to be displayed etc - await setupPinCodeRobot.isSetupPinCodePage(); - - await setupPinCodeRobot.enterPinCode(pin, true); - await setupPinCodeRobot.enterPinCode(pin, false); - await setupPinCodeRobot.tapSuccessButton(); - - // ----------- NewWalletType Page ------------- - // Confirm scroll behaviour works properly - await newWalletTypePageRobot.findParticularWalletTypeInScrollableList(WalletType.solana); - - // Select a wallet and route to next page - await newWalletTypePageRobot.selectWalletType(testWalletType); - await newWalletTypePageRobot.onNextButtonPressed(); - - // ----------- RestoreFromSeedOrKeys Page ------------- - await restoreFromSeedOrKeysPageRobot.enterWalletNameText(testWalletName); - await restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore(secrets.seeds); - await restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed(); + await commonTestFlows.restoreWalletThroughSeedsFlow(); // ----------- RestoreFromSeedOrKeys Page ------------- await dashboardPageRobot.navigateToExchangePage(); @@ -106,21 +44,22 @@ void main() { exchangePageRobot.confirmRightComponentsDisplayOnDepositExchangeCards(); exchangePageRobot.confirmRightComponentsDisplayOnReceiveExchangeCards(); - await exchangePageRobot.selectDepositCurrency(testDepositCurrency); - await exchangePageRobot.selectReceiveCurrency(testReceiveCurrency); + await exchangePageRobot.selectDepositCurrency(CommonTestConstants.testDepositCurrency); + await exchangePageRobot.selectReceiveCurrency(CommonTestConstants.testReceiveCurrency); - await exchangePageRobot.enterDepositAmount(testAmount); - await exchangePageRobot.enterDepositRefundAddress(depositAddress: testWalletAddress); + await exchangePageRobot.enterDepositAmount(CommonTestConstants.exchangeTestAmount); + await exchangePageRobot.enterDepositRefundAddress( + depositAddress: CommonTestConstants.testWalletAddress); - await exchangePageRobot.enterReceiveAddress(testWalletAddress); + await exchangePageRobot.enterReceiveAddress(CommonTestConstants.testWalletAddress); await exchangePageRobot.onExchangeButtonPressed(); - await exchangePageRobot.handleErrors(testAmount); + await exchangePageRobot.handleErrors(CommonTestConstants.exchangeTestAmount); final onAuthPage = authPageRobot.onAuthPage(); if (onAuthPage) { - await authPageRobot.enterPinCode(pin, false); + await authPageRobot.enterPinCode(CommonTestConstants.pin, false); } // ----------- Exchange Confirm Page ------------- @@ -137,7 +76,7 @@ void main() { await exchangeTradePageRobot.onConfirmSendingButtonPressed(); - await exchangeTradePageRobot.handleSendSuccessOrFailure(); + await exchangeTradePageRobot.handleConfirmSendResult(); await exchangeTradePageRobot.onSendButtonOnConfirmSendingDialogPressed(); }); diff --git a/integration_test/robots/auth_page_robot.dart b/integration_test/robots/auth_page_robot.dart index e45a82c0c..6358d4398 100644 --- a/integration_test/robots/auth_page_robot.dart +++ b/integration_test/robots/auth_page_robot.dart @@ -3,7 +3,7 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; import 'pin_code_widget_robot.dart'; class AuthPageRobot extends PinCodeWidgetRobot { diff --git a/integration_test/robots/dashboard_page_robot.dart b/integration_test/robots/dashboard_page_robot.dart index d8fb4211a..fc917c3b2 100644 --- a/integration_test/robots/dashboard_page_robot.dart +++ b/integration_test/robots/dashboard_page_robot.dart @@ -3,7 +3,7 @@ import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class DashboardPageRobot { DashboardPageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -58,7 +58,7 @@ class DashboardPageRobot { } Future navigateToSendPage() async { - await commonTestCases.tapItemByKey('dashboard_page_${S.current.buy}_action_button_key'); + await commonTestCases.tapItemByKey('dashboard_page_${S.current.send}_action_button_key'); } Future navigateToSellPage() async { diff --git a/integration_test/robots/disclaimer_page_robot.dart b/integration_test/robots/disclaimer_page_robot.dart index 642f56bca..18861fc29 100644 --- a/integration_test/robots/disclaimer_page_robot.dart +++ b/integration_test/robots/disclaimer_page_robot.dart @@ -2,7 +2,7 @@ import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class DisclaimerPageRobot { DisclaimerPageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -26,15 +26,14 @@ class DisclaimerPageRobot { } Future tapDisclaimerCheckbox() async { - final checkBox = find.byKey(ValueKey('disclaimer_check_key')); - await tester.tap(checkBox); - await tester.pumpAndSettle(); + await commonTestCases.tapItemByKey('disclaimer_check_key'); + await commonTestCases.defaultSleepTime(); } Future tapAcceptButton() async { - final checkBox = find.byKey(ValueKey('disclaimer_accept_button_key')); - await tester.tap(checkBox); - await tester.pumpAndSettle(); + await commonTestCases.tapItemByKey('disclaimer_accept_button_key'); + + await commonTestCases.defaultSleepTime(); } } diff --git a/integration_test/robots/exchange_confirm_page_robot.dart b/integration_test/robots/exchange_confirm_page_robot.dart index dd2ec68d5..160fd9dfb 100644 --- a/integration_test/robots/exchange_confirm_page_robot.dart +++ b/integration_test/robots/exchange_confirm_page_robot.dart @@ -3,7 +3,7 @@ import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dar import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class ExchangeConfirmPageRobot { ExchangeConfirmPageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -38,6 +38,8 @@ class ExchangeConfirmPageRobot { } Future onSavedTradeIdButtonPressed() async { + await tester.pumpAndSettle(); + await commonTestCases.defaultSleepTime(); await commonTestCases.tapItemByKey('exchange_confirm_page_saved_id_button_key'); } } diff --git a/integration_test/robots/exchange_page_robot.dart b/integration_test/robots/exchange_page_robot.dart index a0d9bd337..b439e4791 100644 --- a/integration_test/robots/exchange_page_robot.dart +++ b/integration_test/robots/exchange_page_robot.dart @@ -5,7 +5,7 @@ import 'package:cw_core/crypto_currency.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class ExchangePageRobot { ExchangePageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -317,6 +317,8 @@ class ExchangePageRobot { } Future handleErrors(String initialAmount) async { + await tester.pumpAndSettle(); + await _handleMinLimitError(initialAmount); await _handleMaxLimitError(initialAmount); diff --git a/integration_test/robots/exchange_trade_page_robot.dart b/integration_test/robots/exchange_trade_page_robot.dart index 30ddb474e..5708b6fae 100644 --- a/integration_test/robots/exchange_trade_page_robot.dart +++ b/integration_test/robots/exchange_trade_page_robot.dart @@ -6,7 +6,7 @@ import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart' import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class ExchangeTradePageRobot { ExchangeTradePageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -30,9 +30,10 @@ class ExchangeTradePageRobot { Future onConfirmSendingButtonPressed() async { tester.printToConsole('Now confirming sending'); - final widget = find.byKey(ValueKey('exchange_trade_page_confirm_sending_button_key')); - await tester.tap(widget); - await tester.pump(); + await commonTestCases.tapItemByKey( + 'exchange_trade_page_confirm_sending_button_key', + shouldPumpAndSettle: false, + ); final Completer completer = Completer(); @@ -116,7 +117,7 @@ class ExchangeTradePageRobot { return hasError; } - Future handleSendSuccessOrFailure() async { + Future handleConfirmSendResult() async { bool hasError = false; hasError = await hasErrorWhileSending(); diff --git a/integration_test/robots/new_wallet_type_page_robot.dart b/integration_test/robots/new_wallet_type_page_robot.dart index 16525d981..89fc8d390 100644 --- a/integration_test/robots/new_wallet_type_page_robot.dart +++ b/integration_test/robots/new_wallet_type_page_robot.dart @@ -5,7 +5,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class NewWalletTypePageRobot { NewWalletTypePageRobot(this.tester) : commonTestCases = CommonTestCases(tester); diff --git a/integration_test/robots/pin_code_widget_robot.dart b/integration_test/robots/pin_code_widget_robot.dart index 3b1df0cc7..b6805e9e0 100644 --- a/integration_test/robots/pin_code_widget_robot.dart +++ b/integration_test/robots/pin_code_widget_robot.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class PinCodeWidgetRobot { PinCodeWidgetRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -29,7 +29,6 @@ class PinCodeWidgetRobot { } Future enterPinCode(List pinCode, bool isFirstEntry) async { - for (int pin in pinCode) { await pushPinButton(pin); } diff --git a/integration_test/robots/restore_from_seed_or_key_robot.dart b/integration_test/robots/restore_from_seed_or_key_robot.dart index 28f95df83..43a65095d 100644 --- a/integration_test/robots/restore_from_seed_or_key_robot.dart +++ b/integration_test/robots/restore_from_seed_or_key_robot.dart @@ -3,7 +3,7 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/widgets/validable_annotated_editable_text.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class RestoreFromSeedOrKeysPageRobot { RestoreFromSeedOrKeysPageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -49,13 +49,17 @@ class RestoreFromSeedOrKeysPageRobot { commonTestCases.hasValueKey('wallet_restore_advanced_settings_button_key'); } - Future enterWalletNameText(String walletName) async { + Future enterWalletNameText(String walletName, {bool isSeedFormEntry = true}) async { await commonTestCases.enterText( - walletName, 'wallet_restore_from_seed_wallet_name_textfield_key'); + walletName, + 'wallet_restore_from_${isSeedFormEntry ? 'seed' : 'keys'}_wallet_name_textfield_key', + ); } - Future selectWalletNameFromAvailableOptions() async { - await commonTestCases.tapItemByKey('wallet_restore_from_seed_wallet_name_refresh_button_key'); + Future selectWalletNameFromAvailableOptions({bool isSeedFormEntry = true}) async { + await commonTestCases.tapItemByKey( + 'wallet_restore_from_${isSeedFormEntry ? 'seed' : 'keys'}_wallet_name_refresh_button_key', + ); } Future enterSeedPhraseForWalletRestore(String text) async { @@ -70,6 +74,14 @@ class RestoreFromSeedOrKeysPageRobot { await commonTestCases.tapItemByKey('wallet_restore_from_seed_wallet_seeds_paste_button_key'); } + Future enterPrivateKeyForWalletRestore(String privateKey) async { + await commonTestCases.enterText( + privateKey, + 'wallet_restore_from_key_private_key_textfield_key', + ); + await tester.pumpAndSettle(); + } + Future onRestoreWalletButtonPressed() async { await commonTestCases.tapItemByKey('wallet_restore_seed_or_key_restore_button_key'); await commonTestCases.defaultSleepTime(); diff --git a/integration_test/robots/restore_options_page_robot.dart b/integration_test/robots/restore_options_page_robot.dart index 2037b913a..b3cefc90c 100644 --- a/integration_test/robots/restore_options_page_robot.dart +++ b/integration_test/robots/restore_options_page_robot.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class RestoreOptionsPageRobot { RestoreOptionsPageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -21,19 +21,16 @@ class RestoreOptionsPageRobot { } Future navigateToRestoreFromSeedsPage() async { - tester.printToConsole('Routing to restore from seeds page'); await commonTestCases.tapItemByKey('restore_options_from_seeds_button_key'); await commonTestCases.defaultSleepTime(); } Future navigateToRestoreFromBackupPage() async { - tester.printToConsole('Routing to restore from backup page'); await commonTestCases.tapItemByKey('restore_options_from_backup_button_key'); await commonTestCases.defaultSleepTime(); } Future navigateToRestoreFromHardwareWalletPage() async { - tester.printToConsole('Routing to restore from hardware wallet page'); await commonTestCases.tapItemByKey('restore_options_from_hardware_wallet_button_key'); await commonTestCases.defaultSleepTime(); } diff --git a/integration_test/robots/send_page_robot.dart b/integration_test/robots/send_page_robot.dart new file mode 100644 index 000000000..68cd73f46 --- /dev/null +++ b/integration_test/robots/send_page_robot.dart @@ -0,0 +1,349 @@ +import 'dart:async'; + +import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/send/send_page.dart'; +import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; +import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../components/common_test_cases.dart'; +import '../components/common_test_constants.dart'; +import 'auth_page_robot.dart'; + +class SendPageRobot { + SendPageRobot({required this.tester}) + : commonTestCases = CommonTestCases(tester), + authPageRobot = AuthPageRobot(tester); + + WidgetTester tester; + CommonTestCases commonTestCases; + AuthPageRobot authPageRobot; + + Future isSendPage() async { + await commonTestCases.isSpecificPage(); + } + + void hasTitle() { + commonTestCases.hasText(S.current.send); + } + + void confirmViewComponentsDisplayProperly() { + SendPage sendPage = tester.widget(find.byType(SendPage)); + final sendViewModel = sendPage.sendViewModel; + + commonTestCases.hasValueKey('send_page_address_textfield_key'); + commonTestCases.hasValueKey('send_page_note_textfield_key'); + commonTestCases.hasValueKey('send_page_amount_textfield_key'); + commonTestCases.hasValueKey('send_page_add_template_button_key'); + + if (sendViewModel.hasMultipleTokens) { + commonTestCases.hasValueKey('send_page_currency_picker_button_key'); + } + + if (!sendViewModel.isBatchSending) { + commonTestCases.hasValueKey('send_page_send_all_button_key'); + } + + if (!sendViewModel.isFiatDisabled) { + commonTestCases.hasValueKey('send_page_fiat_amount_textfield_key'); + } + + if (sendViewModel.hasFees) { + commonTestCases.hasValueKey('send_page_select_fee_priority_button_key'); + } + + if (sendViewModel.hasCoinControl) { + commonTestCases.hasValueKey('send_page_unspent_coin_button_key'); + } + + if (sendViewModel.hasCurrecyChanger) { + commonTestCases.hasValueKey('send_page_change_asset_button_key'); + } + + if (sendViewModel.sendTemplateViewModel.hasMultiRecipient) { + commonTestCases.hasValueKey('send_page_add_receiver_button_key'); + } + } + + Future selectReceiveCurrency(CryptoCurrency receiveCurrency) async { + final currencyPickerKey = 'send_page_currency_picker_button_key'; + final currencyPickerDialogKey = 'send_page_currency_picker_dialog_button_key'; + + await commonTestCases.tapItemByKey(currencyPickerKey); + commonTestCases.hasValueKey(currencyPickerDialogKey); + + SendPage sendPage = tester.widget(find.byType(SendPage)); + final sendViewModel = sendPage.sendViewModel; + + if (receiveCurrency == sendViewModel.selectedCryptoCurrency) { + await commonTestCases + .tapItemByKey('picker_items_index_${receiveCurrency.name}_selected_item_button_key'); + return; + } + + await commonTestCases.scrollUntilVisible( + 'picker_items_index_${receiveCurrency.name}_button_key', + 'picker_scrollbar_key', + ); + await commonTestCases.defaultSleepTime(); + + await commonTestCases.tapItemByKey('picker_items_index_${receiveCurrency.name}_button_key'); + } + + Future enterReceiveAddress(String receiveAddress) async { + await commonTestCases.enterText(receiveAddress, 'send_page_address_textfield_key'); + await commonTestCases.defaultSleepTime(); + } + + Future enterAmount(String amount) async { + await commonTestCases.enterText(amount, 'send_page_amount_textfield_key'); + } + + Future selectTransactionPriority({TransactionPriority? priority}) async { + SendPage sendPage = tester.widget(find.byType(SendPage)); + final sendViewModel = sendPage.sendViewModel; + + if (!sendViewModel.hasFees || priority == null) return; + + final transactionPriorityPickerKey = 'send_page_select_fee_priority_button_key'; + await commonTestCases.tapItemByKey(transactionPriorityPickerKey); + + if (priority == sendViewModel.transactionPriority) { + await commonTestCases + .tapItemByKey('picker_items_index_${priority.title}_selected_item_button_key'); + return; + } + + await commonTestCases.scrollUntilVisible( + 'picker_items_index_${priority.title}_button_key', + 'picker_scrollbar_key', + ); + await commonTestCases.defaultSleepTime(); + + await commonTestCases.tapItemByKey('picker_items_index_${priority.title}_button_key'); + } + + Future onSendButtonPressed() async { + tester.printToConsole('Pressing send'); + + await commonTestCases.tapItemByKey( + 'send_page_send_button_key', + shouldPumpAndSettle: false, + ); + + await _waitForSendTransactionCompletion(); + + await commonTestCases.defaultSleepTime(); + } + + Future _waitForSendTransactionCompletion() async { + + final Completer completer = Completer(); + + // Loop to wait for the async operation to complete + while (true) { + await Future.delayed(Duration(seconds: 1)); + + tester.printToConsole('Before in auth'); + + await _handleAuthPage(); + + tester.printToConsole('After in auth'); + + final sendPage = tester.widget(find.byType(SendPage)); + final state = sendPage.sendViewModel.state; + + bool isDone = state is ExecutedSuccessfullyState; + bool isFailed = state is FailureState; + + tester.printToConsole('isDone: $isDone'); + tester.printToConsole('isFailed: $isFailed'); + + if (isDone || isFailed) { + tester.printToConsole( + isDone ? 'Completer is done' : 'Completer is done though operation failed', + ); + completer.complete(); + await tester.pump(); + break; + } else { + tester.printToConsole('Completer is not done'); + await tester.pump(); + } + } + + await expectLater(completer.future, completes); + + tester.printToConsole('Done confirming sending operation'); + } + + Future _handleAuthPage() async { + final authPage = authPageRobot.onAuthPage(); + if (authPage) { + tester.printToConsole('Auth'); + await tester.pump(); + + await authPageRobot.enterPinCode(CommonTestConstants.pin, false); + tester.printToConsole('Auth done'); + + await tester.pump(); + + tester.printToConsole('Auth pump done'); + } + } + + Future handleSendResult() async { + tester.printToConsole('Inside handle function'); + + bool hasError = false; + + hasError = await hasErrorWhileSending(); + + tester.printToConsole('Has an Error in the handle: $hasError'); + + int maxRetries = 20; + int retries = 0; + + while (hasError && retries < maxRetries) { + tester.printToConsole('hasErrorInLoop: $hasError'); + await tester.pump(); + + await onSendFailureDialogButtonPressed(); + tester.printToConsole('Failure button tapped'); + + await commonTestCases.defaultSleepTime(); + + await onSendButtonPressed(); + tester.printToConsole('Send button tapped'); + + hasError = await hasErrorWhileSending(); + + retries++; + } + + if (!hasError) { + tester.printToConsole('No error, proceeding with flow'); + await tester.pump(); + } + + await commonTestCases.defaultSleepTime(); + } + + //* ------ On Sending Failure ------------ + Future hasErrorWhileSending() async { + await tester.pump(); + + tester.printToConsole('Checking if there is an error'); + + final errorDialog = find.byKey(ValueKey('send_page_send_failure_dialog_button_key')); + + bool hasError = errorDialog.tryEvaluate(); + + tester.printToConsole('Has error: $hasError'); + + return hasError; + } + + Future onSendFailureDialogButtonPressed() async { + await commonTestCases.defaultSleepTime(); + + tester.printToConsole('Send Button Failure Dialog Triggered'); + + await commonTestCases.tapItemByKey('send_page_send_failure_dialog_button_key'); + } + + //* ------ On Sending Success ------------ + Future onSendButtonOnConfirmSendingDialogPressed() async { + tester.printToConsole('Inside confirm sending dialog: For sending'); + await commonTestCases.defaultSleepTime(); + await tester.pump(); + + final sendText = find.text(S.current.send).last; + bool hasText = sendText.tryEvaluate(); + tester.printToConsole('Has Text: $hasText'); + + if (hasText) { + await commonTestCases.tapItemByFinder(sendText, shouldPumpAndSettle: false); + // Loop to wait for the operation to commit transaction + await _waitForCommitTransactionCompletion(); + + await commonTestCases.defaultSleepTime(seconds: 4); + } else { + await commonTestCases.defaultSleepTime(); + await tester.pump(); + onSendButtonOnConfirmSendingDialogPressed(); + } + } + + Future _waitForCommitTransactionCompletion() async { + final Completer completer = Completer(); + + while (true) { + await Future.delayed(Duration(seconds: 1)); + + final sendPage = tester.widget(find.byType(SendPage)); + final state = sendPage.sendViewModel.state; + + bool isDone = state is TransactionCommitted; + bool isFailed = state is FailureState; + + tester.printToConsole('isDone: $isDone'); + tester.printToConsole('isFailed: $isFailed'); + + if (isDone || isFailed) { + tester.printToConsole( + isDone ? 'Completer is done' : 'Completer is done though operation failed', + ); + completer.complete(); + await tester.pump(); + break; + } else { + tester.printToConsole('Completer is not done'); + await tester.pump(); + } + } + + await expectLater(completer.future, completes); + + tester.printToConsole('Done Committing Transaction'); + } + + Future onCancelButtonOnConfirmSendingDialogPressed() async { + tester.printToConsole('Inside confirm sending dialog: For canceling'); + await commonTestCases.defaultSleepTime(seconds: 4); + + final cancelText = find.text(S.current.cancel); + bool hasText = cancelText.tryEvaluate(); + + if (hasText) { + await commonTestCases.tapItemByFinder(cancelText); + + await commonTestCases.defaultSleepTime(seconds: 4); + } + } + + //* ---- Add Contact Dialog On Send Successful Dialog ----- + Future onSentDialogPopUp() async { + SendPage sendPage = tester.widget(find.byType(SendPage)); + final sendViewModel = sendPage.sendViewModel; + + final newContactAddress = sendPage.newContactAddress ?? sendViewModel.newContactAddress(); + if (newContactAddress != null) { + await _onAddContactButtonOnSentDialogPressed(); + } + + await commonTestCases.defaultSleepTime(); + } + + Future _onAddContactButtonOnSentDialogPressed() async { + await commonTestCases.tapItemByKey('send_page_sent_dialog_add_contact_button_key'); + } + + // ignore: unused_element + Future _onIgnoreButtonOnSentDialogPressed() async { + await commonTestCases.tapItemByKey('send_page_sent_dialog_ignore_button_key'); + } +} diff --git a/integration_test/robots/setup_pin_code_robot.dart b/integration_test/robots/setup_pin_code_robot.dart index 3f3d81c16..0888aac30 100644 --- a/integration_test/robots/setup_pin_code_robot.dart +++ b/integration_test/robots/setup_pin_code_robot.dart @@ -2,7 +2,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; import 'pin_code_widget_robot.dart'; class SetupPinCodeRobot extends PinCodeWidgetRobot { diff --git a/integration_test/robots/welcome_page_robot.dart b/integration_test/robots/welcome_page_robot.dart index 4943666d0..510f63556 100644 --- a/integration_test/robots/welcome_page_robot.dart +++ b/integration_test/robots/welcome_page_robot.dart @@ -2,7 +2,7 @@ import 'package:cake_wallet/src/screens/welcome/welcome_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../components/common_checks.dart'; +import '../components/common_test_cases.dart'; class WelcomePageRobot { WelcomePageRobot(this.tester) : commonTestCases = CommonTestCases(tester); @@ -24,13 +24,11 @@ class WelcomePageRobot { } Future navigateToCreateNewWalletPage() async { - tester.printToConsole('Routing to create new wallet page'); await commonTestCases.tapItemByKey('welcome_page_create_new_wallet_button_key'); await commonTestCases.defaultSleepTime(); } Future navigateToRestoreWalletPage() async { - tester.printToConsole('Routing to restore wallet page'); await commonTestCases.tapItemByKey('welcome_page_restore_wallet_button_key'); await commonTestCases.defaultSleepTime(); } diff --git a/integration_test/test_suites/exchange_flow_test.dart b/integration_test/test_suites/exchange_flow_test.dart new file mode 100644 index 000000000..6c993634c --- /dev/null +++ b/integration_test/test_suites/exchange_flow_test.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import '../components/common_test_constants.dart'; +import '../components/common_test_flows.dart'; +import '../robots/auth_page_robot.dart'; +import '../robots/dashboard_page_robot.dart'; +import '../robots/exchange_confirm_page_robot.dart'; +import '../robots/exchange_page_robot.dart'; +import '../robots/exchange_trade_page_robot.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + AuthPageRobot authPageRobot; + CommonTestFlows commonTestFlows; + ExchangePageRobot exchangePageRobot; + DashboardPageRobot dashboardPageRobot; + ExchangeTradePageRobot exchangeTradePageRobot; + ExchangeConfirmPageRobot exchangeConfirmPageRobot; + + group('Exchange Flow Tests', () { + testWidgets('Exchange flow', (tester) async { + authPageRobot = AuthPageRobot(tester); + commonTestFlows = CommonTestFlows(tester); + exchangePageRobot = ExchangePageRobot(tester); + dashboardPageRobot = DashboardPageRobot(tester); + exchangeTradePageRobot = ExchangeTradePageRobot(tester); + exchangeConfirmPageRobot = ExchangeConfirmPageRobot(tester); + + await commonTestFlows.startAppFlow(ValueKey('exchange_app_test_key')); + await commonTestFlows.restoreWalletThroughSeedsFlow(); + await dashboardPageRobot.navigateToExchangePage(); + + // ----------- Exchange Page ------------- + await exchangePageRobot.selectDepositCurrency(CommonTestConstants.testDepositCurrency); + await exchangePageRobot.selectReceiveCurrency(CommonTestConstants.testReceiveCurrency); + + await exchangePageRobot.enterDepositAmount(CommonTestConstants.exchangeTestAmount); + await exchangePageRobot.enterDepositRefundAddress( + depositAddress: CommonTestConstants.testWalletAddress, + ); + await exchangePageRobot.enterReceiveAddress(CommonTestConstants.testWalletAddress); + + await exchangePageRobot.onExchangeButtonPressed(); + + await exchangePageRobot.handleErrors(CommonTestConstants.exchangeTestAmount); + + final onAuthPage = authPageRobot.onAuthPage(); + if (onAuthPage) { + await authPageRobot.enterPinCode(CommonTestConstants.pin, false); + } + + await exchangeConfirmPageRobot.onSavedTradeIdButtonPressed(); + await exchangeTradePageRobot.onGotItButtonPressed(); + }); + }); +} diff --git a/integration_test/test_suites/send_flow_test.dart b/integration_test/test_suites/send_flow_test.dart new file mode 100644 index 000000000..38ac1574f --- /dev/null +++ b/integration_test/test_suites/send_flow_test.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import '../components/common_test_constants.dart'; +import '../components/common_test_flows.dart'; +import '../robots/dashboard_page_robot.dart'; +import '../robots/send_page_robot.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + SendPageRobot sendPageRobot; + CommonTestFlows commonTestFlows; + DashboardPageRobot dashboardPageRobot; + + group('Send Flow Tests', () { + testWidgets('Send flow', (tester) async { + commonTestFlows = CommonTestFlows(tester); + sendPageRobot = SendPageRobot(tester: tester); + dashboardPageRobot = DashboardPageRobot(tester); + + await commonTestFlows.startAppFlow(ValueKey('send_test_app_key')); + await commonTestFlows.restoreWalletThroughSeedsFlow(); + await dashboardPageRobot.navigateToSendPage(); + + await sendPageRobot.enterReceiveAddress(CommonTestConstants.testWalletAddress); + await sendPageRobot.selectReceiveCurrency(CommonTestConstants.testReceiveCurrency); + await sendPageRobot.enterAmount(CommonTestConstants.sendTestAmount); + await sendPageRobot.selectTransactionPriority(); + + await sendPageRobot.onSendButtonPressed(); + + await sendPageRobot.handleSendResult(); + + await sendPageRobot.onSendButtonOnConfirmSendingDialogPressed(); + + await sendPageRobot.onSentDialogPopUp(); + }); + }); +} diff --git a/lib/main.dart b/lib/main.dart index 014d5f011..a7b5d53d9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -46,7 +46,7 @@ final navigatorKey = GlobalKey(); final rootKey = GlobalKey(); final RouteObserver> routeObserver = RouteObserver>(); -Future main() async { +Future main({Key? topLevelKey}) async { bool isAppRunning = false; await runZonedGuarded(() async { WidgetsFlutterBinding.ensureInitialized(); @@ -67,7 +67,7 @@ Future main() async { await initializeAppConfigs(); - runApp(App()); + runApp(App(key: topLevelKey)); isAppRunning = true; }, (error, stackTrace) async { @@ -253,6 +253,9 @@ Future initialSetup( } class App extends StatefulWidget { + App({this.key}); + + final Key? key; @override AppState createState() => AppState(); } @@ -281,7 +284,7 @@ class AppState extends State with SingleTickerProviderStateMixin { statusBarIconBrightness: statusBarIconBrightness)); return Root( - key: rootKey, + key: widget.key ?? rootKey, appStore: appStore, authenticationStore: authenticationStore, navigatorKey: navigatorKey, diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 1bc0c5942..430e309b9 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -365,7 +365,7 @@ class ExchangeCardState extends State { child: Padding( padding: EdgeInsets.only(top: 20), child: AddressTextField( - key: ValueKey('${_cardInstanceName}_editable_address_textfield_key'), + addressKey: ValueKey('${_cardInstanceName}_editable_address_textfield_key'), focusNode: widget.addressFocusNode, controller: addressController, onURIScanned: (uri) { diff --git a/lib/src/screens/restore/wallet_restore_from_keys_form.dart b/lib/src/screens/restore/wallet_restore_from_keys_form.dart index f8336a2e8..c34e6c968 100644 --- a/lib/src/screens/restore/wallet_restore_from_keys_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_keys_form.dart @@ -82,10 +82,12 @@ class WalletRestoreFromKeysFromState extends State { alignment: Alignment.centerRight, children: [ BaseTextFormField( + key: ValueKey('wallet_restore_from_keys_wallet_name_textfield_key'), controller: nameTextEditingController, hintText: S.of(context).wallet_name, validator: WalletNameValidator(), suffixIcon: IconButton( + key: ValueKey('wallet_restore_from_keys_wallet_name_refresh_button_key'), onPressed: () async { final rName = await generateName(); FocusManager.instance.primaryFocus?.unfocus(); @@ -132,6 +134,7 @@ class WalletRestoreFromKeysFromState extends State { bool nanoBased = widget.walletRestoreViewModel.type == WalletType.nano || widget.walletRestoreViewModel.type == WalletType.banano; return AddressTextField( + addressKey: ValueKey('wallet_restore_from_key_private_key_textfield_key'), controller: privateKeyController, placeholder: nanoBased ? S.of(context).seed_hex_form : S.of(context).private_key, options: [AddressTextFieldOption.paste], diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index 5ebf0b924..aa3b743fc 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -112,7 +112,7 @@ class WalletRestoreFromSeedFormState extends State { controller: nameTextEditingController, hintText: S.of(context).wallet_name, suffixIcon: IconButton( - key: ValueKey('wallet_restore_from_seed_wallet_name_refresh_button_key'), + key: ValueKey('wallet_restore_from_seed_wallet_name_refresh_button_key'), onPressed: () async { final rName = await generateName(); FocusManager.instance.primaryFocus?.unfocus(); @@ -145,10 +145,13 @@ class WalletRestoreFromSeedFormState extends State { )), Container(height: 20), SeedWidget( - key: seedWidgetStateKey, - language: language, - type: widget.type, - onSeedChange: onSeedChange), + key: seedWidgetStateKey, + language: language, + type: widget.type, + onSeedChange: onSeedChange, + seedTextFieldKey: ValueKey('wallet_restore_from_seed_wallet_seeds_textfield_key'), + pasteButtonKey: ValueKey('wallet_restore_from_seed_wallet_seeds_paste_button_key'), + ), if (widget.type == WalletType.monero || widget.type == WalletType.wownero) GestureDetector( onTap: () async { diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index b46a7f3db..2814fdef1 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -68,11 +68,11 @@ class SendPage extends BasePage { @override Function(BuildContext)? get pushToNextWidget => (context) { - FocusScopeNode currentFocus = FocusScope.of(context); - if (!currentFocus.hasPrimaryFocus) { - currentFocus.focusedChild?.unfocus(); - } - }; + FocusScopeNode currentFocus = FocusScope.of(context); + if (!currentFocus.hasPrimaryFocus) { + currentFocus.focusedChild?.unfocus(); + } + }; @override Widget? leading(BuildContext context) { @@ -246,6 +246,7 @@ class SendPage extends BasePage { return Row( children: [ AddTemplateButton( + key: ValueKey('send_page_add_template_button_key'), onTap: () => Navigator.of(context).pushNamed(Routes.sendTemplate), currentTemplatesLength: templates.length, ), @@ -333,19 +334,22 @@ class SendPage extends BasePage { children: [ if (sendViewModel.hasCurrecyChanger) Observer( - builder: (_) => Padding( - padding: EdgeInsets.only(bottom: 12), - child: PrimaryButton( - onPressed: () => presentCurrencyPicker(context), - text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})', - color: Colors.transparent, - textColor: - Theme.of(context).extension()!.hintTextColor, - ))), + builder: (_) => Padding( + padding: EdgeInsets.only(bottom: 12), + child: PrimaryButton( + key: ValueKey('send_page_change_asset_button_key'), + onPressed: () => presentCurrencyPicker(context), + text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})', + color: Colors.transparent, + textColor: Theme.of(context).extension()!.hintTextColor, + ), + ), + ), if (sendViewModel.sendTemplateViewModel.hasMultiRecipient) Padding( padding: EdgeInsets.only(bottom: 12), child: PrimaryButton( + key: ValueKey('send_page_add_receiver_button_key'), onPressed: () { sendViewModel.addOutput(); Future.delayed(const Duration(milliseconds: 250), () { @@ -362,6 +366,7 @@ class SendPage extends BasePage { Observer( builder: (_) { return LoadingPrimaryButton( + key: ValueKey('send_page_send_button_key'), onPressed: () async { if (_formKey.currentState != null && !_formKey.currentState!.validate()) { if (sendViewModel.outputs.length > 1) { @@ -444,6 +449,8 @@ class SendPage extends BasePage { context: context, builder: (BuildContext context) { return AlertWithOneAction( + key: ValueKey('send_page_send_failure_dialog_key'), + buttonKey: ValueKey('send_page_send_failure_dialog_button_key'), alertTitle: S.of(context).error, alertContent: state.error, buttonText: S.of(context).ok, @@ -459,6 +466,7 @@ class SendPage extends BasePage { context: context, builder: (BuildContext _dialogContext) { return ConfirmSendingAlert( + key: ValueKey('send_page_confirm_sending_dialog_key'), alertTitle: S.of(_dialogContext).confirm_sending, amount: S.of(_dialogContext).send_amount, amountValue: sendViewModel.pendingTransaction!.amountFormatted, @@ -472,6 +480,10 @@ class SendPage extends BasePage { outputs: sendViewModel.outputs, rightButtonText: S.of(_dialogContext).send, leftButtonText: S.of(_dialogContext).cancel, + alertRightActionButtonKey: + ValueKey('send_page_confirm_sending_dialog_send_button_key'), + alertLeftActionButtonKey: + ValueKey('send_page_confirm_sending_dialog_cancel_button_key'), actionRightButton: () async { Navigator.of(_dialogContext).pop(); sendViewModel.commitTransaction(); @@ -505,10 +517,15 @@ class SendPage extends BasePage { if (newContactAddress != null) { return AlertWithTwoActions( + alertDialogKey: ValueKey('send_page_sent_dialog_key'), alertTitle: '', alertContent: alertContent, rightButtonText: S.of(_dialogContext).add_contact, leftButtonText: S.of(_dialogContext).ignor, + alertLeftActionButtonKey: + ValueKey('send_page_sent_dialog_ignore_button_key'), + alertRightActionButtonKey: ValueKey( + 'send_page_sent_dialog_add_contact_button_key'), actionRightButton: () { Navigator.of(_dialogContext).pop(); RequestReviewHandler.requestReview(); diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index ec833159f..e5cad86c2 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -157,6 +157,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin _presentPicker(context), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -298,6 +300,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin with AutomaticKeepAliveClientMixin GestureDetector( + key: ValueKey('send_page_select_fee_priority_button_key'), onTap: sendViewModel.hasFeesPriority ? () => pickTransactionPriority(context) : () {}, @@ -531,6 +538,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin Navigator.of(context).pushNamed(Routes.unspentCoinsList), child: Container( color: Colors.transparent, @@ -715,6 +723,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin( context: context, builder: (_) => CurrencyPicker( + key: ValueKey('send_page_currency_picker_dialog_button_key'), selectedAtIndex: sendViewModel.currencies.indexOf(sendViewModel.selectedCryptoCurrency), items: sendViewModel.currencies, hintText: S.of(context).search_currency, diff --git a/lib/src/widgets/address_text_field.dart b/lib/src/widgets/address_text_field.dart index 044e10f2f..46e9708f1 100644 --- a/lib/src/widgets/address_text_field.dart +++ b/lib/src/widgets/address_text_field.dart @@ -15,24 +15,26 @@ import 'package:permission_handler/permission_handler.dart'; enum AddressTextFieldOption { paste, qrCode, addressBook } class AddressTextField extends StatelessWidget { - AddressTextField( - {required this.controller, - this.isActive = true, - this.placeholder, - this.options = const [AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook], - this.onURIScanned, - this.focusNode, - this.isBorderExist = true, - this.buttonColor, - this.borderColor, - this.iconColor, - this.textStyle, - this.hintStyle, - this.validator, - this.onPushPasteButton, - this.onPushAddressBookButton, - this.onSelectedContact, - this.selectedCurrency, super.key}); + AddressTextField({ + required this.controller, + this.isActive = true, + this.placeholder, + this.options = const [AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook], + this.onURIScanned, + this.focusNode, + this.isBorderExist = true, + this.buttonColor, + this.borderColor, + this.iconColor, + this.textStyle, + this.hintStyle, + this.validator, + this.onPushPasteButton, + this.onPushAddressBookButton, + this.onSelectedContact, + this.selectedCurrency, + this.addressKey, + }); static const prefixIconWidth = 34.0; static const prefixIconHeight = 34.0; @@ -55,28 +57,27 @@ class AddressTextField extends StatelessWidget { final Function(BuildContext context)? onPushAddressBookButton; final Function(ContactBase contact)? onSelectedContact; final CryptoCurrency? selectedCurrency; + final Key? addressKey; @override Widget build(BuildContext context) { return Stack( children: [ TextFormField( + key: addressKey, enableIMEPersonalizedLearning: false, keyboardType: TextInputType.visiblePassword, onFieldSubmitted: (_) => FocusScope.of(context).unfocus(), enabled: isActive, controller: controller, focusNode: focusNode, - style: textStyle ?? TextStyle( fontSize: 16, color: Theme.of(context).extension()!.titleColor), decoration: InputDecoration( - suffixIcon: SizedBox( width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length), ), - hintStyle: hintStyle ?? TextStyle(fontSize: 16, color: Theme.of(context).hintColor), hintText: placeholder ?? S.current.widgets_address, focusedBorder: isBorderExist @@ -194,7 +195,7 @@ class AddressTextField extends StatelessWidget { Future _presentQRScanner(BuildContext context) async { bool isCameraPermissionGranted = - await PermissionHandler.checkPermission(Permission.camera, context); + await PermissionHandler.checkPermission(Permission.camera, context); if (!isCameraPermissionGranted) return; final code = await presentQRScanner(); if (code.isEmpty) { diff --git a/lib/src/widgets/alert_with_two_actions.dart b/lib/src/widgets/alert_with_two_actions.dart index ddb11c3ee..e3d4408a6 100644 --- a/lib/src/widgets/alert_with_two_actions.dart +++ b/lib/src/widgets/alert_with_two_actions.dart @@ -14,6 +14,9 @@ class AlertWithTwoActions extends BaseAlertDialog { this.isDividerExist = false, // this.leftActionColor, // this.rightActionColor, + this.alertRightActionButtonKey, + this.alertLeftActionButtonKey, + this.alertDialogKey, }); final String alertTitle; @@ -26,6 +29,9 @@ class AlertWithTwoActions extends BaseAlertDialog { // final Color leftActionColor; // final Color rightActionColor; final bool isDividerExist; + final Key? alertRightActionButtonKey; + final Key? alertLeftActionButtonKey; + final Key? alertDialogKey; @override String get titleText => alertTitle; @@ -47,4 +53,13 @@ class AlertWithTwoActions extends BaseAlertDialog { // Color get rightButtonColor => rightActionColor; @override bool get isDividerExists => isDividerExist; + + @override + Key? get dialogKey => alertDialogKey; + + @override + Key? get leftActionButtonKey => alertLeftActionButtonKey; + + @override + Key? get rightActionButtonKey => alertRightActionButtonKey; } diff --git a/lib/src/widgets/base_alert_dialog.dart b/lib/src/widgets/base_alert_dialog.dart index 0f1ca988c..2e6f1571e 100644 --- a/lib/src/widgets/base_alert_dialog.dart +++ b/lib/src/widgets/base_alert_dialog.dart @@ -37,6 +37,8 @@ class BaseAlertDialog extends StatelessWidget { Key? rightActionButtonKey; + Key? dialogKey; + Widget title(BuildContext context) { return Text( titleText, @@ -158,6 +160,7 @@ class BaseAlertDialog extends StatelessWidget { @override Widget build(BuildContext context) { return GestureDetector( + key: key, onTap: () => barrierDismissible ? Navigator.of(context).pop() : null, child: Container( color: Colors.transparent, diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index 4c9601e0d..2d7ade610 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:cake_wallet/src/widgets/search_bar_widget.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:cw_core/transaction_priority.dart'; import 'package:flutter/material.dart'; import 'package:cw_core/currency.dart'; import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart'; @@ -303,11 +304,25 @@ class _PickerState extends State> { ); } + String _getItemName(Item item) { + String itemName; + if (item is Currency) { + itemName = item.name; + } else if (item is TransactionPriority) { + itemName = item.title; + } else { + itemName = ''; + } + + return itemName; + } + Widget buildItem(int index) { final item = widget.headerEnabled ? filteredItems[index] : items[index]; final tag = item is Currency ? item.tag : null; - final currencyName = item is Currency ? item.name : ''; + final itemName = _getItemName(item); + final icon = _getItemIcon(item); final image = images.isNotEmpty ? filteredImages[index] : icon; @@ -327,7 +342,7 @@ class _PickerState extends State> { children: [ Flexible( child: Text( - key: ValueKey('picker_items_index_${currencyName}_text_key'), + key: ValueKey('picker_items_index_${itemName}_text_key'), widget.displayItem?.call(item) ?? item.toString(), softWrap: true, style: TextStyle( @@ -371,7 +386,7 @@ class _PickerState extends State> { ); return GestureDetector( - key: ValueKey('picker_items_index_${currencyName}_button_key'), + key: ValueKey('picker_items_index_${itemName}_button_key'), onTap: () { if (widget.closeOnItemSelected) Navigator.of(context).pop(); onItemSelected(item!); @@ -397,7 +412,7 @@ class _PickerState extends State> { final item = items[index]; final tag = item is Currency ? item.tag : null; - final currencyName = item is Currency ? item.name : ''; + final itemName = _getItemName(item); final icon = _getItemIcon(item); final image = images.isNotEmpty ? images[index] : icon; @@ -418,7 +433,7 @@ class _PickerState extends State> { children: [ Flexible( child: Text( - key: ValueKey('picker_items_index_${currencyName}_selected_item_text_key'), + key: ValueKey('picker_items_index_${itemName}_selected_item_text_key'), widget.displayItem?.call(item) ?? item.toString(), softWrap: true, style: TextStyle( @@ -462,7 +477,7 @@ class _PickerState extends State> { ); return GestureDetector( - key: ValueKey('picker_items_index_${currencyName}_selected_item_button_key'), + key: ValueKey('picker_items_index_${itemName}_selected_item_button_key'), onTap: () { if (widget.closeOnItemSelected) Navigator.of(context).pop(); }, diff --git a/lib/src/widgets/seed_widget.dart b/lib/src/widgets/seed_widget.dart index 72c7ec93a..1c94718f1 100644 --- a/lib/src/widgets/seed_widget.dart +++ b/lib/src/widgets/seed_widget.dart @@ -12,9 +12,12 @@ class SeedWidget extends StatefulWidget { required this.language, required this.type, this.onSeedChange, + this.pasteButtonKey, + this.seedTextFieldKey, super.key, }); - + final Key? seedTextFieldKey; + final Key? pasteButtonKey; final String language; final WalletType type; final void Function(String)? onSeedChange; @@ -79,12 +82,11 @@ class SeedWidgetState extends State { top: 10, left: 0, child: Text(S.of(context).enter_seed_phrase, - style: TextStyle( - fontSize: 16.0, color: Theme.of(context).hintColor))), + style: TextStyle(fontSize: 16.0, color: Theme.of(context).hintColor))), Padding( padding: EdgeInsets.only(right: 40, top: 10), child: ValidatableAnnotatedEditableText( - key: ValueKey('wallet_restore_from_seed_wallet_seeds_textfield_key'), + key: widget.seedTextFieldKey, cursorColor: Colors.blue, backgroundCursorColor: Colors.blue, validStyle: TextStyle( @@ -114,16 +116,17 @@ class SeedWidgetState extends State { width: 32, height: 32, child: InkWell( - key: ValueKey('wallet_restore_from_seed_wallet_seeds_paste_button_key'), + key: widget.pasteButtonKey, onTap: () async => _pasteText(), child: Container( padding: EdgeInsets.all(8), decoration: BoxDecoration( color: Theme.of(context).hintColor, - borderRadius: - BorderRadius.all(Radius.circular(6))), + borderRadius: BorderRadius.all(Radius.circular(6))), child: Image.asset('assets/images/paste_ios.png', - color: Theme.of(context).extension()!.textFieldButtonIconColor)), + color: Theme.of(context) + .extension()! + .textFieldButtonIconColor)), ))) ]), Container(