test: Test for Send flow scenario and minor restructuring for test folders and files

This commit is contained in:
Blazebrain 2024-07-31 14:13:23 +01:00
parent 9dc96d7d22
commit 989ecbb2de
38 changed files with 787 additions and 366 deletions

View file

@ -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

View file

@ -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"

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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:

View file

@ -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();
});
});
}

View file

@ -10,15 +10,15 @@ class CommonTestCases {
hasType<T>();
}
Future<void> tapItemByKey(String key) async {
Future<void> 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<void> tapItemByFinder(Finder finder) async {
Future<void> 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}) {

View file

@ -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';
}

View file

@ -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<void> 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<void> restoreWalletThroughSeedsFlow() async {
await _welcomeToRestoreFromSeedsPath();
await _restoreFromSeeds();
}
Future<void> restoreWalletThroughKeysFlow() async {
await _welcomeToRestoreFromSeedsPath();
await _restoreFromKeys();
}
Future<void> _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<void> _restoreFromSeeds() async {
// ----------- RestoreFromSeedOrKeys Page -------------
await _restoreFromSeedOrKeysPageRobot.enterWalletNameText(CommonTestConstants.testWalletName);
await _restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore(secrets.seeds);
await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed();
}
Future<void> _restoreFromKeys() async {
await _commonTestCases.swipePage();
await _commonTestCases.defaultSleepTime();
await _restoreFromSeedOrKeysPageRobot.enterWalletNameText(CommonTestConstants.testWalletName);
await _restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore('');
await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed();
}
}

View file

@ -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();
});

View file

@ -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 {

View file

@ -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<void> 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<void> navigateToSellPage() async {

View file

@ -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<void> 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<void> 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();
}
}

View file

@ -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<void> onSavedTradeIdButtonPressed() async {
await tester.pumpAndSettle();
await commonTestCases.defaultSleepTime();
await commonTestCases.tapItemByKey('exchange_confirm_page_saved_id_button_key');
}
}

View file

@ -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<void> handleErrors(String initialAmount) async {
await tester.pumpAndSettle();
await _handleMinLimitError(initialAmount);
await _handleMaxLimitError(initialAmount);

View file

@ -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<void> 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<void> completer = Completer<void>();
@ -116,7 +117,7 @@ class ExchangeTradePageRobot {
return hasError;
}
Future<void> handleSendSuccessOrFailure() async {
Future<void> handleConfirmSendResult() async {
bool hasError = false;
hasError = await hasErrorWhileSending();

View file

@ -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);

View file

@ -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<void> enterPinCode(List<int> pinCode, bool isFirstEntry) async {
for (int pin in pinCode) {
await pushPinButton(pin);
}

View file

@ -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<void> enterWalletNameText(String walletName) async {
Future<void> 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<void> selectWalletNameFromAvailableOptions() async {
await commonTestCases.tapItemByKey('wallet_restore_from_seed_wallet_name_refresh_button_key');
Future<void> selectWalletNameFromAvailableOptions({bool isSeedFormEntry = true}) async {
await commonTestCases.tapItemByKey(
'wallet_restore_from_${isSeedFormEntry ? 'seed' : 'keys'}_wallet_name_refresh_button_key',
);
}
Future<void> enterSeedPhraseForWalletRestore(String text) async {
@ -70,6 +74,14 @@ class RestoreFromSeedOrKeysPageRobot {
await commonTestCases.tapItemByKey('wallet_restore_from_seed_wallet_seeds_paste_button_key');
}
Future<void> enterPrivateKeyForWalletRestore(String privateKey) async {
await commonTestCases.enterText(
privateKey,
'wallet_restore_from_key_private_key_textfield_key',
);
await tester.pumpAndSettle();
}
Future<void> onRestoreWalletButtonPressed() async {
await commonTestCases.tapItemByKey('wallet_restore_seed_or_key_restore_button_key');
await commonTestCases.defaultSleepTime();

View file

@ -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<void> navigateToRestoreFromSeedsPage() async {
tester.printToConsole('Routing to restore from seeds page');
await commonTestCases.tapItemByKey('restore_options_from_seeds_button_key');
await commonTestCases.defaultSleepTime();
}
Future<void> navigateToRestoreFromBackupPage() async {
tester.printToConsole('Routing to restore from backup page');
await commonTestCases.tapItemByKey('restore_options_from_backup_button_key');
await commonTestCases.defaultSleepTime();
}
Future<void> navigateToRestoreFromHardwareWalletPage() async {
tester.printToConsole('Routing to restore from hardware wallet page');
await commonTestCases.tapItemByKey('restore_options_from_hardware_wallet_button_key');
await commonTestCases.defaultSleepTime();
}

View file

@ -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<void> isSendPage() async {
await commonTestCases.isSpecificPage<SendPage>();
}
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<void> 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<void> enterReceiveAddress(String receiveAddress) async {
await commonTestCases.enterText(receiveAddress, 'send_page_address_textfield_key');
await commonTestCases.defaultSleepTime();
}
Future<void> enterAmount(String amount) async {
await commonTestCases.enterText(amount, 'send_page_amount_textfield_key');
}
Future<void> 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<void> onSendButtonPressed() async {
tester.printToConsole('Pressing send');
await commonTestCases.tapItemByKey(
'send_page_send_button_key',
shouldPumpAndSettle: false,
);
await _waitForSendTransactionCompletion();
await commonTestCases.defaultSleepTime();
}
Future<void> _waitForSendTransactionCompletion() async {
final Completer<void> completer = Completer<void>();
// 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<SendPage>(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<void> _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<void> 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<bool> 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<void> 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<void> 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<void> _waitForCommitTransactionCompletion() async {
final Completer<void> completer = Completer<void>();
while (true) {
await Future.delayed(Duration(seconds: 1));
final sendPage = tester.widget<SendPage>(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<void> 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<void> 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<void> _onAddContactButtonOnSentDialogPressed() async {
await commonTestCases.tapItemByKey('send_page_sent_dialog_add_contact_button_key');
}
// ignore: unused_element
Future<void> _onIgnoreButtonOnSentDialogPressed() async {
await commonTestCases.tapItemByKey('send_page_sent_dialog_ignore_button_key');
}
}

View file

@ -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 {

View file

@ -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<void> navigateToCreateNewWalletPage() async {
tester.printToConsole('Routing to create new wallet page');
await commonTestCases.tapItemByKey('welcome_page_create_new_wallet_button_key');
await commonTestCases.defaultSleepTime();
}
Future<void> navigateToRestoreWalletPage() async {
tester.printToConsole('Routing to restore wallet page');
await commonTestCases.tapItemByKey('welcome_page_restore_wallet_button_key');
await commonTestCases.defaultSleepTime();
}

View file

@ -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();
});
});
}

View file

@ -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();
});
});
}

