Files
791ce40355 tests(mobile): close WC e2e gaps for collision and reconnect mismatch (#7770)
* refactor(mobile): extract findCollidingSigner pure helper

Move the matching predicate out of useSignerCollisionGuard and into
apps/mobile/src/features/ImportSigner/utils/findCollidingSigner.ts so the
e2e WalletConnect mock and the production hook can share one
implementation. The hook becomes a thin wrapper over the helper plus
Alert.alert dispatch.

Address comparison uses `sameAddress` from @safe-global/utils — case-
insensitive equality consistent with the rest of the mobile WC code.

The Map-based normalisation in the previous hook is dropped (signer
lists are <10 entries; the hook is called at most once per import).
Existing useSignerCollisionGuard.test.ts continues to pass without
modification.

* refactor(mobile): share WalletConnectContextValue across real + e2e providers

Move the context interface to apps/mobile/src/features/WalletConnect/
context/types.ts so both WalletConnectContext.tsx and
WalletConnectContext.e2e.tsx import the same contract. Drift between
the two providers is now a TypeScript error rather than a runtime crash.

The migration surfaced two latent type mismatches that the contract now
prevents:
- walletConnectE2eState.chainId was typed `number | undefined` but
  `useAccount.chainId` is `string | undefined`. Nothing read the field,
  so the bug was latent. Corrected.
- The e2e mock's `sign` was typed `(unknown) => Promise<unknown>`
  instead of `(SignParams) => Promise<SigningResponse>`. Retyped via
  `useCallback<WalletConnectContextValue['sign']>(…)` so the contract
  drives the signature.

* refactor(mobile): add resetE2EState reset boundary for e2e tests

Introduce a top-level `resetE2EState` action that every e2e-relevant
slice handles via `extraReducers` by returning its initialState:
signersSlice, activeSignerSlice, safesSlice, activeSafeSlice,
executionMethodSlice, estimatedFeeSlice, notificationsSlice.

Setup helpers now call a single `resetReduxForE2E(dispatch)` helper
that dispatches the action AND resets `walletConnectE2eState`. The
existing four connectSignerSetup helpers migrate from the bare
`walletConnectE2eState.reset()` to the unified entrypoint.

settingsSlice intentionally NOT reset — `onboardingVersionSeen` must
persist for setup paths that skip `setupBaseConfig`.

* feat(mobile): extend WC e2e mock with reconnect-mismatch + collision branches

walletConnectE2eState gains a single-shot `reconnectMismatch` flag and
field-grouping comments separating scenario directives from session state.

WalletConnectContext.e2e.tsx mock now mirrors two production behaviours:

- `reconnect(addr)` — when reconnectMismatch is true, routes to
  /import-signers/reconnect-error AND clears the flag so a follow-up
  retry succeeds. Mirrors useReconnectFlow on address mismatch.
- `initiateConnection()` — after the isOwner check, calls
  findCollidingSigner (the shared helper landed in Phase 1A). On a
  type-mismatched collision: Alert.alert + clear session state +
  return without navigation. Mirrors useImportSignerFlow's
  guardAgainstCollision branch.

Both branches use the shared findCollidingSigner helper so logic drift
between mock and prod is caught at compile time.

* tests(mobile): add TestCtrls helpers for reconnect-mismatch + collision

Two new setup helpers + e2e<PascalCase> TestCtrls triggers:

- setupWcGateReconnectWrongWallet / e2eWcGateReconnectWrongWallet:
  delegates to setupWcGateBase with `reconnectMismatch: true` so the
  next reconnect routes to ReconnectError before the single-shot flag
  clears.
- setupConnectSignerCollision / e2eConnectSignerCollision: pre-seeds a
  private-key signer at OWNER_ADDRESS and configures the WC mock to
  return the same address. The mock's collision branch fires
  Alert.alert + clears the WC session, mirroring useImportSignerFlow's
  guardAgainstCollision via the shared findCollidingSigner helper.

The signer-selection setup (Notion #19/#20) is deferred — it needs a
real testnet Safe with 2 owners + pending tx with empty confirmations
that doesn't exist yet. To be addressed in a follow-up round.

* tests(mobile): harden Try-again retry assertion in connect-signer-not-owner

The retry tap calls router.dismiss() before initiateConnection re-runs.
Without an animation wait, the next assertion can race the dismiss and
flake intermittently. Add waitForAnimationToEnd + extendedWaitUntil
notVisible on connect-signer-error so the test waits for the screen
transition before asserting the Name screen.

Notion #6 (Try-again recovery) was already covered by PR #7676 — this
is robustness only.

* tests(mobile): add reconnect-wrong-wallet e2e flow

Exercises useReconnectFlow's address-mismatch path → ReconnectError
screen → retry. Drives the new single-shot reconnectMismatch flag end
to end:

  setup (flag=true)
  → tap reconnect → mock routes to /import-signers/reconnect-error,
    clears flag
  → tap "Connect a different wallet" → router.dismiss() + reconnect
    retries
  → flag cleared so retry succeeds → gate dismisses

First test to reach ReconnectError + reconnect-error-done testIDs.
Outside Notion's documented cases — extra coverage for production code
that previously had only the happy reconnect path tested.

* tests(mobile): add connect-signer-collision e2e flow

First test in the repo to assert against and dismiss a native iOS
Alert.alert. Setup pre-seeds a private-key signer at OWNER_ADDRESS,
then triggers a WC connect flow that returns the same address — the
type mismatch fires findCollidingSigner via the e2e mock's collision
branch, which mirrors useImportSignerFlow's guardAgainstCollision.

The test asserts:
1. The native alert "Signer already imported" appears
2. Tapping "OK" dismisses it
3. The flow does NOT advance to Name your signer (collision bailed)

Same-type re-imports are intentionally NOT covered — findCollidingSigner
returns null for them (silent overwrite per production behavior).

Closes Notion #7. iOS-only — Android native dialog handling differs and
is out of scope for this round.

* tests(mobile): wire collision + reconnect-wrong-wallet into suite, drop ordering

Three updates:

- __suite__.yml — add connect-signer-collision and reconnect-wrong-wallet
  to the suite. Tags trimmed to `connect-signer` + `suite` (narrow tags
  live on per-test files, so --include-tags filtering works as
  intended). Order is presentation-only: every setup helper now calls
  resetReduxForE2E so tests are independent.
- plan — record post-implementation reality: Phase 5A was already
  shipped by PR #7676 (this round added robustness only); Phase 5D /
  signer-selection deferred (extractAppSigners reads from CGW's
  on-chain owner list, not Redux SafeOverview, so a synthetic Safe
  fixture won't surface a tx); R1 (native iOS Alert.alert) closed by
  simulator verification.
- brainstorm — same updates for future readers.

* refactor(mobile): apply WC e2e code-review findings (todos 027-038)

Resolves 8 todos surfaced by the code review on tests/wa-1858-wc-coverage-extensions:

P1:
- 027 — Extract showCollisionAlert to a shared util
  (apps/mobile/src/features/ImportSigner/utils/showCollisionAlert.ts).
  The e2e mock and useSignerCollisionGuard now share one source of truth
  for the alert title + body — drift impossible.
- 028 — resetReduxForE2E now also dispatches resetApiState() on cgwClient,
  hypernativeApi, and web3API. Closes the cache-leak vector via
  safesSlice.addMatcher.
- 029 — Add `wc-gate` tag to reconnect-wrong-wallet.yml so
  --include-tags wc-gate filtering picks it up.

P2:
- 031 — settingsSlice now selectively resets on resetE2EState — preserves
  onboardingVersionSeen, resets everything else. Per-test theme/currency/
  RPC overrides no longer leak across the suite.
- 032 — Each WC hook now exports a named return type
  (ImportSignerFlowResult, ReconnectFlowResult, SwitchNetworkResult,
  WalletConnectSigningResult). types.ts imports those instead of using
  `Pick<ReturnType<typeof useX>>`. Same compile-time guarantee, narrower
  dependency footprint.
- 034 — Extracted clearSession helper in WalletConnectContext.e2e.tsx so
  the collision branch + disconnect callback share one implementation.
  reconnect mismatch branch now also calls clearSession before routing
  to ReconnectError, mirroring useReconnectFlow's production semantics.
- 035 — extraReducers in notificationsSlice / signersSlice / safesSlice /
  activeSignerSlice now return fresh objects on reset rather than the
  module-level initialState reference. Defensive against future in-place
  mutation.

P3:
- 038 — Trimmed verbose comment preambles in connect-signer-collision.yml
  and reconnect-wrong-wallet.yml. Doc retention decision deferred.

Verification:
- yarn workspace @safe-global/mobile type-check ✓
- yarn workspace @safe-global/mobile lint ✓
- yarn workspace @safe-global/mobile prettier ✓
- jest (9 suites / 103 tests) ✓
- maestro suite (6/6 child tests) ✓

* refactor(mobile): hoist resetE2EState handling to root reducer

Wrap the combined reducer with `withE2EReset` so every slice resets to its
`initialState` automatically. Slices that need custom reset behavior (only
settingsSlice, which preserves `onboardingVersionSeen`) keep their own
`extraReducers` — the wrapper detects which slices changed in response to
the action and preserves those values.

Drops the boilerplate `extraReducers` from signers, activeSigner, safes,
activeSafe, estimatedFee, executionMethod, and notifications slices. New
slices are now covered by the e2e reset automatically; opt-in is only
needed for selective-reset cases.

* fix(mobile): ensure chains are re-fetched after redux reset in e2e tests

Updated the `resetReduxForE2E` function to dispatch a re-fetch for chains configuration immediately after resetting the Redux state. This change prevents `selectActiveChain` from returning `undefined`, ensuring that the `WalletConnectGate` correctly identifies the active signer during the pending transaction flow. This addresses potential issues with the execution method fallback and improves the reliability of the e2e tests.

* fix(mobile): prevent opening explorer link if block explorer URI is not defined

Added a check in the `viewOnExplorer` function to ensure that the `activeChain` has a valid `blockExplorerUriTemplate` before attempting to generate and open the explorer link. This prevents potential errors when the URI is undefined.

---------

Co-authored-by: Daniel Dimitrov <[email protected]>
2026-05-04 11:29:08 +02:00
..

E2E Tests

The E2E tests are written for Maestro

Use Cursor together with Meastro Studio. In Cursor attach the maestro docs, screenshots, any source file or test file that would be of help for the model to write the tests. Guide the AI - tell it what test you want to write, how it should be structured, point it to a similar test, give it the screenshots, tell it which steps follows after which, tell it if it needs to do something like scroll in the test. With this the test should be 99% correct. You run it in Maestro and make small modifications.

Quick Start

# Run all tests
maestro test .

# Run smoke tests (fast validation)
maestro test --include-tags smoke tests/

# Run pending transaction suite
maestro test tests/transactions/pending/__suite__.yml

# Run single test
maestro test tests/transactions/pending/send-transaction.yml

Structure

e2e/
├── tests/                          # All runnable tests
│   ├── onboarding/
│   ├── assets/
│   ├── transactions/
│   │   ├── pending/__suite__.yml   # Suite for fast execution
│   │   └── history/__suite__.yml
│   └── settings/
└── utils/                          # Reusable utilities (not run standalone)
    ├── setup/
    ├── assertions/
    └── components/

Tags

  • smoke - Critical path tests (run on every PR)
  • onboarding, assets, transactions, settings - Feature categories
  • pending, history - Transaction subcategories

Test Modes

Development (Fast Iteration)

maestro test tests/transactions/pending/send-transaction.yml

PR Validation (Smoke Tests)

maestro test --include-tags smoke tests/

Feature Testing

maestro test --include-tags pending tests/
maestro test --include-tags cow-protocol tests/

Full Run (Nightly)

maestro test tests/transactions/pending/__suite__.yml
maestro test tests/transactions/history/__suite__.yml
maestro test tests/onboarding/__suite__.yml
maestro test tests/assets/__suite__.yml