Setting Up Vitest in a Tauri + Vite Monorepo Package
Setting Up Vitest in a Tauri + Vite Monorepo Package
Problem
The apps/desktop package had no unit test framework. Utility functions like parseCommandError had no test coverage,
and there was no established pattern for adding frontend tests alongside Tauri + Vite.
Symptoms:
- No
testscript inapps/desktop/package.json - No test runner installed —
pnpm --filter desktop testfails - Jest would require
--experimental-vm-modulesbecause the package uses"type": "module" - Path aliases (
@/) would need separate configuration in a Jest setup
Investigation
Options Considered
- Jest — Industry standard, but ESM support requires
--experimental-vm-modulesand a separatemoduleNameMapperconfig for path aliases. Multiple devDependencies needed (jest,ts-jestor@swc/jest,@types/jest). - Vitest — Reuses existing
vite.config.ts(path aliases work automatically), ESM-native (no experimental flags), single devDependency. Designed for Vite projects.
Vitest was the clear choice given the existing Vite setup.
Root Cause
Not a bug — this was a greenfield setup. The key decisions that matter for future packages are documented below.
Solution
1. Install Vitest
pnpm --filter desktop add -D vitestSingle dependency. No @types/vitest needed — the triple-slash reference handles types.
2. Add Vitest reference and test config to vite.config.ts
/// <reference types="vitest" />import { defineConfig } from "vite";
export default defineConfig({ // ... existing config ... test: { include: ["src-react/**/*.test.ts", "src-react/**/*.test.tsx"], }, // ... existing config ...});Key decision: test.include scoping. Without this, Vitest would scan the entire package directory including
src-tauri/ (Rust code) and e2e/ (Playwright specs). The include pattern restricts discovery to React/TypeScript
source only.
3. Add test script to package.json
"test": "vitest run"vitest run executes once and exits (CI-friendly). Use vitest (no run) for watch mode during development. The
existing turbo.json test task auto-discovers this script — no Turbo config changes needed.
4. Use explicit imports (not globals)
import { describe, it, expect } from "vitest";This avoids needing globals: true in the Vitest config and adding "vitest/globals" to tsconfig.json’s types
array. Each test file is self-contained.
Trade-off: Slightly more verbose per file, but zero additional configuration and no risk of global type conflicts
with Playwright’s expect (used in e2e/).
Implementation Notes
- Test files are co-located next to source:
errors.test.tsbesideerrors.ts - Pure utility tests don’t need
jsdomenvironment — runs in Node by default - Component tests (future) will need
environment: 'jsdom'orhappy-dom
Prevention
Best Practices
- Co-locate tests next to their source files (
foo.test.tsbesidefoo.ts) - Use explicit vitest imports to avoid global conflicts with Playwright
- Scope
test.includein packages with mixed content (Rust, E2E, frontend) - Use
vitest runin CI scripts,vitest(watch mode) for local dev
Adding Tests to a New Package
Follow this same pattern for any new packages/* or apps/* entry:
pnpm --filter <pkg> add -D vitest- Add
/// <reference types="vitest" />+testblock tovite.config.ts - Add
"test": "vitest run"topackage.jsonscripts - Turbo picks it up automatically — no
turbo.jsonchanges
Warning Signs
ReferenceError: describe is not defined— missing vitest import (if not using globals)- Tests picking up Playwright specs or Rust test files — missing
test.includescoping Cannot find module '@/...'in tests — not using Vitest with Vite config (path aliases)
References
- Commit:
d76b667on branchmatt/ink-50-create-page-does-not-persist-to-filesystem - Vitest + Vite integration docs
- Related:
parseCommandErrortests cover the utility documented indocs/solutions/integration-issues/tauri2-structured-error-serialization.md
Page Tree Loading Flash Masks Successful Refresh Next
Settings Struct Literal Tests Break When Domain Fields Are Added
Was this page helpful?
Thanks for your feedback!