From javascript
Vitest testing framework conventions and practices. Invoke whenever task involves any interaction with Vitest — writing tests, configuring vitest.config.ts, mocking modules, debugging test failures, snapshots, coverage, or migrating from Jest.
npx claudepluginhub xobotyi/cc-foundry --plugin javascriptThis skill uses the workspace's default tool permissions.
**Test behavior, not implementation. Mock boundaries, not internals.**
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Test behavior, not implementation. Mock boundaries, not internals.
Vitest is a Vite-native test framework with Jest-compatible APIs. It shares your app's Vite config (aliases, plugins, transforms) so tests run against the same code you ship.
${CLAUDE_SKILL_DIR}/references/mocking.md]: Full mocking rules, module mocking patterns, cleanup
strategy${CLAUDE_SKILL_DIR}/references/assertions.md]: Matcher tables, asymmetric matchers, soft
assertions${CLAUDE_SKILL_DIR}/references/lifecycle.md]: Hook execution order, test context, setup files,
global setup${CLAUDE_SKILL_DIR}/references/configuration.md]: Config file options, projects, pools,
sharding, env vars${CLAUDE_SKILL_DIR}/references/coverage.md]: Coverage providers, thresholds, ignore comments,
performance${CLAUDE_SKILL_DIR}/references/jest-migration.md]: Jest API translation, key behavioral
differences, Mocha/Sinonimport { describe, it, expect, vi } from 'vitest' — do not rely on globals unless the project
has globals: true configured.'returns empty array when input is null', not 'test case 1'.describe for grouping. Group by unit (function, class, component), not by test type.it over test. Both work, but it reads better inside describe:
describe('parseUrl', () => { it('extracts hostname', ...) }).it.each / describe.each for parametrized tests. Supports array form and template literal form with $key
interpolation.it.skip, it.only, it.todo, it.fails, it.skipIf(cond), it.runIf(cond).it('name', { retry: 3 }, fn). Repeat: it('name', { repeats: 100 }, fn).describe.concurrent(...) — use expect from test context (destructured parameter) for correct
snapshot/assertion tracking.vi.fn() creates a standalone trackable mock. Optionally accepts implementation.vi.spyOn(obj, 'method') wraps existing method while preserving original. Also supports
vi.spyOn(obj, 'prop', 'get') for getters/setters.vi.spyOn over vi.mock when you only need to observe or override a single export.vi.mock() is hoisted. It moves to top of file regardless of where you write it. Always runs before imports.default key:
vi.mock('./mod', () => ({ default: val, namedExport: vi.fn() })).importOriginal:
vi.mock(import('./api'), async (importOriginal) => ({ ...await importOriginal(), fetchUser: vi.fn() })).vi.doMock() is not hoisted — runs at position. Only affects subsequent dynamic import() calls. Use when you
need per-test mock behavior.vi.mock cannot intercept internal calls. If foo() calls bar() in the same file, mocking bar externally
does not affect foo. Refactor to separate modules or use dependency injection.restoreMocks: true in config (recommended) or afterEach(() => vi.restoreAllMocks()).vi.useFakeTimers() with vi.useRealTimers() — in beforeEach/afterEach or use fakeTimers config
option.vi.mocked(fn) narrows TypeScript types to mock types without runtime changes.Full mocking rules (auto-mocking, spy mode, vi.hoisted, __mocks__ directory, env/globals stubbing, async helpers):
see ${CLAUDE_SKILL_DIR}/references/mocking.md.
toBe(val) — primitives or same reference (Object.is)toEqual(val) — deep structural equality (ignores undefined in expected)toStrictEqual(val) — deep equality + checks undefined keys, sparse arrays, class typestoMatchObject(subset) — object contains at least these propertiesexpect(() => throwingFn()).toThrow('message').await expect(asyncFn()).rejects.toThrow('message').await async assertions. await expect(promise).resolves.toEqual(...) — an un-awaited assertion silently
passes.expect.poll(() => value, { timeout, interval }) — retries assertion until pass or timeout. Prefer over manual
waitFor loops.expect.soft(val) continues after failure, reports all errors at end.toEqual, toHaveBeenCalledWith: expect.any(Number),
expect.arrayContaining([...]), expect.objectContaining({}), expect.stringMatching(/regex/). Negate with
expect.not.*.expect.assertions(n) — exactly n assertions must run. expect.hasAssertions() — at least one. Guard against
missing assertions in async code.Truthiness, number, string/array/object matchers, type checks, spy assertions, custom error messages,
expect.unreachable: see ${CLAUDE_SKILL_DIR}/references/assertions.md.
expect(val).toMatchSnapshot() — writes to .snap file.expect(val).toMatchInlineSnapshot(\"expected"`)` — Vitest auto-updates the string argument.toMatchSnapshot({ id: expect.any(String) }). Never snapshot timestamps,
random IDs, or other volatile data without matchers.vitest -u or press u in watch mode.beforeAll/afterAll run once per describe block (or per file at top level).beforeEach/afterEach run before/after every test in current scope.beforeAll or beforeEach returns a function, it runs as teardown.
Vitest-specific, not in Jest. Be careful not to accidentally return values — wrap in braces:
beforeEach(() => { setupFn() }).onTestFinished(fn) — register cleanup inside a test. Always runs regardless of pass/fail.onTestFailed(fn) — runs only on failure. Useful for diagnostics.setupFiles: ['./test/setup.ts'] — runs before each test file in the same process. Use for global hooks, custom
matchers, shared setup.globalSetup: ['./test/global-setup.ts'] — runs once before any test workers. Use for expensive one-time setup
(database seeding, server startup). Return a function for teardown.Hook execution order, test context, hook order config, provide/inject, and global vs setup file comparison: see
${CLAUDE_SKILL_DIR}/references/lifecycle.md.
vitest.config.ts with defineConfig from 'vitest/config'. Inherits Vite plugins and aliases
automatically.vite.config.ts, add /// <reference types="vitest/config" /> directive.projects (v3.2+, replaces deprecated workspace) for multi-environment setups. Every project must have a
unique name.Config file merging, key options table, pools and parallelism, environment variables, in-source testing, and sharding:
see ${CLAUDE_SKILL_DIR}/references/configuration.md.
Set coverage.include to catch uncovered files. Use v8 provider (default, recommended). Set thresholds in config. Run
coverage only in CI, not in watch mode.
Provider comparison, reporters, ignore comments, and performance tips: see ${CLAUDE_SKILL_DIR}/references/coverage.md.
Define via expect.extend({ matcherName(received, ...args) {} }). Return { pass, message }. Add TypeScript
declarations via interface Matchers<T> in vitest.d.ts.
Matcher context, TypeScript setup, and diff output: see ${CLAUDE_SKILL_DIR}/references/assertions.md.
Replace jest.* with vi.*. Key differences: mock factory must return object with explicit exports, mockReset
restores original impl, auto-mocking requires explicit vi.mock() call, hook return values are teardown functions.
Full API translation table, behavioral differences, and Mocha/Sinon migration: see
${CLAUDE_SKILL_DIR}/references/jest-migration.md.
When writing tests:
When reviewing tests:
The javascript skill governs language choices; this skill governs Vitest testing decisions. Activate the relevant runtime skill (nodejs or bun) for runtime-specific behavior.
Test behavior, not implementation. When in doubt, mock less.