Skip to content

iOS uitests added - #2157

Open
vinotestlio wants to merge 157 commits into
bitpay:developfrom
vinotestlio:testlio/ui-tests
Open

vinotestlio wants to merge 157 commits into
bitpay:developfrom
vinotestlio:testlio/ui-tests

Conversation

@vinotestlio

@vinotestlio vinotestlio commented May 4, 2026

Copy link
Copy Markdown
  • Added XCUITest smoke tests
  • Implemented Page Object Model (POM) structure with relevant pages
  • Integrated Allure reporting for test results
  • Added GitHub Actions workflow to execute tests on macOS runner

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an iOS XCUITest smoke-test setup to the BitPay app, introducing a Page Object Model (POM) layer, Allure-compatible test metadata, and a GitHub Actions workflow intended to run UI tests on macOS CI and publish results (artifacts + Testlio).

Changes:

  • Introduces new UI test target wiring in the Xcode project/scheme and adds an .xctestplan.
  • Adds POM-style page objects and initial UI test cases (plus additional placeholder tests currently checked in as fully commented-out Swift files).
  • Adds CI workflow to build/run XCUITests, convert .xcresult to Allure artifacts, and upload results to Testlio using committed CLI config.

Reviewed changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
testlio-cli/test-config.json Testlio CLI test run naming configuration.
testlio-cli/project-config.json Testlio CLI project/workspace GUID configuration.
ios/UITests/tests/test591_OnboardingCreateWallet.swift Active XCUITest covering onboarding wallet creation flow.
ios/UITests/tests/test592_ImportWalletRecoveryPhrase.swift Active XCUITest covering wallet import via recovery phrase.
ios/UITests/tests/test593_BTCConfirmPaymentScreen.swift Placeholder UI test file (currently fully commented out).
ios/UITests/tests/test594_BasicSwapBTC_.swift Placeholder UI test file (currently fully commented out).
ios/UITests/tests/test595_BasicSellBTC_.swift Placeholder UI test file (currently fully commented out).
ios/UITests/tests/test596_BasicBuyBTC_.swift Placeholder UI test file (currently fully commented out).
ios/UITests/pages/AddCryptoOptionPage.swift POM for “Select an Option” wallet/key options screen.
ios/UITests/pages/BuyPage.swift POM for Buy flow entry screen.
ios/UITests/pages/ConfirmPaymentPage.swift POM for confirm payment screen assertions.
ios/UITests/pages/EnterAmountPage.swift POM for entering amount and continuing.
ios/UITests/pages/HomePage.swift POM for onboarding/home-related controls (includes tracking prompt handler).
ios/UITests/pages/ImportRecoveryPhrasePage.swift POM for import by recovery phrase.
ios/UITests/pages/MykeyPage.swift POM for My Key screen validations.
ios/UITests/pages/NewRecoveryPhrasePage.swift POM helper logic to parse required word indexes and fill recovery phrase fields.
ios/UITests/pages/OnboardingPage.swift POM for onboarding steps (continue/skip/create key/accept terms).
ios/UITests/pages/PortfolioBalancePage.swift POM for portfolio balance screen navigation actions (buy/sell/send/swap/add crypto).
ios/UITests/pages/SelectCurrencyPage.swift POM for selecting currency and entering recipient address.
ios/UITests/pages/SelectKeyToDepositPage.swift POM for key selection modal during buy/swap flows.
ios/UITests/pages/SellPage.swift POM for Sell flow entry screen.
ios/UITests/pages/SwapPage.swift POM for Swap flow entry and selections.
ios/UITests/pages/VerifyYourPhrasePage.swift POM for recovery phrase verification (file/class naming mismatch).
ios/UITests/AllureReport/AllureXCTestSupport.swift Allure metadata helpers and screenshot attachment helpers for XCTest.
ios/UITests/BitPayApp.xctestplan UI test plan (currently references a stale/nonexistent target identifier).
ios/BitPayAppUITests/tests/test594_BasicSwapBTC_.swift Placeholder UI test file (currently fully commented out).
ios/BitPayAppUITests/tests/test595_BasicSellBTC_.swift Placeholder UI test file (currently fully commented out).
ios/BitPayAppUITests/tests/test596_BasicBuyBTC_.swift Placeholder UI test file (currently fully commented out).
ios/BitPayApp.xcodeproj/xcshareddata/xcschemes/BitPayApp.xcscheme Adds BitPayAppUITests as a testable reference in the shared scheme.
ios/BitPayApp.xcodeproj/project.pbxproj Adds a new UI test target and updates project format settings.
.github/workflows/ios-xcuitest.yml CI workflow to run XCUITests, generate Allure artifacts, and upload to Testlio.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/ios-xcuitest.yml
Comment thread ios/BitPayApp.xcodeproj/project.pbxproj Outdated
Comment thread ios/UITests/BitPayApp.xctestplan
Comment thread ios/BitPayAppUITests/pages/VerifyYourPhrasePage.swift
Comment thread ios/UITests/tests/test592_ImportWalletRecoveryPhrase.swift Outdated
Comment thread ios/BitPayAppUITests/tests/test594_BasicSwapBTC_.swift Outdated
Comment thread ios/BitPayAppUITests/tests/test595_BasicSellBTC_.swift Outdated
Comment thread ios/BitPayAppUITests/tests/test596_BasicBuyBTC_.swift Outdated
Comment thread ios/BitPayAppUITests/tests/Test591_OnboardingCreateWallet.swift
Comment thread ios/BitPayAppUITests/tests/Test592_ImportWalletRecoveryPhrase.swift
@JohnathanWhite
JohnathanWhite changed the base branch from master to develop May 6, 2026 15:39
@JohnathanWhite
JohnathanWhite requested a review from Copilot May 6, 2026 15:39
vinotestlio added 21 commits May 7, 2026 19:46
…estlio/ui-tests

