Files
hash-wallet/test/new-ui/pages/card_customizer_test.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

253 lines
8.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import "dart:async";
import "package:hash_wallet/di.dart";
import "package:hash_wallet/entities/balance_display_mode.dart";
import "package:hash_wallet/entities/bitcoin_amount_display_mode.dart";
import "package:hash_wallet/generated/i18n.dart";
import "package:hash_wallet/locales/locale.dart";
import "package:hash_wallet/new-ui/pages/card_customizer.dart";
import "package:hash_wallet/new-ui/viewmodels/card_customizer/card_customizer_bloc.dart";
import "package:hash_wallet/new-ui/widgets/coins_page/cards/balance_card.dart";
import "package:hash_wallet/new-ui/widgets/money/money_settings_provider.dart";
import "package:hash_wallet/themes/core/theme_store.dart";
import "package:hash_wallet/store/settings_store.dart";
import "package:hash_wallet/view_model/dashboard/dashboard_view_model.dart";
import "package:hash_wallet/view_model/wallet_account_list/account_list_item.dart";
import "package:cw_core/amount/money.dart";
import "package:cw_core/card_design.dart";
import "package:cw_core/crypto_currency.dart";
import "package:flutter/material.dart";
import "package:flutter_bloc/flutter_bloc.dart";
import "package:flutter_test/flutter_test.dart";
import "package:mocktail/mocktail.dart";
class _MockCardCustomizerBloc extends Mock implements CardCustomizerBloc {}
class _MockDashboardViewModel extends Mock implements DashboardViewModel {}
class _MockSettingsStore extends Mock implements SettingsStore {}
class _RouteTracker {
bool completed = false;
bool? result;
}
CardCustomizerState _accountState(String accountName) => CardCustomizerInitial(
0,
0,
const <CardDesign>[CardDesign.gradientOnlyDesign],
const <Gradient>[CardDesign.gradientBlue],
accountName,
0,
false,
0,
);
Future<_RouteTracker> _openCustomizer(
WidgetTester tester,
CardCustomizerBloc bloc, {
required DashboardViewModel dashboardViewModel,
required AccountListItem? account,
String fiatBalance = "",
}) async {
final tracker = _RouteTracker();
await tester.pumpWidget(
MaterialApp(
localizationsDelegates: localizationDelegates,
supportedLocales: S.delegate.supportedLocales,
builder: (_, child) => MoneySettingsProvider(
settingsStore: dashboardViewModel.settingsStore,
child: child!,
),
home: Builder(
builder: (context) => Scaffold(
body: TextButton(
onPressed: () {
Navigator.of(context)
.push<bool>(
MaterialPageRoute(
builder: (_) => BlocProvider<CardCustomizerBloc>.value(
value: bloc,
child: Material(
child: CardCustomizer(
cryptoTitle: "Monero",
cryptoName: "xmr",
account: account,
fiatBalance: fiatBalance,
),
),
),
),
)
.then((result) {
tracker
..completed = true
..result = result;
});
},
child: const Text("Open card customizer"),
),
),
),
),
);
await tester.tap(find.text("Open card customizer"));
await tester.pumpAndSettle();
return tracker;
}
void main() {
late _MockCardCustomizerBloc bloc;
late _MockDashboardViewModel dashboardViewModel;
late AccountListItem account;
late StreamController<CardCustomizerState> stateController;
setUpAll(() {
getIt.registerSingleton(ThemeStore());
});
tearDownAll(() async {
await getIt.unregister<ThemeStore>();
});
setUp(() {
bloc = _MockCardCustomizerBloc();
dashboardViewModel = _MockDashboardViewModel();
account =
AccountListItem(id: 0, label: "Savings", balance: Money.parse("1.25", CryptoCurrency.xmr));
final settingsStore = _MockSettingsStore();
when(() => settingsStore.balanceDisplayMode).thenReturn(BalanceDisplayMode.displayableBalance);
when(() => dashboardViewModel.settingsStore).thenReturn(settingsStore);
when(() => settingsStore.displayAmountsInSatoshi).thenReturn(BitcoinAmountDisplayMode.bitcoin);
stateController = StreamController<CardCustomizerState>.broadcast();
when(() => bloc.stream).thenAnswer((_) => stateController.stream);
when(() => bloc.canHide).thenReturn(true);
});
tearDown(() async {
await stateController.close();
});
testWidgets("account mode renders edit controls and cancels funded archival", (tester) async {
when(() => bloc.state).thenReturn(_accountState(""));
final tracker = await _openCustomizer(
tester,
bloc,
dashboardViewModel: dashboardViewModel,
account: account,
fiatBalance: "2.50 USD",
);
expect(find.text("Edit Account"), findsOneWidget);
expect(find.text("#1"), findsOneWidget);
expect(find.text("Unnamed Account"), findsOneWidget);
expect(find.text("1.25"), findsWidgets);
expect(find.text("2.50 USD"), findsOneWidget);
expect(find.text("Account name"), findsOneWidget);
expect(find.byType(TextField), findsOneWidget);
expect(find.text("Archive Account"), findsOneWidget);
expect(find.text("This change can be reverted"), findsOneWidget);
expect(
tester.getTopLeft(find.byType(BalanceCard).first).dy,
lessThan(tester.getTopLeft(find.text("Account name")).dy),
);
await tester.ensureVisible(find.text("Archive Account"));
await tester.pumpAndSettle();
await tester.tap(find.text("Archive Account"));
await tester.pumpAndSettle();
expect(find.text("Are you sure you want to Archive this account?"), findsOneWidget);
expect(find.text("This account has the following funds:"), findsOneWidget);
expect(find.text("1.25 XMR"), findsOneWidget);
expect(
find.text("Before proceeding, it is recommended you move them to an account you will use"),
findsOneWidget,
);
expect(
find.text(
"Archiving an account does not delete any funds or activity. You can reverse this action",
),
findsOneWidget,
);
await tester.tap(find.text("Cancel"));
await tester.pumpAndSettle();
expect(find.byType(CardCustomizer), findsOneWidget);
expect(tracker.completed, isFalse);
});
testWidgets("confirming hidden empty-account archival returns true to the parent",
(tester) async {
account =
AccountListItem(id: 0, label: "Savings", balance: Money.parse("0", CryptoCurrency.xmr));
final settingsStore = dashboardViewModel.settingsStore;
when(() => settingsStore.balanceDisplayMode).thenReturn(BalanceDisplayMode.hiddenBalance);
when(() => bloc.state).thenReturn(_accountState("Savings"));
final tracker = await _openCustomizer(
tester,
bloc,
dashboardViewModel: dashboardViewModel,
account: account,
);
await tester.ensureVisible(find.text("Archive Account"));
await tester.pumpAndSettle();
await tester.tap(find.text("Archive Account"));
await tester.pumpAndSettle();
expect(find.text("1. Savings"), findsOneWidget);
expect(find.text("This account has the following funds:"), findsNothing);
expect(
find.text(
"This action won’t delete the account or its past activity, but only hide it inside Hash Bags",
),
findsOneWidget,
);
expect(find.text("You can reverse this action from Accounts settings"), findsOneWidget);
await tester.tap(find.text("Continue"));
await tester.pumpAndSettle();
expect(find.byType(CardCustomizer), findsNothing);
expect(tracker.completed, isTrue);
expect(tracker.result, isTrue);
});
testWidgets("account editing remains available when archival is disabled", (tester) async {
when(() => bloc.state).thenReturn(_accountState("Savings"));
when(() => bloc.canHide).thenReturn(false);
await _openCustomizer(
tester,
bloc,
dashboardViewModel: dashboardViewModel,
account: account,
);
expect(find.text("Edit Account"), findsOneWidget);
expect(find.text("Account name"), findsOneWidget);
expect(find.byType(TextField), findsOneWidget);
expect(find.text("Savings"), findsWidgets);
expect(find.text("Archive Account"), findsNothing);
});
testWidgets("wallet card editing does not require an account", (tester) async {
when(() => bloc.state).thenReturn(_accountState(""));
when(() => bloc.canHide).thenReturn(false);
await _openCustomizer(
tester,
bloc,
dashboardViewModel: dashboardViewModel,
account: null,
);
expect(find.text("Edit Card"), findsOneWidget);
expect(find.byType(BalanceCard), findsWidgets);
expect(find.byType(TextField), findsNothing);
expect(find.text("Archive Account"), findsNothing);
});
}