View file

@ -46,7 +46,7 @@ final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>();
final RouteObserver<PageRoute<dynamic>> routeObserver = RouteObserver<PageRoute<dynamic>>();
Future<void> main() async {
Future<void> main({Key? topLevelKey}) async {
bool isAppRunning = false;
await runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
@ -67,7 +67,7 @@ Future<void> main() async {
await initializeAppConfigs();
runApp(App());
runApp(App(key: topLevelKey));
isAppRunning = true;
}, (error, stackTrace) async {
@ -253,6 +253,9 @@ Future<void> initialSetup(
}
class App extends StatefulWidget {
App({this.key});
final Key? key;
@override
AppState createState() => AppState();
}
@ -281,7 +284,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
statusBarIconBrightness: statusBarIconBrightness));
return Root(
key: rootKey,
key: widget.key ?? rootKey,
appStore: appStore,
authenticationStore: authenticationStore,
navigatorKey: navigatorKey,

View file

@ -365,7 +365,7 @@ class ExchangeCardState extends State<ExchangeCard> {
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) {

View file

@ -82,10 +82,12 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
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<WalletRestoreFromKeysFrom> {
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],

View file

@ -112,7 +112,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
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<WalletRestoreFromSeedForm> {
)),
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 {

View file

@ -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: <Widget>[
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<SeedWidgetTheme>()!.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<SeedWidgetTheme>()!.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();

View file

@ -157,6 +157,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
: sendViewModel.addressValidator;
return AddressTextField(
addressKey: ValueKey('send_page_address_textfield_key'),
focusNode: addressFocusNode,
controller: addressController,
onURIScanned: (uri) {
@ -221,6 +222,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
padding: EdgeInsets.only(right: 8),
height: 32,
child: InkWell(
key: ValueKey('send_page_currency_picker_button_key'),
onTap: () => _presentPicker(context),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -298,6 +300,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
child: Stack(
children: [
BaseTextFormField(
key: ValueKey('send_page_amount_textfield_key'),
focusNode: cryptoAmountFocus,
controller: cryptoAmountController,
keyboardType: TextInputType.numberWithOptions(
@ -332,6 +335,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
width: prefixIconWidth,
height: prefixIconHeight,
child: InkWell(
key: ValueKey('send_page_send_all_button_key'),
onTap: () async {
output.setSendAll(sendViewModel.balance);
},
@ -405,6 +409,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
Padding(
padding: const EdgeInsets.only(top: 20),
child: BaseTextFormField(
key: ValueKey('send_page_fiat_amount_textfield_key'),
focusNode: fiatAmountFocus,
controller: fiatAmountController,
keyboardType:
@ -440,6 +445,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
Padding(
padding: EdgeInsets.only(top: 20),
child: BaseTextFormField(
key: ValueKey('send_page_note_textfield_key'),
controller: noteController,
keyboardType: TextInputType.multiline,
maxLines: null,
@ -458,6 +464,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
if (sendViewModel.hasFees)
Observer(
builder: (_) => GestureDetector(
key: ValueKey('send_page_select_fee_priority_button_key'),
onTap: sendViewModel.hasFeesPriority
? () => pickTransactionPriority(context)
: () {},
@ -531,6 +538,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
Padding(
padding: EdgeInsets.only(top: 6),
child: GestureDetector(
key: ValueKey('send_page_unspent_coin_button_key'),
onTap: () => Navigator.of(context).pushNamed(Routes.unspentCoinsList),
child: Container(
color: Colors.transparent,
@ -715,6 +723,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
showPopUp<void>(
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,

View file

@ -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: <Widget>[
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<CakeTextTheme>()!.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<void> _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) {

View file

@ -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;
}

View file

@ -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,

View file

@ -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<Item> extends State<Picker<Item>> {
);
}
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<Item> extends State<Picker<Item>> {
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<Item> extends State<Picker<Item>> {
);
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<Item> extends State<Picker<Item>> {
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<Item> extends State<Picker<Item>> {
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<Item> extends State<Picker<Item>> {
);
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();
},

View file

@ -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<SeedWidget> {
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<SeedWidget> {
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<SendPageTheme>()!.textFieldButtonIconColor)),
color: Theme.of(context)
.extension<SendPageTheme>()!
.textFieldButtonIconColor)),
)))
]),
Container(