* 'develop' of https://github.com/bitpay/bitpay-app: (78 commits)
  fix: use live wallet fiat balances in asset list item aggregate balance
  fix: better handle malformed balances
  ref: run portfolio math in worklet
  fix: preserve extrema when downsampling rate data
  fix: normalize PnL of first point in 'All' series
  ref: include hours and minutes in 'All' timeframe timestamps if shorter than 3M
  ref: stop truncating trailing 0s
  fix: do not show balance chart in wallet details if balance is 0
  fix: use more granular rates for the 'All' timeframe
  ref: split balance history and price chart screens
  fix: hide gain timeframe selector when app goes to background
  fix: omit coin query param from rates fetches and fix pol tokens and only fetch rates for chartable assets
  feat: display asset balance history charts
  fix: fix misc ts errors
  ref: make 1D timeframe the default
  fix: prevent duplicate wallets in asset list
  fix: hide balance charts for testnet wallets and use smaller font size for large balances
  feat: add balance charts
  TSS: Enhancement - simplify TSS session ID format to txpId:input
  Braze: Fix - Ensure new users start with email notifications subscribed
  ...

# Conflicts:
#	android/app/build.gradle
#	ios/BitPayApp.xcodeproj/project.pbxproj
#	ios/BitPayApp/Info.plist
#	package.json
#	src/components/checkbox/Checkbox.tsx
#	src/navigation/onboarding/screens/CreateKey.tsx
#	src/navigation/onboarding/screens/TermsOfUse.tsx
#	src/navigation/services/swap-crypto/components/BottomAmount.tsx
#	src/navigation/tabs/home/components/AssetRow.tsx
#	src/navigation/tabs/home/components/LinkingButtons.tsx
#	src/navigation/tabs/home/components/PortfolioBalance.tsx
#	src/navigation/tabs/home/hooks/usePortfolioAssetRows.ts
#	src/navigation/tabs/settings/about/screens/PortfolioWalletDebug.tsx
#	src/navigation/wallet/screens/WalletDetails.tsx
#	src/store/wallet/effects/tss-send/tss-send.ts
…estlio/ui-tests

* 'develop' of https://github.com/bitpay/bitpay-app:
  TSS: Fix - Cancel ceremony when unmounting mid-join (new-join path)
  TSS: Fix - Prevent keygen endpoint spam from leaked polling intervals
…estlio/ui-tests

* 'develop' of https://github.com/bitpay/bitpay-app:
  fix: hide balance charts and asset lists if no non-zero balance wallets
  fix: use correct bwc version for tx history requests
