Testing & CI
The pnpm gate, the PR-gate workflow, the shared test config, and how to add tests.
Testing & CI
Quality is enforced by a single command that runs the same way locally and in
CI. There is one source of truth — pnpm gate — and CI just runs it.
pnpm gate
pnpm gate # lint && type:check && test && build
Run it before pushing; if it's green locally, CI is green by construction. The
test step also runs the verify:* checks (below). Most quality slices (axe,
keyboard, RSC smoke) extend pnpm gate, so they flow through both the local
command and CI automatically.
The one exception is the visual gate: its committed screenshots are only
stable inside a pinned container, so it runs as a separate pnpm gate:visual
(Docker) instead of inside the bare pnpm gate. The complete pre-push check is:
pnpm gate && pnpm gate:visual
See the Visual regression gate page for the why and the workflow.
Quality gates
Four component-quality gates ride this harness, each with its own page:
| Gate | Catches | Runs via |
|---|---|---|
| Axe accessibility | WCAG 2.2 AA structure violations (auto, every story) | pnpm gate |
| Keyboard & focus | Controls not operable by keyboard (per-component play) | pnpm gate |
| RSC smoke | Components unsafe across the server/client boundary (next build) | pnpm gate |
| Visual regression | Unintended pixel changes vs committed baselines | pnpm gate:visual |
The PR gate (CI)
.github/workflows/ci.yml runs pnpm gate on every pull request (and merge
queue). It's separate from the push-only deploy.yml.
Enforcement is plan-dependent. GitHub does not enforce branch protection on private repos on the Free plan, so today the gate is advisory: the
gatecheck runs and reports on every PR, and the team rule is don't merge a red PR. After a GitHub Team upgrade, onegh apicall turns the same check into a hard block — no code change. See ADR 0008 anddocs/agents/branch-protection.md.
Adding tests to a package
Library packages share a root vitest.config.base.ts (jsdom + Testing
Library + @testing-library/jest-dom + globals). To add tests to a package:
-
Add a 3-line
vitest.config.tsthat extends the base:import { defineConfig, mergeConfig } from 'vitest/config'; import base from '../../vitest.config.base'; export default mergeConfig(base, defineConfig({})); -
Add a
testscript:"test": "vitest run". -
Write tests next to the code (
src/**/__tests__/*.test.ts(x));describe,it,expectare globals, andrenderfrom@testing-library/reactworks out of the box.
turbo run test picks up any package with a test script. Apps (e.g. the docs
site) keep their own Vite-integrated vitest.
What the gate aggregates
pnpm test runs turbo run test plus repo-level verify:* checks:
| Check | Guards |
|---|---|
verify:lint-rules | package-boundary + icon-import lint rules (fixtures) |
verify:ui-build | @infinitibit_gmbh/ui builds with working ESM + types; core import excludes chart/data runtime |
verify:icons | icon sprite/manifest generate from the alias source; no drift |
verify:tokens | tokens generate from the DTCG source; no drift |
verify:bundle-budget | core stays within size budgets (no chart/data leaks) |
verify:storybook | Storybook builds; default + docs themes present |
verify:rsc | @infinitibit_gmbh/ui is RSC-safe (a real Next App Router next build) |
After changing dependencies
Incremental pnpm add can drift pnpm's peer-dependency graph (it once broke the
Storybook addon's React resolution). If a build fails resolving a dependency
right after an install, do a clean reinstall:
rm -rf **/node_modules && pnpm install
CI always installs with --frozen-lockfile, so it gets a consistent graph.