backend
Layout
Tests live in a flat tests folder in the API, one file per behaviour, for example billing-enforcement.test.ts, client-plan-filters.test.ts or app-trust-proxy.test.ts. The tests/unit and tests/integration split described in backend/docs/TESTING.md does not exist.
Running
In turbo.json the test task depends on ^build, so Turborepo builds the packages a workspace depends on before it runs that workspace’s tests.
pnpm test:integration runs vitest run --config vitest.integration.config.mts in the API. That config file is not in the repository at the time of writing, so the script fails. Treat the integration suite as not set up.
Style
- Construct a service with a fake repository, a plain object that matches the repository’s shape, and assert behaviour. Most tests need no database.
- HTTP-level tests build the Express app with a test context and drive it with
supertest.
- Cover each branch that throws an
AppError, each role gate, and tenancy: a request scoped to studio A must not read or change studio B’s data.
- Webhook handlers need a test that a bad token or signature is rejected.
- ESLint relaxes
no-console, no-explicit-any and the process.env rule inside tests/ and *.test.ts files and applies the Vitest plugin’s recommended rules.
Versions
pnpm-workspace.yaml overrides vitest to 3.2.6 and vite to 6.4.3 for the whole workspace, whatever the individual manifests say.
frontend
Vitest
apps/saas/vitest.config.ts:
globals: true, so describe, it and expect need no import.
- Default environment is
node. Component tests that need a DOM opt in per file with a // @vitest-environment happy-dom comment on the first line.
- Excludes
node_modules, .next and the tests folder, which belongs to Playwright.
- JSX uses the automatic runtime to match Next.js.
- Mirrors the path aliases from
tsconfig.json.
Tests sit next to the code they cover, as *.test.ts or *.test.tsx. There are about 175 of them under apps and packages.
When you test a component that imports a Radix primitive, set up the DOM stubs before you import the component. Radix reads browser globals at import time.
Playwright
apps/saas/playwright.config.ts:
The suite is small. At the time of writing apps/saas/tests contains login.spec.ts only. Playwright does not run in CI. It needs a running core API with a database behind it.
mobile
There is no Jest or Vitest setup. Pure logic is tested with Node’s built-in test runner. Each file under mobile/scripts/test-*.cjs transpiles the TypeScript source file it covers with the typescript package, loads it in a VM with explicit mocks for its imports, and asserts with node:assert/strict.
There is no test script in package.json and CI does not run these files. Run the relevant one by hand when you touch the code it covers. Current files cover meal targets, nutrition portions, added food amounts, rep ranges, workout weeks, exercise substitutes, plan kinds, range sliders, modal overflow, content chips and filters, upload retry, the image shrink guard, the health first-launch prompt, trainee sessions and step-away handling.
Anything that depends on native behaviour (notifications, HealthKit, Health Connect, Live Activity, RTL, keyboard handling) has to be checked on a device or simulator.
Worker limits
No repository sets a Vitest worker limit. Playwright is limited to one worker only in CI. Type-checks and full test runs are heavy, so do not run several of them at once on one machine. To cap workers locally, pass the flag yourself:
What to run before you push