InfinitiBit Design System

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:

GateCatchesRuns via
Axe accessibilityWCAG 2.2 AA structure violations (auto, every story)pnpm gate
Keyboard & focusControls not operable by keyboard (per-component play)pnpm gate
RSC smokeComponents unsafe across the server/client boundary (next build)pnpm gate
Visual regressionUnintended pixel changes vs committed baselinespnpm 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 gate check runs and reports on every PR, and the team rule is don't merge a red PR. After a GitHub Team upgrade, one gh api call turns the same check into a hard block — no code change. See ADR 0008 and docs/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:

  1. Add a 3-line vitest.config.ts that extends the base:

    import { defineConfig, mergeConfig } from 'vitest/config';
    
    import base from '../../vitest.config.base';
    
    export default mergeConfig(base, defineConfig({}));
  2. Add a test script: "test": "vitest run".

  3. Write tests next to the code (src/**/__tests__/*.test.ts(x)); describe, it, expect are globals, and render from @testing-library/react works 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:

CheckGuards
verify:lint-rulespackage-boundary + icon-import lint rules (fixtures)
verify:ui-build@infinitibit_gmbh/ui builds with working ESM + types; core import excludes chart/data runtime
verify:iconsicon sprite/manifest generate from the alias source; no drift
verify:tokenstokens generate from the DTCG source; no drift
verify:bundle-budgetcore stays within size budgets (no chart/data leaks)
verify:storybookStorybook 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.

On this page