Skip to content

test(swift-sdk): add testnet identity-discovery UI test#3560

Open
llbartekll wants to merge 9 commits intov3.1-devfrom
claude/swift-xcuitest-wallet-persistence
Open

test(swift-sdk): add testnet identity-discovery UI test#3560
llbartekll wants to merge 9 commits intov3.1-devfrom
claude/swift-xcuitest-wallet-persistence

Conversation

@llbartekll
Copy link
Copy Markdown
Contributor

@llbartekll llbartekll commented Apr 28, 2026

Issue being fixed or feature implemented

Extends the SwiftExampleApp UI test suite with an end-to-end test that exercises the SDK paths the existing local-only wallet tests don't touch: importing a wallet from a known mnemonic, DIP-9 identity discovery, and reading the discovered identity's balance from Platform.

This is one regression check across roughly half the SDK — PlatformWallet.fromMnemonic, platform_wallet_discover_identities, DIP-9 TRANSFER key derivation, DAPI lookup, balance refresh.

What was done?

  • New CreditTransferTest.testImportWalletAndDiscoverIdentity — imports a testnet wallet from UI_TEST_TESTNET_MNEMONIC, runs identity discovery, asserts the expected identity ID surfaces with a non-zero balance. Skipped when the env var is unset, so the rest of the suite continues to run locally without test-network credentials.
  • Accessibility identifiers added across the views the test traverses (wallet create/import, identities tab, search-wallets sheet, identity detail, options/network picker, transitions form). accessibilityValue on the balance label so the raw credit count is parseable from XCUITest (the displayed %.8f DASH rounding hides amounts below 1000 credits).
  • Helpers extended in WalletFlow.swift for testnet flows: import-with-mnemonic, network switch, identity discovery, balance read, plus credit-transfer form navigation/execution scaffolding kept in place for the follow-up that re-adds the transfer assertion.

The class and file are named CreditTransferTest so the deferred transfer assertion can be re-added as a sibling method — helpers are already wired up.

How Has This Been Tested?

  • Manual run from Xcode against testnet, with UI_TEST_TESTNET_MNEMONIC exported through Xcode's scheme env vars (note: xcodebuild test ENV=... does not propagate env vars to the XCUITest runner — use the TEST_RUNNER_<NAME> prefix from the command line, see the file's leading comment).
  • Test passes against a wallet whose mnemonic discovers identity 3ou98WEERy6ExmmHWYWsFtyhgW8rmr1giceZYTFqdAAA with a non-zero balance.
  • Verified the existing 3 wallet UI tests (testCreateGeneratedWalletFlow, testWalletPersistsAcrossRelaunch, testWalletDeletionCleanupSurvivesRelaunch) still pass.
  • Verified the test skips cleanly when the env var is unset.

Breaking Changes

None — accessibility identifiers and accessibilityValue are additive; no app-side logic changed.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

  • Tests

    • Added UI tests for wallet persistence, wallet-deletion cleanup, and credit-transfer flows; introduced shared test helpers and deterministic waits; CI smoke runs now include these scenarios and run on a daily schedule with concurrency cancellation.
  • Accessibility

    • Added stable accessibility identifiers across the app for improved assistive tech support and reliable UI testing.
  • UI

    • Network status shows a consistent Switching/Connected/Disconnected presentation with stable identifiers; balance display exposes stable metadata for testing without changing its visible format.
  • Documentation

    • Added README describing the UI test suite and local/CI run instructions.

llbartekll and others added 2 commits April 28, 2026 09:00
Extract Identifier enum, XCUIElement helpers, and the create/delete
wallet flow into Support/ for cross-class reuse. Rewrite the existing
testCreateGeneratedWalletFlow on top of the extracted helpers; behavior
is unchanged.

Add WalletPersistenceTests with two SDK-backed integration tests:
- testWalletPersistsAcrossRelaunch validates loadFromPersistor() after
  a cold restart (SwiftData rehydration + Keychain read).
