feat: Modify Exchange and send tests to fit new flow

This commit is contained in:
Blazebrain 2025-04-09 17:18:24 +01:00
parent 9faec1c640
commit c2e77c08a9
12 changed files with 120 additions and 78 deletions

View file

@ -71,6 +71,14 @@ class CommonTestCases {
} }
Future<void> startGesture(String key, Offset gestureOffset) async { Future<void> startGesture(String key, Offset gestureOffset) async {
await tester.pumpAndSettle();
final hasKey = isKeyPresent(key);
tester.printToConsole("Has gestureKey: $hasKey");
if (!hasKey) return;
final gesture = await tester.startGesture(tester.getCenter(find.byKey(ValueKey(key)))); final gesture = await tester.startGesture(tester.getCenter(find.byKey(ValueKey(key))));
// Drag to the left // Drag to the left
@ -176,6 +184,31 @@ class CommonTestCases {
} }
} }
Future<void> scrollItemIntoView(
String itemKeyId,
double scrollPixels,
String scrollableFinderKey,
) async {
final Finder itemFinder = find.byKey(ValueKey(itemKeyId));
final scrollableFinder = find.descendant(
of: find.byKey(ValueKey(scrollableFinderKey)),
matching: find.byType(Scrollable),
);
try {
await tester.scrollUntilVisible(
itemFinder,
scrollPixels,
scrollable: scrollableFinder,
);
} catch (e) {
tester.printToConsole('Could not find $itemKeyId');
}
await tester.pumpAndSettle();
}
Future<void> enterText(String text, String editableTextKey) async { Future<void> enterText(String text, String editableTextKey) async {
final editableTextWidget = find.byKey(ValueKey((editableTextKey))); final editableTextWidget = find.byKey(ValueKey((editableTextKey)));

View file

@ -10,8 +10,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:cake_wallet/main.dart' as app; import 'package:cake_wallet/main.dart' as app;
import '../robots/create_pin_welcome_page_robot.dart'; import '../robots/create_pin_welcome_page_robot.dart';
import '../robots/dashboard_page_robot.dart';
import '../robots/disclaimer_page_robot.dart';
import '../robots/new_wallet_page_robot.dart'; import '../robots/new_wallet_page_robot.dart';
import '../robots/new_wallet_type_page_robot.dart'; import '../robots/new_wallet_type_page_robot.dart';
import '../robots/pre_seed_page_robot.dart'; import '../robots/pre_seed_page_robot.dart';
@ -34,9 +32,7 @@ class CommonTestFlows {
_welcomePageRobot = WelcomePageRobot(_tester), _welcomePageRobot = WelcomePageRobot(_tester),
_preSeedPageRobot = PreSeedPageRobot(_tester), _preSeedPageRobot = PreSeedPageRobot(_tester),
_setupPinCodeRobot = SetupPinCodeRobot(_tester), _setupPinCodeRobot = SetupPinCodeRobot(_tester),
_dashboardPageRobot = DashboardPageRobot(_tester),
_newWalletPageRobot = NewWalletPageRobot(_tester), _newWalletPageRobot = NewWalletPageRobot(_tester),
_disclaimerPageRobot = DisclaimerPageRobot(_tester),
_walletSeedPageRobot = WalletSeedPageRobot(_tester), _walletSeedPageRobot = WalletSeedPageRobot(_tester),
_walletListPageRobot = WalletListPageRobot(_tester), _walletListPageRobot = WalletListPageRobot(_tester),
_newWalletTypePageRobot = NewWalletTypePageRobot(_tester), _newWalletTypePageRobot = NewWalletTypePageRobot(_tester),
@ -53,8 +49,6 @@ class CommonTestFlows {
final PreSeedPageRobot _preSeedPageRobot; final PreSeedPageRobot _preSeedPageRobot;
final SetupPinCodeRobot _setupPinCodeRobot; final SetupPinCodeRobot _setupPinCodeRobot;
final NewWalletPageRobot _newWalletPageRobot; final NewWalletPageRobot _newWalletPageRobot;
final DashboardPageRobot _dashboardPageRobot;
final DisclaimerPageRobot _disclaimerPageRobot;
final WalletSeedPageRobot _walletSeedPageRobot; final WalletSeedPageRobot _walletSeedPageRobot;
final WalletListPageRobot _walletListPageRobot; final WalletListPageRobot _walletListPageRobot;
final NewWalletTypePageRobot _newWalletTypePageRobot; final NewWalletTypePageRobot _newWalletTypePageRobot;
@ -113,13 +107,6 @@ class CommonTestFlows {
await _restoreFromKeys(); await _restoreFromKeys();
} }
//* ========== Handles switching to wallet list or menu from dashboard ===============
Future<void> switchToWalletMenuFromDashboardPage() async {
_tester.printToConsole('Switching to Wallet Menu');
await _dashboardPageRobot.dashboardMenuWidgetRobot.navigateToWalletMenu();
}
void confirmAllAvailableWalletTypeIconsDisplayCorrectly() { void confirmAllAvailableWalletTypeIconsDisplayCorrectly() {
for (var walletType in availableWalletTypes) { for (var walletType in availableWalletTypes) {
final imageUrl = walletTypeToCryptoCurrency(walletType).iconPath; final imageUrl = walletTypeToCryptoCurrency(walletType).iconPath;

View file

@ -27,7 +27,7 @@ class DashboardMenuWidgetRobot {
} }
Future<void> navigateToWalletMenu() async { Future<void> navigateToWalletMenu() async {
await commonTestCases.tapItemByKey('dashboard_page_Wallets_action_button_key'); await commonTestCases.tapItemByKey('dashboard_page_menu_widget_wallet_menu_button_key');
await commonTestCases.defaultSleepTime(); await commonTestCases.defaultSleepTime();
} }

View file

@ -92,6 +92,10 @@ class DashboardPageRobot {
await commonTestCases.tapItemByKey('dashboard_page_${S.current.buy}_action_button_key'); await commonTestCases.tapItemByKey('dashboard_page_${S.current.buy}_action_button_key');
} }
Future<void> navigateToWalletsListPage() async {
await commonTestCases.tapItemByKey('dashboard_page_${S.current.wallets}_action_button_key');
}
Future<void> navigateToSendPage() async { Future<void> navigateToSendPage() async {
await commonTestCases.tapItemByKey('dashboard_page_${S.current.send}_action_button_key'); await commonTestCases.tapItemByKey('dashboard_page_${S.current.send}_action_button_key');
} }

View file

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/send/send_page.dart'; import 'package:cake_wallet/src/screens/send/send_page.dart';
import 'package:cake_wallet/src/widgets/standard_slide_button_widget.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
@ -299,37 +300,36 @@ class SendPageRobot {
//* ------ On Sending Success ------------ //* ------ On Sending Success ------------
Future<void> onSendSliderOnConfirmSendingBottomSheetDragged() async { Future<void> onSendSliderOnConfirmSendingBottomSheetDragged() async {
tester.printToConsole('Inside confirm sending dialog: For sending');
await commonTestCases.defaultSleepTime(); await commonTestCases.defaultSleepTime();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
bool hasConfirmSendBottomSheet = if (commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key')) {
commonTestCases.isKeyPresent('send_page_confirm_sending_bottom_sheet_key'); final state = tester.state<StandardSlideButtonState>(find.byType(StandardSlideButton));
final double effectiveMaxWidth = state.effectiveMaxWidth;
final double sliderWidth = state.sliderWidth;
final double threshold = effectiveMaxWidth - sliderWidth - 10;
tester.printToConsole('Has Confirm Send BottomSheet: $hasConfirmSendBottomSheet'); final sliderFinder =
find.byKey(const ValueKey('standard_slide_button_widget_slider_container_key'));
expect(sliderFinder, findsOneWidget);
if (hasConfirmSendBottomSheet) { // Using the center of the container as the drag start.
await commonTestCases.startGesture( final Offset dragStart = tester.getCenter(sliderFinder);
'standard_slide_button_widget_slider_key',
Offset(200, 0),
);
tester.printToConsole('Slider moved'); // Dragging by an offset sufficient to exceed the threshold.
await tester.dragFrom(dragStart, Offset(threshold + 20, 0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
tester.printToConsole('Slider pump done'); tester.printToConsole('Final slider dragPosition: ${state.dragPosition}');
// Loop to wait for the operation to commit transaction // Loop to wait for the operation to commit transaction
await _waitForCommitTransactionCompletion(); await _waitForCommitTransactionCompletion();
await tester.pump();
await commonTestCases.defaultSleepTime(seconds: 4); await commonTestCases.defaultSleepTime(seconds: 4);
} else { } else {
await commonTestCases.defaultSleepTime(); await commonTestCases.defaultSleepTime();
await tester.pump(); await tester.pump();
onSendSliderOnConfirmSendingBottomSheetDragged(); await onSendSliderOnConfirmSendingBottomSheetDragged();
} }
} }

View file

@ -52,10 +52,10 @@ class TransactionsPageRobot {
// Define a timeout to prevent infinite loops // Define a timeout to prevent infinite loops
// Putting at one hour for cases like monero that takes time to sync // Putting at one hour for cases like monero that takes time to sync
final timeout = Duration(hours: 1); final timeout = Duration(hours: 1);
final pollingInterval = Duration(seconds: 2);
final endTime = DateTime.now().add(timeout); final endTime = DateTime.now().add(timeout);
while (DateTime.now().isBefore(endTime)) { while (DateTime.now().isBefore(endTime)) {
await tester.pump(Duration(seconds: 5));
final isSynced = dashboardViewModel.status is SyncedSyncStatus; final isSynced = dashboardViewModel.status is SyncedSyncStatus;
final itemsLoaded = dashboardViewModel.items.isNotEmpty; final itemsLoaded = dashboardViewModel.items.isNotEmpty;
@ -64,21 +64,29 @@ class TransactionsPageRobot {
await _performItemChecks(dashboardViewModel); await _performItemChecks(dashboardViewModel);
} else { } else {
// Verify placeholder when items are not loaded // Verify placeholder when items are not loaded
await tester.pump(Duration(seconds: 5));
_verifyPlaceholder(); _verifyPlaceholder();
tester.printToConsole('No item to check for');
} }
// Determine if we should exit the loop // Determine if we should exit the loop
if (_shouldExitLoop(hasTxHistoryWhileSyncing, isSynced, itemsLoaded)) { bool shouldExit = _shouldExitLoop(hasTxHistoryWhileSyncing, isSynced, itemsLoaded);
await tester.pump(Duration(seconds: 2));
if (shouldExit) {
await tester.pump(Duration(seconds: 2));
break; break;
} }
// Pump the UI and wait for the next polling interval // Pump the UI and wait for the next polling interval
await tester.pumpAndSettle(pollingInterval); await commonTestCases.defaultSleepTime();
await tester.pump(Duration(seconds: 2));
await tester.pumpAndSettle();
} }
// After the loop, verify that both status is synced and items are loaded // After the loop, verify that both status is synced and items are loaded
if (!_isFinalStateValid(dashboardViewModel)) { if (!_isFinalStateValid(dashboardViewModel)) {
throw TimeoutException('Dashboard did not sync and load items within the allotted time.'); tester.printToConsole('Dashboard did not sync and load items within the allotted time.');
} }
} }
@ -119,10 +127,14 @@ class TransactionsPageRobot {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Scroll the item into view // Scroll the item into view
await commonTestCases.dragUntilVisible(keyId, 'transactions_page_list_view_builder_key'); await commonTestCases.scrollItemIntoView(
keyId,
20,
'transactions_page_list_view_builder_key',
);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Check if the widget is visible // Verify the widget is visible; if not, skip to the next one.
if (!tester.any(find.byKey(ValueKey(keyId)))) { if (!tester.any(find.byKey(ValueKey(keyId)))) {
tester.printToConsole('Item not visible: $keyId. Moving to the next.'); tester.printToConsole('Item not visible: $keyId. Moving to the next.');
continue; continue;
@ -130,6 +142,7 @@ class TransactionsPageRobot {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Execute the proper check depending on item type.
switch (item.runtimeType) { switch (item.runtimeType) {
case TransactionListItem: case TransactionListItem:
final transactionItem = item as TransactionListItem; final transactionItem = item as TransactionListItem;

View file

@ -183,6 +183,5 @@ class WalletKeysAndSeedPageRobot {
tester.printToConsole('Going back to dashboard from credentials page'); tester.printToConsole('Going back to dashboard from credentials page');
await commonTestCases.goBack(); await commonTestCases.goBack();
await commonTestCases.goBack(); await commonTestCases.goBack();
await commonTestCases.goBack();
} }
} }

View file

@ -1,4 +1,3 @@
import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -58,7 +57,7 @@ void main() {
continue; continue;
} }
await commonTestFlows.switchToWalletMenuFromDashboardPage(); await dashboardPageRobot.navigateToWalletsListPage();
await commonTestFlows.createNewWalletFromWalletMenu(walletType); await commonTestFlows.createNewWalletFromWalletMenu(walletType);

View file

@ -39,7 +39,7 @@ void main() {
continue; continue;
} }
await commonTestFlows.switchToWalletMenuFromDashboardPage(); await dashboardPageRobot.navigateToWalletsListPage();
await commonTestFlows.createNewWalletFromWalletMenu(walletType); await commonTestFlows.createNewWalletFromWalletMenu(walletType);
@ -47,7 +47,7 @@ void main() {
} }
// Goes to the wallet menu and provides a confirmation that all the wallets were correctly restored // Goes to the wallet menu and provides a confirmation that all the wallets were correctly restored
await commonTestFlows.switchToWalletMenuFromDashboardPage(); await dashboardPageRobot.navigateToWalletsListPage();
commonTestFlows.confirmAllAvailableWalletTypeIconsDisplayCorrectly(); commonTestFlows.confirmAllAvailableWalletTypeIconsDisplayCorrectly();

View file

@ -1,4 +1,3 @@
import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -51,7 +50,7 @@ void main() {
continue; continue;
} }
await commonTestFlows.switchToWalletMenuFromDashboardPage(); await dashboardPageRobot.navigateToWalletsListPage();
await commonTestFlows.restoreWalletFromWalletMenu( await commonTestFlows.restoreWalletFromWalletMenu(
walletType, walletType,
@ -62,7 +61,7 @@ void main() {
} }
// Goes to the wallet menu and provides a visual confirmation that all the wallets were correctly restored // Goes to the wallet menu and provides a visual confirmation that all the wallets were correctly restored
await commonTestFlows.switchToWalletMenuFromDashboardPage(); await dashboardPageRobot.navigateToWalletsListPage();
commonTestFlows.confirmAllAvailableWalletTypeIconsDisplayCorrectly(); commonTestFlows.confirmAllAvailableWalletTypeIconsDisplayCorrectly();

View file

@ -28,33 +28,13 @@ void main() {
ValueKey('confirm_creds_display_correctly_flow_app_key'), ValueKey('confirm_creds_display_correctly_flow_app_key'),
); );
/// Test Scenario 1 - Displays transaction history list after fully synchronizing. /// Test Scenario 1 - Displays transaction history list while synchronizing.
///
/// For Solana/Tron WalletTypes.
await commonTestFlows.welcomePageToRestoreWalletThroughSeedsFlow(
WalletType.solana,
secrets.solanaTestWalletSeeds,
CommonTestConstants.pin,
);
await dashboardPageRobot.confirmWalletTypeIsDisplayedCorrectly(WalletType.solana);
await dashboardPageRobot.swipeDashboardTab(true);
await transactionsPageRobot.isTransactionsPage();
await transactionsPageRobot.confirmTransactionsPageConstantsDisplayProperly();
await transactionsPageRobot.confirmTransactionHistoryListDisplaysCorrectly(false);
/// Test Scenario 2 - Displays transaction history list while synchronizing.
/// ///
/// For bitcoin/Monero/Wownero WalletTypes. /// For bitcoin/Monero/Wownero WalletTypes.
await commonTestFlows.switchToWalletMenuFromDashboardPage(); await commonTestFlows.welcomePageToRestoreWalletThroughSeedsFlow(
await commonTestFlows.restoreWalletFromWalletMenu(
WalletType.bitcoin, WalletType.bitcoin,
secrets.bitcoinTestWalletSeeds, secrets.bitcoinTestWalletSeeds,
CommonTestConstants.pin,
); );
await dashboardPageRobot.confirmWalletTypeIsDisplayedCorrectly(WalletType.bitcoin); await dashboardPageRobot.confirmWalletTypeIsDisplayedCorrectly(WalletType.bitcoin);
@ -65,6 +45,26 @@ void main() {
await transactionsPageRobot.confirmTransactionsPageConstantsDisplayProperly(); await transactionsPageRobot.confirmTransactionsPageConstantsDisplayProperly();
await transactionsPageRobot.confirmTransactionHistoryListDisplaysCorrectly(false);
/// Test Scenario 2 - Displays transaction history list after fully synchronizing.
///
/// For Solana/Tron WalletTypes.
await dashboardPageRobot.navigateToWalletsListPage();
await commonTestFlows.restoreWalletFromWalletMenu(
WalletType.solana,
secrets.solanaTestWalletSeeds,
);
await dashboardPageRobot.confirmWalletTypeIsDisplayedCorrectly(WalletType.solana);
await dashboardPageRobot.swipeDashboardTab(true);
await transactionsPageRobot.isTransactionsPage();
await transactionsPageRobot.confirmTransactionsPageConstantsDisplayProperly();
await transactionsPageRobot.confirmTransactionHistoryListDisplaysCorrectly(true); await transactionsPageRobot.confirmTransactionHistoryListDisplaysCorrectly(true);
}); });
} }

