From 5df59da163842f4502d5b2bb80afc8fa5d76f454 Mon Sep 17 00:00:00 2001 From: vinotestlio Date: Mon, 4 May 2026 12:10:47 +0530 Subject: [PATCH 001/150] uitests pages and tests added --- .github/workflows/ios-xcuitest.yml | 183 +++++++++++++++ ios/BitPayApp.xcodeproj/project.pbxproj | 216 ++++++++++++++++-- .../xcshareddata/xcschemes/BitPayApp.xcscheme | 11 + .../tests/test594_BasicSwapBTC_.swift | 81 +++++++ .../tests/test595_BasicSellBTC_.swift | 74 ++++++ .../tests/test596_BasicBuyBTC_.swift | 82 +++++++ .../AllureReport/AllureXCTestSupport.swift | 75 ++++++ ios/UITests/BitPayApp.xctestplan | 33 +++ ios/UITests/pages/AddCryptoOptionPage.swift | 34 +++ ios/UITests/pages/BuyPage.swift | 37 +++ ios/UITests/pages/ConfirmPaymentPage.swift | 45 ++++ ios/UITests/pages/EnterAmountPage.swift | 57 +++++ ios/UITests/pages/HomePage.swift | 152 ++++++++++++ .../pages/ImportRecoveryPhrasePage.swift | 39 ++++ ios/UITests/pages/MykeyPage.swift | 45 ++++ ios/UITests/pages/NewRecoveryPhrasePage.swift | 89 ++++++++ ios/UITests/pages/OnboardingPage.swift | 174 ++++++++++++++ ios/UITests/pages/PortfolioBalancePage.swift | 82 +++++++ ios/UITests/pages/SelectCurrencyPage.swift | 65 ++++++ .../pages/SelectKeyToDepositPage.swift | 37 +++ ios/UITests/pages/SellPage.swift | 37 +++ ios/UITests/pages/SwapPage.swift | 113 +++++++++ ios/UITests/pages/VerifyYourPhrasePage.swift | 100 ++++++++ .../test591_OnboardingCreateWallet.swift | 96 ++++++++ .../test592_ImportWalletRecoveryPhrase.swift | 83 +++++++ .../test593_BTCConfirmPaymentScreen.swift | 78 +++++++ ios/UITests/tests/test594_BasicSwapBTC_.swift | 81 +++++++ ios/UITests/tests/test595_BasicSellBTC_.swift | 74 ++++++ ios/UITests/tests/test596_BasicBuyBTC_.swift | 82 +++++++ testlio-cli/project-config.json | 10 + testlio-cli/test-config.json | 4 + 31 files changed, 2347 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/ios-xcuitest.yml create mode 100644 ios/BitPayAppUITests/tests/test594_BasicSwapBTC_.swift create mode 100644 ios/BitPayAppUITests/tests/test595_BasicSellBTC_.swift create mode 100644 ios/BitPayAppUITests/tests/test596_BasicBuyBTC_.swift create mode 100644 ios/UITests/AllureReport/AllureXCTestSupport.swift create mode 100644 ios/UITests/BitPayApp.xctestplan create mode 100644 ios/UITests/pages/AddCryptoOptionPage.swift create mode 100644 ios/UITests/pages/BuyPage.swift create mode 100644 ios/UITests/pages/ConfirmPaymentPage.swift create mode 100644 ios/UITests/pages/EnterAmountPage.swift create mode 100644 ios/UITests/pages/HomePage.swift create mode 100644 ios/UITests/pages/ImportRecoveryPhrasePage.swift create mode 100644 ios/UITests/pages/MykeyPage.swift create mode 100644 ios/UITests/pages/NewRecoveryPhrasePage.swift create mode 100644 ios/UITests/pages/OnboardingPage.swift create mode 100644 ios/UITests/pages/PortfolioBalancePage.swift create mode 100644 ios/UITests/pages/SelectCurrencyPage.swift create mode 100644 ios/UITests/pages/SelectKeyToDepositPage.swift create mode 100644 ios/UITests/pages/SellPage.swift create mode 100644 ios/UITests/pages/SwapPage.swift create mode 100644 ios/UITests/pages/VerifyYourPhrasePage.swift create mode 100644 ios/UITests/tests/test591_OnboardingCreateWallet.swift create mode 100644 ios/UITests/tests/test592_ImportWalletRecoveryPhrase.swift create mode 100644 ios/UITests/tests/test593_BTCConfirmPaymentScreen.swift create mode 100644 ios/UITests/tests/test594_BasicSwapBTC_.swift create mode 100644 ios/UITests/tests/test595_BasicSellBTC_.swift create mode 100644 ios/UITests/tests/test596_BasicBuyBTC_.swift create mode 100644 testlio-cli/project-config.json create mode 100644 testlio-cli/test-config.json diff --git a/.github/workflows/ios-xcuitest.yml b/.github/workflows/ios-xcuitest.yml new file mode 100644 index 0000000000..66cba343d5 --- /dev/null +++ b/.github/workflows/ios-xcuitest.yml @@ -0,0 +1,183 @@ +name: iOS XCUITest + +on: + workflow_dispatch: + push: + branches: [main, develop] + +jobs: + ios-ui-tests: + runs-on: macos-latest + timeout-minutes: 180 + + steps: + # Checkout Repo + - name: Checkout Repo + uses: actions/checkout@v4 + + # Select Xcode + - name: Select Xcode 26.0.1 + run: sudo xcode-select -switch /Applications/Xcode_26.0.1.app + + # Setup Node + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: yarn + + # Install JS dependencies + - name: Install JS dependencies + run: yarn install --frozen-lockfile + + # Setup Ruby + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: false + + # Install Gems + - name: Install Gems + run: | + bundle config set deployment false + bundle install + + # Disable Sentry upload + - name: Disable Sentry upload + run: | + echo "SENTRY_DISABLE_AUTO_UPLOAD=true" >> $GITHUB_ENV + + # Install Pods + - name: Install Pods + run: | + cd ios + bundle exec pod install + + # Generate React Native Bundle + - name: Generate React Native Bundle + run: | + npx react-native bundle \ + --entry-file index.js \ + --platform ios \ + --dev false \ + --bundle-output ios/main.jsbundle \ + --assets-dest ios + + # Boot Simulator + - name: Boot Simulator + run: | + xcrun simctl boot "iPhone 17" || true + xcrun simctl bootstatus "iPhone 17" -b + + # Build for testing + - name: Build for testing + run: | + set -o pipefail + xcodebuild \ + -workspace ios/BitPayApp.xcworkspace \ + -scheme BitPayApp \ + -configuration Release \ + -destination 'platform=iOS Simulator,name=iPhone 17' \ + -derivedDataPath Build \ + -only-testing:BitPayAppUITests \ + build-for-testing + + # Run XCUITest + - name: Run XCUITest + run: | + set -o pipefail + xcodebuild \ + -workspace ios/BitPayApp.xcworkspace \ + -scheme BitPayApp \ + -configuration Release \ + -destination 'platform=iOS Simulator,name=iPhone 17' \ + -derivedDataPath Build \ + -resultBundlePath TestResults.xcresult \ + -only-testing:BitPayAppUITests \ + test-without-building + + # Upload XCResult + - name: Upload XCUITest Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: xcuitest-results + path: TestResults.xcresult + + # Install Allure CLI + - name: Install Allure CLI + if: always() + run: brew install allure || true + + # Install allure-xcresult + - name: Install allure-xcresult + if: always() + run: | + if ! command -v allure-xcresult &> /dev/null; then + curl -L -o allure-xcresult https://github.com/kvld/allure-xcresult/releases/download/v1.5.0/allure-xcresult + chmod +x allure-xcresult + sudo mv allure-xcresult /usr/local/bin/ + fi + + # Convert to Allure + - name: Convert XCResult to Allure Results + if: always() + run: | + allure-xcresult \ + --input TestResults.xcresult \ + --output allure-results + + # Zip Allure + - name: Zip Allure Results + if: always() + run: | + zip -r allure-results.zip allure-results + + # Upload Allure Results + - name: Upload Allure Results Artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: allure-results + path: allure-results.zip + + # Generate HTML + - name: Generate Allure HTML Report + if: always() + run: | + allure generate allure-results --clean -o allure-report + + # Upload HTML + - name: Upload Allure HTML Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: allure-html-report + path: allure-report + + # Install Testlio CLI + - name: Install Testlio CLI + if: always() + run: npm install -g @testlio/cli + + # Upload to Testlio + - name: Upload Results to Testlio + if: always() + run: | + export RUN_API_TOKEN="${{ secrets.TESTLIO_RUN_TOKEN }}" + + testlio create-run \ + --testConfig testlio-cli/test-config.json \ + --projectConfig testlio-cli/project-config.json \ + --externalResults true \ + --resultProvider local \ + --automatedDeviceIds 363a5ce7-6cef-436c-8531-6c07195c8fe8 + + testlio parse-run-results \ + --projectConfig testlio-cli/project-config.json \ + --path allure-results.zip \ + --automatedDeviceId 363a5ce7-6cef-436c-8531-6c07195c8fe8 + + testlio finalize-results \ + --projectConfig testlio-cli/project-config.json diff --git a/ios/BitPayApp.xcodeproj/project.pbxproj b/ios/BitPayApp.xcodeproj/project.pbxproj index 1a694d0948..cd2f56b0a9 100644 --- a/ios/BitPayApp.xcodeproj/project.pbxproj +++ b/ios/BitPayApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -14,7 +14,7 @@ 256B92C82AC0ED0D00C1DF97 /* bitpay-recovery-phrase-template.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 256B92C72AC0ED0D00C1DF97 /* bitpay-recovery-phrase-template.pdf */; }; 4054F0E729AEFAF0009BBC9C /* AllowedUrlPrefixProtocol.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4054F0E529AEFAEF009BBC9C /* AllowedUrlPrefixProtocol.mm */; }; 523EFC4FD62CB0CD2FBD02FC /* libPods-BitPayApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B1AA7DD4226DBC3478258808 /* libPods-BitPayApp.a */; }; - 52C9714B9004BCE190EA469D /* BundleHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = E30DDDCF29E5E048D75CCF1A /* BundleHash.swift */; }; + 52C9714B9004BCE190EA469D /* BitPayApp/BundleHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = E30DDDCF29E5E048D75CCF1A /* BitPayApp/BundleHash.swift */; }; 5B31AAFE2786387B002765E1 /* RCTDosh.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B31AAFD2786387B002765E1 /* RCTDosh.mm */; }; 5B31AB5F2786515A002765E1 /* Dosh.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B31AB5E2786515A002765E1 /* Dosh.swift */; }; 5B5631782838854000C395A8 /* PassKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B5631772838854000C395A8 /* PassKit.framework */; }; @@ -32,6 +32,16 @@ F64C0BF216CE4402A51A5ACC /* Archivo-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BCDAB70057C84109A9201936 /* Archivo-Regular.ttf */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + CD12DFFA2FA86D5D004E0E3B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = BitPayApp; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 13B07F961A680F5B00A75B9A /* BitPayApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BitPayApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = BitPayApp/Images.xcassets; sourceTree = ""; }; @@ -52,7 +62,7 @@ 5BCF1C342740B8B2006D872E /* BootSplash.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = BootSplash.storyboard; path = BitPayApp/BootSplash.storyboard; sourceTree = ""; }; 5BEA4BEE2838400700AB2121 /* RCTPaymentPass.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTPaymentPass.h; sourceTree = ""; }; 5BEA4BEF2838402700AB2121 /* RCTPaymentPass.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTPaymentPass.mm; sourceTree = ""; }; - 5D5D40A7E7B5265D652187F9 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = BitPayApp/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 5D5D40A7E7B5265D652187F9 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = BitPayApp/PrivacyInfo.xcprivacy; sourceTree = ""; }; 6D615C4729725FD70063DB8E /* RCTTimer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTimer.mm; sourceTree = ""; }; 6D615C4829725FD70063DB8E /* RCTTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTimer.h; sourceTree = ""; }; 6D649751C2644FDCA18A5547 /* Archivo-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Archivo-Light.ttf"; path = "../assets/fonts/Archivo-Light.ttf"; sourceTree = ""; }; @@ -61,7 +71,8 @@ 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = BitPayApp/AppDelegate.swift; sourceTree = ""; }; B1AA7DD4226DBC3478258808 /* libPods-BitPayApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BitPayApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BCDAB70057C84109A9201936 /* Archivo-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Archivo-Regular.ttf"; path = "../assets/fonts/Archivo-Regular.ttf"; sourceTree = ""; }; - E30DDDCF29E5E048D75CCF1A /* BundleHash.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BitPayApp/BundleHash.swift; sourceTree = ""; }; + CD12DFF42FA86D5D004E0E3B /* BitPayAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BitPayAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + E30DDDCF29E5E048D75CCF1A /* BitPayApp/BundleHash.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BitPayApp/BundleHash.swift; sourceTree = ""; }; E639E0380D05420CD0D64119 /* Pods-BitPayApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BitPayApp.release.xcconfig"; path = "Target Support Files/Pods-BitPayApp/Pods-BitPayApp.release.xcconfig"; sourceTree = ""; }; EC0FDD692CC92E90009E1B35 /* InAppMessageModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InAppMessageModule.mm; sourceTree = ""; }; ECCB92762860B25D00F9CF8B /* SilentPushEvent.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SilentPushEvent.mm; sourceTree = ""; }; @@ -71,6 +82,10 @@ F4435EB64D2448EC885B3036 /* Archivo-Black.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Archivo-Black.ttf"; path = "../assets/fonts/Archivo-Black.ttf"; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + CD12E04A2FA87645004E0E3B /* UITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = UITests; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -81,6 +96,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CD12DFF12FA86D5D004E0E3B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -107,7 +129,7 @@ 5BEA4BEE2838400700AB2121 /* RCTPaymentPass.h */, 5BEA4BEF2838402700AB2121 /* RCTPaymentPass.mm */, 5D5D40A7E7B5265D652187F9 /* PrivacyInfo.xcprivacy */, - E30DDDCF29E5E048D75CCF1A /* BundleHash.swift */, + E30DDDCF29E5E048D75CCF1A /* BitPayApp/BundleHash.swift */, ); name = BitPayApp; sourceTree = ""; @@ -156,12 +178,14 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + CD12E04A2FA87645004E0E3B /* UITests */, 13B07FAE1A68108700A75B9A /* BitPayApp */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 83CBBA001A601CBA00E9B192 /* Products */, 2D16E6871FA4F8E400B85C8A /* Frameworks */, 7950FCCDC90E6306EDD77BCD /* Pods */, 3A6F88D6BF7C4F7A8D93264A /* Resources */, + CD12DFEF2FA86C56004E0E3B /* Recovered References */, ); indentWidth = 2; sourceTree = ""; @@ -172,10 +196,19 @@ isa = PBXGroup; children = ( 13B07F961A680F5B00A75B9A /* BitPayApp.app */, + CD12DFF42FA86D5D004E0E3B /* BitPayAppUITests.xctest */, ); name = Products; sourceTree = ""; }; + CD12DFEF2FA86C56004E0E3B /* Recovered References */ = { + isa = PBXGroup; + children = ( + 6D7C95E62E9007E200F0D481 /* RCTPushPermissionManager.mm */, + ); + name = "Recovered References"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -202,17 +235,45 @@ productReference = 13B07F961A680F5B00A75B9A /* BitPayApp.app */; productType = "com.apple.product-type.application"; }; + CD12DFF32FA86D5D004E0E3B /* BitPayAppUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = CD12DFFE2FA86D5D004E0E3B /* Build configuration list for PBXNativeTarget "BitPayAppUITests" */; + buildPhases = ( + CD12DFF02FA86D5D004E0E3B /* Sources */, + CD12DFF12FA86D5D004E0E3B /* Frameworks */, + CD12DFF22FA86D5D004E0E3B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CD12DFFB2FA86D5D004E0E3B /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + CD12E04A2FA87645004E0E3B /* UITests */, + ); + name = BitPayAppUITests; + packageProductDependencies = ( + ); + productName = BitPayAppUITests; + productReference = CD12DFF42FA86D5D004E0E3B /* BitPayAppUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 2600; LastUpgradeCheck = 1210; TargetAttributes = { 13B07F861A680F5B00A75B9A = { LastSwiftMigration = 1240; }; + CD12DFF32FA86D5D004E0E3B = { + CreatedOnToolsVersion = 26.0.1; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; }; }; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "BitPayApp" */; @@ -229,6 +290,7 @@ projectRoot = ""; targets = ( 13B07F861A680F5B00A75B9A /* BitPayApp */, + CD12DFF32FA86D5D004E0E3B /* BitPayAppUITests */, ); }; /* End PBXProject section */ @@ -252,6 +314,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CD12DFF22FA86D5D004E0E3B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -269,20 +338,6 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nSENTRY_XCODE=\"../node_modules/@sentry/react-native/scripts/sentry-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $SENTRY_XCODE\"\n"; }; - EBB510F948BF432C893398ED /* Upload Debug Symbols to Sentry */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Upload Debug Symbols to Sentry"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export SENTRY_PROPERTIES=\"$SRCROOT/../sentry.properties\"\n/bin/sh \"$SRCROOT/../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh\"\n"; - }; 3C1070649A3322DC1391852F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -313,10 +368,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-BitPayApp/Pods-BitPayApp-resources-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Copy Pods Resources"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-BitPayApp/Pods-BitPayApp-resources-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BitPayApp/Pods-BitPayApp-resources.sh\"\n"; @@ -349,15 +408,33 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-BitPayApp/Pods-BitPayApp-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-BitPayApp/Pods-BitPayApp-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BitPayApp/Pods-BitPayApp-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + EBB510F948BF432C893398ED /* Upload Debug Symbols to Sentry */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Upload Debug Symbols to Sentry"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export SENTRY_PROPERTIES=\"$SRCROOT/../sentry.properties\"\n/bin/sh \"$SRCROOT/../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh\"\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -374,19 +451,33 @@ 6D615C4929725FD70063DB8E /* RCTTimer.mm in Sources */, ECCB92782860B25D00F9CF8B /* SilentPushEvent.mm in Sources */, 5B31AB5F2786515A002765E1 /* Dosh.swift in Sources */, - 52C9714B9004BCE190EA469D /* BundleHash.swift in Sources */, + 52C9714B9004BCE190EA469D /* BitPayApp/BundleHash.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CD12DFF02FA86D5D004E0E3B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + CD12DFFB2FA86D5D004E0E3B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* BitPayApp */; + targetProxy = CD12DFFA2FA86D5D004E0E3B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 4221E54904CA4996330CEB54 /* Pods-BitPayApp.debug.xcconfig */; buildSettings = { ALLOWED_URL_PREFIXES = "ALLOWED-URL-PREFIXES-REPLACE-ME"; - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; @@ -430,7 +521,6 @@ baseConfigurationReference = E639E0380D05420CD0D64119 /* Pods-BitPayApp.release.xcconfig */; buildSettings = { ALLOWED_URL_PREFIXES = "ALLOWED-URL-PREFIXES-REPLACE-ME"; - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = NO; CLANG_ENABLE_MODULES = YES; @@ -645,6 +735,79 @@ }; name = Release; }; + CD12DFFC2FA86D5D004E0E3B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.test.appium.BitPayAppUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = BitPayApp; + }; + name = Debug; + }; + CD12DFFD2FA86D5D004E0E3B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 26.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.test.appium.BitPayAppUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRING_CATALOG_GENERATE_SYMBOLS = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = BitPayApp; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -666,6 +829,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + CD12DFFE2FA86D5D004E0E3B /* Build configuration list for PBXNativeTarget "BitPayAppUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CD12DFFC2FA86D5D004E0E3B /* Debug */, + CD12DFFD2FA86D5D004E0E3B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; diff --git a/ios/BitPayApp.xcodeproj/xcshareddata/xcschemes/BitPayApp.xcscheme b/ios/BitPayApp.xcodeproj/xcshareddata/xcschemes/BitPayApp.xcscheme index 3874813c99..5798fabddf 100644 --- a/ios/BitPayApp.xcodeproj/xcshareddata/xcschemes/BitPayApp.xcscheme +++ b/ios/BitPayApp.xcodeproj/xcshareddata/xcschemes/BitPayApp.xcscheme @@ -38,6 +38,17 @@ ReferencedContainer = "container:BitPayApp.xcodeproj"> + + + + :` convention. + static func addLabel(_ name: String, value: String) { + XCTContext.runActivity(named: "allure.label.\(name):\(value)") { _ in } + } + + /// Sets a custom test name visible in the report. + static func setDisplayName(_ name: String) { + XCTContext.runActivity(named: "allure.name:\(name)") { _ in } + } + + /// Adds a link to an issue tracker entry. + static func addIssue(_ title: String, url: String) { + XCTContext.runActivity(named: "allure.issue.\(title):\(url)") { _ in } + } + + /// Adds a generic hyperlink label. + static func addLink(_ title: String, url: String) { + XCTContext.runActivity(named: "allure.link.\(title):\(url)") { _ in } + } + + /// Adds a plain description to the test. + static func addDescription(_ description: String) { + XCTContext.runActivity(named: "allure.description:\(description)") { _ in } + } + + /// Wraps a code block in an activity that appears as a step in Allure. + @discardableResult + static func step(_ name: String, block: () throws -> T) rethrows -> T { + try XCTContext.runActivity(named: name) { _ in try block() } + } +} + +extension XCTestCase { + /// Attaches a full-screen capture to the current test. Lifetime is `.keepAlways` + /// so it remains available in the `.xcresult` bundle for Allure to render. + func allureAttachScreenshot(name: String = "Screenshot") { + let screenshot = XCUIScreen.main.screenshot() + let attachment = XCTAttachment(screenshot: screenshot) + attachment.name = name + attachment.lifetime = .keepAlways + add(attachment) + } + + /// Attaches a screenshot of the supplied application. + func allureAttachAppScreenshot(app: XCUIApplication, name: String = "App Screenshot") { + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = name + attachment.lifetime = .keepAlways + add(attachment) + } + + /// Captures failure-time artifacts if the current test did not succeed. + func allureCaptureFailureArtifacts(app: XCUIApplication?, name: String = "Failure Screenshot") { + guard let app else { return } + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = name + attachment.lifetime = .keepAlways + add(attachment) + } +} + diff --git a/ios/UITests/BitPayApp.xctestplan b/ios/UITests/BitPayApp.xctestplan new file mode 100644 index 0000000000..fa2bba8979 --- /dev/null +++ b/ios/UITests/BitPayApp.xctestplan @@ -0,0 +1,33 @@ +{ + "configurations" : [ + { + "id" : "B276FD7D-F0C0-4086-A649-A4A594C8C330", + "name" : "Configuration 1", + "options" : { + + } + } + ], + "defaultOptions" : { + "codeCoverage" : false, + "performanceAntipatternCheckerEnabled" : true, + "targetForVariableExpansion" : { + "containerPath" : "container:BitPayApp.xcodeproj", + "identifier" : "13B07F861A680F5B00A75B9A", + "name" : "BitPayApp" + }, + "uiTestingScreenshotsLifetime" : "keepAlways", + "userAttachmentLifetime" : "keepAlways" + }, + "testTargets" : [ + { + "parallelizable" : true, + "target" : { + "containerPath" : "container:BitPayApp.xcodeproj", + "identifier" : "CD620BE32F90C491005CB04D", + "name" : "BitPayAppUITests" + } + } + ], + "version" : 1 +} diff --git a/ios/UITests/pages/AddCryptoOptionPage.swift b/ios/UITests/pages/AddCryptoOptionPage.swift new file mode 100644 index 0000000000..4b26128fee --- /dev/null +++ b/ios/UITests/pages/AddCryptoOptionPage.swift @@ -0,0 +1,34 @@ +import XCTest + +class AddCryptoOptionPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + var selectAnOptionText: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Select an Option'") + ).firstMatch + } + + var importKey: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Import Key'") + ).firstMatch + } + + // MARK: - Actions + + func isSelectAnOptionTitleDisplayed(timeout: TimeInterval = 30) -> Bool { + return selectAnOptionText.waitForExistence(timeout: timeout) + } + + func tapImportKey() { + importKey.tap() + } + +} diff --git a/ios/UITests/pages/BuyPage.swift b/ios/UITests/pages/BuyPage.swift new file mode 100644 index 0000000000..127302d882 --- /dev/null +++ b/ios/UITests/pages/BuyPage.swift @@ -0,0 +1,37 @@ +import XCTest + +class BuyPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var buyTitle: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Buy'") + ).firstMatch + } + + var bitcoin: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Bitcoin'") + ).firstMatch + } + + + // MARK: - Validations + + func isBuyPageTitleDisplayed(timeout: TimeInterval = 180) -> Bool { + return buyTitle.waitForExistence(timeout: timeout) + } + + func tapBitcoin() { + bitcoin.tap() + } + + +} diff --git a/ios/UITests/pages/ConfirmPaymentPage.swift b/ios/UITests/pages/ConfirmPaymentPage.swift new file mode 100644 index 0000000000..fd82fc0aac --- /dev/null +++ b/ios/UITests/pages/ConfirmPaymentPage.swift @@ -0,0 +1,45 @@ + +// +// OnboardingPage.swift +// BitPayApp +// +// Created by vinoth vasu on 16/03/26. +// + +import XCTest + +class ConfirmPaymentPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var confirmPaymentText: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Confirm Payment'") + ).firstMatch + } + + var summaryText: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'SUMMARY'") + ).firstMatch + } + + + // MARK: - Actions + + func isConfirmPaymentTitleDisplayed(timeout: TimeInterval = 180) -> Bool { + return confirmPaymentText.waitForExistence(timeout: timeout) + } + + func isSummaryTextDisplayed(timeout: TimeInterval = 5) -> Bool { + return summaryText.waitForExistence(timeout: timeout) + } + + +} diff --git a/ios/UITests/pages/EnterAmountPage.swift b/ios/UITests/pages/EnterAmountPage.swift new file mode 100644 index 0000000000..1c226e614b --- /dev/null +++ b/ios/UITests/pages/EnterAmountPage.swift @@ -0,0 +1,57 @@ + +// +// OnboardingPage.swift +// BitPayApp +// +// Created by vinoth vasu on 16/03/26. +// + +import XCTest + +class EnterAmountPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var amountField: XCUIElement { + app.textFields.firstMatch + } + + var continueButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label CONTAINS 'Continue'") + ).firstMatch + } + + + // MARK: - Actions + + func enterAmount(amount: String = "0") { + let element = amountField + + element.tap() + + if let currentValue = element.value as? String, + !currentValue.isEmpty { + + let deleteString = String( + repeating: XCUIKeyboardKey.delete.rawValue, + count: currentValue.count + ) + element.typeText(deleteString) + } + + element.typeText(amount) + } + + func tapContinue() { + continueButton.tap() + } + + +} diff --git a/ios/UITests/pages/HomePage.swift b/ios/UITests/pages/HomePage.swift new file mode 100644 index 0000000000..1c6a9139bf --- /dev/null +++ b/ios/UITests/pages/HomePage.swift @@ -0,0 +1,152 @@ +// +// OnboardingPage.swift +// BitPayApp +// +// Created by vinoth vasu on 16/03/26. +// + +import XCTest + +class HomePage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var continueWithoutAnAccountButton: XCUIElement { + app.descendants(matching: .any) + .matching( + NSPredicate(format: "label == 'continue-without-an-account-button'") + ) + .firstMatch + } + + var skipButton: XCUIElement { + app.otherElements["skip-button"].firstMatch + } + + var createKeyButton: XCUIElement { + app.otherElements["create-a-key-button"].firstMatch + } + + var backupKeyLabel: XCUIElement { + app.staticTexts["Would you like to backup your key?"] + } + + var laterButton: XCUIElement { + app.staticTexts["LATER"].firstMatch + } + + var gotItButton: XCUIElement { + app.staticTexts["GOT IT"] + } + + var checkbox1: XCUIElement { + app.otherElements.matching(identifier: "checkboxBorder").element(boundBy: 0) + } + + var checkbox2: XCUIElement { + app.otherElements.matching(identifier: "checkboxBorder").element(boundBy: 1) + } + + var checkbox3: XCUIElement { + app.otherElements.matching(identifier: "checkboxBorder").element(boundBy: 2) + } + + var agreeAndContinueButton: XCUIElement { + app.otherElements["agree-and-continue-button"].firstMatch + } + + var yourPortfolioBalanceText: XCUIElement { + app.otherElements["Portfolio Balance"].firstMatch + } + + var onboardingScrollView: XCUIElement { + app.scrollViews.firstMatch + } + + var buyButton: XCUIElement { + app.buttons.containing(NSPredicate(format: "label CONTAINS 'Buy'")) + .firstMatch + } + + var backUpKey: XCUIElement { + app.staticTexts["BACK UP KEY"] + } + + var addCryptoButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Add crypto wallet'") + ).firstMatch + } + + // MARK: - Actions + + func handleTrackingPermissionIfDisplayed() { + let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + let askAppNotToTrackButton = springboard.buttons["Ask App Not to Track"] + + if askAppNotToTrackButton.waitForExistence(timeout: 30) { + askAppNotToTrackButton.tap() + } + } + + func tapContinuewithoutAnAccount() { + continueWithoutAnAccountButton.tap() + } + + func skipOnboarding() { + skipButton.tap() + } + + func createWallet() { + createKeyButton.tap() + } + + func isBackupKeyLabelDisplayed(timeout: TimeInterval = 25) -> Bool { + return backupKeyLabel.waitForExistence(timeout: timeout) + } + + func tapLater() { + laterButton.tap() + } + + func acceptTerms() { + checkbox1.tap() + checkbox2.tap() + checkbox3.tap() + } + + func tapAgreeAndContinueButton() { + agreeAndContinueButton.tap() + } + + func isYourPortfolioBalanceTextDisplayed(timeout: TimeInterval = 25) -> Bool { + return yourPortfolioBalanceText.waitForExistence(timeout: timeout) + } + + func swipeOnboarding() { + onboardingScrollView.swipeRight() + } + + func tapGotIt() { + gotItButton.tap() + } + + func tapBuyOption() { + buyButton.tap() + } + + func tapBackUpKey() { + backUpKey.tap() + } + + func tapAddCryptoButton() { + addCryptoButton.tap() + } + +} diff --git a/ios/UITests/pages/ImportRecoveryPhrasePage.swift b/ios/UITests/pages/ImportRecoveryPhrasePage.swift new file mode 100644 index 0000000000..1ea89287a5 --- /dev/null +++ b/ios/UITests/pages/ImportRecoveryPhrasePage.swift @@ -0,0 +1,39 @@ +import XCTest + +class ImportRecoveryPhrasePage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var inputRecoveryPhrase: XCUIElement { + app.descendants(matching: .any) + .matching(NSPredicate(format: "label == 'Enter recovery phrase'")) + .firstMatch + } + + var importWalletButton: XCUIElement { + app.descendants(matching: .any) + .matching(NSPredicate(format: "label == 'Import wallet'")) + .firstMatch + } + + // MARK: - Actions + + func isImportScreenDisplayed(timeout: TimeInterval = 5) -> Bool { + return inputRecoveryPhrase.waitForExistence(timeout: timeout) + } + + func enterRecoveryPhrase(_ phrase: String) { + inputRecoveryPhrase.tap() + inputRecoveryPhrase.typeText(phrase) + } + + func tapImportWallet() { + importWalletButton.tap() + } +} diff --git a/ios/UITests/pages/MykeyPage.swift b/ios/UITests/pages/MykeyPage.swift new file mode 100644 index 0000000000..f4e12d2f50 --- /dev/null +++ b/ios/UITests/pages/MykeyPage.swift @@ -0,0 +1,45 @@ +import XCTest + +class MyKeyPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var myKeyTab: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'My Key'") + ).firstMatch + } + + var myWalletsText: XCUIElement { + app.staticTexts.matching( + NSPredicate(format: "label == 'My Wallets'") + ).firstMatch + } + + var bitcoinWallet: XCUIElement { + app.staticTexts.matching( + NSPredicate(format: "label CONTAINS 'Bitcoin'") + ).firstMatch + } + + // MARK: - Validations + + func isMyKeyDisplayed(timeout: TimeInterval = 180) -> Bool { + return myKeyTab.waitForExistence(timeout: timeout) + } + + func isMyWalletsDisplayed(timeout: TimeInterval = 5) -> Bool { + return myWalletsText.waitForExistence(timeout: timeout) + } + + func isBitcoinBTCWalletDisplayed(timeout: TimeInterval = 5) -> Bool { + return bitcoinWallet.waitForExistence(timeout: timeout) + } + +} diff --git a/ios/UITests/pages/NewRecoveryPhrasePage.swift b/ios/UITests/pages/NewRecoveryPhrasePage.swift new file mode 100644 index 0000000000..37ded1f5ba --- /dev/null +++ b/ios/UITests/pages/NewRecoveryPhrasePage.swift @@ -0,0 +1,89 @@ +import XCTest + +class NewRecoveryPhrasePage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var instructionText: XCUIElement { + app.staticTexts.matching( + NSPredicate(format: "label CONTAINS 'recovery phrase'") + ).firstMatch + } + + var textFields: [XCUIElement] { + app.textFields.allElementsBoundByIndex + } + + var confirmButton: XCUIElement { + app.otherElements["Confirm"].firstMatch + } + + var okButton: XCUIElement { + app.staticTexts["OK"].firstMatch + } + + // MARK: - Actions + + func isVerifyScreenDisplayed(timeout: TimeInterval = 10) -> Bool { + return instructionText.waitForExistence(timeout: timeout) + } + + func tapConfirm() { + XCTAssertTrue(confirmButton.waitForExistence(timeout: 5)) + confirmButton.tap() + } + + func tapOK() { + XCTAssertTrue(okButton.waitForExistence(timeout: 5)) + okButton.tap() + } + + // MARK: - Business Logic + + /// Extract indexes dynamically (e.g. 11, 9, 12) + func getRequiredIndexes() -> [Int] { + + XCTAssertTrue(instructionText.waitForExistence(timeout: 5)) + + let instruction = instructionText.label + + return extractNumbers(from: instruction).compactMap { Int($0) } + } + + /// Enter words dynamically + func enterRecoveryPhrase(words: [String]) { + + let indexes = getRequiredIndexes() + + for (i, index) in indexes.enumerated() { + let word = words[index - 1] + + let field = textFields[i] + XCTAssertTrue(field.waitForExistence(timeout: 5)) + + field.tap() + field.typeText(word) + } + + NSLog("Entered words for indexes \(indexes)") + } + + func extractNumbers(from text: String) -> [String] { + do { + let regex = try NSRegularExpression(pattern: "\\d+") + let results = regex.matches( + in: text, + range: NSRange(text.startIndex..., in: text) + ) + return results.map { String(text[Range($0.range, in: text)!]) } + } catch { + return [] + } + } +} diff --git a/ios/UITests/pages/OnboardingPage.swift b/ios/UITests/pages/OnboardingPage.swift new file mode 100644 index 0000000000..51d5d2a8f6 --- /dev/null +++ b/ios/UITests/pages/OnboardingPage.swift @@ -0,0 +1,174 @@ +// +// OnboardingPage.swift +// BitPayApp +// +// Created by vinoth vasu on 10/03/26. +// + +import XCTest + +class OnboardingPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var continueWithoutAnAccountButton: XCUIElement { + app.descendants(matching: .any) + .matching(NSPredicate(format: "label == 'Continue without an account'")) + .firstMatch + } + + var skipButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Skip'") + ).firstMatch + } + + var skipBackupButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Skip backup'") + ).firstMatch + } + + var createKeyButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Create a key'") + ).firstMatch + } + + var backupKeyLabel: XCUIElement { + app.staticTexts["Would you like to backup your key?"] + } + + var laterButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'LATER'") + ).firstMatch + } + + var gotItButton: XCUIElement { + app.staticTexts["GOT IT"] + } + + // var checkbox1: XCUIElement { + // app.otherElements.matching(identifier: "checkboxBorder").element(boundBy: 0) + // } + + var checkbox1: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate( + format: + "label CONTAINS 'Checkbox, My funds are held and controlled on this device'" + ) + ).firstMatch + } + + var checkbox2: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate( + format: + "label CONTAINS 'Checkbox, BitPay can never recover my funds for me'" + ) + ).firstMatch + } + + var checkbox3: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate( + format: + "label CONTAINS 'Checkbox, I have read, understood and accepted the Wallet Terms of Use'" + ) + ).firstMatch + } + + // var checkbox2: XCUIElement { + // app.otherElements.matching(identifier: "checkboxBorder").element(boundBy: 1) + // } + // + // var checkbox3: XCUIElement { + // app.otherElements.matching(identifier: "checkboxBorder").element(boundBy: 2) + // } + + var agreeAndContinueButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Agree and continue'") + ).firstMatch + } + + var yourPortfolioBalanceText: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Portfolio balance info'") + ).firstMatch + } + + var onboardingScrollView: XCUIElement { + app.scrollViews.firstMatch + } + + // MARK: - Actions + + func handleTrackingPermissionIfDisplayed() { + let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + let askAppNotToTrackButton = springboard.buttons["Ask App Not to Track"] + + if askAppNotToTrackButton.waitForExistence(timeout: 30) { + askAppNotToTrackButton.tap() + } + } + + func tapContinuewithoutAnAccount() { + continueWithoutAnAccountButton.tap() + } + + func skipOnboarding() { + skipButton.tap() + } + + func skipBackup() { + skipBackupButton.tap() + } + + func createWallet() { + createKeyButton.tap() + } + + func isBackupKeyLabelDisplayed(timeout: TimeInterval = 120) -> Bool { + return backupKeyLabel.waitForExistence(timeout: timeout) + } + + func tapLater() { + laterButton.tap() + } + + func acceptTerms() { + XCTAssertTrue(checkbox1.waitForExistence(timeout: 10)) + checkbox1.tap() + checkbox2.tap() + + let coordinate = checkbox3.coordinate( + withNormalizedOffset: CGVector(dx: 0.1, dy: 0.5) + ) + coordinate.tap() + } + + func tapAgreeAndContinueButton() { + agreeAndContinueButton.tap() + } + + func isYourPortfolioBalanceTextDisplayed(timeout: TimeInterval = 25) -> Bool { + return yourPortfolioBalanceText.waitForExistence(timeout: timeout) + } + + func swipeOnboarding() { + onboardingScrollView.swipeRight() + } + + func tapGotIt() { + gotItButton.tap() + } +} diff --git a/ios/UITests/pages/PortfolioBalancePage.swift b/ios/UITests/pages/PortfolioBalancePage.swift new file mode 100644 index 0000000000..673a9a3a64 --- /dev/null +++ b/ios/UITests/pages/PortfolioBalancePage.swift @@ -0,0 +1,82 @@ + +// +// OnboardingPage.swift +// BitPayApp +// +// Created by vinoth vasu on 16/03/26. +// + +import XCTest + +class PortfolioBalancePage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var yourPortfolioBalanceText: XCUIElement { + app.otherElements["Portfolio Balance"].firstMatch + } + + + var addCryptoButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Add crypto wallet'") + ).firstMatch + } + + var buyButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Buy'") + ).firstMatch + } + + var sellButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Sell'") + ).firstMatch + } + + var sendButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Send'") + ).firstMatch + } + + var swapButton: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Swap'") + ).firstMatch + } + + // MARK: - Actions + + func isYourPortfolioBalanceTextDisplayed(timeout: TimeInterval = 25) -> Bool { + return yourPortfolioBalanceText.waitForExistence(timeout: timeout) + } + + func tapBuyButton() { + buyButton.tap() + } + + func tapSellButton() { + sellButton.tap() + } + + func tapAddCryptoButton() { + addCryptoButton.tap() + } + + func tapSendButton() { + sendButton.tap() + } + + func tapSwapButton() { + swapButton.tap() + } + +} diff --git a/ios/UITests/pages/SelectCurrencyPage.swift b/ios/UITests/pages/SelectCurrencyPage.swift new file mode 100644 index 0000000000..690a327e82 --- /dev/null +++ b/ios/UITests/pages/SelectCurrencyPage.swift @@ -0,0 +1,65 @@ + +// +// OnboardingPage.swift +// BitPayApp +// +// Created by vinoth vasu on 16/03/26. +// + +import XCTest + +class SelectCurrencyPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var selectCurrencyTitle: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Select a Currency'") + ).firstMatch + } + + var bitcoinCurrency: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label CONTAINS 'Bitcoin, BTC'") + ).firstMatch + } + + var sendToTitle: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Send To'") + ).firstMatch + } + + var recipientAddress: XCUIElement { + app.descendants(matching: .any).element( + matching: NSPredicate(format: "label == 'Recipient address'") + ).firstMatch + } + + + // MARK: - Actions + + func isSelectCurrencyTitleDisplayed(timeout: TimeInterval = 180) -> Bool { + return selectCurrencyTitle.waitForExistence(timeout: timeout) + } + + func tapBitcoinCurrency() { + bitcoinCurrency.tap() + } + + func isSendToTitleDisplayed(timeout: TimeInterval = 180) -> Bool { + return sendToTitle.waitForExistence(timeout: timeout) + } + + func enterRecipientAddress(address: String) { + recipientAddress.tap() + recipientAddress.typeText(address) + } + +} diff --git a/ios/UITests/pages/SelectKeyToDepositPage.swift b/ios/UITests/pages/SelectKeyToDepositPage.swift new file mode 100644 index 0000000000..9f37acdcb4 --- /dev/null +++ b/ios/UITests/pages/SelectKeyToDepositPage.swift @@ -0,0 +1,37 @@ +import XCTest + +class SelectKeyToDepositPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var selectKeyToDeposit: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Select Key to Deposit to'") + ).firstMatch + } + + var myKeyWallet: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'My Key wallet'") + ).firstMatch + } + + + // MARK: - Validations + + func isSelectKeyToDepositToDisplayed(timeout: TimeInterval = 5) -> Bool { + return selectKeyToDeposit.waitForExistence(timeout: timeout) + } + + func tapMyKeyWallet() { + myKeyWallet.tap() + } + + +} diff --git a/ios/UITests/pages/SellPage.swift b/ios/UITests/pages/SellPage.swift new file mode 100644 index 0000000000..7b691165a5 --- /dev/null +++ b/ios/UITests/pages/SellPage.swift @@ -0,0 +1,37 @@ +import XCTest + +class SellPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var sellTitle: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Sell'") + ).firstMatch + } + + var chooseCrypto: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Choose Crypto'") + ).firstMatch + } + + + // MARK: - Validations + + func isSellPageTitleDisplayed(timeout: TimeInterval = 180) -> Bool { + return sellTitle.waitForExistence(timeout: timeout) + } + + func tapChooseCrypto() { + chooseCrypto.tap() + } + + +} diff --git a/ios/UITests/pages/SwapPage.swift b/ios/UITests/pages/SwapPage.swift new file mode 100644 index 0000000000..b642827b34 --- /dev/null +++ b/ios/UITests/pages/SwapPage.swift @@ -0,0 +1,113 @@ +import XCTest + +class SwapPage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var swapTitle: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Swap'") + ).firstMatch + } + + var selectWalletFrom: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Select wallet to swap from'") + ).firstMatch + } + + var cryptoToSwapPage: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Crypto to Swap'") + ).firstMatch + } + + var bitcoin: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Bitcoin, BTC'") + ).firstMatch + } + + var selectWalletTo: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Select wallet to swap to'") + ).firstMatch + } + + var swapTo: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Swap To'") + ).firstMatch + } + + var ethereum: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Ethereum, ETH'") + ).firstMatch + } + + var selectKeyToDeposit: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'Select Key to Deposit to'") + ).firstMatch + } + + var myKeyWallet: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'My Key wallet'") + ).firstMatch + } + + var evmAccount: XCUIElement { + app.otherElements.matching( + NSPredicate(format: "label == 'EVM Account'") + ).firstMatch + } + + + // MARK: - Validations + + func isSwapPageTitleDisplayed(timeout: TimeInterval = 5) -> Bool { + return swapTitle.waitForExistence(timeout: timeout) + } + + func tapSelectWalletFrom() { + selectWalletFrom.tap() + } + + func isCryptoToSwapPageDisplayed(timeout: TimeInterval = 5) -> Bool { + return cryptoToSwapPage.waitForExistence(timeout: timeout) + } + + func tapBitcoin() { + bitcoin.tap() + } + + func tapSelectWalletTo() { + selectWalletTo.tap() + } + + func isSwapToPageDisplayed(timeout: TimeInterval = 5) -> Bool { + return swapTo.waitForExistence(timeout: timeout) + } + + func tapEthereum() { + ethereum.tap() + } + + func isSelectKeyToDepositToDisplayed(timeout: TimeInterval = 5) -> Bool { + return selectKeyToDeposit.waitForExistence(timeout: timeout) + } + + func tapMyKeyWallet() { + myKeyWallet.tap() + } + + +} diff --git a/ios/UITests/pages/VerifyYourPhrasePage.swift b/ios/UITests/pages/VerifyYourPhrasePage.swift new file mode 100644 index 0000000000..dde1166147 --- /dev/null +++ b/ios/UITests/pages/VerifyYourPhrasePage.swift @@ -0,0 +1,100 @@ +import XCTest + +class RecoveryPhrasePage { + + let app: XCUIApplication + + init(app: XCUIApplication) { + self.app = app + } + + // MARK: - Elements + + var recoveryPhraseTitle: XCUIElement { + app.staticTexts["Recovery Phrase"] + } + + var nextButton: XCUIElement { + app.otherElements["next-button"].firstMatch + } + + var allStaticTexts: XCUIElementQuery { + app.staticTexts + } + + var textFields: [XCUIElement] { + app.textFields.allElementsBoundByIndex + } + + // MARK: - Actions + + func isRecoveryPhraseDisplayed(timeout: TimeInterval = 10) -> Bool { + return recoveryPhraseTitle.waitForExistence(timeout: timeout) + } + + func tapNext() { + XCTAssertTrue(nextButton.waitForExistence(timeout: 5)) + nextButton.tap() + } + + // MARK: - Business Logic + + /// Extract 12 recovery words dynamically + func getRecoveryWords() -> [String] { + return allStaticTexts.allElementsBoundByIndex.compactMap { element in + let text = element.label.trimmingCharacters(in: .whitespaces) + + // Only lowercase words (filters headers, numbers) + let isWord = + text.range(of: "^[a-z]+$", options: .regularExpression) != nil + return isWord ? text : nil + } + .prefix(12) + .map { $0 } + } + + func extractNumbers(from text: String) -> [String] { + do { + let regex = try NSRegularExpression(pattern: "\\d+") + let results = regex.matches( + in: text, + range: NSRange(text.startIndex..., in: text) + ) + return results.map { String(text[Range($0.range, in: text)!]) } + } catch { + return [] + } + } + + /// Extract indexes like 2nd, 11th, 9th dynamically + func getRequiredIndexes() -> [Int] { + + let instructionElement = app.staticTexts.matching( + NSPredicate(format: "label CONTAINS 'recovery phrase correctly'") + ).firstMatch + + XCTAssertTrue(instructionElement.waitForExistence(timeout: 5)) + + let instruction = instructionElement.label + + return extractNumbers(from: instruction).compactMap { Int($0) } + } + + /// Enter recovery phrase based on required indexes + func enterRecoveryPhrase(words: [String]) { + + let indexes = getRequiredIndexes() + + for (i, index) in indexes.enumerated() { + let word = words[index - 1] + + let field = textFields[i] + XCTAssertTrue(field.waitForExistence(timeout: 5)) + + field.tap() + field.typeText(word) + } + + NSLog("Entered words for indexes \(indexes)") + } +} diff --git a/ios/UITests/tests/test591_OnboardingCreateWallet.swift b/ios/UITests/tests/test591_OnboardingCreateWallet.swift new file mode 100644 index 0000000000..7bb1afe676 --- /dev/null +++ b/ios/UITests/tests/test591_OnboardingCreateWallet.swift @@ -0,0 +1,96 @@ +import XCTest + +final class OnboardingCreateWallet: XCTestCase { + + var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + + app = XCUIApplication() + app.launch() + + let onboardingPage = OnboardingPage(app: app) + onboardingPage.handleTrackingPermissionIfDisplayed() + } + + @MainActor + func testOnboardingCreateWallet() throws { + + AllureXCTestSupport.setDisplayName( + "Onboarding Create Wallet (Continue Without Account)" + ) + AllureXCTestSupport.addLabel( + "testlioManualTestID", + value: "003e6686-1128-4e66-b020-aa03f3b6ec67" + ) + AllureXCTestSupport.addDescription( + "Onboarding Create Wallet without creating a new" + ) + + let onboardingPage = OnboardingPage(app: app) + + AllureXCTestSupport.step( + "(4) On the onboarding screen “Seamlessly buy & swap”, tap Continue without an account." + ) { + onboardingPage.tapContinuewithoutAnAccount() + } + + AllureXCTestSupport.step( + "(5) On the “Turn on notifications” screen, tap Skip." + ) { + onboardingPage.skipOnboarding() + } + + AllureXCTestSupport.step( + "(6) On the “Protect your wallet” screen, tap Skip" + ) { + onboardingPage.skipOnboarding() //To skip protect wallet + } + + AllureXCTestSupport.step( + "(7) On the “Create a key or import an existing key” screen, tap Create a Key." + ) { + onboardingPage.createWallet() + } + + AllureXCTestSupport.step( + "(8) Wait until wallet creation finishes and the “Would you like to backup your key?” screen is displayed." + ) { + XCTAssertTrue( + onboardingPage.isBackupKeyLabelDisplayed(), + "Backup key page did not appear after tapping 'Create a Key'" + ) + } + + AllureXCTestSupport.step("(9) Tap Skip on the backup prompt screen.") { + onboardingPage.skipBackup() //To skip backup key + } + + AllureXCTestSupport.step( + "(10) On the “Are you sure?” confirmation modal, tap Later. " + ) { + onboardingPage.tapLater() + } + + AllureXCTestSupport.step( + "(11, 12 & 13) On the “Important” screen, select all the three agreements checkbox. " + ) { + onboardingPage.acceptTerms() + } + + AllureXCTestSupport.step("(14) Tap Agree and Continue. ") { + onboardingPage.tapAgreeAndContinueButton() + } + + AllureXCTestSupport.step( + "(15) Wait until the Home screen finishes loading. " + ) { + XCTAssertTrue( + onboardingPage.isYourPortfolioBalanceTextDisplayed(), + "Home Page - Your Portfolio Balance text is not displayed" + ) + } + } + +} diff --git a/ios/UITests/tests/test592_ImportWalletRecoveryPhrase.swift b/ios/UITests/tests/test592_ImportWalletRecoveryPhrase.swift new file mode 100644 index 0000000000..af38ef6f71 --- /dev/null +++ b/ios/UITests/tests/test592_ImportWalletRecoveryPhrase.swift @@ -0,0 +1,83 @@ +import XCTest + +final class ImportWalletRecoveryPhrase: XCTestCase { + + var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + + app = XCUIApplication() + app.launch() + + let onboardingPage = OnboardingPage(app: app) + // onboardingPage.handleTrackingPermissionIfDisplayed() + } + + @MainActor + func testImportWalletRecoveryPhrase() throws { + + AllureXCTestSupport.setDisplayName( + "Import Wallet via Recovery Phrase (BTC)" + ) + AllureXCTestSupport.addLabel( + "testlioManualTestID", + value: "1ca663da-1909-451f-9908-25ba9cdc0396" + ) + AllureXCTestSupport.addDescription( + "Import Wallet via Recovery Phrase (BTC)" + ) + + let portfolioBalancePage = PortfolioBalancePage(app: app) + let selectAnOptionPage = AddCryptoOptionPage(app: app) + let importRecoveryPhasePage = ImportRecoveryPhrasePage(app: app) + let myKeyPage = MyKeyPage(app: app) + + AllureXCTestSupport.step( + "(1 to 3) On the Home screen, tap the Plus (+) control near the “Your Crypto” section to open wallet/key options." + ) { + portfolioBalancePage.tapAddCryptoButton() + } + + AllureXCTestSupport.step( + "(4) Wait until the “Select an Option” screen is displayed." + ) { + XCTAssertTrue( + selectAnOptionPage.isSelectAnOptionTitleDisplayed(), + "Select an option page is not displayed" + ) + } + + AllureXCTestSupport.step("(5) Tap Import Key") { + selectAnOptionPage.tapImportKey() + } + + AllureXCTestSupport.step( + "(6 to 7) On the “Import” screen, tap the Recovery Phrase input field & Enter a valid Recovery Phrase. " + ) { + importRecoveryPhasePage.enterRecoveryPhrase( + "hobby short divert lady spare quit act settle body town license alone" + ) + } + + AllureXCTestSupport.step("(8) Tap Import Wallet") { + importRecoveryPhasePage.tapImportWallet() + } + + AllureXCTestSupport.step( + "(9) Wait until the import finishes and the My Key screen is displayed (use an extended wait to account for slower imports). " + ) { + XCTAssertTrue(myKeyPage.isMyKeyDisplayed(), "Is My Key not displayed") + XCTAssertTrue( + myKeyPage.isMyWalletsDisplayed(), + "My Wallets not displayed" + ) + XCTAssertTrue( + myKeyPage.isBitcoinBTCWalletDisplayed(), + "Bitcoin BTC Wallet not displayed" + ) + } + + } + +} diff --git a/ios/UITests/tests/test593_BTCConfirmPaymentScreen.swift b/ios/UITests/tests/test593_BTCConfirmPaymentScreen.swift new file mode 100644 index 0000000000..c7bcdbc8df --- /dev/null +++ b/ios/UITests/tests/test593_BTCConfirmPaymentScreen.swift @@ -0,0 +1,78 @@ +//import XCTest +// +//final class BTCConfirmPayment: XCTestCase { +// +// var app: XCUIApplication! +// +// override func setUp() { +// continueAfterFailure = false +// +// app = XCUIApplication() +// app.launch() +// +// let onboardingPage = OnboardingPage(app: app) +// onboardingPage.handleTrackingPermissionIfDisplayed() +// } +// +// @MainActor +// func testBTCConfirmPayment() throws { +// +// AllureXCTestSupport.setDisplayName( +// "Basic Send (BTC) - Confirm Payment Screen" +// ) +// AllureXCTestSupport.addLabel( +// "testlioManualTestID", +// value: "be4a044a-2c3a-4cad-aa5c-9398f415b483" +// ) +// AllureXCTestSupport.addDescription( +// "Basic Send (BTC) - Confirm Payment Screen" +// ) +// +// let portfolioBalancePage = PortfolioBalancePage(app: app) +// let selectCurrencyPage = SelectCurrencyPage(app: app) +// let enterAmountPage = EnterAmountPage(app: app) +// let confirmPaymentPage = ConfirmPaymentPage(app: app) +// +// AllureXCTestSupport.step("(1 to 4) On the Home screen, tap Send in the top menu.") { +// portfolioBalancePage.tapSendButton() +// } +// +// AllureXCTestSupport.step("(5) Wait until the Select a Currency screen is displayed. ") { +// XCTAssertTrue( +// selectCurrencyPage.isSelectCurrencyTitleDisplayed(), +// "Select Currency page not displayed" +// ) +// } +// +// AllureXCTestSupport.step("(6) Tap Bitcoin.") { +// selectCurrencyPage.tapBitcoinCurrency() +// } +// +// AllureXCTestSupport.step("(7) Wait until the Send To screen is displayed.") { +// selectCurrencyPage.isSendToTitleDisplayed() +// } +// +// AllureXCTestSupport.step("(8 & 9) Tap the Search contact or enter address field & Paste the receiver address.") { +// selectCurrencyPage.enterRecipientAddress(address: "bc1q0effzahtsn685tyjppgukpvfhv37hrlm4g67ws") +// } +// +// AllureXCTestSupport.step("(10 & 11) Wait until the Amount screen is displayed & Enter 0.00005 BTC.") { +// enterAmountPage.enterAmount(amount: "0.00005") +// } +// +// AllureXCTestSupport.step("(12) Tap Continue and Verify") { +// enterAmountPage.tapContinue() +// +// XCTAssertTrue( +// confirmPaymentPage.isConfirmPaymentTitleDisplayed(), +// "Confirm payment page not displayed" +// ) +// +// XCTAssertTrue( +// confirmPaymentPage.isSummaryTextDisplayed(), +// "Confirm payment - Summary text not displayed" +// ) +// +// } +// } +//} diff --git a/ios/UITests/tests/test594_BasicSwapBTC_.swift b/ios/UITests/tests/test594_BasicSwapBTC_.swift new file mode 100644 index 0000000000..d7c2707aaa --- /dev/null +++ b/ios/UITests/tests/test594_BasicSwapBTC_.swift @@ -0,0 +1,81 @@ +//import XCTest +// +//final class BasicSwapBTC: XCTestCase { +// +// var app: XCUIApplication! +// +// override func setUp() { +// continueAfterFailure = false +// +// app = XCUIApplication() +// app.launch() +// +// let onboardingPage = OnboardingPage(app: app) +// onboardingPage.handleTrackingPermissionIfDisplayed() +// } +// +// @MainActor +// func testBasicSwapBTC() throws { +// +// AllureXCTestSupport.setDisplayName( +// "Basic Swap BTC to ETH" +// ) +// AllureXCTestSupport.addLabel( +// "testlioManualTestID", +// value: "947f685f-e106-4708-922f-43419f9f8f50" +// ) +// AllureXCTestSupport.addDescription( +// "Basic Swap BTC to ETH (View Offers)" +// ) +// +// let portfolioBalancePage = PortfolioBalancePage(app: app) +// let swapPage = SwapPage(app: app) +// let selectKeyToDepositPage = SelectKeyToDepositPage(app: app) +// let enterAmountPage = EnterAmountPage(app: app) +// let selectCurrencyPage = SelectCurrencyPage(app: app) +// +// AllureXCTestSupport.step("(1 to 4) From the Home screen, tap Swap in the top menu. ") { +// portfolioBalancePage.tapSwapButton() +// } +// +// AllureXCTestSupport.step("(5) Wait until the Swap Crypto screen is displayed.") { +// XCTAssertTrue(swapPage.isSwapPageTitleDisplayed(), "Swap page not displayed") +// } +// +// AllureXCTestSupport.step("(6) In the Swap From section, tap Select Wallet") { +// swapPage.tapSelectWalletFrom() +// } +// +// AllureXCTestSupport.step("(7) On the Crypto to Swap screen, tap Bitcoin (BTC)") { +// swapPage.tapBitcoin() +// } +// +// AllureXCTestSupport.step("(8) Back on the Swap Crypto screen, in the Swap To section, tap Select Crypto.") { +// swapPage.tapSelectWalletTo() +// } +// +// AllureXCTestSupport.step("(9) On the Swap To list screen, tap Ethereum (ETH).") { +// swapPage.tapEthereum() +// } +// +// AllureXCTestSupport.step("(10) On the Select Key to Deposit to screen, tap My Key (choose the entry that has funds available if multiple are shown).") { +// XCTAssertTrue(selectKeyToDepositPage.isSelectKeyToDepositToDisplayed(), "Select Key to Deposit to dialog not displayed") +// selectKeyToDepositPage.tapMyKeyWallet() +// } +// +// AllureXCTestSupport.step("(11) On the Swap Crypto screen, tap Enter Amount.") { +// } +// +// AllureXCTestSupport.step("(12) On the Swap Amount bottom sheet, enter 0.0006 BTC.") { +// } +// +// AllureXCTestSupport.step("(13) Tap Continue.") { +// } +// +// AllureXCTestSupport.step("(14) Tap View Offers.") { +// } +// +// AllureXCTestSupport.step("(15) Wait until the Offers screen is displayed. ") { +// } +// } +//} diff --git a/ios/UITests/tests/test595_BasicSellBTC_.swift b/ios/UITests/tests/test595_BasicSellBTC_.swift new file mode 100644 index 0000000000..3612bc7f7a --- /dev/null +++ b/ios/UITests/tests/test595_BasicSellBTC_.swift @@ -0,0 +1,74 @@ +//import XCTest +// +//final class BasicSellBTC: XCTestCase { +// +// var app: XCUIApplication! +// +// override func setUp() { +// continueAfterFailure = false +// +// app = XCUIApplication() +// app.launch() +// +// let onboardingPage = OnboardingPage(app: app) +// onboardingPage.handleTrackingPermissionIfDisplayed() +// } +// +// @MainActor +// func testBasicSellBTC() throws { +// +// AllureXCTestSupport.setDisplayName( +// "Basic Sell BTC" +// ) +// AllureXCTestSupport.addLabel( +// "testlioManualTestID", +// value: "dfc4301a-fad9-4d7f-a13a-91ca1f1d54a7" +// ) +// AllureXCTestSupport.addDescription( +// "Basic Sell BTC (Continue With Opens Browser)" +// ) +// +// let portfolioBalancePage = PortfolioBalancePage(app: app) +// let sellPage = SellPage(app: app) +// let enterAmountPage = EnterAmountPage(app: app) +// let selectCurrencyPage = SelectCurrencyPage(app: app) +// +// AllureXCTestSupport.step("(1 to 4) From the Home screen, tap Sell.") { +// portfolioBalancePage.tapSellButton() +// } +// +// AllureXCTestSupport.step("(5) Wait until the Sell screen is displayed.") { +// XCTAssertTrue(sellPage.isSellPageTitleDisplayed(), "Sell page not displayed") +// } +// +// AllureXCTestSupport.step("(6) Enter $30 as the sell amount.") { +// enterAmountPage.enterAmount(amount: "30") +// } +// +// AllureXCTestSupport.step("(7) Tap Choose Crypto.") { +// sellPage.tapChooseCrypto() +// } +// +// AllureXCTestSupport.step("(8) On the Select Crypto screen, tap Bitcoin (BTC). ") { +// selectCurrencyPage.tapBitcoinCurrency() +// } +// +// AllureXCTestSupport.step("(9) Tap Continue with.") { +// enterAmountPage.tapContinue() +// } +// +// AllureXCTestSupport.step("(10) Wait for the browser / in-app webview to open. ") { +// // +// // XCTAssertTrue( +// // confirmPaymentPage.isConfirmPaymentTitleDisplayed(), +// // "Confirm payment page not displayed" +// // ) +// // +// // XCTAssertTrue( +// // confirmPaymentPage.isSummaryTextDisplayed(), +// // "Confirm payment - Summary text not displayed" +// // ) +// +// } +// } +//} diff --git a/ios/UITests/tests/test596_BasicBuyBTC_.swift b/ios/UITests/tests/test596_BasicBuyBTC_.swift new file mode 100644 index 0000000000..8ce96f218d --- /dev/null +++ b/ios/UITests/tests/test596_BasicBuyBTC_.swift @@ -0,0 +1,82 @@ +//import XCTest +// +//final class BasicBuyBTC: XCTestCase { +// +// var app: XCUIApplication! +// +// override func setUp() { +// continueAfterFailure = false +// +// app = XCUIApplication() +// app.launch() +// +// let onboardingPage = OnboardingPage(app: app) +// onboardingPage.handleTrackingPermissionIfDisplayed() +// } +// +// @MainActor +// func testBasicBuyBTC() throws { +// +// AllureXCTestSupport.setDisplayName( +// "Basic Buy BTC" +// ) +// AllureXCTestSupport.addLabel( +// "testlioManualTestID", +// value: "3c9c0271-76ba-4da3-88ad-8a5d6ea60211" +// ) +// AllureXCTestSupport.addDescription( +// "Basic Buy BTC (Best Offer → Continue With Opens Browser)" +// ) +// +// let portfolioBalancePage = PortfolioBalancePage(app: app) +// let buyPage = BuyPage(app: app) +// let selectKeyToDepositPage = SelectKeyToDepositPage(app: app) +// let enterAmountPage = EnterAmountPage(app: app) +// let selectCurrencyPage = SelectCurrencyPage(app: app) +// +// AllureXCTestSupport.step("(1 to 4) From the Home screen, tap Buy.") { +// portfolioBalancePage.tapBuyButton() +// } +// +// AllureXCTestSupport.step("(5) Wait until the Buy screen is displayed. ") { +// XCTAssertTrue(buyPage.isBuyPageTitleDisplayed(), "Buy page not displayed") +// } +// +// AllureXCTestSupport.step("(6) Enter $30 as the buy amount.") { +// enterAmountPage.enterAmount(amount: "30") +// } +// +// AllureXCTestSupport.step("(7) Tap Choose Crypto.") { +// buyPage.tapBitcoin() +// } +// +// AllureXCTestSupport.step("(8) On the Select Crypto screen, tap Bitcoin (BTC). ") { +// selectCurrencyPage.tapBitcoinCurrency() +// } +// +// AllureXCTestSupport.step("(9) On the Select Key to Deposit to screen, tap My Key with funds available.") { +// XCTAssertTrue(selectKeyToDepositPage.isSelectKeyToDepositToDisplayed(), "Select Key to Deposit to dialog not displayed") +// selectKeyToDepositPage.tapMyKeyWallet() +// } +// +// AllureXCTestSupport.step("(10) Wait until best-offer search completes and at least one offer is displayed.") { +// // +// // XCTAssertTrue( +// // confirmPaymentPage.isConfirmPaymentTitleDisplayed(), +// // "Confirm payment page not displayed" +// // ) +// // +// // XCTAssertTrue( +// // confirmPaymentPage.isSummaryTextDisplayed(), +// // "Confirm payment - Summary text not displayed" +// // ) +// +// } +// +// AllureXCTestSupport.step("(11) Tap Continue with {ProviderName}.") { +// } +// +// AllureXCTestSupport.step("(12) Wait for the browser / in-app webview to open.") { +// } +// } +//} diff --git a/testlio-cli/project-config.json b/testlio-cli/project-config.json new file mode 100644 index 0000000000..1ea5da9269 --- /dev/null +++ b/testlio-cli/project-config.json @@ -0,0 +1,10 @@ +{ + "baseURI": "https://api.testlio.com/", + "platformURI": "https://app.testlio.com/tmt/project/", + "projectId": "3307", + "automatedRunCollectionGuid": "2df42fa4-225b-430e-882d-e5fc3ec31de7", + "testRunCollectionGuid": "a77e695c-5061-4451-98fe-4f93b8a26d9b", + "resultCollectionGuid": "3688212d-e132-40a3-a4bd-aae8b664a2d8", + "workspaceName": "bitpay---sandbox" +} + diff --git a/testlio-cli/test-config.json b/testlio-cli/test-config.json new file mode 100644 index 0000000000..a22b7fcc48 --- /dev/null +++ b/testlio-cli/test-config.json @@ -0,0 +1,4 @@ +{ + "automatedTestNamePrefix": "Automation Run: iOS __BUILD_VERSION__" +} + From 0538771f1aa13f5891f3ae8a23f9f51bdd54b493 Mon Sep 17 00:00:00 2001 From: vinotestlio Date: Mon, 4 May 2026 16:40:30 +0530 Subject: [PATCH 002/150] master branch updated --- .github/workflows/ios-xcuitest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ios-xcuitest.yml b/.github/workflows/ios-xcuitest.yml index 66cba343d5..fa91dae7cf 100644 --- a/.github/workflows/ios-xcuitest.yml +++ b/.github/workflows/ios-xcuitest.yml @@ -3,7 +3,7 @@ name: iOS XCUITest on: workflow_dispatch: push: - branches: [main, develop] + branches: [master, develop] jobs: ios-ui-tests: From 656b12f4dbe2df4bfaee09fa86328ae76173a537 Mon Sep 17 00:00:00 2001 From: vinotestlio Date: Mon, 4 May 2026 16:49:56 +0530 Subject: [PATCH 003/150] pull request codn added --- .github/workflows/ios-xcuitest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ios-xcuitest.yml b/.github/workflows/ios-xcuitest.yml index fa91dae7cf..77875e387a 100644 --- a/.github/workflows/ios-xcuitest.yml +++ b/.github/workflows/ios-xcuitest.yml @@ -4,6 +4,8 @@ on: workflow_dispatch: push: branches: [master, develop] + pull_request: + branches: [master, develop] jobs: ios-ui-tests: From 8c918507ddb6fdd0cdd9ce5432f8d1d4fca366dc Mon Sep 17 00:00:00 2001 From: vinotestlio Date: Thu, 7 May 2026 19:52:42 +0530 Subject: [PATCH 004/150] master src merge --- .../onboarding/screens/TermsOfUse.tsx | 16 - .../ExternalServicesLoadingWalletSkeleton.tsx | 38 ++ .../tabs/home/components/AssetRow.tsx | 5 - .../tabs/home/hooks/usePortfolioAssetRows.ts | 394 ------------------ src/navigation/wallet/screens/send/SendTo.tsx | 4 +- src/portfolio/core/pnl/types.ts | 43 ++ src/store/wallet/effects/import/import.ts | 21 - src/store/wallet/effects/status/status.ts | 16 - 8 files changed, 83 insertions(+), 454 deletions(-) create mode 100644 src/navigation/services/components/ExternalServicesLoadingWalletSkeleton.tsx delete mode 100644 src/navigation/tabs/home/hooks/usePortfolioAssetRows.ts create mode 100644 src/portfolio/core/pnl/types.ts diff --git a/src/navigation/onboarding/screens/TermsOfUse.tsx b/src/navigation/onboarding/screens/TermsOfUse.tsx index 44e0c73eed..1b2a900025 100644 --- a/src/navigation/onboarding/screens/TermsOfUse.tsx +++ b/src/navigation/onboarding/screens/TermsOfUse.tsx @@ -175,21 +175,6 @@ const TermsOfUse: React.FC = ({route, navigation}) => {