-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Clear and concise description of the problem
Draft plan brainstorming with Claude
As a developer migrating from Jest to Vitest, I want to create custom snapshot matchers via expect.extend so that I can reuse the built-in snapshot infrastructure (matching, updating, inline writing) with custom value transformations.
In Jest, this is a common and well-supported pattern — wrapping toMatchSnapshot / toMatchInlineSnapshot inside a custom matcher via expect.extend is straightforward:
import { toMatchInlineSnapshot } from "jest-snapshot";
expect.extend({
toMatchInlineCodeframe(received, ...rest) {
const formatted = formatCodeframe(received);
return toMatchInlineSnapshot.call(this, formatted, ...rest);
}
});
// Usage
expect(result).toMatchInlineCodeframe(`
expected codeframe here
`);Vitest currently has no ergonomic equivalent. This has been a blocker for users migrating from Jest:
- #9080 — user migrating 3000+ tests with custom inline snapshot matchers from Jest, found that inline snapshot updating is hardcoded to built-in matcher names
- #7848 — fabric.js depends on
jest-snapshotpackage inside Vitest for custom snapshot matchers, which breaks in browser mode - #9481 —
updateSnapshotnot publicly accessible for custom matcher authors - #9081 — proposal to support custom matcher names in inline snapshot updating
Why existing alternatives don't cover this
addSnapshotSerializer: only controls serialization, not matcher behavior. Thetest()predicate is tricky for generic shapes and can unintentionally affect other snapshots. Less explicit than a named custom matcher.- Chai prototype hijacking (#7847): technically works as an escape hatch but requires Chai internals knowledge (
chai.util.flag,chai.util.addMethod), error stacktraces point to the wrong location on mismatch, and custom matchers don't get TypeScript types or integrate withexpect.extend. - Using
jest-snapshotpackage directly:jest-snapshot'stoMatchSnapshotexpects Jest's ownSnapshotState(different class, different.match()signature), so wrapping it inside Vitest'sexpect.extenddoesn't work. Packages likejest-image-snapshotwork because they manage their own snapshot files and only usesnapshotStatefor metadata — but this approach is Node-only (breaks browser mode).
Additionally, none of the above workarounds support custom inline snapshot matchers. Vitest's inline snapshot updating has hardcoded regex matching only toMatchInlineSnapshot and toThrowErrorMatchingInlineSnapshot (replaceInlineSnap), so even if matching works via snapshotState.match(), writing/updating the inline snapshot in the source file silently fails for custom matcher names.
Suggested solution
Provide a way for custom matchers (via expect.extend) to leverage Vitest's built-in snapshot infrastructure — including file snapshots, inline snapshots, and snapshot updating. A few possible approaches:
Option A: Export composable snapshot matcher functions
Export toMatchSnapshot / toMatchInlineSnapshot as functions that return { pass, message } and work with the expect.extend matcher context (this), like Jest does with jest-snapshot:
import { toMatchInlineSnapshot } from '@vitest/snapshot'; // or 'vitest'
expect.extend({
toMatchInlineCodeframe(received, ...rest) {
const formatted = formatCodeframe(received);
return toMatchInlineSnapshot.call(this, formatted, ...rest);
}
});
expect(result).toMatchInlineCodeframe(`
expected codeframe here
`);This gives Jest-like ergonomics — custom snapshot matchers become a thin wrapper around the built-in ones.
Option B: Fix the blockers for snapshotState.match() usage
this.snapshotState is already accessible in expect.extend matchers. The main blockers preventing users from building custom snapshot matchers on top of it are:
- Inline snapshot updating is hardcoded to only recognize
toMatchInlineSnapshot/toThrowErrorMatchingInlineSnapshot— support custom matcher names (cf. #9081) updateSnapshotis not publicly typed — expose it onSnapshotState(cf. #9481)
This is lower-level and requires users to understand snapshotState.match(), but it's the most flexible and smallest change.
Option C: Both
Composable high-level functions (Option A) for the common "transform and delegate" pattern, plus publicly documented lower-level APIs (Option B) for advanced use cases.
Alternative
No response
Additional context
Real-world examples of custom snapshot matchers in Jest that users want to port:
- html-validate: toMatchInlineCodeframe
- fabric.js: toMatchObjectSnapshot
- jest-image-snapshot (already works with Vitest, but shows the pattern)
Validations
- Follow our Code of Conduct
- Read the Contributing Guidelines.
- Read the docs.
- Check that there isn't already an issue that requests the same feature to avoid creating a duplicate.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status