- Cache iOS Pods dependencies using Podfile.lock to reduce network requests
- Add retry mechanism (3 attempts with 30s backoff) to handle 429 rate limit errors from Sumsub Maven repository
- Improves reliability and speeds up pod installation on subsequent runs

@JohnathanWhite JohnathanWhite left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The branch merged master while targeting develop, so GitHub's "Files changed" tab is computed against a stale merge base and does not show all of the production changessrc/navigation/wallet/screens/send/SendTo.tsx is missing from it entirely. Diff against the real merge base (git diff $(git merge-base upstream/develop <branch>) <branch>) to see them: 66 files, not 205.


Blocking

1. SendTo.tsx — the new email regex is a catastrophic-backtracking hang in the Send flow

src/navigation/wallet/screens/send/SendTo.tsx:157

-  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(text);
+  const reg = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w\w+)+$/;
+  return reg.test(text);

The replacement has nested quantifiers ((\w+)* twice). Measured on Node 22:

input new regex old regex
"a"*18 + "@" + "a"*18 + "!" 77,724 ms < 0.01 ms
"a"*20 + "@" + "a"*20 + "!" did not finish in 2 min < 0.01 ms

It's reachable on the hot path. isEmailAddress is called from validateAndNavigateToConfirm, which is wired to:

  • SendTo.tsx:629SearchInput onChangeText, i.e. every keystroke in "Search contact or enter address"
  • SendTo.tsx:647onScanComplete, i.e. whatever a scanned QR code contains

So a signed-in user typing or pasting any <word chars>@<word chars><non-word> string into the Send recipient field freezes the JS thread, and a hostile QR code does it without the user typing anything. The old pattern has no nested quantifier and cannot backtrack this way.

It's also a behaviour regression independent of the hang — measured, user+tag@example.com returns true under the old regex and false under the new one, so plus-addressed recipients stop being recognised.

I can't find anything in this PR that needs this change. Please revert the hunk. Worth flagging: this file merges into develop cleanly — no conflict — so it would land silently.

2. Generated yarn set:dev output was committed

Three separate production-reaching defects share one root cause. yarn set:dev runs yarn set-all (which rewrites the pbxproj) and then scripts/dev-overrides.js, and the resulting working-tree changes are in the commit.

a. android/app/src/main/res/xml/network_security_config.xml:3-4cleartextTrafficPermitted flipped falsetrue on both base-config and domain-config. scripts/dev-overrides.js:12-15 does exactly this replaceAll. The file is referenced from src/main, so this ships: it disables Android's HTTPS enforcement app-wide, in a cryptocurrency wallet, in release builds.

b. ios/BitPayApp.xcodeproj/project.pbxproj, both app configsALLOWED_URL_PREFIXES = "ALLOWED-URL-PREFIXES-REPLACE-ME" has been overwritten with a generated literal list in both Debug and Release. develop has 2 occurrences of that sentinel; this branch has 0. scripts/allowed-url-prefixes.js works by replacing the sentinel, so yarn set:prod is now a silent no-op and the allowlist is frozen at whatever your local set:dev produced. That list is missing the Sentry ingest domain (the script derives it from SENTRY_DSN at build time), so AllowedUrlPrefixProtocol will block Sentry in production.

c. Same file, the app target's "Bundle React Native code and images" phaseexport SENTRY_DISABLE_AUTO_UPLOAD=true has been prepended, unconditionally, for every configuration. Every release archive stops uploading JS bundles and source maps, so production JS crashes arrive unsymbolicated. The workflow already sets this via $GITHUB_ENV; it doesn't need to be in the project file.

(b) and (c) independently blind production crash reporting, which is a bad thing to ship quietly.

Fix: git checkout origin/develop -- ios/BitPayApp.xcodeproj/project.pbxproj android/app/src/main/res/xml/network_security_config.xml, then re-apply only the deliberate Xcode changes (the new BitPayAppUITests target and its scheme entry). Please double-check git status before committing after any yarn set:dev.

3. android/build.gradle:56-97gradle.afterProject rewrites packaging for the release APK

