Skip to content

Quality & testing

The repository's contributing guide puts it in one line: a test that would pass with the feature deleted is worse than none. The suites below are built to that standard, and several of them were written against the broken build first to prove they fail.

The numbers

Suite Count Runner
Web unit and module tests 3,672 tests in 202 files Vitest, Node environment
Web end-to-end 13 specs in two Playwright projects (desktop Chromium; 390×844 mobile) Playwright + axe-core
API service 447 tests (357 test functions) in 22 files pytest
Agent service 109 tests in 8 files pytest
Python client 6 test files pytest

All of them run on every push and pull request. None needs a network, an API key or a vendor SDK.

What the suites prove

Physics

The engine's unit tests assert analytic values (Bell, GHZ, Grover finds |11⟩ with probability 1, teleportation lands RY(π/3) on qubit 2 with P(1) = 0.25), properties over random circuits (norm stays 1, circuit-then-inverse is the identity, simulateSteps agrees with simulate), and exact QASM round-trips. The cross-checks against Qiskit and PennyLane are described in Verification. Every physics module beside a visualization has a test against a closed form; every learned component trains with the one tested optimiser.

Boundaries

Three tests walk import graphs rather than check behaviour: lesson bodies must be unreachable from client modules, exam questions must be unreachable from client modules, and a module marked server-only must not be imported by anything that ships. These are the tests that make the paywall structural.

Security

Each audit finding has a regression test named in docs/SECURITY.md: the hardware gate chain (submitGate.test.ts), the shared-secret middleware including blank and trailing-comma entries, BYOK header parsing and isolation, rate-limit route coverage, webhook SSRF validation, agent injection clamps, the plan-copy-matches-plan-limits invariant, and the store-app detection script with look-alike referrers and parameters.

End-to-end

Spec Proves
routes Every route returns 200, has exactly one h1, a unique title and no console errors
a11y and axe 21 hand-written structural checks plus axe-core at WCAG 2.1 AA tags
mobile and responsive-matrix Phone journeys at 390×844; no horizontal overflow and nothing hidden under the tab bar at six widths across every route
simulator, challenges, onboarding Presets load, gates place by keyboard and pointer, QASM exports, the Bell-pair challenge grades, the guided first circuit completes
learning, exam Course progress survives a reload; the exam timer, resume-after-refresh and score review work
billing The degraded paths: with no Stripe and no Supabase, pricing renders, checkout says so plainly, the account shows Free, all 16 qubits are selectable without an account
search The ⌘K palette opens, filters, navigates and dismisses

The Playwright web server builds the production app rather than running the dev server, so the end-to-end suite tests what ships. Service workers are blocked in tests so a cached page can never mask a regression.

Performance

scripts/bundle-size.mjs enforces a per-route First Load JS budget (measured size + 10 %, rounded up to 0.5 kB) for 97 routes, and a route with no budget entry fails the check so a new page cannot ship unmeasured. The homepage is 239.5 kB; shared JavaScript is 144 kB. docs/PERFORMANCE.md records the work behind the numbers: dynamic imports took the Bloch-sphere page from 239 kB to 108 kB, and pausing animation loops offscreen took a visualization's frames-per-two-seconds from 117 to 0 when scrolled away. The simulator benchmark page times real simulate() calls and diffs against closed-form states; nothing is hardcoded.

Static checks

TypeScript runs in strict mode with tsc --noEmit as a CI step. ESLint extends next/core-web-vitals and the TypeScript preset, with react/no-danger as an error (two reviewed exceptions, each with an inline reason), a no-restricted-syntax rule forbidding toLocaleString() with no arguments because it breaks hydration, and the React Compiler-era hooks rules as warnings. scripts/preflight.mjs validates the environment, lockfile, workspace linkage and configuration files with Node built-ins only, and a warning never fails the run because a zero-variable deployment is valid.

Accessibility and responsiveness

Treated as correctness, with their own page: Accessibility & responsive.

Where the gates run

CI pipeline describes the five jobs, what each fails on, and how the deployment workflow is chained to a green run.