mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
* 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]>
E2E Tests
The E2E tests are written for Maestro
Recommended setup for writing tests
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 categoriespending,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