vitest
jezweb/claude-skills
Auto-detect project type and scaffold Vitest testing infrastructure with config, setup, and sample tests.
What is vitest?
Vitest Setup detects your project type (Cloudflare Workers, React, Node, library), generates a tailored vitest.config.ts, creates test utilities and a working sample test, and wires up package.json scripts. Use it whenever you need to add tests, migrate from Jest, or fix testing infrastructure.
- Detects project type by scanning package.json, tsconfig.json, wrangler.toml, and vite.config.ts
- Generates environment-specific vitest.config.ts (jsdom for React, node for APIs, Workers pool for Cloudflare)
- Creates test setup files and TypeScript configuration for globals
- Scaffolds a working sample test matching your project's patterns
- Adds test scripts (test, test:run, test:coverage, test:ui) to package.json
- Handles Jest-to-Vitest migration by identifying and removing Jest dependencies
How to install vitest
npx skills add https://github.com/jezweb/claude-skills --skill vitest- Node.js and a package manager (pnpm, npm, yarn, or bun)
- An existing project with package.json
How to use vitest
- 1.Run the skill to detect your project type and dependencies
- 2.Review and confirm the generated vitest.config.ts and test setup files
- 3.Install dependencies via the suggested pnpm/npm/yarn command
- 4.Add TypeScript types to tsconfig.json if prompted
- 5.Run pnpm test:run to verify the sample test passes
- 6.Replace the sample test with real tests for your codebase
Use cases
- Setting up Vitest in a new React project with jsdom environment and Testing Library
- Configuring Vitest for a Cloudflare Workers project with the Workers pool
- Adding tests to an existing Node.js or Hono API with sample route tests
- Migrating a Jest test suite to Vitest with auto-detection of project structure
- Enabling code coverage reporting with v8 provider and HTML output
- Frontend developers building React applications with Vite
- Backend developers working with Node.js, Hono, or Express APIs
- Cloudflare Workers developers needing Workers-specific test pools
- Library maintainers setting up testing infrastructure
- Teams migrating from Jest to Vitest
vitest FAQ
The skill identifies Jest dependencies and suggests removal commands, but you run them. It won't overwrite existing jest.config files — review and delete them after migration.
The skill extends your existing Vite config by adding the test block rather than creating a separate vitest.config.ts, preserving your current setup.
The skill detects single projects. For monorepos, run it in each workspace root or configure vitest.workspace.ts manually after initial setup.
The skill installs mocking libraries (vitest has vi.fn() built-in) and shows patterns in the sample test, but you write mocks for your specific code.
jsdom and happy-dom for React, node for APIs and libraries, and @cloudflare/vitest-pool-workers for Cloudflare Workers.
Full instructions (SKILL.md)
Source of truth, from jezweb/claude-skills.
name: vitest description: "Set up Vitest testing in any project — detects type (Cloudflare Workers, React, Node, library), generates vitest.config.ts, test setup, utilities, and a sample test. Covers mocking patterns, coverage config, workspace setup, Jest migration. Use whenever the user mentions adding tests, setting up Vitest, configuring tests, migrating from Jest, fixing testing infrastructure, or asks 'how do I test this'." license: MIT
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:
# Base (always)
pnpm add -D vitest
# React projects — add jsdom and Testing Library
pnpm add -D @vitest/coverage-v8 jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
# Workers projects — add Cloudflare test utilities
pnpm add -D @vitest/coverage-v8 @cloudflare/vitest-pool-workers
# Node/Hono projects
pnpm add -D @vitest/coverage-v8
# If migrating from Jest, also remove:
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:
/// <reference types="vitest/config" />
import { defineConfig } from "vite";
import { cloudflare } from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [cloudflare()],
test: {
globals: true,
},
});
React (Vite)
/// <reference types="vitest/config" />
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: {
// ... existing config
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.getByRole("button", { name: /click me/i })).toBeInTheDocument();
});
it("calls onClick when clicked", async () => {
const user = userEvent.setup();
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
await user.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledOnce();
});
});
For a utility function (e.g. src/utils/format.ts):
import { describe, it, expect } from "vitest";
import { formatCurrency } from "./format";
describe("formatCurrency", () => {
it("formats whole numbers", () => {
expect(formatCurrency(1000)).toBe("$1,000.00");
});
it("formats decimals", () => {
expect(formatCurrency(49.9)).toBe("$49.90");
});
it("handles zero", () => {
expect(formatCurrency(0)).toBe("$0.00");
});
});
Pick a real file from the project to test. Don't invent a fake module — the sample test should run immediately after setup.
Step 8: Verify
Run the tests to confirm everything works:
pnpm test:run
If it fails, diagnose and fix. Common issues:
| Error | Fix |
|---|---|
Cannot find module 'vitest' | Check install completed, check node_modules/.vitest exists |
ReferenceError: describe is not defined | Add globals: true to config, or add types: ["vitest/globals"] to tsconfig |
document is not defined | Wrong environment — set environment: "jsdom" for React tests |
Cannot use import.meta | Ensure vitest.config uses .ts extension and project has "type": "module" or Vite handles transforms |
| Workers bindings undefined | Use @cloudflare/vitest-pool-workers instead of plain vitest, check wrangler.toml path |
Mocking Reference
These patterns are for writing tests after setup is complete. Include them in the sample test or a src/test/examples.test.ts if the user asks for mocking examples.
Module mocking (vi.mock)
import { vi, describe, it, expect } from "vitest";
import { getUser } from "./api";
vi.mock("./api", () => ({
getUser: vi.fn(),
}));
it("mocks a module function", async () => {
vi.mocked(getUser).mockResolvedValue({ id: 1, name: "Test" });
const user = await getUser(1);
expect(user.name).toBe("Test");
expect(getUser).toHaveBeenCalledWith(1);
});
Spy on methods (vi.spyOn)
it("spies on console.warn", () => {
const spy = vi.spyOn(console, "warn").mockImplementation(() => {});
doSomethingThatWarns();
expect(spy).toHaveBeenCalledOnce();
spy.mockRestore();
});
Fake timers
import { vi, beforeEach, afterEach, it, expect } from "vitest";
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-01-15T10:00:00Z"));
});
afterEach(() => {
vi.useRealTimers();
});
it("uses controlled time", () => {
expect(new Date().toISOString()).toBe("2026-01-15T10:00:00.000Z");
});
Global stubs
it("stubs fetch", async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve({ data: "test" }),
});
vi.stubGlobal("fetch", mockFetch);
const res = await fetch("/api/data");
expect(mockFetch).toHaveBeenCalledWith("/api/data");
vi.unstubAllGlobals();
});
Snapshot testing
it("matches snapshot", () => {
const result = generateConfig({ debug: true });
expect(result).toMatchSnapshot();
});
it("matches inline snapshot", () => {
expect({ status: "ok", count: 3 }).toMatchInlineSnapshot(`
{
"count": 3,
"status": "ok",
}
`);
});
Parameterized tests
describe.each([
{ input: "hello", expected: "HELLO" },
{ input: "world", expected: "WORLD" },
{ input: "", expected: "" },
])("toUpperCase($input)", ({ input, expected }) => {
it(`returns ${expected}`, () => {
expect(input.toUpperCase()).toBe(expected);
});
});
Jest Migration
When the detected project has Jest (jest.config.*, @types/jest, ts-jest in dependencies):
- Generate the vitest.config.ts using the steps above
- Update imports in existing test files:
// Before
import { jest } from "@jest/globals";
jest.mock("./api");
jest.fn();
jest.spyOn(obj, "method");
// After
import { vi } from "vitest";
vi.mock("./api");
vi.fn();
vi.spyOn(obj, "method");
- Remove Jest packages:
pnpm remove jest ts-jest @types/jest jest-environment-jsdom babel-jest @jest/globals
-
Update tsconfig — replace
"types": ["jest"]with"types": ["vitest/globals"] -
Run tests and fix any remaining issues
Key replacements:
| Jest | Vitest |
|---|---|
jest.fn() | vi.fn() |
jest.mock() | vi.mock() |
jest.spyOn() | vi.spyOn() |
jest.useFakeTimers() | vi.useFakeTimers() |
jest.clearAllMocks() | vi.clearAllMocks() |
jest.requireActual() | vi.importActual() |
@jest/globals | vitest |
jest.config.js | vitest.config.ts |
Workspace Setup (Monorepos)
For monorepo projects with multiple packages:
// vitest.workspace.ts
import { defineWorkspace } from "vitest/config";
export default defineWorkspace([
"packages/*/vitest.config.ts",
]);
Each package gets its own config. The workspace file just points to them.
What This Skill Produces
After running, the project should have:
vitest.config.ts(or test block added to existing vite.config.ts)src/test/setup.ts(React projects)- Updated
tsconfig.jsonwith vitest/globals type - Updated
package.jsonwith test scripts - At least one passing sample test against real source code
- Dependencies installed
The tests should pass on first run. If they don't, fix them before finishing.
Related skills
More from jezweb/claude-skills and the wider catalog.

wordpress-content
Create and manage WordPress posts, pages, media, categories, tags, and menus via WP-CLI or REST API.

wordpress-elementor
Edit Elementor pages and manage templates on WordPress sites via browser automation or WP-CLI.

wordpress-setup
Connect to WordPress sites via WP-CLI over SSH or REST API with verified access and saved config.

zustand-state-management
Type-safe global state management for React with Zustand, persist middleware, and Next.js SSR support.

drawio
Create and export draw.io diagrams as native .drawio files, PNG, SVG, PDF, or browser URLs.

python-backend
Production-ready Python backend patterns for FastAPI, SQLAlchemy, and Upstash integrations.