Skip to content
Documentation GitHub
Test Failures

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 test script in apps/desktop/package.json
  • No test runner installed — pnpm --filter desktop test fails
  • Jest would require --experimental-vm-modules because the package uses "type": "module"
  • Path aliases (@/) would need separate configuration in a Jest setup

Investigation

Options Considered

  1. Jest — Industry standard, but ESM support requires --experimental-vm-modules and a separate moduleNameMapper config for path aliases. Multiple devDependencies needed (jest, ts-jest or @swc/jest, @types/jest).
  2. 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

Terminal window
pnpm --filter desktop add -D vitest

Single 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.ts beside errors.ts
  • Pure utility tests don’t need jsdom environment — runs in Node by default
  • Component tests (future) will need environment: 'jsdom' or happy-dom

Prevention

Best Practices

  • Co-locate tests next to their source files (foo.test.ts beside foo.ts)
  • Use explicit vitest imports to avoid global conflicts with Playwright
  • Scope test.include in packages with mixed content (Rust, E2E, frontend)
  • Use vitest run in 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:

  1. pnpm --filter <pkg> add -D vitest
  2. Add /// <reference types="vitest" /> + test block to vite.config.ts
  3. Add "test": "vitest run" to package.json scripts
  4. Turbo picks it up automatically — no turbo.json changes

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.include scoping
  • Cannot find module '@/...' in tests — not using Vitest with Vite config (path aliases)

References

  • Commit: d76b667 on branch matt/ink-50-create-page-does-not-persist-to-filesystem
  • Vitest + Vite integration docs
  • Related: parseCommandError tests cover the utility documented in docs/solutions/integration-issues/tauri2-structured-error-serialization.md

Was this page helpful?