The minSdkVersion guard is fine (:app is 30, so it never fires). The packagingOptions block has no such guard and applies to every subproject including :app, so it reaches the release APK:

  • pickFirst on libhermes.so, libjsi.so, libreactnative.so, libc++_shared.so, libfbjni.so — silences duplicate-native-library errors by picking an arbitrary copy. That error is the guard against an ABI mismatch between a vendored module's copy and react-android's; silencing it globally converts a build failure into a runtime crash that only shows up on user devices.
  • excludes += ['META-INF/LICENSE', 'META-INF/NOTICE', 'META-INF/LICENSE.md', ...] — strips third-party licence and notice files from the shipping APK. Apache-2.0 requires NOTICE redistribution, so this is a compliance problem, not just tidiness.
  • excludes += 'META-INF/*.kotlin_module' — can break Kotlin top-level function resolution and reflection at runtime.
  • compileSdkVersion 36 forced onto any subproject declaring < 34, compiling third-party modules against an SDK they never declared.

If this is only needed to build assembleDebugAndroidTest, please scope it — if (proj.name == 'app') plus a variant filter, or move it into the androidTest configuration. A global afterProject hook that silently rewrites every module's packaging is a lot of blast radius for a test PR, and it's also one of the 14 merge conflicts (below).

4. A real BIP39 mnemonic is committed in plaintext

hobby short divert lady spare quit act settle body town license alone — a valid mainnet BIP39 phrase, in 5 Android test files and 1 iOS test file, in a public repo.

I checked: BIP44, BIP49 and BIP84 BTC addresses and the first three m/44'/60' ETH addresses are all empty right now, so nothing is at immediate risk — but:

  • The wallet is permanently burned. If anyone funds it later (and Test596BuyBTC step 9 literally says "tap My Key with funds available", so the suite expects it to be funded), those funds are spendable by anyone who reads this PR.
  • It leaves a second way: Test592ImportWalletRecoveryPhrase.kt takes allureScreenshot("Recovery phrase entered") with the phrase on screen, and that screenshot is uploaded to Testlio and as a CI artifact.
  • And a third: android-espresso.yml uploads the full adb logcat as the device-logcat artifact. The workflow's own comment at line 95 says RN dev logging serialises "every dispatched action" and that "a recovery-phrase import fires hundreds of actions". DISABLE_DEVELOPMENT_LOGGING=true is the only thing between the phrase and that artifact, on a debug build.
  • It will trip secret scanners, and it can't be removed by a later commit.

Please treat it as compromised, generate a fresh fixture, and inject it via a GitHub secret rather than committing it. Same for bc1q0effzahtsn685tyjppgukpvfhv37hrlm4g67ws, the hardcoded send destination — that's a live mainnet address with 5 transactions and 181,650 sat received, now publicly linked to BitPay's test infrastructure.

5. The branch doesn't merge into develop

14 conflicts: android/app/build.gradle, android/build.gradle, ios/BitPayApp/Info.plist, ios/Podfile.lock, package.json, src/lib/Braze/index.ts, src/navigation/wallet/screens/WalletDetails.tsx, AssetBalanceHistoryScreen.spec.tsx, portfolioChartVisibility.spec.tsx, app.effects.spec.ts, bitpay-id.effects.spec.ts, bitpay-id.effects.ts, wallet.spec.ts, wallet.reducer.spec.ts.

The cause is c75df96 / 7d5d0e9, which merged master into a branch targeting develop. Relative to the merge base this branch reverts test coverage that is current on develop:

  • src/store/wallet/utils/wallet.spec.ts — the whole buildWalletObj block (4 tests covering tssKeyId/tssMetadata propagation and the readonly keyId fallback)
  • src/store/bitpay-id/bitpay-id.effects.spec.ts — 5 Cloudflare-challenge tests plus the startLogin describe, and the Braze merge-failure test

Those all conflict, so they can't land silently — but whoever resolves needs to take develop's side on every one of them. Please merge develop (not master) into the branch and resolve before this is reviewable.


Non-blocking, but please address

