Skip to content

test(wallet): Tier-2 browser-signing e2e (Playwright + mock provider on anvil)#380

Merged
MuncleUscles merged 1 commit into
v0.40-devfrom
feat/cli-wallet-e2e
Jul 9, 2026
Merged

test(wallet): Tier-2 browser-signing e2e (Playwright + mock provider on anvil)#380
MuncleUscles merged 1 commit into
v0.40-devfrom
feat/cli-wallet-e2e

Conversation

@MuncleUscles

Copy link
Copy Markdown
Member

What

Adds the Tier-2 browser-wallet signing e2e harness for genlayer-cli, per the design doc. It drives the real daemon bridge page (src/lib/wallet/bridgePage.ts) in a headless chromium with an injected mock window.ethereum, and delegates signing to Node via page.exposeFunction("__glSign", …) where a viem local account (anvil dev key) actually signs + broadcasts eth_sendTransaction to an ephemeral anvil, returning a real tx hash.

So the full loop runs for real — bridge + served page JS + session daemon + real chain — with zero human and zero MetaMask.

How the mock works

  • e2e/fixtures/mockProvider.tsinstallMockProvider({address, chainIdHex, behavior}) returns an init-script string installed via page.addInitScript(...) before page.goto() (beats the injection race). Behaviors: approve | reject (4001) | wrong-network (4901). eth_sendTransactionwindow.__glSign.
  • e2e/helpers/bridgePage.ts — launches chromium, wires __glSign to a viem sendTransaction on anvil, injects the mock, and drives connect.
  • e2e/fixtures/chain.tsstartAnvil() boots anvil on a random port and deploys the recording StakingStub.
  • e2e/fixtures/cli.ts — runs dist/index.js under a scratch HOME with a seeded custom-network config (chain-id / rpc / staking → the anvil + stub) so ensureChain() matches; spawnConnect() scrapes the printed session URL.

Lanes (all green on anvil, ~16s, serial workers:1)

  • S1 connect/status/disconnect — descriptor 0600 {pid live, port, token, address, chainId}, /api/ping 200, "Connected as …", teardown removes descriptor + pid.
  • S2 staking validator-join --wallet browser — page auto-signs → asserted via a real receipt + on-chain stub callCount.
  • S4 session reuse — two sequential joins over one daemon/tab, distinct hashes, same pid.
  • S5 config default walletMode=browser — bare validator-join signs via session; --wallet keystore overrides (keystore path, no enqueue).
  • S6 user-reject (4001 → "Transaction rejected in wallet", session survives) + tab-closed (page.close() → stale heartbeat → fail-fast "tab appears to be closed"). Short GENLAYER_E2E_* timeout overrides keep these in seconds.

Staking stub

e2e/fixtures/StakingStub.sol (+ committed compiled StakingStub.json) is a ~10-line recording stub: validatorJoin() / validatorJoin(address) are payable, record the call (callCount, lastOperator/Validator/Amount), and emit the ValidatorJoin(operator, validator, amount) event the CLI decodes. It proves the sign→broadcast→receipt loop only — no consensus.

Deferred (nightly follow-up)

  • Lane B (S3) — IC deploy on Docker localnet: describe.skip, guarded by GENLAYER_E2E_LOCALNET, documented in e2e/lane-b-deploy.e2e.ts. Bare anvil has no GenVM/consensus.
  • Tier 3 (Synpress / real MetaMask) — out of scope.

Prod-safe support changes

  • sessionConstants.tsGENLAYER_E2E_{LONG_POLL,HEARTBEAT_DEAD,CONNECT_TIMEOUT}_MS overrides; unset/invalid → production defaults unchanged.
  • browserBridge.ts — skip openUrl when GENLAYER_E2E_NO_OPEN is set (harness drives its own chromium); production unaffected.

CI

New .github/workflows/e2e-wallet.yml (distinct name from the synced e2e.yml): ubuntu + node 20 → npm ci → build → foundry-toolchain → playwright install --with-deps chromiumnpm run test:e2e. Informational — keep off required checks until stable, then promote.

How to run

npm run build            # specs spawn dist/index.js
npx playwright install chromium
npm run test:e2e

Verification

  • npm run test:e2e9 passed, 1 skipped (Lane B), stable across 3 runs.
  • npm run test:coverage (Tier-1 vitest) → 733 passed; Playwright specs excluded from the vitest glob.
  • npm run build clean; tsc --noEmit = 32 errors (pre-existing baseline, zero new).

…on anvil)

Add a headless, deterministic Tier-2 e2e harness that drives the real
bridge page (bridgePage.ts) in chromium with an injected mock
window.ethereum. eth_sendTransaction is delegated to Node via
page.exposeFunction(__glSign), where a viem local account (anvil dev
key) actually signs+broadcasts to an ephemeral anvil, so the full loop
runs for real: bridge + page JS + session daemon + chain, with zero
human/extension.

Lanes (all green on anvil):
- S1 connect/status/disconnect (descriptor 0600, /api/ping, teardown)
- S2 validator-join --wallet browser signs + mines against a recording
  StakingStub, asserted via real receipt + on-chain callCount
- S4 session reuse: two sequential joins over one daemon/tab
- S5 config default walletMode=browser; --wallet keystore overrides
- S6 user-reject (4001) + tab-closed fail-fast (short env timeouts)

Lane B (S3, IC deploy on Docker localnet) is deferred as a nightly
follow-up: describe.skip guarded by GENLAYER_E2E_LOCALNET.

Prod-safe support changes: GENLAYER_E2E_* env overrides for the timing
constants (unset in prod) and a GENLAYER_E2E_NO_OPEN guard so the daemon
does not auto-open a system browser under test. New e2e-wallet.yml CI job
(informational, not a required gate initially). Playwright specs are kept
out of the vitest glob so test:coverage is unaffected.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 31e970ee-5801-430d-8c8d-58cc5c61255f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cli-wallet-e2e

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@MuncleUscles MuncleUscles merged commit 4b26cc6 into v0.40-dev Jul 9, 2026
10 of 11 checks passed
@MuncleUscles MuncleUscles deleted the feat/cli-wallet-e2e branch July 9, 2026 20:01
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