Testing overview
There are two suites, and every run of either is offline. Neither needs a Gemini key, Neon, the AI Gateway or a Cloudflare login, and neither touches the dev server on :8787 or its data.
| Tool | Runs against | Command | Time | |
|---|---|---|---|---|
| Unit and integration | Vitest 5 | Node (shared, API) and jsdom (front end) | npm test | seconds |
| End to end | Playwright | Chromium against the real Worker under wrangler dev, serving the built SPA | npm run test:e2e | about 30 seconds plus the build |
The one idea that makes it work: the scripted model
api/src/ai/scripted.ts is a second AiProvider adapter. It plays the model's part deterministically: it calls get_claim_section for the section the question is about (and highlight_panel when offered), then streams "Scripted answer about {claim id} — {title}. I looked up: {tools}.". A question containing [scripted:fail] throws.
So the whole real pipeline runs (prompt assembly, the tool loop, the tools reading a real claim, screen actions, SSE, turn ids, prompt fingerprints, feedback) and only the language model is replaced. The answer names the claim from the prompt's brief, so a test can tell the right claim reached the model.
Gemini itself, the Live voice relay, the browser voice client and the generator talk to Google over the network or a live socket. They are excluded from coverage and checked by hand against the real service. A unit test with a mocked SDK would only prove the mock agrees with itself.
Commands
npm test # every Vitest project, once
npm run test:watch # Vitest in watch mode
npm run test:coverage # with v8 coverage and the per-layer gates; HTML in coverage/
npm run test:e2e # build, start the e2e Worker, run every Playwright spec
npm run test:e2e:ui # the same in Playwright's UI mode
npm run test:all # typecheck + Vitest + Playwright: what CI runs
First Playwright run on a machine: npx playwright install chromium.
Coverage gates
npm run test:coverage fails if a layer drops below its gate:
| Layer | Statements | Branches | Functions | Lines |
|---|---|---|---|---|
packages/shared/src | 95 | 95 | 95 | 95 |
api/src | 95 | 80 | 95 | 95 |
frontend/src/{lib,region,chat,embed,brand} | 80 | 70 | 75 | 80 |
promptStore.ts, claimView.ts | 90 | 80 | 90 | 90 |
The gates sit just under what the suite reaches. Page and layout components have no gate: they are presentation over claimView.ts, which is unit-tested, and Playwright exercises them in a real browser. The blended "All files" figure is low for the same reason, so read the per-layer numbers.
Excluded from coverage: the prompt catalogue and scenario briefs (prose and data), the network-bound modules above, main.tsx, and the unlisted /status page.
Read next
- Unit and integration tests: the three Vitest projects and their helpers.
- End-to-end tests: the test Worker and the specs.
- Writing tests: where a new test goes and how to write it.