View file

@ -20,31 +20,33 @@ class StandardSlideButton extends StatefulWidget {
final ThemeBase currentTheme; final ThemeBase currentTheme;
@override @override
_StandardSlideButtonState createState() => _StandardSlideButtonState(); StandardSlideButtonState createState() => StandardSlideButtonState();
} }
class _StandardSlideButtonState extends State<StandardSlideButton> { class StandardSlideButtonState extends State<StandardSlideButton> {
double _dragPosition = 0.0; double _dragPosition = 0.0;
double get dragPosition => _dragPosition;
double sideMargin = 4.0;
double effectiveMaxWidth = 0.0;
double sliderWidth = 42.0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
final double maxWidth = constraints.maxWidth; final double maxWidth = constraints.maxWidth;
const double sideMargin = 4.0; effectiveMaxWidth = maxWidth - 2 * sideMargin;
final double effectiveMaxWidth = maxWidth - 2 * sideMargin;
const double sliderWidth = 42.0;
final tileBackgroundColor = widget.currentTheme.type == ThemeType.light final tileBackgroundColor = widget.currentTheme.type == ThemeType.light
? Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor ? Theme.of(context).extension<SyncIndicatorTheme>()!.syncedBackgroundColor
: widget.currentTheme.type == ThemeType.oled : widget.currentTheme.type == ThemeType.oled
? Colors.black.withOpacity(0.5) ? Colors.black.withOpacity(0.5)
: Theme.of(context).extension<FilterTheme>()!.buttonColor; : Theme.of(context).extension<FilterTheme>()!.buttonColor;
return Container( return Container(
height: widget.height, height: widget.height,
decoration: BoxDecoration( decoration:
borderRadius: BorderRadius.circular(10), BoxDecoration(borderRadius: BorderRadius.circular(10), color: tileBackgroundColor),
color: tileBackgroundColor),
child: Stack( child: Stack(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
children: [ children: [
@ -76,6 +78,7 @@ class _StandardSlideButtonState extends State<StandardSlideButton> {
} }
}, },
child: Container( child: Container(
key: ValueKey('standard_slide_button_widget_slider_container_key'),
width: sliderWidth, width: sliderWidth,
height: widget.height - 8, height: widget.height - 8,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -83,8 +86,13 @@ class _StandardSlideButtonState extends State<StandardSlideButton> {
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor, color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
), ),
alignment: Alignment.center, alignment: Alignment.center,
child: Icon(Icons.arrow_forward, child: Icon(
color: widget.currentTheme.type == ThemeType.bright ? Theme.of(context).extension<CakeMenuTheme>()!.backgroundColor : Theme.of(context).extension<FilterTheme>()!.buttonColor), key: ValueKey('standard_slide_button_widget_slider_icon_key'),
Icons.arrow_forward,
color: widget.currentTheme.type == ThemeType.bright
? Theme.of(context).extension<CakeMenuTheme>()!.backgroundColor
: Theme.of(context).extension<FilterTheme>()!.buttonColor,
),
), ),
), ),
) )