diff --git a/.vitepress/components/FeaturesList.vue b/.vitepress/components/FeaturesList.vue
index 11c24230..82526fba 100644
--- a/.vitepress/components/FeaturesList.vue
+++ b/.vitepress/components/FeaturesList.vue
@@ -36,9 +36,7 @@ import ListItem from './ListItem.vue'
Jest expect API
- 内置
- Tinyspy 用于对象
- Mock
+ 兼容 Jest 对象模拟
使用
diff --git a/.vitepress/config.ts b/.vitepress/config.ts
index 3b05b7ed..507100c4 100644
--- a/.vitepress/config.ts
+++ b/.vitepress/config.ts
@@ -51,7 +51,7 @@ export default ({ mode }: { mode: string }) => {
['link', { rel: 'icon', href: '/favicon.ico', sizes: '48x48' }],
['link', { rel: 'icon', href: '/logo-without-border.svg', type: 'image/svg+xml' }],
['meta', { name: 'author', content: `${teamMembers.map(c => c.name).join(', ')} and ${vitestName} contributors` }],
- ['meta', { name: 'keywords', content: 'vitest, vite, test, coverage, snapshot, react, vue, preact, svelte, solid, lit, marko, ruby, cypress, puppeteer, jsdom, happy-dom, test-runner, jest, typescript, esm, tinyspy, node' }],
+ ['meta', { name: 'keywords', content: 'vitest, vite, test, coverage, snapshot, react, vue, preact, svelte, solid, lit, marko, ruby, cypress, puppeteer, jsdom, happy-dom, test-runner, jest, typescript, esm, node' }],
['meta', { property: 'og:title', content: vitestName }],
['meta', { property: 'og:description', content: vitestDescription }],
['meta', { property: 'og:url', content: ogUrl }],
diff --git a/api/expect.md b/api/expect.md
index c611043c..1ec7ba5b 100644
--- a/api/expect.md
+++ b/api/expect.md
@@ -861,6 +861,52 @@ test('throws non-Error values', () => {
})
```
:::
+
+:::warning Unhandled Rejections with Fake Timers
+When using fake timers, an async function that rejects _during_ a `vi.advanceTimersByTimeAsync` call will trigger an [unhandled rejection](https://nodejs.org/api/process.html#event-unhandledrejection) — even if you later assert it with `.rejects.toThrow()`. This happens because the error is thrown before the `expect` chain has a chance to catch it.
+
+```ts
+async function foo() {
+ await new Promise(resolve => setTimeout(resolve, 100))
+ throw new Error('boom')
+}
+
+test('rejects', async () => {
+ const result = foo()
+
+ await vi.advanceTimersByTimeAsync(100)
+
+ // The assertion passes, but the error was already "unhandled" during advanceTimersByTimeAsync
+ await expect(result).rejects.toThrow()
+})
+```
+
+To avoid this, prefer [`vi.setTimerTickMode('nextTimerAsync')`](/api/vi#vi-settimertickmode) so that timers tick automatically as promises settle, without needing a manual advance:
+
+```ts
+beforeEach(() => {
+ vi.useFakeTimers()
+ vi.setTimerTickMode('nextTimerAsync')
+})
+
+test('rejects', async () => {
+ // No advanceTimersByTimeAsync needed — the error is caught by rejects.toThrow()
+ await expect(foo()).rejects.toThrow('boom')
+})
+```
+
+Alternatively, set up the `.rejects.toThrow()` assertion _before_ advancing timers so the rejection is handled immediately:
+
+```ts
+test('rejects', async () => {
+ const result = foo()
+ const assertion = expect(result).rejects.toThrow('boom')
+
+ await vi.advanceTimersByTimeAsync(100)
+ await assertion
+})
+```
+:::
## toMatchSnapshot
diff --git a/guide/features.md b/guide/features.md
index 5d480c74..3dc5411d 100644
--- a/guide/features.md
+++ b/guide/features.md
@@ -120,7 +120,7 @@ it('renders correctly', () => {
## 对象模拟 (Mocking) {#mocking}
-内置 [Tinyspy](https://github.com/tinylibs/tinyspy) 用于在 `vi` 对象上使用 `jest` 兼容的 API 进行对象模拟。
+Vitest 在 `vi` 对象上提供了与 `Jest` 兼容的 API 接口。
```ts
import { expect, vi } from 'vitest'