Tests that can't fail

  • ios/.../Test596_BasicBuyBTC_.swift:67-74 — steps 10, 11 and 12 have empty bodies. The test's own description is "Best Offer → Continue With Opens Browser" and none of that is exercised or asserted. As written it passes whether or not offers load.
  • ios/.../Test593_BTCConfirmPaymentScreen.swift:56 — step "(7) Wait until the Send To screen is displayed" calls isSendToTitleDisplayed() and discards the result. No assertion.
  • android/.../Test596BuyBTC.kt:90verifySelectCryptoTitleDisplayed() result discarded. (Every other Android verify* call is correctly wrapped in assertTrue — this is the one exception.)
  • android/.../Test596BuyBTC.kt:95-105 — an entire allureStep block is commented out, so the buy test never selects a deposit wallet. Same at Test594SwapBTC.kt:128.
  • android/.../pages/OnboardingPage.kt:194-224verifyIUnderstandCheckbox1Displayed() returns true when post-onboarding state is visible, and clickIUnderstandCheckbox1/2/3 and clickAgreeAndContinue all early-return before their assertions in the same state. If a regression causes the app to skip the mandatory terms-consent screen entirely, this suite goes green. The recovery behaviour is reasonable — it just needs to live in a separately named helper that can't satisfy an assertion.
  • ios/.../Test595_BasicSellBTC_.swift — one assertion across six steps; step "(6) Enter $30 as the sell amount" enters 0.007; the described outcome ("Continue With Opens Browser") is never asserted.

ios/BitPayAppUITests/flows/AppFlows.swift:78-82 — blind coordinate taps before money flows

When goToHomeScreen can't find the portfolio button, a Home tab, or a back control, it falls through to tapping absolute screen coordinate (0.12, 0.11) and dragging from the left edge — up to 15 times, with no knowledge of what screen it's on. This runs in setUp() for the send, swap, sell and buy tests, i.e. immediately before the flows that move money. (The element-relative element.coordinate(...) calls elsewhere in the page objects are fine — this is the only absolute one.)

It also never fails: after attempts it returns silently, so the test proceeds from an unknown screen and the eventual failure points at the wrong step. An XCTFail at the end would make these much easier to debug.

iOS tests aren't isolated (Android's are)

scripts/run-tests.sh does adb shell pm clear before each Android class, and each Android money-flow test re-imports the fixture itself. The iOS side does neither: only Test592 imports the phrase, and Test593596 call completeOnboardingIfRequired, which on clean state creates a brand-new empty random wallet rather than importing the fixture. So the iOS money-flow tests either inherit whatever Test592 left behind or run against an empty wallet, and none of them can be run individually. Given the PR is specifically about the iOS suite, this is worth fixing before merge.

