import 'dart:async'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/send/send_page.dart'; import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import '../components/common_test_cases.dart'; import '../components/common_test_constants.dart'; import 'auth_page_robot.dart'; class SendPageRobot { SendPageRobot({required this.tester}) : commonTestCases = CommonTestCases(tester), authPageRobot = AuthPageRobot(tester); WidgetTester tester; CommonTestCases commonTestCases; AuthPageRobot authPageRobot; Future isSendPage() async { await commonTestCases.isSpecificPage(); } void hasTitle() { commonTestCases.hasText(S.current.send); } void confirmViewComponentsDisplayProperly() { SendPage sendPage = tester.widget(find.byType(SendPage)); final sendViewModel = sendPage.sendViewModel; commonTestCases.hasValueKey('send_page_address_textfield_key'); commonTestCases.hasValueKey('send_page_note_textfield_key'); commonTestCases.hasValueKey('send_page_amount_textfield_key'); commonTestCases.hasValueKey('send_page_add_template_button_key'); if (sendViewModel.hasMultipleTokens) { commonTestCases.hasValueKey('send_page_currency_picker_button_key'); } if (!sendViewModel.isBatchSending) { commonTestCases.hasValueKey('send_page_send_all_button_key'); } if (!sendViewModel.isFiatDisabled) { commonTestCases.hasValueKey('send_page_fiat_amount_textfield_key'); } if (sendViewModel.hasFees) { commonTestCases.hasValueKey('send_page_select_fee_priority_button_key'); } if (sendViewModel.hasCoinControl) { commonTestCases.hasValueKey('send_page_unspent_coin_button_key'); } if (sendViewModel.hasCurrecyChanger) { commonTestCases.hasValueKey('send_page_change_asset_button_key'); } if (sendViewModel.sendTemplateViewModel.hasMultiRecipient) { commonTestCases.hasValueKey('send_page_add_receiver_button_key'); } } Future selectReceiveCurrency(CryptoCurrency receiveCurrency) async { final currencyPickerKey = 'send_page_currency_picker_button_key'; final currencyPickerDialogKey = 'send_page_currency_picker_dialog_button_key'; await commonTestCases.tapItemByKey(currencyPickerKey); commonTestCases.hasValueKey(currencyPickerDialogKey); SendPage sendPage = tester.widget(find.byType(SendPage)); final sendViewModel = sendPage.sendViewModel; if (receiveCurrency == sendViewModel.selectedCryptoCurrency) { await commonTestCases .tapItemByKey('picker_items_index_${receiveCurrency.name}_selected_item_button_key'); return; } await commonTestCases.dragUntilVisible( 'picker_items_index_${receiveCurrency.name}_button_key', 'picker_scrollbar_key', ); await commonTestCases.defaultSleepTime(); await commonTestCases.tapItemByKey('picker_items_index_${receiveCurrency.name}_button_key'); } Future enterReceiveAddress(String receiveAddress) async { await commonTestCases.enterText(receiveAddress, 'send_page_address_textfield_key'); await commonTestCases.defaultSleepTime(); } Future enterAmount(String amount) async { await commonTestCases.enterText(amount, 'send_page_amount_textfield_key'); } Future selectTransactionPriority({TransactionPriority? priority}) async { SendPage sendPage = tester.widget(find.byType(SendPage)); final sendViewModel = sendPage.sendViewModel; if (!sendViewModel.hasFees || priority == null) return; final transactionPriorityPickerKey = 'send_page_select_fee_priority_button_key'; await commonTestCases.tapItemByKey(transactionPriorityPickerKey); if (priority == sendViewModel.transactionPriority) { await commonTestCases .tapItemByKey('picker_items_index_${priority.title}_selected_item_button_key'); return; } await commonTestCases.dragUntilVisible( 'picker_items_index_${priority.title}_button_key', 'picker_scrollbar_key', ); await commonTestCases.defaultSleepTime(); await commonTestCases.tapItemByKey('picker_items_index_${priority.title}_button_key'); } Future onSendButtonPressed() async { tester.printToConsole('Pressing send'); await commonTestCases.tapItemByKey( 'send_page_send_button_key', shouldPumpAndSettle: false, ); await _waitForSendTransactionCompletion(); await commonTestCases.defaultSleepTime(); } Future _waitForSendTransactionCompletion() async { await tester.pump(); final Completer completer = Completer(); // Loop to wait for the async operation to complete while (true) { await Future.delayed(Duration(seconds: 1)); tester.printToConsole('Before _handleAuth'); await _handleAuthPage(); tester.printToConsole('After _handleAuth'); await tester.pump(); final sendPage = tester.widget(find.byType(SendPage)); final state = sendPage.sendViewModel.state; await tester.pump(); bool isDone = state is ExecutedSuccessfullyState; bool isFailed = state is FailureState; tester.printToConsole('isDone: $isDone'); tester.printToConsole('isFailed: $isFailed'); if (isDone || isFailed) { tester.printToConsole( isDone ? 'Completer is done' : 'Completer is done though operation failed', ); completer.complete(); await tester.pump(); break; } else { tester.printToConsole('Completer is not done'); await tester.pump(); } } await expectLater(completer.future, completes); tester.printToConsole('Done confirming sending operation'); } Future _handleAuthPage() async { tester.printToConsole('Inside _handleAuth'); await tester.pump(); tester.printToConsole('starting auth checks'); final authPage = authPageRobot.onAuthPage(); tester.printToConsole('hasAuth:$authPage'); if (authPage) { await tester.pump(); tester.printToConsole('Starting inner _handleAuth loop checks'); try { await authPageRobot.enterPinCode(CommonTestConstants.pin, pumpDuration: 500); tester.printToConsole('Auth done'); await tester.pump(); tester.printToConsole('Auth pump done'); } catch (e) { tester.printToConsole('Auth failed, retrying'); await tester.pump(); _handleAuthPage(); } } } Future handleSendResult() async { await tester.pump(); tester.printToConsole('Inside handle function'); bool hasError = false; hasError = await hasErrorWhileSending(); tester.printToConsole('Has an Error in the handle: $hasError'); int maxRetries = 20; int retries = 0; while (hasError && retries < maxRetries) { tester.printToConsole('hasErrorInLoop: $hasError'); await tester.pump(); await onSendFailureDialogButtonPressed(); tester.printToConsole('Failure button tapped'); await commonTestCases.defaultSleepTime(); await onSendButtonPressed(); tester.printToConsole('Send button tapped'); hasError = await hasErrorWhileSending(); retries++; } if (!hasError) { tester.printToConsole('No error, proceeding with flow'); await tester.pump(); } await commonTestCases.defaultSleepTime(); } //* ------ On Sending Failure ------------ Future hasErrorWhileSending() async { await tester.pump(); tester.printToConsole('Checking if there is an error'); final errorDialog = find.byKey(ValueKey('send_page_send_failure_dialog_button_key')); bool hasError = errorDialog.tryEvaluate(); tester.printToConsole('Has error: $hasError'); return hasError; } Future onSendFailureDialogButtonPressed() async { await commonTestCases.defaultSleepTime(); tester.printToConsole('Send Button Failure Dialog Triggered'); await commonTestCases.tapItemByKey('send_page_send_failure_dialog_button_key'); } //* ------ On Sending Success ------------ Future onSendButtonOnConfirmSendingDialogPressed() async { tester.printToConsole('Inside confirm sending dialog: For sending'); await commonTestCases.defaultSleepTime(); await tester.pump(); final sendText = find.text(S.current.send).last; bool hasText = sendText.tryEvaluate(); tester.printToConsole('Has Text: $hasText'); if (hasText) { await commonTestCases.tapItemByFinder(sendText, shouldPumpAndSettle: false); // Loop to wait for the operation to commit transaction await _waitForCommitTransactionCompletion(); await tester.pump(); await commonTestCases.defaultSleepTime(seconds: 4); } else { await commonTestCases.defaultSleepTime(); await tester.pump(); onSendButtonOnConfirmSendingDialogPressed(); } } Future _waitForCommitTransactionCompletion() async { final Completer completer = Completer(); while (true) { await Future.delayed(Duration(seconds: 1)); final sendPage = tester.widget(find.byType(SendPage)); final state = sendPage.sendViewModel.state; bool isDone = state is TransactionCommitted; bool isFailed = state is FailureState; tester.printToConsole('isDone: $isDone'); tester.printToConsole('isFailed: $isFailed'); if (isDone || isFailed) { tester.printToConsole( isDone ? 'Completer is done' : 'Completer is done though operation failed', ); completer.complete(); await tester.pump(); break; } else { tester.printToConsole('Completer is not done'); await tester.pump(); } } await expectLater(completer.future, completes); tester.printToConsole('Done Committing Transaction'); } Future onCancelButtonOnConfirmSendingDialogPressed() async { tester.printToConsole('Inside confirm sending dialog: For canceling'); await commonTestCases.defaultSleepTime(seconds: 4); final cancelText = find.text(S.current.cancel); bool hasText = cancelText.tryEvaluate(); if (hasText) { await commonTestCases.tapItemByFinder(cancelText); await commonTestCases.defaultSleepTime(seconds: 4); } } //* ---- Add Contact Dialog On Send Successful Dialog ----- Future onSentDialogPopUp() async { SendPage sendPage = tester.widget(find.byType(SendPage)); final sendViewModel = sendPage.sendViewModel; final newContactAddress = sendPage.newContactAddress ?? sendViewModel.newContactAddress(); if (newContactAddress != null) { await _onAddContactButtonOnSentDialogPressed(); } await commonTestCases.defaultSleepTime(); } Future _onAddContactButtonOnSentDialogPressed() async { await commonTestCases.tapItemByKey('send_page_sent_dialog_add_contact_button_key'); } // ignore: unused_element Future _onIgnoreButtonOnSentDialogPressed() async { await commonTestCases.tapItemByKey('send_page_sent_dialog_ignore_button_key'); } }