mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-11-12 08:53:57 +01:00
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.
253 lines
8.7 KiB
Dart
253 lines
8.7 KiB
Dart
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);
|
||
});
|
||
}
|