Workflows

  • Duplicate workflow names. ios-xcuitest.yml and ios-xcuitest-sandbox.yml are both name: iOS XCUITest; both Android files are both name: Android Espresso E2E. android-espresso.yml then triggers on workflow_run: workflows: ["iOS XCUITest"], which matches either — so a manual sandbox run kicks off the Android job using the production Testlio token and project.
  • No success guard. That same workflow_run has types: [completed] with no github.event.workflow_run.conclusion == 'success' check, so the Android suite runs after a failed or cancelled iOS run.
  • workflow_run checks out the default branch. android-espresso.yml:21 is a bare actions/checkout@v4; under workflow_run that's master, not the ref the triggering run tested. Relatedly, schedule: only fires on the default branch, so the nightly won't run at all until this reaches master.
  • Colliding concurrency groups. Both iOS workflows use e2e-ios-xcui-${{ github.ref }} and both Android ones use e2e-android-espresso-${{ github.ref }}, with cancel-in-progress: true — a manual run cancels the nightly.
  • "Sandbox" doesn't sandbox anything. The only difference between the two variants is which Testlio workspace receives the report. Both build identically (cp .env.template .env.development + yarn set:dev), and src/constants/config.ts:41 hardcodes APP_NETWORK = Network.mainnet with no env override, so both run on mainnet against https://bitpay.com and https://bws.bitpay.com and hit live Changelly/Moonpay endpoints nightly. The naming implies an isolation that doesn't exist — please either rename it or wire the input through to the app config. (The send test does stop at the Confirm Payment summary and never slides to send, which is the right call.)
  • Broken logcat pid capture. android-espresso.yml:167-172: android-emulator-runner splits script: per line and runs each as a separate sh -c, so adb logcat ... & and echo $! > /tmp/logcat.pid are in different shells, $! is unset, and kill $(cat /tmp/logcat.pid) || true is a silent no-op. (Metro's pid capture is fine — it's inside one run: | block. And test failures do propagate correctly; I checked the action's source.)
  • Secrets inline in run: bodies. echo "${{ secrets.SENTRY_PROPERTIES }}" > sentry.properties and export RUN_API_TOKEN="${{ secrets.TESTLIO_RUN_TOKEN }}" splice secret values into the script text. Please pass them via env: instead. Also: Android writes real Sentry properties, iOS just touches empty files — one of the two is wrong.
  • No permissions: block on any of the four new workflows.
  • Empty results upload cleanly. run-tests.sh adb pull ... || true, then cp ... || true, then a "Check Allure results" step that emits ::warning:: and exits 0 — so total loss of results is a green step that uploads an empty archive to Testlio.
  • ~540 lines of near-duplicate YAML across each sandbox/scheduled pair; one workflow with an input would be much easier to keep in sync.

Timeouts and retries

  • RetryRule never retries. BaseTest.kt:37 documents "Defaults to 3"; BaseTest.kt:50 is retryCount: Int = 1; RetryRule.kt:8 does repeat(retryCount), so 1 means one attempt. The rule, the ordering comment, and the screenshot-on-final-failure design are all inert as configured. Either fix the default or drop the machinery.
  • Very long single-element waits. MyKeyPage.kt:25 is 900,000 ms (15 min); OnboardingPage.kt:93,104,162 are 600,000 ms; :199 is 420,000 ms. WaitUtils.waitForView catches Throwable, so if the app has crashed it retries for the full window instead of failing fast — a couple of those and the 75-minute emulator step is gone before anything reports.

Xcode project

  • RCTCrashTest removed from the app target. develop has 6 references in the pbxproj; this branch has 0, while ios/RCTCrashTest.mm is still in the tree. src/lib/crash-test/index.ts will fail with "native module unavailable". Looks unintentional — probably the same pbxproj regeneration as §2.
  • ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)" deleted from both app configs (2 occurrences on develop, 0 here). Also looks unintentional.
  • PRODUCT_BUNDLE_IDENTIFIER = com.test.appium.BitPayAppUITests — template leftover; should be under a BitPay identifier.
  • IPHONEOS_DEPLOYMENT_TARGET = 26.0 on the test target is what pins CI to Xcode 26.2 and iPhone 17 only.
  • TestPlan.xctestplan is dead config — the scheme has no TestPlanReference, neither workflow passes -testPlan, and the file is in Copy Bundle Resources (project.pbxproj:423), so it ships inside the .xctest bundle as a resource rather than configuring anything.
  • parallelizable = "YES" on the new TestableReference while the suite is order-dependent and CI passes -parallel-testing-enabled NO. Anyone running the scheme from Xcode gets a broken suite.
  • systemAttachmentLifetime/userAttachmentLifetime = "keepAlways" in the shared scheme makes every developer's local test run keep all attachments forever.

