diff --git a/guide/extending-matchers.md b/guide/extending-matchers.md
index 06300420..f262d369 100644
--- a/guide/extending-matchers.md
+++ b/guide/extending-matchers.md
@@ -107,6 +107,10 @@ function customMatcher(this: MatcherState, received: unknown, arg1: unknown, arg
expect.extend({ customMatcher })
```
+
+::: tip
+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).
+:::
断言方法可以访问上下文 `this` 对象中的这些属性:
diff --git a/guide/migration.md b/guide/migration.md
index 1776f724..189f6577 100644
--- a/guide/migration.md
+++ b/guide/migration.md
@@ -640,6 +640,36 @@ export default defineConfig({
```
否则快照中会出现大量转义的 `"` 字符。
+
+### Custom Snapshot Matchers experimental 4.1.3
+
+Jest imports snapshot composables from `jest-snapshot`. In Vitest, import from `vitest/runtime` instead:
+
+```ts
+const { toMatchSnapshot } = require('jest-snapshot') // [!code --]
+import { toMatchSnapshot } from 'vitest/runtime' // [!code ++]
+
+expect.extend({
+ toMatchTrimmedSnapshot(received: string, length: number) {
+ return toMatchSnapshot.call(this, received.slice(0, length))
+ },
+})
+```
+
+For inline snapshots, the same applies:
+
+```ts
+const { toMatchInlineSnapshot } = require('jest-snapshot') // [!code --]
+import { toMatchInlineSnapshot } from 'vitest/runtime' // [!code ++]
+
+expect.extend({
+ toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) {
+ return toMatchInlineSnapshot.call(this, received.slice(0, 10), inlineSnapshot)
+ },
+})
+```
+
+See [Custom Snapshot Matchers](/guide/snapshot#custom-snapshot-matchers) for the full guide.
## 从 Mocha + Chai + Sinon 迁移 {#mocha-chai-sinon}
diff --git a/guide/snapshot.md b/guide/snapshot.md
index 78e69b78..49233055 100644
--- a/guide/snapshot.md
+++ b/guide/snapshot.md
@@ -192,6 +192,76 @@ Pretty foo: Object {
```
我们使用的是 Jest 的 `pretty-format` 来序列化快照。你可以在这里阅读更多相关内容:[pretty-format](https://github.com/facebook/jest/blob/main/packages/pretty-format/README.md#serialize).
+
+## Custom Snapshot Matchers experimental 4.1.3 {#custom-snapshot-matchers}
+
+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).
+
+```ts
+import { expect, test } from 'vitest'
+import { toMatchFileSnapshot, toMatchInlineSnapshot, toMatchSnapshot } from 'vitest/runtime'
+
+expect.extend({
+ toMatchTrimmedSnapshot(received: string, length: number) {
+ return toMatchSnapshot.call(this, received.slice(0, length))
+ },
+ toMatchTrimmedInlineSnapshot(received: string, inlineSnapshot?: string) {
+ return toMatchInlineSnapshot.call(this, received.slice(0, 10), inlineSnapshot)
+ },
+ async toMatchTrimmedFileSnapshot(received: string, file: string) {
+ return toMatchFileSnapshot.call(this, received.slice(0, 10), file)
+ },
+})
+
+test('file snapshot', () => {
+ expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10)
+})
+
+test('inline snapshot', () => {
+ expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot()
+})
+
+test('raw file snapshot', async () => {
+ await expect('extra long string oh my gerd').toMatchTrimmedFileSnapshot('./raw-file.txt')
+})
+```
+
+The composables return `{ pass, message }` so you can further customize the error:
+
+```ts
+expect.extend({
+ toMatchTrimmedSnapshot(received: string, length: number) {
+ const result = toMatchSnapshot.call(this, received.slice(0, length))
+ return { ...result, message: () => `Trimmed snapshot failed: ${result.message()}` }
+ },
+})
+```
+
+::: warning
+For inline snapshot matchers, the snapshot argument must be the last parameter (or second-to-last when using property matchers). Vitest rewrites the last string argument in the source code, so custom arguments before the snapshot work, but custom arguments after it are not supported.
+:::
+
+::: tip
+File snapshot matchers must be `async` — `toMatchFileSnapshot` returns a `Promise`. Remember to `await` the result in the matcher and in your test.
+:::
+
+For TypeScript, extend the `Assertion` interface:
+
+```ts
+import 'vitest'
+
+declare module 'vitest' {
+ interface Assertion {
+ toMatchTrimmedSnapshot: (length: number) => T
+ toMatchTrimmedInlineSnapshot: (inlineSnapshot?: string) => T
+ toMatchTrimmedFileSnapshot: (file: string) => Promise
+ }
+}
+```
+
+::: tip
+See [Extending Matchers](/guide/extending-matchers) for more on `expect.extend` and custom matcher conventions.
+:::
## 与 Jest 的区别 {#difference-from-jest}
diff --git a/guide/test-context.md b/guide/test-context.md
index 9bf6759b..1a954a3d 100644
--- a/guide/test-context.md
+++ b/guide/test-context.md
@@ -894,7 +894,7 @@ This applies to all suite-level hooks: `beforeAll`, `afterAll`, and `aroundAll`.
:::
::: tip
-Suite-level hooks can only access [**file-scoped** and **worker-scoped** fixtures](#fixture-scopes). Test-scoped fixtures are not available in these hooks because they run outside the context of individual tests. If you try to access a test-scoped fixture in a suite-level hook, Vitest will throw an error.
+Suite-level hooks can only access [**file-scoped** and **worker-scoped** fixtures](#fixture-scopes), including `auto` fixtures. Test-scoped fixtures are not available in these hooks because they run outside the context of individual tests. If you try to access a test-scoped fixture in a suite-level hook, Vitest will throw an error.
```ts
const test = baseTest