- testWalletDeletionCleanupSurvivesRelaunch guards atomic SwiftData +
  Keychain mnemonic cleanup; the orphan-mnemonic recovery prompt fires
  on relaunch if either side leaks.

Both new tests use addTeardownBlock + MainActor.assumeIsolated for
best-effort wallet cleanup if assertions halt mid-flow.

Extend swift-example-app-ui-smoke.yml -only-testing list to include
the two new tests; -parallel-testing-enabled NO and
-maximum-concurrent-test-simulator-destinations 1 retained.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Imports a wallet from `UI_TEST_TESTNET_MNEMONIC`, runs DIP-9 identity
discovery, and asserts the registered identity surfaces with a non-zero
balance. Skipped when the env var is unset. Test is scaffolded under
CreditTransferTest so the credit-transfer assertion can be re-added as
a sibling method (helpers for that flow are already in WalletFlow).

Adds accessibility identifiers across the views the test traverses
(wallet creation, identities tab, search-wallets sheet, identity detail,
options/network picker, transition form) and one accessibilityValue on
the balance label so the raw credit count is parseable from XCUITest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v3.1.0 milestone Apr 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

Warning

Rate limit exceeded

@QuantumExplorer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 5 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 53fce408-5207-48b3-8d89-843040ee82b1

📥 Commits

Reviewing files that changed from the base of the PR and between 4e4cf1a and 3823ee4.

📒 Files selected for processing (7)
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AppState.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/ContentView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CreateWalletView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/IdentitiesContentView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/IdentityDetailView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/OptionsView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/SearchWalletsForIdentitiesView.swift
📝 Walkthrough

Walkthrough

Adds deterministic accessibility identifiers across the SwiftExampleApp UI, centralizes test identifier constants and XCUI helper utilities, implements extensive UI-test flow helpers and new UI test suites (wallet persistence and credit transfer), updates network-switch state handling in AppState, and extends the CI workflow to run the UI smoke tests.

Changes

Cohort / File(s) Summary
App Views — Accessibility & Network UI
packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/ContentView.swift, .../Core/Views/CreateWalletView.swift, .../Core/Views/IdentitiesContentView.swift, .../Views/IdentitiesView.swift, .../Views/IdentityDetailView.swift, .../Views/OptionsView.swift, .../Views/SearchWalletsForIdentitiesView.swift, .../Views/TransitionDetailView.swift, .../Views/TransitionInputView.swift
Added stable accessibilityIdentifiers to tabs, controls, inputs, menu items, rows, and balance metadata; refactored OptionsView to surface network-switch readiness via AppState.isSwitchingNetwork and expose identifiers for picker/status.
App State
packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AppState.swift
Introduced @Published var isSwitchingNetwork and a monotonic request ID to track async network switches and prevent premature readiness resets.
UI Test Identifiers
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/Identifiers.swift
New centralized Identifier enum with constants and helpers (tabs, wallet controls, options, identities, search, identity detail) for deterministic test lookups.
XCUI Helper Utilities
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/XCUIElement+Helpers.swift
Added element lookup helpers (returning firstMatch), waiter wrappers (enabled, non-existence, switch-on), scroll-to-hittable, isSwitchOn convenience, and a recovery-prompt guard.
Shared Wallet Flow Helpers
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift
Large collection of @MainActor helper routines for tab navigation (with fallbacks), create/import/delete wallet flows (including robust toggle handling and backup flow), network switching to Testnet (with status wait), identity discovery flow, balance parsing via accessibilityValue, and cleanup utilities.
New UI Test Suites
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift, .../WalletPersistenceTests.swift
Added CreditTransferTest (mnemonic-gated import, identity discovery, read balance) and WalletPersistenceTests (persistency and deletion cleanup across relaunch) with setup/teardown and recovery-prompt checks.
UI Tests Consolidation
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/SwiftExampleAppUITests.swift
Removed in-file identifier constants and ad-hoc helpers in favor of centralized helpers and common utilities; replaced direct waits with shared assertion helpers.
CI Workflow
.github/workflows/swift-example-app-ui-smoke.yml
Workflow now runs daily and on dispatch, adds concurrency cancellation, forwards a secret-backed TEST_RUNNER_UI_TEST_MNEMONIC env var into the XCUITest process, and expands -only-testing to include wallet persistence and credit-transfer UI tests.
Docs
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/README.md
Added README documenting the UI test suite, local/CI run instructions, simulator hygiene, and mnemonic/secret guidance for CI forks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through code with nimble paws,

