--- title: Testing description: One Vitest preset shared by every package and app. type: reference related: - /en/docs/deployment/gitlab --- # Testing `@kreogen/testing` exports a Vitest configuration preset. A package that wants tests writes three lines: ```ts title="packages/auth/vitest.config.ts" import { definePreset } from '@kreogen/testing'; export default definePreset({ root: import.meta.dirname }); ``` ```sh bun run test # everything, through turbo bun test --filter auth ``` ## Options | Option | Default | For | | ------------------- | ------------------ | -------------------------------------- | | `root` | required | `import.meta.dirname` of the package | | `environment` | `"node"` | `"jsdom"` for anything rendering React | | `react` | implied by `jsdom` | Adds the React plugin | | `setupFiles` | `[]` | Resolved relative to `root` | | `coverageThreshold` | none | Minimum line and function coverage | The threshold applies only when coverage is collected, so a plain `vitest run` stays fast. ## What the preset settles **The `@kreogen/*` alias.** Packages resolve to source, and that alias has to exist wherever code is resolved. It used to be written by hand in every vitest config as well as in the tsconfig — four copies of one path, with a note in `CLAUDE.md` asking people to remember all of them. Two remain: `packages/typescript-config/nextjs.json`, which the editor and `tsc` read, and this package, which every test run reads. A test in `packages/testing/__tests__` fails when they disagree — nothing else would notice, and the symptom is a suite resolving a stale path and passing against code nobody is shipping. **A `server-only` stub.** That module throws outside a React Server Component graph, and a unit test is not one. Stubbing it is what makes the server modules — the ones that most need testing — importable at all. **Placeholder environment variables.** Every package validates its environment at import through `keys()`, which is right for a running app and wrong for a test asserting on a pure function. `DATABASE_URL`, `BETTER_AUTH_SECRET` and the public URLs get parseable defaults, applied only when genuinely unset — so a suite running against real infrastructure still gets the real values. `DATABASE_URL`'s placeholder is a valid connection string rather than an obviously fake one, because it is handed to a `pg` pool whose constructor parses it eagerly. Nothing connects unless a test issues a query. **Machine-readable output in CI.** JUnit reporter and coverage in `text-summary`, `lcov` and `cobertura`, written under the package directory. That placement matters: turbo runs tests per package, and the pipeline's earlier globs pointed at repository-root paths nothing wrote — so every run published an empty report and no coverage at all, while going green. ## Why it calls `defineConfig` itself `definePreset` returns a finished config rather than an object for the caller to wrap. Wrapping was the obvious API and it does not typecheck: bun resolves more than one copy of vite, and a config produced against one copy's types is not assignable to another's `defineConfig`, even at identical versions. Doing it here means only this package's copy is ever involved. ## What is excluded from coverage `__tests__`, `generated`, `*.config.*` and `keys.ts`. Generated Prisma output and env schemas are not code anyone writes tests for, and leaving them in makes the number meaningless. ## The one suite that boots a server `apps/web/__tests__/routes.test.ts` is the exception to everything above. It starts a real Next server with every declared variable emptied and requests every route, asserting that none answers 5xx, that an unknown post is a 404 and that `/sitemap.xml` matches the pages on disk. It exists because the property it checks cannot be checked structurally. `packages/cms` degraded correctly in its query functions and threw from the `Feed` component beside them, which put `/legal/terms` and `/legal/privacy` — both linked from the sign-up form — on a 500 in any project generated with `cms` and no token. A structural check was tried against both bugs in that route and caught neither. It costs roughly ninety seconds, almost all of it Turbopack compiling routes on first request. If that becomes too slow, shorten the route table rather than replacing the approach. ## In CI The `test` job runs against a real Postgres service. Most suites use stand-ins; that database exists so tenancy and auth are checked against the thing that actually enforces the constraints. --- For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md) For an index of all available documentation, see [/llms.txt](/llms.txt)