skills/vitest-snap-setup/SKILL.md
Install vitest-snap, register matchers via side-effect import in Vitest setupFiles, configure global defaults with configureGlobalSnapOptions. Covers option merge order (defaults → global → per-call), yaml peer dependency for toYamlSnapshot, UndefinedFilter/Date redaction defaults, and snapshot file migration when changing global dir.
npx skillsauth add Odonno/vitest-snap vitest-snap-setupInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
npm i vitest-snap -D
# bun: bun add -D vitest-snap
# pnpm: pnpm add -D vitest-snap
# yarn: yarn add -D vitest-snap
Side-effect import in a setup file — mandatory before any matcher call.
import "vitest-snap";
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
setupFiles: ["./vitest.setup.ts"],
},
});
expect.extend() is called at import time. Missing this causes all matcher calls to throw at runtime.
Call configureGlobalSnapOptions in the setup file only — never inside test bodies.
import {
configureGlobalSnapOptions,
NullFilter,
UndefinedFilter,
} from "vitest-snap";
configureGlobalSnapOptions({
dir: "./__snapshots__",
filters: [new UndefinedFilter(), new NullFilter()],
});
built-in defaults → global (configureGlobalSnapOptions) → per-call options
Per-call always wins. Each layer fully replaces array options (no merging of arrays).
// built-in defaults apply
// global sets dir
configureGlobalSnapOptions({ dir: "./__snapshots__" });
// per-call overrides dir and filters for this one call only
await expect(data).toJsonSnapshot({ dir: "./other", indent: 4 });
| Option | Default |
| ------------ | ---------------------------------------- |
| dir | "./snapshots" |
| filters | [new UndefinedFilter()] |
| redactions | [new ValueRedaction(isDate, "[Date]")] |
| indent | 2 |
name, fileExtension, args have no default — derived from test name or left empty.
toYamlSnapshot does a dynamic import("yaml") at runtime. Install separately:
npm i -D yaml
All four matchers return Promise<void>. Omitting await makes the test pass without writing or diffing the snapshot.
// WRONG
test("user", () => {
expect(user).toJsonSnapshot();
});
// CORRECT
test("user", async () => {
await expect(user).toJsonSnapshot();
});
Source: src/index.ts — expect.extend async implementations
// WRONG — no setupFiles configured
export default defineConfig({ test: {} });
// CORRECT
// vitest.setup.ts
import "vitest-snap";
// vitest.config.ts
export default defineConfig({
test: { setupFiles: ["./vitest.setup.ts"] },
});
Source: docs/content/docs/index.mdx
globalOptions is a module-level variable. Setting it inside a test is order-dependent and unpredictable.
// WRONG
test("my test", async () => {
configureGlobalSnapOptions({ dir: "./__snapshots__" });
await expect(data).toJsonSnapshot();
});
// CORRECT — vitest.setup.ts
import { configureGlobalSnapOptions } from "vitest-snap";
configureGlobalSnapOptions({ dir: "./__snapshots__" });
Source: src/options/global.ts; docs/content/docs/matchers/configuration.mdx
// WRONG — "yaml" not in package.json
await expect(data).toYamlSnapshot(); // throws module-not-found
// CORRECT — install first
// npm i -D yaml
await expect(data).toYamlSnapshot();
Source: src/functions/yaml.ts
Providing filters: [...] completely replaces [new UndefinedFilter()]. Undefined props reappear in the snapshot.
// WRONG
await expect(data).toJsonSnapshot({
filters: [new NullFilter()],
});
// CORRECT
import { NullFilter, UndefinedFilter } from "vitest-snap";
await expect(data).toJsonSnapshot({
filters: [new UndefinedFilter(), new NullFilter()],
});
Source: src/options/index.ts — DEFAULT_OPTIONS
toMatchFileSnapshot looks in the new location. Existing files in the old location are not moved automatically.
// WRONG — orphans existing ./snapshots/* files
configureGlobalSnapOptions({ dir: "./__snapshots__" });
// CORRECT
// 1. update config
configureGlobalSnapOptions({ dir: "./__snapshots__" });
// 2. move files
// mv ./snapshots/* ./__snapshots__/
Source: src/options/file.ts
tools
Write snapshot tests with toJsonSnapshot, toYamlSnapshot, toMarkdownSnapshot, toTextSnapshot. Covers SnapOptions (dir, name, fileExtension, args, indent), auto-naming from test name, parametric snapshots with test.each and args, Markdown table rendering rules, and yaml peer dependency for toYamlSnapshot. Use when writing any vitest-snap matcher call.
testing
Apply redactions to replace or stabilize values before snapshotting: ValueRedaction(predicate, replaced, counting), ReplacedRedaction(selector, replaced, counting), SortedRedaction(selector). Covers counting behaviour ([Tag] → [Tag_1], [Tag_2]), deepest-first traversal order, default Date redaction override (custom array replaces it), .** selector root replacement risk, and Selector<T> typed paths. Use when configuring the redactions option on any matcher.
testing
End-to-end guide for adding vitest-snap to an existing Vitest project: install package, register matchers in setupFiles via side-effect import, write first toJsonSnapshot call, understand auto-naming and built-in defaults (UndefinedFilter, Date redaction), then add a filter or redaction. Use when bootstrapping vitest-snap from scratch.
testing
Apply filters to remove nodes before snapshotting: UndefinedFilter (default), NullFilter, ExcludeFilter(selector), IncludeFilter(selector[]). Covers Selector<T> typed path DSL, bare selector string shorthand for IncludeFilter, adjacent IncludeFilter auto-merge via regroupFilters, default filters override behaviour (custom array replaces UndefinedFilter), and array slot preservation semantics. Use when configuring the filters option on any matcher.