Labels placed without a pause,
Wallets, tests, and network flows align,
Identifiers steady — outputs fine,
A carrot-cheered commit, neat and bright! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'test(swift-sdk): add testnet identity-discovery UI test' accurately describes the primary change: adding a new UI test for testnet identity discovery workflows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/swift-xcuitest-wallet-persistence

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 3 minutes and 5 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@thepastaclaw
Copy link
Copy Markdown
Collaborator

thepastaclaw commented Apr 28, 2026

🕓 Ready for review (commit 3823ee4)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/OptionsView.swift`:
- Around line 88-110: The status label incorrectly shows "Connected" whenever
appState.sdk != nil even while a network switch is in progress; change the logic
so isSwitchingNetwork remains true until the async switch completes (or derive
connected state from a network-specific readiness signal on the SDK) so that the
UI only shows "Connected" when the new network is actually ready; update the
state transitions in switchAppNetworkToTestnet (and any reset helpers) to
set/clear isSwitchingNetwork around the full async completion and use that
combined with appState.sdk (or a new appState.sdk.isReady flag) to drive the
Label bound to accessibilityIdentifier "options.networkStatusLabel".

In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift`:
- Around line 96-101: The test is asserting a live testnet funding floor
(XCTAssertGreaterThan(balance, 0)); instead assert only that a readable balance
was returned: call readIdentityBalanceCredits(in:) and replace the
XCTAssertGreaterThan check with an assertion that the read succeeded (if the
function returns an Optional use XCTAssertNotNil(balance, "..."), or if it
returns an Int use XCTAssertGreaterThanOrEqual(balance, 0, "...") to allow
zero). Update the failure message to say the balance should be readable rather
than non-zero and keep the test focused on discovery/readability rather than
funding.

