|
| 1 | +# txkit-e2e |
| 2 | + |
| 3 | +End-to-end tests for txKit, run with Playwright against the story |
| 4 | +playground and a local Anvil fork. |
| 5 | + |
| 6 | +**Standalone package** — not part of the pnpm workspace. Has its own |
| 7 | +`node_modules`. See [plans/txkit-testing-strategy-v0.1.md](../plans/txkit-testing-strategy-v0.1.md) |
| 8 | +for the full architecture rationale. |
| 9 | + |
| 10 | +## Setup |
| 11 | + |
| 12 | +```bash |
| 13 | +# 1. Install isolated deps |
| 14 | +cd e2e |
| 15 | +pnpm install --ignore-workspace |
| 16 | + |
| 17 | +# 2. Install Playwright browsers |
| 18 | +pnpm exec playwright install chromium |
| 19 | + |
| 20 | +# 3. Configure RPC for Anvil fork |
| 21 | +cp .env.example .env |
| 22 | +# Edit .env, paste your Alchemy/QuickNode Sepolia URL into SEPOLIA_RPC_URL |
| 23 | + |
| 24 | +# 4. Verify Foundry is available |
| 25 | +anvil --version || curl -L https://foundry.paradigm.xyz | bash && foundryup |
| 26 | +``` |
| 27 | + |
| 28 | +## Run |
| 29 | + |
| 30 | +```bash |
| 31 | +pnpm test # all projects (desktop + mobile) |
| 32 | +pnpm test:desktop # desktop chrome 1280x800 |
| 33 | +pnpm test:mobile # iPhone 14 viewport |
| 34 | +pnpm test:headed # non-headless (watch the browser) |
| 35 | +pnpm test:debug # interactive Playwright debugger |
| 36 | +pnpm report # open last HTML report |
| 37 | +``` |
| 38 | + |
| 39 | +Playwright auto-starts both the story dev server (`pnpm --filter @txkit/story dev`) |
| 40 | +and Anvil fork (`./scripts/anvil-ci.sh`). Just run `pnpm test`. |
| 41 | + |
| 42 | +For smoke tests against the deployed playground: |
| 43 | + |
| 44 | +```bash |
| 45 | +E2E_BASE_URL=https://story.txkit.dev pnpm test |
| 46 | +``` |
| 47 | + |
| 48 | +## Architecture |
| 49 | + |
| 50 | +- **EIP-1193 injection** — `helpers/eip1193-provider.js` replaces |
| 51 | + `window.ethereum` with a stub that forwards to Anvil and bridges |
| 52 | + signing to ethers `Wallet` via `page.exposeFunction`. Adapted from |
| 53 | + StakeWise testwise. |
| 54 | +- **Anvil fork** — Sepolia fork at `localhost:8545`, block-time 2s. |
| 55 | + `anvil_impersonateAccount` + `anvil_setBalance` give each test a |
| 56 | + funded address without needing a real private key. |
| 57 | +- **EIP-6963 announce** — the injected provider announces itself so |
| 58 | + wagmi's connector discovery picks it up automatically. |
| 59 | +- **Risk fixtures** — `fixtures/risk/*.json` capture real Blowfish / |
| 60 | + Blockaid / GoPlus responses. `risk.use('blowfish-warn-token-drain')` |
| 61 | + routes outbound API calls to the fixture instead of upstream. |
| 62 | + |
| 63 | +## Writing a new spec |
| 64 | + |
| 65 | +```ts |
| 66 | +import { test, expect } from '../../fixtures' |
| 67 | + |
| 68 | + |
| 69 | +test('TransactionButton runs full happy path', async ({ page, wallet }) => { |
| 70 | + await wallet.init({ chain: 'sepolia' }) |
| 71 | + await page.goto('/?state=pending') |
| 72 | + await wallet.connect() |
| 73 | + |
| 74 | + await page.locator('[data-testid="transaction-button-main"]').click() |
| 75 | + |
| 76 | + await expect( |
| 77 | + page.locator('[data-testid="transaction-button-main"]') |
| 78 | + ).toHaveAttribute('data-state', 'completed', { timeout: 30_000 }) |
| 79 | +}) |
| 80 | +``` |
| 81 | + |
| 82 | +State of the world is controlled via URL search params (story reads |
| 83 | +them in `useControls()`): |
| 84 | + |
| 85 | +``` |
| 86 | +?state=signing # force a specific TransactionButton state |
| 87 | +?safetyDelayMs=5000 # safety countdown |
| 88 | +?warnMaxApproval=true # max-approval risk warning enabled |
| 89 | +?riskMode=warn # mock risk-provider mode |
| 90 | +?clearSigning=registry|fallback # ERC-7730 vs ABI fallback |
| 91 | +``` |
| 92 | + |
| 93 | +## Debugging a failing test |
| 94 | + |
| 95 | +```bash |
| 96 | +# Replay the trace from a failed run |
| 97 | +pnpm exec playwright show-trace test-results/.../trace.zip |
| 98 | + |
| 99 | +# Or step through interactively |
| 100 | +pnpm test:debug -g "your test name" |
| 101 | +``` |
| 102 | + |
| 103 | +Each retry produces a video (`test-results/.../video.webm`) and |
| 104 | +screenshot for forensics. |
| 105 | + |
| 106 | +## CI |
| 107 | + |
| 108 | +Single Playwright workflow runs both projects with `workers=1` (Anvil |
| 109 | +shared state). Foundry preinstalled via |
| 110 | +`foundry-rs/foundry-toolchain@v1` action. See `.github/workflows/e2e.yml`. |
| 111 | + |
| 112 | +## Conventions |
| 113 | + |
| 114 | +- Test files end in `.spec.ts`, never `.test.ts` |
| 115 | +- Imports through `../../fixtures` (extended `test`, not raw Playwright) |
| 116 | +- Wallet init **before** `page.goto` so injection happens on first frame |
| 117 | +- Test names use plain ASCII (`->` not `→`); they appear in CI logs |
0 commit comments