Smaller things

  • MainActivity.kt:51 — gating FLAG_SECURE on !BuildConfig.DEBUG is scoped to debug and doesn't affect release, so I'm fine with it in principle, but it does mean debug builds can screenshot the recovery-phrase screen. Worth a deliberate nod from someone rather than arriving inside a test PR.
  • README.md:26-31 — the network_security_config example got mangled into invalid XML (<domain-config></domain>, <trust-anchors> merged into the closing tag, the <certificates> line lost). There's also a stray # Tests H1 at the top of the file.
  • Android tests carry no testlioManualTestID — all 6 iOS tests set it, 0 of 6 Android tests do, so the Android results won't link to their manual cases in Testlio.
  • Android and iOS aren't testing the same artifact — Android builds assembleDebug and serves JS from a live Metro bundler over adb reverse (__DEV__ true, unminified); iOS builds -configuration Release against a pre-generated --dev false bundle.
  • VerifyYourPhrasePage.swift and NewRecoveryPhrasePage.swift are dead — not referenced by any test or flow. If they're kept: extractNumbers matches \d+ against instructions that repeat each index ("2nd (2)"), yielding six indexes for three fields and then indexing past the end of textFields; there's also a force-unwrapped Range(...)! and a hardcoded .prefix(12).
  • SwapPage.swift:13 / SellPage.swift:13 wait on the global "Swap"/"Sell" static texts, which LinkingButtons.tsx already renders on Home — so a failed navigation satisfies the wait immediately and the test fails later in a misleading place.
  • scripts/run-tests.sh hardcodes /home/runner/work/_temp/allure-results, so it can't run locally despite living in scripts/. It's also mode 100644 (fine as long as it's always invoked via bash).
  • Missing trailing newlines on android/build.gradle, scripts/run-tests.sh, all four workflows and androidTest/AndroidManifest.xml.
  • AppFlows.swift:5 carries a personal name in the file header.

Relayed, not verified by me

androidTestImplementation "io.qameta.allure:allure-kotlin-*:2.4.0" (android/app/build.gradle:181) — flagged as having a path where an uncaught app RuntimeException is recorded as a passing result (allure-kotlin#81). I haven't reproduced it on an emulator, so treat it as a lead. If it holds, a crash of the app under test could be reported to Testlio as green, which would be worth pinning around.


<!-- Set cleartextTrafficPermitted = true for development -->
<base-config cleartextTrafficPermitted="false" />
<domain-config cleartextTrafficPermitted="false">
<base-config cleartextTrafficPermitted="true" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking. This is scripts/dev-overrides.js:12-15 output — it does exactly this replaceAll on cleartextTrafficPermitted. The file is referenced from src/main, so false -> true on base-config disables Android's HTTPS enforcement app-wide in release builds of a cryptocurrency wallet.

Please git checkout origin/develop -- android/app/src/main/res/xml/network_security_config.xml and check git status after any yarn set:dev.

WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
if (!BuildConfig.DEBUG) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking — this is correctly scoped to debug and release is unaffected, so I'm fine with it in principle.

It does mean debug builds can screenshot the recovery-phrase screen, which is a deliberate loosening of a security control arriving inside a test PR. Could you get an explicit nod from someone on the app side rather than having it ride along here?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is intentionally limited to debug builds to support Espresso/Appium automation and Layout Inspector screenshots. Release builds will retain FLAG_SECURE.

Could you please direct me to the appropriate Android/app security owner for explicit approval? I’ll wait for their confirmation before proceeding.

Comment thread android/build.gradle Outdated
}
}

gradle.afterProject { proj ->

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking. The minSdkVersion guard is fine (:app is 30, so it never fires). The packagingOptions block below has no guard and applies to every subproject including :app, so it reaches the release APK:

  • pickFirst on libhermes.so / libjsi.so / libreactnative.so / libc++_shared.so / libfbjni.so silences the duplicate-native-library error. That error is the guard against an ABI mismatch between a vendored module's copy and react-android's — silencing it globally turns a build failure into a runtime crash that only appears on user devices.
  • excludes += ['META-INF/LICENSE', 'META-INF/NOTICE', ...] strips third-party licence and notice files from the shipping APK. Apache-2.0 requires NOTICE redistribution, so that's a compliance issue.
  • excludes += 'META-INF/*.kotlin_module' can break Kotlin top-level function resolution and reflection at runtime.
  • compileSdkVersion 36 is forced onto any subproject declaring < 34, compiling third-party modules against an SDK they never declared.

If this is only needed for assembleDebugAndroidTest, please scope it — if (proj.name == 'app') plus a variant filter, or move it into the androidTest configuration. This file is also one of the 14 merge conflicts against develop.

}

