Vitest Migration & Config — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Vitest Migration & Config (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill makes the agent migrate a Jest test suite to Vitest correctly and configure Vitest from scratch. Vitest is mostly Jest-compatible, but the differences bite: the vi namespace replaces jest, vi.mock hoisting needs vi.hoisted, ESM is first-class, and Vitest v4 removed workspace files in favor of inline projects. The agent should produce a config that runs fast, types cleanly, and reports coverage.
Use this skill when migrating from Jest, setting up vitest.config.ts, splitting unit vs browser tests, or resolving v4 upgrade breakage.
jest.fn -> vi.fn, jest.mock -> vi.mock, jest.spyOn -> vi.spyOn, jest.useFakeTimers -> vi.useFakeTimers. The assertion API (expect, matchers) is unchanged.test.clearMocks/restoreMocks in config or call vi.clearAllMocks() in beforeEach — Vitest does not reset by default.vi.hoisted(() => ...), since the factory runs before imports.globals: true (Jest-like, no imports) or import { describe, it, expect, vi } from vitest. Pick one and add types: ['vitest/globals'] if using globals.vitest.workspace.ts file is removed; declare multiple environments via the inline projects array.browser.instances (v4) to run component tests in a real browser engine via Playwright.Most test bodies migrate with a namespace rename. With globals: true, even the imports can stay as-is.
// Before (Jest):
// const fn = jest.fn();
// jest.spyOn(obj, 'method');
// jest.useFakeTimers();
// After (Vitest) — explicit imports (recommended, ESM-safe):
import { describe, it, expect, vi, beforeEach } from 'vitest';
describe('cart', () => {
beforeEach(() => vi.clearAllMocks());
it('adds an item', () => {
const onChange = vi.fn();
const cart = createCart(onChange);
cart.add({ sku: 'A1', qty: 2 });
expect(cart.total).toBe(2);
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ sku: 'A1' }));
});
});vi.mock with vi.hoisted (the hoisting fix)In Jest you prefix variables with mock. In Vitest, wrap them in vi.hoisted so they exist when the hoisted factory runs.
import { vi, test, expect, beforeEach } from 'vitest';
import { getUser } from './user-service';
import axios from 'axios';
// Create the spy in a hoisted block so the factory below can reference it.
const { mockGet } = vi.hoisted(() => ({ mockGet: vi.fn() }));
vi.mock('axios', () => ({
default: { get: mockGet }, // note: ESM default export shape
}));
beforeEach(() => vi.clearAllMocks());
test('resolves user from API', async () => {
mockGet.mockResolvedValue({ data: { id: 1, name: 'Ada' } });
const user = await getUser(1);
expect(user).toEqual({ id: 1, name: 'Ada' });
expect(axios.get).toHaveBeenCalledWith('/api/users/1');
});importActualThe Vitest equivalent of jest.requireActual. The factory is async.
vi.mock('./config', async (importOriginal) => {
const actual = await importOriginal<typeof import('./config')>();
return {
...actual,
isProduction: vi.fn(() => false), // override one export, keep the rest real
};
});vitest.config.tsCovers globals, environment, setup files, and coverage. This replaces jest.config.js.
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true, // Jest-like; otherwise import from 'vitest'
environment: 'node', // 'jsdom' for component DOM tests
setupFiles: ['./test/setup.ts'],
clearMocks: true, // reset vi.fn() call data before each test
restoreMocks: true, // restore spies to original impl
coverage: {
provider: 'v8', // fast, no instrumentation step
reporter: ['text', 'html', 'lcov'],
include: ['src/**/*.ts'],
exclude: ['src/**/*.d.ts', 'src/**/index.ts'],
thresholds: { lines: 80, functions: 80, branches: 75 },
},
},
});If using globals: true, add to tsconfig.json:
{ "compilerOptions": { "types": ["vitest/globals"] } }projects (v4)The v4 replacement for vitest.workspace.ts. Run Node unit tests and jsdom component tests in one command, each with its own config.
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: { provider: 'v8' },
projects: [
{
// Fast pure-logic tests in Node.
test: {
name: 'unit',
environment: 'node',
include: ['src/**/*.unit.test.ts'],
},
},
{
// DOM/component tests in jsdom.
test: {
name: 'dom',
environment: 'jsdom',
setupFiles: ['./test/dom-setup.ts'],
include: ['src/**/*.dom.test.ts'],
},
},
],
},
});instances)For component tests that need a real browser. v4 replaced the singular browser.name with a browser.instances array.
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright',
headless: true,
instances: [
{ browser: 'chromium' },
{ browser: 'firefox' },
],
},
},
});// component.browser.test.ts — runs in a real browser
import { render } from 'vitest-browser-react';
import { expect, test } from 'vitest';
import { Counter } from './Counter';
test('increments in a real browser', async () => {
const screen = render(<Counter />);
await screen.getByRole('button', { name: 'Increment' }).click();
await expect.element(screen.getByText('Count: 1')).toBeVisible();
});{
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui",
"coverage": "vitest run --coverage"
}
}globals: true, add types: ['vitest/globals'] so TypeScript knows expect/vi.istanbul only if you need its specific report nuances.browser.name to browser.instances.vi.hoisted and { default }.undefined when the hoisted factory executes.projects.node.vitest instead.vitest.config.ts" / "configure Vitest coverage"vi.mock isn't working / variable is undefined in the factory"workspace / browser.name config"requireActual equivalent in Vitest"~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.