Files
hash-wallet/integration_test/flows/funds_flows.dart
T
jwinterm dd449f2594 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.
2026-10-24 23:49:29 -04:00

156 lines
4.5 KiB
Dart

import "package:hash_wallet/di.dart";
import "package:hash_wallet/store/app_store.dart";
import "package:cw_core/sync_status.dart";
import "package:cw_core/wallet_type.dart";
import "package:flutter/material.dart";
import "package:flutter_test/flutter_test.dart";
import "../core/test_config.dart";
import "../core/test_wallets.dart";
import "../robots/home_page_robot.dart";
import "../robots/new_dashboard_robot.dart";
import "../robots/new_send_page_robot.dart";
import "onboarding_flows.dart";
class FundsFlows {
FundsFlows(this.tester)
: _onboardingFlows = OnboardingFlows(tester),
_dashboardRobot = NewDashboardRobot(tester),
_homePageRobot = HomePageRobot(tester),
_sendRobot = NewSendPageRobot(tester);
final WidgetTester tester;
final OnboardingFlows _onboardingFlows;
final NewDashboardRobot _dashboardRobot;
final HomePageRobot _homePageRobot;
final NewSendPageRobot _sendRobot;
bool _hasRestoredAnyWallet = false;
Future<bool> openFundedWallet(WalletType type) async {
final wallets = TestWallets.fundedWalletsFor(type);
for (int index = 0; index < wallets.length; index++) {
final wallet = wallets[index];
tester.printToConsole("Trying funded wallet ${index + 1}/${wallets.length} for ${type.name}");
if (_hasRestoredAnyWallet) {
await _dashboardRobot.openWalletsTab();
final isWalletListReady = await _dashboardRobot.pumpUntil(
() =>
tester.any(find.byKey(const ValueKey("wallet_list_page_restore_wallet_button_key"))),
timeout: const Duration(seconds: 60),
);
expect(
isWalletListReady,
true,
reason: "The wallet list restore button didn't show before ${type.name}, "
"Currently have: ${_dashboardRobot.describeScreen()} on screen",
);
await _onboardingFlows.restoreAdditionalWalletFromWalletList(
type,
seed: wallet.seed,
passphrase: wallet.passphrase,
);
} else {
await _onboardingFlows.restoreFirstWalletFromSeed(
type,
seed: wallet.seed,
passphrase: wallet.passphrase,
);
}
_hasRestoredAnyWallet = true;
await _waitForDashboard(type);
await _homePageRobot.isDisplayed();
if (await _hasSpendableBalance()) {
return true;
}
tester.printToConsole("Wallet ${index + 1} for ${type.name} has no spendable balance");
}
return false;
}
Future<bool> returnToHome({int maxPops = 6}) async {
for (int attempt = 0; attempt <= maxPops; attempt++) {
if (await _homePageRobot.pumpUntil(
() => _isHomeInFront,
timeout: const Duration(seconds: 3),
)) {
return true;
}
try {
await _homePageRobot.dismissModal();
} catch (e) {
tester.printToConsole("Nothing left to pop while returning home: $e");
break;
}
}
return _isHomeInFront;
}
Future<void> _waitForDashboard(WalletType type) async {
final reached = await _dashboardRobot.pumpUntil(
() => tester.any(find.byKey(const ValueKey("new_dashboard_page_key"))),
timeout: TestConfig.walletSyncBudget,
);
if (reached) {
return;
}
final status = getIt.get<AppStore>().wallet?.syncStatus;
final stillSyncing = status is SyncingSyncStatus || status is SyncronizingSyncStatus;
throw TestFailure(
stillSyncing
? "${type.name} never became ready to send, it is still syncing (${status.runtimeType})"
: "${type.name} never reached the dashboard, its status was ${status.runtimeType}",
);
}
bool get _isHomeInFront => _homePageRobot.isInFront && !_sendRobot.isSendFlowOpen;
Future<bool> _hasSpendableBalance({Duration timeout = TestConfig.walletSyncBudget}) async {
final appStore = getIt.get<AppStore>();
final endTime = DateTime.now().add(timeout);
DateTime? syncedSince;
while (DateTime.now().isBefore(endTime)) {
await tester.pump(const Duration(milliseconds: 250));
final wallet = appStore.wallet;
if (wallet == null) {
continue;
}
if (wallet.balance.values.any((balance) => balance.available.sign > 0)) {
return true;
}
if (wallet.syncStatus is SyncedSyncStatus) {
syncedSince ??= DateTime.now();
if (DateTime.now().difference(syncedSince) > const Duration(seconds: 15)) {
return false;
}
} else {
syncedSince = null;
}
}
return false;
}
}