Vitest Setup
Detect the project type, generate the right Vitest configuration, and produce working test infrastructure. Not a reference card โ this skill creates files.
Workflow
- Detect โ scan the project to determine type and existing setup
- Configure โ generate vitest.config.ts tailored to the environment
- Scaffold โ create test setup, utilities, and a sample test
- Wire up โ add package.json scripts and TypeScript config
Step 1: Detect Project Type
Read these files to determine the project:
package.json โ dependencies, scripts, type field
tsconfig.json โ paths, compiler options
wrangler.toml โ Cloudflare Workers project
vite.config.ts โ existing Vite setup (extend, don't replace)
vitest.config.ts โ already configured? just fill gaps
jest.config.* โ migration candidate
src/ โ source structure
Classify as one of:
| Type |
Signals |
Environment |
| Cloudflare Workers |
wrangler.toml, @cloudflare/workers-types, cloudflare vite plugin |
node with Workers-specific setup |
| React (Vite) |
@vitejs/plugin-react, react-dom |
jsdom or happy-dom |
| React (SSR/TanStack Start) |
@tanstack/start, vinxi |
Split: node for server, jsdom for client |
| Node/Hono API |
hono, express, no react-dom |
node |
| Library |
exports field, no framework deps |
node |
If a vite.config.ts already exists, extend it rather than creating a separate vitest.config.ts โ Vitest reads Vite config natively.
Step 2: Install Dependencies
Generate the install command based on detected type:
pnpm add -D vitest
pnpm add -D @vitest/coverage-v8 jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
pnpm add -D @vitest/coverage-v8 @cloudflare/vitest-pool-workers
pnpm add -D @vitest/coverage-v8
pnpm remove jest ts-jest @types/jest jest-environment-jsdom babel-jest
Use the project's package manager (check for pnpm-lock.yaml, yarn.lock, bun.lockb, or package-lock.json).
Step 3: Generate vitest.config.ts
Cloudflare Workers
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({
test: {
globals: true,
poolOptions: {
workers: {
wrangler: { configPath: "./wrangler.toml" },
},
},
},
});
If the project uses the Cloudflare Vite plugin (@cloudflare/vite-plugin), integrate into the existing vite.config.ts instead:
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [cloudflare()],
test: {
globals: true,
},
});
React (Vite)
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
css: true,
},
});
If a vite.config.ts already exists, add the test block to it rather than creating a new file.
Node / Hono API
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "node",
},
});
With Coverage (add to any config)
test: {
coverage: {
provider: "v8",
reporter: ["text", "html", "lcov"],
exclude: [
"node_modules/",
"**/*.config.*",
"**/*.d.ts",
"**/test/**",
],
},
},
Step 4: Generate Test Setup File
Create src/test/setup.ts (React projects only):
import "@testing-library/jest-dom/vitest";
That single import adds all the custom matchers (toBeInTheDocument, toHaveTextContent, etc.) and registers the Vitest expect.extend automatically.
Step 5: Add TypeScript Config
Add to tsconfig.json compilerOptions:
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
For projects with multiple tsconfig files (e.g. tsconfig.app.json + tsconfig.node.json), add to the one that covers test files โ usually the root tsconfig.json or create a tsconfig.test.json that extends it.
Step 6: Add Package.json Scripts
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui"
}
}
Don't overwrite existing scripts โ merge with what's there.
Step 7: Generate Sample Test
Write one test file that demonstrates the right patterns for this specific project. Place it next to real source code, not in a separate __tests__ directory.
For a Hono API route (e.g. src/routes/health.ts):
import { describe, it, expect } from "vitest";
import { app } from "../index";
describe("GET /health", () => {
it("returns 200 with status ok", async () => {
const res = await app.request("/health");
expect(res.status).toBe(200);
const body = await res.json();
expect(body).toEqual({ status: "ok" });
});
});
For a React component (e.g. src/components/Button.tsx):
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Button } from "./Button";
describe("Button", () => {
it("renders with label", () => {
render(<Button>Click me</Button>);
expect(screen