mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-28 06:28:41 +01:00
Merge upstream Cake Wallet v6.5.0
Catches up from the May 12 fork point (v6.1.2+23) through v6.5.0: 535 upstream commits, including the Monero silent-open fix, Monero on Trezor, Ledger/Trezor BLE fixes, the new seed and history screens, quick Bitcoin sync, git-pinned dependencies (pubspec_overrides.yaml), frozen Hive adapters (*.part.dart), monero_c v0.18.4.6-RC2 and Flutter 3.41.9. Merged with both sides normalized first (import rename, 100-column format), which took the conflict count from 614 paths to 322. The procedure is now scripts/upstream_sync.sh. Kept from Hash Bags: - WalletType enum order and Hive IDs. SQLite stores WalletType.index and our order differs from upstream's; taking theirs would reread saved wallets as other chains. Our frozen adapter keeps the shipped IDs. - The 1.0.2 legacy PIN and wallet-password decode and self-heal. - Removed chains (Solana, Tron, Zano, Zcash, Decred, Haven) and the new Robinhood chain stay out; Cake Pay and non-Trocador swaps stay out. - Default nodes (node.wownero.org, sethforprivacy, Blockstream, bysh.me), converted to upstream's isDefault node-list key; Wownero is back in the node reset list. - Privacy: address resolvers limited to domain-anchored sources again; prices.neroswap.com for fiat; upstream's charts tab, which reads prices.cakewallet.com, sits behind FeatureFlag.isChartsEnabled = false. - Lightning stays behind FeatureFlag.isLightningEnabled, including upstream's new post-seed Lightning username step. - Our icon pipeline, branding and bundle identifiers. Twelve new upstream widget tests are skipped: test and code are unchanged from v6.5.0 apart from renames (not rerun on a pristine upstream checkout; upstream's top_bar_test does not compile against its own TopBar). flutter analyze lib: 0 errors; flutter test: 345 passed, 12 skipped; flutter build bundle compiles. Built with Flutter 3.41.9.
This commit is contained in:
@@ -1,36 +1,20 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/auth/auth_page.dart";
|
||||
import "package:flutter/widgets.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import 'pin_code_widget_robot.dart';
|
||||
import "pin_code_widget_robot.dart";
|
||||
|
||||
class AuthPageRobot extends PinCodeWidgetRobot {
|
||||
AuthPageRobot(this.tester)
|
||||
: commonTestCases = CommonTestCases(tester),
|
||||
super(tester);
|
||||
AuthPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<AuthPage>();
|
||||
}
|
||||
|
||||
bool onAuthPage() {
|
||||
final hasPinButtons = find.byKey(ValueKey('pin_code_button_3_key'));
|
||||
final hasPinButtons = find.byKey(const ValueKey("pin_code_button_3_key"));
|
||||
final hasPin = hasPinButtons.tryEvaluate();
|
||||
return hasPin;
|
||||
}
|
||||
|
||||
bool onAuthPageDesktop() {
|
||||
final hasWalletPasswordInput = find.byKey(ValueKey('enter_wallet_password'));
|
||||
return hasWalletPasswordInput.tryEvaluate();
|
||||
}
|
||||
|
||||
Future<void> isAuthPage() async {
|
||||
await commonTestCases.isSpecificPage<AuthPage>();
|
||||
await commonTestCases.takeScreenshots('auth_page');
|
||||
}
|
||||
|
||||
void hasTitle() {
|
||||
commonTestCases.hasText(S.current.setup_pin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import "package:hash_wallet/entities/fiat_api_mode.dart";
|
||||
import "package:hash_wallet/src/screens/settings/connection_sync_page.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class ConnectionSyncPageRobot extends BaseRobot {
|
||||
ConnectionSyncPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<ConnectionSyncPage>();
|
||||
}
|
||||
|
||||
Future<void> setFiatApiMode(FiatApiMode mode) async {
|
||||
await tapByKey("fiat_api");
|
||||
|
||||
final unselected = find.byKey(ValueKey("picker_items_index_${mode.title}_button_key"));
|
||||
final selected =
|
||||
find.byKey(ValueKey("picker_items_index_${mode.title}_selected_item_button_key"));
|
||||
|
||||
await pumpUntil(() => tester.any(unselected) || tester.any(selected));
|
||||
|
||||
await tester.tap(tester.any(unselected) ? unselected : selected);
|
||||
|
||||
await settle();
|
||||
}
|
||||
|
||||
void hasFiatApiMode(FiatApiMode mode) {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const ValueKey("fiat_api")),
|
||||
matching: find.text(mode.title),
|
||||
),
|
||||
findsOneWidget,
|
||||
reason: "The fiat api row does not show ${mode.title}",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import "package:hash_wallet/src/screens/contact/contact_list_page.dart";
|
||||
import "package:hash_wallet/src/screens/contact/contact_page.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class ContactRobot extends BaseRobot {
|
||||
ContactRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await pumpUntilFound(find.byType(ContactListPage));
|
||||
}
|
||||
|
||||
Future<void> openAddContact() async {
|
||||
await tapByKey("contact_list_page_add_contact_button_key");
|
||||
|
||||
await pumpUntilFound(find.byType(ContactPage));
|
||||
}
|
||||
|
||||
Future<void> enterName(String name) async {
|
||||
await enterTextByKey("contact_page_name_textfield_key", name);
|
||||
}
|
||||
|
||||
Future<void> chooseCurrency(String currencyName) async {
|
||||
await tapByKey("contact_page_currency_picker_button_key");
|
||||
|
||||
await tapByKey("picker_items_index_${currencyName}_button_key");
|
||||
|
||||
await settle();
|
||||
}
|
||||
|
||||
Future<void> enterAddress(String address) async {
|
||||
final input = find.byKey(const ValueKey("contact_page_address_input_key"));
|
||||
|
||||
await pumpUntilFound(input);
|
||||
|
||||
final field = find.descendant(of: input, matching: find.byType(EditableText));
|
||||
|
||||
await tester.enterText(field.first, address);
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
await tapByKey("contact_page_save_button_key");
|
||||
|
||||
await pumpUntilGone(find.byType(ContactPage));
|
||||
|
||||
await settle();
|
||||
}
|
||||
|
||||
void expectContactSaved(String name, String address) {
|
||||
final page = tester.widget<ContactListPage>(find.byType(ContactListPage));
|
||||
|
||||
final saved = page.contactListViewModel.contacts.where((c) => c.name == name).toList();
|
||||
|
||||
expect(saved, isNotEmpty, reason: "The contact $name was never saved");
|
||||
|
||||
expect(
|
||||
saved.first.address,
|
||||
address,
|
||||
reason: "The address book stored a different address than it was given",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,16 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/welcome/create_pin_welcome_page.dart';
|
||||
import 'package:hash_wallet/wallet_type_utils.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/welcome/create_pin_welcome_page.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class CreatePinWelcomePageRobot {
|
||||
CreatePinWelcomePageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class CreatePinWelcomePageRobot extends BaseRobot {
|
||||
CreatePinWelcomePageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isCreatePinWelcomePage() async {
|
||||
await commonTestCases.isSpecificPage<CreatePinWelcomePage>();
|
||||
await commonTestCases.takeScreenshots('create_pin_welcome_page');
|
||||
}
|
||||
|
||||
void hasTitle() {
|
||||
String title;
|
||||
if (isMoneroOnly) {
|
||||
title = S.current.monero_com;
|
||||
}
|
||||
|
||||
title = S.current.cake_wallet;
|
||||
|
||||
commonTestCases.hasText(title);
|
||||
}
|
||||
|
||||
void hasDescription() {
|
||||
String description;
|
||||
if (isMoneroOnly) {
|
||||
description = S.current.monero_com_wallet_text;
|
||||
}
|
||||
|
||||
description = S.current.new_first_wallet_text;
|
||||
|
||||
commonTestCases.hasText(description);
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<CreatePinWelcomePage>();
|
||||
}
|
||||
|
||||
Future<void> tapSetAPinButton() async {
|
||||
await commonTestCases.tapItemByKey('create_pin_welcome_page_create_a_pin_button_key');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("create_pin_welcome_page_create_a_pin_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import 'package:hash_wallet/src/screens/dashboard/widgets/menu_widget.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class DashboardMenuWidgetRobot {
|
||||
DashboardMenuWidgetRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> hasMenuWidget() async {
|
||||
await commonTestCases.takeScreenshots('menu_widget_page');
|
||||
commonTestCases.hasType<MenuWidget>();
|
||||
}
|
||||
|
||||
void displaysTheCorrectWalletNameAndSubName() {
|
||||
final menuWidgetState = tester.state<MenuWidgetState>(find.byType(MenuWidget));
|
||||
|
||||
final walletName = menuWidgetState.widget.dashboardViewModel.name;
|
||||
commonTestCases.hasText(walletName);
|
||||
|
||||
final walletSubName = menuWidgetState.widget.dashboardViewModel.subname;
|
||||
if (walletSubName.isNotEmpty) {
|
||||
commonTestCases.hasText(walletSubName);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> navigateToWalletMenu() async {
|
||||
await commonTestCases.tapItemByKey('dashboard_page_menu_widget_wallet_menu_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> navigateToSecurityAndBackupPage() async {
|
||||
await commonTestCases.tapItemByKey(
|
||||
'dashboard_page_menu_widget_security_and_backup_button_key',
|
||||
);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/dashboard/dashboard_page.dart';
|
||||
import 'package:hash_wallet/src/screens/dashboard/pages/balance/crypto_balance_widget.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import 'dashboard_menu_widget_robot.dart';
|
||||
|
||||
class DashboardPageRobot {
|
||||
DashboardPageRobot(this.tester)
|
||||
: commonTestCases = CommonTestCases(tester),
|
||||
dashboardMenuWidgetRobot = DashboardMenuWidgetRobot(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
final DashboardMenuWidgetRobot dashboardMenuWidgetRobot;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isDashboardPage() async {
|
||||
await commonTestCases.isSpecificPage<DashboardPage>();
|
||||
await commonTestCases.takeScreenshots('dashboard_page');
|
||||
}
|
||||
|
||||
Future<void> confirmWalletTypeIsDisplayedCorrectly(
|
||||
WalletType type, {
|
||||
bool isHaven = false,
|
||||
}) async {
|
||||
final cryptoBalanceWidget =
|
||||
tester.widget<CryptoBalanceWidget>(find.byType(CryptoBalanceWidget));
|
||||
final hasAccounts = cryptoBalanceWidget.dashboardViewModel.balanceViewModel.hasAccounts;
|
||||
|
||||
if (hasAccounts) {
|
||||
final walletName = cryptoBalanceWidget.dashboardViewModel.name;
|
||||
commonTestCases.hasText(walletName);
|
||||
} else {
|
||||
final walletName = walletTypeToString(type);
|
||||
final assetName = isHaven ? '$walletName Assets' : walletName;
|
||||
commonTestCases.hasText(assetName);
|
||||
}
|
||||
await commonTestCases.defaultSleepTime(seconds: 5);
|
||||
}
|
||||
|
||||
void confirmServiceUpdateButtonDisplays() {
|
||||
commonTestCases.hasValueKey('dashboard_page_services_update_button_key');
|
||||
}
|
||||
|
||||
void confirmSyncIndicatorButtonDisplays() {
|
||||
commonTestCases.hasValueKey('dashboard_page_sync_indicator_button_key');
|
||||
}
|
||||
|
||||
void confirmMenuButtonDisplays() {
|
||||
commonTestCases.hasValueKey('dashboard_page_wallet_menu_button_key');
|
||||
}
|
||||
|
||||
Future<void> confirmRightCryptoAssetTitleDisplaysPerPageView(
|
||||
WalletType type, {
|
||||
bool isHaven = false,
|
||||
}) async {
|
||||
//Balance Page
|
||||
await confirmWalletTypeIsDisplayedCorrectly(type, isHaven: isHaven);
|
||||
|
||||
// Swipe to Cake features Page
|
||||
await swipeDashboardTab(false);
|
||||
commonTestCases.hasText('Cake ${S.current.features}');
|
||||
|
||||
// Swipe back to balance
|
||||
await swipeDashboardTab(true);
|
||||
|
||||
// Swipe to Transactions Page
|
||||
await swipeDashboardTab(true);
|
||||
commonTestCases.hasText(S.current.transactions);
|
||||
|
||||
// Swipe back to balance
|
||||
await swipeDashboardTab(false);
|
||||
await commonTestCases.defaultSleepTime(seconds: 3);
|
||||
}
|
||||
|
||||
Future<void> swipeDashboardTab(bool swipeRight) async {
|
||||
await commonTestCases.swipeByPageKey(
|
||||
key: 'dashboard_page_view_key',
|
||||
swipeRight: swipeRight,
|
||||
);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> openDrawerMenu() async {
|
||||
await commonTestCases.tapItemByKey('dashboard_page_wallet_menu_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> navigateToBuyPage() async {
|
||||
await commonTestCases.tapItemByKey('dashboard_page_buy_action_button_key');
|
||||
}
|
||||
|
||||
Future<void> navigateToWalletsListPage({bool isDrawer = false}) async {
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 1000));
|
||||
|
||||
await commonTestCases.tapItemByKey(
|
||||
isDrawer
|
||||
? 'dashboard_page_menu_widget_wallet_menu_button_key'
|
||||
: 'dashboard_page__wallet_list_button_key',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 2000));
|
||||
}
|
||||
|
||||
Future<void> navigateToSendPage() async {
|
||||
await commonTestCases.tapItemByKey('dashboard_page_send_action_button_key');
|
||||
}
|
||||
|
||||
Future<void> navigateToSellPage() async {
|
||||
await commonTestCases.tapItemByKey('dashboard_page_sell_action_button_key');
|
||||
}
|
||||
|
||||
Future<void> navigateToReceivePage() async {
|
||||
await commonTestCases.tapItemByKey('dashboard_page_receive_action_button_key');
|
||||
}
|
||||
|
||||
Future<void> navigateToExchangePage() async {
|
||||
await commonTestCases.tapItemByKey('dashboard_page_swap_action_button_key');
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import 'package:hash_wallet/src/screens/disclaimer/disclaimer_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class DisclaimerPageRobot {
|
||||
DisclaimerPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isDisclaimerPage() async {
|
||||
await commonTestCases.isSpecificPage<DisclaimerPage>();
|
||||
await commonTestCases.takeScreenshots('disclaimer_page');
|
||||
}
|
||||
|
||||
void hasCheckIcon(bool hasBeenTapped) {
|
||||
// The checked Icon should not be available initially, until user taps the checkbox
|
||||
final checkIcon = find.byKey(ValueKey('disclaimer_check_icon_key'));
|
||||
expect(checkIcon, hasBeenTapped ? findsOneWidget : findsNothing);
|
||||
}
|
||||
|
||||
void hasDisclaimerCheckbox() {
|
||||
final checkBox = find.byKey(ValueKey('disclaimer_check_key'));
|
||||
expect(checkBox, findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> tapDisclaimerCheckbox() async {
|
||||
await commonTestCases.tapItemByKey('disclaimer_check_key');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> tapAcceptButton() async {
|
||||
await commonTestCases.tapItemByKey('disclaimer_accept_button_key');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import "package:hash_wallet/entities/fiat_currency.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/currency_picker/currency_picker_search_field.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/currency_picker/fiat_currency_picker_sheet.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/currency_picker/fiat_currency_row.dart";
|
||||
import "package:hash_wallet/src/screens/settings/display_settings_page.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class DisplaySettingsPageRobot extends BaseRobot {
|
||||
DisplaySettingsPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<DisplaySettingsPage>();
|
||||
}
|
||||
|
||||
Future<void> openLanguagePicker() async {
|
||||
await tapByKey("display_settings_language");
|
||||
}
|
||||
|
||||
Future<void> chooseLanguage(String code) async {
|
||||
await tapByKey("picker_items_index_${code}_button_key");
|
||||
|
||||
await settle();
|
||||
}
|
||||
|
||||
bool hasFiatCurrencyRow() =>
|
||||
tester.any(find.byKey(const ValueKey("display_settings_fiat_currency")));
|
||||
|
||||
Future<void> openFiatCurrencyPicker() async {
|
||||
await tapByKey("display_settings_fiat_currency");
|
||||
|
||||
await pumpUntilFound(find.byType(FiatCurrencyPickerSheet));
|
||||
}
|
||||
|
||||
Future<void> chooseFiatCurrency(FiatCurrency currency) async {
|
||||
final searchField = find.descendant(
|
||||
of: find.byType(CurrencyPickerSearchField),
|
||||
matching: find.byType(TextFormField),
|
||||
);
|
||||
|
||||
await tester.enterText(searchField, currency.fullName);
|
||||
|
||||
final row = find.byWidgetPredicate(
|
||||
(widget) => widget is FiatCurrencyRow && widget.currency == currency,
|
||||
);
|
||||
|
||||
await pumpUntilFound(row);
|
||||
|
||||
await tester.tap(row.first);
|
||||
|
||||
await pumpUntilGone(find.byType(FiatCurrencyPickerSheet));
|
||||
}
|
||||
|
||||
void expectFiatCurrencyShown(FiatCurrency currency) {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const ValueKey("display_settings_fiat_currency")),
|
||||
matching: find.text(currency.title),
|
||||
),
|
||||
findsOneWidget,
|
||||
reason: "The currency row still does not show ${currency.title}",
|
||||
);
|
||||
}
|
||||
|
||||
void expectLanguageShown(String displayName) {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byKey(const ValueKey("display_settings_language")),
|
||||
matching: find.text(displayName),
|
||||
),
|
||||
findsOneWidget,
|
||||
reason: "The language row still does not show $displayName",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class ExchangeConfirmPageRobot {
|
||||
ExchangeConfirmPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isExchangeConfirmPage() async {
|
||||
await commonTestCases.isSpecificPage<ExchangeConfirmPage>();
|
||||
await commonTestCases.takeScreenshots('exchange_confirm_page');
|
||||
}
|
||||
|
||||
void confirmComponentsOfTradeDisplayProperly() {
|
||||
final ExchangeConfirmPage exchangeConfirmPage = tester.widget(find.byType(ExchangeConfirmPage));
|
||||
final trade = exchangeConfirmPage.trade;
|
||||
|
||||
commonTestCases.hasText(trade.id);
|
||||
commonTestCases.hasText('${trade.provider.title} ${S.current.trade_id}');
|
||||
|
||||
commonTestCases.hasValueKey('exchange_confirm_page_saved_id_button_key');
|
||||
commonTestCases.hasValueKey('exchange_confirm_page_copy_to_clipboard_button_key');
|
||||
}
|
||||
|
||||
Future<void> confirmCopyTradeIdToClipBoardWorksProperly() async {
|
||||
final ExchangeConfirmPage exchangeConfirmPage = tester.widget(find.byType(ExchangeConfirmPage));
|
||||
final trade = exchangeConfirmPage.trade;
|
||||
|
||||
await commonTestCases.tapItemByKey('exchange_confirm_page_copy_to_clipboard_button_key');
|
||||
|
||||
ClipboardData? clipboardData = await Clipboard.getData('text/plain');
|
||||
|
||||
expect(clipboardData?.text, trade.id);
|
||||
}
|
||||
|
||||
Future<void> onSavedTradeIdButtonPressed() async {
|
||||
await tester.pumpAndSettle();
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await commonTestCases.tapItemByKey('exchange_confirm_page_saved_id_button_key');
|
||||
}
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/exchange/exchange_page.dart';
|
||||
import 'package:hash_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
|
||||
import 'package:cw_core/crypto_currency.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 ExchangePageRobot {
|
||||
ExchangePageRobot(this.tester)
|
||||
: commonTestCases = CommonTestCases(tester),
|
||||
authPageRobot = AuthPageRobot(tester);
|
||||
|
||||
WidgetTester tester;
|
||||
AuthPageRobot authPageRobot;
|
||||
CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isExchangePage() async {
|
||||
await commonTestCases.isSpecificPage<ExchangePage>();
|
||||
await commonTestCases.takeScreenshots('exchange_page');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
void hasResetButton() {
|
||||
commonTestCases.hasText(S.current.reset);
|
||||
}
|
||||
|
||||
void displaysPresentProviderPicker() {
|
||||
commonTestCases.hasType<PresentProviderPicker>();
|
||||
}
|
||||
|
||||
Future<void> displayBothExchangeCards() async {
|
||||
final ExchangePage exchangeCard = tester.widget<ExchangePage>(
|
||||
find.byType(ExchangePage),
|
||||
);
|
||||
|
||||
final depositKey = exchangeCard.depositKey;
|
||||
final receiveKey = exchangeCard.receiveKey;
|
||||
|
||||
final depositExchangeCard = find.byKey(depositKey);
|
||||
expect(depositExchangeCard, findsOneWidget);
|
||||
|
||||
final receiveExchangeCard = find.byKey(receiveKey);
|
||||
expect(receiveExchangeCard, findsOneWidget);
|
||||
}
|
||||
|
||||
void confirmRightComponentsDisplayOnDepositExchangeCards() {
|
||||
ExchangePage exchangePage = tester.widget(find.byType(ExchangePage));
|
||||
final exchangeViewModel = exchangePage.exchangeViewModel;
|
||||
final depositCardPrefix = 'deposit_exchange_card';
|
||||
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_title_key');
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_currency_picker_button_key');
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_selected_currency_text_key');
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_amount_textfield_key');
|
||||
|
||||
exchangePage.depositKey.currentState!.changeLimits(min: '0.1');
|
||||
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_min_limit_text_key');
|
||||
|
||||
final initialCurrency = exchangeViewModel.depositCurrency;
|
||||
if (initialCurrency.tag != null) {
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_selected_currency_tag_text_key');
|
||||
}
|
||||
|
||||
if (exchangeViewModel.hasAllAmount) {
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_send_all_button_key');
|
||||
}
|
||||
|
||||
if (exchangeViewModel.isMoneroWallet) {
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_address_book_button_key');
|
||||
}
|
||||
|
||||
if (exchangeViewModel.isDepositAddressEnabled) {
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_editable_address_textfield_key');
|
||||
} else {
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_non_editable_address_textfield_key');
|
||||
commonTestCases.hasValueKey('${depositCardPrefix}_copy_refund_address_button_key');
|
||||
}
|
||||
|
||||
// commonTestCases.hasValueKey('${depositCardPrefix}_max_limit_text_key');
|
||||
}
|
||||
|
||||
void confirmRightComponentsDisplayOnReceiveExchangeCards() {
|
||||
ExchangePage exchangePage = tester.widget(find.byType(ExchangePage));
|
||||
final exchangeViewModel = exchangePage.exchangeViewModel;
|
||||
final receiveCardPrefix = 'receive_exchange_card';
|
||||
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_title_key');
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_currency_picker_button_key');
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_selected_currency_text_key');
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_amount_textfield_key');
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_min_limit_text_key');
|
||||
|
||||
final initialCurrency = exchangeViewModel.receiveCurrency;
|
||||
if (initialCurrency.tag != null) {
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_selected_currency_tag_text_key');
|
||||
}
|
||||
|
||||
if (exchangeViewModel.hasAllAmount) {
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_send_all_button_key');
|
||||
}
|
||||
|
||||
if (exchangeViewModel.isMoneroWallet) {
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_address_book_button_key');
|
||||
}
|
||||
|
||||
commonTestCases.hasValueKey('${receiveCardPrefix}_editable_address_textfield_key');
|
||||
}
|
||||
|
||||
Future<void> selectDepositCurrency(CryptoCurrency depositCurrency) async {
|
||||
final depositPrefix = 'deposit_exchange_card';
|
||||
final currencyPickerKey = '${depositPrefix}_currency_picker_button_key';
|
||||
final currencyPickerDialogKey = '${depositPrefix}_currency_picker_dialog_button_key';
|
||||
|
||||
await commonTestCases.tapItemByKey(currencyPickerKey);
|
||||
commonTestCases.hasValueKey(currencyPickerDialogKey);
|
||||
|
||||
ExchangePage exchangePage = tester.widget(find.byType(ExchangePage));
|
||||
final exchangeViewModel = exchangePage.exchangeViewModel;
|
||||
|
||||
if (depositCurrency == exchangeViewModel.depositCurrency) {
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await commonTestCases
|
||||
.tapItemByKey('picker_items_index_${depositCurrency.name}_selected_item_button_key');
|
||||
return;
|
||||
}
|
||||
|
||||
await commonTestCases.enterText(depositCurrency.name, 'search_bar_widget_key');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
await commonTestCases.tapItemByKey('picker_items_index_${depositCurrency.name}_button_key');
|
||||
}
|
||||
|
||||
Future<void> selectReceiveCurrency(CryptoCurrency receiveCurrency) async {
|
||||
final receivePrefix = 'receive_exchange_card';
|
||||
final currencyPickerKey = '${receivePrefix}_currency_picker_button_key';
|
||||
final currencyPickerDialogKey = '${receivePrefix}_currency_picker_dialog_button_key';
|
||||
|
||||
await commonTestCases.tapItemByKey(currencyPickerKey);
|
||||
commonTestCases.hasValueKey(currencyPickerDialogKey);
|
||||
|
||||
ExchangePage exchangePage = tester.widget(find.byType(ExchangePage));
|
||||
final exchangeViewModel = exchangePage.exchangeViewModel;
|
||||
|
||||
if (receiveCurrency == exchangeViewModel.receiveCurrency) {
|
||||
await commonTestCases
|
||||
.tapItemByKey('picker_items_index_${receiveCurrency.name}_selected_item_button_key');
|
||||
return;
|
||||
}
|
||||
|
||||
await commonTestCases.enterText(receiveCurrency.name, 'search_bar_widget_key');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
await commonTestCases.tapItemByKey('picker_items_index_${receiveCurrency.name}_button_key');
|
||||
}
|
||||
|
||||
Future<void> enterDepositAmount(String amount) async {
|
||||
await commonTestCases.enterText(amount, 'deposit_exchange_card_amount_textfield_key');
|
||||
}
|
||||
|
||||
Future<void> enterDepositRefundAddress({String? depositAddress}) async {
|
||||
ExchangePage exchangePage = tester.widget(find.byType(ExchangePage));
|
||||
final exchangeViewModel = exchangePage.exchangeViewModel;
|
||||
|
||||
if (exchangeViewModel.isDepositAddressEnabled && depositAddress != null) {
|
||||
await commonTestCases.enterText(
|
||||
depositAddress, 'deposit_exchange_card_editable_address_textfield_key');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> enterReceiveAddress(String receiveAddress) async {
|
||||
await commonTestCases.enterText(
|
||||
receiveAddress,
|
||||
'receive_exchange_card_editable_address_textfield_key',
|
||||
);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onExchangeButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('exchange_page_exchange_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
bool hasMaxLimitError() {
|
||||
final maxErrorText = find.text(S.current.error_text_input_above_maximum_limit);
|
||||
|
||||
bool hasMaxError = maxErrorText.tryEvaluate();
|
||||
|
||||
return hasMaxError;
|
||||
}
|
||||
|
||||
bool hasMinLimitError() {
|
||||
final minErrorText = find.text(S.current.error_text_input_below_minimum_limit);
|
||||
|
||||
bool hasMinError = minErrorText.tryEvaluate();
|
||||
|
||||
return hasMinError;
|
||||
}
|
||||
|
||||
bool hasTradeCreationFailureError() {
|
||||
final tradeCreationFailureDialogButton =
|
||||
find.byKey(ValueKey('exchange_page_trade_creation_failure_dialog_button_key'));
|
||||
|
||||
bool hasTradeCreationFailure = tradeCreationFailureDialogButton.tryEvaluate();
|
||||
tester.printToConsole('Trade not created error: $hasTradeCreationFailure');
|
||||
return hasTradeCreationFailure;
|
||||
}
|
||||
|
||||
Future<void> onTradeCreationFailureDialogButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('exchange_page_trade_creation_failure_dialog_button_key');
|
||||
}
|
||||
|
||||
/// Handling Trade Failure Errors or errors shown through the Failure Dialog.
|
||||
///
|
||||
/// Simulating the user's flow and response when this error comes up.
|
||||
/// Examples are:
|
||||
/// - No provider can handle this trade error,
|
||||
/// - Trade amount below limit error.
|
||||
Future<void> _handleTradeCreationFailureErrors() async {
|
||||
tester.printToConsole('Inside trade creation failure handle');
|
||||
bool isTradeCreationFailure = false;
|
||||
|
||||
isTradeCreationFailure = hasTradeCreationFailureError();
|
||||
|
||||
int maxRetries = 3;
|
||||
int retries = 0;
|
||||
|
||||
while (isTradeCreationFailure && retries < maxRetries) {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await onTradeCreationFailureDialogButtonPressed();
|
||||
|
||||
await commonTestCases.defaultSleepTime(seconds: 5);
|
||||
|
||||
await onExchangeButtonPressed();
|
||||
|
||||
await _handleAuthPage();
|
||||
|
||||
isTradeCreationFailure = hasTradeCreationFailureError();
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the min limit error.
|
||||
///
|
||||
/// Simulates the user's flow and response when it comes up.
|
||||
///
|
||||
/// Has a max retry of 20 times.
|
||||
Future<void> _handleMinLimitError(String initialAmount) async {
|
||||
double amount;
|
||||
|
||||
amount = double.parse(initialAmount);
|
||||
|
||||
ExchangePage exchangePage = tester.widget(find.byType(ExchangePage));
|
||||
final exchangeViewModel = exchangePage.exchangeViewModel;
|
||||
|
||||
amount = (exchangeViewModel.limits.min ?? 0.0) + 1.0;
|
||||
|
||||
await enterDepositAmount(amount.toString());
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await onExchangeButtonPressed();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Handles the max limit error.
|
||||
///
|
||||
/// Simulates the user's flow and response when it comes up.
|
||||
///
|
||||
/// Has a max retry of 20 times.
|
||||
Future<void> _handleMaxLimitError(String initialAmount) async {
|
||||
double amount;
|
||||
|
||||
amount = double.parse(initialAmount);
|
||||
|
||||
ExchangePage exchangePage = tester.widget(find.byType(ExchangePage));
|
||||
final exchangeViewModel = exchangePage.exchangeViewModel;
|
||||
|
||||
amount = (exchangeViewModel.limits.max ?? 0.0) - 1.0;
|
||||
|
||||
await enterDepositAmount(amount.toString());
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await onExchangeButtonPressed();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> handleErrors(String initialAmount) async {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
bool isMinLimitError = hasMinLimitError();
|
||||
|
||||
if (isMinLimitError) {
|
||||
tester.printToConsole('Has min limit error');
|
||||
await _handleMinLimitError(initialAmount);
|
||||
}
|
||||
|
||||
bool isMaxLimitError = hasMaxLimitError();
|
||||
|
||||
if (isMaxLimitError) {
|
||||
await _handleMaxLimitError(initialAmount);
|
||||
}
|
||||
|
||||
tester.printToConsole('No limits error, proceeding with flow');
|
||||
|
||||
await _handleAuthPage();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await _handleTradeCreationFailureErrors();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> _handleAuthPage() async {
|
||||
final onAuthPage = authPageRobot.onAuthPage();
|
||||
if (onAuthPage) {
|
||||
await authPageRobot.enterPinCode(CommonTestConstants.pin);
|
||||
}
|
||||
|
||||
final onAuthPageDesktop = authPageRobot.onAuthPageDesktop();
|
||||
if (onAuthPageDesktop) {
|
||||
await authPageRobot.enterPassword(CommonTestConstants.pin.join(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:hash_wallet/src/screens/exchange_trade/exchange_trade_external_send_page.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class ExchangeTradeExternalSendPageRobot {
|
||||
ExchangeTradeExternalSendPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isExchangeTradeExternalSendPage() async {
|
||||
await commonTestCases.isSpecificPage<ExchangeTradeExternalSendPage>();
|
||||
await commonTestCases.takeScreenshots('exchange_trade_external_send_page');
|
||||
}
|
||||
|
||||
Future<void> verifySendDetailsItemsDisplayProperly() async {
|
||||
final widget =
|
||||
tester.widget<ExchangeTradeExternalSendPage>(find.byType(ExchangeTradeExternalSendPage));
|
||||
final exchangeTradeViewModel = widget.exchangeTradeViewModel;
|
||||
final items = exchangeTradeViewModel.items.where((item) => item.isExternalSendDetail).toList();
|
||||
|
||||
for (var item in items) {
|
||||
commonTestCases.hasValueKey('exchange_trade_external_send_page_send_item_${item.title}_key');
|
||||
tester.printToConsole('${item.title} present on screen');
|
||||
}
|
||||
|
||||
commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onContinueButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('exchange_trade_external_send_page_continue_button_key');
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:hash_wallet/core/execution_state.dart';
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class ExchangeTradePageRobot {
|
||||
ExchangeTradePageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isExchangeTradePage() async {
|
||||
await commonTestCases.isSpecificPage<ExchangeTradePage>();
|
||||
await commonTestCases.takeScreenshots('exchange_trade_page');
|
||||
}
|
||||
|
||||
void hasInformationDialog() {
|
||||
commonTestCases.hasValueKey('information_page_dialog_key');
|
||||
}
|
||||
|
||||
Future<void> onGotItButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('information_page_got_it_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onSendFromExternalButtonPressed() async {
|
||||
tester.printToConsole('Routing to send from external details page');
|
||||
|
||||
await commonTestCases.tapItemByKey(
|
||||
'exchange_trade_page_send_from_external_button_key',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onSendFromCakeButtonPressed() async {
|
||||
tester.printToConsole('Now sending from cake');
|
||||
|
||||
await commonTestCases.tapItemByKey(
|
||||
'exchange_trade_page_send_from_cake_button_key',
|
||||
shouldPumpAndSettle: false,
|
||||
);
|
||||
|
||||
final Completer<void> completer = Completer<void>();
|
||||
|
||||
// Loop to wait for the async operation to complete
|
||||
while (true) {
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
final ExchangeTradeState state = tester.state(find.byType(ExchangeTradeForm));
|
||||
final execState = state.widget.exchangeTradeViewModel.sendViewModel.state;
|
||||
|
||||
bool isDone = execState is ExecutedSuccessfullyState;
|
||||
bool isFailed = execState 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');
|
||||
|
||||
await commonTestCases.defaultSleepTime(seconds: 4);
|
||||
}
|
||||
|
||||
Future<void> onSendButtonOnConfirmSendingDialogPressed() async {
|
||||
tester.printToConsole('Send Button on Confirm Dialog Triggered');
|
||||
await commonTestCases.defaultSleepTime(seconds: 4);
|
||||
|
||||
final sendText = find.text(S.current.send);
|
||||
bool hasText = sendText.tryEvaluate();
|
||||
|
||||
if (hasText) {
|
||||
await commonTestCases.tapItemByFinder(sendText);
|
||||
|
||||
await commonTestCases.defaultSleepTime(seconds: 4);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onCancelButtonOnConfirmSendingDialogPressed() async {
|
||||
tester.printToConsole('Cancel Button on Confirm Dialog Triggered');
|
||||
|
||||
await commonTestCases.tapItemByKey(
|
||||
'exchange_trade_page_confirm_sending_dialog_cancel_button_key',
|
||||
);
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onSendFailureDialogButtonPressed() async {
|
||||
await commonTestCases.defaultSleepTime(seconds: 6);
|
||||
|
||||
tester.printToConsole('Send Button Failure Dialog Triggered');
|
||||
|
||||
await commonTestCases.tapItemByKey('exchange_trade_page_send_failure_dialog_button_key');
|
||||
}
|
||||
|
||||
Future<bool> hasErrorWhileSending() async {
|
||||
await tester.pump();
|
||||
|
||||
tester.printToConsole('Checking if there is an error');
|
||||
|
||||
final errorDialog = find.byKey(
|
||||
ValueKey('exchange_trade_page_send_failure_dialog_button_key'),
|
||||
);
|
||||
|
||||
bool hasError = errorDialog.tryEvaluate();
|
||||
|
||||
tester.printToConsole('Has error: $hasError');
|
||||
|
||||
return hasError;
|
||||
}
|
||||
|
||||
Future<void> handleConfirmSendResult() async {
|
||||
bool hasError = false;
|
||||
|
||||
hasError = await hasErrorWhileSending();
|
||||
|
||||
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 onSendFromCakeButtonPressed();
|
||||
tester.printToConsole('Confirm sending button tapped');
|
||||
|
||||
hasError = await hasErrorWhileSending();
|
||||
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (!hasError) {
|
||||
tester.printToConsole('No error, proceeding with flow');
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import "package:hash_wallet/new-ui/pages/home_page.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/coins_page/assets_history/history_tile.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/coins_page/top_bar_widget/sync_bar.dart";
|
||||
import "package:cw_core/sync_status.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class HomePageRobot extends BaseRobot {
|
||||
HomePageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
final shown = await pumpUntil(() => isInFront);
|
||||
|
||||
expect(shown, true, reason: "The home tab never came to the front");
|
||||
}
|
||||
|
||||
bool get isInFront {
|
||||
final stackFinder = find.byType(IndexedStack);
|
||||
|
||||
if (!tester.any(stackFinder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final stack = tester.widget<IndexedStack>(stackFinder.first);
|
||||
final index = stack.children.indexWhere((child) => child is NewHomePage);
|
||||
|
||||
return index >= 0 && stack.index == index;
|
||||
}
|
||||
|
||||
Future<void> hasWalletName(String name) async {
|
||||
final nameFinder = find.byKey(const ValueKey("home_page_wallet_name_text_key"));
|
||||
final syncBarFinder = find.byType(SyncBar);
|
||||
|
||||
final shown = await pumpUntil(() => tester.any(nameFinder) || tester.any(syncBarFinder));
|
||||
|
||||
expect(shown, true, reason: "Top bar showed neither the wallet name nor the sync bar");
|
||||
|
||||
if (!tester.any(nameFinder)) {
|
||||
tester.printToConsole("Sync bar is in place of the wallet name, not checking it on screen");
|
||||
return;
|
||||
}
|
||||
|
||||
expect(textByKey("home_page_wallet_name_text_key"), name);
|
||||
}
|
||||
|
||||
Future<void> openSendSheet() async {
|
||||
await tapByKey("home_page_send_button_key");
|
||||
}
|
||||
|
||||
Future<void> openReceiveSheet() async {
|
||||
await tapByKey("home_page_receive_button_key");
|
||||
}
|
||||
|
||||
Future<void> openSwapSheet() async {
|
||||
await tapByKey("home_page_swap_button_key");
|
||||
}
|
||||
|
||||
Future<void> openSettingsSheet() async {
|
||||
await tapByKey("home_page_settings_button_key");
|
||||
}
|
||||
|
||||
Future<void> openHistoryTab() async {
|
||||
final tabFinder = find.byKey(const ValueKey("line_tab_switcher_1_key"));
|
||||
|
||||
if (!tester.any(tabFinder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await tapWhenVisible(tabFinder);
|
||||
|
||||
await settle();
|
||||
}
|
||||
|
||||
Future<void> confirmTransactionHistoryVisible({
|
||||
Duration timeout = const Duration(minutes: 3),
|
||||
}) async {
|
||||
final hasTransactions = await pumpUntil(
|
||||
() => _dashboardTransactionCount() > 0,
|
||||
timeout: timeout,
|
||||
);
|
||||
|
||||
expect(
|
||||
hasTransactions,
|
||||
true,
|
||||
reason: "No transactions arrived within ${timeout.inSeconds}s",
|
||||
);
|
||||
|
||||
await openHistoryTab();
|
||||
|
||||
await pumpUntilFound(find.byType(HistoryTile));
|
||||
|
||||
expect(
|
||||
tester.widgetList(find.byType(HistoryTile)).length,
|
||||
lessThanOrEqualTo(3),
|
||||
reason: "The home page preview should never render more than the short history",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> openAllTransactions() async {
|
||||
final actionButton = find.byKey(const ValueKey("assets_history_action_button_key"));
|
||||
final historyBar = find.byKey(const ValueKey("history_top_bar_key"));
|
||||
|
||||
await tapWhenVisible(tester.any(actionButton) ? actionButton : historyBar);
|
||||
|
||||
await pumpUntilFound(find.byKey(const ValueKey("history_modal_key")));
|
||||
}
|
||||
|
||||
Future<void> confirmAllTransactionsVisible() async {
|
||||
await pumpUntilFound(_allViewTiles);
|
||||
|
||||
final rendered = tester.widgetList(_allViewTiles).length;
|
||||
|
||||
if (_dashboardTransactionCount() > 3) {
|
||||
expect(
|
||||
rendered,
|
||||
greaterThan(3),
|
||||
reason: "The All view rendered $rendered tiles, no more than the home preview does",
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
expect(_allViewTiles, findsWidgets);
|
||||
}
|
||||
|
||||
String firstTransactionIdInAllView() {
|
||||
final tile = tester.widgetList<HistoryTile>(_allViewTiles).first;
|
||||
final key = tile.key! as ValueKey<String>;
|
||||
|
||||
return key.value.substring("home_page_transaction_".length, key.value.length - "_key".length);
|
||||
}
|
||||
|
||||
Future<void> openFirstTransactionDetails() async {
|
||||
await pumpUntilFound(_allViewTiles);
|
||||
|
||||
await tester.tap(_allViewTiles.first);
|
||||
|
||||
await pumpUntilFound(find.byType(TransactionDetailsModal));
|
||||
|
||||
await settle();
|
||||
}
|
||||
|
||||
String openedTransactionId() {
|
||||
final modal = tester.widget<TransactionDetailsModal>(find.byType(TransactionDetailsModal));
|
||||
|
||||
return modal.transactionDetailsViewModel.transactionInfo.id;
|
||||
}
|
||||
|
||||
void hasTransactionIdRow() {
|
||||
expect(
|
||||
find.byKey(const ValueKey("standard_list_item_transaction_details_id_key")),
|
||||
findsOneWidget,
|
||||
reason: "The details opened without showing the transaction id",
|
||||
);
|
||||
}
|
||||
|
||||
// The home preview stays mounted underneath the modal, so its tiles have to be left out.
|
||||
Finder get _allViewTiles => find.descendant(
|
||||
of: find.byKey(const ValueKey("history_modal_key")),
|
||||
matching: find.byType(HistoryTile),
|
||||
);
|
||||
|
||||
Future<void> confirmSyncIndicatorShown({Duration timeout = const Duration(minutes: 3)}) async {
|
||||
final syncBar = find.byType(SyncBar);
|
||||
final tick = find.descendant(of: syncBar, matching: find.byIcon(Icons.check));
|
||||
|
||||
SyncStatus? lastStatus;
|
||||
var sawIndicator = false;
|
||||
|
||||
final synced = await pumpUntil(
|
||||
() {
|
||||
lastStatus = _dashboardStatus();
|
||||
|
||||
if (lastStatus == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final keyed = find.descendant(
|
||||
of: syncBar,
|
||||
matching: find.byKey(ValueKey(lastStatus.runtimeType)),
|
||||
);
|
||||
|
||||
sawIndicator = sawIndicator || tester.any(keyed) || tester.any(tick);
|
||||
|
||||
return lastStatus is SyncedSyncStatus;
|
||||
},
|
||||
timeout: timeout,
|
||||
);
|
||||
|
||||
expect(
|
||||
synced,
|
||||
true,
|
||||
reason: "The wallet never synced within ${timeout.inMinutes}m, "
|
||||
"it reported ${lastStatus.runtimeType}",
|
||||
);
|
||||
|
||||
expect(
|
||||
sawIndicator,
|
||||
true,
|
||||
reason: "The sync bar never showed a status while the wallet synced",
|
||||
);
|
||||
}
|
||||
|
||||
SyncStatus? _dashboardStatus() {
|
||||
final finder = find.byType(NewHomePage);
|
||||
|
||||
if (!tester.any(finder)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tester.widget<NewHomePage>(finder.first).dashboardViewModel.status;
|
||||
}
|
||||
|
||||
int _dashboardTransactionCount() {
|
||||
final finder = find.byType(NewHomePage);
|
||||
|
||||
if (!tester.any(finder)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tester.widget<NewHomePage>(finder.first).dashboardViewModel.transactions.length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class LightningUsernamePageRobot extends BaseRobot {
|
||||
LightningUsernamePageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await pumpUntilFound(find.byKey(const ValueKey("lightning_username_page_skip_button_key")));
|
||||
}
|
||||
|
||||
Future<void> tapSkip() async {
|
||||
await tapByKey("lightning_username_page_skip_button_key");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import "package:hash_wallet/generated/i18n.dart";
|
||||
import "package:hash_wallet/src/screens/nodes/widgets/node_list_row.dart";
|
||||
import "package:hash_wallet/src/screens/settings/manage_nodes_page.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class ManageNodesPageRobot extends BaseRobot {
|
||||
ManageNodesPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<ManageNodesPage>();
|
||||
}
|
||||
|
||||
String currentNodeUri() {
|
||||
final page = tester.widget<ManageNodesPage>(find.byType(ManageNodesPage));
|
||||
|
||||
return page.nodeListViewModel.currentNode.uriRaw;
|
||||
}
|
||||
|
||||
Future<void> switchToAnotherNode() async {
|
||||
final rows = find.byType(NodeListRow);
|
||||
|
||||
await pumpUntilFound(rows.first);
|
||||
|
||||
expect(
|
||||
tester.widgetList(rows).length,
|
||||
greaterThan(1),
|
||||
reason: "Only the current node is listed, there is nothing to switch to",
|
||||
);
|
||||
|
||||
await tapWhenVisible(rows.at(1));
|
||||
|
||||
await tapWhenVisible(find.text(S.current.change));
|
||||
|
||||
await settle();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class NewDashboardRobot extends BaseRobot {
|
||||
NewDashboardRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed({Duration timeout = const Duration(seconds: 30)}) async {
|
||||
await pumpUntilFound(find.byKey(const ValueKey("new_dashboard_page_key")), timeout: timeout);
|
||||
}
|
||||
|
||||
Future<void> openHomeTab() async {
|
||||
await tapByKey("dashboard_page_home_action_button_key");
|
||||
}
|
||||
|
||||
Future<void> openWalletsTab() async {
|
||||
await tapByKey("dashboard_page_wallets_action_button_key");
|
||||
}
|
||||
|
||||
Future<void> openContactsTab() async {
|
||||
await tapByKey("dashboard_page_contacts_action_button_key");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import "package:hash_wallet/new-ui/pages/receive_page.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class NewReceivePageRobot extends BaseRobot {
|
||||
NewReceivePageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await pumpUntilFound(find.byType(NewReceivePage));
|
||||
}
|
||||
|
||||
Future<void> confirmAddressMatches(String expected) async {
|
||||
final wanted = _normalise(expected);
|
||||
|
||||
await pumpUntilFound(find.byKey(const ValueKey("receive_page_address_key")));
|
||||
|
||||
final matched = await pumpUntil(() => _renderedAddress() == wanted);
|
||||
|
||||
expect(
|
||||
matched,
|
||||
true,
|
||||
reason: "Receive page should show $wanted but showed ${_renderedAddress()}",
|
||||
);
|
||||
}
|
||||
|
||||
String _normalise(String address) =>
|
||||
address.replaceAll("bitcoincash:", "").replaceAll(RegExp(r"\s"), "");
|
||||
|
||||
String? _renderedAddress() {
|
||||
final root = find.byKey(const ValueKey("receive_page_address_key"));
|
||||
|
||||
if (!tester.any(root)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final rich = find.descendant(of: root, matching: find.byType(RichText));
|
||||
|
||||
if (tester.any(rich)) {
|
||||
final text = _normalise(tester.widget<RichText>(rich.first).text.toPlainText());
|
||||
|
||||
if (text.isNotEmpty) {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
final plain = find.descendant(of: root, matching: find.byType(Text));
|
||||
|
||||
if (tester.any(plain)) {
|
||||
return _normalise(tester.widget<Text>(plain.first).data ?? "");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
import "package:hash_wallet/core/execution_state.dart";
|
||||
import "package:hash_wallet/new-ui/pages/send_page.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/confirm_swiper.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/new_primary_button.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/send_page/send_confirm_bottom_widget.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/send_page/send_confirm_sheet.dart";
|
||||
import "package:hash_wallet/view_model/send/send_view_model.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
import "../core/test_config.dart";
|
||||
|
||||
class NewSendPageRobot extends BaseRobot {
|
||||
NewSendPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await pumpUntilFound(find.byType(NewSendPage));
|
||||
}
|
||||
|
||||
Future<void> enterAddress(String address) async {
|
||||
await _enterTextInInput("send_page_address_input_key", address);
|
||||
}
|
||||
|
||||
Future<void> enterAmount(String amount) async {
|
||||
await _enterTextInInput("send_page_amount_input_key", amount);
|
||||
}
|
||||
|
||||
String enteredAddress() => _textInInput("send_page_address_input_key");
|
||||
|
||||
Future<void> pickContactFromAddressBook(String contactName) async {
|
||||
await tapByKey("send_page_address_book_button_key");
|
||||
|
||||
final tabs = find.byType(Tab);
|
||||
|
||||
await pumpUntilFound(tabs);
|
||||
|
||||
await tester.tap(tabs.at(1));
|
||||
|
||||
await settle();
|
||||
|
||||
final contactFinder = find.byKey(ValueKey(contactName));
|
||||
|
||||
await pumpUntilFound(contactFinder);
|
||||
|
||||
await tester.tap(contactFinder.first);
|
||||
|
||||
await pumpUntilGone(contactFinder);
|
||||
}
|
||||
|
||||
Future<void> tapSendButton() async {
|
||||
await tapByKey("send_page_send_button_key");
|
||||
}
|
||||
|
||||
Future<void> tapSendButtonWhenReady({Duration timeout = TestConfig.walletSyncBudget}) async {
|
||||
final ready = await pumpUntil(() => _model()?.isReadyForSend ?? false, timeout: timeout);
|
||||
|
||||
expect(
|
||||
ready,
|
||||
true,
|
||||
reason: "The wallet never became ready to send within ${timeout.inMinutes}m, "
|
||||
"so the send button stayed disabled",
|
||||
);
|
||||
|
||||
await tapSendButton();
|
||||
}
|
||||
|
||||
bool get isSendFlowOpen =>
|
||||
tester.any(find.byType(NewSendPage)) || tester.any(find.byType(SendConfirmSheet));
|
||||
|
||||
Future<void> swipeToConfirm({Duration timeout = const Duration(seconds: 90)}) async {
|
||||
final finder = find.byKey(const ValueKey("send_page_confirm_swiper_key"));
|
||||
|
||||
await pumpUntilFound(finder, timeout: timeout);
|
||||
|
||||
await settle(max: const Duration(seconds: 2));
|
||||
|
||||
final width = tester.view.physicalSize.width / tester.view.devicePixelRatio;
|
||||
await tester.drag(finder.first, Offset(width, 0));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
Future<void> confirmSendButtonReplacesSwiper() async {
|
||||
final swiper = find.byKey(const ValueKey("send_page_confirm_swiper_key"));
|
||||
|
||||
await pumpUntilFound(swiper);
|
||||
|
||||
tester.binding.platformDispatcher.accessibilityFeaturesTestValue =
|
||||
const FakeAccessibilityFeatures(accessibleNavigation: true);
|
||||
addTearDown(tester.binding.platformDispatcher.clearAccessibilityFeaturesTestValue);
|
||||
|
||||
await settle(max: const Duration(seconds: 2));
|
||||
|
||||
final button = find.descendant(of: swiper, matching: find.byType(NewPrimaryButton));
|
||||
|
||||
expect(
|
||||
tester.any(button),
|
||||
true,
|
||||
reason: "Accessible navigation is on but the sheet still only offers the swiper",
|
||||
);
|
||||
|
||||
expect(
|
||||
tester.widget<NewPrimaryButton>(button.first).onPressed,
|
||||
isNotNull,
|
||||
reason: "The send button a screen reader gets is disabled",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> confirmTransactionBuilt({Duration timeout = const Duration(minutes: 3)}) async {
|
||||
await pumpUntil(
|
||||
() => tester.any(find.byType(ConfirmSwiper)) || _model()?.state is FailureState,
|
||||
timeout: timeout,
|
||||
);
|
||||
|
||||
if (tester.any(find.byType(ConfirmSwiper))) {
|
||||
return;
|
||||
}
|
||||
|
||||
fail("No confirm sheet after ${timeout.inSeconds}s. ${_describeSendState()}");
|
||||
}
|
||||
|
||||
Future<void> confirmTransactionCommitted({
|
||||
Duration timeout = const Duration(minutes: 3),
|
||||
}) async {
|
||||
await pumpUntilFound(find.byType(TransactionCommittedScreenActionButton), timeout: timeout);
|
||||
}
|
||||
|
||||
Future<void> expectNoTransactionBuilt({
|
||||
Duration window = const Duration(seconds: 15),
|
||||
}) async {
|
||||
final offered = await pumpUntil(
|
||||
() => tester.any(find.byType(ConfirmSwiper)),
|
||||
timeout: window,
|
||||
);
|
||||
|
||||
expect(
|
||||
offered,
|
||||
false,
|
||||
reason:
|
||||
"The send screen showed the swiper, wanting to broadcast a transaction it should have refused",
|
||||
);
|
||||
}
|
||||
|
||||
SendViewModel? _model() {
|
||||
final sheet = find.byType(SendConfirmSheet);
|
||||
|
||||
if (tester.any(sheet)) {
|
||||
return tester.widget<SendConfirmSheet>(sheet.first).sendViewModel;
|
||||
}
|
||||
|
||||
final page = find.byType(NewSendPage);
|
||||
|
||||
if (tester.any(page)) {
|
||||
return tester.widget<NewSendPage>(page.first).sendViewModel;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
String _describeSendState() {
|
||||
final model = _model();
|
||||
|
||||
final available = model?.wallet.balance.values
|
||||
.map((balance) => balance.available)
|
||||
.where((amount) => amount.sign > 0)
|
||||
.join(", ");
|
||||
|
||||
final errorFinder = find.byType(TransactionErrorActions);
|
||||
final error = tester.any(errorFinder)
|
||||
? tester.widget<TransactionErrorActions>(errorFinder.first).errorText
|
||||
: model?.state is FailureState
|
||||
? (model!.state as FailureState).error
|
||||
: null;
|
||||
|
||||
return "state=${model?.state.runtimeType}, "
|
||||
"wallet holds ${available == null || available.isEmpty ? "nothing" : available}"
|
||||
"${error == null ? "" : ", the chain said: $error"}";
|
||||
}
|
||||
|
||||
String _textInInput(String inputKey) {
|
||||
final inputFinder = find.byKey(ValueKey(inputKey));
|
||||
|
||||
if (!tester.any(inputFinder)) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
final field = find.descendant(of: inputFinder, matching: find.byType(EditableText));
|
||||
|
||||
return tester.widget<EditableText>(field.first).controller.text;
|
||||
}
|
||||
|
||||
Future<void> _enterTextInInput(String inputKey, String text) async {
|
||||
final inputFinder = find.byKey(ValueKey(inputKey));
|
||||
|
||||
await pumpUntilFound(inputFinder);
|
||||
|
||||
final fieldFinder = find.descendant(of: inputFinder, matching: find.byType(EditableText));
|
||||
|
||||
await tester.enterText(fieldFinder.first, text);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import "package:hash_wallet/new-ui/pages/settings_page.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class NewSettingsPageRobot extends BaseRobot {
|
||||
NewSettingsPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await pumpUntilFound(find.byType(SettingsMainPage));
|
||||
}
|
||||
|
||||
Future<void> openRow(String route) async {
|
||||
await scrollUntilRowVisible(route);
|
||||
await tapByKey(route);
|
||||
}
|
||||
|
||||
Future<void> scrollUntilRowVisible(String route) async {
|
||||
final rowFinder = find.byKey(ValueKey(route));
|
||||
|
||||
if (tester.any(rowFinder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final scrollableFinder = find.descendant(
|
||||
of: find.byType(SettingsMainPage),
|
||||
matching: find.byType(Scrollable),
|
||||
);
|
||||
|
||||
await tester.scrollUntilVisible(
|
||||
rowFinder,
|
||||
200,
|
||||
scrollable: scrollableFinder.first,
|
||||
maxScrolls: 30,
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
Future<void> confirmLeafPageDisplayed(Type pageType) async {
|
||||
await pumpUntilFound(find.byType(pageType));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
import "package:hash_wallet/core/execution_state.dart";
|
||||
import "package:hash_wallet/exchange/limits_state.dart";
|
||||
import "package:hash_wallet/new-ui/pages/swap_page.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/currency_picker/currency_picker_row.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/currency_picker/currency_picker_search_field.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/swap_page/swap_amount_box.dart";
|
||||
import "package:hash_wallet/new-ui/widgets/swap_page/swap_confirm_sheet.dart";
|
||||
import "package:hash_wallet/src/widgets/primary_button.dart";
|
||||
import "package:hash_wallet/view_model/send/send_view_model_state.dart";
|
||||
import "package:cw_core/crypto_currency.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
import "../core/test_config.dart";
|
||||
import "../core/test_wallets.dart";
|
||||
|
||||
class NewSwapPageRobot extends BaseRobot {
|
||||
NewSwapPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await pumpUntilFound(find.byType(NewSwapPage));
|
||||
}
|
||||
|
||||
Future<void> enterDepositAmount(String amount) async {
|
||||
await enterTextByKey("swap_page_deposit_amount_field_key", amount);
|
||||
}
|
||||
|
||||
Future<void> chooseReceiveCurrency(CryptoCurrency currency) async {
|
||||
await tapByKey("swap_amount_box_receive_currency_button_key");
|
||||
|
||||
final search = find.descendant(
|
||||
of: find.byType(CurrencyPickerSearchField),
|
||||
matching: find.byType(EditableText),
|
||||
);
|
||||
|
||||
await pumpUntilFound(search);
|
||||
|
||||
await tester.enterText(search.first, currency.title);
|
||||
await settle(max: const Duration(seconds: 2));
|
||||
|
||||
await tapWhenVisible(
|
||||
find.byWidgetPredicate(
|
||||
(widget) => widget is CurrencyPickerRow && _isSameCurrency(widget.currency, currency),
|
||||
),
|
||||
);
|
||||
|
||||
final chosen = await pumpUntil(
|
||||
() => _isSameCurrency(
|
||||
tester.widget<NewSwapPage>(find.byType(NewSwapPage)).exchangeViewModel.receiveCurrency,
|
||||
currency,
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
chosen,
|
||||
true,
|
||||
reason: "The picker did not switch the receive currency to ${currency.title}",
|
||||
);
|
||||
}
|
||||
|
||||
static bool _isSameCurrency(CryptoCurrency one, CryptoCurrency other) =>
|
||||
one.title.toLowerCase() == other.title.toLowerCase() &&
|
||||
(one.tag ?? "").toLowerCase() == (other.tag ?? "").toLowerCase();
|
||||
|
||||
Future<void> confirmQuoteReceived({Duration timeout = const Duration(seconds: 90)}) async {
|
||||
final received = await pumpUntil(() => _bestRate() > 0, timeout: timeout);
|
||||
|
||||
expect(received, true, reason: "No provider quote arrived within ${timeout.inSeconds}s");
|
||||
}
|
||||
|
||||
Future<void> enterMinimumViableDepositAmount() async {
|
||||
final limitsLoaded = await pumpUntil(
|
||||
() => _limitsState() is LimitsLoadedSuccessfully,
|
||||
timeout: const Duration(seconds: 90),
|
||||
);
|
||||
|
||||
expect(limitsLoaded, true, reason: "Provider limits never loaded");
|
||||
|
||||
final min = _limitsMin() ?? 0;
|
||||
|
||||
// 5 percent over the minimum, so a small rate move does not invalidate the trade.
|
||||
final amount = min > 0 ? (min * 1.05).toStringAsFixed(8) : "0.01";
|
||||
await enterDepositAmount(amount);
|
||||
}
|
||||
|
||||
Future<String> enterTestReceiveAddress() async {
|
||||
final exchangeViewModel =
|
||||
tester.widget<NewSwapPage>(find.byType(NewSwapPage)).exchangeViewModel;
|
||||
final receiveType = cryptoCurrencyOrTokenToWalletType(exchangeViewModel.receiveCurrency);
|
||||
final address = receiveType == null ? "" : TestWallets.receiveAddressFor(receiveType);
|
||||
|
||||
expect(
|
||||
address.isNotEmpty,
|
||||
true,
|
||||
reason: "No test receive address for ${exchangeViewModel.receiveCurrency.title}",
|
||||
);
|
||||
|
||||
final receiveCard = find.byWidgetPredicate(
|
||||
(widget) => widget is SwapAmountBox && widget.isReceiverCard,
|
||||
);
|
||||
|
||||
tester.state<SwapAmountBoxState>(receiveCard).addressController.text = address;
|
||||
await settle();
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
Future<void> confirmSwapButtonEnabled({Duration timeout = TestConfig.walletSyncBudget}) async {
|
||||
final button = find.byKey(const ValueKey("exchange_page_exchange_button_key"));
|
||||
|
||||
final enabled = await pumpUntil(
|
||||
() => tester.any(button) && !tester.widget<LoadingPrimaryButton>(button).isDisabled,
|
||||
timeout: timeout,
|
||||
);
|
||||
|
||||
expect(
|
||||
enabled,
|
||||
true,
|
||||
reason: "The swap button stayed disabled with an amount, a quote and a receive address",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> tapSwapButton() async {
|
||||
await tapByKey("exchange_page_exchange_button_key");
|
||||
}
|
||||
|
||||
Future<void> confirmTradeCreated({Duration timeout = const Duration(minutes: 2)}) async {
|
||||
await pumpUntilFound(find.byType(SwapConfirmSheet), timeout: timeout);
|
||||
}
|
||||
|
||||
void confirmPayoutAddress(String expected) {
|
||||
final sheet = tester.widget<SwapConfirmSheet>(find.byType(SwapConfirmSheet));
|
||||
final payout = sheet.exchangeTradeViewModel.trade.payoutAddress ?? "";
|
||||
final isHex = expected.startsWith("0x");
|
||||
|
||||
expect(
|
||||
isHex ? payout.toLowerCase() : payout,
|
||||
isHex ? expected.toLowerCase() : expected,
|
||||
reason: "The trade pays out somewhere other than the test wallet",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> confirmDepositCommitted({Duration timeout = const Duration(minutes: 3)}) async {
|
||||
ExecutionState? lastState;
|
||||
|
||||
final committed = await pumpUntil(
|
||||
() {
|
||||
final sheet = find.byType(SwapConfirmSheet);
|
||||
|
||||
if (!tester.any(sheet)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
lastState =
|
||||
tester.widget<SwapConfirmSheet>(sheet).exchangeTradeViewModel.sendViewModel.state;
|
||||
|
||||
return lastState is TransactionCommitted;
|
||||
},
|
||||
timeout: timeout,
|
||||
);
|
||||
|
||||
expect(
|
||||
committed,
|
||||
true,
|
||||
reason: "The deposit never committed within ${timeout.inMinutes}m, "
|
||||
"the sheet reported ${lastState.runtimeType}",
|
||||
);
|
||||
|
||||
expect(
|
||||
lastState,
|
||||
isA<TransactionCommitted>(),
|
||||
reason: "The confirm sheet closed before the deposit committed, "
|
||||
"it last reported ${lastState.runtimeType}",
|
||||
);
|
||||
|
||||
await pumpUntilGone(find.byType(NewSwapPage));
|
||||
}
|
||||
|
||||
double? _limitsMin() {
|
||||
final finder = find.byType(NewSwapPage);
|
||||
|
||||
if (!tester.any(finder)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tester.widget<NewSwapPage>(finder.first).exchangeViewModel.limits.min;
|
||||
}
|
||||
|
||||
LimitsState? _limitsState() {
|
||||
final finder = find.byType(NewSwapPage);
|
||||
|
||||
if (!tester.any(finder)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tester.widget<NewSwapPage>(finder.first).exchangeViewModel.limitsState;
|
||||
}
|
||||
|
||||
double _bestRate() {
|
||||
final finder = find.byType(NewSwapPage);
|
||||
|
||||
if (!tester.any(finder)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tester.widget<NewSwapPage>(finder.first).exchangeViewModel.bestRate;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,32 @@
|
||||
import 'package:hash_wallet/src/screens/new_wallet/new_wallet_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/new_wallet/new_wallet_page.dart";
|
||||
import "package:hash_wallet/src/widgets/alert_with_one_action.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class NewWalletPageRobot {
|
||||
NewWalletPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class NewWalletPageRobot extends BaseRobot {
|
||||
NewWalletPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isNewWalletPage() async {
|
||||
await commonTestCases.isSpecificPage<NewWalletPage>();
|
||||
await commonTestCases.takeScreenshots('new_wallet_page');
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<NewWalletPage>();
|
||||
}
|
||||
|
||||
Future<void> enterWalletName(String walletName) async {
|
||||
await commonTestCases.enterText(
|
||||
walletName,
|
||||
'new_wallet_page_wallet_name_textformfield_key',
|
||||
);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await enterTextByKey("new_wallet_page_wallet_name_textformfield_key", walletName);
|
||||
}
|
||||
|
||||
Future<void> generateWalletName() async {
|
||||
await commonTestCases.tapItemByKey(
|
||||
'new_wallet_page_wallet_name_textformfield_generate_name_button_key',
|
||||
);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("new_wallet_page_wallet_name_textformfield_generate_name_button_key");
|
||||
}
|
||||
|
||||
Future<void> expectNameRejected() async {
|
||||
await pumpUntilFound(find.byType(AlertWithOneAction));
|
||||
|
||||
hasType<NewWalletPage>();
|
||||
}
|
||||
|
||||
Future<void> onNextButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('new_wallet_page_confirm_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("new_wallet_page_confirm_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,29 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
||||
import 'package:hash_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/new_wallet/new_wallet_type_page.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class NewWalletTypePageRobot {
|
||||
NewWalletTypePageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class NewWalletTypePageRobot extends BaseRobot {
|
||||
NewWalletTypePageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isNewWalletTypePage() async {
|
||||
await commonTestCases.isSpecificPage<NewWalletTypePage>();
|
||||
await commonTestCases.takeScreenshots('new_wallet_type_page');
|
||||
}
|
||||
|
||||
void displaysCorrectTitle(bool isCreate) {
|
||||
commonTestCases.hasText(
|
||||
isCreate ? S.current.wallet_list_create_new_wallet : S.current.wallet_list_restore_wallet,
|
||||
);
|
||||
}
|
||||
|
||||
void hasWalletTypeForm() {
|
||||
commonTestCases.hasType<WalletTypeForm>();
|
||||
}
|
||||
|
||||
void displaysCorrectImage(ThemeType type) {
|
||||
final walletTypeImage = Image.asset('assets/images/wallet_type.png').image;
|
||||
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png').image;
|
||||
|
||||
find.image(
|
||||
type == ThemeType.dark ? walletTypeImage : walletTypeLightImage,
|
||||
);
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<NewWalletTypePage>();
|
||||
}
|
||||
|
||||
Future<void> findParticularWalletTypeInScrollableList(WalletType type) async {
|
||||
await tester.pump(Duration(milliseconds: 1000));
|
||||
|
||||
final scrollableWidget = find.descendant(
|
||||
of: find.byKey(Key('new_wallet_type_scrollable_key')),
|
||||
of: find.byKey(const Key("new_wallet_type_scrollable_key")),
|
||||
matching: find.byType(Scrollable),
|
||||
);
|
||||
|
||||
final targetWidget = find.byKey(ValueKey('new_wallet_type_${type.name}_button_key'));
|
||||
final targetWidget = find.byKey(ValueKey("new_wallet_type_${type.name}_button_key"));
|
||||
|
||||
tester.printToConsole('Attempting to scroll to wallet type ${type.name}');
|
||||
tester.printToConsole("Attempting to scroll to wallet type ${type.name}");
|
||||
|
||||
await tester.pump(Duration(milliseconds: 500));
|
||||
await pumpUntilFound(scrollableWidget.first);
|
||||
|
||||
await tester.scrollUntilVisible(
|
||||
targetWidget,
|
||||
@@ -58,12 +32,14 @@ class NewWalletTypePageRobot {
|
||||
maxScrolls: 20,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 1000));
|
||||
await settle();
|
||||
|
||||
expect(tester.any(targetWidget), true,
|
||||
reason: 'Wallet type ${type.name} should be visible after scrolling');
|
||||
expect(
|
||||
tester.any(targetWidget),
|
||||
true,
|
||||
reason: "Wallet type ${type.name} should be visible after scrolling",
|
||||
);
|
||||
|
||||
// Additional check to ensure the widget is actually on screen
|
||||
final widgetRect = tester.getRect(targetWidget);
|
||||
final screenSize = tester.view.physicalSize;
|
||||
final screenRect = Rect.fromLTWH(0, 0, screenSize.width, screenSize.height);
|
||||
@@ -71,17 +47,13 @@ class NewWalletTypePageRobot {
|
||||
expect(
|
||||
screenRect.overlaps(widgetRect),
|
||||
true,
|
||||
reason: 'Wallet type ${type.name} should be within screen bounds',
|
||||
reason: "Wallet type ${type.name} should be within screen bounds",
|
||||
);
|
||||
|
||||
tester.printToConsole('Wallet type ${type.name} is now visible and tappable');
|
||||
tester.printToConsole("Wallet type ${type.name} is now visible and tappable");
|
||||
}
|
||||
|
||||
Future<void> selectWalletType(WalletType type) async {
|
||||
await commonTestCases.tapItemByKey('new_wallet_type_${type.name}_button_key');
|
||||
}
|
||||
|
||||
Future<void> onNextButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('new_wallet_type_next_button_key');
|
||||
await tapByKey("new_wallet_type_${type.name}_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,24 @@
|
||||
import 'package:hash_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/pin_code/pin_code_widget.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class PinCodeWidgetRobot {
|
||||
PinCodeWidgetRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class PinCodeWidgetRobot extends BaseRobot {
|
||||
PinCodeWidgetRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
@override
|
||||
Future<void> isDisplayed() async => hasPinCodeWidget();
|
||||
|
||||
void hasPinCodeWidget() {
|
||||
final pinCodeWidget = find.bySubtype<PinCodeWidget>();
|
||||
expect(pinCodeWidget, findsOneWidget);
|
||||
}
|
||||
|
||||
void hasNumberButtonsVisible() {
|
||||
// Confirmation for buttons 1-9
|
||||
for (var i = 1; i < 10; i++) {
|
||||
commonTestCases.hasValueKey('pin_code_button_${i}_key');
|
||||
Future<void> enterPinCode(List<int> pinCode) async {
|
||||
for (final pin in pinCode) {
|
||||
await tapByKey("pin_code_button_${pin}_key");
|
||||
}
|
||||
|
||||
// Confirmation for 0 button
|
||||
commonTestCases.hasValueKey('pin_code_button_0_key');
|
||||
}
|
||||
|
||||
Future<void> enterPassword(String password, {int pumpDuration = 100}) async {
|
||||
await commonTestCases.enterText(
|
||||
password,
|
||||
'enter_wallet_password',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await commonTestCases.tapItemByKey(
|
||||
'unlock',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> enterPinCode(List<int> pinCode, {int pumpDuration = 100}) async {
|
||||
for (int pin in pinCode) {
|
||||
await commonTestCases.tapItemByKey(
|
||||
'pin_code_button_${pin}_key',
|
||||
pumpDuration: pumpDuration,
|
||||
);
|
||||
}
|
||||
|
||||
await commonTestCases.takeScreenshots('pin_code_widget');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await settle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import 'package:hash_wallet/src/screens/seed/pre_seed_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/new-ui/pages/seed/pre_seed_page.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class PreSeedPageRobot {
|
||||
PreSeedPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class PreSeedPageRobot extends BaseRobot {
|
||||
PreSeedPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isPreSeedPage() async {
|
||||
await commonTestCases.isSpecificPage<PreSeedPage>();
|
||||
await commonTestCases.takeScreenshots('pre_seed_page');
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<PreSeedPage>();
|
||||
}
|
||||
|
||||
// The button is only built once all three boxes are ticked
|
||||
Future<void> onConfirmButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('pre_seed_page_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("pre_seed_page_only_way_checkbox_key");
|
||||
await tapByKey("pre_seed_page_write_down_checkbox_key");
|
||||
await tapByKey("pre_seed_page_never_share_checkbox_key");
|
||||
await tapByKey("pre_seed_page_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,123 +1,101 @@
|
||||
import 'package:hash_wallet/entities/seed_type.dart';
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/restore/wallet_restore_page.dart';
|
||||
import 'package:hash_wallet/src/widgets/validable_annotated_editable_text.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/entities/seed_type.dart";
|
||||
import "package:hash_wallet/src/screens/restore/wallet_restore_page.dart";
|
||||
import "package:hash_wallet/src/widgets/primary_button.dart";
|
||||
import "package:hash_wallet/src/widgets/validable_annotated_editable_text.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class RestoreFromSeedOrKeysPageRobot {
|
||||
RestoreFromSeedOrKeysPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class RestoreFromSeedOrKeysPageRobot extends BaseRobot {
|
||||
RestoreFromSeedOrKeysPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isRestoreFromSeedKeyPage() async {
|
||||
await commonTestCases.isSpecificPage<WalletRestorePage>();
|
||||
await commonTestCases.takeScreenshots('wallet_restore_page');
|
||||
}
|
||||
|
||||
Future<void> confirmViewComponentsDisplayProperlyPerPageView() async {
|
||||
commonTestCases.hasText(S.current.wallet_name);
|
||||
commonTestCases.hasText(S.current.enter_seed_phrase);
|
||||
commonTestCases.hasText(S.current.restore_title_from_seed);
|
||||
|
||||
commonTestCases.hasValueKey('wallet_restore_from_seed_wallet_name_textfield_key');
|
||||
commonTestCases.hasValueKey('wallet_restore_from_seed_wallet_name_refresh_button_key');
|
||||
commonTestCases.hasValueKey('wallet_restore_from_seed_wallet_seeds_paste_button_key');
|
||||
commonTestCases.hasValueKey('wallet_restore_from_seed_wallet_seeds_textfield_key');
|
||||
|
||||
commonTestCases.hasText(S.current.private_key, hasWidget: false);
|
||||
commonTestCases.hasText(S.current.restore_title_from_keys, hasWidget: false);
|
||||
|
||||
await commonTestCases.swipePage();
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
commonTestCases.hasText(S.current.wallet_name);
|
||||
commonTestCases.hasText(S.current.private_key);
|
||||
commonTestCases.hasText(S.current.restore_title_from_keys);
|
||||
|
||||
commonTestCases.hasText(S.current.enter_seed_phrase, hasWidget: false);
|
||||
commonTestCases.hasText(S.current.restore_title_from_seed, hasWidget: false);
|
||||
|
||||
await commonTestCases.swipePage(swipeRight: false);
|
||||
}
|
||||
|
||||
void confirmRestoreButtonDisplays() {
|
||||
commonTestCases.hasValueKey('wallet_restore_seed_or_key_restore_button_key');
|
||||
}
|
||||
|
||||
void confirmAdvancedSettingButtonDisplays() {
|
||||
commonTestCases.hasValueKey('wallet_restore_advanced_settings_button_key');
|
||||
}
|
||||
|
||||
Future<void> enterWalletNameText(String walletName, {bool isSeedFormEntry = true}) async {
|
||||
await commonTestCases.enterText(
|
||||
walletName,
|
||||
'wallet_restore_from_${isSeedFormEntry ? 'seed' : 'keys'}_wallet_name_textfield_key',
|
||||
);
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WalletRestorePage>();
|
||||
}
|
||||
|
||||
Future<void> selectWalletNameFromAvailableOptions({bool isSeedFormEntry = true}) async {
|
||||
await commonTestCases.tapItemByKey(
|
||||
await tapByKey(
|
||||
'wallet_restore_from_${isSeedFormEntry ? 'seed' : 'keys'}_wallet_name_refresh_button_key',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> enterSeedPhraseForWalletRestore(String text) async {
|
||||
ValidatableAnnotatedEditableTextState seedTextState =
|
||||
await tester.state(find.byType(ValidatableAnnotatedEditableText));
|
||||
final ValidatableAnnotatedEditableTextState seedTextState =
|
||||
tester.state(find.byType(ValidatableAnnotatedEditableText));
|
||||
|
||||
seedTextState.widget.controller.text = text;
|
||||
await tester.pumpAndSettle();
|
||||
await settle();
|
||||
}
|
||||
|
||||
Future<void> enterPasswordForWalletRestore(String text) async {
|
||||
await commonTestCases.enterText(
|
||||
text,
|
||||
'password',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await enterTextByKey("password", text);
|
||||
await settle();
|
||||
}
|
||||
|
||||
Future<void> enterPasswordRepeatForWalletRestore(String text) async {
|
||||
await commonTestCases.enterText(
|
||||
text,
|
||||
'repeat_wallet_password',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await enterTextByKey("repeat_wallet_password", text);
|
||||
await settle();
|
||||
}
|
||||
|
||||
Future<void> enterBlockHeightForWalletRestore(String blockHeight) async {
|
||||
await commonTestCases.enterText(
|
||||
blockHeight,
|
||||
'wallet_restore_from_seed_blockheight_textfield_key',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await enterTextByKey("wallet_restore_from_seed_blockheight_textfield_key", blockHeight);
|
||||
await settle();
|
||||
}
|
||||
|
||||
Future<void> chooseSeedTypeForMoneroOrWowneroWallets(MoneroSeedType selectedType) async {
|
||||
await commonTestCases.tapItemByKey('wallet_restore_from_seed_seedtype_picker_button_key');
|
||||
await tapByKey("wallet_restore_from_seed_seedtype_picker_button_key");
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
await commonTestCases.tapItemByKey('picker_items_index_${selectedType.title}_button_key');
|
||||
await tapByKey("picker_items_index_${selectedType.title}_button_key");
|
||||
}
|
||||
|
||||
Future<void> onPasteSeedPhraseButtonPressed() async {
|
||||
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',
|
||||
Future<void> expectRestoreRefused({Duration window = const Duration(seconds: 10)}) async {
|
||||
final button = tester.widget<LoadingPrimaryButton>(
|
||||
find.byKey(const ValueKey("wallet_restore_seed_or_key_restore_button_key")),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
button.isDisabled,
|
||||
true,
|
||||
reason: "The restore button accepted a seed the wallet cannot parse",
|
||||
);
|
||||
|
||||
final left = await pumpUntil(
|
||||
() => !tester.any(find.byType(WalletRestorePage)),
|
||||
timeout: window,
|
||||
);
|
||||
|
||||
expect(left, false, reason: "A seed the wallet cannot parse still restored something");
|
||||
}
|
||||
|
||||
Future<void> onRestoreWalletButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_restore_seed_or_key_restore_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("wallet_restore_seed_or_key_restore_button_key");
|
||||
}
|
||||
|
||||
Future<void> enablePassphrase() async {
|
||||
await tapByKey("wallet_restore_has_passphrase_checkbox_key");
|
||||
await settle();
|
||||
|
||||
final page = tester.widget<WalletRestorePage>(find.byType(WalletRestorePage));
|
||||
|
||||
expect(
|
||||
page.walletRestoreViewModel.hasPassphrase,
|
||||
true,
|
||||
reason: "The passphrase box did not tick, the wallet would restore without it",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> enterPassphrase(String passphrase) async {
|
||||
await enterTextByKey(
|
||||
"add_passphrase_bottom_sheet_widget_passphrase_textfield_key",
|
||||
passphrase,
|
||||
);
|
||||
await enterTextByKey(
|
||||
"add_passphrase_bottom_sheet_widget_confirm_passphrase_textfield_key",
|
||||
passphrase,
|
||||
);
|
||||
|
||||
await tapByKey("add_passphrase_bottom_sheet_widget_restore_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,16 @@
|
||||
import 'package:hash_wallet/src/screens/restore/restore_options_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/restore/restore_options_page.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class RestoreOptionsPageRobot {
|
||||
RestoreOptionsPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class RestoreOptionsPageRobot extends BaseRobot {
|
||||
RestoreOptionsPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isRestoreOptionsPage() async {
|
||||
await commonTestCases.isSpecificPage<RestoreOptionsPage>();
|
||||
await commonTestCases.takeScreenshots('restore_options');
|
||||
}
|
||||
|
||||
void hasRestoreOptionsButton() {
|
||||
commonTestCases.hasValueKey('restore_options_from_seeds_or_keys_button_key');
|
||||
commonTestCases.hasValueKey('restore_options_from_backup_button_key');
|
||||
commonTestCases.hasValueKey('restore_options_from_hardware_wallet_button_key');
|
||||
commonTestCases.hasValueKey('restore_options_from_qr_button_key');
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<RestoreOptionsPage>();
|
||||
}
|
||||
|
||||
Future<void> navigateToRestoreFromSeedsOrKeysPage() async {
|
||||
await commonTestCases.tapItemByKey('restore_options_from_seeds_or_keys_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> navigateToRestoreFromBackupPage() async {
|
||||
await commonTestCases.tapItemByKey('restore_options_from_backup_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> navigateToRestoreFromHardwareWalletPage() async {
|
||||
await commonTestCases.tapItemByKey('restore_options_from_hardware_wallet_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> backAndVerify() async {
|
||||
await commonTestCases.goBack();
|
||||
await isRestoreOptionsPage();
|
||||
await tapByKey("restore_options_from_seeds_or_keys_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/settings/security_backup_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class SecurityAndBackupPageRobot {
|
||||
SecurityAndBackupPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
final CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isSecurityAndBackupPage() async {
|
||||
await commonTestCases.isSpecificPage<SecurityBackupPage>();
|
||||
await commonTestCases.takeScreenshots('security_backup_page');
|
||||
}
|
||||
|
||||
void hasTitle() {
|
||||
commonTestCases.hasText(S.current.security_and_backup);
|
||||
}
|
||||
|
||||
Future<void> navigateToShowKeysPage() async {
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 1000));
|
||||
|
||||
await commonTestCases.tapItemByKey('security_backup_page_show_keys_button_key');
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 1000));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import "package:hash_wallet/src/screens/settings/security_backup_page.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class SecurityBackupPageRobot extends BaseRobot {
|
||||
SecurityBackupPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<SecurityBackupPage>();
|
||||
}
|
||||
|
||||
Future<void> openChangePin() async {
|
||||
await tapByKey("security_backup_page_change_pin_button_key");
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/seed/seed_verification/seed_verification_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/generated/i18n.dart";
|
||||
import "package:hash_wallet/src/screens/seed/seed_verification/seed_verification_page.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class SeedVerificationPageRobot {
|
||||
SeedVerificationPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class SeedVerificationPageRobot extends BaseRobot {
|
||||
SeedVerificationPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
final CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isSeedVerificationPage() async {
|
||||
await commonTestCases.isSpecificPage<SeedVerificationPage>();
|
||||
await commonTestCases.takeScreenshots('seed_verification_page');
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<SeedVerificationPage>();
|
||||
}
|
||||
|
||||
void hasTitle() {
|
||||
commonTestCases.hasText(S.current.verify_seed);
|
||||
hasText(S.current.verify_seed);
|
||||
}
|
||||
|
||||
Future<void> verifyWalletSeeds() async {
|
||||
@@ -28,18 +25,23 @@ class SeedVerificationPageRobot {
|
||||
while (!walletSeedViewModel.isVerificationComplete &&
|
||||
walletSeedViewModel.verificationWordCount != 0) {
|
||||
final currentCorrectWord = walletSeedViewModel.currentCorrectWord;
|
||||
final currentStep = walletSeedViewModel.currentStepIndex;
|
||||
|
||||
commonTestCases.hasTextAtLestOnce(currentCorrectWord);
|
||||
hasTextAtLeastOnce(currentCorrectWord);
|
||||
|
||||
await commonTestCases.tapItemByKey(
|
||||
'seed_verification_option_${currentCorrectWord}_button_key',
|
||||
await tapByKey("seed_verification_option_${currentCorrectWord}_button_key");
|
||||
|
||||
final hasMovedOn = await pumpUntil(
|
||||
() =>
|
||||
walletSeedViewModel.isVerificationComplete ||
|
||||
walletSeedViewModel.currentStepIndex != currentStep,
|
||||
);
|
||||
|
||||
await commonTestCases.defaultSleepTime(seconds: 1);
|
||||
if (!hasMovedOn) {
|
||||
throw TestFailure("Seed verification never moved past step $currentStep");
|
||||
}
|
||||
}
|
||||
|
||||
await commonTestCases.tapItemByKey('wallet_seed_page_open_wallet_button_key');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("wallet_seed_page_open_wallet_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,716 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:hash_wallet/core/execution_state.dart';
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/send/send_page.dart';
|
||||
import 'package:hash_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:hash_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:hash_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/material.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';
|
||||
import 'package:hash_wallet/src/widgets/standard_slide_button_widget.dart';
|
||||
import 'package:hash_wallet/src/screens/dashboard/dashboard_page.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>();
|
||||
await commonTestCases.takeScreenshots('send_page');
|
||||
}
|
||||
|
||||
Future<void> waitForSendPage() async {
|
||||
tester.printToConsole('Waiting for SendPage to be available');
|
||||
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final maxDuration = Duration(seconds: 30);
|
||||
|
||||
while (stopwatch.elapsed < maxDuration) {
|
||||
await tester.pump(Duration(milliseconds: 500));
|
||||
|
||||
// Check if we're still on auth page
|
||||
if (authPageRobot.onAuthPage()) {
|
||||
tester.printToConsole('Still on auth page, waiting...');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if SendPage is available
|
||||
final sendPageFinder = find.byType(SendPage);
|
||||
if (sendPageFinder.tryEvaluate()) {
|
||||
tester.printToConsole('SendPage found!');
|
||||
return;
|
||||
}
|
||||
|
||||
tester.printToConsole('SendPage not found yet, waiting...');
|
||||
}
|
||||
|
||||
throw Exception('SendPage not found after ${maxDuration.inSeconds} seconds');
|
||||
}
|
||||
|
||||
Future<void> checkIfSendPageIsVisible() async {
|
||||
tester.printToConsole('Confirming SendPage is visible');
|
||||
|
||||
if (authPageRobot.onAuthPage()) {
|
||||
tester.printToConsole('Auth page is currently visible');
|
||||
await _handleAuthPage();
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
await waitForSendPage();
|
||||
}
|
||||
|
||||
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.feesViewModel.hasFees) {
|
||||
commonTestCases.hasValueKey('send_page_select_fee_priority_button_key');
|
||||
}
|
||||
|
||||
if (sendViewModel.hasCoinControl) {
|
||||
commonTestCases.hasValueKey('send_page_unspent_coin_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);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
// Check if picker dialog is present
|
||||
if (!commonTestCases.isKeyPresent(currencyPickerDialogKey)) {
|
||||
tester.printToConsole('Currency picker dialog not found, may already be selected');
|
||||
return;
|
||||
}
|
||||
|
||||
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.enterText(receiveCurrency.title, 'search_bar_widget_key');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
await commonTestCases.tapItemByKey('picker_items_index_${receiveCurrency.fullName}_button_key');
|
||||
}
|
||||
|
||||
Future<void> enterReceiveAddress(String receiveAddress) async {
|
||||
await commonTestCases.enterText(receiveAddress, 'send_page_address_textfield_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> enterSendAmount(String amount, {bool isFiat = false}) async {
|
||||
await commonTestCases.enterText(
|
||||
amount,
|
||||
isFiat ? 'send_page_fiat_amount_textfield_key' : 'send_page_amount_textfield_key',
|
||||
);
|
||||
}
|
||||
|
||||
String _getTextFromField(ValueKey<String> key) {
|
||||
final field = find.byKey(key);
|
||||
if (!field.tryEvaluate()) return '';
|
||||
final baseTextFormField = tester.widget<BaseTextFormField>(field);
|
||||
return baseTextFormField.controller?.text ?? '';
|
||||
}
|
||||
|
||||
/// Validates wallet balance for $1 send by checking against the converted crypto amount
|
||||
/// Returns true if wallet has sufficient balance, false otherwise
|
||||
Future<bool> validateWalletBalanceForOneDollarSend() async {
|
||||
SendPage sendPage = tester.widget(find.byType(SendPage));
|
||||
final sendViewModel = sendPage.sendViewModel;
|
||||
|
||||
final balance = await sendViewModel.sendingBalance;
|
||||
|
||||
await setupOneDollarSend();
|
||||
|
||||
// Get the crypto amount that was set for the $1 send
|
||||
String cryptoAmount = _getTextFromField(ValueKey('send_page_amount_textfield_key'));
|
||||
if (cryptoAmount.isEmpty || cryptoAmount == '0' || cryptoAmount == '0.0') {
|
||||
cryptoAmount = '0.001'; // fallback amount
|
||||
}
|
||||
|
||||
final amount = double.tryParse(cryptoAmount) ?? 0.0;
|
||||
|
||||
tester.printToConsole(
|
||||
'Wallet balance: $balance, sending amount for \$${CommonTestConstants.sendTestFiatAmount}: $cryptoAmount',
|
||||
);
|
||||
|
||||
if (balance.isEmpty || double.tryParse(balance) == null) {
|
||||
tester.printToConsole('Invalid wallet balance: $balance');
|
||||
return false;
|
||||
}
|
||||
|
||||
final balanceValue = double.parse(balance);
|
||||
if (balanceValue < amount) {
|
||||
tester.printToConsole(
|
||||
'Insufficient balance for \$${CommonTestConstants.sendTestFiatAmount} send: $balanceValue < $amount',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
tester.printToConsole(
|
||||
'Wallet has sufficient balance for \$${CommonTestConstants.sendTestFiatAmount} send',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> selectTransactionPriority({TransactionPriority? priority}) async {
|
||||
SendPage sendPage = tester.widget(find.byType(SendPage));
|
||||
final sendViewModel = sendPage.sendViewModel;
|
||||
|
||||
if (!sendViewModel.feesViewModel.hasFees || priority == null) return;
|
||||
|
||||
final transactionPriorityPickerKey = 'send_page_select_fee_priority_button_key';
|
||||
await commonTestCases.tapItemByKey(transactionPriorityPickerKey);
|
||||
|
||||
if (priority == sendViewModel.feesViewModel.transactionPriority) {
|
||||
await commonTestCases
|
||||
.tapItemByKey('picker_items_index_${priority.title}_selected_item_button_key');
|
||||
return;
|
||||
}
|
||||
|
||||
await commonTestCases.dragUntilVisible(
|
||||
'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 checkIfSendPageIsVisible();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
final sendPage = tester.widget<SendPage>(find.byType(SendPage));
|
||||
|
||||
while (true) {
|
||||
bool isReadyForSend = sendPage.sendViewModel.isReadyForSend;
|
||||
await tester.pump();
|
||||
if (isReadyForSend) {
|
||||
tester.printToConsole('Is ready for send');
|
||||
break;
|
||||
} else {
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tester.pumpAndSettle();
|
||||
tester.printToConsole('not yet ready for send');
|
||||
}
|
||||
}
|
||||
await commonTestCases.tapItemByKey(
|
||||
'send_page_send_button_key',
|
||||
shouldPumpAndSettle: false,
|
||||
);
|
||||
|
||||
await _waitForSendTransactionCompletion();
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> _waitForSendTransactionCompletion() async {
|
||||
await tester.pump();
|
||||
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 _handleAuth');
|
||||
|
||||
await _handleAuthPage();
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
tester.printToConsole('After _handleAuth');
|
||||
|
||||
await tester.pump();
|
||||
|
||||
if (authPageRobot.onAuthPage()) {
|
||||
tester.printToConsole('Still on auth page, continuing to wait');
|
||||
continue;
|
||||
}
|
||||
|
||||
final sendPageFinder = find.byType(SendPage);
|
||||
if (!sendPageFinder.tryEvaluate()) {
|
||||
tester.printToConsole('SendPage not found yet, continuing to wait');
|
||||
continue;
|
||||
}
|
||||
|
||||
final sendPage = tester.widget<SendPage>(sendPageFinder);
|
||||
final state = sendPage.sendViewModel.state;
|
||||
|
||||
await tester.pump();
|
||||
|
||||
bool isDone = state is ExecutedSuccessfullyState || 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 confirming sending operation');
|
||||
}
|
||||
|
||||
Future<void> _handleAuthPage() async {
|
||||
tester.printToConsole('Inside _handleAuth');
|
||||
|
||||
final onAuthPageDesktop = authPageRobot.onAuthPageDesktop();
|
||||
if (onAuthPageDesktop) {
|
||||
await authPageRobot.enterPassword(CommonTestConstants.pin.join(""));
|
||||
await commonTestCases.defaultSleepTime();
|
||||
return;
|
||||
}
|
||||
|
||||
await tester.pump();
|
||||
tester.printToConsole('starting auth checks');
|
||||
|
||||
final authPage = authPageRobot.onAuthPage();
|
||||
|
||||
tester.printToConsole('hasAuth:$authPage');
|
||||
|
||||
if (authPage) {
|
||||
await tester.pump();
|
||||
tester.printToConsole('Starting inner _handleAuth loop checks');
|
||||
|
||||
try {
|
||||
await authPageRobot.enterPinCode(CommonTestConstants.pin, pumpDuration: 500);
|
||||
tester.printToConsole('Auth done');
|
||||
|
||||
await tester.pump(Duration(seconds: 3));
|
||||
|
||||
tester.printToConsole('Auth pump done');
|
||||
|
||||
await commonTestCases.defaultSleepTime();
|
||||
} catch (e) {
|
||||
tester.printToConsole('Auth failed, retrying: $e');
|
||||
await tester.pump();
|
||||
|
||||
await commonTestCases.defaultSleepTime(seconds: 1);
|
||||
await _handleAuthPage();
|
||||
}
|
||||
} else {
|
||||
tester.printToConsole('No auth page detected, proceeding');
|
||||
}
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
//* ------ 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> onSendSliderOnConfirmSendingBottomSheetDragged() async {
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tester.pump();
|
||||
|
||||
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
||||
tester.printToConsole('Found confirm sending bottom sheet, starting slider drag');
|
||||
|
||||
final accessibleButton = find.byType(PrimaryButton);
|
||||
if (accessibleButton.tryEvaluate()) {
|
||||
tester.printToConsole('Found accessible navigation button, tapping it');
|
||||
await tester.tap(accessibleButton);
|
||||
await tester.pumpAndSettle();
|
||||
} else {
|
||||
await _performSendSliderDrag();
|
||||
}
|
||||
|
||||
tester.printToConsole('Slider/button action completed, waiting for transaction completion');
|
||||
|
||||
// Check if bottom sheet is dismissed
|
||||
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
||||
tester.printToConsole('Bottom sheet still present, waiting a bit more for dismissal');
|
||||
await commonTestCases.defaultSleepTime(seconds: 3);
|
||||
|
||||
// If still present, try one more manual dismissal attempt
|
||||
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
||||
tester.printToConsole('Bottom sheet still present, attempting final manual dismissal');
|
||||
await tester.tapAt(Offset(200, 200)); // Tap outside
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for transaction completion
|
||||
await _waitForCommitTransactionCompletion();
|
||||
await commonTestCases.defaultSleepTime(seconds: 2);
|
||||
} else {
|
||||
tester.printToConsole('Confirm sending bottom sheet not found, waiting and retrying');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tester.pump();
|
||||
await onSendSliderOnConfirmSendingBottomSheetDragged();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _performSendSliderDrag() async {
|
||||
final sliderFinder = find.byKey(const ValueKey('standard_slide_button_widget_slider_key'));
|
||||
expect(sliderFinder, findsOneWidget, reason: 'Slider should be found');
|
||||
|
||||
// Find the StandardSlideButton widget to get the main container
|
||||
final slideButtonFinder = find.byType(StandardSlideButton);
|
||||
expect(slideButtonFinder, findsOneWidget, reason: 'StandardSlideButton should be found');
|
||||
|
||||
// Get the main container bounds (the outer container, not the slider container)
|
||||
final mainContainerRect = tester.getRect(slideButtonFinder);
|
||||
final containerWidth = mainContainerRect.width;
|
||||
|
||||
final sideMargin = 4.0;
|
||||
final sliderWidth = 42.0;
|
||||
final effectiveMaxWidth = containerWidth - 2 * sideMargin;
|
||||
final threshold = effectiveMaxWidth - sliderWidth - 10;
|
||||
|
||||
// Add a small buffer to ensure we exceed the threshold
|
||||
final dragDistance = threshold + 20;
|
||||
|
||||
tester.printToConsole(
|
||||
'Main container width: $containerWidth, Threshold: $threshold, Drag distance: $dragDistance',
|
||||
);
|
||||
|
||||
// Start the drag
|
||||
await tester.drag(sliderFinder, Offset(dragDistance, 0));
|
||||
|
||||
// Wait for the drag to complete and trigger onHorizontalDragEnd
|
||||
await tester.pump(Duration(milliseconds: 100));
|
||||
|
||||
// Release the drag (this should trigger onHorizontalDragEnd)
|
||||
await tester.pump(Duration(seconds: 2));
|
||||
|
||||
tester.printToConsole('Drag completed, waiting for callback');
|
||||
|
||||
// Wait for the slide completion callback to trigger and bottom sheet to dismiss
|
||||
await _waitForBottomSheetDismissal();
|
||||
}
|
||||
|
||||
Future<void> _waitForBottomSheetDismissal() async {
|
||||
tester.printToConsole('Waiting for bottom sheet dismissal');
|
||||
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final maxDuration = Duration(seconds: 10);
|
||||
|
||||
while (stopwatch.elapsed < maxDuration) {
|
||||
await tester.pump(Duration(milliseconds: 500));
|
||||
|
||||
// Check if the confirm bottom sheet is still present
|
||||
if (!commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
||||
tester.printToConsole('Bottom sheet dismissed successfully!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if transaction has started (this indicates slider was successful)
|
||||
if (_isTransactionStarted()) {
|
||||
tester.printToConsole(
|
||||
'Transaction started, slider was successful even if bottom sheet is still visible');
|
||||
return;
|
||||
}
|
||||
|
||||
tester.printToConsole('Bottom sheet still present, waiting');
|
||||
}
|
||||
|
||||
tester.printToConsole('Bottom sheet dismissal timeout reached, trying manual dismissal');
|
||||
|
||||
// If the bottom sheet is still present after timeout, try to manually dismiss it
|
||||
// This might happen if the slider drag didn't trigger the onSlideComplete properly
|
||||
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
||||
tester.printToConsole('Attempting manual bottom sheet dismissal');
|
||||
|
||||
// Try to find and tap a close button or back button
|
||||
final closeButton = find.byIcon(Icons.close);
|
||||
if (closeButton.tryEvaluate()) {
|
||||
await tester.tap(closeButton);
|
||||
await tester.pumpAndSettle();
|
||||
tester.printToConsole('Manual close button tapped');
|
||||
} else {
|
||||
// Try to pop the bottom sheet by tapping outside or using back gesture
|
||||
await tester.tapAt(Offset(100, 100)); // Tap outside the bottom sheet
|
||||
await tester.pumpAndSettle();
|
||||
tester.printToConsole('Tapped outside bottom sheet');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _isTransactionStarted() {
|
||||
try {
|
||||
// Check if SendPage is available and transaction state has changed
|
||||
final sendPageFinder = find.byType(SendPage);
|
||||
if (!sendPageFinder.tryEvaluate()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final sendPage = tester.widget<SendPage>(sendPageFinder);
|
||||
final state = sendPage.sendViewModel.state;
|
||||
|
||||
// Check if we're in a transaction-related state
|
||||
return state is TransactionCommitting ||
|
||||
state is IsExecutingState ||
|
||||
state is TransactionCommitted ||
|
||||
state is ExecutedSuccessfullyState;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _waitForCommitTransactionCompletion() async {
|
||||
tester.printToConsole('Starting to wait for transaction completion');
|
||||
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final maxDuration = Duration(seconds: 60);
|
||||
|
||||
while (stopwatch.elapsed < maxDuration) {
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
|
||||
// Check if the confirm bottom sheet is gone (transaction started)
|
||||
if (!commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
||||
tester.printToConsole('Confirm bottom sheet disappeared, transaction may be processing');
|
||||
}
|
||||
|
||||
// Check if we've navigated back to the dashboard (for cases of successful transaction)
|
||||
final dashboardFinder = find.byType(DashboardPage);
|
||||
if (dashboardFinder.tryEvaluate()) {
|
||||
tester.printToConsole('Dashboard detected, transaction completed successfully!');
|
||||
await tester.pump();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if SendPage is still available (transaction still processing)
|
||||
final sendPageFinder = find.byType(SendPage);
|
||||
if (sendPageFinder.tryEvaluate()) {
|
||||
final sendPage = tester.widget<SendPage>(sendPageFinder);
|
||||
final state = sendPage.sendViewModel.state;
|
||||
|
||||
bool isDone = state is ExecutedSuccessfullyState || state is TransactionCommitted;
|
||||
bool isFailed = state is FailureState;
|
||||
|
||||
tester.printToConsole('Transaction state: $state');
|
||||
tester.printToConsole('isDone: $isDone');
|
||||
tester.printToConsole('isFailed: $isFailed');
|
||||
|
||||
if (isDone) {
|
||||
tester.printToConsole('Transaction committed successfully!');
|
||||
await tester.pump();
|
||||
return;
|
||||
} else if (isFailed) {
|
||||
tester.printToConsole('Transaction failed: $state');
|
||||
await tester.pump();
|
||||
return;
|
||||
} else {
|
||||
tester.printToConsole('Transaction still processing');
|
||||
await tester.pump();
|
||||
}
|
||||
} else {
|
||||
// SendPage not found, check if we're on dashboard or still processing
|
||||
if (dashboardFinder.tryEvaluate()) {
|
||||
tester.printToConsole('Dashboard detected - transaction completed successfully!');
|
||||
await tester.pump();
|
||||
return;
|
||||
} else {
|
||||
tester.printToConsole(
|
||||
'SendPage not found, but dashboard not yet visible - continuing to wait...');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stopwatch.elapsed >= maxDuration) {
|
||||
tester.printToConsole('Transaction completion timeout reached');
|
||||
}
|
||||
|
||||
tester.printToConsole('Done waiting for transaction completion');
|
||||
}
|
||||
|
||||
//* ---- Handle Transaction Success Flow -----
|
||||
Future<void> handleTransactionSuccessFlow() async {
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
// Wait for any success dialogs to appear
|
||||
await tester.pump(Duration(seconds: 2));
|
||||
|
||||
// Check for contact addition dialog first (if new contact address exists)
|
||||
final contactDialog = find.byKey(ValueKey('send_page_add_contact_bottom_sheet_yes_button_key'));
|
||||
if (contactDialog.tryEvaluate()) {
|
||||
tester.printToConsole('Found contact addition dialog, selecting Yes');
|
||||
|
||||
// Check if the button is actually visible and tappable
|
||||
final buttonRect = tester.getRect(contactDialog);
|
||||
final screenSize = tester.view.physicalSize / tester.view.devicePixelRatio;
|
||||
|
||||
if (buttonRect.bottom <= screenSize.height && buttonRect.top >= 0) {
|
||||
await commonTestCases.tapItemByKey('send_page_add_contact_bottom_sheet_yes_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
} else {
|
||||
tester.printToConsole('Contact dialog button is off-screen, skipping');
|
||||
}
|
||||
}
|
||||
|
||||
// Check for the main success dialog
|
||||
final successDialog = find.byKey(ValueKey('send_page_sent_dialog_ok_button_key'));
|
||||
if (successDialog.tryEvaluate()) {
|
||||
tester.printToConsole('Found transaction success dialog, closing it');
|
||||
await commonTestCases.tapItemByKey('send_page_sent_dialog_ok_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
}
|
||||
|
||||
//* ---- Fiat/Crypto Amount Validation -----
|
||||
Future<void> testFiatAmountEntry() async {
|
||||
tester.printToConsole('Testing fiat amount entry...');
|
||||
|
||||
await enterSendAmount('');
|
||||
await enterSendAmount('', isFiat: true);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
await enterSendAmount(CommonTestConstants.sendTestFiatAmount, isFiat: true);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
// Wait for conversion to complete
|
||||
await tester.pump(Duration(seconds: 3));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Next we get the crypto amount value and validate it's not 0
|
||||
final cryptoAmount = _getTextFromField(ValueKey('send_page_amount_textfield_key'));
|
||||
|
||||
tester.printToConsole(
|
||||
'Crypto amount after entering \$${CommonTestConstants.sendTestFiatAmount}: $cryptoAmount',
|
||||
);
|
||||
|
||||
if (cryptoAmount.isNotEmpty && cryptoAmount != '0' && cryptoAmount != '0.0') {
|
||||
tester.printToConsole('Fiat to crypto conversion working - crypto amount: $cryptoAmount');
|
||||
} else {
|
||||
tester.printToConsole(
|
||||
'Fiat to crypto conversion may not be working - crypto amount: $cryptoAmount',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCryptoAmountEntry() async {
|
||||
tester.printToConsole('Testing crypto amount entry...');
|
||||
|
||||
await enterSendAmount('');
|
||||
await enterSendAmount('', isFiat: true);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
String cryptoAmount = '0.001';
|
||||
|
||||
await enterSendAmount(cryptoAmount);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
// Wait for conversion to complete
|
||||
await tester.pump(Duration(seconds: 3));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Get the fiat amount value and validate it's not 0
|
||||
final fiatAmount = _getTextFromField(ValueKey('send_page_fiat_amount_textfield_key'));
|
||||
|
||||
tester.printToConsole('Fiat amount after entering $cryptoAmount: $fiatAmount');
|
||||
|
||||
if (fiatAmount.isNotEmpty && fiatAmount != '0' && fiatAmount != '0.0') {
|
||||
tester.printToConsole('Crypto to fiat conversion working, fiat amount: $fiatAmount');
|
||||
} else {
|
||||
tester.printToConsole(
|
||||
'Crypto to fiat conversion may not be working - fiat amount: $fiatAmount');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setupOneDollarSend() async {
|
||||
// Clear existing amounts
|
||||
await enterSendAmount('');
|
||||
await enterSendAmount('', isFiat: true);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
await enterSendAmount(CommonTestConstants.sendTestFiatAmount, isFiat: true);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
// Wait for conversion to complete
|
||||
await tester.pump(Duration(seconds: 3));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Get the converted crypto amount
|
||||
String cryptoAmount = _getTextFromField(ValueKey('send_page_amount_textfield_key'));
|
||||
if (cryptoAmount.isEmpty || cryptoAmount == '0' || cryptoAmount == '0.0') {
|
||||
cryptoAmount = '0.001'; // fallback amount
|
||||
}
|
||||
|
||||
tester.printToConsole(
|
||||
'Sending $cryptoAmount (equivalent to \$${CommonTestConstants.sendTestFiatAmount}) to test wallet',
|
||||
);
|
||||
|
||||
// Update the amount field with the converted value
|
||||
await enterSendAmount(cryptoAmount);
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,16 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/setup_pin_code/setup_pin_code.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import 'pin_code_widget_robot.dart';
|
||||
import "pin_code_widget_robot.dart";
|
||||
|
||||
class SetupPinCodeRobot extends PinCodeWidgetRobot {
|
||||
SetupPinCodeRobot(this.tester)
|
||||
: commonTestCases = CommonTestCases(tester),
|
||||
super(tester);
|
||||
SetupPinCodeRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isSetupPinCodePage() async {
|
||||
await commonTestCases.isSpecificPage<SetupPinCodePage>();
|
||||
await commonTestCases.takeScreenshots('setup_pin_code_page');
|
||||
}
|
||||
|
||||
void hasTitle() {
|
||||
commonTestCases.hasText(S.current.setup_pin);
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<SetupPinCodePage>();
|
||||
}
|
||||
|
||||
Future<void> tapSuccessButton() async {
|
||||
await commonTestCases.tapItemByKey('setup_pin_code_success_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("setup_pin_code_success_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import "package:hash_wallet/new-ui/pages/seed/show_keys_disclaimer_page.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class ShowKeysDisclaimerPageRobot extends BaseRobot {
|
||||
ShowKeysDisclaimerPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<ShowKeysDisclaimerPage>();
|
||||
}
|
||||
|
||||
Future<void> tapShowKeys() async {
|
||||
await tapByKey("show_keys_disclaimer_page_button_key");
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import 'package:hash_wallet/src/screens/send/transaction_success_info_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class TransactionSuccessInfoRobot {
|
||||
TransactionSuccessInfoRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isTransactionSuccessInfoPage() async {
|
||||
await commonTestCases.isSpecificPage<TransactionSuccessPage>();
|
||||
await commonTestCases.takeScreenshots('transaction_success_info_page');
|
||||
}
|
||||
|
||||
Future<void> onConfirmButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('transaction_success_info_page_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
}
|
||||
@@ -1,439 +0,0 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/dashboard/pages/transactions_page.dart';
|
||||
import 'package:hash_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
import 'package:hash_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:hash_wallet/view_model/dashboard/date_section_item.dart';
|
||||
import 'package:hash_wallet/view_model/dashboard/order_list_item.dart';
|
||||
import 'package:hash_wallet/view_model/dashboard/trade_list_item.dart';
|
||||
import 'package:hash_wallet/view_model/dashboard/transaction_list_item.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/sync_status.dart';
|
||||
import 'package:cw_core/transaction_direction.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
|
||||
class TransactionsPageRobot {
|
||||
TransactionsPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isTransactionsPage() async {
|
||||
await commonTestCases.isSpecificPage<TransactionsPage>();
|
||||
await commonTestCases.takeScreenshots('transactions_page');
|
||||
}
|
||||
|
||||
Future<void> confirmTransactionsPageConstantsDisplayProperly() async {
|
||||
await commonTestCases.defaultSleepTime();
|
||||
|
||||
final transactionsPage = tester.widget<TransactionsPage>(find.byType(TransactionsPage));
|
||||
final dashboardViewModel = transactionsPage.dashboardViewModel;
|
||||
if (dashboardViewModel.status is SyncingSyncStatus) {
|
||||
commonTestCases.hasValueKey('transactions_page_syncing_alert_card_key');
|
||||
commonTestCases.hasText(S.current.syncing_wallet_alert_title);
|
||||
commonTestCases.hasText(S.current.syncing_wallet_alert_content);
|
||||
}
|
||||
|
||||
commonTestCases.hasValueKey('transactions_page_header_row_key');
|
||||
commonTestCases.hasText(S.current.transactions);
|
||||
commonTestCases.hasValueKey('transactions_page_header_row_transaction_filter_button_key');
|
||||
}
|
||||
|
||||
Future<void> confirmTransactionHistoryListDisplaysCorrectly(bool hasTxHistoryWhileSyncing) async {
|
||||
try {
|
||||
final transactionsPage = tester.widget<TransactionsPage>(find.byType(TransactionsPage));
|
||||
final dashboardViewModel = transactionsPage.dashboardViewModel;
|
||||
|
||||
await _waitForListToBeReady();
|
||||
|
||||
if (!hasTxHistoryWhileSyncing) {
|
||||
await _waitForSyncToComplete(dashboardViewModel);
|
||||
} else {
|
||||
await tester.pump(Duration(seconds: 2));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
await _performComprehensiveItemCheck(dashboardViewModel);
|
||||
} catch (e) {
|
||||
tester.printToConsole('Error in transaction history check: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _waitForSyncToComplete(DashboardViewModel dashboardViewModel) async {
|
||||
const maxWaitTime = Duration(minutes: 2);
|
||||
final endTime = DateTime.now().add(maxWaitTime);
|
||||
|
||||
tester.printToConsole('Waiting for wallet to sync...');
|
||||
|
||||
while (DateTime.now().isBefore(endTime)) {
|
||||
await tester.pump(Duration(seconds: 2));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
if (dashboardViewModel.status is SyncedSyncStatus) {
|
||||
tester.printToConsole('Wallet synced successfully');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have now have transaction items available
|
||||
if (dashboardViewModel.items.isNotEmpty) {
|
||||
tester.printToConsole('Items available while syncing, proceeding with test');
|
||||
return;
|
||||
}
|
||||
|
||||
tester.printToConsole('Sync status: ${dashboardViewModel.status.runtimeType}');
|
||||
}
|
||||
|
||||
tester.printToConsole('Warning: Wallet did not sync within expected time, proceeding anyway');
|
||||
}
|
||||
|
||||
Future<void> _performComprehensiveItemCheck(DashboardViewModel dashboardViewModel) async {
|
||||
try {
|
||||
await _waitForItemsToLoad(dashboardViewModel);
|
||||
|
||||
final itemsToProcess = dashboardViewModel.items.where((item) {
|
||||
if (item is DateSectionItem) return false;
|
||||
if (item is TransactionListItem) {
|
||||
return !(item.hasTokens && item.assetOfTransaction == null);
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
|
||||
if (itemsToProcess.isEmpty) {
|
||||
tester.printToConsole('No transaction items to process - checking for placeholder');
|
||||
_verifyPlaceholder();
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a temporary limit to prevent the test from taking too long
|
||||
final maxItemsToProcess = 100;
|
||||
final itemsToCheck = itemsToProcess.take(maxItemsToProcess).toList();
|
||||
|
||||
tester.printToConsole(
|
||||
'Processing ${itemsToCheck.length} items out of ${itemsToProcess.length} total items',
|
||||
);
|
||||
|
||||
await _processVisibleItems(itemsToCheck, dashboardViewModel);
|
||||
|
||||
// Try to scroll and process more items if needed
|
||||
await _processItemsWithScrolling(itemsToCheck, dashboardViewModel);
|
||||
} catch (e) {
|
||||
tester.printToConsole('Error in comprehensive item check: $e');
|
||||
try {
|
||||
_verifyPlaceholder();
|
||||
} catch (placeholderError) {
|
||||
tester.printToConsole('Could not verify placeholder either: $placeholderError');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _waitForItemsToLoad(DashboardViewModel dashboardViewModel) async {
|
||||
const maxWaitTime = Duration(seconds: 30);
|
||||
final endTime = DateTime.now().add(maxWaitTime);
|
||||
|
||||
while (DateTime.now().isBefore(endTime)) {
|
||||
if (dashboardViewModel.items.isNotEmpty) {
|
||||
tester.printToConsole('Items loaded: ${dashboardViewModel.items.length}');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if placeholder is shown
|
||||
if (tester.any(find.byKey(ValueKey('transactions_page_placeholder_transactions_text_key')))) {
|
||||
tester.printToConsole('Placeholder is shown - no items to load');
|
||||
return;
|
||||
}
|
||||
|
||||
await tester.pump(Duration(seconds: 1));
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
tester.printToConsole('Warning: No items loaded and no placeholder shown within expected time');
|
||||
}
|
||||
|
||||
Future<void> _processVisibleItems(
|
||||
List<dynamic> items,
|
||||
DashboardViewModel dashboardViewModel,
|
||||
) async {
|
||||
try {
|
||||
int processedCount = 0;
|
||||
|
||||
for (var item in items) {
|
||||
try {
|
||||
final keyId = (item.key as ValueKey<String>).value;
|
||||
|
||||
// Check if item is already visible
|
||||
if (tester.any(find.byKey(ValueKey(keyId)))) {
|
||||
tester.printToConsole('Processing visible item: $keyId');
|
||||
await _verifyItemDisplay(item, dashboardViewModel);
|
||||
processedCount++;
|
||||
}
|
||||
} catch (itemError) {
|
||||
tester.printToConsole('Error processing item: $itemError');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
tester.printToConsole('Processed $processedCount visible items\n');
|
||||
} catch (e) {
|
||||
tester.printToConsole('Error in visible items processing: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _processItemsWithScrolling(
|
||||
List<dynamic> items,
|
||||
DashboardViewModel dashboardViewModel,
|
||||
) async {
|
||||
try {
|
||||
final scrollableFinder = find.descendant(
|
||||
of: find.byKey(ValueKey('transactions_page_list_view_builder_key')),
|
||||
matching: find.byType(Scrollable),
|
||||
);
|
||||
|
||||
if (!tester.any(scrollableFinder)) {
|
||||
tester.printToConsole('No scrollable found, skipping scroll processing');
|
||||
return;
|
||||
}
|
||||
|
||||
int processedCount = 0;
|
||||
const maxScrollAttempts = 10;
|
||||
|
||||
for (var item in items) {
|
||||
try {
|
||||
final keyId = (item.key as ValueKey<String>).value;
|
||||
|
||||
// Skip if already processed
|
||||
if (tester.any(find.byKey(ValueKey(keyId)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to scroll to the item
|
||||
bool found = false;
|
||||
for (int attempt = 0; attempt < maxScrollAttempts; attempt++) {
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 200));
|
||||
|
||||
if (tester.any(find.byKey(ValueKey(keyId)))) {
|
||||
tester.printToConsole('Found item after scrolling: $keyId');
|
||||
await _verifyItemDisplay(item, dashboardViewModel);
|
||||
processedCount++;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Perform scroll
|
||||
try {
|
||||
await tester.drag(scrollableFinder, Offset(0, -100));
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 300));
|
||||
} catch (scrollError) {
|
||||
tester.printToConsole('Scroll failed: $scrollError');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
tester.printToConsole('Could not find item after scrolling: $keyId');
|
||||
}
|
||||
} catch (itemError) {
|
||||
tester.printToConsole('Error processing item with scrolling: $itemError');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
tester.printToConsole('Processed $processedCount items with scrolling');
|
||||
} catch (e) {
|
||||
tester.printToConsole('Error in scroll processing: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _verifyPlaceholder() {
|
||||
commonTestCases.hasValueKey('transactions_page_placeholder_transactions_text_key');
|
||||
commonTestCases.hasText(S.current.placeholder_transactions);
|
||||
}
|
||||
|
||||
Future<void> _waitForListToBeReady() async {
|
||||
const maxWaitAttempts = 10;
|
||||
int attempts = 0;
|
||||
|
||||
while (attempts < maxWaitAttempts) {
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 500));
|
||||
|
||||
// Check if the list view is present
|
||||
final listViewFinder = find.byKey(ValueKey('transactions_page_list_view_builder_key'));
|
||||
if (tester.any(listViewFinder)) {
|
||||
tester.printToConsole('List view is ready');
|
||||
return;
|
||||
}
|
||||
|
||||
attempts++;
|
||||
tester.printToConsole('Waiting for list view to be ready, attempt $attempts\n');
|
||||
}
|
||||
|
||||
tester.printToConsole('List view did not become ready within expected time');
|
||||
}
|
||||
|
||||
Future<void> _verifyItemDisplay(dynamic item, DashboardViewModel dashboardViewModel) async {
|
||||
// Execute the proper check depending on item type.
|
||||
switch (item.runtimeType) {
|
||||
case TransactionListItem:
|
||||
final transactionItem = item as TransactionListItem;
|
||||
tester.printToConsole(transactionItem.formattedTitle);
|
||||
tester.printToConsole(transactionItem.formattedFiatAmount);
|
||||
tester.printToConsole('\n');
|
||||
await _verifyTransactionListItemDisplay(transactionItem, dashboardViewModel);
|
||||
break;
|
||||
|
||||
case AnonpayTransactionListItem:
|
||||
await _verifyAnonpayTransactionListItemDisplay(item as AnonpayTransactionListItem);
|
||||
break;
|
||||
|
||||
case TradeListItem:
|
||||
await _verifyTradeListItemDisplay(item as TradeListItem);
|
||||
break;
|
||||
|
||||
case OrderListItem:
|
||||
await _verifyOrderListItemDisplay(item as OrderListItem);
|
||||
break;
|
||||
|
||||
default:
|
||||
tester.printToConsole('Unhandled item type: ${item.runtimeType}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _verifyTransactionListItemDisplay(
|
||||
TransactionListItem item,
|
||||
DashboardViewModel dashboardViewModel,
|
||||
) async {
|
||||
final keyId =
|
||||
'${dashboardViewModel.type.name}_transaction_history_item_${item.transaction.id}_key';
|
||||
|
||||
if (!tester.any(find.byKey(ValueKey(keyId)))) {
|
||||
tester.printToConsole(
|
||||
'Could not find transaction item with key: $keyId for transaction ${item.transaction.id}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.hasTokens && item.assetOfTransaction == null) return;
|
||||
|
||||
try {
|
||||
//* ==============Confirm it has the right key for this item ========
|
||||
commonTestCases.hasValueKey(keyId);
|
||||
|
||||
//* ======Confirm it displays the properly formatted amount==========
|
||||
commonTestCases.findWidgetViaDescendant(
|
||||
of: find.byKey(ValueKey(keyId)),
|
||||
matching: find.text(item.formattedCryptoAmount),
|
||||
);
|
||||
|
||||
//* ======Confirm it displays the properly formatted date============
|
||||
final formattedDate = DateFormat('HH:mm').format(item.transaction.date);
|
||||
commonTestCases.findWidgetViaDescendant(
|
||||
of: find.byKey(ValueKey(keyId)),
|
||||
matching: find.text(formattedDate),
|
||||
);
|
||||
|
||||
//* ======Confirm it displays the properly formatted fiat amount=====
|
||||
final formattedFiatAmount =
|
||||
dashboardViewModel.balanceViewModel.isFiatDisabled ? '' : item.formattedFiatAmount;
|
||||
if (formattedFiatAmount.isNotEmpty) {
|
||||
commonTestCases.findWidgetViaDescendant(
|
||||
of: find.byKey(ValueKey(keyId)),
|
||||
matching: find.text(formattedFiatAmount),
|
||||
);
|
||||
}
|
||||
|
||||
//* ======Confirm it displays the right image based on the transaction direction=====
|
||||
final imageToUse = item.transaction.direction == TransactionDirection.incoming
|
||||
? 'assets/images/down_arrow.png'
|
||||
: 'assets/images/up_arrow.png';
|
||||
|
||||
find.widgetWithImage(Container, AssetImage(imageToUse));
|
||||
} catch (e) {
|
||||
tester.printToConsole('Error verifying transaction item ${item.transaction.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _verifyAnonpayTransactionListItemDisplay(AnonpayTransactionListItem item) async {
|
||||
final keyId = 'anonpay_invoice_transaction_list_item_${item.transaction.invoiceId}_key';
|
||||
|
||||
//* ==============Confirm it has the right key for this item ========
|
||||
commonTestCases.hasValueKey(keyId);
|
||||
|
||||
//* ==============Confirm it displays the correct provider =========================
|
||||
commonTestCases.hasText(item.transaction.provider);
|
||||
|
||||
//* ===========Confirm it displays the properly formatted amount with currency ========
|
||||
final currency = item.transaction.fiatAmount != null
|
||||
? item.transaction.fiatEquiv ?? ''
|
||||
: CryptoCurrency.fromFullName(item.transaction.coinTo).name.toUpperCase();
|
||||
|
||||
final amount =
|
||||
item.transaction.fiatAmount?.toString() ?? (item.transaction.amountTo?.toString() ?? '');
|
||||
|
||||
final amountCurrencyText = amount + ' ' + currency;
|
||||
|
||||
commonTestCases.hasText(amountCurrencyText);
|
||||
|
||||
//* ======Confirm it displays the properly formatted date=================
|
||||
final formattedDate = DateFormat('HH:mm').format(item.transaction.createdAt);
|
||||
commonTestCases.hasText(formattedDate);
|
||||
|
||||
//* ===============Confirm it displays the right image====================
|
||||
find.widgetWithImage(ClipRRect, AssetImage('assets/images/trocador.png'));
|
||||
}
|
||||
|
||||
Future<void> _verifyTradeListItemDisplay(TradeListItem item) async {
|
||||
final keyId = 'trade_list_item_${item.trade.id}_key';
|
||||
final from = item.trade.from?.toString() ?? '';
|
||||
final to = item.trade.to?.toString() ?? '';
|
||||
|
||||
//* ==============Confirm it has the right key for this item ========
|
||||
commonTestCases.hasValueKey(keyId);
|
||||
|
||||
//* ==============Confirm it displays the correct provider =========================
|
||||
final conversionFlow = '$from → $to';
|
||||
|
||||
commonTestCases.hasText(conversionFlow);
|
||||
|
||||
//* ===========Confirm it displays the properly formatted amount with its crypto tag ========
|
||||
|
||||
final amountCryptoText = item.tradeFormattedAmount + ' ' + from;
|
||||
|
||||
commonTestCases.hasText(amountCryptoText);
|
||||
|
||||
//* ======Confirm it displays the properly formatted date=================
|
||||
final createdAtFormattedDate =
|
||||
item.trade.createdAt != null ? DateFormat('HH:mm').format(item.trade.createdAt!) : null;
|
||||
|
||||
if (createdAtFormattedDate != null) {
|
||||
commonTestCases.hasText(createdAtFormattedDate);
|
||||
}
|
||||
|
||||
//* ===============Confirm it displays the right image====================
|
||||
commonTestCases.hasValueKey(item.trade.provider.image);
|
||||
}
|
||||
|
||||
Future<void> _verifyOrderListItemDisplay(OrderListItem item) async {
|
||||
final keyId = 'order_list_item_${item.order.id}_key';
|
||||
|
||||
//* ==============Confirm it has the right key for this item ========
|
||||
commonTestCases.hasValueKey(keyId);
|
||||
|
||||
//* ==============Confirm it displays the correct provider =========================
|
||||
final orderFlow = '${item.order.from!} → ${item.order.to}';
|
||||
|
||||
commonTestCases.hasText(orderFlow);
|
||||
|
||||
//* ===========Confirm it displays the properly formatted amount with its crypto tag ========
|
||||
|
||||
final amountCryptoText = item.orderFormattedAmount + ' ' + item.order.to!;
|
||||
|
||||
commonTestCases.hasText(amountCryptoText);
|
||||
|
||||
//* ======Confirm it displays the properly formatted date=================
|
||||
final createdAtFormattedDate = DateFormat('HH:mm').format(item.order.createdAt);
|
||||
|
||||
commonTestCases.hasText(createdAtFormattedDate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import "package:hash_wallet/src/screens/wallet/wallet_edit_page.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WalletEditPageRobot extends BaseRobot {
|
||||
WalletEditPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WalletEditPage>();
|
||||
}
|
||||
|
||||
Future<void> enterName(String name) async {
|
||||
final input = find.byKey(const ValueKey("wallet_edit_page_name_input_key"));
|
||||
|
||||
await pumpUntilFound(input);
|
||||
|
||||
final field = find.descendant(of: input, matching: find.byType(EditableText));
|
||||
|
||||
await tester.enterText(field.first, name);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
await tapByKey("wallet_edit_page_save_button_key");
|
||||
}
|
||||
|
||||
Future<void> tapDelete() async {
|
||||
await tapByKey("wallet_edit_page_delete_button_key");
|
||||
}
|
||||
|
||||
Future<void> confirmDelete() async {
|
||||
await tapByKey("wallet_edit_page_confirm_delete_button_key");
|
||||
}
|
||||
|
||||
Future<void> expectNameTakenRefusal() async {
|
||||
await pumpUntilFound(find.byKey(const ValueKey("wallet_edit_page_name_taken_ok_button_key")));
|
||||
|
||||
hasType<WalletEditPage>();
|
||||
}
|
||||
|
||||
Future<void> dismissNameTakenRefusal() async {
|
||||
await tapByKey("wallet_edit_page_name_taken_ok_button_key");
|
||||
|
||||
await settle();
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,25 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/new_wallet/wallet_group_description_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/new_wallet/wallet_group_description_page.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WalletGroupDescriptionPageRobot {
|
||||
WalletGroupDescriptionPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class WalletGroupDescriptionPageRobot extends BaseRobot {
|
||||
WalletGroupDescriptionPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
final CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isWalletGroupDescriptionPage() async {
|
||||
await commonTestCases.isSpecificPage<WalletGroupDescriptionPage>();
|
||||
await commonTestCases.takeScreenshots('wallet_group_description_page');
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WalletGroupDescriptionPage>();
|
||||
}
|
||||
|
||||
void hasTitle() {
|
||||
commonTestCases.hasText(S.current.wallet_group);
|
||||
}
|
||||
|
||||
bool hasNewSingleSeedButton() {
|
||||
return commonTestCases.isKeyPresent('wallet_group_description_page_create_new_seed_button_key');
|
||||
}
|
||||
bool hasNewSingleSeedButton() =>
|
||||
isKeyPresent("wallet_group_description_page_create_new_seed_button_key");
|
||||
|
||||
Future<void> navigateToCreateNewSeedPage() async {
|
||||
if (hasNewSingleSeedButton()) {
|
||||
await commonTestCases.tapItemByKey(
|
||||
'wallet_group_description_page_create_new_seed_button_key',
|
||||
);
|
||||
await tapByKey("wallet_group_description_page_create_new_seed_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> navigateToChooseWalletGroup() async {
|
||||
await commonTestCases.tapItemByKey(
|
||||
'wallet_group_description_page_choose_wallet_group_button_key',
|
||||
);
|
||||
await tapByKey("wallet_group_description_page_choose_wallet_group_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import "package:hash_wallet/src/screens/new_wallet/wallet_group_existing_seed_description_page.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WalletGroupExistingSeedPageRobot extends BaseRobot {
|
||||
WalletGroupExistingSeedPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WalletGroupExistingSeedDescriptionPage>();
|
||||
}
|
||||
|
||||
Future<void> openWallet() async {
|
||||
await tapByKey("wallet_group_existing_seed_description_page_open_wallet_button_key");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import "package:hash_wallet/src/screens/new_wallet/wallet_group_display_page.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WalletGroupsDisplayPageRobot extends BaseRobot {
|
||||
WalletGroupsDisplayPageRobot(super.tester);
|
||||
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WalletGroupsDisplayPage>();
|
||||
}
|
||||
|
||||
Future<void> selectWallet(String name) async {
|
||||
await tapWhenVisible(find.text(name));
|
||||
}
|
||||
|
||||
Future<void> continueWithSelectedSeed() async {
|
||||
await tapByKey("wallet_group_display_page_next_button_key");
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,38 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/reactions/wallet_connect.dart';
|
||||
import 'package:hash_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||
import 'package:hash_wallet/store/app_store.dart';
|
||||
import 'package:cw_core/monero_wallet_keys.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cw_monero/monero_wallet.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:polyseed/polyseed.dart';
|
||||
import "package:hash_wallet/reactions/wallet_connect.dart";
|
||||
import "package:hash_wallet/src/screens/wallet_keys/wallet_keys_page.dart";
|
||||
import "package:hash_wallet/store/app_store.dart";
|
||||
import "package:cw_core/monero_wallet_keys.dart";
|
||||
import "package:cw_core/wallet_type.dart";
|
||||
import "package:cw_monero/monero_wallet.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
import "package:polyseed/polyseed.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WalletKeysAndSeedPageRobot {
|
||||
WalletKeysAndSeedPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class WalletKeysAndSeedPageRobot extends BaseRobot {
|
||||
WalletKeysAndSeedPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
final CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isWalletKeysAndSeedPage() async {
|
||||
await commonTestCases.isSpecificPage<WalletKeysPage>();
|
||||
await commonTestCases.takeScreenshots('wallet_keys_page');
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WalletKeysPage>();
|
||||
}
|
||||
|
||||
void hasTitle() {
|
||||
final walletKeysPage = tester.widget<WalletKeysPage>(find.byType(WalletKeysPage));
|
||||
final walletKeysViewModel = walletKeysPage.walletKeysViewModel;
|
||||
commonTestCases.hasText(walletKeysViewModel.title);
|
||||
}
|
||||
|
||||
void hasShareWarning() {
|
||||
commonTestCases.hasText(S.current.do_not_share_warning_text.toUpperCase());
|
||||
}
|
||||
int _verifiedCredentials = 0;
|
||||
|
||||
Future<void> confirmWalletCredentials(WalletType walletType) async {
|
||||
_verifiedCredentials = 0;
|
||||
|
||||
final walletKeysPage = tester.widget<WalletKeysPage>(find.byType(WalletKeysPage));
|
||||
final walletKeysViewModel = walletKeysPage.walletKeysViewModel;
|
||||
|
||||
final appStore = walletKeysViewModel.appStore;
|
||||
final walletName = walletType.name;
|
||||
bool hasSeed = appStore.wallet!.seed != null;
|
||||
bool hasHexSeed = appStore.wallet!.hexSeed != null;
|
||||
bool hasPrivateKey = appStore.wallet!.privateKey != null;
|
||||
final hasSeed = appStore.wallet!.seed != null;
|
||||
final hasHexSeed = appStore.wallet!.hexSeed != null;
|
||||
final hasPrivateKey = appStore.wallet!.privateKey != null;
|
||||
|
||||
if (walletType == WalletType.monero) {
|
||||
final moneroWallet = appStore.wallet as MoneroWalletBase;
|
||||
final moneroWallet = appStore.wallet! as MoneroWalletBase;
|
||||
final lang = PolyseedLang.getByPhrase(moneroWallet.seed);
|
||||
final legacySeed = moneroWallet.seedLegacy(lang.nameEnglish);
|
||||
|
||||
@@ -57,51 +47,89 @@ class WalletKeysAndSeedPageRobot {
|
||||
if (walletType == WalletType.bitcoin ||
|
||||
walletType == WalletType.litecoin ||
|
||||
walletType == WalletType.bitcoinCash) {
|
||||
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||
for (var seedWord in seedWords) {
|
||||
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||
}
|
||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||
_showsSeedWords(appStore.wallet!.seed!);
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has seeds properly displayed");
|
||||
}
|
||||
|
||||
// Hash Bags does not ship Solana or Tron; those WalletType members were
|
||||
// removed with the chains, so only the EVM chains remain here.
|
||||
if (isEVMCompatibleChain(walletType)) {
|
||||
if (hasSeed) {
|
||||
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||
for (var seedWord in seedWords) {
|
||||
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||
}
|
||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||
_showsSeedWords(appStore.wallet!.seed!);
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has seeds properly displayed");
|
||||
}
|
||||
if (hasPrivateKey) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
||||
tester.printToConsole('$walletName wallet has private key properly displayed');
|
||||
await _openKeysTab();
|
||||
_showsCredentialOnce(appStore.wallet!.privateKey!, "private key");
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has private key properly displayed");
|
||||
}
|
||||
}
|
||||
|
||||
if (walletType == WalletType.nano || walletType == WalletType.banano) {
|
||||
if (hasSeed) {
|
||||
final seedWords = appStore.wallet!.seed!.split(" ");
|
||||
for (var seedWord in seedWords) {
|
||||
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||
}
|
||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||
_showsSeedWords(appStore.wallet!.seed!);
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has seeds properly displayed");
|
||||
}
|
||||
if (hasHexSeed) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||
commonTestCases.hasText(appStore.wallet!.hexSeed!);
|
||||
tester.printToConsole('$walletName wallet has hexSeed properly displayed');
|
||||
await _openKeysTab();
|
||||
_showsCredentialOnce(appStore.wallet!.hexSeed!, "hex seed");
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has hexSeed properly displayed");
|
||||
}
|
||||
if (hasPrivateKey) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||
commonTestCases.hasText(appStore.wallet!.privateKey!);
|
||||
tester.printToConsole('$walletName wallet has private key properly displayed');
|
||||
await _openKeysTab();
|
||||
_showsCredentialOnce(appStore.wallet!.privateKey!, "private key");
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has private key properly displayed");
|
||||
}
|
||||
}
|
||||
|
||||
await commonTestCases.defaultSleepTime(seconds: 5);
|
||||
_expectSomethingWasVerified(walletType);
|
||||
}
|
||||
|
||||
void _expectSomethingWasVerified(WalletType walletType) {
|
||||
expect(
|
||||
_verifiedCredentials,
|
||||
greaterThan(0),
|
||||
reason: "Nothing was verified for ${walletType.name}, this suite covers no credential "
|
||||
"for that type so it cannot tell a working keys page from a broken one",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _openKeysTab() async {
|
||||
await tapByKey("wallet_keys_page_keys");
|
||||
|
||||
await settle();
|
||||
}
|
||||
|
||||
void _showsCredential(String value, String label) {
|
||||
expect(
|
||||
tester.any(find.text(value, skipOffstage: false)),
|
||||
true,
|
||||
reason: "The keys page did not show the wallet's $label",
|
||||
);
|
||||
}
|
||||
|
||||
void _showsCredentialOnce(String value, String label) {
|
||||
final rows = tester.widgetList(find.text(value, skipOffstage: false)).length;
|
||||
|
||||
expect(
|
||||
rows,
|
||||
1,
|
||||
reason: "The keys page showed $rows rows for the wallet's $label, expected one",
|
||||
);
|
||||
}
|
||||
|
||||
void _showsSeedWords(String seed) {
|
||||
final words = seed.split(" ");
|
||||
|
||||
for (var index = 0; index < words.length; index++) {
|
||||
_showsCredential(words[index], "seed word ${index + 1}");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _confirmMoneroWalletCredentials(
|
||||
@@ -111,68 +139,42 @@ class WalletKeysAndSeedPageRobot {
|
||||
String legacySeed,
|
||||
) async {
|
||||
final keys = appStore.wallet!.keys as MoneroWalletKeys;
|
||||
|
||||
final hasPublicSpendKey = commonTestCases.isKeyPresent(
|
||||
'${walletName}_wallet_public_spend_key_item_key',
|
||||
);
|
||||
final hasPrivateSpendKey = commonTestCases.isKeyPresent(
|
||||
'${walletName}_wallet_private_spend_key_item_key',
|
||||
);
|
||||
final hasPublicViewKey = commonTestCases.isKeyPresent(
|
||||
'${walletName}_wallet_public_view_key_item_key',
|
||||
);
|
||||
final hasPrivateViewKey = commonTestCases.isKeyPresent(
|
||||
'${walletName}_wallet_private_view_key_item_key',
|
||||
);
|
||||
final hasSeeds = seed.isNotEmpty;
|
||||
final hasSeedLegacy = Polyseed.isValidSeed(seed);
|
||||
|
||||
if (hasPublicSpendKey) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||
commonTestCases.hasText(keys.publicSpendKey);
|
||||
tester.printToConsole('$walletName wallet has public spend key properly displayed');
|
||||
}
|
||||
if (hasPrivateSpendKey) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||
commonTestCases.hasText(keys.privateSpendKey);
|
||||
tester.printToConsole('$walletName wallet has private spend key properly displayed');
|
||||
}
|
||||
if (hasPublicViewKey) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||
commonTestCases.hasText(keys.publicViewKey);
|
||||
tester.printToConsole('$walletName wallet has public view key properly displayed');
|
||||
}
|
||||
if (hasPrivateViewKey) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_keys');
|
||||
commonTestCases.hasText(keys.privateViewKey);
|
||||
tester.printToConsole('$walletName wallet has private view key properly displayed');
|
||||
}
|
||||
await _openKeysTab();
|
||||
|
||||
_showsCredentialOnce(keys.publicSpendKey, "public spend key");
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has public spend key properly displayed");
|
||||
|
||||
_showsCredentialOnce(keys.privateSpendKey, "private spend key");
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has private spend key properly displayed");
|
||||
|
||||
_showsCredentialOnce(keys.publicViewKey, "public view key");
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has public view key properly displayed");
|
||||
|
||||
_showsCredentialOnce(keys.privateViewKey, "private view key");
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has private view key properly displayed");
|
||||
|
||||
if (hasSeeds) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_seed');
|
||||
final seedWords = seed.split(" ");
|
||||
for (var seedWord in seedWords) {
|
||||
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||
}
|
||||
tester.printToConsole('$walletName wallet has seeds properly displayed');
|
||||
await tapByKey("wallet_keys_page_seed");
|
||||
await settle();
|
||||
|
||||
_showsSeedWords(seed);
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has seeds properly displayed");
|
||||
}
|
||||
if (hasSeedLegacy) {
|
||||
await commonTestCases.tapItemByKey('wallet_keys_page_seed_legacy');
|
||||
final seedWords = legacySeed.split(" ");
|
||||
for (var seedWord in seedWords) {
|
||||
commonTestCases.hasTextAtLestOnce(seedWord);
|
||||
}
|
||||
tester.printToConsole('$walletName wallet has legacy seeds properly displayed');
|
||||
await tapByKey("wallet_keys_page_seed_legacy");
|
||||
await settle();
|
||||
|
||||
_showsSeedWords(legacySeed);
|
||||
_verifiedCredentials++;
|
||||
tester.printToConsole("$walletName wallet has legacy seeds properly displayed");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> backToDashboard() async {
|
||||
tester.printToConsole('Going back to dashboard from credentials page');
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 1000));
|
||||
|
||||
await commonTestCases.goBack();
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 1000));
|
||||
|
||||
await commonTestCases.goBack();
|
||||
await tester.pumpAndSettle(Duration(milliseconds: 2000));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,48 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/wallet_list/wallet_list_page.dart";
|
||||
import "package:flutter/material.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WalletListPageRobot {
|
||||
WalletListPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class WalletListPageRobot extends BaseRobot {
|
||||
WalletListPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
final shown = await pumpUntil(_isShowing);
|
||||
|
||||
Future<void> isWalletListPage() async {
|
||||
await commonTestCases.isSpecificPage<WalletListPageRobot>();
|
||||
await commonTestCases.takeScreenshots('wallet_list_page');
|
||||
expect(shown, true, reason: "The wallets tab never came to the front");
|
||||
}
|
||||
|
||||
void displaysCorrectTitle() {
|
||||
commonTestCases.hasText(S.current.wallets);
|
||||
Future<void> openEditFor(String walletName) async {
|
||||
await tapByKey("wallet_list_edit_${walletName}_button_key");
|
||||
}
|
||||
|
||||
bool hasWallet(String walletName) => tester.any(
|
||||
find.descendant(
|
||||
of: find.byType(WalletListPage),
|
||||
matching: find.text(walletName),
|
||||
),
|
||||
);
|
||||
|
||||
Future<void> navigateToCreateNewWalletPage() async {
|
||||
await commonTestCases.tapItemByKey('wallet_list_page_create_new_wallet_button_key');
|
||||
await tapByKey("wallet_list_page_create_new_wallet_button_key");
|
||||
}
|
||||
|
||||
Future<void> navigateToRestoreWalletOptionsPage() async {
|
||||
await commonTestCases.tapItemByKey('wallet_list_page_restore_wallet_button_key');
|
||||
await tapByKey("wallet_list_page_restore_wallet_button_key");
|
||||
}
|
||||
|
||||
bool _isShowing() {
|
||||
final stackFinder = find.byType(IndexedStack);
|
||||
|
||||
if (!tester.any(stackFinder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final stack = tester.widget<IndexedStack>(stackFinder.first);
|
||||
final index = stack.children.indexWhere((child) => child is WalletListPage);
|
||||
|
||||
return index >= 0 && stack.index == index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,19 @@
|
||||
import 'package:hash_wallet/generated/i18n.dart';
|
||||
import 'package:hash_wallet/src/screens/seed/wallet_seed_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/generated/i18n.dart";
|
||||
import "package:hash_wallet/src/screens/seed/wallet_seed_page.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WalletSeedPageRobot {
|
||||
WalletSeedPageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class WalletSeedPageRobot extends BaseRobot {
|
||||
WalletSeedPageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isWalletSeedPage() async {
|
||||
await commonTestCases.isSpecificPage<WalletSeedPage>();
|
||||
await commonTestCases.takeScreenshots('wallet_seed_page');
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WalletSeedPage>();
|
||||
}
|
||||
|
||||
Future<void> onVerifySeedButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_seed_page_verify_seed_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onSaveSeedButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_seed_page_save_seeds_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onConfirmButtonOnSeedAlertDialogPressed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_seed_page_seed_alert_confirm_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onBackButtonOnSeedAlertDialogPressed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_seed_page_seed_alert_back_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("wallet_seed_page_verify_seed_button_key");
|
||||
}
|
||||
|
||||
void confirmWalletDetailsDisplayCorrectly() {
|
||||
@@ -42,23 +23,18 @@ class WalletSeedPageRobot {
|
||||
|
||||
final walletName = walletSeedViewModel.name;
|
||||
final walletSeeds = walletSeedViewModel.seedSplit;
|
||||
commonTestCases.hasText(walletName);
|
||||
for (var seed in walletSeeds) {
|
||||
commonTestCases.hasTextAtLestOnce(seed);
|
||||
hasText(walletName);
|
||||
|
||||
for (var index = 0; index < walletSeeds.length; index++) {
|
||||
expect(
|
||||
tester.any(find.text(walletSeeds[index])),
|
||||
true,
|
||||
reason: "The seed page did not show seed word ${index + 1}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void confirmWalletSeedReminderDisplays() {
|
||||
commonTestCases.hasText(S.current.cake_seeds_save_disclaimer);
|
||||
}
|
||||
|
||||
Future<void> onSaveSeedsButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_seed_page_save_seeds_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> onCopySeedsButtonPressed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_seed_page_copy_seeds_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
hasText(S.current.cake_seeds_save_disclaimer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,28 @@
|
||||
import 'package:hash_wallet/src/screens/welcome/welcome_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import "package:hash_wallet/src/screens/welcome/welcome_page.dart";
|
||||
import "package:flutter_test/flutter_test.dart";
|
||||
|
||||
import '../components/common_test_cases.dart';
|
||||
import "../core/base_robot.dart";
|
||||
|
||||
class WelcomePageRobot {
|
||||
WelcomePageRobot(this.tester) : commonTestCases = CommonTestCases(tester);
|
||||
class WelcomePageRobot extends BaseRobot {
|
||||
WelcomePageRobot(super.tester);
|
||||
|
||||
final WidgetTester tester;
|
||||
late CommonTestCases commonTestCases;
|
||||
|
||||
Future<void> isWelcomePage() async {
|
||||
await commonTestCases.isSpecificPage<WelcomePage>();
|
||||
await commonTestCases.takeScreenshots('welcome_page');
|
||||
}
|
||||
|
||||
void confirmActionButtonsDisplay() {
|
||||
final createNewWalletButton = find.byKey(ValueKey('welcome_page_create_new_wallet_button_key'));
|
||||
|
||||
final restoreWalletButton = find.byKey(ValueKey('welcome_page_restore_wallet_button_key'));
|
||||
|
||||
expect(createNewWalletButton, findsOneWidget);
|
||||
expect(restoreWalletButton, findsOneWidget);
|
||||
@override
|
||||
Future<void> isDisplayed() async {
|
||||
await isSpecificPage<WelcomePage>();
|
||||
}
|
||||
|
||||
Future<void> navigateToCreateNewWalletPage() async {
|
||||
await commonTestCases.tapItemByKey('welcome_page_create_new_wallet_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("welcome_page_create_new_wallet_button_key");
|
||||
}
|
||||
|
||||
bool hasNewSingleSeedButton() {
|
||||
return commonTestCases.isKeyPresent('wallet_group_description_page_create_new_seed_button_key');
|
||||
}
|
||||
bool hasNewSingleSeedButton() =>
|
||||
isKeyPresent("wallet_group_description_page_create_new_seed_button_key");
|
||||
|
||||
Future<void> tapNewSingleSeed() async {
|
||||
await commonTestCases.tapItemByKey('wallet_group_description_page_create_new_seed_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
await tapByKey("wallet_group_description_page_create_new_seed_button_key");
|
||||
}
|
||||
|
||||
Future<void> navigateToRestoreWalletPage() async {
|
||||
await commonTestCases.tapItemByKey('welcome_page_restore_wallet_button_key');
|
||||
await commonTestCases.defaultSleepTime();
|
||||
}
|
||||
|
||||
Future<void> backAndVerify() async {
|
||||
await commonTestCases.goBack();
|
||||
await isWelcomePage();
|
||||
await tapByKey("welcome_page_restore_wallet_button_key");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user