From 4463f8d1d427880d31d0764c4499a9934c591a00 Mon Sep 17 00:00:00 2001 From: Ethan Lee <125412902+ethan-tbd@users.noreply.github.com> Date: Tue, 14 May 2024 09:17:11 -0700 Subject: [PATCH] feat: get transactions from pfi (#152) --- lib/features/home/home_page.dart | 89 ++++++++++---- lib/features/home/transaction.dart | 113 ++++++++++-------- .../home/transaction_details_page.dart | 4 +- lib/features/tbdex/quote_notifier.dart | 25 +--- lib/features/tbdex/tbdex_exceptions.dart | 8 +- lib/features/tbdex/tbdex_providers.dart | 44 +++++++ lib/features/tbdex/transactions_notifier.dart | 31 +++++ lib/l10n/app_en.arb | 3 +- lib/l10n/app_localizations.dart | 6 + lib/l10n/app_localizations_en.dart | 3 + test/features/app/app_tabs_test.dart | 62 ++++++---- test/features/home/home_page_test.dart | 56 +++++++-- .../home/transaction_details_page_test.dart | 6 + .../payment/review_payment_page_test.dart | 11 +- test/helpers/mocks.dart | 16 +++ test/shared/success_page_test.dart | 13 -- 16 files changed, 340 insertions(+), 150 deletions(-) create mode 100644 lib/features/tbdex/transactions_notifier.dart diff --git a/lib/features/home/home_page.dart b/lib/features/home/home_page.dart index 8c6bb54e..80ebc49e 100644 --- a/lib/features/home/home_page.dart +++ b/lib/features/home/home_page.dart @@ -4,11 +4,15 @@ import 'package:didpay/features/home/transaction_details_page.dart'; import 'package:didpay/features/payin/deposit_page.dart'; import 'package:didpay/features/payout/withdraw_page.dart'; import 'package:didpay/features/tbdex/rfq_state.dart'; +import 'package:didpay/features/tbdex/tbdex_providers.dart'; +import 'package:didpay/features/tbdex/transactions_notifier.dart'; import 'package:didpay/l10n/app_localizations.dart'; import 'package:didpay/shared/theme/grid.dart'; import 'package:didpay/shared/utils/currency_util.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:tbdex/tbdex.dart'; class HomePage extends HookConsumerWidget { const HomePage({super.key}); @@ -19,6 +23,17 @@ class HomePage extends HookConsumerWidget { // TODO(ethan-tbd): get balance from pfi, https://github.com/TBD54566975/didpay/issues/109 final accountBalance = CurrencyUtil.formatFromDouble(0); + TransactionsAsyncNotifier getTransactionsNotifier() => + ref.read(transactionsProvider.notifier); + + useEffect( + () { + Future.delayed(Duration.zero, () => getTransactionsNotifier().fetch()); + return null; + }, + [], + ); + return Scaffold( body: SafeArea( child: Column( @@ -26,7 +41,7 @@ class HomePage extends HookConsumerWidget { children: [ _buildAccountBalance(context, accountBalance), Expanded( - child: _buildActivityList(context, txns), + child: _buildActivity(context, getTransactionsNotifier(), txns), ), ], ), @@ -126,7 +141,11 @@ class HomePage extends HookConsumerWidget { ), ); - Widget _buildActivityList(BuildContext context, List txns) => + Widget _buildActivity( + BuildContext context, + TransactionsAsyncNotifier notifier, + AsyncValue?> exchangesStatus, + ) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -141,18 +160,25 @@ class HomePage extends HookConsumerWidget { ), ), Expanded( - child: txns.isEmpty - ? _buildEmptyState(context) - : _buildTransactionsList(context, txns), + child: exchangesStatus.when( + data: (exchange) => exchange == null || exchange.isEmpty + ? _buildEmptyState(context) + : RefreshIndicator( + onRefresh: () async => notifier.fetch(), + child: _buildTransactionsList(context, exchange), + ), + error: (error, stackTrace) => _buildErrorState(context), + loading: () => const Center(child: CircularProgressIndicator()), + ), ), ], ); + // TODO(ethan-tbd): update empty state, https://github.com/TBD54566975/didpay/issues/125 Widget _buildEmptyState(BuildContext context) => Center( child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox(height: Grid.xxs), + const SizedBox(height: Grid.xs), Text( Loc.of(context).noTransactionsYet, style: Theme.of(context).textTheme.titleMedium?.copyWith( @@ -176,33 +202,53 @@ class HomePage extends HookConsumerWidget { ), ); - Widget _buildTransactionsList(BuildContext context, List txns) => + // TODO(ethan-tbd): update error state, https://github.com/TBD54566975/didpay/issues/125 + Widget _buildErrorState(BuildContext context) => Center( + child: Column( + children: [ + const SizedBox(height: Grid.xs), + Text( + Loc.of(context).unableToRetrieveTxns, + style: Theme.of(context).textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: Grid.xxs), + ], + ), + ); + + Widget _buildTransactionsList( + BuildContext context, + List exchanges, + ) => ListView( - children: txns.map((txn) { + children: exchanges.map((exchange) { + final transaction = Transaction.fromExchange(exchange); final payoutAmount = CurrencyUtil.formatFromDouble( - txn.payoutAmount, - currency: txn.payoutCurrency.toUpperCase(), + transaction.payoutAmount, + currency: transaction.payoutCurrency.toUpperCase(), ); final payinAmount = CurrencyUtil.formatFromDouble( - txn.payinAmount, - currency: txn.payinCurrency.toUpperCase(), + transaction.payinAmount, + currency: transaction.payinCurrency.toUpperCase(), ); return ListTile( title: Text( - '${txn.type}', + '${transaction.type}', style: Theme.of(context).textTheme.titleSmall?.copyWith( fontWeight: FontWeight.bold, ), ), subtitle: Text( - '${txn.status}', + _getSubtitle(transaction), style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.w300, ), ), trailing: Text( - '${txn.type == TransactionType.deposit ? payoutAmount : payinAmount} USDC', + '${transaction.type == TransactionType.deposit ? payoutAmount : payinAmount} USDC', ), leading: Container( width: Grid.md, @@ -212,16 +258,14 @@ class HomePage extends HookConsumerWidget { borderRadius: BorderRadius.circular(Grid.xxs), ), child: Center( - child: txn.type == TransactionType.deposit - ? const Icon(Icons.south_west) - : const Icon(Icons.north_east), + child: Transaction.getIcon(transaction.type), ), ), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) { return TransactionDetailsPage( - txn: txn, + txn: transaction, ); }, ), @@ -229,4 +273,9 @@ class HomePage extends HookConsumerWidget { ); }).toList(), ); + + String _getSubtitle(Transaction transaction) => + transaction.type == TransactionType.deposit + ? '${transaction.payinCurrency} → account balance' + : 'Account balance → ${transaction.payoutCurrency}'; } diff --git a/lib/features/home/transaction.dart b/lib/features/home/transaction.dart index e68d0868..21a41f89 100644 --- a/lib/features/home/transaction.dart +++ b/lib/features/home/transaction.dart @@ -1,11 +1,13 @@ -import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:didpay/shared/theme/grid.dart'; +import 'package:flutter/material.dart'; +import 'package:tbdex/tbdex.dart'; -// TODO(ethan-tbd): remove this file, https://github.com/TBD54566975/didpay/issues/136 class Transaction { final double payinAmount; final double payoutAmount; final String payinCurrency; final String payoutCurrency; + final DateTime createdAt; final TransactionStatus status; final TransactionType type; @@ -14,57 +16,72 @@ class Transaction { required this.payoutAmount, required this.payinCurrency, required this.payoutCurrency, + required this.createdAt, required this.status, required this.type, }); -} -final _defaultList = [ - Transaction( - payinAmount: 25, - payoutAmount: 1.47, - payinCurrency: 'MXN', - payoutCurrency: 'USDC', - status: TransactionStatus.pending, - type: TransactionType.deposit, - ), - Transaction( - payinAmount: 1, - payoutAmount: 17, - payinCurrency: 'USDC', - payoutCurrency: 'MXN', - status: TransactionStatus.pending, - type: TransactionType.withdraw, - ), - Transaction( - payinAmount: 0.00085, - payoutAmount: 35.42, - payinCurrency: 'BTC', - payoutCurrency: 'USDC', - status: TransactionStatus.completed, - type: TransactionType.deposit, - ), - Transaction( - payinAmount: 33, - payoutAmount: 0.000792, - payinCurrency: 'USDC', - payoutCurrency: 'BTC', - status: TransactionStatus.completed, - type: TransactionType.withdraw, - ), - Transaction( - payinAmount: 1, - payoutAmount: 1, - payinCurrency: 'USDC', - payoutCurrency: 'USD', - status: TransactionStatus.failed, - type: TransactionType.withdraw, - ), -]; + factory Transaction.fromExchange(Exchange exchange) { + var payinAmount = '0'; + var payoutAmount = '0'; + var payinCurrency = 'USD'; + var payoutCurrency = 'MXN'; + var latestCreatedAt = DateTime.fromMillisecondsSinceEpoch(0); + var status = TransactionStatus.pending; + var type = TransactionType.send; + + for (final msg in exchange) { + switch (msg.metadata.kind) { + case MessageKind.rfq: + payinAmount = (msg as Rfq).data.payin.amount; + break; + case MessageKind.quote: + payinAmount = (msg as Quote).data.payin.amount; + payoutAmount = msg.data.payout.amount; + payinCurrency = msg.data.payin.currencyCode; + payoutCurrency = msg.data.payout.currencyCode; + break; + case MessageKind.order: + status = TransactionStatus.completed; + break; + // TODO(ethan-tbd): add additional order statuses + case MessageKind.orderstatus: + status = TransactionStatus.completed; + break; + case MessageKind.close: + status = TransactionStatus.failed; + break; + } + } + + type = (payinCurrency == 'STORED_BALANCE') + ? TransactionType.withdraw + : (payoutCurrency == 'STORED_BALANCE') + ? TransactionType.deposit + : type; -final transactionsProvider = StateProvider>((ref) { - return _defaultList; -}); + return Transaction( + payinAmount: double.parse(payinAmount), + payoutAmount: double.parse(payoutAmount), + payinCurrency: payinCurrency, + payoutCurrency: payoutCurrency, + createdAt: latestCreatedAt, + status: status, + type: type, + ); + } + + static Icon getIcon(TransactionType type, {double size = Grid.sm}) { + switch (type) { + case TransactionType.deposit: + return Icon(Icons.south_west, size: size); + case TransactionType.withdraw: + return Icon(Icons.north_east, size: size); + case TransactionType.send: + return Icon(Icons.attach_money, size: size); + } + } +} enum TransactionStatus { failed, diff --git a/lib/features/home/transaction_details_page.dart b/lib/features/home/transaction_details_page.dart index bf80e213..5917022e 100644 --- a/lib/features/home/transaction_details_page.dart +++ b/lib/features/home/transaction_details_page.dart @@ -54,9 +54,7 @@ class TransactionDetailsPage extends HookWidget { const SizedBox(height: Grid.xs), ExcludeSemantics( child: Center( - child: txn.type == TransactionType.deposit - ? const Icon(Icons.south_west, size: Grid.lg) - : const Icon(Icons.north_east, size: Grid.lg), + child: Transaction.getIcon(txn.type, size: Grid.lg), ), ), const SizedBox(height: Grid.xxs), diff --git a/lib/features/tbdex/quote_notifier.dart b/lib/features/tbdex/quote_notifier.dart index dc1fce81..5ca75eb6 100644 --- a/lib/features/tbdex/quote_notifier.dart +++ b/lib/features/tbdex/quote_notifier.dart @@ -1,10 +1,7 @@ import 'dart:async'; import 'dart:math'; -import 'package:didpay/config/config.dart'; -import 'package:didpay/features/account/account_providers.dart'; -import 'package:didpay/features/tbdex/tbdex_exceptions.dart'; -import 'package:didpay/shared/http_status.dart'; +import 'package:didpay/features/tbdex/tbdex_providers.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:tbdex/tbdex.dart'; @@ -43,7 +40,7 @@ class QuoteAsyncNotifier extends AutoDisposeAsyncNotifier { _timer = Timer.periodic(_currentInterval, (_) async { try { - final exchange = await _fetchExchange(exchangeId); + final exchange = await ref.read(exchangeProvider(exchangeId).future); if (_containsQuote(exchange)) { state = AsyncValue.data(_getQuote(exchange)); stopPolling(); @@ -76,24 +73,6 @@ class QuoteAsyncNotifier extends AutoDisposeAsyncNotifier { _currentInterval = _backoffIntervals.first; } - Future _fetchExchange(String exchangeId) async { - final did = ref.read(didProvider); - final country = ref.read(countryProvider); - final pfi = Config.getPfi(country); - - final response = await TbdexHttpClient.getExchange( - did, - pfi?.didUri ?? '', - exchangeId, - ); - - if (response.statusCode.category != HttpStatus.success) { - throw QuoteException('failed to retrieve quote', response.statusCode); - } - - return response.data ?? []; - } - bool _containsQuote(Exchange exchange) => exchange.any((message) => message.metadata.kind == MessageKind.quote); diff --git a/lib/features/tbdex/tbdex_exceptions.dart b/lib/features/tbdex/tbdex_exceptions.dart index 29e9318c..8c0ec007 100644 --- a/lib/features/tbdex/tbdex_exceptions.dart +++ b/lib/features/tbdex/tbdex_exceptions.dart @@ -21,6 +21,10 @@ class OfferingException extends TbdexException { OfferingException(super.message, super.errorCode); } -class QuoteException extends TbdexException { - QuoteException(super.message, super.errorCode); +class ExchangeException extends TbdexException { + ExchangeException(super.message, super.errorCode); +} + +class ExchangesException extends TbdexException { + ExchangesException(super.message, super.errorCode); } diff --git a/lib/features/tbdex/tbdex_providers.dart b/lib/features/tbdex/tbdex_providers.dart index 4769b870..343cff2e 100644 --- a/lib/features/tbdex/tbdex_providers.dart +++ b/lib/features/tbdex/tbdex_providers.dart @@ -2,6 +2,7 @@ import 'package:didpay/config/config.dart'; import 'package:didpay/features/account/account_providers.dart'; import 'package:didpay/features/tbdex/quote_notifier.dart'; import 'package:didpay/features/tbdex/tbdex_exceptions.dart'; +import 'package:didpay/features/tbdex/transactions_notifier.dart'; import 'package:didpay/shared/http_status.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:tbdex/tbdex.dart'; @@ -34,7 +35,50 @@ final rfqProvider = } }); +final exchangeProvider = FutureProvider.family + .autoDispose((ref, exchangeId) async { + final did = ref.read(didProvider); + final country = ref.read(countryProvider); + final pfi = Config.getPfi(country); + + final response = await TbdexHttpClient.getExchange( + did, + pfi?.didUri ?? '', + exchangeId, + ); + + return response.statusCode.category == HttpStatus.success + ? response.data! + : throw ExchangeException( + 'failed to fetch exchange', + response.statusCode, + ); +}); + +final exchangesProvider = FutureProvider.autoDispose>((ref) async { + final did = ref.read(didProvider); + final country = ref.read(countryProvider); + final pfi = Config.getPfi(country); + + final response = await TbdexHttpClient.listExchanges( + did, + pfi?.didUri ?? '', + ); + + return response.statusCode.category == HttpStatus.success + ? response.data! + : throw ExchangesException( + 'failed to fetch exchanges', + response.statusCode, + ); +}); + final quoteProvider = AsyncNotifierProvider.autoDispose( QuoteAsyncNotifier.new, ); + +final transactionsProvider = AsyncNotifierProvider.autoDispose< + TransactionsAsyncNotifier, List?>( + TransactionsAsyncNotifier.new, +); diff --git a/lib/features/tbdex/transactions_notifier.dart b/lib/features/tbdex/transactions_notifier.dart new file mode 100644 index 00000000..98350e9c --- /dev/null +++ b/lib/features/tbdex/transactions_notifier.dart @@ -0,0 +1,31 @@ +import 'dart:async'; + +import 'package:didpay/features/tbdex/tbdex_providers.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:tbdex/tbdex.dart'; + +// TODO(ethan-tbd): add polling to fetch transactions, https://github.com/TBD54566975/didpay/issues/136 +class TransactionsAsyncNotifier + extends AutoDisposeAsyncNotifier?> { + @override + FutureOr?> build() => null; + + Future fetch() async { + state = const AsyncValue.loading(); + + try { + final exchangeIds = await ref.read(exchangesProvider.future); + final exchanges = []; + for (final exchangeId in exchangeIds) { + final exchange = await ref.read(exchangeProvider(exchangeId).future); + exchanges.add(exchange); + } + state = AsyncValue.data(exchanges); + } on Exception catch (e) { + state = AsyncValue.error( + e, + StackTrace.current, + ); + } + } +} diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 68e00bd4..2cbfe1ea 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -116,5 +116,6 @@ "totalToRecipient": "Total to recipient", "pay": "Pay", "errorFound": "Error found", - "tapToRetry": "Tap to retry" + "tapToRetry": "Tap to retry", + "unableToRetrieveTxns": "Unable to retrieve transactions" } \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 22c732ef..7f9f97b6 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -612,6 +612,12 @@ abstract class Loc { /// In en, this message translates to: /// **'Tap to retry'** String get tapToRetry; + + /// No description provided for @unableToRetrieveTxns. + /// + /// In en, this message translates to: + /// **'Unable to retrieve transactions'** + String get unableToRetrieveTxns; } class _LocDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index e3eccade..1b9195ff 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -272,4 +272,7 @@ class LocEn extends Loc { @override String get tapToRetry => 'Tap to retry'; + + @override + String get unableToRetrieveTxns => 'Unable to retrieve transactions'; } diff --git a/test/features/app/app_tabs_test.dart b/test/features/app/app_tabs_test.dart index dc4fef5c..b4d9c811 100644 --- a/test/features/app/app_tabs_test.dart +++ b/test/features/app/app_tabs_test.dart @@ -2,36 +2,56 @@ import 'package:didpay/features/account/account_page.dart'; import 'package:didpay/features/app/app_tabs.dart'; import 'package:didpay/features/home/home_page.dart'; import 'package:didpay/features/send/send_page.dart'; +import 'package:didpay/features/tbdex/tbdex_providers.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../../helpers/mocks.dart'; import '../../helpers/widget_helpers.dart'; void main() { - testWidgets('should start on HomePage', (tester) async { - await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const AppTabs()), - ); + group('AppTabs', () { + testWidgets('should start on HomePage', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: const AppTabs(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), + ); + await tester.pumpAndSettle(); - expect(find.byType(HomePage), findsOneWidget); - }); + expect(find.byType(HomePage), findsOneWidget); + }); - testWidgets('should show SendPage when tapped', (tester) async { - await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const AppTabs()), - ); + testWidgets('should show SendPage when tapped', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: const AppTabs(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), + ); - await tester.tap(find.text('Send')); - await tester.pumpAndSettle(); - expect(find.byType(SendPage), findsOneWidget); - }); + await tester.tap(find.text('Send')); + await tester.pumpAndSettle(); + expect(find.byType(SendPage), findsOneWidget); + }); - testWidgets('should show AccountPage when tapped', (tester) async { - await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const AppTabs()), - ); + testWidgets('should show AccountPage when tapped', (tester) async { + await tester.pumpWidget( + WidgetHelpers.testableWidget( + child: const AppTabs(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), + ); - await tester.tap(find.text('Account')); - await tester.pumpAndSettle(); - expect(find.byType(AccountPage), findsOneWidget); + await tester.tap(find.text('Account')); + await tester.pumpAndSettle(); + expect(find.byType(AccountPage), findsOneWidget); + }); }); } diff --git a/test/features/home/home_page_test.dart b/test/features/home/home_page_test.dart index 9a84d6e2..d19c900d 100644 --- a/test/features/home/home_page_test.dart +++ b/test/features/home/home_page_test.dart @@ -1,26 +1,39 @@ import 'package:didpay/features/home/home_page.dart'; -import 'package:didpay/features/home/transaction.dart'; import 'package:didpay/features/payin/deposit_page.dart'; import 'package:didpay/features/payout/withdraw_page.dart'; +import 'package:didpay/features/tbdex/tbdex_providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../../helpers/mocks.dart'; import '../../helpers/widget_helpers.dart'; void main() { group('HomePage', () { testWidgets('should show account balance', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), ); + await tester.pumpAndSettle(); expect(find.text('Account balance'), findsOneWidget); }); testWidgets('should show valid account balance amount', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), ); + await tester.pumpAndSettle(); final numberPattern = RegExp(r'[0-9]+(\.[0-9]{2})?$'); @@ -29,8 +42,14 @@ void main() { testWidgets('should show deposit button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), ); + await tester.pumpAndSettle(); expect(find.widgetWithText(FilledButton, 'Deposit'), findsOneWidget); }); @@ -38,8 +57,14 @@ void main() { testWidgets('should navigate to DepositPage on tap of deposit button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), ); + await tester.pumpAndSettle(); await tester.tap(find.widgetWithText(FilledButton, 'Deposit')); await tester.pumpAndSettle(); @@ -49,8 +74,14 @@ void main() { testWidgets('should show withdraw button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), ); + await tester.pumpAndSettle(); expect(find.widgetWithText(FilledButton, 'Withdraw'), findsOneWidget); }); @@ -58,8 +89,14 @@ void main() { testWidgets('should navigate to WithdrawPage on tap of withdraw button', (tester) async { await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const HomePage()), + WidgetHelpers.testableWidget( + child: const HomePage(), + overrides: [ + transactionsProvider.overrideWith(MockTransactionsNotifier.new), + ], + ), ); + await tester.pumpAndSettle(); await tester.tap(find.widgetWithText(FilledButton, 'Withdraw')); await tester.pumpAndSettle(); @@ -72,10 +109,11 @@ void main() { WidgetHelpers.testableWidget( child: const HomePage(), overrides: [ - transactionsProvider.overrideWith((ref) => []), + transactionsProvider.overrideWith(MockTransactionsNotifier.new), ], ), ); + await tester.pumpAndSettle(); expect(find.text('No transactions yet'), findsOneWidget); expect( @@ -91,7 +129,7 @@ void main() { WidgetHelpers.testableWidget( child: const HomePage(), overrides: [ - transactionsProvider.overrideWith((ref) => []), + transactionsProvider.overrideWith(MockTransactionsNotifier.new), ], ), ); diff --git a/test/features/home/transaction_details_page_test.dart b/test/features/home/transaction_details_page_test.dart index 1d2160b1..c7f1fb65 100644 --- a/test/features/home/transaction_details_page_test.dart +++ b/test/features/home/transaction_details_page_test.dart @@ -16,6 +16,7 @@ void main() { payoutAmount: 0, payinCurrency: 'USDC', payoutCurrency: 'USDC', + createdAt: DateTime.now(), status: TransactionStatus.completed, type: TransactionType.deposit, ), @@ -35,6 +36,7 @@ void main() { payoutAmount: 0, payinCurrency: 'USDC', payoutCurrency: 'USDC', + createdAt: DateTime.now(), status: TransactionStatus.completed, type: TransactionType.withdraw, ), @@ -54,6 +56,7 @@ void main() { payoutAmount: 123, payinCurrency: 'USDC', payoutCurrency: 'MXN', + createdAt: DateTime.now(), status: TransactionStatus.completed, type: TransactionType.deposit, ), @@ -76,6 +79,7 @@ void main() { payoutAmount: 123, payinCurrency: 'USDC', payoutCurrency: 'USDC', + createdAt: DateTime.now(), status: TransactionStatus.pending, type: TransactionType.deposit, ), @@ -95,6 +99,7 @@ void main() { payoutAmount: 123, payinCurrency: 'USDC', payoutCurrency: 'USDC', + createdAt: DateTime.now(), status: TransactionStatus.completed, type: TransactionType.deposit, ), @@ -114,6 +119,7 @@ void main() { payoutAmount: 123, payinCurrency: 'USDC', payoutCurrency: 'USDC', + createdAt: DateTime.now(), status: TransactionStatus.failed, type: TransactionType.deposit, ), diff --git a/test/features/payment/review_payment_page_test.dart b/test/features/payment/review_payment_page_test.dart index 8274f840..25d26d29 100644 --- a/test/features/payment/review_payment_page_test.dart +++ b/test/features/payment/review_payment_page_test.dart @@ -2,8 +2,6 @@ import 'dart:async'; import 'dart:convert'; import 'package:auto_size_text/auto_size_text.dart'; -import 'package:didpay/features/account/account_providers.dart'; -import 'package:didpay/features/countries/country.dart'; import 'package:didpay/features/home/transaction.dart'; import 'package:didpay/features/payment/payment_confirmation_page.dart'; import 'package:didpay/features/payment/payment_state.dart'; @@ -15,13 +13,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:tbdex/tbdex.dart'; -import 'package:web5/web5.dart'; import '../../helpers/widget_helpers.dart'; -void main() async { - final did = await DidDht.create(); - +void main() { Widget reviewPaymentPageTestWidget({List overrides = const []}) => WidgetHelpers.testableWidget( child: const ReviewPaymentPage( @@ -38,10 +33,6 @@ void main() async { ), overrides: [ quoteProvider.overrideWith(_MockQuoteNotifier.new), - didProvider.overrideWithValue(did), - countryProvider.overrideWith( - (ref) => const Country(name: 'Mexico', code: 'MX'), - ), ], ); group('ReviewPaymentPage', () { diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index 90ec363e..8e3abbc0 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -1,7 +1,23 @@ +import 'dart:async'; + +import 'package:didpay/features/tbdex/transactions_notifier.dart'; import 'package:http/http.dart' as http; import 'package:mocktail/mocktail.dart'; +import 'package:tbdex/tbdex.dart'; import 'package:web5/web5.dart'; class MockHttpClient extends Mock implements http.Client {} class MockBearerDid extends Mock implements BearerDid {} + +class MockTransactionsNotifier extends TransactionsAsyncNotifier { + MockTransactionsNotifier() : super(); + + @override + FutureOr?> build() { + return []; + } + + @override + Future fetch() async {} +} diff --git a/test/shared/success_page_test.dart b/test/shared/success_page_test.dart index ed46d030..b689624d 100644 --- a/test/shared/success_page_test.dart +++ b/test/shared/success_page_test.dart @@ -1,4 +1,3 @@ -import 'package:didpay/features/app/app_tabs.dart'; import 'package:didpay/shared/success_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -29,17 +28,5 @@ void main() { expect(find.widgetWithText(FilledButton, 'Done'), findsOneWidget); }); - - testWidgets('should navigate to AppTabs on tap of done button', - (tester) async { - await tester.pumpWidget( - WidgetHelpers.testableWidget(child: const SuccessPage(text: '')), - ); - - await tester.tap(find.widgetWithText(FilledButton, 'Done')); - await tester.pumpAndSettle(); - - expect(find.byType(AppTabs), findsOneWidget); - }); }); }