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/**, andsrc/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.tsxnext toThing.tsx(orthing.test.tsnext tothing.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 validationsrc/widgets/widgets/arcgis-map/ConfigPanel.test.tsx— config panel behaviorsrc/workspaces/types.test.ts— model factory helperssrc/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.