In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift`:
- Around line 933-969: The negative branch in assertWalletRowVisible(true/false)
can false-pass because it never scrolls and queries app-wide button/text rows
that may be off-screen; change the exists == false path to reuse
scrollToWalletRow(named:in:) to bring the specific wallet row into the viewport
(or obtain the per-wallet XCUIElement) and then assert non-existence against
that element (e.g. wait for row.exists == false or use an
XCTNSPredicateExpectation on the returned row) instead of the current app-level
buttonRow/textRow checks so off-screen elements can't make the assertion pass
incorrectly.
- Around line 492-518: The test currently assumes the picker auto-selects the
imported wallet (walletPicker / Identifier.SearchWallets.walletPicker) which is
flaky; instead explicitly drive selection to walletName: after
walletPicker.waitForExistence, open the picker UI and locate the row/button/cell
whose label equals walletName (or the table cell in
SearchWalletsForIdentitiesView) and tap it to select that wallet, then assert
the selection; alternatively, if simpler, add a deterministic cleanup step
(delete competing wallets) before importing so only walletName remains—reference
walletPicker, walletName, and SearchWalletsForIdentitiesView when updating the
helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 655844a1-5da6-46a1-acaf-193e6c0801cd

📥 Commits

Reviewing files that changed from the base of the PR and between 9bd37f2 and b3c85a2.

📒 Files selected for processing (16)
  • .github/workflows/swift-example-app-ui-smoke.yml
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/ContentView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/CreateWalletView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Core/Views/IdentitiesContentView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/IdentitiesView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/IdentityDetailView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/OptionsView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/SearchWalletsForIdentitiesView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/TransitionDetailView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/TransitionInputView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/Identifiers.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/XCUIElement+Helpers.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/SwiftExampleAppUITests.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/WalletPersistenceTests.swift

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 28, 2026

✅ DashSDKFFI.xcframework built for this PR.

SwiftPM (host the zip at a stable URL, then use):

.binaryTarget(
  name: "DashSDKFFI",
  url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
  checksum: "ff27351f1cc521742a7be124e9ebc864b4e8d297884c979eeef0a9680d7d69dc"
)

Xcode manual integration:

  • Download 'DashSDKFFI.xcframework' artifact from the run link above.
  • Drag it into your app target (Frameworks, Libraries & Embedded Content) and set Embed & Sign.
  • If using the Swift wrapper package, point its binaryTarget to the xcframework location or add the package and place the xcframework at the expected path.

Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Code Review

Test-only PR that adds accessibility identifiers and a new XCUITest (CreditTransferTest.testImportWalletAndDiscoverIdentity) plus shared helpers extracted from the existing test class. No app-side logic or consensus-critical code is touched. The main concerns are about test reliability: a verifiable race in switchAppNetworkToTestnet (the Connected label can be true via the stale SDK throughout the switch), brittle assumptions about picker default state, and ~200 lines of unused credit-transfer scaffolding likely to rot before the follow-up lands.

Reviewed commit: b3c85a2

🔴 1 blocking | 🟡 4 suggestion(s) | 💬 2 nitpick(s)

🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift`:
- [BLOCKING] lines 375-439: switchAppNetworkToTestnet can return while the app is still on the previous network
  `switchAppNetworkToTestnet` waits for `options.networkStatusLabel` to contain `Connected` as proof the switch finished, but the app provides no such guarantee. In `OptionsView` (lines 27-50), tapping the segmented control sets `appState.currentNetwork` and clears `isSwitchingNetwork` once the local resets finish; the actual SDK rebuild runs separately on the `currentNetwork.didSet` task in `AppState` (lines 12-17, 105-126). The status label renders `Connected` whenever `appState.sdk != nil`, and `switchNetwork` never nils the old SDK before constructing the new one (`sdk = newSDK` only after `try SDK(network:)` succeeds). On a simulator left on a non-testnet network from a previous run, the helper can therefore observe `Connected` against the stale SDK and return before testnet DAPI is bound — the discovery step then runs against the wrong backend and the test result is nondeterministic. Gate on a stronger signal: e.g. an SDK-level state transition published from `AppState` or a test-only flag flipped after `switchNetwork` completes, rather than the always-green status label.
- [SUGGESTION] lines 492-519: runIdentityDiscovery relies on "only one wallet exists" without enforcing it
  `runIdentityDiscovery` trusts the picker's default-first auto-selection and only sanity-checks `walletPicker.label.contains(walletName)`. The invariant is never enforced: `SearchWalletsForIdentitiesView` queries `@Query(sort: \PersistentWallet.createdAt) hdWallets` unfiltered, and `CreditTransferTest` only deletes the row matching `expectedSenderWalletIdHex` (lines 69-76 of `CreditTransferTest.swift`). Any other persisted wallet from prior local runs still appears in the picker and, being older, becomes the default — the picker-label assertion will then fail loudly even though import and discovery are themselves working. The comment in the helper acknowledges this assumption but the test does not enforce it. Either reset `PersistentWallet` rows for unrelated wallets before discovery (e.g. a `cleanupWalletsByPrefix`-style sweep on entry, or a full SwiftData reset), or drive the picker to explicitly select `walletName` instead of trusting the default.
