skills/vitest-snap-filter-data/SKILL.md
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.
npx skillsauth add Odonno/vitest-snap vitest-snap-filter-dataInstall 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.
import {
UndefinedFilter,
NullFilter,
ExcludeFilter,
IncludeFilter,
} from "vitest-snap";
await expect(data).toJsonSnapshot({
filters: [
new UndefinedFilter(),
new NullFilter(),
new ExcludeFilter(".internalId"),
],
});
// UndefinedFilter — removes undefined props at any depth (default)
new UndefinedFilter();
// { a: 1, b: undefined } → { a: 1 }
// NullFilter — removes null props at any depth
new NullFilter();
// { a: 1, b: null } → { a: 1 }
// ExcludeFilter — removes nodes matched by selector
new ExcludeFilter(".password");
// ExcludeFilter with nested path
new ExcludeFilter(".metadata.internalId");
// ExcludeFilter — exclude field from every array element
new ExcludeFilter(".users[].token");
// IncludeFilter — keeps ONLY matched nodes (allowlist)
new IncludeFilter(".name"); // single selector
new IncludeFilter([".id", ".name", ".email"]); // multiple selectors
Adjacent bare Selector<T> strings in the filters array are auto-merged into a single IncludeFilter:
await expect(user).toJsonSnapshot({
filters: [".id", ".name", ".email"],
// equivalent to: [new IncludeFilter([".id", ".name", ".email"])]
});
type User = { name: string; address: { city: string; zip: string } };
new ExcludeFilter<User>(".address.city"); // ✓ type-checked
new ExcludeFilter<User>(".missing"); // ✗ compile-time type error
Selector syntax reference:
| Selector | Matches |
| ------------- | ------------------------- |
| .key | Property key |
| .a.b.c | Nested path |
| [n] | Array index n |
| [] | All array items |
| [start:end] | Array slice |
| .* | All object keys |
| .** | All descendants |
| .**.key | All key fields anywhere |
Always re-add UndefinedFilter when providing a custom filters array:
import { UndefinedFilter, NullFilter } from "vitest-snap";
await expect(data).toJsonSnapshot({
filters: [new UndefinedFilter(), new NullFilter()],
});
Wrong:
await expect(data).toJsonSnapshot({
filters: [new NullFilter()], // UndefinedFilter no longer applied
});
Correct:
import { NullFilter, UndefinedFilter } from "vitest-snap";
await expect(data).toJsonSnapshot({
filters: [new UndefinedFilter(), new NullFilter()],
});
Providing filters: [...] completely replaces the default [new UndefinedFilter()]. Undefined props reappear in the snapshot.
Source: src/options/index.ts — DEFAULT_OPTIONS; maintainer interview
Wrong:
// removes ALL fields except .name — likely unintended
filters: [new IncludeFilter(".name")];
Correct:
// removes only .password, keeps everything else
filters: [new ExcludeFilter(".password")];
IncludeFilter is an allowlist — it removes everything not listed. For removing one field while keeping all others, use ExcludeFilter.
Source: docs/content/docs/filters.mdx
Wrong:
filters: [
new IncludeFilter(".a"),
new ExcludeFilter(".a"), // breaks adjacency
new IncludeFilter(".b"), // runs on nearly empty object
];
Correct:
// reorder so IncludeFilters are adjacent
filters: [new IncludeFilter([".a", ".b"]), new ExcludeFilter(".a")];
regroupFilters only merges adjacent IncludeFilter instances. A non-IncludeFilter between them breaks adjacency; the second IncludeFilter receives an already-stripped object.
Source: src/options/index.ts — regroupFilters
Wrong:
// ".passwrod" is a silent no-op at runtime
new ExcludeFilter(".passwrod");
Correct:
type User = { name: string; password: string };
// ".passwrod" would be a compile-time type error
new ExcludeFilter<User>(".password");
Selector<T> computes all valid paths at compile time. Without the generic argument, a typo becomes a silent runtime no-op instead of a type error.
Source: src/selectors/index.ts; maintainer interview
Wrong:
// expecting [1, 3] — gets [1, null, 3]
new NullFilter().apply({ items: [1, null, 3] });
Correct:
// null inside arrays stays; use ExcludeFilter with index if needed
new ExcludeFilter(".items[1]").apply({ items: [1, null, 3] });
unsetAtPath is called with allowArrayUnset=false for value-based filters; null/undefined inside arrays are kept as-is. Only object properties are deleted.
Source: src/functions/object.ts — unsetAtPath
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
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.
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.