ShipKit docs
Guides

Testing

Bun's runner for units, Playwright for the paths that must not break, and one command that gates a commit.

Two layers, and a single command that runs everything a commit has to pass.

bun run verify

That is lint, typecheck, test, rules:check and lint:cyrillic in sequence. It is also what the pre-commit hook runs, so a commit that would fail CI usually fails on your machine first.

bun run verify:changed narrows the same gate to what the working tree touched, which is the one to reach for mid-feature.

Unit tests

33 .test.ts files ship, run by Bun's own runner. No Jest, no Vitest, no configuration file.

bun run test                                  # everything, through Turbo
bun test packages/entitlements                # one package
bun test --watch packages/email               # while writing

They sit next to the code they cover: catalog.test.ts beside catalog.ts. Worth reading before writing your own, because they show what this codebase thinks is worth asserting: render.test.ts renders every email template so a broken one fails in CI rather than in an inbox, and schema.test.ts guards the shape the whole app depends on.

End-to-end

Playwright, two specs, against the real API and a real database.

bun run test:e2e

playwright.config.ts starts both servers itself. Postgres and Redis must already be up:

bun run setup      # or docker compose up -d
bun run test:e2e

The critical-path specs exercise real authentication and real API calls, which is the point: a smoke test against mocks proves the mocks work. It also means these tests write to whatever database .env points at. Point it at a throwaway one.

Useful knobs, all optional:

VariableEffect
PLAYWRIGHT_BASE_URLTest a deployed environment instead of a local server
PLAYWRIGHT_API_URLWhere the API is expected, default http://localhost:3001
PLAYWRIGHT_PORTWeb port, default 3002
PLAYWRIGHT_CHANNELSet to chrome to use the system browser when Playwright's CDN is unreachable

In CI the config sets forbidOnly, retries twice, and switches to the GitHub reporter. trace: "on-first-retry" means a flake leaves a trace file to open rather than a line of red text.

The two checks that are not tests

bun run rules:check regenerates the rules documentation from rules/registry.ts and fails if the committed copy has drifted. The rules page cannot silently stop matching the rules.

bun run lint:cyrillic fails on non-English text inside .ts and .tsx sources. Translations belong in JSON catalogs, where a translator can edit them without touching code that compiles. See email for the same rule applied to templates.

check-no-cyrillic.sh uses mapfile, which bash 3.2 does not have, so bun run verify fails on stock macOS while every other task passes. Run the other tasks individually there, or install a newer bash.

On this page