Skip to content

Commit 691d341

Browse files
hi-ogawacodex
andauthored
fix(snapshot): export custom snapshot matcher helper from vitest (#10042)
Co-authored-by: Codex <noreply@openai.com>
1 parent d562712 commit 691d341

File tree

13 files changed

+268
-167
lines changed

13 files changed

+268
-167
lines changed

docs/guide/extending-matchers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ expect.extend({ customMatcher })
108108
```
109109

110110
::: tip
111-
To build custom **snapshot matchers** (wrappers around `toMatchSnapshot` / `toMatchInlineSnapshot` / `toMatchFileSnapshot`), use the composable functions from `vitest/runtime`. See [Custom Snapshot Matchers](/guide/snapshot#custom-snapshot-matchers).
111+
To build custom **snapshot matchers** (wrappers around `toMatchSnapshot()` / `toMatchInlineSnapshot()` / `toMatchFileSnapshot()`), use `Snapshots` exported from `vitest`. See [Custom Snapshot Matchers](/guide/snapshot#custom-snapshot-matchers).
112112
:::
113113

114114
Matcher function has access to `this` context with the following properties:

docs/guide/migration.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -652,11 +652,12 @@ Otherwise your snapshots will have a lot of escaped `"` characters.
652652

653653
### Custom Snapshot Matchers <Badge type="warning">experimental</Badge> <Version>4.1.3</Version>
654654

655-
Jest imports snapshot composables from `jest-snapshot`. In Vitest, import from `vitest/runtime` instead:
655+
Jest imports snapshot composables from `jest-snapshot`. In Vitest, use `Snapshots` from `vitest` instead:
656656

657657
```ts
658658
const { toMatchSnapshot } = require('jest-snapshot') // [!code --]
659-
import { toMatchSnapshot } from 'vitest/runtime' // [!code ++]
659+
import { Snapshots } from 'vitest' // [!code ++]
660+
const { toMatchSnapshot } = Snapshots // [!code ++]
660661

661662
expect.extend({
662663
toMatchTrimmedSnapshot(received: string, length: number) {
@@ -669,7 +670,8 @@ For inline snapshots, the same applies:
669670

670671
```ts
671672
const { toMatchInlineSnapshot } = require('jest-snapshot') // [!code --]
672-
import { toMatchInlineSnapshot } from 'vitest/runtime' // [!code ++]
673+
import { Snapshots } from 'vitest' // [!code ++]
674+
const { toMatchInlineSnapshot } = Snapshots // [!code ++]
673675

674676
expect.extend({
675677
toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) {

docs/guide/snapshot.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,12 @@ Pretty foo: Object {
202202

203203
## Custom Snapshot Matchers <Badge type="warning">experimental</Badge> <Version>4.1.3</Version> {#custom-snapshot-matchers}
204204

205-
You can build custom snapshot matchers using the composable functions exported from `vitest/runtime`. These let you transform values before snapshotting while preserving full snapshot lifecycle support (creation, update, inline rewriting).
205+
You can build custom snapshot matchers using the composable functions exposed on `Snapshots` from `vitest`. These let you transform values before snapshotting while preserving full snapshot lifecycle support (creation, update, inline rewriting).
206206

207207
```ts
208-
import { expect, test } from 'vitest'
209-
import { toMatchFileSnapshot, toMatchInlineSnapshot, toMatchSnapshot } from 'vitest/runtime'
208+
import { expect, test, Snapshots } from 'vitest'
209+
210+
const { toMatchFileSnapshot, toMatchInlineSnapshot, toMatchSnapshot } = Snapshots
210211

211212
expect.extend({
212213
toMatchTrimmedSnapshot(received: string, length: number) {
@@ -236,6 +237,10 @@ test('raw file snapshot', async () => {
236237
The composables return `{ pass, message }` so you can further customize the error:
237238

238239
```ts
240+
import { Snapshots } from 'vitest'
241+
242+
const { toMatchSnapshot } = Snapshots
243+
239244
expect.extend({
240245
toMatchTrimmedSnapshot(received: string, length: number) {
241246
const result = toMatchSnapshot.call(this, received.slice(0, length))

packages/vitest/src/integrations/snapshot/chai.ts

Lines changed: 100 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ChaiPlugin, MatcherState, SyncExpectationResult } from '@vitest/expect'
1+
import type { AsyncExpectationResult, ChaiPlugin, MatcherState, SyncExpectationResult } from '@vitest/expect'
22
import type { Test } from '@vitest/runner'
33
import { chai, createAssertionMessage, equals, iterableEquality, recordAsyncExpect, subsetEquality, wrapAssertion } from '@vitest/expect'
44
import { getNames } from '@vitest/runner/utils'
@@ -268,100 +268,109 @@ function assertMatchResult(result: SyncExpectationResult): void {
268268
}
269269

270270
/**
271-
* Composable for building custom snapshot matchers via `expect.extend`.
272-
* Call with `this` bound to the matcher state. Returns `{ pass, message }`
273-
* compatible with the custom matcher return contract.
274-
*
275-
* @example
276-
* ```ts
277-
* import { toMatchSnapshot } from 'vitest/runtime'
278-
*
279-
* expect.extend({
280-
* toMatchTrimmedSnapshot(received: string) {
281-
* return toMatchSnapshot.call(this, received.slice(0, 10))
282-
* },
283-
* })
284-
* ```
271+
* Composable snapshot matcher helpers for building custom snapshot matchers
272+
* with `expect.extend`.
285273
*
286274
* @experimental
287275
* @see https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers
288276
*/
289-
export function toMatchSnapshot(
290-
this: MatcherState,
291-
received: unknown,
292-
propertiesOrHint?: object | string,
293-
hint?: string,
294-
): SyncExpectationResult {
295-
return toMatchSnapshotImpl({
296-
assertion: this.__vitest_assertion__,
297-
received,
298-
...normalizeArguments(propertiesOrHint, hint),
299-
})
300-
}
277+
export const Snapshots = {
278+
/**
279+
* Composable for building custom snapshot matchers via `expect.extend`.
280+
* Call with `this` bound to the matcher state. Returns `{ pass, message }`
281+
* compatible with the custom matcher return contract.
282+
*
283+
* @example
284+
* ```ts
285+
* import { Snapshots } from 'vitest/runtime'
286+
*
287+
* expect.extend({
288+
* toMatchTrimmedSnapshot(received: string) {
289+
* return Snapshots.toMatchSnapshot.call(this, received.slice(0, 10))
290+
* },
291+
* })
292+
* ```
293+
*
294+
* @experimental
295+
* @see https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers
296+
*/
297+
toMatchSnapshot(
298+
this: MatcherState,
299+
received: unknown,
300+
propertiesOrHint?: object | string,
301+
hint?: string,
302+
): SyncExpectationResult {
303+
return toMatchSnapshotImpl({
304+
assertion: this.__vitest_assertion__,
305+
received,
306+
...normalizeArguments(propertiesOrHint, hint),
307+
})
308+
},
301309

302-
/**
303-
* Composable for building custom inline snapshot matchers via `expect.extend`.
304-
* Call with `this` bound to the matcher state. Returns `{ pass, message }`
305-
* compatible with the custom matcher return contract.
306-
*
307-
* @example
308-
* ```ts
309-
* import { toMatchInlineSnapshot } from 'vitest/runtime'
310-
*
311-
* expect.extend({
312-
* toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) {
313-
* return toMatchInlineSnapshot.call(this, received.slice(0, 10), inlineSnapshot)
314-
* },
315-
* })
316-
* ```
317-
*
318-
* @experimental
319-
* @see https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers
320-
*/
321-
export function toMatchInlineSnapshot(
322-
this: MatcherState,
323-
received: unknown,
324-
propertiesOrInlineSnapshot?: object | string,
325-
inlineSnapshotOrHint?: string,
326-
hint?: string,
327-
): SyncExpectationResult {
328-
return toMatchSnapshotImpl({
329-
assertion: this.__vitest_assertion__,
330-
received,
331-
isInline: true,
332-
...normalizeInlineArguments(propertiesOrInlineSnapshot, inlineSnapshotOrHint, hint),
333-
})
334-
}
310+
/**
311+
* Composable for building custom inline snapshot matchers via `expect.extend`.
312+
* Call with `this` bound to the matcher state. Returns `{ pass, message }`
313+
* compatible with the custom matcher return contract.
314+
*
315+
* @example
316+
* ```ts
317+
* import { Snapshots } from 'vitest/runtime'
318+
*
319+
* expect.extend({
320+
* toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) {
321+
* return Snapshots.toMatchInlineSnapshot.call(this, received.slice(0, 10), inlineSnapshot)
322+
* },
323+
* })
324+
* ```
325+
*
326+
* @experimental
327+
* @see https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers
328+
*/
329+
toMatchInlineSnapshot(
330+
this: MatcherState,
331+
received: unknown,
332+
propertiesOrInlineSnapshot?: object | string,
333+
inlineSnapshotOrHint?: string,
334+
hint?: string,
335+
): SyncExpectationResult {
336+
return toMatchSnapshotImpl({
337+
assertion: this.__vitest_assertion__,
338+
received,
339+
isInline: true,
340+
...normalizeInlineArguments(propertiesOrInlineSnapshot, inlineSnapshotOrHint, hint),
341+
})
342+
},
335343

336-
/**
337-
* Composable for building custom file snapshot matchers via `expect.extend`.
338-
* Call with `this` bound to the matcher state. Returns a `Promise<{ pass, message }>`
339-
* compatible with the custom matcher return contract.
340-
*
341-
* @example
342-
* ```ts
343-
* import { toMatchFileSnapshot } from 'vitest/runtime'
344-
*
345-
* expect.extend({
346-
* async toMatchTrimmedFileSnapshot(received: string, file: string) {
347-
* return toMatchFileSnapshot.call(this, received.slice(0, 10), file)
348-
* },
349-
* })
350-
* ```
351-
*
352-
* @experimental
353-
* @see https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers
354-
*/
355-
export function toMatchFileSnapshot(
356-
this: MatcherState,
357-
received: unknown,
358-
filepath: string,
359-
hint?: string,
360-
): Promise<SyncExpectationResult> {
361-
return toMatchFileSnapshotImpl({
362-
assertion: this.__vitest_assertion__,
363-
received,
364-
filepath,
365-
hint,
366-
})
344+
/**
345+
* Composable for building custom file snapshot matchers via `expect.extend`.
346+
* Call with `this` bound to the matcher state. Returns a `Promise<{ pass, message }>`
347+
* compatible with the custom matcher return contract.
348+
*
349+
* @example
350+
* ```ts
351+
* import { Snapshots } from 'vitest/runtime'
352+
*
353+
* expect.extend({
354+
* async toMatchTrimmedFileSnapshot(received: string, file: string) {
355+
* return Snapshots.toMatchFileSnapshot.call(this, received.slice(0, 10), file)
356+
* },
357+
* })
358+
* ```
359+
*
360+
* @experimental
361+
* @see https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers
362+
*/
363+
toMatchFileSnapshot(
364+
this: MatcherState,
365+
received: unknown,
366+
filepath: string,
367+
hint?: string,
368+
): AsyncExpectationResult {
369+
return toMatchFileSnapshotImpl({
370+
assertion: this.__vitest_assertion__,
371+
received,
372+
filepath,
373+
hint,
374+
})
375+
},
367376
}

packages/vitest/src/public/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export type {
3030
} from '../api/types'
3131
export { assert, chai, createExpect, expect, should } from '../integrations/chai'
3232
export { inject } from '../integrations/inject'
33+
export { Snapshots } from '../integrations/snapshot/chai'
3334

3435
export { vi, vitest } from '../integrations/vi'
3536
export type { VitestUtils } from '../integrations/vi'

packages/vitest/src/public/runtime.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { getWorkerState } from '../runtime/utils'
1111

1212
export { environments as builtinEnvironments } from '../integrations/env/index'
1313
export { populateGlobal } from '../integrations/env/utils'
14-
export { toMatchFileSnapshot, toMatchInlineSnapshot, toMatchSnapshot } from '../integrations/snapshot/chai'
1514
export { VitestNodeSnapshotEnvironment as VitestSnapshotEnvironment } from '../integrations/snapshot/environments/node'
1615
export type {
1716
Environment,

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/core/test/exports.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ it('exports snapshot', async ({ skip, task }) => {
1919
".": {
2020
"BenchmarkRunner": "function",
2121
"EvaluatedModules": "function",
22+
"Snapshots": "object",
2223
"TestRunner": "function",
2324
"afterAll": "function",
2425
"afterEach": "function",
@@ -165,9 +166,6 @@ it('exports snapshot', async ({ skip, task }) => {
165166
"__INTERNAL": "object",
166167
"builtinEnvironments": "object",
167168
"populateGlobal": "function",
168-
"toMatchFileSnapshot": "function",
169-
"toMatchInlineSnapshot": "function",
170-
"toMatchSnapshot": "function",
171169
},
172170
"./snapshot": {
173171
"VitestSnapshotEnvironment": "function",

test/snapshots/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"vitest": "workspace:*"
1010
},
1111
"devDependencies": {
12+
"@vitest/browser-playwright": "workspace:*",
1213
"jest-image-snapshot": "^6.5.1"
1314
}
1415
}

0 commit comments

Comments
 (0)