mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-18 05:51:34 +02:00
* fix: Generic fixes * fix: Modify hasSufficientFundsForRent to work for spl transactions * feat: Integrate seed verification into integration tests * fix: Adjust workflow file * chore: Remove previous working directory * fix: Revert change * chore: Remove step to setup kvm, already existing in new setup * chore: Remove a couple of steps from workflow * chore: Try switching test commands * fix: Switch back to flutter drive * fix: Flutter error when running tests with Flutter drive * feat: Add screenshots to individual test pages and modify integration test script to add a watch dog to prevent infinite running on CI * feat: Implement transaction success info robot and fix issue with send flow test * fix: Improve integration testing for exchange flow and clean up logic and resulting errors * fix: Handle duplicate words for seeds and test restore through seeds * fix: Optimize implementation for transactions history test * Skip seed verification when in debug and when not in CI flow * chore: Remove print statements and add TODOs * fix: Upgrade version for upload action * fix: Add missing secrets to testing workflow file * fix tests not working in latest main [skip slack] # Conflicts: # lib/src/screens/wallet_keys/wallet_keys_page.dart * add zano seed [skip slack] [run tests] * disable exchange_flow_test.dart [run tests] [skip slack] * Run tests on linux always (since nobody ever used [run tests]) * fix tests after ui updates * [skip slack] tests update * Fixing tests WIP * Fix create wallet integraiton test flow * Update and fix Exchange flow test flow * Switch printV to printToConsole * Adjustments to the the flows on Send and Exchange * feat: Modify Exchange and send tests to fit new flow * fix: - Pause screenshots for now, results in a super large json response file - Fix delay on send bottomsheet because of pumpAndSettle animation - Fix issue with integration test runner script * fix(integration-tests): Fix issues relating to send flow integration tests. This change: - Handles the slider during the send confirmation flow during the send integration test - Adds the missing seeds for the restore flow to the relevant workflow files * feat: Enhance Send flow Automation Integration Test Flow This change: - Updates test wallet address in CommonTestConstants. - Modifies startAppFlow to accept a dynamic key. - Improves waitForSendPage and checkIfSendPageIsVisible methods for better page visibility handling. - Adds validateWalletBalance method to ensure sufficient funds before sending. - Enhances transaction success flow handling in SendPageRobot. * refactor: Update SendPageRobot for improved transaction state handling This change: - Adds support for the TransactionCommitted state in the SendPageRobot. - Removes the handleSendResult method to streamline the send flow. - Enhances checks for successful transaction completion by verifying navigation to the DashboardPage. - Improves visibility handling for the contact addition dialog in the transaction success flow. * chore: Update automated integration test workflow This change: - Updates apkl file naming to use sanitized branch names for better consistency. - Adds a step to output system environment information to aid in troubleshooting. - Enhances KVM setup with checks to see if device exists and there is proper permission. - Adjusts working directory references for Android emulator setup. * chore: Enhance automated integration test workflow This change: - Adds cleanup steps for existing emulator processes to ensure fresh environment. - Updates APK file naming to use sanitized branch names for consistency. - Enhances debug output for APK file creation and searching, improving visibility during the workflow. * chore: Modify apk file renaming step in workflow * chore: Enhance automated integration test workflow This change: - Increases timeout for integration tests to 60 minutes. - Adds a timeout of 90 minutes for the automated integration test job. - Improves KVM setup with detailed checks and outputs for existing device, permissions, and functionality. - Adds system resource checks to provide information on memory, disk space, and CPU cores during the workflow. - Implements error handling for the integration test runner to ensure graceful failure reporting. * chore: Enhance AVD configuration and emulator management in integration test workflow * refactor: Improve transaction history flow tests and item visibility checks This change: - Enhances the scrolling logic in the CommonTestCases to check for item visibility before scrolling. - Adds error handling and logging for transaction history checks in TransactionsPageRobot. - Adds comprehensive item checks and scrolling for more reliable tests. * refactor: Modify integration test runner for improved error handling and logging This change: - Implements error handling and logging throughout the integration test runner. - Introduces a cleanup function for data directories and signal handling. - Enhances test execution with retry logic and detailed duration tracking. - Provides a comprehensive summary of test results, including passed and failed tests with their durations. * refactor: Update error handling in transaction history flow tests * refactor: Remove manual emulator startup logic from integration test workflow This change simplifies the automated integration test workflow by removing the manual emulator startup process, which was previously included to handle cases where no emulator was running. The workflow now focuses on checking the emulator status and gathering device information without attempting to start an emulator manually. * refactor: Enhance integration test workflow and error handling * feat: Add emulator readiness checks and enhance send flow tests This change: - Modifies the send flow test to send $1 worth of any of the wallet currencies - Set up switching between test wallet in case there's zero balance in any of the wallets when the test is run - Add a mini script to check emulator readiness for the tests * fix: More CI workflow errors for the tests * refactor: Update emulator readiness checks and error handling in CI workflow * fix: Readiness check successful but broken pipe when accessing settings service, plus some syntax errors in the workflow * refactor: Enhance integration tests with improved visibility checks and timing adjustments This change: - Adds visibility checks for widgets before interaction in various test flows. - Introduces consistent pump and settle durations to ensure UI stability during tests. - Refines scrolling logic to confirm wallet types are visible before selection. - Updates tap actions to include warnings for missed taps, improving test reliability. * fix: Issues with confirm seeds and keys Automated test * fix: Add missing await for tap calls * fix: Temporarily disable CI workflow and minor cleanup to testflows * fix: Add missing keys to CI workflow * chore: Temporarily disable Automation CI * chore: Cleanup --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>
721 lines
25 KiB
Dart
721 lines
25 KiB
Dart
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/src/widgets/base_text_form_field.dart';
|
|
import 'package:cake_wallet/src/widgets/primary_button.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/material.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';
|
|
import 'package:cake_wallet/src/widgets/standard_slide_button_widget.dart';
|
|
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
|
|
|
class SendPageRobot {
|
|
SendPageRobot({required this.tester})
|
|
: commonTestCases = CommonTestCases(tester),
|
|
authPageRobot = AuthPageRobot(tester);
|
|
|
|
WidgetTester tester;
|
|
CommonTestCases commonTestCases;
|
|
AuthPageRobot authPageRobot;
|
|
|
|
Future<void> isSendPage() async {
|
|
await commonTestCases.isSpecificPage<SendPage>();
|
|
await commonTestCases.takeScreenshots('send_page');
|
|
}
|
|
|
|
Future<void> waitForSendPage() async {
|
|
tester.printToConsole('Waiting for SendPage to be available');
|
|
|
|
final stopwatch = Stopwatch()..start();
|
|
final maxDuration = Duration(seconds: 30);
|
|
|
|
while (stopwatch.elapsed < maxDuration) {
|
|
await tester.pump(Duration(milliseconds: 500));
|
|
|
|
// Check if we're still on auth page
|
|
if (authPageRobot.onAuthPage()) {
|
|
tester.printToConsole('Still on auth page, waiting...');
|
|
continue;
|
|
}
|
|
|
|
// Check if SendPage is available
|
|
final sendPageFinder = find.byType(SendPage);
|
|
if (sendPageFinder.tryEvaluate()) {
|
|
tester.printToConsole('SendPage found!');
|
|
return;
|
|
}
|
|
|
|
tester.printToConsole('SendPage not found yet, waiting...');
|
|
}
|
|
|
|
throw Exception('SendPage not found after ${maxDuration.inSeconds} seconds');
|
|
}
|
|
|
|
Future<void> checkIfSendPageIsVisible() async {
|
|
tester.printToConsole('Confirming SendPage is visible');
|
|
|
|
if (authPageRobot.onAuthPage()) {
|
|
tester.printToConsole('Auth page is currently visible');
|
|
await _handleAuthPage();
|
|
await commonTestCases.defaultSleepTime();
|
|
}
|
|
|
|
await waitForSendPage();
|
|
}
|
|
|
|
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.feesViewModel.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<void> 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);
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
// Check if picker dialog is present
|
|
if (!commonTestCases.isKeyPresent(currencyPickerDialogKey)) {
|
|
tester.printToConsole('Currency picker dialog not found, may already be selected');
|
|
return;
|
|
}
|
|
|
|
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.enterText(receiveCurrency.title, 'search_bar_widget_key');
|
|
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
await commonTestCases.tapItemByKey('picker_items_index_${receiveCurrency.fullName}_button_key');
|
|
}
|
|
|
|
Future<void> enterReceiveAddress(String receiveAddress) async {
|
|
await commonTestCases.enterText(receiveAddress, 'send_page_address_textfield_key');
|
|
await commonTestCases.defaultSleepTime();
|
|
}
|
|
|
|
Future<void> enterSendAmount(String amount, {bool isFiat = false}) async {
|
|
await commonTestCases.enterText(
|
|
amount,
|
|
isFiat ? 'send_page_fiat_amount_textfield_key' : 'send_page_amount_textfield_key',
|
|
);
|
|
}
|
|
|
|
String _getTextFromField(ValueKey<String> key) {
|
|
final field = find.byKey(key);
|
|
if (!field.tryEvaluate()) return '';
|
|
final baseTextFormField = tester.widget<BaseTextFormField>(field);
|
|
return baseTextFormField.controller?.text ?? '';
|
|
}
|
|
|
|
/// Validates wallet balance for $1 send by checking against the converted crypto amount
|
|
/// Returns true if wallet has sufficient balance, false otherwise
|
|
Future<bool> validateWalletBalanceForOneDollarSend() async {
|
|
SendPage sendPage = tester.widget(find.byType(SendPage));
|
|
final sendViewModel = sendPage.sendViewModel;
|
|
|
|
final balance = await sendViewModel.sendingBalance;
|
|
|
|
await setupOneDollarSend();
|
|
|
|
// Get the crypto amount that was set for the $1 send
|
|
String cryptoAmount = _getTextFromField(ValueKey('send_page_amount_textfield_key'));
|
|
if (cryptoAmount.isEmpty || cryptoAmount == '0' || cryptoAmount == '0.0') {
|
|
cryptoAmount = '0.001'; // fallback amount
|
|
}
|
|
|
|
final amount = double.tryParse(cryptoAmount) ?? 0.0;
|
|
|
|
tester.printToConsole(
|
|
'Wallet balance: $balance, sending amount for \$${CommonTestConstants.sendTestFiatAmount}: $cryptoAmount',
|
|
);
|
|
|
|
if (balance.isEmpty || double.tryParse(balance) == null) {
|
|
tester.printToConsole('Invalid wallet balance: $balance');
|
|
return false;
|
|
}
|
|
|
|
final balanceValue = double.parse(balance);
|
|
if (balanceValue < amount) {
|
|
tester.printToConsole(
|
|
'Insufficient balance for \$${CommonTestConstants.sendTestFiatAmount} send: $balanceValue < $amount',
|
|
);
|
|
return false;
|
|
}
|
|
|
|
tester.printToConsole(
|
|
'Wallet has sufficient balance for \$${CommonTestConstants.sendTestFiatAmount} send',
|
|
);
|
|
return true;
|
|
}
|
|
|
|
Future<void> selectTransactionPriority({TransactionPriority? priority}) async {
|
|
SendPage sendPage = tester.widget(find.byType(SendPage));
|
|
final sendViewModel = sendPage.sendViewModel;
|
|
|
|
if (!sendViewModel.feesViewModel.hasFees || priority == null) return;
|
|
|
|
final transactionPriorityPickerKey = 'send_page_select_fee_priority_button_key';
|
|
await commonTestCases.tapItemByKey(transactionPriorityPickerKey);
|
|
|
|
if (priority == sendViewModel.feesViewModel.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<void> onSendButtonPressed() async {
|
|
tester.printToConsole('Pressing send');
|
|
|
|
await checkIfSendPageIsVisible();
|
|
|
|
await tester.pumpAndSettle();
|
|
final sendPage = tester.widget<SendPage>(find.byType(SendPage));
|
|
|
|
while (true) {
|
|
bool isReadyForSend = sendPage.sendViewModel.isReadyForSend;
|
|
await tester.pump();
|
|
if (isReadyForSend) {
|
|
tester.printToConsole('Is ready for send');
|
|
break;
|
|
} else {
|
|
await commonTestCases.defaultSleepTime();
|
|
await tester.pumpAndSettle();
|
|
tester.printToConsole('not yet ready for send');
|
|
}
|
|
}
|
|
await commonTestCases.tapItemByKey(
|
|
'send_page_send_button_key',
|
|
shouldPumpAndSettle: false,
|
|
);
|
|
|
|
await _waitForSendTransactionCompletion();
|
|
|
|
await commonTestCases.defaultSleepTime();
|
|
}
|
|
|
|
Future<void> _waitForSendTransactionCompletion() async {
|
|
await tester.pump();
|
|
final Completer<void> completer = Completer<void>();
|
|
|
|
// Loop to wait for the async operation to complete
|
|
while (true) {
|
|
await Future.delayed(Duration(seconds: 1));
|
|
|
|
tester.printToConsole('Before _handleAuth');
|
|
|
|
await _handleAuthPage();
|
|
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
tester.printToConsole('After _handleAuth');
|
|
|
|
await tester.pump();
|
|
|
|
if (authPageRobot.onAuthPage()) {
|
|
tester.printToConsole('Still on auth page, continuing to wait');
|
|
continue;
|
|
}
|
|
|
|
final sendPageFinder = find.byType(SendPage);
|
|
if (!sendPageFinder.tryEvaluate()) {
|
|
tester.printToConsole('SendPage not found yet, continuing to wait');
|
|
continue;
|
|
}
|
|
|
|
final sendPage = tester.widget<SendPage>(sendPageFinder);
|
|
final state = sendPage.sendViewModel.state;
|
|
|
|
await tester.pump();
|
|
|
|
bool isDone = state is ExecutedSuccessfullyState || 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 confirming sending operation');
|
|
}
|
|
|
|
Future<void> _handleAuthPage() async {
|
|
tester.printToConsole('Inside _handleAuth');
|
|
|
|
final onAuthPageDesktop = authPageRobot.onAuthPageDesktop();
|
|
if (onAuthPageDesktop) {
|
|
await authPageRobot.enterPassword(CommonTestConstants.pin.join(""));
|
|
await commonTestCases.defaultSleepTime();
|
|
return;
|
|
}
|
|
|
|
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(Duration(seconds: 3));
|
|
|
|
tester.printToConsole('Auth pump done');
|
|
|
|
await commonTestCases.defaultSleepTime();
|
|
} catch (e) {
|
|
tester.printToConsole('Auth failed, retrying: $e');
|
|
await tester.pump();
|
|
|
|
await commonTestCases.defaultSleepTime(seconds: 1);
|
|
await _handleAuthPage();
|
|
}
|
|
} else {
|
|
tester.printToConsole('No auth page detected, proceeding');
|
|
}
|
|
await tester.pump();
|
|
}
|
|
|
|
//* ------ On Sending Failure ------------
|
|
Future<bool> 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<void> 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<void> onSendSliderOnConfirmSendingBottomSheetDragged() async {
|
|
await commonTestCases.defaultSleepTime();
|
|
await tester.pump();
|
|
|
|
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
|
tester.printToConsole('Found confirm sending bottom sheet, starting slider drag');
|
|
|
|
final accessibleButton = find.byType(PrimaryButton);
|
|
if (accessibleButton.tryEvaluate()) {
|
|
tester.printToConsole('Found accessible navigation button, tapping it');
|
|
await tester.tap(accessibleButton);
|
|
await tester.pumpAndSettle();
|
|
} else {
|
|
await _performSendSliderDrag();
|
|
}
|
|
|
|
tester.printToConsole('Slider/button action completed, waiting for transaction completion');
|
|
|
|
// Check if bottom sheet is dismissed
|
|
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
|
tester.printToConsole('Bottom sheet still present, waiting a bit more for dismissal');
|
|
await commonTestCases.defaultSleepTime(seconds: 3);
|
|
|
|
// If still present, try one more manual dismissal attempt
|
|
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
|
tester.printToConsole('Bottom sheet still present, attempting final manual dismissal');
|
|
await tester.tapAt(Offset(200, 200)); // Tap outside
|
|
await tester.pumpAndSettle();
|
|
}
|
|
}
|
|
|
|
// Wait for transaction completion
|
|
await _waitForCommitTransactionCompletion();
|
|
await commonTestCases.defaultSleepTime(seconds: 2);
|
|
} else {
|
|
tester.printToConsole('Confirm sending bottom sheet not found, waiting and retrying');
|
|
await commonTestCases.defaultSleepTime();
|
|
await tester.pump();
|
|
await onSendSliderOnConfirmSendingBottomSheetDragged();
|
|
}
|
|
}
|
|
|
|
Future<void> _performSendSliderDrag() async {
|
|
final sliderFinder = find.byKey(const ValueKey('standard_slide_button_widget_slider_key'));
|
|
expect(sliderFinder, findsOneWidget, reason: 'Slider should be found');
|
|
|
|
// Find the StandardSlideButton widget to get the main container
|
|
final slideButtonFinder = find.byType(StandardSlideButton);
|
|
expect(slideButtonFinder, findsOneWidget, reason: 'StandardSlideButton should be found');
|
|
|
|
// Get the main container bounds (the outer container, not the slider container)
|
|
final mainContainerRect = tester.getRect(slideButtonFinder);
|
|
final containerWidth = mainContainerRect.width;
|
|
|
|
final sideMargin = 4.0;
|
|
final sliderWidth = 42.0;
|
|
final effectiveMaxWidth = containerWidth - 2 * sideMargin;
|
|
final threshold = effectiveMaxWidth - sliderWidth - 10;
|
|
|
|
// Add a small buffer to ensure we exceed the threshold
|
|
final dragDistance = threshold + 20;
|
|
|
|
tester.printToConsole(
|
|
'Main container width: $containerWidth, Threshold: $threshold, Drag distance: $dragDistance',
|
|
);
|
|
|
|
// Start the drag
|
|
await tester.drag(sliderFinder, Offset(dragDistance, 0));
|
|
|
|
// Wait for the drag to complete and trigger onHorizontalDragEnd
|
|
await tester.pump(Duration(milliseconds: 100));
|
|
|
|
// Release the drag (this should trigger onHorizontalDragEnd)
|
|
await tester.pump(Duration(seconds: 2));
|
|
|
|
tester.printToConsole('Drag completed, waiting for callback');
|
|
|
|
// Wait for the slide completion callback to trigger and bottom sheet to dismiss
|
|
await _waitForBottomSheetDismissal();
|
|
}
|
|
|
|
Future<void> _waitForBottomSheetDismissal() async {
|
|
tester.printToConsole('Waiting for bottom sheet dismissal');
|
|
|
|
final stopwatch = Stopwatch()..start();
|
|
final maxDuration = Duration(seconds: 10);
|
|
|
|
while (stopwatch.elapsed < maxDuration) {
|
|
await tester.pump(Duration(milliseconds: 500));
|
|
|
|
// Check if the confirm bottom sheet is still present
|
|
if (!commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
|
tester.printToConsole('Bottom sheet dismissed successfully!');
|
|
return;
|
|
}
|
|
|
|
// Check if transaction has started (this indicates slider was successful)
|
|
if (_isTransactionStarted()) {
|
|
tester.printToConsole(
|
|
'Transaction started, slider was successful even if bottom sheet is still visible');
|
|
return;
|
|
}
|
|
|
|
tester.printToConsole('Bottom sheet still present, waiting');
|
|
}
|
|
|
|
tester.printToConsole('Bottom sheet dismissal timeout reached, trying manual dismissal');
|
|
|
|
// If the bottom sheet is still present after timeout, try to manually dismiss it
|
|
// This might happen if the slider drag didn't trigger the onSlideComplete properly
|
|
if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
|
tester.printToConsole('Attempting manual bottom sheet dismissal');
|
|
|
|
// Try to find and tap a close button or back button
|
|
final closeButton = find.byIcon(Icons.close);
|
|
if (closeButton.tryEvaluate()) {
|
|
await tester.tap(closeButton);
|
|
await tester.pumpAndSettle();
|
|
tester.printToConsole('Manual close button tapped');
|
|
} else {
|
|
// Try to pop the bottom sheet by tapping outside or using back gesture
|
|
await tester.tapAt(Offset(100, 100)); // Tap outside the bottom sheet
|
|
await tester.pumpAndSettle();
|
|
tester.printToConsole('Tapped outside bottom sheet');
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _isTransactionStarted() {
|
|
try {
|
|
// Check if SendPage is available and transaction state has changed
|
|
final sendPageFinder = find.byType(SendPage);
|
|
if (!sendPageFinder.tryEvaluate()) {
|
|
return false;
|
|
}
|
|
|
|
final sendPage = tester.widget<SendPage>(sendPageFinder);
|
|
final state = sendPage.sendViewModel.state;
|
|
|
|
// Check if we're in a transaction-related state
|
|
return state is TransactionCommitting ||
|
|
state is IsExecutingState ||
|
|
state is TransactionCommitted ||
|
|
state is ExecutedSuccessfullyState;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<void> _waitForCommitTransactionCompletion() async {
|
|
tester.printToConsole('Starting to wait for transaction completion');
|
|
|
|
final stopwatch = Stopwatch()..start();
|
|
final maxDuration = Duration(seconds: 60);
|
|
|
|
while (stopwatch.elapsed < maxDuration) {
|
|
await Future.delayed(Duration(seconds: 1));
|
|
|
|
// Check if the confirm bottom sheet is gone (transaction started)
|
|
if (!commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
|
|
tester.printToConsole('Confirm bottom sheet disappeared, transaction may be processing');
|
|
}
|
|
|
|
// Check if we've navigated back to the dashboard (for cases of successful transaction)
|
|
final dashboardFinder = find.byType(DashboardPage);
|
|
if (dashboardFinder.tryEvaluate()) {
|
|
tester.printToConsole('Dashboard detected, transaction completed successfully!');
|
|
await tester.pump();
|
|
return;
|
|
}
|
|
|
|
// Check if SendPage is still available (transaction still processing)
|
|
final sendPageFinder = find.byType(SendPage);
|
|
if (sendPageFinder.tryEvaluate()) {
|
|
final sendPage = tester.widget<SendPage>(sendPageFinder);
|
|
final state = sendPage.sendViewModel.state;
|
|
|
|
bool isDone = state is ExecutedSuccessfullyState || state is TransactionCommitted;
|
|
bool isFailed = state is FailureState;
|
|
|
|
tester.printToConsole('Transaction state: $state');
|
|
tester.printToConsole('isDone: $isDone');
|
|
tester.printToConsole('isFailed: $isFailed');
|
|
|
|
if (isDone) {
|
|
tester.printToConsole('Transaction committed successfully!');
|
|
await tester.pump();
|
|
return;
|
|
} else if (isFailed) {
|
|
tester.printToConsole('Transaction failed: $state');
|
|
await tester.pump();
|
|
return;
|
|
} else {
|
|
tester.printToConsole('Transaction still processing');
|
|
await tester.pump();
|
|
}
|
|
} else {
|
|
// SendPage not found, check if we're on dashboard or still processing
|
|
if (dashboardFinder.tryEvaluate()) {
|
|
tester.printToConsole('Dashboard detected - transaction completed successfully!');
|
|
await tester.pump();
|
|
return;
|
|
} else {
|
|
tester.printToConsole(
|
|
'SendPage not found, but dashboard not yet visible - continuing to wait...');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stopwatch.elapsed >= maxDuration) {
|
|
tester.printToConsole('Transaction completion timeout reached');
|
|
}
|
|
|
|
tester.printToConsole('Done waiting for transaction completion');
|
|
}
|
|
|
|
//* ---- Handle Transaction Success Flow -----
|
|
Future<void> handleTransactionSuccessFlow() async {
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
// Wait for any success dialogs to appear
|
|
await tester.pump(Duration(seconds: 2));
|
|
|
|
// Check for contact addition dialog first (if new contact address exists)
|
|
final contactDialog = find.byKey(ValueKey('send_page_add_contact_bottom_sheet_yes_button_key'));
|
|
if (contactDialog.tryEvaluate()) {
|
|
tester.printToConsole('Found contact addition dialog, selecting Yes');
|
|
|
|
// Check if the button is actually visible and tappable
|
|
final buttonRect = tester.getRect(contactDialog);
|
|
final screenSize = tester.view.physicalSize / tester.view.devicePixelRatio;
|
|
|
|
if (buttonRect.bottom <= screenSize.height && buttonRect.top >= 0) {
|
|
await commonTestCases.tapItemByKey('send_page_add_contact_bottom_sheet_yes_button_key');
|
|
await commonTestCases.defaultSleepTime();
|
|
} else {
|
|
tester.printToConsole('Contact dialog button is off-screen, skipping');
|
|
}
|
|
}
|
|
|
|
// Check for the main success dialog
|
|
final successDialog = find.byKey(ValueKey('send_page_sent_dialog_ok_button_key'));
|
|
if (successDialog.tryEvaluate()) {
|
|
tester.printToConsole('Found transaction success dialog, closing it');
|
|
await commonTestCases.tapItemByKey('send_page_sent_dialog_ok_button_key');
|
|
await commonTestCases.defaultSleepTime();
|
|
}
|
|
}
|
|
|
|
//* ---- Fiat/Crypto Amount Validation -----
|
|
Future<void> testFiatAmountEntry() async {
|
|
tester.printToConsole('Testing fiat amount entry...');
|
|
|
|
await enterSendAmount('');
|
|
await enterSendAmount('', isFiat: true);
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
await enterSendAmount(CommonTestConstants.sendTestFiatAmount, isFiat: true);
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
// Wait for conversion to complete
|
|
await tester.pump(Duration(seconds: 3));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Next we get the crypto amount value and validate it's not 0
|
|
final cryptoAmount = _getTextFromField(ValueKey('send_page_amount_textfield_key'));
|
|
|
|
tester.printToConsole(
|
|
'Crypto amount after entering \$${CommonTestConstants.sendTestFiatAmount}: $cryptoAmount',
|
|
);
|
|
|
|
if (cryptoAmount.isNotEmpty && cryptoAmount != '0' && cryptoAmount != '0.0') {
|
|
tester.printToConsole('Fiat to crypto conversion working - crypto amount: $cryptoAmount');
|
|
} else {
|
|
tester.printToConsole(
|
|
'Fiat to crypto conversion may not be working - crypto amount: $cryptoAmount',
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> testCryptoAmountEntry() async {
|
|
tester.printToConsole('Testing crypto amount entry...');
|
|
|
|
await enterSendAmount('');
|
|
await enterSendAmount('', isFiat: true);
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
String cryptoAmount = '0.001';
|
|
|
|
await enterSendAmount(cryptoAmount);
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
// Wait for conversion to complete
|
|
await tester.pump(Duration(seconds: 3));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Get the fiat amount value and validate it's not 0
|
|
final fiatAmount = _getTextFromField(ValueKey('send_page_fiat_amount_textfield_key'));
|
|
|
|
tester.printToConsole('Fiat amount after entering $cryptoAmount: $fiatAmount');
|
|
|
|
if (fiatAmount.isNotEmpty && fiatAmount != '0' && fiatAmount != '0.0') {
|
|
tester.printToConsole('Crypto to fiat conversion working, fiat amount: $fiatAmount');
|
|
} else {
|
|
tester.printToConsole(
|
|
'Crypto to fiat conversion may not be working - fiat amount: $fiatAmount');
|
|
}
|
|
}
|
|
|
|
Future<void> setupOneDollarSend() async {
|
|
// Clear existing amounts
|
|
await enterSendAmount('');
|
|
await enterSendAmount('', isFiat: true);
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
await enterSendAmount(CommonTestConstants.sendTestFiatAmount, isFiat: true);
|
|
await commonTestCases.defaultSleepTime();
|
|
|
|
// Wait for conversion to complete
|
|
await tester.pump(Duration(seconds: 3));
|
|
await tester.pumpAndSettle();
|
|
|
|
// Get the converted crypto amount
|
|
String cryptoAmount = _getTextFromField(ValueKey('send_page_amount_textfield_key'));
|
|
if (cryptoAmount.isEmpty || cryptoAmount == '0' || cryptoAmount == '0.0') {
|
|
cryptoAmount = '0.001'; // fallback amount
|
|
}
|
|
|
|
tester.printToConsole(
|
|
'Sending $cryptoAmount (equivalent to \$${CommonTestConstants.sendTestFiatAmount}) to test wallet',
|
|
);
|
|
|
|
// Update the amount field with the converted value
|
|
await enterSendAmount(cryptoAmount);
|
|
await commonTestCases.defaultSleepTime();
|
|
}
|
|
}
|