- [SUGGESTION] lines 624-833: Credit-transfer helpers (~200 lines) are added but unused
  `navigateToIdentityCreditTransferForm`, `executeCreditTransfer`, `waitForCreditTransferSuccess`, and `cleanupWalletsByPrefix` are defined but have no callers anywhere in the test suite — grep confirms only the definitions exist. Carrying ~200 lines of unexecuted UI test helpers is risky: the most fragile string matching in the file lives here (`label CONTAINS[c] "manage identities"`, the `"💳 Manually Enter Recipient"` literal, the `transition.input.toIdentityId` identifier, the `"State Transitions"` cell label), and none of it is exercised, so it will silently rot against UI changes until the follow-up tries to use it. Note also that `executeCreditTransfer` calls `app.swipeDown()` after typing the amount — `importWallet` explicitly warns that `swipeDown` can dismiss sheets; the Transfer Credits screen is push-navigated rather than presented so it may not bite, but the inconsistency is worth verifying before the helper is wired in. Either land these helpers together with the test that uses them, or trim to what the discovery test actually needs and re-add the rest in the follow-up PR.
- [SUGGESTION] lines 466-490: Identity-discovery retry loop can close an already-open menu
  The retry loop binds `for attempt in 1...3 where !sheetOpened` purely as a counter and then has to do `_ = attempt` to silence the unused-variable warning — that's a tell that the index shouldn't be bound at all (`for _ in 1...3 where !sheetOpened` is clearer). More substantively, every iteration unconditionally calls `addMenu.tap()` again. If the previous attempt actually opened the SwiftUI Menu but the search-item lookup transiently failed, the next `addMenu.tap()` will *close* the menu, putting the retry into a worse state than the first attempt. Probe the search item or the menu's selected state before re-tapping, e.g. only call `addMenu.tap()` when neither `searchMenuItem.exists` nor `addMenu.isSelected`.

In `packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift`:
- [SUGGESTION] lines 53-59: Recovery-prompt guard runs before the network switch
  `failIfRecoveryPromptVisible(in: app, timeout: 2)` runs immediately after `app.launch()`, then `switchAppNetworkToTestnet` runs. A wallet left over from a previous testnet run, when the simulator boots on a different default network (regtest, etc.), only triggers the orphan-mnemonic recovery prompt *after* the network switch rebinds the wallet-scoped services. In that scenario the guard runs against a state where the prompt has not yet appeared and misses it. Re-run the guard after `switchAppNetworkToTestnet`, or move the existing call to right after the switch so the prompt is detected on whichever network the test actually executes against.

Comment thread .github/workflows/swift-example-app-ui-smoke.yml
@llbartekll llbartekll marked this pull request as draft April 29, 2026 06:05
Network-switch race (BLOCKING from both reviewers): lift
`isSwitchingNetwork` from OptionsView's local @State to AppState. The
flag now spans the full async cycle (currentNetwork.didSet → Task →
switchNetwork → sdk = newSDK), so the network status label and
test-side `Connected` predicate stop reading the previous network's
SDK as a successful switch.

Test reliability:
- runIdentityDiscovery drives the wallet picker explicitly instead of
  trusting default-first auto-selection, so unrelated wallets on the
  simulator no longer hijack discovery.
- assertWalletRowVisible(exists: false) scrolls to the top and sweeps
  down before declaring absent, closing a false-pass on long lists
  where SwiftUI's lazy List could leave a still-persisted row outside
  the accessibility tree.
- CreditTransferTest drops the `balance > 0` floor (couples to live
  testnet funding state); readIdentityBalanceCredits already XCFails
  on parse error, so reaching the next line is the readability signal.
- Re-run failIfRecoveryPromptVisible after switchAppNetworkToTestnet —
  a leftover testnet wallet on a simulator booted to a different
  default network only triggers the orphan-mnemonic prompt after the
  network switch rebinds wallet-scoped services.
- cleanupWalletsByPrefix("ImportTransfer-") on entry sweeps random-
  suffixed wallets from prior failed runs (the deterministic
  walletId-based check missed them).
- Discovery retry loop only re-taps the Add menu when its menu item
  isn't still visible from a prior attempt — re-tapping while the menu
  is open closes it.

Trim:
- Delete unused credit-transfer helpers (navigateToIdentityCreditTransferForm,
  executeCreditTransfer, waitForCreditTransferSuccess) and
  Identifier.Transition. They will return alongside the credit-transfer
  test in a follow-up; the corresponding app-side .accessibilityIdentifier
  calls on TransitionDetailView/TransitionInputView are kept (harmless).

