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
..