Skip to main content

Testing

The frontend is tested with Vitest and React Testing Library. Tests are colocated next to the code they cover.

Running tests

npm run test # run the suite once
npm run test:watch # watch mode for the tight loop
npm run coverage # V8 coverage report (text + html)
npm run typecheck # tsc --noEmit

Configuration

Vitest is configured in vite.config.ts:

  • Environment: jsdom
  • Globals: enabled (no need to import describe/it/expect)
  • Setup file: ./src/test/setup.ts
  • Test match: src/**/*.{test,spec}.{ts,tsx}
  • Coverage: V8 provider, including src/**/*.{ts,tsx} and excluding test files, src/test/**, and src/main.tsx

The setup file src/test/setup.ts wires up @testing-library/jest-dom matchers and cleans up the DOM after each test.

Conventions

  • Colocate tests. Put Thing.test.tsx next to Thing.tsx (or thing.test.ts next to thing.ts).
  • Test behavior, not internals. Prefer rendering a component and asserting on what the user sees over reaching into implementation details.
  • Validate schemas. Widgets ship a Zod schema and a schema.test.ts — keep schema tests in step with config changes.

Examples already in the codebase:

  • src/widgets/widgets/world-clock/schema.test.ts — schema validation
  • src/widgets/widgets/arcgis-map/ConfigPanel.test.tsx — config panel behavior
  • src/workspaces/types.test.ts — model factory helpers
  • src/lib/utils.test.ts — shared utilities

Local workflow before committing

After code changes, run the tests relevant to what you touched, then the affected side's full suite once before committing. CI runs everything on push.

  • Touched src/npm run typecheck && npm run test
  • Touched both frontend and backend, or shared config/CI → run both sides
  • Trivial changes (docs, comments, formatting) → rely on CI

Use watch mode (npm run test:watch) for the tight inner loop.