From f37fd4dac4855c4cd3e07b6b5623adc8949c0e49 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Sat, 24 Jan 2026 11:20:49 +0100 Subject: [PATCH 1/3] feat: implement `aroundEach` and `aroundAll` hooks (#9450) --- .vitepress/config.ts | 19 +- .vitepress/theme/index.ts | 4 + api/describe.md | 378 ++++++++++ api/expect.md | 4 +- api/hooks.md | 461 ++++++++++++ api/index.md | 1316 ----------------------------------- api/test.md | 857 +++++++++++++++++++++++ api/vi.md | 2 +- config/allowonly.md | 6 +- config/browser.md | 4 +- config/browser/expect.md | 4 +- config/fileparallelism.md | 2 +- config/sequence.md | 2 +- config/tags.md | 58 +- guide/browser/trace-view.md | 2 +- guide/cli-generated.md | 14 + guide/features.md | 4 +- guide/index.md | 2 +- guide/lifecycle.md | 6 +- guide/migration.md | 6 +- guide/parallelism.md | 2 +- guide/test-context.md | 4 +- 22 files changed, 1760 insertions(+), 1397 deletions(-) create mode 100644 api/describe.md create mode 100644 api/hooks.md delete mode 100644 api/index.md create mode 100644 api/test.md diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 15ce40ce..14b779f4 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -154,7 +154,7 @@ export default ({ mode }: { mode: string }) => { title: 'Vitest', items: [ { text: 'Guides', link: '/guide/' }, - { text: 'API', link: '/api/' }, + { text: 'API', link: '/api/test' }, { text: 'Config', link: '/config/' }, ], }, @@ -195,7 +195,7 @@ export default ({ mode }: { mode: string }) => { nav: [ { text: 'Guides', link: '/guide/', activeMatch: '^/guide/' }, - { text: 'API', link: '/api/', activeMatch: '^/api/' }, + { text: 'API', link: '/api/test', activeMatch: '^/api/' }, { text: 'Config', link: '/config/', activeMatch: '^/config/' }, { text: 'Blog', @@ -961,7 +961,20 @@ export default ({ mode }: { mode: string }) => { '/api': [ { text: 'Test API Reference', - link: '/api/', + items: [ + { + text: 'Test', + link: '/api/test', + }, + { + text: 'Describe', + link: '/api/describe', + }, + { + text: 'Hooks', + link: '/api/hooks', + }, + ], }, { text: 'Mocks', diff --git a/.vitepress/theme/index.ts b/.vitepress/theme/index.ts index ff5c5f62..84d65ad0 100644 --- a/.vitepress/theme/index.ts +++ b/.vitepress/theme/index.ts @@ -25,6 +25,10 @@ export default { return true } const url = new URL(to, location.href) + if (url.pathname === '/api/' || url.pathname === '/api' || url.pathname === '/api/index.html') { + setTimeout(() => { router.go(`/api/test`) }) + return false + } if (!url.hash) { return true } diff --git a/api/describe.md b/api/describe.md new file mode 100644 index 00000000..0eca4f42 --- /dev/null +++ b/api/describe.md @@ -0,0 +1,378 @@ +--- +outline: deep +--- + +# describe + +- **Alias:** `suite` + +```ts +function describe( + name: string | Function, + body?: () => unknown, + timeout?: number +): void +function describe( + name: string | Function, + options: SuiteOptions, + body?: () => unknown, +): void +``` + +`describe` is used to group related tests and benchmarks into a suite. Suites help organize your test files by creating logical blocks, making test output easier to read and enabling shared setup/teardown through [lifecycle hooks](/api/hooks). + +When you use `test` in the top level of file, they are collected as part of the implicit suite for it. Using `describe` you can define a new suite in the current context, as a set of related tests or benchmarks and other nested suites. + +```ts [basic.spec.ts] +import { describe, expect, test } from 'vitest' + +const person = { + isActive: true, + age: 32, +} + +describe('person', () => { + test('person is defined', () => { + expect(person).toBeDefined() + }) + + test('is active', () => { + expect(person.isActive).toBeTruthy() + }) + + test('age limit', () => { + expect(person.age).toBeLessThanOrEqual(32) + }) +}) +``` + +You can also nest `describe` blocks if you have a hierarchy of tests: + +```ts +import { describe, expect, test } from 'vitest' + +function numberToCurrency(value: number | string) { + if (typeof value !== 'number') { + throw new TypeError('Value must be a number') + } + + return value.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') +} + +describe('numberToCurrency', () => { + describe('given an invalid number', () => { + test('composed of non-numbers to throw error', () => { + expect(() => numberToCurrency('abc')).toThrowError() + }) + }) + + describe('given a valid number', () => { + test('returns the correct currency format', () => { + expect(numberToCurrency(10000)).toBe('10,000.00') + }) + }) +}) +``` + +## Test Options + +You can use [test options](/api/test#test-options) to apply configuration to every test inside a suite, including nested suites. This is useful when you want to set timeouts, retries, or other options for a group of related tests. + +```ts +import { describe, test } from 'vitest' + +describe('slow tests', { timeout: 10_000 }, () => { + test('test 1', () => { /* ... */ }) + test('test 2', () => { /* ... */ }) + + // nested suites also inherit the timeout + describe('nested', () => { + test('test 3', () => { /* ... */ }) + }) +}) +``` + +### `shuffle` + +- **Type:** `boolean` +- **Default:** `false` (configured by [`sequence.shuffle`](/config/sequence#sequence-shuffle)) +- **Alias:** [`describe.shuffle`](#describe-shuffle) + +Run tests within the suite in random order. This option is inherited by nested suites. + +```ts +import { describe, test } from 'vitest' + +describe('randomized tests', { shuffle: true }, () => { + test('test 1', () => { /* ... */ }) + test('test 2', () => { /* ... */ }) + test('test 3', () => { /* ... */ }) +}) +``` + +## describe.skip + +- **Alias:** `suite.skip` + +Use `describe.skip` in a suite to avoid running a particular describe block. + +```ts +import { assert, describe, test } from 'vitest' + +describe.skip('skipped suite', () => { + test('sqrt', () => { + // Suite skipped, no error + assert.equal(Math.sqrt(4), 3) + }) +}) +``` + +## describe.skipIf + +- **Alias:** `suite.skipIf` + +In some cases, you might run suites multiple times with different environments, and some of the suites might be environment-specific. Instead of wrapping the suite with `if`, you can use `describe.skipIf` to skip the suite whenever the condition is truthy. + +```ts +import { describe, test } from 'vitest' + +const isDev = process.env.NODE_ENV === 'development' + +describe.skipIf(isDev)('prod only test suite', () => { + // this test suite only runs in production +}) +``` + +## describe.runIf + +- **Alias:** `suite.runIf` + +Opposite of [describe.skipIf](#describe-skipif). + +```ts +import { assert, describe, test } from 'vitest' + +const isDev = process.env.NODE_ENV === 'development' + +describe.runIf(isDev)('dev only test suite', () => { + // this test suite only runs in development +}) +``` + +## describe.only + +- **Alias:** `suite.only` + +Use `describe.only` to only run certain suites + +```ts +import { assert, describe, test } from 'vitest' + +// Only this suite (and others marked with only) are run +describe.only('suite', () => { + test('sqrt', () => { + assert.equal(Math.sqrt(4), 3) + }) +}) + +describe('other suite', () => { + // ... will be skipped +}) +``` + +Sometimes it is very useful to run `only` tests in a certain file, ignoring all other tests from the whole test suite, which pollute the output. + +In order to do that, run `vitest` with specific file containing the tests in question: + +```shell +vitest interesting.test.ts +``` + +## describe.concurrent + +- **Alias:** `suite.concurrent` + +`describe.concurrent` runs all inner suites and tests in parallel + +```ts +import { describe, test } from 'vitest' + +// All suites and tests within this suite will be run in parallel +describe.concurrent('suite', () => { + test('concurrent test 1', async () => { /* ... */ }) + describe('concurrent suite 2', async () => { + test('concurrent test inner 1', async () => { /* ... */ }) + test('concurrent test inner 2', async () => { /* ... */ }) + }) + test.concurrent('concurrent test 3', async () => { /* ... */ }) +}) +``` + +`.skip`, `.only`, and `.todo` works with concurrent suites. All the following combinations are valid: + +```ts +describe.concurrent(/* ... */) +describe.skip.concurrent(/* ... */) // or describe.concurrent.skip(/* ... */) +describe.only.concurrent(/* ... */) // or describe.concurrent.only(/* ... */) +describe.todo.concurrent(/* ... */) // or describe.concurrent.todo(/* ... */) +``` + +When running concurrent tests, Snapshots and Assertions must use `expect` from the local [Test Context](/guide/test-context) to ensure the right test is detected. + +```ts +describe.concurrent('suite', () => { + test('concurrent test 1', async ({ expect }) => { + expect(foo).toMatchSnapshot() + }) + test('concurrent test 2', async ({ expect }) => { + expect(foo).toMatchSnapshot() + }) +}) +``` + +## describe.sequential + +- **Alias:** `suite.sequential` + +`describe.sequential` in a suite marks every test as sequential. This is useful if you want to run tests in sequence within `describe.concurrent` or with the `--sequence.concurrent` command option. + +```ts +import { describe, test } from 'vitest' + +describe.concurrent('suite', () => { + test('concurrent test 1', async () => { /* ... */ }) + test('concurrent test 2', async () => { /* ... */ }) + + describe.sequential('', () => { + test('sequential test 1', async () => { /* ... */ }) + test('sequential test 2', async () => { /* ... */ }) + }) +}) +``` + +## describe.shuffle + +- **Alias:** `suite.shuffle` + +Vitest provides a way to run all tests in random order via CLI flag [`--sequence.shuffle`](/guide/cli) or config option [`sequence.shuffle`](/config/#sequence-shuffle), but if you want to have only part of your test suite to run tests in random order, you can mark it with this flag. + +```ts +import { describe, test } from 'vitest' + +// or describe('suite', { shuffle: true }, ...) +describe.shuffle('suite', () => { + test('random test 1', async () => { /* ... */ }) + test('random test 2', async () => { /* ... */ }) + test('random test 3', async () => { /* ... */ }) + + // `shuffle` is inherited + describe('still random', () => { + test('random 4.1', async () => { /* ... */ }) + test('random 4.2', async () => { /* ... */ }) + }) + + // disable shuffle inside + describe('not random', { shuffle: false }, () => { + test('in order 5.1', async () => { /* ... */ }) + test('in order 5.2', async () => { /* ... */ }) + }) +}) +// order depends on sequence.seed option in config (Date.now() by default) +``` + +`.skip`, `.only`, and `.todo` works with random suites. + +## describe.todo + +- **Alias:** `suite.todo` + +Use `describe.todo` to stub suites to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement. + +```ts +// An entry will be shown in the report for this suite +describe.todo('unimplemented suite') +``` + +## describe.each + +- **Alias:** `suite.each` + +::: tip +While `describe.each` is provided for Jest compatibility, +Vitest also has [`describe.for`](#describe-for) which simplifies argument types and aligns with [`test.for`](/api/test#test-for). +::: + +Use `describe.each` if you have more than one test that depends on the same data. + +```ts +import { describe, expect, test } from 'vitest' + +describe.each([ + { a: 1, b: 1, expected: 2 }, + { a: 1, b: 2, expected: 3 }, + { a: 2, b: 1, expected: 3 }, +])('describe object add($a, $b)', ({ a, b, expected }) => { + test(`returns ${expected}`, () => { + expect(a + b).toBe(expected) + }) + + test(`returned value not be greater than ${expected}`, () => { + expect(a + b).not.toBeGreaterThan(expected) + }) + + test(`returned value not be less than ${expected}`, () => { + expect(a + b).not.toBeLessThan(expected) + }) +}) +``` + +* First row should be column names, separated by `|`; +* One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. + +```ts +import { describe, expect, test } from 'vitest' + +describe.each` + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} +`('describe template string add($a, $b)', ({ a, b, expected }) => { + test(`returns ${expected}`, () => { + expect(a + b).toBe(expected) + }) +}) +``` + +## describe.for + +- **Alias:** `suite.for` + +The difference from `describe.each` is how array case is provided in the arguments. +Other non array case (including template string usage) works exactly same. + +```ts +// `each` spreads array case +describe.each([ + [1, 1, 2], + [1, 2, 3], + [2, 1, 3], +])('add(%i, %i) -> %i', (a, b, expected) => { // [!code --] + test('test', () => { + expect(a + b).toBe(expected) + }) +}) + +// `for` doesn't spread array case +describe.for([ + [1, 1, 2], + [1, 2, 3], + [2, 1, 3], +])('add(%i, %i) -> %i', ([a, b, expected]) => { // [!code ++] + test('test', () => { + expect(a + b).toBe(expected) + }) +}) +``` diff --git a/api/expect.md b/api/expect.md index 40c424e6..e6d670ee 100644 --- a/api/expect.md +++ b/api/expect.md @@ -31,7 +31,7 @@ expect(input).to.equal(2) // chai API expect(input).toBe(2) // jest API ``` -Technically this example doesn't use [`test`](/api/#test) function, so in the console you will see Node.js error instead of Vitest output. To learn more about `test`, please read [Test API Reference](/api/). +Technically this example doesn't use [`test`](/api/test) function, so in the console you will see Node.js error instead of Vitest output. To learn more about `test`, please read [Test API Reference](/api/test). Also, `expect` can be used statically to access matcher functions, described later, and more. @@ -98,7 +98,7 @@ test('expect.soft test', () => { ``` ::: warning -`expect.soft` can only be used inside the [`test`](/api/#test) function. +`expect.soft` can only be used inside the [`test`](/api/test) function. ::: ## poll diff --git a/api/hooks.md b/api/hooks.md new file mode 100644 index 00000000..94ba3c1c --- /dev/null +++ b/api/hooks.md @@ -0,0 +1,461 @@ +--- +outline: deep +--- + +# Hooks + +These functions allow you to hook into the life cycle of tests to avoid repeating setup and teardown code. They apply to the current context: the file if they are used at the top-level or the current suite if they are inside a `describe` block. These hooks are not called, when you are running Vitest as a [type checker](/guide/testing-types). + +Test hooks are called in a stack order ("after" hooks are reversed) by default, but you can configure it via [`sequence.hooks`](/config/sequence#sequence-hooks) option. + +## beforeEach + +```ts +function beforeEach( + body: () => unknown, + timeout?: number, +): void +``` + +Register a callback to be called before each of the tests in the current suite runs. +If the function returns a promise, Vitest waits until the promise resolve before running the test. + +Optionally, you can pass a timeout (in milliseconds) defining how long to wait before terminating. The default is 10 seconds, and can be configured globally with [`hookTimeout`](/config/hooktimeout). + +```ts +import { beforeEach } from 'vitest' + +beforeEach(async () => { + // Clear mocks and add some testing data before each test run + await stopMocking() + await addUser({ name: 'John' }) +}) +``` + +Here, the `beforeEach` ensures that user is added for each test. + +`beforeEach` can also return an optional cleanup function (equivalent to [`afterEach`](#aftereach)): + +```ts +import { beforeEach } from 'vitest' + +beforeEach(async () => { + // called once before each test run + await prepareSomething() + + // clean up function, called once after each test run + return async () => { + await resetSomething() + } +}) +``` + +## afterEach + +```ts +function afterEach( + body: () => unknown, + timeout?: number, +): void +``` + +Register a callback to be called after each one of the tests in the current suite completes. +If the function returns a promise, Vitest waits until the promise resolve before continuing. + +Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 10 seconds, and can be configured globally with [`hookTimeout`](/config/hooktimeout). + +```ts +import { afterEach } from 'vitest' + +afterEach(async () => { + await clearTestingData() // clear testing data after each test run +}) +``` + +Here, the `afterEach` ensures that testing data is cleared after each test runs. + +::: tip +You can also use [`onTestFinished`](#ontestfinished) during the test execution to cleanup any state after the test has finished running. +::: + +## beforeAll + +```ts +function beforeAll( + body: () => unknown, + timeout?: number, +): void +``` + +Register a callback to be called once before starting to run all tests in the current suite. +If the function returns a promise, Vitest waits until the promise resolve before running tests. + +Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 10 seconds, and can be configured globally with [`hookTimeout`](/config/hooktimeout). + +```ts +import { beforeAll } from 'vitest' + +beforeAll(async () => { + await startMocking() // called once before all tests run +}) +``` + +Here the `beforeAll` ensures that the mock data is set up before tests run. + +`beforeAll` can also return an optional cleanup function (equivalent to [`afterAll`](#afterall)): + +```ts +import { beforeAll } from 'vitest' + +beforeAll(async () => { + // called once before all tests run + await startMocking() + + // clean up function, called once after all tests run + return async () => { + await stopMocking() + } +}) +``` + +## afterAll + +```ts +function afterAll( + body: () => unknown, + timeout?: number, +): void +``` + +Register a callback to be called once after all tests have run in the current suite. +If the function returns a promise, Vitest waits until the promise resolve before continuing. + +Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 10 seconds, and can be configured globally with [`hookTimeout`](/config/hooktimeout). + +```ts +import { afterAll } from 'vitest' + +afterAll(async () => { + await stopMocking() // this method is called after all tests run +}) +``` + +Here the `afterAll` ensures that `stopMocking` method is called after all tests run. + +## aroundEach + +```ts +function aroundEach( + body: (runTest: () => Promise, context: TestContext) => Promise, + timeout?: number, +): void +``` + +Register a callback function that wraps around each test within the current suite. The callback receives a `runTest` function that **must** be called to run the test. + +The `runTest()` function runs `beforeEach` hooks, the test itself, fixtures accessed in the test, and `afterEach` hooks. Fixtures that are accessed in the `aroundEach` callback are initialized before `runTest()` is called and are torn down after the aroundEach teardown code completes, allowing you to safely use them in both setup and teardown phases. + +::: warning +You **must** call `runTest()` within your callback. If `runTest()` is not called, the test will fail with an error. +::: + +Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The timeout applies independently to the setup phase (before `runTest()`) and teardown phase (after `runTest()`). The default is 10 seconds, and can be configured globally with [`hookTimeout`](/config/hooktimeout). + +```ts +import { aroundEach, test } from 'vitest' + +aroundEach(async (runTest) => { + await db.transaction(runTest) +}) + +test('insert user', async () => { + await db.insert({ name: 'Alice' }) + // transaction is automatically rolled back after the test +}) +``` + +::: tip When to use `aroundEach` +Use `aroundEach` when your test needs to run **inside a context** that wraps around it, such as: +- Wrapping tests in [AsyncLocalStorage](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context +- Wrapping tests with tracing spans +- Database transactions + +If you just need to run code before and after tests, prefer using [`beforeEach`](#beforeeach) with a cleanup return function: +```ts +beforeEach(async () => { + await database.connect() + return async () => { + await database.disconnect() + } +}) +``` +::: + +### Multiple Hooks + +When multiple `aroundEach` hooks are registered, they are nested inside each other. The first registered hook is the outermost wrapper: + +```ts +aroundEach(async (runTest) => { + console.log('outer before') + await runTest() + console.log('outer after') +}) + +aroundEach(async (runTest) => { + console.log('inner before') + await runTest() + console.log('inner after') +}) + +// Output order: +// outer before +// inner before +// test +// inner after +// outer after +``` + +### Context and Fixtures + +The callback receives the test context as the second argument which means that you can use fixtures with `aroundEach`: + +```ts +import { aroundEach, test as base } from 'vitest' + +const test = base.extend<{ db: Database; user: User }>({ + db: async ({}, use) => { + // db is created before `aroundEach` hook + const db = await createTestDatabase() + await use(db) + await db.close() + }, + user: async ({ db }, use) => { + // `user` runs as part of the transaction + // because it's accessed inside the `test` + const user = await db.createUser() + await use(user) + }, +}) + +// note that `aroundEach` is available on test +// for a better TypeScript support of fixtures +test.aroundEach(async (runTest, { db }) => { + await db.transaction(runTest) +}) + +test('insert user', async ({ db, user }) => { + await db.insert(user) +}) +``` + +## aroundAll + +```ts +function aroundAll( + body: (runSuite: () => Promise) => Promise, + timeout?: number, +): void +``` + +Register a callback function that wraps around all tests within the current suite. The callback receives a `runSuite` function that **must** be called to run the suite's tests. + +The `runSuite()` function runs all tests in the suite, including `beforeAll`/`afterAll`/`beforeEach`/`afterEach` hooks, `aroundEach` hooks, and fixtures. + +::: warning +You **must** call `runSuite()` within your callback. If `runSuite()` is not called, the hook will fail with an error and all tests in the suite will be skipped. +::: + +Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The timeout applies independently to the setup phase (before `runSuite()`) and teardown phase (after `runSuite()`). The default is 10 seconds, and can be configured globally with [`hookTimeout`](/config/hooktimeout). + +```ts +import { aroundAll, test } from 'vitest' + +aroundAll(async (runSuite) => { + await tracer.trace('test-suite', runSuite) +}) + +test('test 1', () => { + // Runs within the tracing span +}) + +test('test 2', () => { + // Also runs within the same tracing span +}) +``` + +::: tip When to use `aroundAll` +Use `aroundAll` when your suite needs to run **inside a context** that wraps around all tests, such as: +- Wrapping an entire suite in [AsyncLocalStorage](https://nodejs.org/api/async_context.html#class-asynclocalstorage) context +- Wrapping a suite with tracing spans +- Database transactions + +If you just need to run code once before and after all tests, prefer using [`beforeAll`](#beforeall) with a cleanup return function: +```ts +beforeAll(async () => { + await server.start() + return async () => { + await server.stop() + } +}) +``` +::: + +### Multiple Hooks + +When multiple `aroundAll` hooks are registered, they are nested inside each other. The first registered hook is the outermost wrapper: + +```ts +aroundAll(async (runSuite) => { + console.log('outer before') + await runSuite() + console.log('outer after') +}) + +aroundAll(async (runSuite) => { + console.log('inner before') + await runSuite() + console.log('inner after') +}) + +// Output order: outer before → inner before → tests → inner after → outer after +``` + +Each suite has its own independent `aroundAll` hooks. Parent suite's `aroundAll` wraps around child suite's execution: + +```ts +import { AsyncLocalStorage } from 'node:async_hooks' +import { aroundAll, describe, test } from 'vitest' + +const context = new AsyncLocalStorage<{ suiteId: string }>() + +aroundAll(async (runSuite) => { + await context.run({ suiteId: 'root' }, runSuite) +}) + +test('root test', () => { + // context.getStore() returns { suiteId: 'root' } +}) + +describe('nested', () => { + aroundAll(async (runSuite) => { + // Parent's context is available here + await context.run({ suiteId: 'nested' }, runSuite) + }) + + test('nested test', () => { + // context.getStore() returns { suiteId: 'nested' } + }) +}) +``` + +## Test Hooks + +Vitest provides a few hooks that you can call _during_ the test execution to cleanup the state when the test has finished running. + +::: warning +These hooks will throw an error if they are called outside of the test body. +::: + +### onTestFinished {#ontestfinished} + +This hook is always called after the test has finished running. It is called after `afterEach` hooks since they can influence the test result. It receives an `TestContext` object like `beforeEach` and `afterEach`. + +```ts {1,5} +import { onTestFinished, test } from 'vitest' + +test('performs a query', () => { + const db = connectDb() + onTestFinished(() => db.close()) + db.query('SELECT * FROM users') +}) +``` + +::: warning +If you are running tests concurrently, you should always use `onTestFinished` hook from the test context since Vitest doesn't track concurrent tests in global hooks: + +```ts {3,5} +import { test } from 'vitest' + +test.concurrent('performs a query', ({ onTestFinished }) => { + const db = connectDb() + onTestFinished(() => db.close()) + db.query('SELECT * FROM users') +}) +``` +::: + +This hook is particularly useful when creating reusable logic: + +```ts +// this can be in a separate file +function getTestDb() { + const db = connectMockedDb() + onTestFinished(() => db.close()) + return db +} + +test('performs a user query', async () => { + const db = getTestDb() + expect( + await db.query('SELECT * from users').perform() + ).toEqual([]) +}) + +test('performs an organization query', async () => { + const db = getTestDb() + expect( + await db.query('SELECT * from organizations').perform() + ).toEqual([]) +}) +``` + +It is also a good practice to cleanup your spies after each test, so they don't leak into other tests. You can do so by enabling [`restoreMocks`](/config/restoremocks) config globally, or restoring the spy inside `onTestFinished` (if you try to restore the mock at the end of the test, it won't be restored if one of the assertions fails - using `onTestFinished` ensures the code always runs): + +```ts +import { onTestFinished, test } from 'vitest' + +test('performs a query', () => { + const spy = vi.spyOn(db, 'query') + onTestFinished(() => spy.mockClear()) + + db.query('SELECT * FROM users') + expect(spy).toHaveBeenCalled() +}) +``` + +::: tip +This hook is always called in reverse order and is not affected by [`sequence.hooks`](/config/#sequence-hooks) option. +::: + +### onTestFailed + +This hook is called only after the test has failed. It is called after `afterEach` hooks since they can influence the test result. It receives a `TestContext` object like `beforeEach` and `afterEach`. This hook is useful for debugging. + +```ts {1,5-7} +import { onTestFailed, test } from 'vitest' + +test('performs a query', () => { + const db = connectDb() + onTestFailed(({ task }) => { + console.log(task.result.errors) + }) + db.query('SELECT * FROM users') +}) +``` + +::: warning +If you are running tests concurrently, you should always use `onTestFailed` hook from the test context since Vitest doesn't track concurrent tests in global hooks: + +```ts {3,5-7} +import { test } from 'vitest' + +test.concurrent('performs a query', ({ onTestFailed }) => { + const db = connectDb() + onTestFailed(({ task }) => { + console.log(task.result.errors) + }) + db.query('SELECT * FROM users') +}) +``` +::: diff --git a/api/index.md b/api/index.md deleted file mode 100644 index 850a79a0..00000000 --- a/api/index.md +++ /dev/null @@ -1,1316 +0,0 @@ ---- -outline: deep ---- - -# Test API Reference - -The following types are used in the type signatures below - -```ts -type Awaitable = T | PromiseLike -type TestFunction = () => Awaitable - -interface TestOptions { - /** - * Will fail the test if it takes too long to execute - */ - timeout?: number - /** - * Will retry the test specific number of times if it fails - * - * @default 0 - */ - retry?: number - /** - * Will repeat the same test several times even if it fails each time - * If you have "retry" option and it fails, it will use every retry in each cycle - * Useful for debugging random failings - * - * @default 0 - */ - repeats?: number - /** - * Custom tags of the test. Useful for filtering tests. - */ - tags?: string[] | string -} -``` - - - -When a test function returns a promise, the runner will wait until it is resolved to collect async expectations. If the promise is rejected, the test will fail. - -::: tip -In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If this form is used, the test will not be concluded until `done` is called. You can achieve the same using an `async` function, see the [Migration guide Done Callback section](/guide/migration#done-callback). -::: - -You can define options by chaining properties on a function: - -```ts -import { test } from 'vitest' - -test.skip('skipped test', () => { - // some logic that fails right now -}) - -test.concurrent.skip('skipped concurrent test', () => { - // some logic that fails right now -}) -``` - -But you can also provide an object as a second argument instead: - -```ts -import { test } from 'vitest' - -test('skipped test', { skip: true }, () => { - // some logic that fails right now -}) - -test('skipped concurrent test', { skip: true, concurrent: true }, () => { - // some logic that fails right now -}) -``` - -They both work in exactly the same way. To use either one is purely a stylistic choice. - -Note that if you are providing timeout as the last argument, you cannot use options anymore: - -```ts -import { test } from 'vitest' - -// ✅ this works -test.skip('heavy test', () => { - // ... -}, 10_000) - -// ❌ this doesn't work -test('heavy test', { skip: true }, () => { - // ... -}, 10_000) -``` - -However, you can provide a timeout inside the object: - -```ts -import { test } from 'vitest' - -// ✅ this works -test('heavy test', { skip: true, timeout: 10_000 }, () => { - // ... -}) -``` - -## test - -- **Alias:** `it` - -`test` defines a set of related expectations. It receives the test name and a function that holds the expectations to test. - -Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds, and can be configured globally with [testTimeout](/config/#testtimeout) - -```ts -import { expect, test } from 'vitest' - -test('should work as expected', () => { - expect(Math.sqrt(4)).toBe(2) -}) -``` - -### test.extend {#test-extended} - -- **Alias:** `it.extend` - -Use `test.extend` to extend the test context with custom fixtures. This will return a new `test` and it's also extendable, so you can compose more fixtures or override existing ones by extending it as you need. See [Extend Test Context](/guide/test-context.html#test-extend) for more information. - -```ts -import { expect, test } from 'vitest' - -const todos = [] -const archive = [] - -const myTest = test.extend({ - todos: async ({ task }, use) => { - todos.push(1, 2, 3) - await use(todos) - todos.length = 0 - }, - archive -}) - -myTest('add item', ({ todos }) => { - expect(todos.length).toBe(3) - - todos.push(4) - expect(todos.length).toBe(4) -}) -``` - -### test.skip - -- **Alias:** `it.skip` - -If you want to skip running certain tests, but you don't want to delete the code due to any reason, you can use `test.skip` to avoid running them. - -```ts -import { assert, test } from 'vitest' - -test.skip('skipped test', () => { - // Test skipped, no error - assert.equal(Math.sqrt(4), 3) -}) -``` - -You can also skip test by calling `skip` on its [context](/guide/test-context) dynamically: - -```ts -import { assert, test } from 'vitest' - -test('skipped test', (context) => { - context.skip() - // Test skipped, no error - assert.equal(Math.sqrt(4), 3) -}) -``` - -Since Vitest 3.1, if the condition is unknown, you can provide it to the `skip` method as the first arguments: - -```ts -import { assert, test } from 'vitest' - -test('skipped test', (context) => { - context.skip(Math.random() < 0.5, 'optional message') - // Test skipped, no error - assert.equal(Math.sqrt(4), 3) -}) -``` - -### test.skipIf - -- **Alias:** `it.skipIf` - -In some cases you might run tests multiple times with different environments, and some of the tests might be environment-specific. Instead of wrapping the test code with `if`, you can use `test.skipIf` to skip the test whenever the condition is truthy. - -```ts -import { assert, test } from 'vitest' - -const isDev = process.env.NODE_ENV === 'development' - -test.skipIf(isDev)('prod only test', () => { - // this test only runs in production -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### test.runIf - -- **Alias:** `it.runIf` - -Opposite of [test.skipIf](#test-skipif). - -```ts -import { assert, test } from 'vitest' - -const isDev = process.env.NODE_ENV === 'development' - -test.runIf(isDev)('dev only test', () => { - // this test only runs in development -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### test.only - -- **Alias:** `it.only` - -Use `test.only` to only run certain tests in a given suite. This is useful when debugging. - -Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds, and can be configured globally with [testTimeout](/config/#testtimeout). - -```ts -import { assert, test } from 'vitest' - -test.only('test', () => { - // Only this test (and others marked with only) are run - assert.equal(Math.sqrt(4), 2) -}) -``` - -Sometimes it is very useful to run `only` tests in a certain file, ignoring all other tests from the whole test suite, which pollute the output. - -In order to do that run `vitest` with specific file containing the tests in question. -``` -# vitest interesting.test.ts -``` - -### test.concurrent - -- **Alias:** `it.concurrent` - -`test.concurrent` marks consecutive tests to be run in parallel. It receives the test name, an async function with the tests to collect, and an optional timeout (in milliseconds). - -```ts -import { describe, test } from 'vitest' - -// The two tests marked with concurrent will be run in parallel -describe('suite', () => { - test('serial test', async () => { /* ... */ }) - test.concurrent('concurrent test 1', async () => { /* ... */ }) - test.concurrent('concurrent test 2', async () => { /* ... */ }) -}) -``` - -`test.skip`, `test.only`, and `test.todo` works with concurrent tests. All the following combinations are valid: - -```ts -test.concurrent(/* ... */) -test.skip.concurrent(/* ... */) // or test.concurrent.skip(/* ... */) -test.only.concurrent(/* ... */) // or test.concurrent.only(/* ... */) -test.todo.concurrent(/* ... */) // or test.concurrent.todo(/* ... */) -``` - -When running concurrent tests, Snapshots and Assertions must use `expect` from the local [Test Context](/guide/test-context.md) to ensure the right test is detected. - -```ts -test.concurrent('test 1', async ({ expect }) => { - expect(foo).toMatchSnapshot() -}) -test.concurrent('test 2', async ({ expect }) => { - expect(foo).toMatchSnapshot() -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### test.sequential - -- **Alias:** `it.sequential` - -`test.sequential` marks a test as sequential. This is useful if you want to run tests in sequence within `describe.concurrent` or with the `--sequence.concurrent` command option. - -```ts -import { describe, test } from 'vitest' - -// with config option { sequence: { concurrent: true } } -test('concurrent test 1', async () => { /* ... */ }) -test('concurrent test 2', async () => { /* ... */ }) - -test.sequential('sequential test 1', async () => { /* ... */ }) -test.sequential('sequential test 2', async () => { /* ... */ }) - -// within concurrent suite -describe.concurrent('suite', () => { - test('concurrent test 1', async () => { /* ... */ }) - test('concurrent test 2', async () => { /* ... */ }) - - test.sequential('sequential test 1', async () => { /* ... */ }) - test.sequential('sequential test 2', async () => { /* ... */ }) -}) -``` - -### test.todo - -- **Alias:** `it.todo` - -Use `test.todo` to stub tests to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement. - -```ts -// An entry will be shown in the report for this test -test.todo('unimplemented test') -``` - -### test.fails - -- **Alias:** `it.fails` - -Use `test.fails` to indicate that an assertion will fail explicitly. - -```ts -import { expect, test } from 'vitest' - -function myAsyncFunc() { - return new Promise(resolve => resolve(1)) -} -test.fails('fail test', async () => { - await expect(myAsyncFunc()).rejects.toBe(1) -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### test.each - -- **Alias:** `it.each` - -::: tip -While `test.each` is provided for Jest compatibility, -Vitest also has [`test.for`](#test-for) with an additional feature to integrate [`TestContext`](/guide/test-context). -::: - -Use `test.each` when you need to run the same test with different variables. -You can inject parameters with [printf formatting](https://nodejs.org/api/util.html#util_util_format_format_args) in the test name in the order of the test function parameters. - -- `%s`: string -- `%d`: number -- `%i`: integer -- `%f`: floating point value -- `%j`: json -- `%o`: object -- `%#`: 0-based index of the test case -- `%$`: 1-based index of the test case -- `%%`: single percent sign ('%') - -```ts -import { expect, test } from 'vitest' - -test.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add(%i, %i) -> %i', (a, b, expected) => { - expect(a + b).toBe(expected) -}) - -// this will return -// ✓ add(1, 1) -> 2 -// ✓ add(1, 2) -> 3 -// ✓ add(2, 1) -> 3 -``` - -You can also access object properties and array elements with `$` prefix: - -```ts -test.each([ - { a: 1, b: 1, expected: 2 }, - { a: 1, b: 2, expected: 3 }, - { a: 2, b: 1, expected: 3 }, -])('add($a, $b) -> $expected', ({ a, b, expected }) => { - expect(a + b).toBe(expected) -}) - -// this will return -// ✓ add(1, 1) -> 2 -// ✓ add(1, 2) -> 3 -// ✓ add(2, 1) -> 3 - -test.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add($0, $1) -> $2', (a, b, expected) => { - expect(a + b).toBe(expected) -}) - -// this will return -// ✓ add(1, 1) -> 2 -// ✓ add(1, 2) -> 3 -// ✓ add(2, 1) -> 3 -``` - -You can also access Object attributes with `.`, if you are using objects as arguments: - - ```ts - test.each` - a | b | expected - ${{ val: 1 }} | ${'b'} | ${'1b'} - ${{ val: 2 }} | ${'b'} | ${'2b'} - ${{ val: 3 }} | ${'b'} | ${'3b'} - `('add($a.val, $b) -> $expected', ({ a, b, expected }) => { - expect(a.val + b).toBe(expected) - }) - - // this will return - // ✓ add(1, b) -> 1b - // ✓ add(2, b) -> 2b - // ✓ add(3, b) -> 3b - ``` - -* First row should be column names, separated by `|`; -* One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. - -```ts -import { expect, test } from 'vitest' - -test.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} - ${[]} | ${'b'} | ${'b'} - ${{}} | ${'b'} | ${'[object Object]b'} - ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} -`('returns $expected when $a is added $b', ({ a, b, expected }) => { - expect(a + b).toBe(expected) -}) -``` - -::: tip -Vitest processes `$values` with Chai `format` method. If the value is too truncated, you can increase [chaiConfig.truncateThreshold](/config/#chaiconfig-truncatethreshold) in your config file. -::: - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### test.for - -- **Alias:** `it.for` - -Alternative to `test.each` to provide [`TestContext`](/guide/test-context). - -The difference from `test.each` lies in how arrays are provided in the arguments. -Non-array arguments to `test.for` (including template string usage) work exactly the same as for `test.each`. - -```ts -// `each` spreads arrays -test.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add(%i, %i) -> %i', (a, b, expected) => { // [!code --] - expect(a + b).toBe(expected) -}) - -// `for` doesn't spread arrays (notice the square brackets around the arguments) -test.for([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add(%i, %i) -> %i', ([a, b, expected]) => { // [!code ++] - expect(a + b).toBe(expected) -}) -``` - -The 2nd argument is [`TestContext`](/guide/test-context) and can be used for concurrent snapshots, for example: - -```ts -test.concurrent.for([ - [1, 1], - [1, 2], - [2, 1], -])('add(%i, %i)', ([a, b], { expect }) => { - expect(a + b).matchSnapshot() -}) -``` - -## bench - -- **Type:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` - -`bench` defines a benchmark. In Vitest terms, benchmark is a function that defines a series of operations. Vitest runs this function multiple times to display different performance results. - -Vitest uses the [`tinybench`](https://github.com/tinylibs/tinybench) library under the hood, inheriting all its options that can be used as a third argument. - -```ts -import { bench } from 'vitest' - -bench('normal sorting', () => { - const x = [1, 5, 4, 2, 3] - x.sort((a, b) => { - return a - b - }) -}, { time: 1000 }) -``` - -```ts -export interface Options { - /** - * time needed for running a benchmark task (milliseconds) - * @default 500 - */ - time?: number - - /** - * number of times that a task should run if even the time option is finished - * @default 10 - */ - iterations?: number - - /** - * function to get the current timestamp in milliseconds - */ - now?: () => number - - /** - * An AbortSignal for aborting the benchmark - */ - signal?: AbortSignal - - /** - * Throw if a task fails (events will not work if true) - */ - throws?: boolean - - /** - * warmup time (milliseconds) - * @default 100ms - */ - warmupTime?: number - - /** - * warmup iterations - * @default 5 - */ - warmupIterations?: number - - /** - * setup function to run before each benchmark task (cycle) - */ - setup?: Hook - - /** - * teardown function to run after each benchmark task (cycle) - */ - teardown?: Hook -} -``` -After the test case is run, the output structure information is as follows: - -``` - name hz min max mean p75 p99 p995 p999 rme samples -· normal sorting 6,526,368.12 0.0001 0.3638 0.0002 0.0002 0.0002 0.0002 0.0004 ±1.41% 652638 -``` -```ts -export interface TaskResult { - /* - * the last error that was thrown while running the task - */ - error?: unknown - - /** - * The amount of time in milliseconds to run the benchmark task (cycle). - */ - totalTime: number - - /** - * the minimum value in the samples - */ - min: number - /** - * the maximum value in the samples - */ - max: number - - /** - * the number of operations per second - */ - hz: number - - /** - * how long each operation takes (ms) - */ - period: number - - /** - * task samples of each task iteration time (ms) - */ - samples: number[] - - /** - * samples mean/average (estimate of the population mean) - */ - mean: number - - /** - * samples variance (estimate of the population variance) - */ - variance: number - - /** - * samples standard deviation (estimate of the population standard deviation) - */ - sd: number - - /** - * standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean) - */ - sem: number - - /** - * degrees of freedom - */ - df: number - - /** - * critical value of the samples - */ - critical: number - - /** - * margin of error - */ - moe: number - - /** - * relative margin of error - */ - rme: number - - /** - * median absolute deviation - */ - mad: number - - /** - * p50/median percentile - */ - p50: number - - /** - * p75 percentile - */ - p75: number - - /** - * p99 percentile - */ - p99: number - - /** - * p995 percentile - */ - p995: number - - /** - * p999 percentile - */ - p999: number -} -``` - -### bench.skip - -- **Type:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` - -You can use `bench.skip` syntax to skip running certain benchmarks. - -```ts -import { bench } from 'vitest' - -bench.skip('normal sorting', () => { - const x = [1, 5, 4, 2, 3] - x.sort((a, b) => { - return a - b - }) -}) -``` - -### bench.only - -- **Type:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` - -Use `bench.only` to only run certain benchmarks in a given suite. This is useful when debugging. - -```ts -import { bench } from 'vitest' - -bench.only('normal sorting', () => { - const x = [1, 5, 4, 2, 3] - x.sort((a, b) => { - return a - b - }) -}) -``` - -### bench.todo - -- **Type:** `(name: string | Function) => void` - -Use `bench.todo` to stub benchmarks to be implemented later. - -```ts -import { bench } from 'vitest' - -bench.todo('unimplemented test') -``` - -## describe - -When you use `test` or `bench` in the top level of file, they are collected as part of the implicit suite for it. Using `describe` you can define a new suite in the current context, as a set of related tests or benchmarks and other nested suites. A suite lets you organize your tests and benchmarks so reports are more clear. - -```ts -// basic.spec.ts -// organizing tests - -import { describe, expect, test } from 'vitest' - -const person = { - isActive: true, - age: 32, -} - -describe('person', () => { - test('person is defined', () => { - expect(person).toBeDefined() - }) - - test('is active', () => { - expect(person.isActive).toBeTruthy() - }) - - test('age limit', () => { - expect(person.age).toBeLessThanOrEqual(32) - }) -}) -``` - -```ts -// basic.bench.ts -// organizing benchmarks - -import { bench, describe } from 'vitest' - -describe('sort', () => { - bench('normal', () => { - const x = [1, 5, 4, 2, 3] - x.sort((a, b) => { - return a - b - }) - }) - - bench('reverse', () => { - const x = [1, 5, 4, 2, 3] - x.reverse().sort((a, b) => { - return a - b - }) - }) -}) -``` - -You can also nest describe blocks if you have a hierarchy of tests or benchmarks: - -```ts -import { describe, expect, test } from 'vitest' - -function numberToCurrency(value: number | string) { - if (typeof value !== 'number') { - throw new TypeError('Value must be a number') - } - - return value.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') -} - -describe('numberToCurrency', () => { - describe('given an invalid number', () => { - test('composed of non-numbers to throw error', () => { - expect(() => numberToCurrency('abc')).toThrowError() - }) - }) - - describe('given a valid number', () => { - test('returns the correct currency format', () => { - expect(numberToCurrency(10000)).toBe('10,000.00') - }) - }) -}) -``` - -### describe.skip - -- **Alias:** `suite.skip` - -Use `describe.skip` in a suite to avoid running a particular describe block. - -```ts -import { assert, describe, test } from 'vitest' - -describe.skip('skipped suite', () => { - test('sqrt', () => { - // Suite skipped, no error - assert.equal(Math.sqrt(4), 3) - }) -}) -``` - -### describe.skipIf - -- **Alias:** `suite.skipIf` - -In some cases, you might run suites multiple times with different environments, and some of the suites might be environment-specific. Instead of wrapping the suite with `if`, you can use `describe.skipIf` to skip the suite whenever the condition is truthy. - -```ts -import { describe, test } from 'vitest' - -const isDev = process.env.NODE_ENV === 'development' - -describe.skipIf(isDev)('prod only test suite', () => { - // this test suite only runs in production -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### describe.runIf - -- **Alias:** `suite.runIf` - -Opposite of [describe.skipIf](#describe-skipif). - -```ts -import { assert, describe, test } from 'vitest' - -const isDev = process.env.NODE_ENV === 'development' - -describe.runIf(isDev)('dev only test suite', () => { - // this test suite only runs in development -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### describe.only - -- **Type:** `(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void` - -Use `describe.only` to only run certain suites - -```ts -import { assert, describe, test } from 'vitest' - -// Only this suite (and others marked with only) are run -describe.only('suite', () => { - test('sqrt', () => { - assert.equal(Math.sqrt(4), 3) - }) -}) - -describe('other suite', () => { - // ... will be skipped -}) -``` - -Sometimes it is very useful to run `only` tests in a certain file, ignoring all other tests from the whole test suite, which pollute the output. - -In order to do that run `vitest` with specific file containing the tests in question. -``` -# vitest interesting.test.ts -``` - -### describe.concurrent - -- **Alias:** `suite.concurrent` - -`describe.concurrent` runs all inner suites and tests in parallel - -```ts -import { describe, test } from 'vitest' - -// All suites and tests within this suite will be run in parallel -describe.concurrent('suite', () => { - test('concurrent test 1', async () => { /* ... */ }) - describe('concurrent suite 2', async () => { - test('concurrent test inner 1', async () => { /* ... */ }) - test('concurrent test inner 2', async () => { /* ... */ }) - }) - test.concurrent('concurrent test 3', async () => { /* ... */ }) -}) -``` - -`.skip`, `.only`, and `.todo` works with concurrent suites. All the following combinations are valid: - -```ts -describe.concurrent(/* ... */) -describe.skip.concurrent(/* ... */) // or describe.concurrent.skip(/* ... */) -describe.only.concurrent(/* ... */) // or describe.concurrent.only(/* ... */) -describe.todo.concurrent(/* ... */) // or describe.concurrent.todo(/* ... */) -``` - -When running concurrent tests, Snapshots and Assertions must use `expect` from the local [Test Context](/guide/test-context) to ensure the right test is detected. - -```ts -describe.concurrent('suite', () => { - test('concurrent test 1', async ({ expect }) => { - expect(foo).toMatchSnapshot() - }) - test('concurrent test 2', async ({ expect }) => { - expect(foo).toMatchSnapshot() - }) -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### describe.sequential - -- **Alias:** `suite.sequential` - -`describe.sequential` in a suite marks every test as sequential. This is useful if you want to run tests in sequence within `describe.concurrent` or with the `--sequence.concurrent` command option. - -```ts -import { describe, test } from 'vitest' - -describe.concurrent('suite', () => { - test('concurrent test 1', async () => { /* ... */ }) - test('concurrent test 2', async () => { /* ... */ }) - - describe.sequential('', () => { - test('sequential test 1', async () => { /* ... */ }) - test('sequential test 2', async () => { /* ... */ }) - }) -}) -``` - -### describe.shuffle - -- **Alias:** `suite.shuffle` - -Vitest provides a way to run all tests in random order via CLI flag [`--sequence.shuffle`](/guide/cli) or config option [`sequence.shuffle`](/config/#sequence-shuffle), but if you want to have only part of your test suite to run tests in random order, you can mark it with this flag. - -```ts -import { describe, test } from 'vitest' - -// or describe('suite', { shuffle: true }, ...) -describe.shuffle('suite', () => { - test('random test 1', async () => { /* ... */ }) - test('random test 2', async () => { /* ... */ }) - test('random test 3', async () => { /* ... */ }) - - // `shuffle` is inherited - describe('still random', () => { - test('random 4.1', async () => { /* ... */ }) - test('random 4.2', async () => { /* ... */ }) - }) - - // disable shuffle inside - describe('not random', { shuffle: false }, () => { - test('in order 5.1', async () => { /* ... */ }) - test('in order 5.2', async () => { /* ... */ }) - }) -}) -// order depends on sequence.seed option in config (Date.now() by default) -``` - -`.skip`, `.only`, and `.todo` works with random suites. - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### describe.todo - -- **Alias:** `suite.todo` - -Use `describe.todo` to stub suites to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement. - -```ts -// An entry will be shown in the report for this suite -describe.todo('unimplemented suite') -``` - -### describe.each - -- **Alias:** `suite.each` - -::: tip -While `describe.each` is provided for Jest compatibility, -Vitest also has [`describe.for`](#describe-for) which simplifies argument types and aligns with [`test.for`](#test-for). -::: - -Use `describe.each` if you have more than one test that depends on the same data. - -```ts -import { describe, expect, test } from 'vitest' - -describe.each([ - { a: 1, b: 1, expected: 2 }, - { a: 1, b: 2, expected: 3 }, - { a: 2, b: 1, expected: 3 }, -])('describe object add($a, $b)', ({ a, b, expected }) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected) - }) - - test(`returned value not be greater than ${expected}`, () => { - expect(a + b).not.toBeGreaterThan(expected) - }) - - test(`returned value not be less than ${expected}`, () => { - expect(a + b).not.toBeLessThan(expected) - }) -}) -``` - -* First row should be column names, separated by `|`; -* One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. - -```ts -import { describe, expect, test } from 'vitest' - -describe.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} - ${[]} | ${'b'} | ${'b'} - ${{}} | ${'b'} | ${'[object Object]b'} - ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} -`('describe template string add($a, $b)', ({ a, b, expected }) => { - test(`returns ${expected}`, () => { - expect(a + b).toBe(expected) - }) -}) -``` - -::: warning -You cannot use this syntax when using Vitest as [type checker](/guide/testing-types). -::: - -### describe.for - -- **Alias:** `suite.for` - -The difference from `describe.each` is how array case is provided in the arguments. -Other non array case (including template string usage) works exactly same. - -```ts -// `each` spreads array case -describe.each([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add(%i, %i) -> %i', (a, b, expected) => { // [!code --] - test('test', () => { - expect(a + b).toBe(expected) - }) -}) - -// `for` doesn't spread array case -describe.for([ - [1, 1, 2], - [1, 2, 3], - [2, 1, 3], -])('add(%i, %i) -> %i', ([a, b, expected]) => { // [!code ++] - test('test', () => { - expect(a + b).toBe(expected) - }) -}) -``` - -## Setup and Teardown - -These functions allow you to hook into the life cycle of tests to avoid repeating setup and teardown code. They apply to the current context: the file if they are used at the top-level or the current suite if they are inside a `describe` block. These hooks are not called, when you are running Vitest as a type checker. - -### beforeEach - -- **Type:** `beforeEach(fn: () => Awaitable, timeout?: number)` - -Register a callback to be called before each of the tests in the current context runs. -If the function returns a promise, Vitest waits until the promise resolve before running the test. - -Optionally, you can pass a timeout (in milliseconds) defining how long to wait before terminating. The default is 5 seconds. - -```ts -import { beforeEach } from 'vitest' - -beforeEach(async () => { - // Clear mocks and add some testing data before each test run - await stopMocking() - await addUser({ name: 'John' }) -}) -``` - -Here, the `beforeEach` ensures that user is added for each test. - -`beforeEach` also accepts an optional cleanup function (equivalent to `afterEach`). - -```ts -import { beforeEach } from 'vitest' - -beforeEach(async () => { - // called once before each test run - await prepareSomething() - - // clean up function, called once after each test run - return async () => { - await resetSomething() - } -}) -``` - -### afterEach - -- **Type:** `afterEach(fn: () => Awaitable, timeout?: number)` - -Register a callback to be called after each one of the tests in the current context completes. -If the function returns a promise, Vitest waits until the promise resolve before continuing. - -Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds. - -```ts -import { afterEach } from 'vitest' - -afterEach(async () => { - await clearTestingData() // clear testing data after each test run -}) -``` - -Here, the `afterEach` ensures that testing data is cleared after each test runs. - -::: tip -You can also use [`onTestFinished`](#ontestfinished) during the test execution to cleanup any state after the test has finished running. -::: - -### beforeAll - -- **Type:** `beforeAll(fn: () => Awaitable, timeout?: number)` - -Register a callback to be called once before starting to run all tests in the current context. -If the function returns a promise, Vitest waits until the promise resolve before running tests. - -Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds. - -```ts -import { beforeAll } from 'vitest' - -beforeAll(async () => { - await startMocking() // called once before all tests run -}) -``` - -Here the `beforeAll` ensures that the mock data is set up before tests run. - -`beforeAll` also accepts an optional cleanup function (equivalent to `afterAll`). - -```ts -import { beforeAll } from 'vitest' - -beforeAll(async () => { - // called once before all tests run - await startMocking() - - // clean up function, called once after all tests run - return async () => { - await stopMocking() - } -}) -``` - -### afterAll - -- **Type:** `afterAll(fn: () => Awaitable, timeout?: number)` - -Register a callback to be called once after all tests have run in the current context. -If the function returns a promise, Vitest waits until the promise resolve before continuing. - -Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds. - -```ts -import { afterAll } from 'vitest' - -afterAll(async () => { - await stopMocking() // this method is called after all tests run -}) -``` - -Here the `afterAll` ensures that `stopMocking` method is called after all tests run. - -## Test Hooks - -Vitest provides a few hooks that you can call _during_ the test execution to cleanup the state when the test has finished running. - -::: warning -These hooks will throw an error if they are called outside of the test body. -::: - -### onTestFinished {#ontestfinished} - -This hook is always called after the test has finished running. It is called after `afterEach` hooks since they can influence the test result. It receives an `ExtendedContext` object like `beforeEach` and `afterEach`. - -```ts {1,5} -import { onTestFinished, test } from 'vitest' - -test('performs a query', () => { - const db = connectDb() - onTestFinished(() => db.close()) - db.query('SELECT * FROM users') -}) -``` - -::: warning -If you are running tests concurrently, you should always use `onTestFinished` hook from the test context since Vitest doesn't track concurrent tests in global hooks: - -```ts {3,5} -import { test } from 'vitest' - -test.concurrent('performs a query', ({ onTestFinished }) => { - const db = connectDb() - onTestFinished(() => db.close()) - db.query('SELECT * FROM users') -}) -``` -::: - -This hook is particularly useful when creating reusable logic: - -```ts -// this can be in a separate file -function getTestDb() { - const db = connectMockedDb() - onTestFinished(() => db.close()) - return db -} - -test('performs a user query', async () => { - const db = getTestDb() - expect( - await db.query('SELECT * from users').perform() - ).toEqual([]) -}) - -test('performs an organization query', async () => { - const db = getTestDb() - expect( - await db.query('SELECT * from organizations').perform() - ).toEqual([]) -}) -``` - -::: tip -This hook is always called in reverse order and is not affected by [`sequence.hooks`](/config/#sequence-hooks) option. -::: - -### onTestFailed - -This hook is called only after the test has failed. It is called after `afterEach` hooks since they can influence the test result. It receives an `ExtendedContext` object like `beforeEach` and `afterEach`. This hook is useful for debugging. - -```ts {1,5-7} -import { onTestFailed, test } from 'vitest' - -test('performs a query', () => { - const db = connectDb() - onTestFailed(({ task }) => { - console.log(task.result.errors) - }) - db.query('SELECT * FROM users') -}) -``` - -::: warning -If you are running tests concurrently, you should always use `onTestFailed` hook from the test context since Vitest doesn't track concurrent tests in global hooks: - -```ts {3,5-7} -import { test } from 'vitest' - -test.concurrent('performs a query', ({ onTestFailed }) => { - const db = connectDb() - onTestFailed(({ task }) => { - console.log(task.result.errors) - }) - db.query('SELECT * FROM users') -}) -``` -::: diff --git a/api/test.md b/api/test.md new file mode 100644 index 00000000..c44e890f --- /dev/null +++ b/api/test.md @@ -0,0 +1,857 @@ +--- +outline: deep +--- + +# Test + +- **Alias:** `it` + +```ts +function test( + name: string | Function, + body?: () => unknown, + timeout?: number +): void +function test( + name: string | Function, + options: TestOptions, + body?: () => unknown, +): void +``` + +`test` or `it` defines a set of related expectations. It receives the test name and a function that holds the expectations to test. + +Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating, or a set of [additional options](#test-options). The default timeout is 5 seconds, and can be configured globally with [`testTimeout`](/config/testtimeout). + +```ts +import { expect, test } from 'vitest' + +test('should work as expected', () => { + expect(Math.sqrt(4)).toBe(2) +}) +``` + +::: warning +If the first argument is a function, its `name` property will be used as the name of the test. The function itself will not be called. + +If test body is not provided, the test is marked as `todo`. +::: + +When a test function returns a promise, the runner will wait until it is resolved to collect async expectations. If the promise is rejected, the test will fail. + +::: tip +In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If this form is used, the test will not be concluded until `done` is called. You can achieve the same using an `async` function, see the [Migration guide Done Callback section](/guide/migration#done-callback). +::: + +## Test Options + +You can define boolean options by chaining properties on a function: + +```ts +import { test } from 'vitest' + +test.skip('skipped test', () => { + // some logic that fails right now +}) + +test.concurrent.skip('skipped concurrent test', () => { + // some logic that fails right now +}) +``` + +But you can also provide an object as a second argument instead: + +```ts +import { test } from 'vitest' + +test('skipped test', { skip: true }, () => { + // some logic that fails right now +}) + +test('skipped concurrent test', { skip: true, concurrent: true }, () => { + // some logic that fails right now +}) +``` + +They both work in exactly the same way. To use either one is purely a stylistic choice. + +### timeout + +- **Type:** `number` +- **Default:** `5_000` (configured by [`testTimeout`](/config/testtimeout)) + +Test timeout in milliseconds. + +::: warning +Note that if you are providing timeout as the last argument, you cannot use options anymore: + +```ts +import { test } from 'vitest' + +// ✅ this works +test.skip('heavy test', () => { + // ... +}, 10_000) + +// ❌ this doesn't work +test('heavy test', { skip: true }, () => { + // ... +}, 10_000) +``` + +However, you can provide a timeout inside the object: + +```ts +import { test } from 'vitest' + +// ✅ this works +test('heavy test', { skip: true, timeout: 10_000 }, () => { + // ... +}) +``` +::: + +### retry + +- **Default:** `0` (configured by [`retry`](/config/retry)) +- **Type:** + +```ts +type Retry = number | { + /** + * The number of times to retry the test if it fails. + * @default 0 + */ + count?: number + /** + * Delay in milliseconds between retry attempts. + * @default 0 + */ + delay?: number + /** + * Condition to determine if a test should be retried based on the error. + * - If a RegExp, it is tested against the error message + * - If a function, called with the TestError object; return true to retry + * + * NOTE: Functions can only be used in test files, not in vitest.config.ts, + * because the configuration is serialized when passed to worker threads. + * + * @default undefined (retry on all errors) + */ + condition?: RegExp | ((error: TestError) => boolean) +} +``` + +Retry configuration for the test. If a number, specifies how many times to retry. If an object, allows fine-grained retry control. + +Note that the object configuration is available only since Vitest 4.1. + +### repeats + +- **Type:** `number` +- **Default:** `0` + +How many times the test will run again. If set to `0` (the default), the test will run only one time. + +This can be useful for debugging flaky tests. + +### tags 4.1.0 {#tags} + +- **Type:** `string[]` +- **Default:** `[]` + +Custom user [tags](/guide/test-tags). If the tag is not specified in the [configuration](/config/tags), the test will fail before it starts, unless [`strictTags`](/config/stricttags) is disabled manually. + +```ts +import { it } from 'vitest' + +it('user returns data from db', { tags: ['db', 'flaky'] }, () => { + // ... +}) +``` + +### concurrent + +- **Type:** `boolean` +- **Default:** `false` (configured by [`sequence.concurrent`](/config/sequence#sequence-concurrent)) +- **Alias:** [`test.concurrent`](#test-concurrent) + +Whether this test run concurrently with other concurrent tests in the suite. + +### sequential + +- **Type:** `boolean` +- **Default:** `true` +- **Alias:** [`test.sequential`](#test-sequential) + +Whether tests run sequentially. When both `concurrent` and `sequential` are specified, `concurrent` takes precendence. + +### skip + +- **Type:** `boolean` +- **Default:** `false` +- **Alias:** [`test.skip`](#test-skip) + +Whether the test should be skipped. + +### only + +- **Type:** `boolean` +- **Default:** `false` +- **Alias:** [`test.only`](#test-only) + +Should this test be the only one running in a suite. + +### todo + +- **Type:** `boolean` +- **Default:** `false` +- **Alias:** [`test.todo`](#test-todo) + +Whether the test should be skipped and marked as a todo. + +### fails + +- **Type:** `boolean` +- **Default:** `false` +- **Alias:** [`test.fails`](#test-fails) + +Whether the test is expected to fail. If it does, the test will pass, otherwise it will fail. + +## test.extend + +- **Alias:** `it.extend` + +Use `test.extend` to extend the test context with custom fixtures. This will return a new `test` and it's also extendable, so you can compose more fixtures or override existing ones by extending it as you need. See [Extend Test Context](/guide/test-context.html#test-extend) for more information. + +```ts +import { test as baseTest, expect } from 'vitest' + +const todos = [] +const archive = [] + +const test = baseTest.extend({ + todos: async ({ task }, use) => { + todos.push(1, 2, 3) + await use(todos) + todos.length = 0 + }, + archive, +}) + +test('add item', ({ todos }) => { + expect(todos.length).toBe(3) + + todos.push(4) + expect(todos.length).toBe(4) +}) +``` + +## test.skip + +- **Alias:** `it.skip` + +If you want to skip running certain tests, but you don't want to delete the code due to any reason, you can use `test.skip` to avoid running them. + +```ts +import { assert, test } from 'vitest' + +test.skip('skipped test', () => { + // Test skipped, no error + assert.equal(Math.sqrt(4), 3) +}) +``` + +You can also skip test by calling `skip` on its [context](/guide/test-context) dynamically: + +```ts +import { assert, test } from 'vitest' + +test('skipped test', (context) => { + context.skip() + // Test skipped, no error + assert.equal(Math.sqrt(4), 3) +}) +``` + +If the condition is unknown, you can provide it to the `skip` method as the first arguments: + +```ts +import { assert, test } from 'vitest' + +test('skipped test', (context) => { + context.skip(Math.random() < 0.5, 'optional message') + // Test skipped, no error + assert.equal(Math.sqrt(4), 3) +}) +``` + +## test.skipIf + +- **Alias:** `it.skipIf` + +In some cases you might run tests multiple times with different environments, and some of the tests might be environment-specific. Instead of wrapping the test code with `if`, you can use `test.skipIf` to skip the test whenever the condition is truthy. + +```ts +import { assert, test } from 'vitest' + +const isDev = process.env.NODE_ENV === 'development' + +test.skipIf(isDev)('prod only test', () => { + // this test only runs in production +}) +``` + +## test.runIf + +- **Alias:** `it.runIf` + +Opposite of [test.skipIf](#test-skipif). + +```ts +import { assert, test } from 'vitest' + +const isDev = process.env.NODE_ENV === 'development' + +test.runIf(isDev)('dev only test', () => { + // this test only runs in development +}) +``` + +## test.only + +- **Alias:** `it.only` + +Use `test.only` to only run certain tests in a given suite. This is useful when debugging. + +```ts +import { assert, test } from 'vitest' + +test.only('test', () => { + // Only this test (and others marked with only) are run + assert.equal(Math.sqrt(4), 2) +}) +``` + +Sometimes it is very useful to run `only` tests in a certain file, ignoring all other tests from the whole test suite, which pollute the output. + +In order to do that, run `vitest` with specific file containing the tests in question: + +```shell +vitest interesting.test.ts +``` + +::: warning +Vitest detects when tests are running in CI and will throw an error if any test has `only` flag. You can configure this behaviour via [`allowOnly`](/config/allowonly) option. +::: + +## test.concurrent + +- **Alias:** `it.concurrent` + +`test.concurrent` marks consecutive tests to be run in parallel. It receives the test name, an async function with the tests to collect, and an optional timeout (in milliseconds). + +```ts +import { describe, test } from 'vitest' + +// The two tests marked with concurrent will be run in parallel +describe('suite', () => { + test('serial test', async () => { /* ... */ }) + test.concurrent('concurrent test 1', async () => { /* ... */ }) + test.concurrent('concurrent test 2', async () => { /* ... */ }) +}) +``` + +`test.skip`, `test.only`, and `test.todo` works with concurrent tests. All the following combinations are valid: + +```ts +test.concurrent(/* ... */) +test.skip.concurrent(/* ... */) // or test.concurrent.skip(/* ... */) +test.only.concurrent(/* ... */) // or test.concurrent.only(/* ... */) +test.todo.concurrent(/* ... */) // or test.concurrent.todo(/* ... */) +``` + +When running concurrent tests, Snapshots and Assertions must use `expect` from the local [Test Context](/guide/test-context.md) to ensure the right test is detected. + +```ts +test.concurrent('test 1', async ({ expect }) => { + expect(foo).toMatchSnapshot() +}) +test.concurrent('test 2', async ({ expect }) => { + expect(foo).toMatchSnapshot() +}) +``` + +Note that if tests are synchronous, Vitest will still run them sequentially. + +## test.sequential + +- **Alias:** `it.sequential` + +`test.sequential` marks a test as sequential. This is useful if you want to run tests in sequence within `describe.concurrent` or with the `--sequence.concurrent` command option. + +```ts +import { describe, test } from 'vitest' + +// with config option { sequence: { concurrent: true } } +test('concurrent test 1', async () => { /* ... */ }) +test('concurrent test 2', async () => { /* ... */ }) + +test.sequential('sequential test 1', async () => { /* ... */ }) +test.sequential('sequential test 2', async () => { /* ... */ }) + +// within concurrent suite +describe.concurrent('suite', () => { + test('concurrent test 1', async () => { /* ... */ }) + test('concurrent test 2', async () => { /* ... */ }) + + test.sequential('sequential test 1', async () => { /* ... */ }) + test.sequential('sequential test 2', async () => { /* ... */ }) +}) +``` + +## test.todo + +- **Alias:** `it.todo` + +Use `test.todo` to stub tests to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement. + +```ts +// An entry will be shown in the report for this test +test.todo('unimplemented test', () => { + // failing implementation... +}) +``` + +::: tip +Vitest will automatically mark test as `todo` if test has no body. +::: + +## test.fails + +- **Alias:** `it.fails` + +Use `test.fails` to indicate that an assertion will fail explicitly. + +```ts +import { expect, test } from 'vitest' + +test.fails('repro #1234', () => { + expect(add(1, 2)).toBe(4) +}) +``` + +This flag is useful to track difference in behaviour of your library over time. For example, you can define a failing test without fixing the issue yet due to time constraints. Tests marked with `fails` are tracked in the test summary since Vitest 4.1. + +## test.each + +- **Alias:** `it.each` + +::: tip +While `test.each` is provided for Jest compatibility, +Vitest also has [`test.for`](#test-for) with an additional feature to integrate [`TestContext`](/guide/test-context). +::: + +Use `test.each` when you need to run the same test with different variables. +You can inject parameters with [printf formatting](https://nodejs.org/api/util.html#util_util_format_format_args) in the test name in the order of the test function parameters. + +- `%s`: string +- `%d`: number +- `%i`: integer +- `%f`: floating point value +- `%j`: json +- `%o`: object +- `%#`: 0-based index of the test case +- `%$`: 1-based index of the test case +- `%%`: single percent sign ('%') + +```ts +import { expect, test } from 'vitest' + +test.each([ + [1, 1, 2], + [1, 2, 3], + [2, 1, 3], +])('add(%i, %i) -> %i', (a, b, expected) => { + expect(a + b).toBe(expected) +}) + +// this will return +// ✓ add(1, 1) -> 2 +// ✓ add(1, 2) -> 3 +// ✓ add(2, 1) -> 3 +``` + +You can also access object properties and array elements with `$` prefix: + +```ts +test.each([ + { a: 1, b: 1, expected: 2 }, + { a: 1, b: 2, expected: 3 }, + { a: 2, b: 1, expected: 3 }, +])('add($a, $b) -> $expected', ({ a, b, expected }) => { + expect(a + b).toBe(expected) +}) + +// this will return +// ✓ add(1, 1) -> 2 +// ✓ add(1, 2) -> 3 +// ✓ add(2, 1) -> 3 + +test.each([ + [1, 1, 2], + [1, 2, 3], + [2, 1, 3], +])('add($0, $1) -> $2', (a, b, expected) => { + expect(a + b).toBe(expected) +}) + +// this will return +// ✓ add(1, 1) -> 2 +// ✓ add(1, 2) -> 3 +// ✓ add(2, 1) -> 3 +``` + +You can also access Object attributes with `.`, if you are using objects as arguments: + + ```ts + test.each` + a | b | expected + ${{ val: 1 }} | ${'b'} | ${'1b'} + ${{ val: 2 }} | ${'b'} | ${'2b'} + ${{ val: 3 }} | ${'b'} | ${'3b'} + `('add($a.val, $b) -> $expected', ({ a, b, expected }) => { + expect(a.val + b).toBe(expected) + }) + + // this will return + // ✓ add(1, b) -> 1b + // ✓ add(2, b) -> 2b + // ✓ add(3, b) -> 3b + ``` + +* First row should be column names, separated by `|`; +* One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. + +```ts +import { expect, test } from 'vitest' + +test.each` + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} +`('returns $expected when $a is added $b', ({ a, b, expected }) => { + expect(a + b).toBe(expected) +}) +``` + +::: tip +Vitest processes `$values` with Chai `format` method. If the value is too truncated, you can increase [chaiConfig.truncateThreshold](/config/#chaiconfig-truncatethreshold) in your config file. +::: + +## test.for + +- **Alias:** `it.for` + +Alternative to `test.each` to provide [`TestContext`](/guide/test-context). + +The difference from `test.each` lies in how arrays are provided in the arguments. +Non-array arguments to `test.for` (including template string usage) work exactly the same as for `test.each`. + +```ts +// `each` spreads arrays +test.each([ + [1, 1, 2], + [1, 2, 3], + [2, 1, 3], +])('add(%i, %i) -> %i', (a, b, expected) => { // [!code --] + expect(a + b).toBe(expected) +}) + +// `for` doesn't spread arrays (notice the square brackets around the arguments) +test.for([ + [1, 1, 2], + [1, 2, 3], + [2, 1, 3], +])('add(%i, %i) -> %i', ([a, b, expected]) => { // [!code ++] + expect(a + b).toBe(expected) +}) +``` + +The 2nd argument is [`TestContext`](/guide/test-context) and can be used for concurrent snapshots, for example: + +```ts +test.concurrent.for([ + [1, 1], + [1, 2], + [2, 1], +])('add(%i, %i)', ([a, b], { expect }) => { + expect(a + b).toMatchSnapshot() +}) +``` + +## test.describe 4.1.0 {#test-describe} + +Scoped `describe`. See [describe](/api/describe) for more information. + +## test.beforeEach + +Scoped `beforeEach` hook that inherits types from [`test.extend`](#test-extend). See [beforeEach](/api/hooks#beforeeach) for more information. + +## test.afterEach + +Scoped `afterEach` hook that inherits types from [`test.extend`](#test-extend). See [afterEach](/api/hooks#aftereach) for more information. + +## test.beforeAll + +Scoped `beforeAll` hook. See [beforeAll](/api/hooks#beforeall) for more information. + +## test.afterAll + +Scoped `afterAll` hook. See [afterAll](/api/hooks#afterall) for more information. + +## test.aroundEach 4.1.0 {#test-aroundeach} + +Scoped `aroundEach` hook that inherits types from [`test.extend`](#test-extend). See [aroundEach](/api/hooks#aroundeach) for more information. + +## test.aroundAll 4.1.0 {#test-aroundall} + +Scoped `aroundAll` hook. See [aroundAll](/api/hooks#aroundall) for more information. + +## bench {#bench} + +- **Type:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` + +::: danger +Benchmarking is experimental and does not follow SemVer. +::: + +`bench` defines a benchmark. In Vitest terms, benchmark is a function that defines a series of operations. Vitest runs this function multiple times to display different performance results. + +Vitest uses the [`tinybench`](https://github.com/tinylibs/tinybench) library under the hood, inheriting all its options that can be used as a third argument. + +```ts +import { bench } from 'vitest' + +bench('normal sorting', () => { + const x = [1, 5, 4, 2, 3] + x.sort((a, b) => { + return a - b + }) +}, { time: 1000 }) +``` + +```ts +export interface Options { + /** + * time needed for running a benchmark task (milliseconds) + * @default 500 + */ + time?: number + + /** + * number of times that a task should run if even the time option is finished + * @default 10 + */ + iterations?: number + + /** + * function to get the current timestamp in milliseconds + */ + now?: () => number + + /** + * An AbortSignal for aborting the benchmark + */ + signal?: AbortSignal + + /** + * Throw if a task fails (events will not work if true) + */ + throws?: boolean + + /** + * warmup time (milliseconds) + * @default 100ms + */ + warmupTime?: number + + /** + * warmup iterations + * @default 5 + */ + warmupIterations?: number + + /** + * setup function to run before each benchmark task (cycle) + */ + setup?: Hook + + /** + * teardown function to run after each benchmark task (cycle) + */ + teardown?: Hook +} +``` +After the test case is run, the output structure information is as follows: + +``` + name hz min max mean p75 p99 p995 p999 rme samples +· normal sorting 6,526,368.12 0.0001 0.3638 0.0002 0.0002 0.0002 0.0002 0.0004 ±1.41% 652638 +``` +```ts +export interface TaskResult { + /* + * the last error that was thrown while running the task + */ + error?: unknown + + /** + * The amount of time in milliseconds to run the benchmark task (cycle). + */ + totalTime: number + + /** + * the minimum value in the samples + */ + min: number + /** + * the maximum value in the samples + */ + max: number + + /** + * the number of operations per second + */ + hz: number + + /** + * how long each operation takes (ms) + */ + period: number + + /** + * task samples of each task iteration time (ms) + */ + samples: number[] + + /** + * samples mean/average (estimate of the population mean) + */ + mean: number + + /** + * samples variance (estimate of the population variance) + */ + variance: number + + /** + * samples standard deviation (estimate of the population standard deviation) + */ + sd: number + + /** + * standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean) + */ + sem: number + + /** + * degrees of freedom + */ + df: number + + /** + * critical value of the samples + */ + critical: number + + /** + * margin of error + */ + moe: number + + /** + * relative margin of error + */ + rme: number + + /** + * median absolute deviation + */ + mad: number + + /** + * p50/median percentile + */ + p50: number + + /** + * p75 percentile + */ + p75: number + + /** + * p99 percentile + */ + p99: number + + /** + * p995 percentile + */ + p995: number + + /** + * p999 percentile + */ + p999: number +} +``` + +### bench.skip + +- **Type:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` + +You can use `bench.skip` syntax to skip running certain benchmarks. + +```ts +import { bench } from 'vitest' + +bench.skip('normal sorting', () => { + const x = [1, 5, 4, 2, 3] + x.sort((a, b) => { + return a - b + }) +}) +``` + +### bench.only + +- **Type:** `(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void` + +Use `bench.only` to only run certain benchmarks in a given suite. This is useful when debugging. + +```ts +import { bench } from 'vitest' + +bench.only('normal sorting', () => { + const x = [1, 5, 4, 2, 3] + x.sort((a, b) => { + return a - b + }) +}) +``` + +### bench.todo + +- **Type:** `(name: string | Function) => void` + +Use `bench.todo` to stub benchmarks to be implemented later. + +```ts +import { bench } from 'vitest' + +bench.todo('unimplemented test') +``` diff --git a/api/vi.md b/api/vi.md index e72dcea0..cd63286f 100644 --- a/api/vi.md +++ b/api/vi.md @@ -628,7 +628,7 @@ it('calls console.log', () => { ::: ::: tip -You can call [`vi.restoreAllMocks`](#vi-restoreallmocks) inside [`afterEach`](/api/#aftereach) (or enable [`test.restoreMocks`](/config/#restoreMocks)) to restore all methods to their original implementations after every test. This will restore the original [object descriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty), so you won't be able to change method's implementation anymore, unless you spy again: +You can call [`vi.restoreAllMocks`](#vi-restoreallmocks) inside [`afterEach`](/api/hooks#aftereach) (or enable [`test.restoreMocks`](/config/#restoreMocks)) to restore all methods to their original implementations after every test. This will restore the original [object descriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty), so you won't be able to change method's implementation anymore, unless you spy again: ```ts const cart = { diff --git a/config/allowonly.md b/config/allowonly.md index e6071032..8b7fbaa3 100644 --- a/config/allowonly.md +++ b/config/allowonly.md @@ -9,7 +9,7 @@ outline: deep - **Default**: `!process.env.CI` - **CLI:** `--allowOnly`, `--allowOnly=false` -By default, Vitest does not permit tests marked with the [`only`](/api/#test-only) flag in Continuous Integration (CI) environments. Conversely, in local development environments, Vitest allows these tests to run. +By default, Vitest does not permit tests marked with the [`only`](/api/test#test-only) flag in Continuous Integration (CI) environments. Conversely, in local development environments, Vitest allows these tests to run. ::: info Vitest uses [`std-env`](https://www.npmjs.com/package/std-env) package to detect the environment. @@ -32,6 +32,6 @@ vitest --allowOnly ``` ::: -When enabled, Vitest will not fail the test suite if tests marked with [`only`](/api/#test-only) are detected, including in CI environments. +When enabled, Vitest will not fail the test suite if tests marked with [`only`](/api/test#test-only) are detected, including in CI environments. -When disabled, Vitest will fail the test suite if tests marked with [`only`](/api/#test-only) are detected, including in local development environments. +When disabled, Vitest will fail the test suite if tests marked with [`only`](/api/test#test-only) are detected, including in local development environments. diff --git a/config/browser.md b/config/browser.md index 5e3027bd..32f1b765 100644 --- a/config/browser.md +++ b/config/browser.md @@ -472,8 +472,8 @@ receives an object with the following properties: - `testName: string` - The [`test`](/api/#test)'s name, including parent - [`describe`](/api/#describe), sanitized. + The [`test`](/api/test)'s name, including parent + [`describe`](/api/describe), sanitized. - `attachmentsDir: string` diff --git a/config/browser/expect.md b/config/browser/expect.md index 501fb0c1..3990ed68 100644 --- a/config/browser/expect.md +++ b/config/browser/expect.md @@ -110,8 +110,8 @@ receives an object with the following properties: - `testName: string` - The [`test`](/api/#test)'s name, including parent - [`describe`](/api/#describe), sanitized. + The [`test`](/api/test)'s name, including parent + [`describe`](/api/describe), sanitized. - `attachmentsDir: string` diff --git a/config/fileparallelism.md b/config/fileparallelism.md index 20f1143f..09daf095 100644 --- a/config/fileparallelism.md +++ b/config/fileparallelism.md @@ -12,5 +12,5 @@ outline: deep Should all test files run in parallel. Setting this to `false` will override `maxWorkers` option to `1`. ::: tip -This option doesn't affect tests running in the same file. If you want to run those in parallel, use `concurrent` option on [describe](/api/#describe-concurrent) or via [a config](#sequence-concurrent). +This option doesn't affect tests running in the same file. If you want to run those in parallel, use `concurrent` option on [describe](/api/describe#describe-concurrent) or via [a config](#sequence-concurrent). ::: diff --git a/config/sequence.md b/config/sequence.md index c37fecb4..f3b05eab 100644 --- a/config/sequence.md +++ b/config/sequence.md @@ -148,7 +148,7 @@ Changes the order in which hooks are executed. - `parallel` will run hooks in a single group in parallel (hooks in parent suites will still run before the current suite's hooks) ::: tip -This option doesn't affect [`onTestFinished`](/api/#ontestfinished). It is always called in reverse order. +This option doesn't affect [`onTestFinished`](/api/hooks#ontestfinished). It is always called in reverse order. ::: ## sequence.setupFiles {#sequence-setupfiles} diff --git a/config/tags.md b/config/tags.md index 6ef0a77c..c3ea0260 100644 --- a/config/tags.md +++ b/config/tags.md @@ -99,61 +99,13 @@ When a test has both tags, the `timeout` will be `30_000` because `flaky` has a ## Test Options -Tags can define test options that will be applied to every test marked with the tag. These options are merged with the test's own options, with the test's options taking precedence. +Tags can define [test options](/api/test#test-options) that will be applied to every test marked with the tag. These options are merged with the test's own options, with the test's options taking precedence. -### timeout +::: warning +The [`retry.condition`](/api/test#retry) can onle be a regexp because the config values need to be serialised. -- **Type:** `number` - -Test timeout in milliseconds. - -### retry - -- **Type:** `number | { count?: number, delay?: number, condition?: RegExp }` - -Retry configuration for the test. If a number, specifies how many times to retry. If an object, allows fine-grained retry control. - -### repeats - -- **Type:** `number` - -How many times the test will run again. - -### concurrent - -- **Type:** `boolean` - -Whether suites and tests run concurrently. - -### sequential - -- **Type:** `boolean` - -Whether tests run sequentially. - -### skip - -- **Type:** `boolean` - -Whether the test should be skipped. - -### only - -- **Type:** `boolean` - -Should this test be the only one running in a suite. - -### todo - -- **Type:** `boolean` - -Whether the test should be skipped and marked as a todo. - -### fails - -- **Type:** `boolean` - -Whether the test is expected to fail. If it does, the test will pass, otherwise it will fail. +Tags also cannot apply other [tags](/api/test#tags) via these options. +::: ## Example diff --git a/guide/browser/trace-view.md b/guide/browser/trace-view.md index 52882d6b..20ff5568 100644 --- a/guide/browser/trace-view.md +++ b/guide/browser/trace-view.md @@ -25,7 +25,7 @@ vitest --browser.trace=on ``` ::: -By default, Vitest will generate a trace file for each test. You can also configure it to only generate traces on test failures by setting `trace` to `'on-first-retry'`, `'on-all-retries'` or `'retain-on-failure'`. The files will be saved in `__traces__` folder next to your test files. The name of the trace includes the project name, the test name, the [`repeats` count and `retry` count](/api/#test-api-reference): +By default, Vitest will generate a trace file for each test. You can also configure it to only generate traces on test failures by setting `trace` to `'on-first-retry'`, `'on-all-retries'` or `'retain-on-failure'`. The files will be saved in `__traces__` folder next to your test files. The name of the trace includes the project name, the test name, the [`repeats`](/api/test#repeats) count and [`retry`](/api/test#retry) count: ``` chromium-my-test-0-0.trace.zip diff --git a/guide/cli-generated.md b/guide/cli-generated.md index b1f59e61..7139715d 100644 --- a/guide/cli-generated.md +++ b/guide/cli-generated.md @@ -844,3 +844,17 @@ Enable caching of modules on the file system between reruns. - **Config:** [experimental.printImportBreakdown](/config/experimental#experimental-printimportbreakdown) Print import breakdown after the summary. If the reporter doesn't support summary, this will have no effect. Note that UI's "Module Graph" tab always has an import breakdown. + +### experimental.viteModuleRunner + +- **CLI:** `--experimental.viteModuleRunner` +- **Config:** [experimental.viteModuleRunner](/config/experimental#experimental-vitemodulerunner) + +Control whether Vitest uses Vite's module runner to run the code or fallback to the native `import`. (default: `true`) + +### experimental.nodeLoader + +- **CLI:** `--experimental.nodeLoader` +- **Config:** [experimental.nodeLoader](/config/experimental#experimental-nodeloader) + +Controls whether Vitest will use Node.js Loader API to process in-source or mocked files. This has no effect if `viteModuleRunner` is enabled. Disabling this can increase performance. (default: `true`) diff --git a/guide/features.md b/guide/features.md index c75f1d40..a0dd58de 100644 --- a/guide/features.md +++ b/guide/features.md @@ -77,7 +77,7 @@ describe.concurrent('suite', () => { }) ``` -You can also use `.skip`, `.only`, and `.todo` with concurrent suites and tests. Read more in the [API Reference](/api/#test-concurrent). +You can also use `.skip`, `.only`, and `.todo` with concurrent suites and tests. Read more in the [API Reference](/api/test#test-concurrent). ::: warning When running concurrent tests, Snapshots and Assertions must use `expect` from the local [Test Context](/guide/test-context) to ensure the right test is detected. @@ -192,7 +192,7 @@ Learn more at [In-source testing](/guide/in-source). ## Benchmarking Experimental {#benchmarking} -You can run benchmark tests with [`bench`](/api/#bench) function via [Tinybench](https://github.com/tinylibs/tinybench) to compare performance results. +You can run benchmark tests with [`bench`](/api/test#bench) function via [Tinybench](https://github.com/tinylibs/tinybench) to compare performance results. ```ts [sort.bench.ts] import { bench, describe } from 'vitest' diff --git a/guide/index.md b/guide/index.md index bfa753a0..6d81e9b0 100644 --- a/guide/index.md +++ b/guide/index.md @@ -92,7 +92,7 @@ Test Files 1 passed (1) If you are using Bun as your package manager, make sure to use `bun run test` command instead of `bun test`, otherwise Bun will run its own test runner. ::: -Learn more about the usage of Vitest, see the [API](/api/) section. +Learn more about the usage of Vitest, see the [API](/api/test) section. ## Configuring Vitest diff --git a/guide/lifecycle.md b/guide/lifecycle.md index 90fc6563..88c8dbdd 100644 --- a/guide/lifecycle.md +++ b/guide/lifecycle.md @@ -131,8 +131,8 @@ The execution follows this order: - `beforeEach` hooks execute (in order defined, or based on [`sequence.hooks`](/config/sequence#sequence-hooks)) - Test function executes - `afterEach` hooks execute (reverse order by default with `sequence.hooks: 'stack'`) - - [`onTestFinished`](/api/#ontestfinished) callbacks run (always in reverse order) - - If test failed: [`onTestFailed`](/api/#ontestfailed) callbacks run + - [`onTestFinished`](/api/hooks#ontestfinished) callbacks run (always in reverse order) + - If test failed: [`onTestFailed`](/api/hooks#ontestfailed) callbacks run - Note: if `repeats` or `retry` are set, all of these steps are executed again 5. **`afterAll` hooks** - Run once after all tests in the suite complete @@ -317,4 +317,4 @@ For tips on how to improve performance, read the [Improving Performance](/guide/ - [Isolation Configuration](/config/isolate) - [Pool Configuration](/config/pool) - [Extending Reporters](/guide/advanced/reporters) - for reporter lifecycle events -- [Test API Reference](/api/) - for hook APIs and test functions +- [Test API Reference](/api/hooks) - for hook APIs diff --git a/guide/migration.md b/guide/migration.md index 488b32fb..d0c40059 100644 --- a/guide/migration.md +++ b/guide/migration.md @@ -319,7 +319,7 @@ New pool architecture allows Vitest to simplify many previously complex configur - `maxThreads` and `maxForks` are now `maxWorkers`. - Environment variables `VITEST_MAX_THREADS` and `VITEST_MAX_FORKS` are now `VITEST_MAX_WORKERS`. -- `singleThread` and `singleFork` are now `maxWorkers: 1, isolate: false`. If your tests were relying on module reset between tests, you'll need to add [setupFile](/config/setupfiles) that calls [`vi.resetModules()`](/api/vi.html#vi-resetmodules) in [`beforeAll` test hook](/api/#beforeall). +- `singleThread` and `singleFork` are now `maxWorkers: 1, isolate: false`. If your tests were relying on module reset between tests, you'll need to add [setupFile](/config/setupfiles) that calls [`vi.resetModules()`](/api/vi.html#vi-resetmodules) in [`beforeAll` test hook](/api/hooks#beforeall). - `poolOptions` is removed. All previous `poolOptions` are now top-level options. The `memoryLimit` of VM pools is renamed to `vmMemoryLimit`. - `threads.useAtomics` is removed. If you have a use case for this, feel free to open a new feature request. - Custom pool interface has been rewritten, see [Custom Pool](/guide/advanced/pool#custom-pool) @@ -573,7 +573,7 @@ Just like Jest, Vitest sets `NODE_ENV` to `test`, if it wasn't set before. Vites ### Replace property -If you want to modify the object, you will use [replaceProperty API](https://jestjs.io/docs/jest-object#jestreplacepropertyobject-propertykey-value) in Jest, you can use [`vi.stubEnv`](/api/#vi-stubenv) or [`vi.spyOn`](/api/vi#vi-spyon) to do the same also in Vitest. +If you want to modify the object, you will use [replaceProperty API](https://jestjs.io/docs/jest-object#jestreplacepropertyobject-propertykey-value) in Jest, you can use [`vi.stubEnv`](/api/vi#vi-stubenv) or [`vi.spyOn`](/api/vi#vi-spyon) to do the same also in Vitest. ### Done Callback @@ -583,7 +583,7 @@ Vitest does not support the callback style of declaring tests. You can rewrite t ### Hooks -`beforeAll`/`beforeEach` hooks may return [teardown function](/api/#setup-and-teardown) in Vitest. Because of that you may need to rewrite your hooks declarations, if they return something other than `undefined` or `null`: +`beforeAll`/`beforeEach` hooks may return [teardown function](/api/hooks#beforeach) in Vitest. Because of that you may need to rewrite your hooks declarations, if they return something other than `undefined` or `null`: ```ts beforeEach(() => setActivePinia(createTestingPinia())) // [!code --] diff --git a/guide/parallelism.md b/guide/parallelism.md index 20b47b0e..ce866bf6 100644 --- a/guide/parallelism.md +++ b/guide/parallelism.md @@ -20,7 +20,7 @@ If you have a lot of tests, it is usually faster to run them in parallel, but it Unlike _test files_, Vitest runs _tests_ in sequence. This means that tests inside a single test file will run in the order they are defined. -Vitest supports the [`concurrent`](/api/#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests depends on the [`maxConcurrency`](/config/#maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). +Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests depends on the [`maxConcurrency`](/config/#maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). Vitest doesn't perform any smart analysis and doesn't create additional workers to run these tests. This means that the performance of your tests will improve only if you rely heavily on asynchronous operations. For example, these tests will still run one after another even though the `concurrent` option is specified. This is because they are synchronous: diff --git a/guide/test-context.md b/guide/test-context.md index ad125dae..d19da1fe 100644 --- a/guide/test-context.md +++ b/guide/test-context.md @@ -119,11 +119,11 @@ it('stop request when test times out', async ({ signal }) => { #### `onTestFailed` -The [`onTestFailed`](/api/#ontestfailed) hook bound to the current test. This API is useful if you are running tests concurrently and need to have a special handling only for this specific test. +The [`onTestFailed`](/api/hooks#ontestfailed) hook bound to the current test. This API is useful if you are running tests concurrently and need to have a special handling only for this specific test. #### `onTestFinished` -The [`onTestFinished`](/api/#ontestfailed) hook bound to the current test. This API is useful if you are running tests concurrently and need to have a special handling only for this specific test. +The [`onTestFinished`](/api/hooks#ontestfailed) hook bound to the current test. This API is useful if you are running tests concurrently and need to have a special handling only for this specific test. ## Extend Test Context From bd33ef125c480598f84a12804342385cf0be513d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Sat, 24 Jan 2026 20:37:39 +0900 Subject: [PATCH 2/3] feat(experimental): add `importDurations: { limit, print }` options (#9401) Co-authored-by: Claude Opus 4.5 Co-authored-by: Vladimir --- config/experimental.md | 38 ++++++++++++++++++++++++++++++++++---- guide/cli-generated.md | 15 +++++++++++---- guide/ui.md | 4 ++-- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/config/experimental.md b/config/experimental.md index ca977d9c..13a01957 100644 --- a/config/experimental.md +++ b/config/experimental.md @@ -176,16 +176,32 @@ export default defineConfig({ It's important that Node can process `sdkPath` content because it is not transformed by Vitest. See [the guide](/guide/open-telemetry) on how to work with OpenTelemetry inside of Vitest. ::: -## experimental.printImportBreakdown 4.0.15 {#experimental-printimportbreakdown} +## experimental.importDurations 4.1.0 {#experimental-importdurations} ::: tip FEEDBACK Please leave feedback regarding this feature in a [GitHub Discussion](https://github.com/vitest-dev/vitest/discussions/9224). ::: -- **Type:** `boolean` -- **Default:** `false` +- **Type:** + +```ts +interface ImportDurationsOptions { + /** + * Print import breakdown to CLI terminal after tests finish. + */ + print?: boolean + /** + * Maximum number of imports to collect and display. + */ + limit?: number +} +``` -Show import duration breakdown after tests have finished running. This option only works with [`default`](/guide/reporters#default), [`verbose`](/guide/reporters#verbose), or [`tree`](/guide/reporters#tree) reporters. +- **Default:** `{ print: false, limit: 0 }` (`limit` is 10 if `print` or UI is enabled) + +Configure import duration collection and display. + +The `print` option controls CLI terminal output. The `limit` option controls how many imports to collect and display. [Vitest UI](/guide/ui#import-breakdown) can always toggle the breakdown display regardless of the `print` setting. - Self: the time it took to import the module, excluding static imports; - Total: the time it took to import the module, including static imports. Note that this does not include `transform` time of the current module. @@ -194,6 +210,20 @@ Show import duration breakdown after tests have finished running. This option on Note that if the file path is too long, Vitest will truncate it at the start until it fits 45 character limit. +### experimental.importDurations.print {#experimental-importdurationsprint} + +- **Type:** `boolean` +- **Default:** `false` + +Print import breakdown to CLI terminal after tests finish. This only works with [`default`](/guide/reporters#default), [`verbose`](/guide/reporters#verbose), or [`tree`](/guide/reporters#tree) reporters. + +### experimental.importDurations.limit {#experimental-importdurationslimit} + +- **Type:** `number` +- **Default:** `0` (or `10` if `print` or UI is enabled) + +Maximum number of imports to collect and display in CLI output, [Vitest UI](/guide/ui#import-breakdown), and third-party reporters. + ::: info [Vitest UI](/guide/ui#import-breakdown) shows a breakdown of imports automatically if at least one file took longer than 500 milliseconds to load. You can manually set this option to `false` to disable this. ::: diff --git a/guide/cli-generated.md b/guide/cli-generated.md index 7139715d..b1a61ddf 100644 --- a/guide/cli-generated.md +++ b/guide/cli-generated.md @@ -838,12 +838,19 @@ Should Vitest throw an error if test has a tag that is not defined in the config Enable caching of modules on the file system between reruns. -### experimental.printImportBreakdown +### experimental.importDurations.print -- **CLI:** `--experimental.printImportBreakdown` -- **Config:** [experimental.printImportBreakdown](/config/experimental#experimental-printimportbreakdown) +- **CLI:** `--experimental.importDurations.print` +- **Config:** [experimental.importDurations.print](/config/experimental#experimental-importdurations-print) -Print import breakdown after the summary. If the reporter doesn't support summary, this will have no effect. Note that UI's "Module Graph" tab always has an import breakdown. +Print import breakdown to CLI terminal after tests finish (default: false). + +### experimental.importDurations.limit + +- **CLI:** `--experimental.importDurations.limit ` +- **Config:** [experimental.importDurations.limit](/config/experimental#experimental-importdurations-limit) + +Maximum number of imports to collect and display (default: 0, or 10 if print or UI is enabled). ### experimental.viteModuleRunner diff --git a/guide/ui.md b/guide/ui.md index 45803e21..9840f411 100644 --- a/guide/ui.md +++ b/guide/ui.md @@ -133,7 +133,7 @@ If you are developing a custom integration on top of Vitest, you can use [`vites Please, leave feedback regarding this feature in a [GitHub Discussion](https://github.com/vitest-dev/vitest/discussions/9224). ::: -The Module Graph tab also provides an Import Breakdown with a list of modules that take the longest time to load (top 10 by default, but you can press "Show more" to load 10 more), sorted by Total Time. +The Module Graph tab also provides an Import Breakdown with a list of modules that take the longest time to load (top 10 by default), sorted by Total Time. Import breakdown with a list of top 10 modules that take the longest time to load Import breakdown with a list of top 10 modules that take the longest time to load @@ -144,4 +144,4 @@ The breakdown shows a list of modules with self time, total time, and a percenta The "Show Import Breakdown" icon will have a red color if there is at least one file that took longer than 500 milliseconds to load, and it will be orange if there is at least one file that took longer than 100 milliseconds. -By default, Vitest shows the breakdown automatically if there is at least one module that took longer than 500 milliseconds to load. You can control the behaviour by setting the [`experimental.printImportBreakdown`](/config/experimental#experimental-printimportbreakdown) option. +You can use [`experimental.importDurations.limit`](/config/experimental#experimental-importdurationslimit) to control the number of imports displayed. From 9e70de36174a06783104e02f7439a12729f4348e Mon Sep 17 00:00:00 2001 From: noise Date: Sun, 25 Jan 2026 20:35:12 +0800 Subject: [PATCH 3/3] docs(cn): dissolve the conflict --- .vitepress/config.ts | 19 +------------------ api/describe.md | 2 ++ api/expect.md | 12 ++---------- api/hooks.md | 1 + api/index.md | 1 + api/test.md | 2 ++ api/vi.md | 6 +----- config/allowonly.md | 2 ++ config/browser.md | 7 +------ config/experimental.md | 9 ++------- config/fileparallelism.md | 6 +----- guide/browser/trace-view.md | 6 +----- guide/features.md | 12 ++---------- guide/index.md | 6 +----- guide/migration.md | 12 ++---------- guide/parallelism.md | 6 +----- guide/test-context.md | 12 ++---------- guide/ui.md | 12 ++++-------- 18 files changed, 29 insertions(+), 104 deletions(-) diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 15d78245..5814a73e 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -156,15 +156,9 @@ export default ({ mode }: { mode: string }) => { { title: 'Vitest', items: [ -<<<<<<< HEAD - { text: '指南', link: '/guide/' }, - { text: 'API', link: '/api/' }, - { text: '配置', link: '/config/' }, -======= { text: 'Guides', link: '/guide/' }, { text: 'API', link: '/api/test' }, { text: 'Config', link: '/config/' }, ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d ], }, { @@ -203,15 +197,9 @@ export default ({ mode }: { mode: string }) => { }, nav: [ -<<<<<<< HEAD { text: '指南', link: '/guide/', activeMatch: '^/guide/' }, - { text: 'API', link: '/api/', activeMatch: '^/api/' }, - { text: '配置', link: '/config/', activeMatch: '^/config/' }, -======= - { text: 'Guides', link: '/guide/', activeMatch: '^/guide/' }, { text: 'API', link: '/api/test', activeMatch: '^/api/' }, - { text: 'Config', link: '/config/', activeMatch: '^/config/' }, ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d + { text: '配置', link: '/config/', activeMatch: '^/config/' }, { text: '博客', link: '/blog', @@ -975,11 +963,7 @@ export default ({ mode }: { mode: string }) => { ], '/api': [ { -<<<<<<< HEAD text: 'Test API', - link: '/api/', -======= - text: 'Test API Reference', items: [ { text: 'Test', @@ -994,7 +978,6 @@ export default ({ mode }: { mode: string }) => { link: '/api/hooks', }, ], ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d }, { text: 'Mocks', diff --git a/api/describe.md b/api/describe.md index 0eca4f42..1d6cb48b 100644 --- a/api/describe.md +++ b/api/describe.md @@ -2,6 +2,8 @@ outline: deep --- + + # describe - **Alias:** `suite` diff --git a/api/expect.md b/api/expect.md index 5ba42534..572b2684 100644 --- a/api/expect.md +++ b/api/expect.md @@ -33,11 +33,7 @@ expect(input).to.equal(2) // chai API expect(input).toBe(2) // jest API ``` -<<<<<<< HEAD -从技术上讲,这个示例没有使用 [`test`](/api/#test) 函数,因此在控制台中你将看到 Nodejs 错误而不是 Vitest 输出。 要了解更多关于 `test` 的信息,请阅读[Test API](/api/)。 -======= -Technically this example doesn't use [`test`](/api/test) function, so in the console you will see Node.js error instead of Vitest output. To learn more about `test`, please read [Test API Reference](/api/test). ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +从技术上讲,这个示例没有使用 [`test`](/api/#test) 函数,因此在控制台中你将看到 Nodejs 错误而不是 Vitest 输出。 要了解更多关于 `test` 的信息,请参阅 [Test API](/api/test)。 此外,`expect` 可以静态地使用来访问匹配器函数,稍后将会介绍。 @@ -104,11 +100,7 @@ test('expect.soft test', () => { ``` ::: warning -<<<<<<< HEAD -`expect.soft` 只能在 [`test`](/api/#test) 函数的内部使用。 -======= -`expect.soft` can only be used inside the [`test`](/api/test) function. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +`expect.soft` 只能在 [`test`](/api/test) 函数的内部使用。 ::: ## poll diff --git a/api/hooks.md b/api/hooks.md index 94ba3c1c..2cedf2f0 100644 --- a/api/hooks.md +++ b/api/hooks.md @@ -1,6 +1,7 @@ --- outline: deep --- + # Hooks diff --git a/api/index.md b/api/index.md index 72310ef9..f0a6419f 100644 --- a/api/index.md +++ b/api/index.md @@ -1,6 +1,7 @@ --- outline: deep --- + # Test API 索引 {#test-api-reference} diff --git a/api/test.md b/api/test.md index c44e890f..b0fc69ff 100644 --- a/api/test.md +++ b/api/test.md @@ -2,6 +2,8 @@ outline: deep --- + + # Test - **Alias:** `it` diff --git a/api/vi.md b/api/vi.md index e744738c..e642776d 100644 --- a/api/vi.md +++ b/api/vi.md @@ -632,11 +632,7 @@ it('calls console.log', () => { ::: ::: tip -<<<<<<< HEAD -在每个测试后,于 [`afterEach`](/api/#aftereach) 中调用 [`vi.restoreAllMocks`](#vi-restoreallmocks) 或开启配置项 [`test.restoreMocks`](/config/#restoreMocks),即可将所有方法还原为原始实现。此操作会恢复其 [对象描述符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty),除非重新对其进行 spy ,否则无法再次修改方法实现。 -======= -You can call [`vi.restoreAllMocks`](#vi-restoreallmocks) inside [`afterEach`](/api/hooks#aftereach) (or enable [`test.restoreMocks`](/config/#restoreMocks)) to restore all methods to their original implementations after every test. This will restore the original [object descriptor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty), so you won't be able to change method's implementation anymore, unless you spy again: ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +在每个测试后,于 [`afterEach`](/api/hooks#aftereach) 中调用 [`vi.restoreAllMocks`](#vi-restoreallmocks) 或开启配置项 [`test.restoreMocks`](/config/#restoreMocks),即可将所有方法还原为原始实现。此操作会恢复其 [对象描述符](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty),除非重新对其进行 spy ,否则无法再次修改方法实现。 ```ts const cart = { diff --git a/config/allowonly.md b/config/allowonly.md index 8b7fbaa3..2716ced2 100644 --- a/config/allowonly.md +++ b/config/allowonly.md @@ -3,6 +3,8 @@ title: allowOnly | Config outline: deep --- + + # allowOnly - **Type**: `boolean` diff --git a/config/browser.md b/config/browser.md index 41da8c24..c22bbc7d 100644 --- a/config/browser.md +++ b/config/browser.md @@ -460,12 +460,7 @@ export default defineConfig({ - `testName: string` -<<<<<<< HEAD - [`test`](/api/#test) 的名称,包括父级 [`describe`](/api/#describe) ,已清理。 -======= - The [`test`](/api/test)'s name, including parent - [`describe`](/api/describe), sanitized. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d + [`test`](/api/test) 的名称,包括父级 [`describe`](/api/describe) ,已清理。 - `attachmentsDir: string` diff --git a/config/experimental.md b/config/experimental.md index 1297e16f..eeb56039 100644 --- a/config/experimental.md +++ b/config/experimental.md @@ -177,16 +177,12 @@ export default defineConfig({ ## experimental.importDurations 4.1.0 {#experimental-importdurations} + + ::: tip 功能反馈 请将关于此功能反馈提交至 [GitHub Discussion](https://github.com/vitest-dev/vitest/discussions/9224)。 ::: -<<<<<<< HEAD -- **类型:** `boolean` -- **默认值:** `false` - -在测试运行完成后显示导入耗时明细。此选项仅适用于 [`default`](/guide/reporters#default)、[`verbose`](/guide/reporters#verbose) 或 [`tree`](/guide/reporters#tree) 报告器。 -======= - **Type:** ```ts @@ -207,7 +203,6 @@ interface ImportDurationsOptions { Configure import duration collection and display. The `print` option controls CLI terminal output. The `limit` option controls how many imports to collect and display. [Vitest UI](/guide/ui#import-breakdown) can always toggle the breakdown display regardless of the `print` setting. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d - Self:模块导入耗时,不包括静态导入; - Total:模块导入耗时,包括静态导入。请注意,这不包括当前模块的 `transform` 时间。 diff --git a/config/fileparallelism.md b/config/fileparallelism.md index 017e0992..c39fcc93 100644 --- a/config/fileparallelism.md +++ b/config/fileparallelism.md @@ -12,9 +12,5 @@ outline: deep 所有测试文件应该并行运行。 将其设置为 `false` 将覆盖 `maxWorkers` 选项为 `1`。 ::: tip -<<<<<<< HEAD -此选项不会影响在同一文件中运行的测试。如果你想并行运行这些程序,使用 `concurrent` 选项 [describe](/api/#describe-concurrent) 或通过 [配置](#sequence-concurrent)。 -======= -This option doesn't affect tests running in the same file. If you want to run those in parallel, use `concurrent` option on [describe](/api/describe#describe-concurrent) or via [a config](#sequence-concurrent). ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +此选项不会影响在同一文件中运行的测试。如果你想并行运行这些程序,使用 `concurrent` 选项 [describe](/api/describe#describe-concurrent) 或通过 [配置](#sequence-concurrent)。 ::: diff --git a/guide/browser/trace-view.md b/guide/browser/trace-view.md index 84001d8a..5f6ce258 100644 --- a/guide/browser/trace-view.md +++ b/guide/browser/trace-view.md @@ -25,11 +25,7 @@ vitest --browser.trace=on ``` ::: -<<<<<<< HEAD -默认情况下,Vitest 会为每个测试生成一个追踪文件。你也可以通过设置 `trace` 为 `'on-first-retry'`、`'on-all-retries'` 或 `'retain-on-failure'` 来配置仅在测试失败时生成追踪。这些文件将保存在测试文件相邻的 `__traces__` 文件夹中。追踪文件的名称包括项目名称、测试名称、[`repeats` 次数和 `retry` 次数](/api/#test-api-reference): -======= -By default, Vitest will generate a trace file for each test. You can also configure it to only generate traces on test failures by setting `trace` to `'on-first-retry'`, `'on-all-retries'` or `'retain-on-failure'`. The files will be saved in `__traces__` folder next to your test files. The name of the trace includes the project name, the test name, the [`repeats`](/api/test#repeats) count and [`retry`](/api/test#retry) count: ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +默认情况下,Vitest 会为每个测试生成一个追踪文件。你也可以通过设置 `trace` 为 `'on-first-retry'`、`'on-all-retries'` 或 `'retain-on-failure'` 来配置仅在测试失败时生成追踪。这些文件将保存在测试文件相邻的 `__traces__` 文件夹中。追踪文件的名称包括项目名称、测试名称、the [`repeats`](/api/test#repeats) 次数和 [`retry`](/api/test#retry) 次数: ``` chromium-my-test-0-0.trace.zip diff --git a/guide/features.md b/guide/features.md index dc0102d6..272039bb 100644 --- a/guide/features.md +++ b/guide/features.md @@ -89,13 +89,9 @@ describe.concurrent('suite', () => { }) ``` -<<<<<<< HEAD 你还可以将 `.skip`、`.only` 和 `.todo` 用于并发测试套件和测试用例。 -了解更多信息 [API 索引](../api/#concurrent) -======= -You can also use `.skip`, `.only`, and `.todo` with concurrent suites and tests. Read more in the [API Reference](/api/test#test-concurrent). ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +了解更多信息 [API 索引](/api/test#test-concurrent) ::: warning 在异步并发测试中使用快照时,由于 JavaScript 的限制,你需要使用 [测试环境](/guide/test-context) 中的 `expect` 来确保检测到正确的测试。 @@ -221,11 +217,7 @@ if (import.meta.vitest) { ## 基准测试 实验性 {#benchmarking} -<<<<<<< HEAD -你可以使用 [`bench`](/api/#bench) 运行基准测试通过 [Tinybench](https://github.com/tinylibs/tinybench) 函数来比较基准测试结果。 -======= -You can run benchmark tests with [`bench`](/api/test#bench) function via [Tinybench](https://github.com/tinylibs/tinybench) to compare performance results. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +你可以使用 [`bench`](/api/test#bench) 运行基准测试通过 [Tinybench](https://github.com/tinylibs/tinybench) 函数来比较基准测试结果。 ```ts [sort.bench.ts] import { bench, describe } from 'vitest' diff --git a/guide/index.md b/guide/index.md index 080d9ce7..4aea1d51 100644 --- a/guide/index.md +++ b/guide/index.md @@ -95,11 +95,7 @@ Test Files 1 passed (1) 如果使用 Bun 作为软件包管理器,请确保使用 `bun run test` 命令而不是 `bun test` 命令,否则 Bun 将运行自己的测试运行程序。 ::: -<<<<<<< HEAD -了解更多关于 Vitest 的使用,请参考 [API 索引](/api/) 部分。 -======= -Learn more about the usage of Vitest, see the [API](/api/test) section. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +了解更多关于 Vitest 的使用,请参考 [API 索引](/api/test) 部分。 ## 配置 Vitest {#configuring-vitest} diff --git a/guide/migration.md b/guide/migration.md index 308fd665..6745a942 100644 --- a/guide/migration.md +++ b/guide/migration.md @@ -568,11 +568,7 @@ Just like Jest, Vitest sets `NODE_ENV` to `test`, if it wasn't set before. Vites ### 替换属性 {#replace-property} -<<<<<<< HEAD -如果想修改对象,Jest 使用 [replaceProperty API](https://jestjs.io/docs/jest-object#jestreplacepropertyobject-propertykey-value),Vitest 可使用 [`vi.stubEnv`](/api/#vi-stubenv) 或 [`vi.spyOn`](/api/vi#vi-spyon) 达成相同效果。 -======= -If you want to modify the object, you will use [replaceProperty API](https://jestjs.io/docs/jest-object#jestreplacepropertyobject-propertykey-value) in Jest, you can use [`vi.stubEnv`](/api/vi#vi-stubenv) or [`vi.spyOn`](/api/vi#vi-spyon) to do the same also in Vitest. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +如果想修改对象,Jest 使用 [replaceProperty API](https://jestjs.io/docs/jest-object#jestreplacepropertyobject-propertykey-value),Vitest 可使用 [`vi.stubEnv`](/api/vi#vi-stubenv) 或 [`vi.spyOn`](/api/vi#vi-spyon) 达成相同效果。 ### Done 回调 {#done-callback} @@ -582,11 +578,7 @@ Vitest 不支持回调式测试声明。你可以改写为使用 `async`/`await` ### Hooks {#hooks} -<<<<<<< HEAD -Vitest 中 `beforeAll`/`beforeEach` 钩子可返回 [清理函数](/api/#setup-and-teardown)。因此,如果钩子返回非 `undefined` 或 `null`,可能需改写: -======= -`beforeAll`/`beforeEach` hooks may return [teardown function](/api/hooks#beforeach) in Vitest. Because of that you may need to rewrite your hooks declarations, if they return something other than `undefined` or `null`: ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +Vitest 中 `beforeAll`/`beforeEach` 钩子可返回 [清理函数](/api/hooks#setup-and-teardown)。因此,如果钩子返回非 `undefined` 或 `null`,可能需改写: ```ts beforeEach(() => setActivePinia(createTestingPinia())) // [!code --] diff --git a/guide/parallelism.md b/guide/parallelism.md index 65d7039f..c4216b9d 100644 --- a/guide/parallelism.md +++ b/guide/parallelism.md @@ -20,11 +20,7 @@ outline: deep 与 _测试文件_ 不同, Vitest 在同一个文件中会顺序执行 _测试用例_ 。也就是说,同一个文件里的测试会按定义顺序一个接一个地执行。 -<<<<<<< HEAD -如果希望让同一文件中的多个测试并行执行,可以使用 [`concurrent`](/api/#test-concurrent) 选项。启用后, Vitest 会将同一文件中的并发测试分组,并基于 maxConcurrency 控制并行度,然后通过 [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) 一起执行。 -======= -Vitest supports the [`concurrent`](/api/test#test-concurrent) option to run tests together. If this option is set, Vitest will group concurrent tests in the same _file_ (the number of simultaneously running tests depends on the [`maxConcurrency`](/config/#maxconcurrency) option) and run them with [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all). ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +如果希望让同一文件中的多个测试并行执行,可以使用 [`concurrent`](/api/test#test-concurrent) 选项。启用后, Vitest 会将同一文件中的并发测试分组,并基于 maxConcurrency 控制并行度,然后通过 [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) 一起执行。 Vitest 不会自动分析你的测试是否可以并行,也不会为了并发而额外创建工作者。这意味着,只有在测试中有大量异步操作时,使用并发才能提升性能。例如,以下示例即便指定了 concurrent ,也会顺序执行,因为它们是同步的: diff --git a/guide/test-context.md b/guide/test-context.md index dba4021b..49f32b40 100644 --- a/guide/test-context.md +++ b/guide/test-context.md @@ -119,19 +119,11 @@ it('stop request when test times out', async ({ signal }) => { #### `onTestFailed` -<<<<<<< HEAD -[`onTestFailed`](/api/#ontestfailed) 与当前测试用例绑定。当你并发执行多个测试并希望只对某个具体测试进行特殊处理时,这个 API 会非常有用。 +[`onTestFailed`](/api/hooks#ontestfailed) 与当前测试用例绑定。当你并发执行多个测试并希望只对某个具体测试进行特殊处理时,这个 API 会非常有用。 #### `onTestFinished` -[`onTestFinished`](/api/#ontestfailed) 与当前测试用例绑定。当你并发执行多个测试并希望只对某个特定测试进行特殊处理时,这个 API 会非常有帮助。 -======= -The [`onTestFailed`](/api/hooks#ontestfailed) hook bound to the current test. This API is useful if you are running tests concurrently and need to have a special handling only for this specific test. - -#### `onTestFinished` - -The [`onTestFinished`](/api/hooks#ontestfailed) hook bound to the current test. This API is useful if you are running tests concurrently and need to have a special handling only for this specific test. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d +[`onTestFinished`](/api/hooks#ontestfailed) 与当前测试用例绑定。当你并发执行多个测试并希望只对某个特定测试进行特殊处理时,这个 API 会非常有帮助。 ## 扩展测试上下文 {#extend-test-context} diff --git a/guide/ui.md b/guide/ui.md index 7e94c7a4..8f53c817 100644 --- a/guide/ui.md +++ b/guide/ui.md @@ -133,11 +133,9 @@ npx vite preview --outDir ./html 请将关于此功能反馈提交至 [GitHub Discussion](https://github.com/vitest-dev/vitest/discussions/9224)。 ::: -<<<<<<< HEAD -模块图选项卡还会提供导入耗时分析功能,默认显示加载时间最长的10个模块(点击"显示更多"可追加10个),按总耗时排序。 -======= + + The Module Graph tab also provides an Import Breakdown with a list of modules that take the longest time to load (top 10 by default), sorted by Total Time. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d Import breakdown with a list of top 10 modules that take the longest time to load Import breakdown with a list of top 10 modules that take the longest time to load @@ -148,8 +146,6 @@ The Module Graph tab also provides an Import Breakdown with a list of modules th 如果至少有一个文件加载时间超过 500 毫秒,"Show Import Breakdown" 图标将显示红色;如果至少有一个文件加载时间超过 100 毫秒,它将显示橙色。 -<<<<<<< HEAD -默认情况下,如果至少有一个模块加载时间超过 500 毫秒,Vitest 会自动显示分析结果。你可以通过设置 [`experimental.printImportBreakdown`](/config/experimental#experimental-printimportbreakdown) 选项来控制此行为。 -======= + + You can use [`experimental.importDurations.limit`](/config/experimental#experimental-importdurationslimit) to control the number of imports displayed. ->>>>>>> bd33ef125c480598f84a12804342385cf0be513d