CI:
- Wire CreditTransferTest into the smoke workflow with -only-testing.
  Self-skips when UI_TEST_TESTNET_MNEMONIC is unset, so the new line is
  a no-op for forks/PRs without the secret while still exercising the
  build path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@llbartekll llbartekll marked this pull request as ready for review April 29, 2026 07:10
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift (1)

528-532: Wait for picker selection to propagate before asserting label text.

The assertion on Line 528 can race right after walletOption.tap(). A short predicate wait on walletPicker.label containing walletName will make this less flaky on slower simulators.

Proposed stabilization
-    XCTAssertTrue(
-        walletPicker.label.contains(walletName),
-        "Picker shows \"\(walletPicker.label)\" but the test imported \(walletName).",
-        file: file, line: line
-    )
+    let selectedWalletPredicate = NSPredicate { object, _ in
+        guard let element = object as? XCUIElement, element.exists else { return false }
+        return element.label.contains(walletName)
+    }
+    let selectedWalletResult = XCTWaiter.wait(
+        for: [XCTNSPredicateExpectation(predicate: selectedWalletPredicate, object: walletPicker)],
+        timeout: 5
+    )
+    XCTAssertEqual(
+        selectedWalletResult,
+        .completed,
+        "Picker shows \"\(walletPicker.label)\" but the test imported \(walletName).",
+        file: file, line: line
+    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift`
around lines 528 - 532, The assertion on walletPicker.label can race immediately
after walletOption.tap(); add an explicit predicate wait that walletPicker.label
contains walletName (e.g., create an NSPredicate "label CONTAINS %@", use
expectation(for:evaluatedWith:handler:) or XCTNSPredicateExpectation, then
wait(for:timeout:)) before calling XCTAssertTrue so the picker selection has
time to propagate; update the flow around walletOption.tap(), walletPicker and
the XCTAssertTrue to perform the predicate wait first and then assert.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AppState.swift`:
- Around line 24-28: The isSwitchingNetwork flag is being cleared
unconditionally by each spawned Task after awaiting switchNetwork(to:), which
allows an earlier task to flip it false while a later switch is still running;
fix by introducing a request token/version (e.g., an Int or UUID property like
networkSwitchRequestID) that you increment/assign before creating the Task,
capture that token inside the Task, and only set isSwitchingNetwork = false if
the captured token matches the current networkSwitchRequestID after await
switchNetwork(to:); apply this change to both places that spawn the Task so
completions are serialized/guarded and stale tasks cannot clear readiness.

In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift`:
- Around line 5-8: Update the stale header comment at the top of
CreditTransferTest.swift (the file-level comment in the CreditTransferTest test)
to reflect the current assertions: change the phrase that claims the test
asserts a "non-zero balance" to state it only verifies that the identity's
balance is readable/accessible (and keep or clarify that the actual
credit-transfer assertion is deferred). Edit the header comment block above the
test implementation to match the test's scope (balance readability) and remove
or adjust any misleading wording about asserting a non-zero balance.

---

Nitpick comments:
In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift`:
- Around line 528-532: The assertion on walletPicker.label can race immediately
after walletOption.tap(); add an explicit predicate wait that walletPicker.label
contains walletName (e.g., create an NSPredicate "label CONTAINS %@", use
expectation(for:evaluatedWith:handler:) or XCTNSPredicateExpectation, then
wait(for:timeout:)) before calling XCTAssertTrue so the picker selection has
time to propagate; update the flow around walletOption.tap(), walletPicker and
the XCTAssertTrue to perform the predicate wait first and then assert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fed7c149-4bb2-4a06-b438-e518503b44eb

📥 Commits

Reviewing files that changed from the base of the PR and between b3c85a2 and 7c0ae94.

📒 Files selected for processing (6)
  • .github/workflows/swift-example-app-ui-smoke.yml
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AppState.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/OptionsView.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/Identifiers.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/swift-example-app-ui-smoke.yml
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/Views/OptionsView.swift

Comment thread packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AppState.swift Outdated
@llbartekll llbartekll marked this pull request as draft April 29, 2026 07:17
llbartekll and others added 4 commits April 29, 2026 09:51
Three findings from coderabbit's re-review of 7c0ae94:

- AppState: overlapping network switches could cause an earlier task
  to clear `isSwitchingNetwork` while a later switch was still running.
  Add a monotonic `networkSwitchRequestID`; each spawned task captures
  its id at start and only clears the flag when its id still matches.
- CreditTransferTest header: aligned the file-level comment with the
  current scope (balance readability, not non-zero balance).
- WalletFlow `runIdentityDiscovery`: replaced the bare label-contains
  assertion after `walletOption.tap()` with an `XCTNSPredicateExpectation`
  wait so the picker has time to propagate the selection on slower
  simulators.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renames the testnet mnemonic env var the XCUITest reads to
`UI_TEST_MNEMONIC` and forwards it from a GitHub secret of the same
name into the xcodebuild step as `TEST_RUNNER_UI_TEST_MNEMONIC` (the
prefix is required — xcodebuild strips it before handing the env to
the XCUITest runner process).

Empty on fork PRs (GitHub withholds secrets there), at which point the
test self-skips. Once a repository secret named `UI_TEST_MNEMONIC` is
set, `testImportWalletAndDiscoverIdentity` runs end-to-end against
testnet on every push to a non-fork branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a 23:00 UTC daily cron to the smoke workflow, alongside the
existing manual `workflow_dispatch` trigger. Mirrors the repo's
nightly pattern (separate workflow with its own cron, like
`tests-rs-nightly-long-running.yml`).

Also adds a concurrency block so a manual dispatch and a cron run
don't fight over the single self-hosted macOS ARM64 runner — the
older run is cancelled in favor of the newer one.

The cron only takes effect once this lands on the default branch
(`v3.1-dev`); GitHub Actions schedules run against the default
branch's workflow file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the operational gotchas that took multiple sessions to
re-discover:

- `TEST_RUNNER_` prefix gate when passing `UI_TEST_MNEMONIC` via
  `xcodebuild test ENV=`.
- `simctl erase` is the only way to clear stale Keychain entries
  between runs (uninstall alone leaves orphan mnemonics that trip
  the recovery prompt).
- SwiftData migration failures from leftover stores manifest as a
  "Expected root tab bar" timeout, not an obvious crash.
- The CI nightly cron's reliance on the self-hosted Mac being online.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@llbartekll llbartekll marked this pull request as ready for review April 29, 2026 08:48
@llbartekll llbartekll requested a review from thepastaclaw April 29, 2026 08:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift (1)

407-434: ⚠️ Potential issue | 🟠 Major

Wait for Testnet selection to latch before accepting “Connected”.

Right now the helper can still pass if the app remains connected on the previous network and the segmented-control tap doesn’t take effect. Add an explicit wait that testnetButton.isSelected == true before asserting connection status.

Suggested patch
@@
-    if testnetButton.isSelected {
-        // Already on Testnet; status should already be Connected.
-    } else {
+    if !testnetButton.isSelected {
         testnetButton.tap()
     }
+
+    let selectedResult = XCTWaiter.wait(
+        for: [
+            XCTNSPredicateExpectation(
+                predicate: NSPredicate(format: "isSelected == true"),
+                object: testnetButton
+            ),
+        ],
+        timeout: 10
+    )
+    XCTAssertEqual(
+        selectedResult,
+        .completed,
+        "Expected Testnet segment to be selected before waiting for connected status.",
+        file: file, line: line
+    )
@@
     let result = XCTWaiter.wait(
         for: [XCTNSPredicateExpectation(predicate: connectedPredicate, object: statusLabel)],
         timeout: timeout
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift`
around lines 407 - 434, The helper can report "Connected" for the previous
network if the segmented control tap hasn't latched; fix by waiting for the
Testnet segment selection to take effect before checking network status: after
tapping testnetButton (or the existing isSelected check) add an explicit wait
using an NSPredicate/XCTNSPredicateExpectation that verifies
testnetButton.isSelected == true (with a reasonable timeout) and assert the wait
completes successfully, then proceed to build statusLabel and wait for the
"Connected" predicate (retain connectedPredicate, statusLabel, and the existing
XCTWaiter usage).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift`:
- Around line 518-520: The NSPredicate used to find walletOption is too
permissive and can match prefixes; update the predicate in the walletOption
construction (the app.buttons.matching(...) call) to require a trailing space
after the walletName token (e.g., use "label BEGINSWITH %@ " with walletName as
the argument) so it only matches the exact name token rather than any prefix;
keep the rest of the selector (firstMatch) unchanged.
- Around line 653-667: cleanupWalletsByPrefix is only checking visible buttons
and can miss off-screen wallets; change it to iterate over the full query (use
app.buttons.matching(predicate).allElementsBoundByIndex) or repeatedly scroll
the wallets list (element(Identifier.walletsScreen)) while locating elements
matching the predicate so every persisted wallet is discovered, and call
bestEffortDeleteWallet(named:in:) for each found name until none remain; ensure
the loop continues until the full query is empty rather than stopping when no
visible match is found.

---

Duplicate comments:
In
`@packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift`:
- Around line 407-434: The helper can report "Connected" for the previous
network if the segmented control tap hasn't latched; fix by waiting for the
Testnet segment selection to take effect before checking network status: after
tapping testnetButton (or the existing isSelected check) add an explicit wait
using an NSPredicate/XCTNSPredicateExpectation that verifies
testnetButton.isSelected == true (with a reasonable timeout) and assert the wait
completes successfully, then proceed to build statusLabel and wait for the
"Connected" predicate (retain connectedPredicate, statusLabel, and the existing
XCTWaiter usage).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 032ec7d6-0160-4c45-b81b-78633330445e

📥 Commits

Reviewing files that changed from the base of the PR and between 7c0ae94 and 4e4cf1a.

📒 Files selected for processing (5)
  • .github/workflows/swift-example-app-ui-smoke.yml
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AppState.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/README.md
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/Support/WalletFlow.swift
✅ Files skipped from review due to trivial changes (1)
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/swift-example-app-ui-smoke.yml
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleAppUITests/CreditTransferTest.swift
  • packages/swift-sdk/SwiftExampleApp/SwiftExampleApp/AppState.swift

Three findings from coderabbit's re-review:

- `switchAppNetworkToTestnet`: wait for the testnet segment's
  `isSelected == true` before trusting the "Connected" predicate.
  The AppState fix already covers the rebind-in-progress race; this
  closes the missed-tap window where the picker setter never fires
  and the previous network's "Connected" still satisfies the wait.
- `runIdentityDiscovery`: anchor the wallet-option predicate with a
  trailing space (`BEGINSWITH "<walletName> "`) so a longer wallet
  name sharing a prefix can't win firstMatch.
- `cleanupWalletsByPrefix`: scroll-and-sweep the full wallets list
  instead of bailing when no matching row is visible in the current
  viewport. Resets to top, then up to 10 delete+reset cycles + 10
  scroll passes per call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@thepastaclaw thepastaclaw left a comment

Choose a reason for hiding this comment

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

Re-reviewed 296d9661.

The follow-up fixes address the reliability issues from my earlier pass: the network-status signal now stays honest across the full SDK rebind, overlapping switches no longer clear readiness early, the discovery flow explicitly selects the imported wallet, the recovery-prompt guard runs after the testnet hop, and the unused transfer scaffolding is trimmed back out. The workflow/docs wiring for UI_TEST_MNEMONIC also makes the new smoke path much easier to understand and run.

Looks good to me.

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.

3 participants