3 min read
Testing
bun run test # all packages, via turbo (unchanged packages are cache hits)
bun run affected # typecheck + test for packages changed since origin/main
bun run test:coverage
bun run test:sweeps # guardrail sweeps — see /developer-docs/core-concepts/guardrailsOne vitest project per package/app that has tests.
Testing fast (read this before running the suite)
- Test what changed.
bun run affectedrunsturbo run typecheck test --filter=...[origin/main]. For one package:bun run turbo:shared run typecheck test --filter=@commerce/core. A change tocommerce.config.ts,commerce.blocks.config.tsor a plugin package also affects core and everything above it; runbun run testthen (the cache still skips the rest). - Cached.
testandtypecheckare turbo-cached. The hash covers the package, every workspace package it depends on (a new migration re-runs every database suite),.env.test, and the plugin graph for anything built on core. A failed run is never cached. - Shared between worktrees. The root scripts set
TURBO_CACHE_DIRto the main checkout’s.turbo/cache, so a typecheck or test another worktree already ran is a cache hit. SetTURBO_CACHE_DIRto use a different folder. - Parallel files. Database-backed packages run
TEST_WORKERStest files at once (default 3,TEST_WORKERS=1for one at a time). Each worker gets its own copy of the test database, cloned from a migrated template, so files never see each other’s rows. - Skip
next buildunless you changed build config or something only the bundler checks;bun run typecheckcovers types.
TEST_DB_NAMESPACE=agent7 TEST_WORKERS=2 bun run affectedDetails and the reasons behind each piece: docs/guardrails.md, “Testing fast”.
Admin component tests
Admin components are tested with Testing Library on jsdom. A file
named *.test.tsx next to the component starts with // @vitest-environment jsdom; plain
*.test.ts files stay on Node. apps/admin/test/dom.ts (a vitest setup file) stubs what jsdom
lacks (ResizeObserver, IntersectionObserver, matchMedia, scrollIntoView) and unmounts after
each test.
The Customize editor’s panels read the Puck store through useCustomizePuck, so a test mocks
that one hook with a small store (dispatch as a vi.fn(), config.components, selectedItem)
and asserts the Puck actions dispatched, rather than mounting Puck. Query by role and accessible
name from data/customize.ts copy, not by class names. Examples:
apps/admin/src/components/themes/customize/{template-list,template-picker,section-tree-item,link-picker,add-section-browser,section-inspector}.test.tsx.
cd apps/admin && bunx vitest run src/components/themes/customize/link-picker.test.tsxTest databases
Tests never run against the dev database. packages/config/src/test-env.ts loads .env.test
and points DATABASE_URL/DATABASE_URL_OWNER at commerce_test_<suffix> — a separate
database per test package, so suites can run in parallel without stepping on each other.
The test-DB guard. Before running, assertTestDatabase checks the resolved database name
against /^commerce_test(_[a-z0-9_]+)?$/ and throws immediately if it doesn’t match. This is
what stops a misconfigured .env.test (or an accidentally-loaded .env) from a test suite
truncating the real dev or production database — every test run refuses to start unless its
target database is unmistakably a test database.
TEST_DB_NAMESPACE
Parallel worktrees share the same Postgres instance, so without namespacing two worktrees’
test runs would both reset commerce_test_<suffix> and stomp on each other mid-run. Set
TEST_DB_NAMESPACE (letters, digits, underscores, 1-32 chars) per checkout/worktree, and
every package then uses commerce_test_<namespace>_<suffix> instead — created on first use by
the global test setup. That database is the migrated template; the workers run on
commerce_test_<namespace>_<suffix>_w1, _w2, … copies, cloned once and then migrated with the
template on every run. Keep the namespace short enough that the copy names stay within Postgres’s 63 characters
(the setup refuses longer ones).
TEST_DB_NAMESPACE=agent7 bun run testThis also matters for migrations: parallel worktrees that don’t set distinct namespaces can
see migration numbers collide and the __drizzle_migrations log drift between them.