Skip to content

feat: expand e2e coverage with new specs and 3-layer transaction verification#393

Open
LautaroPetaccio wants to merge 2 commits into
mainfrom
feat/e2e-gap-fill-and-real-auth-tests
Open

feat: expand e2e coverage with new specs and 3-layer transaction verification#393
LautaroPetaccio wants to merge 2 commits into
mainfrom
feat/e2e-gap-fill-and-real-auth-tests

Conversation

@LautaroPetaccio

Copy link
Copy Markdown
Collaborator

Why

The existing Playwright suite is mature for happy-path login flows, but several critical surfaces had no e2e coverage:

  • /auth/setup (the new SetupPage registered at src/main.tsx:88) had zero tests, even though it's a real onboarding path.
  • The production-default routing under dapps-onboarding-to-explorer = true was only implicitly exercised — no spec asserted the URL the user lands on after login.
  • eth_sendTransaction requests in RequestPage.tsx branch three ways (NFT transfer, MANA transfer, generic fallback) and none of them were tested.
  • OAuth callback edge cases, wallet runtime events (accountsChanged/chainChanged/disconnect), and cached-session corruption were uncovered.

A second, structural gap: every existing spec mocked the auth-server (/v2/requests/**). That meant no test ever exercised real recover/outcome behaviour — the dapp's HTTP contract with the auth-server was never validated end-to-end.

This PR fills both gaps in one branch.

How

1. New helpers in e2e/helpers/setup.ts

Helper Purpose
injectFullSession(context) Generates a real viem keypair per test and uses @dcl/crypto's Authenticator.initializeAuthChain to produce a structurally valid AuthIdentity. Pre-populates decentraland-connect-storage-key and the SSO storage key so the dapp restores the session on first load. The ephemeral key is real, so any later Authenticator.signPayload produces a signature that passes server-side ECDSA verification.
createAuthServerRequest({method, params, identity?}) POST https://auth-api.decentraland.zone/requests to mint a real requestId. For eth_sendTransaction it includes the auth-chain (SIGNER + ECDSA_EPHEMERAL) in the request body (not headers — that was the discovery that unblocked these tests).
captureWalletTransactions(context) Exposes window.__e2eRecordTx so the mock provider's eth_sendTransaction / eth_sendRawTransaction cases journal every submission into a Node-side array. Tests assert on it after clicking Allow.
pollForOutcome(requestId) Polls GET /requests/{id} (the auth-server returns 204 No Content while pending, 200 with {sender, result} once the dapp posts the outcome). Mirrors the pattern from auth-e2e-tests so behaviour is consistent.
mockCatalystDeployRoute, mockNewsletterRoute, mockReferralRoute For the SetupPage deploy path, with body-capture for assertions.

2. Three Playwright projects (playwright.config.ts)

Project Target Mocks
local (default) Local Vite preview on localhost:5174 Full HTTP mocking — auth-server, Catalyst, feature flags, Segment, Thirdweb
real-auth https://decentraland.zone/auth + live auth-api.decentraland.zone Wallet only; everything else is real
real-oauth Same as real-auth + real Google OAuth None — auto-skips without E2E_GOOGLE_EMAIL / E2E_GOOGLE_PASSWORD

webServer.timeout was bumped from 30s to 180s (Vite cold start was timing out). SKIP_WEBSERVER=1 env guard added so projects that target deployed dapps don't spin up a local server.

3. Mock provider extended (e2e/fixtures/ethereum-provider.ts)

eth_sendTransaction and eth_sendRawTransaction now:

  1. Journal {method, params, ts} via window.__e2eRecordTx (no-op if not registered).
  2. Return a deterministic fake hash (0x + 'ab'.repeat(32)).

This means tests can verify the dapp's tx forwarding without actually broadcasting on Polygon — broadcasting would either cost real money or clutter the testnet.

4. Three-layer verification for request-types specs

Each request-types-* spec checks:

  1. Display — does the dapp's decoded UI match the data we encoded? (e.g. MANA tip title contains "5 MANA" after decoding our transfer(addr, 5e18) payload.)
  2. Forwarding — when the user clicks Allow/Confirm, are the tx params the dapp hands to the wallet byte-equal to what we POSTed to the auth-server? (expect(txLog[0].params[0]).toMatchObject(txParams).)
  3. Outcome — does the auth-server record the (stubbed) tx hash under the connected wallet's address? (pollForOutcome returns {sender, result}.)

Together these catch: decoder regressions, accidental tx mangling, missing outcome submission, wrong sender reporting, dropped recover calls.

What's new

11 new spec files, 32 new tests:

Spec Project Tests Adds coverage for
setup-flow.spec.ts local 11 /auth/setup validation, deploy, newsletter, referral, error UI
feature-flag-onboarding-to-explorer.spec.ts local 2 URL routing under production-default flags
wallet-runtime-events.spec.ts local 3 accountsChanged / chainChanged / disconnect mid-session
otp-edge-cases.spec.ts local 4 Wrong OTP, send failure, paste-distribute, keyboard navigation
cached-session-edge-cases.spec.ts local 3 Malformed connection JSON, expired identity, quota-exceeded setItem
social-callback-edge-cases.spec.ts local 3 Direct /callback navigation, server_error, malformed state
request-types-sign-message.spec.ts real-auth 2 dcl_personal_sign auto-sign + verification-code branches
request-types-generic-transaction.spec.ts real-auth 1 eth_sendTransaction generic fallback view
request-types-mana-donation.spec.ts real-auth 1 eth_sendTransaction decoded as MANA transfer
request-types-nft-gift.spec.ts real-auth 1 eth_sendTransaction decoded as ERC721 transferFrom
magic-google-real-oauth.spec.ts real-oauth 1 Real Magic + Google OAuth round-trip (nightly, auto-skips without env)

Trade-offs and gotchas

  • real-auth specs need the dapp deployment to be reachable. They use decentraland.zone/auth and auth-api.decentraland.zone; CI should run them on a separate job that doesn't need the local Vite server.
  • Picking a to address matters. Real DCL contract addresses (e.g. the canonical MANA token) live in decentraland-transactions' registry, which routes the dapp through the meta-tx path. The transaction specs deliberately use a clearly-random address (0x1111...1111) so the dapp takes the regular eth_sendTransaction path that our journal observes.
  • NFT view falls back to generic. With an arbitrary to, the on-chain tokenURI lookup fails and RequestPage falls back from WALLET_NFT_INTERACTION to WALLET_INTERACTION. The spec accepts either terminal Allow CTA. To assert the actual NFT view, a real Decentraland Wearables contract on Amoy would be needed.
  • local regression. All 26 new local-project tests + the 76 existing tests pass with CI=true npx playwright test --project=local.

Test plan

  • CI=true npx playwright test --project=local --workers=1 passes (full local-project regression).
  • SKIP_WEBSERVER=1 npx playwright test --project=real-auth --workers=1 passes (5 tests against live dev auth-server).
  • SKIP_WEBSERVER=1 npx playwright test --project=real-oauth auto-skips without env vars.
  • Manual: set E2E_GOOGLE_EMAIL + E2E_GOOGLE_PASSWORD and run --project=real-oauth once headed to verify the OAuth flow renders correctly.
  • Read through e2e/README.md for project descriptions, env-var requirements, and known caveats.

… 3-layer transaction verification

Why
- Several critical-path flows had no e2e coverage: SetupPage (`/auth/setup`),
  production routing under `dapps-onboarding-to-explorer=true`, NFT/MANA/generic
  `eth_sendTransaction` request types, and OAuth callback edge cases.
- The existing suite mocked the auth-server `/v2/requests/**` endpoints, so the
  request-types specs never exercised real recover/outcome behaviour.

How
- New helpers
  - `injectFullSession` generates a real wallet keypair + AuthIdentity per test
    so signatures pass auth-server verification.
  - `createAuthServerRequest` POSTs to the real auth-server at
    auth-api.decentraland.zone with the auth-chain in the body (the SIGNER +
    ECDSA_EPHEMERAL links), matching how dapps actually create requests.
  - `captureWalletTransactions` exposes `__e2eRecordTx` to the mock provider so
    `eth_sendTransaction` submissions are journalled in Node for assertions.
  - `pollForOutcome` polls `GET /requests/{id}` (204 while pending, 200 with
    `{sender,result}` once submitted) to verify the dapp reported the outcome.
- Three Playwright projects
  - `local` — fast, mock-driven, runs against the local Vite preview.
  - `real-auth` — runs request-types specs against decentraland.zone/auth + the
    live auth-server. Only the wallet is mocked; everything else is real.
  - `real-oauth` — nightly-only spec for the full Magic + Google OAuth round-
    trip; auto-skips without `E2E_GOOGLE_EMAIL`/`E2E_GOOGLE_PASSWORD`.
- request-types specs verify three layers
  1. Display: decoded UI matches encoded tx data (e.g. "Confirm 5 MANA Tip for").
  2. Forwarding: tx the dapp forwards to the wallet on Allow is byte-equal to
     the tx params POSTed to the auth-server.
  3. Outcome: auth-server records the tx hash under the connected wallet.
- Mock provider stubs `eth_sendTransaction`/`eth_sendRawTransaction` with a
  deterministic fake hash to avoid real broadcasts.

Adds 32 new tests across 11 spec files. All `local` and `real-auth` specs pass;
`real-oauth` is wired but skips without test credentials.
@vercel

vercel Bot commented May 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
auth Ready Ready Preview, Comment May 15, 2026 6:25pm

Request Review

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.

1 participant