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:
jwinterm
2026-10-24 23:49:29 -04:00
1406 changed files with 81062 additions and 48972 deletions
@@ -0,0 +1,46 @@
import "package:cw_core/wallet_type.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_wallets.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/contact_robot.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("A saved contact keeps the address it was given", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final contactRobot = ContactRobot(tester);
const contactName = "Integration Test Contact";
final contactAddress = TestWallets.receiveAddressFor(WalletType.solana);
await appLauncher.launchApp(testKey: "address_book_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await dashboardRobot.openContactsTab();
await contactRobot.isDisplayed();
await contactRobot.openAddContact();
await contactRobot.enterName(contactName);
await contactRobot.chooseCurrency("sol");
await contactRobot.enterAddress(contactAddress);
await contactRobot.save();
await contactRobot.isDisplayed();
contactRobot.expectContactSaved(contactName, contactAddress);
});
}
@@ -0,0 +1,73 @@
import "package:hash_wallet/routes.dart";
import "package:hash_wallet/src/screens/wallet_keys/wallet_keys_page.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_config.dart";
import "../../flows/auth_flows.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_settings_page_robot.dart";
import "../../robots/security_backup_page_robot.dart";
import "../../robots/setup_pin_code_robot.dart";
import "../../robots/show_keys_disclaimer_page_robot.dart";
import "../../robots/wallet_keys_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Changing the pin replaces the one that unlocks the wallet", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final authFlows = AuthFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final settingsRobot = NewSettingsPageRobot(tester);
final securityRobot = SecurityBackupPageRobot(tester);
final setupPinRobot = SetupPinCodeRobot(tester);
final walletKeysRobot = WalletKeysAndSeedPageRobot(tester);
final disclaimerRobot = ShowKeysDisclaimerPageRobot(tester);
final newPin = [1, 2, 3, 4];
expect(newPin, isNot(TestConfig.pin), reason: "The new pin has to differ from the old one");
await appLauncher.launchApp(testKey: "change_pin_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.openSettingsSheet();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.securityBackupPage);
await securityRobot.isDisplayed();
await securityRobot.openChangePin();
await authFlows.authenticateWithPin();
await setupPinRobot.isDisplayed();
await setupPinRobot.enterPinCode(newPin);
await setupPinRobot.enterPinCode(newPin);
await setupPinRobot.tapSuccessButton();
await securityRobot.isDisplayed();
await securityRobot.goBack();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.showKeysDisclaimer);
await disclaimerRobot.isDisplayed();
await disclaimerRobot.tapShowKeys();
await authFlows.authenticateWithPin(pin: newPin);
await walletKeysRobot.isDisplayed();
expect(find.byType(WalletKeysPage), findsOneWidget);
});
}
@@ -0,0 +1,42 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_wallet_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("A wallet name already in use is refused", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final newWalletPageRobot = NewWalletPageRobot(tester);
const walletType = WalletType.solana;
await appLauncher.launchApp(testKey: "duplicate_wallet_name_test_app_key");
await onboardingFlows.createFirstWallet(walletType);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final existingName = getIt.get<AppStore>().wallet!.name;
await dashboardRobot.openWalletsTab();
await onboardingFlows.startCreatingWalletFromWalletList(walletType);
await newWalletPageRobot.enterWalletName(existingName);
await newWalletPageRobot.onNextButtonPressed();
await newWalletPageRobot.expectNameRejected();
});
}
@@ -0,0 +1,107 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/entities/fiat_api_mode.dart";
import "package:hash_wallet/entities/fiat_currency.dart";
import "package:hash_wallet/routes.dart";
import "package:hash_wallet/store/settings_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/connection_sync_page_robot.dart";
import "../../robots/display_settings_page_robot.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_settings_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Turning the fiat api off hides the currency setting, turning it on returns it",
(tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final settingsRobot = NewSettingsPageRobot(tester);
final connectionRobot = ConnectionSyncPageRobot(tester);
final displayRobot = DisplaySettingsPageRobot(tester);
const wanted = FiatCurrency.eur;
await appLauncher.launchApp(testKey: "fiat_currency_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final settingsStore = getIt.get<SettingsStore>();
expect(
settingsStore.fiatCurrency,
isNot(wanted),
reason: "The wallet already starts on $wanted, so changing to it would prove nothing",
);
await homePageRobot.openSettingsSheet();
await settingsRobot.isDisplayed();
if (settingsStore.fiatApiMode != FiatApiMode.disabled) {
await settingsRobot.openRow(Routes.connectionSync);
await connectionRobot.isDisplayed();
await connectionRobot.setFiatApiMode(FiatApiMode.disabled);
connectionRobot.hasFiatApiMode(FiatApiMode.disabled);
expect(settingsStore.fiatApiMode, FiatApiMode.disabled);
await connectionRobot.goBack();
await settingsRobot.isDisplayed();
}
await settingsRobot.openRow(Routes.displaySettingsPage);
await displayRobot.isDisplayed();
expect(
displayRobot.hasFiatCurrencyRow(),
false,
reason: "The currency setting is still showing while the fiat api is off",
);
await displayRobot.goBack();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.connectionSync);
await connectionRobot.isDisplayed();
await connectionRobot.setFiatApiMode(FiatApiMode.enabled);
connectionRobot.hasFiatApiMode(FiatApiMode.enabled);
expect(settingsStore.fiatApiMode, FiatApiMode.enabled);
await connectionRobot.goBack();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.displaySettingsPage);
await displayRobot.isDisplayed();
expect(
displayRobot.hasFiatCurrencyRow(),
true,
reason: "The currency setting did not come back after the fiat api was turned on",
);
await displayRobot.openFiatCurrencyPicker();
await displayRobot.chooseFiatCurrency(wanted);
displayRobot.expectFiatCurrencyShown(wanted);
expect(
settingsStore.fiatCurrency,
wanted,
reason: "The row shows ${wanted.title} but the setting behind it did not change",
);
});
}
@@ -0,0 +1,51 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_wallets.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/restore_from_seed_or_key_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("A seed the wallet cannot parse restores nothing", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final restoreRobot = RestoreFromSeedOrKeysPageRobot(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
const walletType = WalletType.solana;
await appLauncher.launchApp(testKey: "invalid_seed_test_app_key");
await onboardingFlows.startRestoringFirstWallet(walletType);
// Twelve words that are not in the wordlist. Restoring from this would either fail
// outright or, worse it derives some other wallet the user has no keys for.
await restoreRobot.enterSeedPhraseForWalletRestore(
"alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima",
);
await restoreRobot.onRestoreWalletButtonPressed();
await restoreRobot.expectRestoreRefused();
// The same form with the real seed goes through. Without this the test above would pass
// just as happily if the form were broken and restored nothing for any seed at all.
await restoreRobot.selectWalletNameFromAvailableOptions();
await restoreRobot.enterSeedPhraseForWalletRestore(TestWallets.seedFor(walletType));
await restoreRobot.onRestoreWalletButtonPressed();
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
expect(getIt.get<AppStore>().wallet?.type, walletType);
});
}
@@ -0,0 +1,46 @@
import "package:hash_wallet/routes.dart";
import "package:cw_core/wallet_type.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/display_settings_page_robot.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_settings_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Changing the language changes what the settings screen shows", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final settingsRobot = NewSettingsPageRobot(tester);
final displayRobot = DisplaySettingsPageRobot(tester);
// Not the default, so seeing it afterwards means the change took rather than the screen
// having always said so.
const languageCode = "de";
const languageName = "Deutsch (German)";
await appLauncher.launchApp(testKey: "language_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.openSettingsSheet();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.displaySettingsPage);
await displayRobot.isDisplayed();
await displayRobot.openLanguagePicker();
await displayRobot.chooseLanguage(languageCode);
displayRobot.expectLanguageShown(languageName);
});
}
@@ -0,0 +1,53 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_config.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Create a wallet for each configured wallet type", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
await appLauncher.launchApp(testKey: "onboarding_create_test_app_key");
final walletTypes = TestConfig.walletTypesUnderTest;
final firstType = walletTypes.first;
tester.printToConsole("Creating first wallet: ${firstType.name}");
await onboardingFlows.createFirstWallet(firstType);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final appStore = getIt.get<AppStore>();
expect(appStore.wallet?.type, firstType);
await homePageRobot.hasWalletName(appStore.wallet!.name);
for (final type in walletTypes.skip(1)) {
tester.printToConsole("Creating additional wallet: ${type.name}");
await dashboardRobot.openWalletsTab();
await onboardingFlows.createAdditionalWalletFromWalletList(type);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
expect(appStore.wallet?.type, type);
await homePageRobot.hasWalletName(appStore.wallet!.name);
}
});
}
@@ -0,0 +1,94 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_config.dart";
import "../../core/test_wallets.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Restore a wallet from seed for each configured wallet type", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
await appLauncher.launchApp(testKey: "onboarding_restore_test_app_key");
final walletTypes = TestConfig.walletTypesUnderTest;
final firstType = walletTypes.first;
tester.printToConsole("Restoring first wallet: ${firstType.name}");
await onboardingFlows.restoreFirstWalletFromSeed(firstType);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final appStore = getIt.get<AppStore>();
expect(appStore.wallet?.type, firstType);
await homePageRobot.hasWalletName(appStore.wallet!.name);
await _expectRestoredTheRightWallet(homePageRobot, appStore, firstType);
for (final type in walletTypes.skip(1)) {
tester.printToConsole("Restoring additional wallet: ${type.name}");
await dashboardRobot.openWalletsTab();
await onboardingFlows.restoreAdditionalWalletFromWalletList(type);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
expect(appStore.wallet?.type, type);
await homePageRobot.hasWalletName(appStore.wallet!.name);
await _expectRestoredTheRightWallet(homePageRobot, appStore, type);
}
});
}
Future<void> _expectRestoredTheRightWallet(
HomePageRobot robot,
AppStore appStore,
WalletType type,
) async {
final expectedAddress = TestWallets.receiveAddressFor(type);
if (expectedAddress.isEmpty) {
robot.tester.printToConsole("No known address for ${type.name}, derivation not verified");
return;
}
final settled = await robot.pumpUntil(
() {
final addresses = appStore.wallet?.walletAddresses;
if (addresses == null) {
return false;
}
return addresses.address == expectedAddress ||
addresses.addressesMap.containsKey(expectedAddress) ||
addresses.allAddressesMap.containsKey(expectedAddress);
},
timeout: const Duration(seconds: 60),
);
expect(
settled,
true,
reason: "${type.name} restored from a known seed derived "
"${appStore.wallet?.walletAddresses.address} and does not know $expectedAddress",
);
}
@@ -0,0 +1,43 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_receive_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Receive sheet shows the opened wallet's own address", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final receiveRobot = NewReceivePageRobot(tester);
const walletType = WalletType.solana;
await appLauncher.launchApp(testKey: "receive_address_test_app_key");
await onboardingFlows.restoreFirstWalletFromSeed(walletType);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final expectedAddress = getIt.get<AppStore>().wallet!.walletAddresses.address;
expect(expectedAddress.isNotEmpty, true);
await homePageRobot.openReceiveSheet();
await receiveRobot.isDisplayed();
await receiveRobot.confirmAddressMatches(expectedAddress);
await receiveRobot.dismissModal();
await homePageRobot.isDisplayed();
});
}
@@ -0,0 +1,70 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/routes.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/auth_flows.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_settings_page_robot.dart";
import "../../robots/show_keys_disclaimer_page_robot.dart";
import "../../robots/wallet_keys_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Displayed seed and keys match the opened wallet", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final authFlows = AuthFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final settingsRobot = NewSettingsPageRobot(tester);
final walletKeysRobot = WalletKeysAndSeedPageRobot(tester);
final disclaimerRobot = ShowKeysDisclaimerPageRobot(tester);
// These three cover the different key display paths on the wallet keys page.
final walletTypes = [WalletType.solana, WalletType.bitcoin, WalletType.monero];
await appLauncher.launchApp(testKey: "seed_confirmation_test_app_key");
await onboardingFlows.createFirstWallet(walletTypes.first);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final appStore = getIt.get<AppStore>();
for (final type in walletTypes) {
if (type != walletTypes.first) {
await dashboardRobot.openWalletsTab();
await onboardingFlows.createAdditionalWalletFromWalletList(type);
await homePageRobot.isDisplayed();
}
expect(appStore.wallet?.type, type);
tester.printToConsole("Confirming displayed credentials for ${type.name}");
await homePageRobot.openSettingsSheet();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.showKeysDisclaimer);
await disclaimerRobot.isDisplayed();
await disclaimerRobot.tapShowKeys();
await authFlows.authenticateWithPin();
await walletKeysRobot.isDisplayed();
await walletKeysRobot.confirmWalletCredentials(type);
await settingsRobot.goBack();
await settingsRobot.dismissModal();
await homePageRobot.isDisplayed();
}
});
}
@@ -0,0 +1,59 @@
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_wallets.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/contact_robot.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_send_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("A contact picked on the send screen fills in its own address", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final contactRobot = ContactRobot(tester);
final sendRobot = NewSendPageRobot(tester);
const contactName = "Send Book Contact";
final contactAddress = TestWallets.receiveAddressFor(WalletType.solana);
await appLauncher.launchApp(testKey: "send_from_address_book_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await dashboardRobot.openContactsTab();
await contactRobot.isDisplayed();
await contactRobot.openAddContact();
await contactRobot.enterName(contactName);
await contactRobot.chooseCurrency("sol");
await contactRobot.enterAddress(contactAddress);
await contactRobot.save();
await contactRobot.isDisplayed();
await dashboardRobot.openHomeTab();
await homePageRobot.isDisplayed();
await homePageRobot.openSendSheet();
await sendRobot.isDisplayed();
await sendRobot.pickContactFromAddressBook(contactName);
expect(
sendRobot.enteredAddress(),
contactAddress,
reason: "The send screen was filled with an address the contact does not hold",
);
});
}
@@ -0,0 +1,53 @@
import "package:cw_core/wallet_type.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_wallets.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_send_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Send screen refuses what it cannot send", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final sendRobot = NewSendPageRobot(tester);
const walletType = WalletType.solana;
await appLauncher.launchApp(testKey: "send_validation_test_app_key");
await onboardingFlows.createFirstWallet(walletType);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.openSendSheet();
await sendRobot.isDisplayed();
await sendRobot.tapSendButtonWhenReady(timeout: const Duration(minutes: 5));
await sendRobot.expectNoTransactionBuilt();
// test that no transaction is built when the address is wrong or cannot be parsed
await sendRobot.enterAddress("not-a-real-address");
await sendRobot.enterAmount("0.0001");
await sendRobot.tapSendButton();
await sendRobot.expectNoTransactionBuilt();
// test that no transaction is built even if the address is valid but no balance
await sendRobot.enterAddress(TestWallets.receiveAddressFor(walletType));
await sendRobot.enterAmount("0.0001");
await sendRobot.tapSendButton();
await sendRobot.expectNoTransactionBuilt();
await sendRobot.dismissModal();
await homePageRobot.isDisplayed();
});
}
@@ -0,0 +1,61 @@
import "package:hash_wallet/new-ui/pages/about_page.dart";
import "package:hash_wallet/routes.dart";
import "package:hash_wallet/src/screens/settings/connection_sync_page.dart";
import "package:hash_wallet/src/screens/settings/display_settings_page.dart";
import "package:hash_wallet/src/screens/settings/manage_nodes_page.dart";
import "package:hash_wallet/src/screens/settings/other_settings_page.dart";
import "package:hash_wallet/src/screens/settings/privacy_page.dart";
import "package:hash_wallet/src/screens/settings/security_backup_page.dart";
import "package:cw_core/wallet_type.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_settings_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Every settings row opens its page and backs out", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final settingsRobot = NewSettingsPageRobot(tester);
final rowsUnderTest = <String, Type>{
Routes.manageNodes: ManageNodesPage,
Routes.privacyPage: PrivacyPage,
Routes.otherSettingsPage: OtherSettingsPage,
Routes.connectionSync: ConnectionSyncPage,
Routes.displaySettingsPage: DisplaySettingsPage,
Routes.securityBackupPage: SecurityBackupPage,
Routes.aboutPage: AboutPage,
};
await appLauncher.launchApp(testKey: "settings_nav_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.openSettingsSheet();
await settingsRobot.isDisplayed();
for (final entry in rowsUnderTest.entries) {
tester.printToConsole("Opening settings row ${entry.key}");
await settingsRobot.openRow(entry.key);
await settingsRobot.confirmLeafPageDisplayed(entry.value);
await settingsRobot.goBack();
await settingsRobot.isDisplayed();
}
await settingsRobot.dismissModal();
await homePageRobot.isDisplayed();
});
}
@@ -0,0 +1,61 @@
import "package:hash_wallet/routes.dart";
import "package:hash_wallet/src/screens/wallet_keys/wallet_keys_page.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/auth_flows.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_settings_page_robot.dart";
import "../../robots/show_keys_disclaimer_page_robot.dart";
import "../../robots/wallet_keys_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Seed and keys page rejects a wrong pin and accepts the right one",
(tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final authFlows = AuthFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final settingsRobot = NewSettingsPageRobot(tester);
final walletKeysRobot = WalletKeysAndSeedPageRobot(tester);
final disclaimerRobot = ShowKeysDisclaimerPageRobot(tester);
final wrongPin = [9, 9, 9, 9];
await appLauncher.launchApp(testKey: "show_keys_auth_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.openSettingsSheet();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.showKeysDisclaimer);
await disclaimerRobot.isDisplayed();
await disclaimerRobot.tapShowKeys();
await authFlows.authenticateWithPin(pin: wrongPin);
// A wrong pin must keep the keys page locked away.
await settingsRobot.settle(max: const Duration(seconds: 3));
expect(find.byType(WalletKeysPage), findsNothing);
expect(authFlows.isPinWidgetShown(), true);
await authFlows.authenticateWithPin();
await walletKeysRobot.isDisplayed();
await settingsRobot.goBack();
await settingsRobot.dismissModal();
await homePageRobot.isDisplayed();
});
}
@@ -0,0 +1,85 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_info.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/auth_flows.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/wallet_edit_page_robot.dart";
import "../../robots/wallet_list_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Deleting a wallet takes it off the list and out of storage", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final authFlows = AuthFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final walletListRobot = WalletListPageRobot(tester);
final walletEditRobot = WalletEditPageRobot(tester);
await appLauncher.launchApp(testKey: "wallet_delete_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final appStore = getIt.get<AppStore>();
final deletedWallet = appStore.wallet!.name;
await dashboardRobot.openWalletsTab();
await onboardingFlows.createAdditionalWalletFromWalletList(WalletType.ethereum);
await homePageRobot.isDisplayed();
final keptWallet = appStore.wallet!.name;
expect(keptWallet, isNot(deletedWallet), reason: "The second wallet never became the open one");
await dashboardRobot.openWalletsTab();
await walletListRobot.isDisplayed();
await walletListRobot.openEditFor(deletedWallet);
await walletEditRobot.isDisplayed();
await walletEditRobot.tapDelete();
await authFlows.authenticateWithPin(required: false);
await walletEditRobot.confirmDelete();
await walletListRobot.isDisplayed();
final removed = await walletListRobot.pumpUntil(
() => !walletListRobot.hasWallet(deletedWallet),
timeout: const Duration(minutes: 1),
);
expect(removed, true, reason: "$deletedWallet is still on the list after being deleted");
expect(
walletListRobot.hasWallet(keptWallet),
true,
reason: "Deleting $deletedWallet took $keptWallet with it",
);
final stored = await WalletInfo.getAll();
final names = stored.map((wallet) => wallet.name).toList();
expect(names, isNot(contains(deletedWallet)));
expect(names, contains(keptWallet));
expect(
appStore.wallet?.name,
keptWallet,
reason: "Deleting another wallet changed which one is open",
);
});
}
@@ -0,0 +1,70 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../flows/wallet_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("A wallet added to an existing seed shares it and both stay reachable",
(tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final walletFlows = WalletFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
const firstType = WalletType.solana;
const secondType = WalletType.ethereum;
await appLauncher.launchApp(testKey: "wallet_group_test_app_key");
await onboardingFlows.createFirstWallet(firstType);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final appStore = getIt.get<AppStore>();
final firstWalletName = appStore.wallet!.name;
final sharedSeed = appStore.wallet!.seed;
expect(sharedSeed, isNotNull, reason: "The first wallet has no seed to share");
await dashboardRobot.openWalletsTab();
await onboardingFlows.createWalletInGroupOf(secondType, firstWalletName);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final secondWalletName = appStore.wallet!.name;
expect(
secondWalletName,
isNot(firstWalletName),
reason: "Adding to a group opened the wallet that was already there",
);
expect(appStore.wallet?.type, secondType);
expect(
appStore.wallet!.seed,
sharedSeed,
reason: "The wallet added to the group derived its own seed instead of sharing one",
);
await walletFlows.switchToWallet(firstWalletName);
await homePageRobot.isDisplayed();
await homePageRobot.hasWalletName(firstWalletName);
expect(appStore.wallet?.seed, sharedSeed);
});
}
@@ -0,0 +1,86 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_info.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/wallet_edit_page_robot.dart";
import "../../robots/wallet_list_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("A wallet can be renamed, but not onto a name already in use", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final walletListRobot = WalletListPageRobot(tester);
final walletEditRobot = WalletEditPageRobot(tester);
const renamedTo = "Renamed Test Wallet";
await appLauncher.launchApp(testKey: "wallet_rename_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final appStore = getIt.get<AppStore>();
final renamedWallet = appStore.wallet!.name;
await dashboardRobot.openWalletsTab();
await onboardingFlows.createAdditionalWalletFromWalletList(WalletType.ethereum);
await homePageRobot.isDisplayed();
final openWallet = appStore.wallet!.name;
expect(openWallet, isNot(renamedWallet), reason: "The second wallet never became the open one");
await dashboardRobot.openWalletsTab();
await walletListRobot.isDisplayed();
await walletListRobot.openEditFor(renamedWallet);
await walletEditRobot.isDisplayed();
await walletEditRobot.enterName(openWallet);
await walletEditRobot.save();
await walletEditRobot.expectNameTakenRefusal();
await walletEditRobot.dismissNameTakenRefusal();
await walletEditRobot.enterName(renamedTo);
await walletEditRobot.save();
await walletListRobot.isDisplayed();
final renamed = await walletListRobot.pumpUntil(() => walletListRobot.hasWallet(renamedTo));
expect(renamed, true, reason: "The list never showed $renamedTo after the rename was saved");
expect(
walletListRobot.hasWallet(renamedWallet),
false,
reason: "The list still shows $renamedWallet next to the name it was renamed to",
);
final stored = await WalletInfo.getAll();
final names = stored.map((wallet) => wallet.name).toList();
expect(names, contains(renamedTo));
expect(names, isNot(contains(renamedWallet)));
expect(
appStore.wallet?.name,
openWallet,
reason: "Renaming another wallet changed which one is open",
);
});
}
@@ -0,0 +1,47 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../core/test_wallets.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("A 25 word monero seed restores through the legacy path", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final legacySeed = TestWallets.moneroLegacySeed;
expect(
legacySeed.split(" ").length,
25,
reason: "The legacy seed secret is not a 25 word seed, this suite would restore a polyseed",
);
await appLauncher.launchApp(testKey: "monero_legacy_seed_test_app_key");
await onboardingFlows.restoreFirstWalletFromSeed(WalletType.monero, seed: legacySeed);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
final appStore = getIt.get<AppStore>();
expect(appStore.wallet?.type, WalletType.monero);
expect(
appStore.wallet?.seed?.split(" ").length,
25,
reason: "The wallet came back with a seed of a different length than the one restored",
);
});
}
@@ -0,0 +1,49 @@
import "package:hash_wallet/routes.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/manage_nodes_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_settings_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Switching nodes changes which one the wallet is pointed at", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final settingsRobot = NewSettingsPageRobot(tester);
final nodesRobot = ManageNodesPageRobot(tester);
await appLauncher.launchApp(testKey: "node_switching_test_app_key");
await onboardingFlows.createFirstWallet(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.openSettingsSheet();
await settingsRobot.isDisplayed();
await settingsRobot.openRow(Routes.manageNodes);
await nodesRobot.isDisplayed();
final initialNodeUri = nodesRobot.currentNodeUri();
expect(initialNodeUri, isNotEmpty, reason: "The wallet started with no node at all");
await nodesRobot.switchToAnotherNode();
expect(
nodesRobot.currentNodeUri(),
isNot(initialNodeUri),
reason: "The node was confirmed but the wallet is still pointed at $initialNodeUri",
);
});
}
@@ -0,0 +1,37 @@
import "package:cw_core/wallet_type.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
import "../../robots/new_swap_page_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Swap sheet fetches a provider quote, no trade is created", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
final swapRobot = NewSwapPageRobot(tester);
await appLauncher.launchApp(testKey: "swap_quote_test_app_key");
await onboardingFlows.restoreFirstWalletFromSeed(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.openSwapSheet();
await swapRobot.isDisplayed();
await swapRobot.enterDepositAmount("1");
await swapRobot.confirmQuoteReceived();
await swapRobot.dismissModal();
await homePageRobot.isDisplayed();
});
}
@@ -0,0 +1,27 @@
import "package:cw_core/wallet_type.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Restored wallet connects to a node and starts syncing", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
await appLauncher.launchApp(testKey: "sync_status_test_app_key");
await onboardingFlows.restoreFirstWalletFromSeed(WalletType.bitcoin);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.confirmSyncIndicatorShown();
});
}
@@ -0,0 +1,47 @@
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Tapping a transaction opens the details of that transaction", (tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
await appLauncher.launchApp(testKey: "transaction_details_test_app_key");
await onboardingFlows.restoreFirstWalletFromSeed(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
await homePageRobot.confirmTransactionHistoryVisible();
await homePageRobot.openAllTransactions();
await homePageRobot.confirmAllTransactionsVisible();
final tapped = homePageRobot.firstTransactionIdInAllView();
expect(tapped, isNotEmpty, reason: "The tile carries no transaction id to check against");
await homePageRobot.openFirstTransactionDetails();
// A list that hands the wrong transaction to the details screen shows one person's
// amounts under another's id, and nothing on the screen would say so.
expect(
homePageRobot.openedTransactionId(),
tapped,
reason: "The details opened for a different transaction than the one tapped",
);
homePageRobot.hasTransactionIdRow();
});
}
@@ -0,0 +1,37 @@
import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter_test/flutter_test.dart";
import "package:integration_test/integration_test.dart";
import "../../core/app_launcher.dart";
import "../../flows/onboarding_flows.dart";
import "../../robots/home_page_robot.dart";
import "../../robots/new_dashboard_robot.dart";
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationTest("Transaction history renders for a wallet with known transactions",
(tester) async {
final appLauncher = AppLauncher(tester);
final onboardingFlows = OnboardingFlows(tester);
final dashboardRobot = NewDashboardRobot(tester);
final homePageRobot = HomePageRobot(tester);
await appLauncher.launchApp(testKey: "transaction_history_test_app_key");
await onboardingFlows.restoreFirstWalletFromSeed(WalletType.solana);
await dashboardRobot.isDisplayed();
await homePageRobot.isDisplayed();
expect(getIt.get<AppStore>().wallet?.type, WalletType.solana);
await homePageRobot.confirmTransactionHistoryVisible();
await homePageRobot.openAllTransactions();
await homePageRobot.confirmAllTransactionsVisible();
});
}