| 1 | import 'dart:io'; |
| 2 | |
| 3 | import 'package:cake_wallet/entities/seed_type.dart'; |
| 4 | import 'package:cake_wallet/reactions/wallet_utils.dart'; |
| 5 | import 'package:cake_wallet/wallet_types.g.dart'; |
| 6 | import 'package:cw_core/currency_for_wallet_type.dart'; |
| 7 | import 'package:cw_core/wallet_type.dart'; |
| 8 | import 'package:flutter/material.dart'; |
| 9 | import 'package:flutter_test/flutter_test.dart'; |
| 10 | |
| 11 | import 'package:cake_wallet/main.dart' as app; |
| 12 | |
| 13 | import '../robots/create_pin_welcome_page_robot.dart'; |
| 14 | import '../robots/new_wallet_page_robot.dart'; |
| 15 | import '../robots/new_wallet_type_page_robot.dart'; |
| 16 | import '../robots/pre_seed_page_robot.dart'; |
| 17 | import '../robots/restore_from_seed_or_key_robot.dart'; |
| 18 | import '../robots/restore_options_page_robot.dart'; |
| 19 | import '../robots/seed_verification_page_robot.dart'; |
| 20 | import '../robots/setup_pin_code_robot.dart'; |
| 21 | import '../robots/wallet_group_description_page_robot.dart'; |
| 22 | import '../robots/wallet_list_page_robot.dart'; |
| 23 | import '../robots/wallet_seed_page_robot.dart'; |
| 24 | import '../robots/welcome_page_robot.dart'; |
| 25 | import 'common_test_cases.dart'; |
| 26 | import 'package:cake_wallet/.secrets.g.dart' as secrets; |
| 27 | |
| 28 | import 'common_test_constants.dart'; |
| 29 | |
| 30 | class CommonTestFlows { |
| 31 | CommonTestFlows(this._tester) |
| 32 | : _commonTestCases = CommonTestCases(_tester), |
| 33 | _welcomePageRobot = WelcomePageRobot(_tester), |
| 34 | _preSeedPageRobot = PreSeedPageRobot(_tester), |
| 35 | _setupPinCodeRobot = SetupPinCodeRobot(_tester), |
| 36 | _newWalletPageRobot = NewWalletPageRobot(_tester), |
| 37 | _walletSeedPageRobot = WalletSeedPageRobot(_tester), |
| 38 | _walletListPageRobot = WalletListPageRobot(_tester), |
| 39 | _newWalletTypePageRobot = NewWalletTypePageRobot(_tester), |
| 40 | _restoreOptionsPageRobot = RestoreOptionsPageRobot(_tester), |
| 41 | _seedVerificationPageRobot = SeedVerificationPageRobot(_tester), |
| 42 | _createPinWelcomePageRobot = CreatePinWelcomePageRobot(_tester), |
| 43 | _restoreFromSeedOrKeysPageRobot = RestoreFromSeedOrKeysPageRobot(_tester), |
| 44 | _walletGroupDescriptionPageRobot = WalletGroupDescriptionPageRobot(_tester); |
| 45 | |
| 46 | final WidgetTester _tester; |
| 47 | final CommonTestCases _commonTestCases; |
| 48 | |
| 49 | final WelcomePageRobot _welcomePageRobot; |
| 50 | final PreSeedPageRobot _preSeedPageRobot; |
| 51 | final SetupPinCodeRobot _setupPinCodeRobot; |
| 52 | final NewWalletPageRobot _newWalletPageRobot; |
| 53 | final WalletSeedPageRobot _walletSeedPageRobot; |
| 54 | final WalletListPageRobot _walletListPageRobot; |
| 55 | final NewWalletTypePageRobot _newWalletTypePageRobot; |
| 56 | final RestoreOptionsPageRobot _restoreOptionsPageRobot; |
| 57 | final CreatePinWelcomePageRobot _createPinWelcomePageRobot; |
| 58 | final SeedVerificationPageRobot _seedVerificationPageRobot; |
| 59 | final RestoreFromSeedOrKeysPageRobot _restoreFromSeedOrKeysPageRobot; |
| 60 | final WalletGroupDescriptionPageRobot _walletGroupDescriptionPageRobot; |
| 61 | |
| 62 | //* ========== Handles flow to start the app afresh and accept disclaimer ============= |
| 63 | Future<void> startAppFlow(Key key) async { |
| 64 | await app.main(topLevelKey: key); |
| 65 | |
| 66 | await _tester.pump(Duration(seconds: 2)); |
| 67 | |
| 68 | // // --------- Disclaimer Page ------------ |
| 69 | // // Tap checkbox to accept disclaimer |
| 70 | // await _disclaimerPageRobot.tapDisclaimerCheckbox(); |
| 71 | |
| 72 | // // Tap accept button |
| 73 | // await _disclaimerPageRobot.tapAcceptButton(); |
| 74 | } |
| 75 | |
| 76 | //* ========== Handles flow from welcome to creating a new wallet =============== |
| 77 | Future<void> welcomePageToCreateNewWalletFlow( |
| 78 | WalletType walletTypeToCreate, |
| 79 | List<int> walletPin, |
| 80 | ) async { |
| 81 | await _welcomeToCreateWalletPath(walletTypeToCreate, walletPin); |
| 82 | |
| 83 | await _generateNewWalletDetails(); |
| 84 | |
| 85 | await _confirmPreSeedInfo(); |
| 86 | |
| 87 | await _confirmWalletDetails(); |
| 88 | |
| 89 | await _verifyWalletSeed(); |
| 90 | } |
| 91 | |
| 92 | //* ========== Handles flow from welcome to restoring wallet from seeds =============== |
| 93 | Future<void> welcomePageToRestoreWalletThroughSeedsFlow( |
| 94 | WalletType walletTypeToRestore, |
| 95 | String walletSeed, |
| 96 | List<int> walletPin, |
| 97 | ) async { |
| 98 | await _welcomeToRestoreFromSeedsOrKeysPath(walletTypeToRestore, walletPin); |
| 99 | await _restoreFromSeeds(walletTypeToRestore, walletSeed); |
| 100 | } |
| 101 | |
| 102 | //* ========== Handles flow from welcome to restoring wallet from keys =============== |
| 103 | Future<void> welcomePageToRestoreWalletThroughKeysFlow( |
| 104 | WalletType walletTypeToRestore, |
| 105 | List<int> walletPin, |
| 106 | ) async { |
| 107 | await _welcomeToRestoreFromSeedsOrKeysPath(walletTypeToRestore, walletPin); |
| 108 | await _restoreFromKeys(); |
| 109 | } |
| 110 | |
| 111 | void confirmAllAvailableWalletTypeIconsDisplayCorrectly() { |
| 112 | for (var walletType in availableWalletTypes) { |
| 113 | final imageUrl = walletTypeToCryptoCurrency(walletType).iconPath; |
| 114 | |
| 115 | final walletIconFinder = find.image( |
| 116 | Image.asset( |
| 117 | imageUrl!, |
| 118 | width: 32, |
| 119 | height: 32, |
| 120 | ).image, |
| 121 | ); |
| 122 | |
| 123 | expect(walletIconFinder, findsAny); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | //* ========== Handles creating new wallet flow from wallet list/menu =============== |
| 128 | Future<void> createNewWalletFromWalletMenu(WalletType walletTypeToCreate) async { |
| 129 | _tester.printToConsole('Creating ${walletTypeToCreate.name} Wallet'); |
| 130 | |
| 131 | await _tester.pumpAndSettle(Duration(milliseconds: 1000)); |
| 132 | |
| 133 | await _walletListPageRobot.navigateToCreateNewWalletPage(); |
| 134 | await _commonTestCases.defaultSleepTime(); |
| 135 | |
| 136 | await _selectWalletTypeForWallet(walletTypeToCreate); |
| 137 | await _commonTestCases.defaultSleepTime(); |
| 138 | |
| 139 | // ---- Wallet Group/New Seed Implementation Comes here |
| 140 | await _walletGroupDescriptionPageFlow(true, walletTypeToCreate); |
| 141 | |
| 142 | await _generateNewWalletDetails(); |
| 143 | |
| 144 | await _confirmPreSeedInfo(); |
| 145 | |
| 146 | await _confirmWalletDetails(); |
| 147 | |
| 148 | await _verifyWalletSeed(); |
| 149 | |
| 150 | await _commonTestCases.defaultSleepTime(); |
| 151 | } |
| 152 | |
| 153 | Future<void> _walletGroupDescriptionPageFlow(bool isNewSeed, WalletType walletType) async { |
| 154 | if (!isBIP39Wallet(walletType)) return; |
| 155 | |
| 156 | await _walletGroupDescriptionPageRobot.isWalletGroupDescriptionPage(); |
| 157 | |
| 158 | if (isNewSeed) { |
| 159 | await _walletGroupDescriptionPageRobot.navigateToCreateNewSeedPage(); |
| 160 | } else { |
| 161 | await _walletGroupDescriptionPageRobot.navigateToChooseWalletGroup(); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | //* ========== Handles restore wallet flow from wallet list/menu =============== |
| 166 | Future<void> restoreWalletFromWalletMenu(WalletType walletType, String walletSeed) async { |
| 167 | _tester.printToConsole('Restoring ${walletType.name} Wallet'); |
| 168 | await _walletListPageRobot.navigateToRestoreWalletOptionsPage(); |
| 169 | await _commonTestCases.defaultSleepTime(); |
| 170 | |
| 171 | if (!Platform.isLinux) await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage(); |
| 172 | if (!Platform.isLinux) await _commonTestCases.defaultSleepTime(); |
| 173 | |
| 174 | await _selectWalletTypeForWallet(walletType); |
| 175 | await _commonTestCases.defaultSleepTime(); |
| 176 | |
| 177 | await _restoreFromSeeds(walletType, walletSeed); |
| 178 | await _commonTestCases.defaultSleepTime(); |
| 179 | } |
| 180 | |
| 181 | //* ========== Handles setting up pin code for wallet on first install =============== |
| 182 | Future<void> setupPinCodeForWallet(List<int> pin) async { |
| 183 | if (Platform.isLinux) return; |
| 184 | // ----------- SetupPinCode Page ------------- |
| 185 | // Confirm initial defaults - Widgets to be displayed etc |
| 186 | await _setupPinCodeRobot.isSetupPinCodePage(); |
| 187 | |
| 188 | await _setupPinCodeRobot.enterPinCode(pin); |
| 189 | await _setupPinCodeRobot.enterPinCode(pin); |
| 190 | await _setupPinCodeRobot.tapSuccessButton(); |
| 191 | } |
| 192 | |
| 193 | Future<void> _welcomeToCreateWalletPath( |
| 194 | WalletType walletTypeToCreate, |
| 195 | List<int> pin, |
| 196 | ) async { |
| 197 | await _createPinWelcomePageRobot.tapSetAPinButton(); |
| 198 | |
| 199 | await setupPinCodeForWallet(pin); |
| 200 | |
| 201 | await _welcomePageRobot.navigateToCreateNewWalletPage(); |
| 202 | |
| 203 | await _selectWalletTypeForWallet(walletTypeToCreate); |
| 204 | |
| 205 | if (_welcomePageRobot.hasNewSingleSeedButton()) { |
| 206 | await _welcomePageRobot.tapNewSingleSeed(); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | Future<void> _welcomeToRestoreFromSeedsOrKeysPath( |
| 211 | WalletType walletTypeToRestore, |
| 212 | List<int> pin, |
| 213 | ) async { |
| 214 | await _createPinWelcomePageRobot.tapSetAPinButton(); |
| 215 | |
| 216 | await setupPinCodeForWallet(pin); |
| 217 | |
| 218 | await _welcomePageRobot.navigateToRestoreWalletPage(); |
| 219 | |
| 220 | if (!Platform.isLinux) await _restoreOptionsPageRobot.navigateToRestoreFromSeedsOrKeysPage(); |
| 221 | |
| 222 | await _selectWalletTypeForWallet(walletTypeToRestore); |
| 223 | } |
| 224 | |
| 225 | //* ============ Handles New Wallet Type Page ================== |
| 226 | Future<void> _selectWalletTypeForWallet(WalletType type) async { |
| 227 | // ----------- NewWalletType Page ------------- |
| 228 | _tester.printToConsole('Selecting wallet type: ${type.name}'); |
| 229 | |
| 230 | await _tester.pumpAndSettle(Duration(milliseconds: 1000)); |
| 231 | |
| 232 | await _newWalletTypePageRobot.findParticularWalletTypeInScrollableList(type); |
| 233 | |
| 234 | // Tapping the wallet type navigates directly (no separate Next button). |
| 235 | _tester.printToConsole('Tapping wallet type: ${type.name}'); |
| 236 | await _newWalletTypePageRobot.selectWalletType(type); |
| 237 | |
| 238 | await _tester.pumpAndSettle(Duration(milliseconds: 2000)); |
| 239 | |
| 240 | _tester.printToConsole('Wallet type selection completed for: ${type.name}'); |
| 241 | } |
| 242 | |
| 243 | //* ============ Handles New Wallet Page ================== |
| 244 | Future<void> _generateNewWalletDetails() async { |
| 245 | _tester.printToConsole('Attempting to find NewWalletPage...'); |
| 246 | |
| 247 | await _tester.pumpAndSettle(Duration(milliseconds: 1000)); |
| 248 | |
| 249 | await _newWalletPageRobot.isNewWalletPage(); |
| 250 | _tester.printToConsole('NewWalletPage found successfully'); |
| 251 | |
| 252 | await _newWalletPageRobot.generateWalletName(); |
| 253 | |
| 254 | if (Platform.isLinux) { |
| 255 | // manual pin input |
| 256 | await _restoreFromSeedOrKeysPageRobot |
| 257 | .enterPasswordForWalletRestore(CommonTestConstants.pin.join("")); |
| 258 | await _restoreFromSeedOrKeysPageRobot |
| 259 | .enterPasswordRepeatForWalletRestore(CommonTestConstants.pin.join("")); |
| 260 | } |
| 261 | |
| 262 | await _newWalletPageRobot.onNextButtonPressed(); |
| 263 | } |
| 264 | |
| 265 | //* ============ Handles Pre Seed Page ===================== |
| 266 | Future<void> _confirmPreSeedInfo() async { |
| 267 | await _preSeedPageRobot.isPreSeedPage(); |
| 268 | |
| 269 | await _preSeedPageRobot.onConfirmButtonPressed(); |
| 270 | } |
| 271 | |
| 272 | //* ============ Handles Wallet Seed Page ================== |
| 273 | Future<void> _confirmWalletDetails() async { |
| 274 | await _walletSeedPageRobot.isWalletSeedPage(); |
| 275 | |
| 276 | _walletSeedPageRobot.confirmWalletDetailsDisplayCorrectly(); |
| 277 | |
| 278 | _walletSeedPageRobot.confirmWalletSeedReminderDisplays(); |
| 279 | |
| 280 | // await _walletSeedPageRobot.onCopySeedsButtonPressed(); |
| 281 | |
| 282 | await _walletSeedPageRobot.onVerifySeedButtonPressed(); |
| 283 | } |
| 284 | |
| 285 | //* ============ Handles Wallet Seed Verification Page ================== |
| 286 | |
| 287 | Future<void> _verifyWalletSeed() async { |
| 288 | await _seedVerificationPageRobot.isSeedVerificationPage(); |
| 289 | |
| 290 | _seedVerificationPageRobot.hasTitle(); |
| 291 | |
| 292 | await _seedVerificationPageRobot.verifyWalletSeeds(); |
| 293 | } |
| 294 | |
| 295 | //* Main Restore Actions - On the RestoreFromSeed/Keys Page - Restore from Seeds Action |
| 296 | Future<void> _restoreFromSeeds(WalletType type, String walletSeed) async { |
| 297 | // ----------- RestoreFromSeedOrKeys Page ------------- |
| 298 | |
| 299 | await _restoreFromSeedOrKeysPageRobot.selectWalletNameFromAvailableOptions(); |
| 300 | await _restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore(walletSeed); |
| 301 | |
| 302 | final numberOfWords = walletSeed.split(' ').length; |
| 303 | |
| 304 | if (numberOfWords == 25 && (type == WalletType.monero)) { |
| 305 | await _restoreFromSeedOrKeysPageRobot |
| 306 | .chooseSeedTypeForMoneroOrWowneroWallets(MoneroSeedType.legacy); |
| 307 | |
| 308 | // Using a constant value of 2831400 for the blockheight as its the restore blockheight for our testing wallet |
| 309 | await _restoreFromSeedOrKeysPageRobot |
| 310 | .enterBlockHeightForWalletRestore(secrets.moneroTestWalletBlockHeight); |
| 311 | } |
| 312 | |
| 313 | if (Platform.isLinux) { |
| 314 | // manual pin input |
| 315 | await _restoreFromSeedOrKeysPageRobot |
| 316 | .enterPasswordForWalletRestore(CommonTestConstants.pin.join("")); |
| 317 | await _restoreFromSeedOrKeysPageRobot |
| 318 | .enterPasswordRepeatForWalletRestore(CommonTestConstants.pin.join("")); |
| 319 | } |
| 320 | |
| 321 | await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed(); |
| 322 | } |
| 323 | |
| 324 | //* Main Restore Actions - On the RestoreFromSeed/Keys Page - Restore from Keys Action |
| 325 | Future<void> _restoreFromKeys() async { |
| 326 | await _commonTestCases.swipePage(); |
| 327 | await _commonTestCases.defaultSleepTime(); |
| 328 | |
| 329 | await _restoreFromSeedOrKeysPageRobot.selectWalletNameFromAvailableOptions( |
| 330 | isSeedFormEntry: false, |
| 331 | ); |
| 332 | |
| 333 | await _restoreFromSeedOrKeysPageRobot.enterSeedPhraseForWalletRestore(''); |
| 334 | await _restoreFromSeedOrKeysPageRobot.onRestoreWalletButtonPressed(); |
| 335 | } |
| 336 | |
| 337 | //* ====== Utility Function to get test wallet seeds for each wallet type ======== |
| 338 | String getWalletSeedsByWalletType(WalletType walletType) { |
| 339 | switch (walletType) { |
| 340 | case WalletType.monero: |
| 341 | return secrets.moneroTestWalletSeeds; |
| 342 | case WalletType.bitcoin: |
| 343 | return secrets.bitcoinTestWalletSeeds; |
| 344 | case WalletType.ethereum: |
| 345 | return secrets.ethereumTestWalletSeeds; |
| 346 | case WalletType.litecoin: |
| 347 | return secrets.litecoinTestWalletSeeds; |
| 348 | case WalletType.bitcoinCash: |
| 349 | return secrets.bitcoinCashTestWalletSeeds; |
| 350 | case WalletType.polygon: |
| 351 | return secrets.polygonTestWalletSeeds; |
| 352 | case WalletType.solana: |
| 353 | return secrets.solanaTestWalletSeeds; |
| 354 | case WalletType.base: |
| 355 | return secrets.baseTestWalletSeeds; |
| 356 | case WalletType.arbitrum: |
| 357 | return secrets.arbitrumTestWalletSeeds; |
| 358 | case WalletType.bsc: |
| 359 | return secrets.bscTestWalletSeeds; |
| 360 | case WalletType.tron: |
| 361 | return secrets.tronTestWalletSeeds; |
| 362 | case WalletType.nano: |
| 363 | return secrets.nanoTestWalletSeeds; |
| 364 | case WalletType.wownero: |
| 365 | return secrets.wowneroTestWalletSeeds; |
| 366 | case WalletType.zano: |
| 367 | return secrets.zanoTestWalletSeeds; |
| 368 | case WalletType.decred: |
| 369 | return secrets.decredTestWalletSeeds; |
| 370 | case WalletType.dogecoin: |
| 371 | return secrets.dogeTestWalletSeeds; |
| 372 | case WalletType.zcash: |
| 373 | return secrets.zcashTestWalletSeeds; |
| 374 | case WalletType.none: |
| 375 | case WalletType.haven: |
| 376 | case WalletType.banano: |
| 377 | throw Exception("Unable to get seeds for ${walletType}"); |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | //* ====== Utility Function to get test receive address for each wallet type ======== |
| 382 | String getReceiveAddressByWalletType(WalletType walletType) { |
| 383 | switch (walletType) { |
| 384 | case WalletType.monero: |
| 385 | return secrets.moneroTestWalletReceiveAddress; |
| 386 | case WalletType.bitcoin: |
| 387 | return secrets.bitcoinTestWalletReceiveAddress; |
| 388 | case WalletType.ethereum: |
| 389 | return secrets.ethereumTestWalletReceiveAddress; |
| 390 | case WalletType.litecoin: |
| 391 | return secrets.litecoinTestWalletReceiveAddress; |
| 392 | case WalletType.bitcoinCash: |
| 393 | return secrets.bitcoinCashTestWalletReceiveAddress; |
| 394 | case WalletType.polygon: |
| 395 | return secrets.polygonTestWalletReceiveAddress; |
| 396 | case WalletType.base: |
| 397 | return secrets.baseTestWalletReceiveAddress; |
| 398 | case WalletType.arbitrum: |
| 399 | return secrets.arbitrumTestWalletReceiveAddress; |
| 400 | case WalletType.bsc: |
| 401 | return secrets.bscTestWalletReceiveAddress; |
| 402 | case WalletType.solana: |
| 403 | return secrets.solanaTestWalletReceiveAddress; |
| 404 | case WalletType.tron: |
| 405 | return secrets.tronTestWalletReceiveAddress; |
| 406 | case WalletType.nano: |
| 407 | return secrets.nanoTestWalletReceiveAddress; |
| 408 | case WalletType.wownero: |
| 409 | return secrets.wowneroTestWalletReceiveAddress; |
| 410 | default: |
| 411 | return ''; |
| 412 | } |
| 413 | } |
| 414 | } |