allureStep("Enter recovery phrase and import wallet") {
importWalletPage.enterRecoveryPhrase("hobby short divert lady spare quit act settle body town license alone")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking. This is a valid mainnet BIP39 mnemonic committed in plaintext to a public repo — here and in 4 other Android tests and 1 iOS test.

I checked: the BIP44/49/84 BTC addresses and the first three m/44'/60' ETH addresses are all empty today, so nothing is at immediate risk. But the wallet is permanently burned, and Test596BuyBTC step 9 says "tap My Key with funds available" — so the suite expects it to be funded, and once it is, anyone who reads this PR can sweep it.

It also leaves two other ways:

  • the allureScreenshot("Recovery phrase entered") on the next line captures it on screen, and that image is uploaded to Testlio and as a CI artifact;
  • android-espresso.yml uploads the full adb logcat as the device-logcat artifact, and that workflow's own comment says RN dev logging serialises "every dispatched action" and "a recovery-phrase import fires hundreds of actions".

Please treat this phrase as compromised, generate a fresh fixture, and inject it from a GitHub secret. A secret in git history can't be removed by a later commit.

import org.junit.runner.Description
import org.junit.runners.model.Statement

class RetryRule(private val retryCount: Int = 1) : TestRule {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repeat(retryCount) with retryCount = 1 means one attempt — so RetryRule never actually retries.

BaseTest.kt:37 documents "Defaults to 3" but BaseTest.kt:50 is retryCount: Int = 1, so the rule, the @Rule(order = ...) ordering comment, and the screenshot-on-final-failure design are all inert as configured. Either make the default match the doc or drop the machinery — right now it reads as working retry logic that isn't.

Comment thread scripts/run-tests.sh
# so a timeout or crash on a later test doesn't lose earlier results
echo "=== Pulling allure results after $TEST_CLASS ==="
mkdir -p /home/runner/work/_temp/allure-results
adb pull /sdcard/googletest/test_outputfiles/allure-results/. /home/runner/work/_temp/allure-results/ || true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here:

  1. /home/runner/work/_temp/... is hardcoded, so this script can't run outside a GitHub runner despite living in scripts/. Please take the output directory from an env var with a local default.
  2. Combined with the || true here, the cp ... || true in the workflow, and the "Check Allure results" step that emits ::warning:: and exits 0, total loss of results is a green step that then uploads an empty archive to Testlio. That last check should fail the job when no result JSON exists.


on:
workflow_run:
workflows: ["iOS XCUITest"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking-ish. Three problems in this trigger:

  1. ios-xcuitest.yml and ios-xcuitest-sandbox.yml are both name: iOS XCUITest, so this matches either. A manual sandbox run kicks off this job using the production Testlio token and project. (Both Android files likewise share name: Android Espresso E2E.)
  2. types: [completed] with no github.event.workflow_run.conclusion == 'success' guard — the Android suite runs after a failed or cancelled iOS run.
  3. Under workflow_run, the bare actions/checkout@v4 below checks out the default branch (master), not the ref the triggering run tested. Relatedly, schedule: also only fires on the default branch, so the nightly won't run at all until this reaches master.

Please give the sandbox workflows distinct names, add the conclusion == 'success' guard, and check out github.event.workflow_run.head_sha explicitly.

TESTLIO_DEVICE_ID: 363a5ce7-6cef-436c-8531-6c07195c8fe8
run: |
if [ "${{ inputs.environment }}" = "Sandbox" ]; then
export RUN_API_TOKEN="${{ secrets.TESTLIO_RUN_TOKEN }}"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two notes:

  1. Interpolating ${{ secrets.* }} into the body of a run: script splices the value into the script text. Please pass them via env: instead and reference $RUN_API_TOKEN.
  2. The environment input only picks which Testlio workspace receives the report — the app build is identical (cp .env.template .env.development + yarn set:dev), and src/constants/config.ts:41 hardcodes APP_NETWORK = Network.mainnet with no env override. So "Sandbox" still runs on mainnet against https://bitpay.com and https://bws.bitpay.com and hits live Changelly/Moonpay endpoints. Either rename it so it doesn't imply isolation, or wire the input through to the app config.

}
}
],
"version" : 1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test plan is never used: the scheme has no TestPlanReference/<TestPlans> and neither workflow passes -testPlan, so testTimeoutsEnabled and the attachment lifetimes here do nothing.

It's also wired into Copy Bundle Resources (project.pbxproj:423), so it ships inside the .xctest bundle as a resource rather than configuring anything. Either reference it from the scheme and pass -testPlan in CI, or drop it — and remove it from the Resources phase either way.


func extractNumbers(from text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: "\\d+")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This page object and NewRecoveryPhrasePage.swift aren't referenced by any test or flow, so this is currently dead code — happy for it to be dropped from the PR.

If it's kept: VerifyPhrase.tsx renders each index twice ("2nd (2)"), so \d+ yields [2,2,11,11,9,9] for three fields, and the fourth loop iteration indexes past the end of textFields. Capture only the parenthesised numbers and bounds-check both subscripts. There's also a force-unwrapped Range(...)! just below and a hardcoded .prefix(12).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants