Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../../../models/paymint/fee_object_model.dart';
import '../../../../pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
import '../../../../providers/providers.dart';
import '../../../../providers/ui/fee_rate_type_state_provider.dart';
Expand Down Expand Up @@ -56,6 +57,7 @@ class _DesktopSendFeeFormState extends ConsumerState<DesktopSendFeeForm> {
];

late final CryptoCurrency cryptoCurrency;
Future<FeeObject>? _fees;

bool get isEth => cryptoCurrency is Ethereum;

Expand Down Expand Up @@ -106,6 +108,8 @@ class _DesktopSendFeeFormState extends ConsumerState<DesktopSendFeeForm> {

@override
Widget build(BuildContext context) {
// Keep the send amount alive for the fee estimates.
ref.listen(sendAmountProvider, (_, __) {});
final isCustomFee = ref.watch(feeRateTypeDesktopStateProvider).isCustom;
final locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale),
Expand Down Expand Up @@ -174,12 +178,15 @@ class _DesktopSendFeeFormState extends ConsumerState<DesktopSendFeeForm> {
padding: const EdgeInsets.all(10),
child: (feeSelectionResult?.$2 == null)
? FutureBuilder(
future: ref.watch(
pWallets.select(
(value) => value.getWallet(widget.walletId).fees,
),
),
future: _fees ??= ref
.read(pWallets)
.getWallet(widget.walletId)
.fees,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasError) {
_fees = null;
}
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return DesktopFeeItem(
Expand Down
164 changes: 164 additions & 0 deletions test/pages_desktop_specific/wallet/desktop_send_fee_form_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
import 'package:stackwallet/models/isar/stack_theme.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send_fee_form.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/providers/wallet/desktop_fee_providers.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
import 'package:stackwallet/utilities/amount/amount_unit.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'package:stackwallet/wallets/isar/models/wallet_info.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/widgets/desktop/desktop_fee_dialog.dart';

import '../../sample_data/theme_json.dart';
import '../../wallets/support/xelis_test_fakes.dart';

const _walletId = "wallet id";
final _coin = Bitcoin(CryptoCurrencyNetwork.main);
final _info = WalletInfo(
walletId: _walletId,
name: "Bitcoin wallet",
mainAddressType: AddressType.p2wpkh,
coinName: _coin.identifier,
);

/// Bitcoin wallet counting its fee rate fetches, the first [failingFetches]
/// of which fail.
class _FeeWallet implements Wallet<Bitcoin> {
_FeeWallet({this.failingFetches = 0});

final int failingFetches;
var feeFetches = 0;

@override
WalletInfo get info => _info;

@override
Future<FeeObject> get fees async {
feeFetches++;
if (feeFetches <= failingFetches) throw Exception("node unreachable");
return FeeObject(
numberOfBlocksFast: 1,
numberOfBlocksAverage: 5,
numberOfBlocksSlow: 20,
fast: BigInt.from(20000),
medium: BigInt.from(10000),
slow: BigInt.from(5000),
);
}

@override
Future<Amount> estimateFeeFor(Amount amount, BigInt feeRate) async =>
Amount(rawValue: BigInt.from(1410), fractionDigits: 8);

@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

/// Pump the fee form for [wallet] and return its parent's setState.
Future<StateSetter> _pumpForm(WidgetTester tester, _FeeWallet wallet) async {
final theme = StackTheme.fromJson(json: lightThemeJsonMap);
late StateSetter rebuild;
await tester.pumpWidget(
ProviderScope(
overrides: [
pWallets.overrideWithValue(ConfirmationWallets(wallet)),
pWalletCoin(_walletId).overrideWithValue(_coin),
pAmountFormatter(_coin).overrideWithValue(
AmountFormatter(
unit: AmountUnit.normal,
locale: "en_US",
coin: _coin,
maxDecimals: 8,
),
),
],
child: MaterialApp(
theme: ThemeData(extensions: [StackColors.fromStackColorTheme(theme)]),
home: Material(
child: StatefulBuilder(
builder: (context, setState) {
rebuild = setState;
return DesktopSendFeeForm(
walletId: _walletId,
isToken: false,
onCustomFeeSliderChanged: (_) {},
onCustomFeeOptionChanged: () {},
);
},
),
),
),
),
);
await tester.pump();
return rebuild;
}

void main() {
testWidgets("a rebuild keeps the fee rates and the amount", (tester) async {
final wallet = _FeeWallet();
final rebuild = await _pumpForm(tester, wallet);
final container = ProviderScope.containerOf(
tester.element(find.byType(DesktopSendFeeForm)),
);
final amount = Amount(rawValue: BigInt.from(22000), fractionDigits: 8);
container.read(sendAmountProvider.notifier).state = amount;
await tester.pump();

rebuild(() {});
await tester.pump();
await tester.pump();

expect(wallet.feeFetches, 1);
expect(container.read(sendAmountProvider), amount);
});

testWidgets("a rebuild fetches the fee rates again after a failure", (
tester,
) async {
final wallet = _FeeWallet(failingFetches: 1);
final rebuild = await _pumpForm(tester, wallet);
expect(find.byType(DesktopFeeItem), findsNothing);

rebuild(() {});
await tester.pump();
await tester.pump();

expect(wallet.feeFetches, 2);
expect(find.byType(DesktopFeeItem), findsOneWidget);

rebuild(() {});
await tester.pump();

expect(wallet.feeFetches, 2);
expect(find.byType(DesktopFeeItem), findsOneWidget);
});

testWidgets("the amount is kept while no fee estimate is shown", (
tester,
) async {
final wallet = _FeeWallet(failingFetches: 1);
final rebuild = await _pumpForm(tester, wallet);
final container = ProviderScope.containerOf(
tester.element(find.byType(DesktopSendFeeForm)),
);
final amount = Amount(rawValue: BigInt.from(22000), fractionDigits: 8);
container.read(sendAmountProvider.notifier).state = amount;
await tester.pump();

rebuild(() {});
await tester.pump();
await tester.pump();

expect(find.byType(DesktopFeeItem), findsOneWidget);
expect(container.read(sendAmountProvider), amount);
});
}
Loading