Skip to content

feat(tester): SIWS test-wallet harness (//Alice) + runner fix#62

Merged
sacha-l merged 1 commit intodevelopfrom
feat/siws-test-wallet-harness
Apr 24, 2026
Merged

feat(tester): SIWS test-wallet harness (//Alice) + runner fix#62
sacha-l merged 1 commit intodevelopfrom
feat/siws-test-wallet-harness

Conversation

@sacha-l
Copy link
Copy Markdown
Collaborator

@sacha-l sacha-l commented Apr 24, 2026

Summary

Unblocks autonomous Playwright verification of SIWS-gated write flows. Injects a synthetic Polkadot-JS extension backed by the well-known //Alice dev keypair so stadium-tester can sign in a headless browser. Phase 1 spec §6 flagged this as the biggest agentic-velocity blocker — six of the thirteen Phase 1 issues had write flows the tester couldn't exercise.

Also fixes a latent bug in the tester runner that's why recent PRs (#45 #46 #47 etc.) all shipped with unchecked Playwright boxes in their test plans — the runner had never actually worked end-to-end from /tmp.

What's in the diff

Client:

  • client/src/lib/testWalletInjection.ts (new) — registers window.injectedWeb3['polkadot-js'] synchronously at module load; enable() lazily derives //Alice via @polkadot/keyring and returns real sr25519 signatures. Double-gated on VITE_USE_TEST_WALLET === 'true' && !import.meta.env.PROD — a prod build DCEs the whole block.
  • client/src/main.tsx — imports the injection at the top so it registers before AdminPage starts polling window.injectedWeb3.
  • client/src/lib/mockWinners.ts — Alice's SS58-42 address seeded as a team member on plata-mia-15ac43 so team-gated flows pass the client-side team check.
  • client/package.json@polkadot/keyring promoted to explicit dep (was transitive); @playwright/test promoted to explicit devDep so fresh clones get it from npm install instead of setup.sh.

Tester skill:

  • .claude/skills/stadium-tester/scripts/run-playwright.mjs — fixes the runner. Specs and the generated config both live in /tmp, but they import @playwright/test as a bare specifier; ESM walks up from the importer's directory and /tmp has no node_modules, so resolution fails. The runner now rewrites from '@playwright/test' in the spec and the generated config to the absolute path client/node_modules/@playwright/test/index.mjs. Also forwards VITE_USE_TEST_WALLET through the Playwright spawn env.
  • .claude/skills/stadium-tester/SKILL.md — new "Test-wallet mode" section documenting when/how to use the harness, and what still can't run (webzeroApprove / requestChanges / confirmPayment have no mock-mode branch in api.ts, so they stay SKIPPED pending separate mock coverage).
  • client/.env.example, docs/AGENT_GUIDE.md — env vars + Vercel Preview-only config guidance.

Guardrails

  • Double-gated injection. Even if VITE_USE_TEST_WALLET=true leaks into a prod build, the !import.meta.env.PROD check makes the whole injection statically false, so Rollup DCEs it.
  • Prod-safety verified. VITE_USE_TEST_WALLET=true npm run build produces a dist with zero references to the __TEST_WALLET_ENABLED__ sentinel — confirmed by grepping every file in dist/assets/*.js.
  • Alice stays out of prod auth lists. Alice's mnemonic is publicly known — anyone could sign as her. The PR adds her to VITE_ADMIN_ADDRESSES only via Vercel Preview env var (dashboard action, not code). Production's VITE_ADMIN_ADDRESSES stays unchanged; the server's ADMIN_WALLETS / AUTHORIZED_SIGNERS on prod must not include her either.
  • Prod-URL denylist unchangedrun-playwright.mjs still refuses stadium.joinwebzero.com with exit 3.
  • BYPASS_ADMIN_CHECK unchanged.

Verification

Local end-to-end against http://localhost:8080 with VITE_USE_MOCK_DATA=true VITE_USE_TEST_WALLET=true npm run dev:

Running 3 tests using 1 worker
  ✓ test-wallet harness is active on dev server
  ✓ injected extension registers under window.injectedWeb3["polkadot-js"]
  ✓ injected extension yields //Alice account and signs raw data
  3 passed (3.5s)

enable() returns the canonical SS58-42 Alice address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY and signRaw produces a 0x-prefixed sr25519 signature that verifies through @polkadot/util-crypto.signatureVerify — i.e. if this were pointed at a real server (follow-up), the same signatures would pass the real auth middleware.

server tests:  PASS (66/66)
client build:  PASS
client lint:   98 pre-existing (90 errors, 8 warnings) — NOT introduced by this PR

Lint baseline is broken on develop (confirmed via git stash && npm run lint — same 98). The only GitHub Actions workflow is claude.yml; no CI has ever enforced npm run lint. Not fixed here — separate cleanup PR.

Vercel Preview config needed (dashboard, outside code)

Before this harness is useful on preview URLs:

  1. Set VITE_USE_TEST_WALLET=true on the Preview env (not Production).
  2. Append 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY to VITE_ADMIN_ADDRESSES on the Preview env (not Production).

Without these, SIWS-gated scenarios targeting a preview URL still get SKIPPED (needs-auth-harness) — the harness is inert without the build-time flag.

Out of scope (follow-ups)

  • Admin-approval mock-mode gaps. webzeroApprove, requestChanges, confirmPayment in client/src/lib/api.ts have no mock branch — they always hit the real API. Harness doesn't help those. Separate small PR to add mocks, or keep them SKIPPED.
  • Real-staging server verification. Adding Alice to AUTHORIZED_SIGNERS on a staging Railway env + seeding team_members in staging Supabase would let the tester hit a real backend. Needs a staging env to exist first.
  • Pre-existing lint debt on develop. 98 problems. Separate cleanup PR.

Test plan

Automated (already passed locally):

  • cd server && npm test → 66/66.
  • cd client && npm run build → clean.
  • Tester harness check against local dev server → 3/3 pass.
  • Prod-URL denylist fires (exit 3) against stadium.joinwebzero.com.
  • Prod build (VITE_USE_TEST_WALLET=true npm run build) DCEs the injection — grep __TEST_WALLET_ENABLED__ in dist/assets/*.js returns 0 hits.

Against the Vercel Preview built from this branch (after dashboard env is set):

  • /m2-program/plata-mia-15ac43 Updates tab visible; "Post update" button visible when connected as the injected Alice wallet.
  • Clicking Post update → modal opens → submit triggers SIWS signRaw (no human prompt) → new update appears at top of list (mock-mode in-memory write).
  • /admin → injected Alice is auto-selected as admin account; Programs table renders.

Non-code (manual before next use):

  • VITE_USE_TEST_WALLET set on Vercel Preview only.
  • Alice's address appended to VITE_ADMIN_ADDRESSES on Vercel Preview only.

Injects a synthetic Polkadot-JS extension in the browser, backed by a real
sr25519 keypair (//Alice), so the stadium-tester Skill can exercise SIWS-gated
write flows in mock-mode preview URLs without a human signing step. Phase 1
spec §6 called this out as the biggest agentic-velocity blocker — six of the
thirteen Phase 1 issues had Playwright-untestable write paths.

Client:
- testWalletInjection.ts: registers window.injectedWeb3['polkadot-js'] with a
  lazy enable() that derives //Alice and signs with pair.sign(). Double-gated:
  VITE_USE_TEST_WALLET=true AND !import.meta.env.PROD. Prod builds DCE the
  injection entirely (verified — __TEST_WALLET_ENABLED__ string not present in
  any dist/*.js chunk even with the flag set at build time).
- main.tsx imports the injection at the top so it registers before React mounts
  and before AdminPage's 5s window.injectedWeb3 poll starts.
- mockWinners.ts: Alice's SS58-42 address added as a team member on
  plata-mia-15ac43 so team-gated flows pass the client-side team check.
- @polkadot/keyring added as explicit dep (was transitive via api/extension-dapp).

Tester skill:
- run-playwright.mjs: fixes a latent bug where specs/configs in /tmp couldn't
  resolve the bare '@playwright/test' specifier (ESM walks up from the
  importer's dir; /tmp has no node_modules). Rewrites spec imports and the
  generated config to use the absolute path under client/node_modules. This
  is why recent PRs' Playwright scenarios all shipped as unchecked boxes — the
  runner had never actually worked end-to-end.
- @playwright/test promoted from "setup.sh installs on each machine" to an
  explicit devDep so fresh clones get it via npm install.
- Forwards VITE_USE_TEST_WALLET through the Playwright spawn env.
- SKILL.md: Test-wallet mode section documenting when/how to use it, which
  flows still can't run (admin-approval paths with no mock-mode branch in
  api.ts), and the hard prod-prohibition.
- AGENT_GUIDE.md: Vercel Preview config note — VITE_USE_TEST_WALLET + Alice in
  VITE_ADMIN_ADDRESSES on Preview only, never Production.

Verified locally:
- cd server && npm test → 66/66 pass.
- cd client && npm run build → clean.
- harness check via real runner: 3/3 tests pass against
  http://localhost:8080 (VITE_USE_MOCK_DATA=true VITE_USE_TEST_WALLET=true
  npm run dev) — flag sentinel set, injection registered, enable() yields
  canonical Alice address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
  and produces a 0x-prefixed sr25519 signature.
- prod-URL guard: exit 3 on https://stadium.joinwebzero.com, unchanged.
- prod-build safety: VITE_USE_TEST_WALLET=true npm run build produces a dist
  with zero references to the injection sentinel — DCE works.

Pre-existing lint (98 problems on develop, none introduced here) not fixed
here — separate cleanup PR.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

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

Project Deployment Actions Updated (UTC)
stadium Ready Ready Preview, Comment Apr 24, 2026 0:11am

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