Skip to main content

Testing

The platform has three test layers: Vitest unit tests, Vitest integration tests against a real Postgres branch, and Playwright end-to-end tests driven by a mock wallet. A subset of the e2e suite is tagged @live and runs against a real testnet.

Commands​

All commands run through pnpm and are defined in package.json:

CommandWhat it runs
pnpm testvitest run --passWithNoTests — the full Vitest run (every project)
pnpm test:unitvitest run --project unit — unit tests only
pnpm test:integrationvitest run --project integration --passWithNoTests — integration tests only
pnpm test:e2eplaywright test --grep-invert @live — Playwright e2e, excluding @live
pnpm test:liveplaywright test --grep @live — only the @live smoke tests
pnpm test:coveragevitest run --coverage — Vitest run with a v8 coverage report
tip

Run pnpm lint and pnpm typecheck alongside the tests — they catch unused imports and type errors that the test runner does not.

Unit tests (Vitest)​

Defined as the unit project in vitest.config.ts. They run in the happy-dom environment and pick up any *.test.ts / *.test.tsx file under src/, excluding *.integration.test.*. @testing-library/jest-dom matchers are registered via a setup file, so component and DOM-shaped assertions work out of the box.

These are the fast, dependency-free tests — pure functions (queue serialization, listing-metadata parsing, committed-prefix math) and React component behavior. No database or network is involved.

Integration tests (Vitest)​

Defined as the integration project in vitest.config.ts. They run in the node environment and pick up *.integration.test.{ts,tsx} files under src/ (co-located with the code they exercise — for example the /api/flow-council/** route handlers under src/app/api/flow-council/).

Integration tests hit a real Neon Postgres test branch, not a mock. Key configuration:

  • globalSetup (tests/setup/global.ts) runs prisma migrate deploy against the test branch before any test, targeting it via COUNCIL_DATABASE_URL (overridden from TEST_DATABASE_URL) so production is never touched. It retries on transient Neon/advisory-lock errors.
  • setupFiles (tests/setup/integration-env.ts) loads the per-test environment.
  • fileParallelism: false — the suite shares a single branch, so files run sequentially to avoid foreign-key races during reset and seed.
  • hookTimeout and testTimeout are raised to 30s to ride out Neon cold-start latency.

You must provide TEST_DATABASE_URL (in .env.test.local at the repo root) for these to run; without it globalSetup throws.

End-to-end tests (Playwright)​

Configured in playwright.config.ts. Test files live in tests/e2e/ and match **/*.e2e.ts. They run against a real Next.js server with a seeded test database and a browser-side mock Ethereum provider — there is no real wallet extension.

How the mock works (see tests/e2e/README.md for the full write-up):

  • tests/e2e/setup/global.ts seeds the database once and writes a fixture JSON to the OS tmpdir; each test reads it for the seeded round/council/project/application IDs.
  • Each test calls installMockWallet(page), which exposes a Node-side viem signer (window.__nodeSign) for personal_sign and injects a fake EIP-1193 provider at window.ethereum. The provider presents as MetaMask on OP Sepolia (chainId 11155420) with the address derived from TEST_PRIVATE_KEY.
  • The sign bridge only signs Sign-In with Ethereum messages; eth_signTypedData_v4 is rejected by design, so EIP-712 flows can't be driven through it.

The covered flows are wallet connection, SIWE sign-in, project creation, application submission, application review, and fund-stream navigation. Run order is sequential (fullyParallel: false) because the seeded DB and singleton wallet mock make concurrent runs unreliable.

To run locally, install the browser, provide .env.test.local, start the dev server, and run the suite:

pnpm exec playwright install chromium
pnpm dev
# in another terminal:
pnpm test:e2e

The base URL defaults to http://localhost:3000; override it with E2E_BASE_URL to point at a deployed preview.

Live smoke tests (@live)​

Tests tagged @live are excluded from the default pnpm test:e2e run and selected by pnpm test:live. They exercise the same flow against real OP Sepolia instead of a CI-safe gated render, so they require testnet access. In CI they run on pushes to main, not on every PR.