# Getting Started **WARNING** This guide lists advanced APIs to run tests via a Node.js script. If you just want to [run tests](../guide/index), you probably don't need this. It is primarily used by library authors. You can import any method from the `vitest/node` entry-point. ## startVitest function startVitest( mode: VitestRunMode, cliFilters: string[] = [], options: CliOptions = {}, viteOverrides?: ViteUserConfig, vitestOptions?: VitestOptions, ): Promise You can start running Vitest tests using its Node API: import { startVitest } from 'vitest/node' const vitest = await startVitest('test') await vitest.close() `startVitest` function returns [`Vitest`](api/vitest) instance if tests can be started. If watch mode is not enabled, Vitest will call `close` method automatically. If watch mode is enabled and the terminal supports TTY, Vitest will register console shortcuts. You can pass down a list of filters as a second argument. Vitest will run only tests that contain at least one of the passed-down strings in their file path. Additionally, you can use the third argument to pass in CLI arguments, which will override any test config options. Alternatively, you can pass in the complete Vite config as the fourth argument, which will take precedence over any other user-defined options. After running the tests, you can get the results from the [`state.getTestModules`](api/test-module) API: import type { TestModule } from 'vitest/node' const vitest = await startVitest('test') console.log(vitest.state.getTestModules()) // [TestModule] **TIP** The ["Running Tests"](guide/tests#startvitest) guide has a usage example. ## createVitest function createVitest( mode: VitestRunMode, options: UserConfig, viteOverrides: ViteUserConfig = {}, vitestOptions: VitestOptions = {}, ): Promise You can create Vitest instance by using `createVitest` function. It returns the same [`Vitest`](api/vitest) instance as `startVitest`, but it doesn't start tests and doesn't validate installed packages. import { createVitest } from 'vitest/node' const vitest = await createVitest('test', { watch: false, }) **TIP** The ["Running Tests"](guide/tests#createvitest) guide has a usage example. ## resolveConfig function resolveConfig( options: UserConfig = {}, viteOverrides: ViteUserConfig = {}, ): Promise<{ vitestConfig: ResolvedConfig viteConfig: ResolvedViteConfig }> This method resolves the config with custom parameters. If no parameters are given, the `root` will be `process.cwd()`. import { resolveConfig } from 'vitest/node' // vitestConfig only has resolved "test" properties const { vitestConfig, viteConfig } = await resolveConfig({ mode: 'custom', configFile: false, resolve: { conditions: ['custom'] }, test: { setupFiles: ['/my-setup-file.js'], pool: 'threads', }, }) **INFO** Due to how Vite's `createServer` works, Vitest has to resolve the config during the plugin's `configResolve` hook. Therefore, this method is not actually used internally and is exposed exclusively as a public API. If you pass down the config to the `startVitest` or `createVitest` APIs, Vitest will still resolve the config again. **WARNING** The `resolveConfig` doesn't resolve the `workspace`. To resolve workspace configs, Vitest needs an established Vite server. Also note that `viteConfig.test` will not be fully resolved. If you need Vitest config, use `vitestConfig` instead. ## parseCLI function parseCLI(argv: string | string[], config: CliParseOptions = {}): { filter: string[] options: CliOptions } You can use this method to parse CLI arguments. It accepts a string (where arguments are split by a single space) or a strings array of CLI arguments in the same format that Vitest CLI uses. It returns a filter and `options` that you can later pass down to `createVitest` or `startVitest` methods. import { parseCLI } from 'vitest/node' const result = parseCLI('vitest ./files.ts --coverage --browser=chrome') result.options // { // coverage: { enabled: true }, // browser: { name: 'chrome', enabled: true } // } result.filter // ['./files.ts'] # Getting Started **WARNING** This guide lists advanced APIs to run tests via a Node.js script. If you just want to [run tests](../../guide/index), you probably don't need this. It is primarily used by library authors. You can import any method from the `vitest/node` entry-point. ## startVitest function startVitest( mode: VitestRunMode, cliFilters: string[] = [], options: CliOptions = {}, viteOverrides?: ViteUserConfig, vitestOptions?: VitestOptions, ): Promise You can start running Vitest tests using its Node API: import { startVitest } from 'vitest/node' const vitest = await startVitest('test') await vitest.close() `startVitest` function returns [`Vitest`](vitest) instance if tests can be started. If watch mode is not enabled, Vitest will call `close` method automatically. If watch mode is enabled and the terminal supports TTY, Vitest will register console shortcuts. You can pass down a list of filters as a second argument. Vitest will run only tests that contain at least one of the passed-down strings in their file path. Additionally, you can use the third argument to pass in CLI arguments, which will override any test config options. Alternatively, you can pass in the complete Vite config as the fourth argument, which will take precedence over any other user-defined options. After running the tests, you can get the results from the [`state.getTestModules`](test-module) API: import type { TestModule } from 'vitest/node' const vitest = await startVitest('test') console.log(vitest.state.getTestModules()) // [TestModule] **TIP** The ["Running Tests"](../guide/tests#startvitest) guide has a usage example. ## createVitest function createVitest( mode: VitestRunMode, options: UserConfig, viteOverrides: ViteUserConfig = {}, vitestOptions: VitestOptions = {}, ): Promise You can create Vitest instance by using `createVitest` function. It returns the same [`Vitest`](vitest) instance as `startVitest`, but it doesn't start tests and doesn't validate installed packages. import { createVitest } from 'vitest/node' const vitest = await createVitest('test', { watch: false, }) **TIP** The ["Running Tests"](../guide/tests#createvitest) guide has a usage example. ## resolveConfig function resolveConfig( options: UserConfig = {}, viteOverrides: ViteUserConfig = {}, ): Promise<{ vitestConfig: ResolvedConfig viteConfig: ResolvedViteConfig }> This method resolves the config with custom parameters. If no parameters are given, the `root` will be `process.cwd()`. import { resolveConfig } from 'vitest/node' // vitestConfig only has resolved "test" properties const { vitestConfig, viteConfig } = await resolveConfig({ mode: 'custom', configFile: false, resolve: { conditions: ['custom'] }, test: { setupFiles: ['/my-setup-file.js'], pool: 'threads', }, }) **INFO** Due to how Vite's `createServer` works, Vitest has to resolve the config during the plugin's `configResolve` hook. Therefore, this method is not actually used internally and is exposed exclusively as a public API. If you pass down the config to the `startVitest` or `createVitest` APIs, Vitest will still resolve the config again. **WARNING** The `resolveConfig` doesn't resolve the `workspace`. To resolve workspace configs, Vitest needs an established Vite server. Also note that `viteConfig.test` will not be fully resolved. If you need Vitest config, use `vitestConfig` instead. ## parseCLI function parseCLI(argv: string | string[], config: CliParseOptions = {}): { filter: string[] options: CliOptions } You can use this method to parse CLI arguments. It accepts a string (where arguments are split by a single space) or a strings array of CLI arguments in the same format that Vitest CLI uses. It returns a filter and `options` that you can later pass down to `createVitest` or `startVitest` methods. import { parseCLI } from 'vitest/node' const result = parseCLI('vitest ./files.ts --coverage --browser=chrome') result.options // { // coverage: { enabled: true }, // browser: { name: 'chrome', enabled: true } // } result.filter // ['./files.ts'] # Reporters **WARNING** This is an advanced API. If you just want to configure built-in reporters, read the ["Reporters"](../../guide/reporters) guide. Vitest has its own test run lifecycle. These are represented by reporter's methods: * `onInit` * `onTestRunStart` * `onTestModuleQueued` * `onTestModuleCollected` * `onTestModuleStart` * `onTestSuiteReady` * `onHookStart(beforeAll)` * `onHookEnd(beforeAll)` * `onTestCaseReady` * `onHookStart(beforeEach)` * `onHookEnd(beforeEach)` * `onHookStart(afterEach)` * `onHookEnd(afterEach)` * `onTestCaseResult` * `onHookStart(afterAll)` * `onHookEnd(afterAll)` * `onTestSuiteResult` * `onTestModuleEnd` * `onTestRunEnd` Tests and suites within a single module will be reported in order unless they were skipped. All skipped tests are reported at the end of suite/module. Note that since test modules can run in parallel, Vitest will report them in parallel. This guide lists all supported reporter methods. However, don't forget that instead of creating your own reporter, you can [extend existing one](../reporters) instead: custom-reporter.js import { BaseReporter } from 'vitest/reporters' export default class CustomReporter extends BaseReporter { onTestRunEnd(testModules, errors) { console.log(testModule.length, 'tests finished running') super.onTestRunEnd(testModules, errors) } } ## onInit function onInit(vitest: Vitest): Awaitable This method is called when [Vitest](vitest) was initiated or started, but before the tests were filtered. **INFO** Internally this method is called inside [`vitest.start`](vitest#start), [`vitest.init`](vitest#init) or [`vitest.mergeReports`](vitest#mergereports). If you are using programmatic API, make sure to call either one dependning on your needs before calling [`vitest.runTestSpecifications`](vitest#runtestspecifications), for example. Built-in CLI will always run methods in correct order. Note that you can also get access to `vitest` instance from test cases, suites and test modules via a [`project`](test-project) property, but it might also be useful to store a reference to `vitest` in this method. Example import type { Reporter, TestSpecification, Vitest } from 'vitest/node' class MyReporter implements Reporter { private vitest!: Vitest onInit(vitest: Vitest) { this.vitest = vitest } onTestRunStart(specifications: TestSpecification[]) { console.log( specifications.length, 'test files will run in', this.vitest.config.root, ) } } export default new MyReporter() ## onBrowserInit experimental function onBrowserInit(project: TestProject): Awaitable This method is called when the browser instance is initiated. It receives an instance of the project for which the browser is initiated. `project.browser` will always be defined when this method is called. ## onTestRunStart function onTestRunStart( specifications: TestSpecification[] ): Awaitable This method is called when a new test run has started. It receives an array of [test specifications](test-specification) scheduled to run. This array is readonly and available only for information purposes. If Vitest didn't find any test files to run, this event will be invoked with an empty array, and then `onTestRunEnd` will be called immediately after. Example import type { Reporter, TestSpecification } from 'vitest/node' class MyReporter implements Reporter { onTestRunStart(specifications: TestSpecification[]) { console.log(specifications.length, 'test files will run') } } export default new MyReporter() **DEPRECATION NOTICE** This method was added in Vitest 3, replacing `onPathsCollected` and `onSpecsCollected`, both of which are now deprecated. ## onTestRunEnd function onTestRunEnd( testModules: ReadonlyArray, unhandledErrors: ReadonlyArray, reason: TestRunEndReason ): Awaitable This method is called after all tests have finished running and the coverage merged all reports, if it's enabled. Note that you can get the coverage information in `onCoverage` hook. It receives a readonly list of test modules. You can iterate over it via a [`testModule.children`](test-collection) property to report the state and errors, if any. The second argument is a readonly list of unhandled errors that Vitest wasn't able to attribute to any test. These can happen outside of the test run because of an error in a plugin, or inside the test run as a side-effect of a non-awaited function (for example, a timeout that threw an error after the test has finished running). The third argument indicated why the test run was finished: * `passed`: test run was finished normally and there are no errors * `failed`: test run has at least one error (due to a syntax error during collection or an actual error during test execution) * `interrupted`: test was interruped by [`vitest.cancelCurrentRun`](vitest#cancelcurrentrun) call or `Ctrl+C` was pressed in the terminal (note that it's still possible to have failed tests in this case) If Vitest didn't find any test files to run, this event will be invoked with empty arrays of modules and errors, and the state will depend on the value of [`config.passWithNoTests`](../../config/index#passwithnotests). Example import type { Reporter, SerializedError, TestModule, TestRunEndReason, TestSpecification } from 'vitest/node' class MyReporter implements Reporter { onTestRunEnd( testModules: ReadonlyArray, unhandledErrors: ReadonlyArray, reason: TestRunEndReason, ) { if (reason === 'passed') { testModules.forEach(module => console.log(module.moduleId, 'succeeded')) } else if (reason === 'failed') { // note that this will skip possible errors in suites // you can get them from testSuite.errors() for (const testCase of testModules.children.allTests()) { if (testCase.result().state === 'failed') { console.log(testCase.fullName, 'in', testCase.module.moduleId, 'failed') console.log(testCase.result().errors) } } } else { console.log('test run was interrupted, skipping report') } } } export default new MyReporter() **DEPRECATION NOTICE** This method was added in Vitest 3, replacing `onFinished`, which is now deprecated. ## onCoverage function onCoverage(coverage: unknown): Awaitable This hook is called after coverage results have been processed. Coverage provider's reporters are called after this hook. The typings of `coverage` depends on the `coverage.provider`. For Vitest's default built-in providers you can import the types from `istanbul-lib-coverage` package: import type { CoverageMap } from 'istanbul-lib-coverage' declare function onCoverage(coverage: CoverageMap): Awaitable If Vitest didn't perform any coverage, this hook is not called. ## onTestModuleQueued function onTestModuleQueued(testModule: TestModule): Awaitable This method is called right before Vitest imports the setup file and the test module itself. This means that `testModule` will have no [`children`](test- suite#children) yet, but you can start reporting it as the next test to run. ## onTestModuleCollected function onTestModuleCollected(testModule: TestModule): Awaitable This method is called when all tests inside the file were collected, meaning [`testModule.children`](test-suite#children) collection is populated, but tests don't have any results yet. ## onTestModuleStart function onTestModuleStart(testModule: TestModule): Awaitable This method is called right after `onTestModuleCollected` unless Vitest runs in collection mode ([`vitest.collect()`](vitest#collect) or `vitest collect` in the CLI), in this case it will not be called at all because there are no tests to run. ## onTestModuleEnd function onTestModuleEnd(testModule: TestModule): Awaitable This method is called when every test in the module finished running. This means, every test inside [`testModule.children`](test-suite#children) will have a `test.result()` that is not equal to `pending`. ## onHookStart function onHookStart(context: ReportedHookContext): Awaitable This method is called when any of these hooks have started running: * `beforeAll` * `afterAll` * `beforeEach` * `afterEach` If `beforeAll` or `afterAll` are started, the `entity` will be either [`TestSuite`](test-suite) or [`TestModule`](test-module). If `beforeEach` or `afterEach` are started, the `entity` will always be [`TestCase`](test-case). **WARNING** `onHookStart` method will not be called if the hook did not run during the test run. ## onHookEnd function onHookEnd(context: ReportedHookContext): Awaitable This method is called when any of these hooks have finished running: * `beforeAll` * `afterAll` * `beforeEach` * `afterEach` If `beforeAll` or `afterAll` have finished, the `entity` will be either [`TestSuite`](test-suite) or [`TestModule`](test-module). If `beforeEach` or `afterEach` have finished, the `entity` will always be [`TestCase`](test-case). **WARNING** `onHookEnd` method will not be called if the hook did not run during the test run. ## onTestSuiteReady function onTestSuiteReady(testSuite: TestSuite): Awaitable This method is called before the suite starts to run its tests. This method is also called if the suite was skipped. If the file doesn't have any suites, this method will not be called. Consider using `onTestModuleStart` to cover this use case. ## onTestSuiteResult function onTestSuiteResult(testSuite: TestSuite): Awaitable This method is called after the suite has finished running tests. This method is also called if the suite was skipped. If the file doesn't have any suites, this method will not be called. Consider using `onTestModuleEnd` to cover this use case. ## onTestCaseReady function onTestCaseReady(testCase: TestCase): Awaitable This method is called before the test starts to run or it was skipped. Note that `beforeEach` and `afterEach` hooks are considered part of the test because they can influence the result. **WARNING** Notice that it's possible to have [`testCase.result()`](test-case#result) with `passed` or `failed` state already when `onTestCaseReady` is called. This can happen if test was running too fast and both `onTestCaseReady` and `onTestCaseResult` were scheduled to run in the same microtask. ## onTestCaseResult function onTestCaseResult(testCase: TestCase): Awaitable This method is called when the test has finished running or was just skipped. Note that this will be called after the `afterEach` hook is finished, if there are any. At this point, [`testCase.result()`](test-case#result) will have non-pending state. # TestCase The `TestCase` class represents a single test. This class is only available in the main thread. Refer to the ["Runner API"](../runner#tasks) if you are working with runtime tasks. The `TestCase` instance always has a `type` property with the value of `test`. You can use it to distinguish between different task types: if (task.type === 'test') { task // TestCase } ## project This references the [`TestProject`](test-project) that the test belongs to. ## module This is a direct reference to the [`TestModule`](test-module) where the test is defined. ## name This is a test name that was passed to the `test` function. import { test } from 'vitest' test('the validation works correctly', () => { // ... }) ## fullName The name of the test including all parent suites separated with `>` symbol. This test has a full name "the validation logic > the validation works correctly": import { describe, test } from 'vitest' describe('the validation logic', () => { test('the validation works correctly', () => { // ... }) }) ## id This is test's unique identifier. This ID is deterministic and will be the same for the same test across multiple runs. The ID is based on the [project](test-project) name, module ID and test order. The ID looks like this: 1223128da3_0_0 ^^^^^^^^^^ the file hash ^ suite index ^ test index **TIP** You can generate file hash with `generateFileHash` function from `vitest/node` which is available since Vitest 3: import { generateFileHash } from 'vitest/node' const hash = generateFileHash( '/file/path.js', // relative path undefined, // the project name or `undefined` is not set ) **DANGER** Don't try to parse the ID. It can have a minus at the start: `-1223128da3_0_0_0`. ## location The location in the module where the test was defined. Locations are collected only if [`includeTaskLocation`](../../config/index#includetasklocation) is enabled in the config. Note that this option is automatically enabled if `--reporter=html`, `--ui` or `--browser` flags are used. The location of this test will be equal to `{ line: 3, column: 1 }`: import { test } from 'vitest' test('the validation works correctly', () => { // ... }) ## parent Parent [suite](test-suite). If the test was called directly inside the [module](test-module), the parent will be the module itself. ## options interface TaskOptions { readonly each: boolean | undefined readonly fails: boolean | undefined readonly concurrent: boolean | undefined readonly shuffle: boolean | undefined readonly retry: number | undefined readonly repeats: number | undefined readonly mode: 'run' | 'only' | 'skip' | 'todo' } The options that test was collected with. ## ok function ok(): boolean Checks if the test did not fail the suite. If the test is not finished yet or was skipped, it will return `true`. ## meta function meta(): TaskMeta Custom metadata that was attached to the test during its execution. The meta can be attached by assigning a property to the `ctx.task.meta` object during a test run: import { test } from 'vitest' test('the validation works correctly', ({ task }) => { // ... task.meta.decorated = false }) If the test did not finish running yet, the meta will be an empty object. ## result function result(): TestResult Test results. If test is not finished yet or was just collected, it will be equal to `TestResultPending`: export interface TestResultPending { /** * The test was collected, but didn't finish running yet. */ readonly state: 'pending' /** * Pending tests have no errors. */ readonly errors: undefined } If the test was skipped, the return value will be `TestResultSkipped`: interface TestResultSkipped { /** * The test was skipped with `skip` or `todo` flag. * You can see which one was used in the `options.mode` option. */ readonly state: 'skipped' /** * Skipped tests have no errors. */ readonly errors: undefined /** * A custom note passed down to `ctx.skip(note)`. */ readonly note: string | undefined } **TIP** If the test was skipped because another test has `only` flag, the `options.mode` will be equal to `skip`. If the test failed, the return value will be `TestResultFailed`: interface TestResultFailed { /** * The test failed to execute. */ readonly state: 'failed' /** * Errors that were thrown during the test execution. */ readonly errors: ReadonlyArray } If the test passed, the return value will be `TestResultPassed`: interface TestResultPassed { /** * The test passed successfully. */ readonly state: 'passed' /** * Errors that were thrown during the test execution. */ readonly errors: ReadonlyArray | undefined } **WARNING** Note that the test with `passed` state can still have errors attached - this can happen if `retry` was triggered at least once. ## diagnostic function diagnostic(): TestDiagnostic | undefined Useful information about the test like duration, memory usage, etc: interface TestDiagnostic { /** * If the duration of the test is above `slowTestThreshold`. */ readonly slow: boolean /** * The amount of memory used by the test in bytes. * This value is only available if the test was executed with `logHeapUsage` flag. */ readonly heap: number | undefined /** * The time it takes to execute the test in ms. */ readonly duration: number /** * The time in ms when the test started. */ readonly startTime: number /** * The amount of times the test was retried. */ readonly retryCount: number /** * The amount of times the test was repeated as configured by `repeats` option. * This value can be lower if the test failed during the repeat and no `retry` is configured. */ readonly repeatCount: number /** * If test passed on a second retry. */ readonly flaky: boolean } **INFO** `diagnostic()` will return `undefined` if the test was not scheduled to run yet. # TestCollection `TestCollection` represents a collection of top-level [suites](test-suite) and [tests](test-case) in a suite or a module. It also provides useful methods to iterate over itself. **INFO** Most methods return an iterator instead of an array for better performance in case you don't need every item in the collection. If you prefer working with array, you can spread the iterator: `[...children.allSuites()]`. Also note that the collection itself is an iterator: for (const child of module.children) { console.log(child.type, child.name) } ## size The number of tests and suites in the collection. **WARNING** This number includes only tests and suites at the top-level, it doesn't include nested suites and tests. ## at function at(index: number): TestCase | TestSuite | undefined Returns the test or suite at a specific index. This method accepts negative indexes. ## array function array(): (TestCase | TestSuite)[] The same collection but as an array. This is useful if you want to use `Array` methods like `map` and `filter` that are not supported by the `TaskCollection` implementation. ## allSuites function allSuites(): Generator Filters all suites that are part of this collection and its children. for (const suite of module.children.allSuites()) { if (suite.errors().length) { console.log('failed to collect', suite.errors()) } } ## allTests function allTests(state?: TestState): Generator Filters all tests that are part of this collection and its children. for (const test of module.children.allTests()) { if (test.result().state === 'pending') { console.log('test', test.fullName, 'did not finish') } } You can pass down a `state` value to filter tests by the state. ## tests function tests(state?: TestState): Generator Filters only the tests that are part of this collection. You can pass down a `state` value to filter tests by the state. ## suites function suites(): Generator Filters only the suites that are part of this collection. # TestModule The `TestModule` class represents a single module in a single project. This class is only available in the main thread. Refer to the ["Runner API"](../runner#tasks) if you are working with runtime tasks. The `TestModule` instance always has a `type` property with the value of `module`. You can use it to distinguish between different task types: if (task.type === 'module') { task // TestModule } **Extending Suite Methods** The `TestModule` class inherits all methods and properties from the [`TestSuite`](test-suite). This guide will only list methods and properties unique to the `TestModule`. ## moduleId This is usually an absolute unix file path (even on Windows). It can be a virtual id if the file is not on the disk. This value corresponds to Vite's `ModuleGraph` id. 'C:/Users/Documents/project/example.test.ts' // ✅ '/Users/mac/project/example.test.ts' // ✅ 'C:\\Users\\Documents\\project\\example.test.ts' // ❌ ## state function state(): TestModuleState Works the same way as [`testSuite.state()`](test-suite#state), but can also return `queued` if module wasn't executed yet. ## diagnostic function diagnostic(): ModuleDiagnostic Useful information about the module like duration, memory usage, etc. If the module was not executed yet, all diagnostic values will return `0`. interface ModuleDiagnostic { /** * The time it takes to import and initiate an environment. */ readonly environmentSetupDuration: number /** * The time it takes Vitest to setup test harness (runner, mocks, etc.). */ readonly prepareDuration: number /** * The time it takes to import the test module. * This includes importing everything in the module and executing suite callbacks. */ readonly collectDuration: number /** * The time it takes to import the setup module. */ readonly setupDuration: number /** * Accumulated duration of all tests and hooks in the module. */ readonly duration: number } # TestProject 3.0.0+ * **Alias** : `WorkspaceProject` before 3.0.0 **WARNING** This guide describes the advanced Node.js API. If you just want to create a workspace, follow the ["Workspace"](../../guide/workspace) guide. ## name The name is a unique string assigned by the user or interpreted by Vitest. If user did not provide a name, Vitest tries to load a `package.json` in the root of the project and takes the `name` property from there. If there is no `package.json`, Vitest uses the name of the folder by default. Inline projects use numbers as the name (converted to string). node.jsvitest.workspace.js import { createVitest } from 'vitest/node' const vitest = await createVitest('test') vitest.projects.map(p => p.name) === [ '@pkg/server', 'utils', '2', 'custom' ] export default [ './packages/server', // has package.json with "@pkg/server" './utils', // doesn't have a package.json file { // doesn't customize the name test: { pool: 'threads', }, }, { // customized the name test: { name: 'custom', }, }, ] **INFO** If the [root project](vitest#getroottestproject) is not part of a user workspace, its `name` will not be resolved. ## vitest `vitest` references the global [`Vitest`](vitest) process. ## serializedConfig This is the config that test processes receive. Vitest [serializes config](https://github.com/vitest- dev/vitest/blob/main/packages/vitest/src/node/config/serializeConfig.ts) manually by removing all functions and properties that are not possible to serialize. Since this value is available in both tests and node, its type is exported from the main entry point. import type { SerializedConfig } from 'vitest' const config: SerializedConfig = vitest.projects[0].serializedConfig **WARNING** The `serializedConfig` property is a getter. Every time it's accessed Vitest serializes the config again in case it was changed. This also means that it always returns a different reference: project.serializedConfig === project.serializedConfig // ❌ ## globalConfig The test config that [`Vitest`](vitest) was initialized with. If this is the [root project](vitest#getroottestproject), `globalConfig` and `config` will reference the same object. This config is useful for values that cannot be set on the project level, like `coverage` or `reporters`. import type { ResolvedConfig } from 'vitest/node' vitest.config === vitest.projects[0].globalConfig ## config This is the project's resolved test config. ## vite This is project's [`ViteDevServer`](https://vite.dev/guide/api- javascript#vitedevserver). All projects have their own Vite servers. ## browser This value will be set only if tests are running in the browser. If `browser` is enabled, but tests didn't run yet, this will be `undefined`. If you need to check if the project supports browser tests, use `project.isBrowserEnabled()` method. **WARNING** The browser API is even more experimental and doesn't follow SemVer. The browser API will be standardized separately from the rest of the APIs. ## provide function provide( key: T, value: ProvidedContext[T], ): void A way to provide custom values to tests in addition to [`config.provide`](../../config/index#provide) field. All values are validated with [`structuredClone`](https://developer.mozilla.org/en- US/docs/Web/API/Window/structuredClone) before they are stored, but the values on `providedContext` themselves are not cloned. node.jstest.spec.js import { createVitest } from 'vitest/node' const vitest = await createVitest('test') const project = vitest.projects.find(p => p.name === 'custom') project.provide('key', 'value') await vitest.start() import { inject } from 'vitest' const value = inject('key') The values can be provided dynamicaly. Provided value in tests will be updated on their next run. **TIP** This method is also available to [global setup files](../../config/index#globalsetup) for cases where you cannot use the public API: export default function setup({ provide }) { provide('wsPort', 3000) } ## getProvidedContext function getProvidedContext(): ProvidedContext This returns the context object. Every project also inherits the global context set by `vitest.provide`. import { createVitest } from 'vitest/node' const vitest = await createVitest('test') vitest.provide('global', true) const project = vitest.projects.find(p => p.name === 'custom') project.provide('key', 'value') // { global: true, key: 'value' } const context = project.getProvidedContext() **TIP** Project context values will always override root project's context. ## createSpecification function createSpecification( moduleId: string, locations?: number[], ): TestSpecification Create a [test specification](test-specification) that can be used in [`vitest.runTestSpecifications`](vitest#runtestspecifications). Specification scopes the test file to a specific `project` and test `locations` (optional). Test [locations](test-case#location) are code lines where the test is defined in the source code. If locations are provided, Vitest will only run tests defined on those lines. Note that if [`testNamePattern`](../../config/index#testnamepattern) is defined, then it will also be applied. import { createVitest } from 'vitest/node' import { resolve } from 'node:path/posix' const vitest = await createVitest('test') const project = vitest.projects[0] const specification = project.createSpecification( resolve('./example.test.ts'), [20, 40], // optional test lines ) await vitest.runTestSpecifications([specification]) **WARNING** `createSpecification` expects resolved [module ID](test- specification#moduleid). It doesn't auto-resolve the file or check that it exists on the file system. Also note that `project.createSpecification` always returns a new instance. ## isRootProject function isRootProject(): boolean Checks if the current project is the root project. You can also get the root project by calling `vitest.getRootProject()`. ## globTestFiles function globTestFiles(filters?: string[]): { /** * Test files that match the filters. */ testFiles: string[] /** * Typecheck test files that match the filters. This will be empty unless `typecheck.enabled` is `true`. */ typecheckTestFiles: string[] } Globs all test files. This function returns an object with regular tests and typecheck tests. This method accepts `filters`. Filters can only a part of the file path, unlike in other methods on the [`Vitest`](vitest) instance: project.globTestFiles(['foo']) // ✅ project.globTestFiles(['basic/foo.js:10']) // ❌ **TIP** Vitest uses [fast-glob](https://www.npmjs.com/package/fast-glob) to find test files. `test.dir`, `test.root`, `root` or `process.cwd()` define the `cwd` option. This method looks at several config options: * `test.include`, `test.exclude` to find regular test files * `test.includeSource`, `test.exclude` to find in-source tests * `test.typecheck.include`, `test.typecheck.exclude` to find typecheck tests ## matchesTestGlob function matchesTestGlob( moduleId: string, source?: () => string ): boolean This method checks if the file is a regular test file. It uses the same config properties that `globTestFiles` uses for validation. This method also accepts a second parameter, which is the source code. This is used to validate if the file is an in-source test. If you are calling this method several times for several projects it is recommended to read the file once and pass it down directly. If the file is not a test file, but matches the `includeSource` glob, Vitest will synchronously read the file unless the `source` is provided. import { createVitest } from 'vitest/node' import { resolve } from 'node:path/posix' const vitest = await createVitest('test') const project = vitest.projects[0] project.matchesTestGlob(resolve('./basic.test.ts')) // true project.matchesTestGlob(resolve('./basic.ts')) // false project.matchesTestGlob(resolve('./basic.ts'), () => ` if (import.meta.vitest) { // ... } `) // true if `includeSource` is set ## import function import(moduleId: string): Promise Import a file using Vite module runner. The file will be transformed by Vite with provided project's config and executed in a separate context. Note that `moduleId` will be relative to the `config.root`. **DANGER** `project.import` reuses Vite's module graph, so importing the same module using a regular import will return a different module: import * as staticExample from './example.js' const dynamicExample = await project.import('./example.js') dynamicExample !== staticExample // ✅ **INFO** Internally, Vitest uses this method to import global setups, custom coverage providers, workspace file, and custom reporters, meaning all of them share the same module graph as long as they belong to the same Vite server. ## onTestsRerun function onTestsRerun(cb: OnTestsRerunHandler): void This is a shorthand for [`project.vitest.onTestsRerun`](vitest#ontestsrerun). It accepts a callback that will be awaited when the tests have been scheduled to rerun (usually, due to a file change). project.onTestsRerun((specs) => { console.log(specs) }) ## isBrowserEnabled function isBrowserEnabled(): boolean Returns `true` if this project runs tests in the browser. ## close function close(): Promise Closes the project and all associated resources. This can only be called once; the closing promise is cached until the server restarts. If the resources are needed again, create a new project. In detail, this method closes the Vite server, stops the typechecker service, closes the browser if it's running, deletes the temporary directory that holds the source code, and resets the provided context. # TestSpecification The `TestSpecification` class describes what module to run as a test and its parameters. You can only create a specification by calling [`createSpecification`](test- project#createspecification) method on a test project: const specification = project.createSpecification( resolve('./example.test.ts'), [20, 40], // optional test lines ) `createSpecification` expects resolved module ID. It doesn't auto-resolve the file or check that it exists on the file system. ## taskId [Test module's](test-suite#id) identifier. ## project This references the [`TestProject`](test-project) that the test module belongs to. ## moduleId The ID of the module in Vite's module graph. Usually, it's an absolute file path using posix separator: 'C:/Users/Documents/project/example.test.ts' // ✅ '/Users/mac/project/example.test.ts' // ✅ 'C:\\Users\\Documents\\project\\example.test.ts' // ❌ ## testModule Instance of [`TestModule`](test-module) assosiated with the specification. If test wasn't queued yet, this will be `undefined`. ## pool experimental The [`pool`](../../config/index#pool) in which the test module will run. **DANGER** It's possible to have multiple pools in a single test project with [`poolMatchGlob`](../../config/index#poolmatchglob) and [`typecheck.enabled`](../../config/index#typecheck-enabled). This means it's possible to have several specifications with the same `moduleId` but different `pool`. In Vitest 4, the project will only support a single pool, and this property will be removed. ## testLines This is an array of lines in the source code where the test files are defined. This field is defined only if the `createSpecification` method received an array. Note that if there is no test on at least one of the lines, the whole suite will fail. An example of a correct `testLines` configuration: script.jsexample.test.js const specification = project.createSpecification( resolve('./example.test.ts'), [3, 8, 9], ) import { test, describe } from 'vitest' test('verification works') describe('a group of tests', () => { // ... test('nested test') test.skip('skipped test') }) ## toJSON function toJSON(): SerializedTestSpecification `toJSON` generates a JSON-friendly object that can be consumed by the [Browser Mode](../../guide/browser/index) or [Vitest UI](../../guide/ui). # TestSuite The `TestSuite` class represents a single suite. This class is only available in the main thread. Refer to the ["Runner API"](../runner#tasks) if you are working with runtime tasks. The `TestSuite` instance always has a `type` property with the value of `suite`. You can use it to distinguish between different task types: if (task.type === 'suite') { task // TestSuite } ## project This references the [`TestProject`](test-project) that the test belongs to. ## module This is a direct reference to the [`TestModule`](test-module) where the test is defined. ## name This is a suite name that was passed to the `describe` function. import { describe } from 'vitest' describe('the validation logic', () => { // ... }) ## fullName The name of the suite including all parent suites separated with `>` symbol. This suite has a full name "the validation logic > validating cities": import { describe, test } from 'vitest' describe('the validation logic', () => { describe('validating cities', () => { // ... }) }) ## id This is suite's unique identifier. This ID is deterministic and will be the same for the same suite across multiple runs. The ID is based on the [project](test-project) name, module ID and suite order. The ID looks like this: 1223128da3_0_0_0 ^^^^^^^^^^ the file hash ^ suite index ^ nested suite index ^ test index **TIP** You can generate file hash with `generateFileHash` function from `vitest/node` which is available since Vitest 3: import { generateFileHash } from 'vitest/node' const hash = generateFileHash( '/file/path.js', // relative path undefined, // the project name or `undefined` is not set ) **DANGER** Don't try to parse the ID. It can have a minus at the start: `-1223128da3_0_0_0`. ## location The location in the module where the suite was defined. Locations are collected only if [`includeTaskLocation`](../../config/index#includetasklocation) is enabled in the config. Note that this option is automatically enabled if `--reporter=html`, `--ui` or `--browser` flags are used. The location of this suite will be equal to `{ line: 3, column: 1 }`: import { describe } from 'vitest' describe('the validation works correctly', () => { // ... }) ## parent Parent suite. If the suite was called directly inside the [module](test- module), the parent will be the module itself. ## options interface TaskOptions { readonly each: boolean | undefined readonly fails: boolean | undefined readonly concurrent: boolean | undefined readonly shuffle: boolean | undefined readonly retry: number | undefined readonly repeats: number | undefined readonly mode: 'run' | 'only' | 'skip' | 'todo' } The options that suite was collected with. ## children This is a [collection](test-collection) of all suites and tests inside the current suite. for (const task of suite.children) { if (task.type === 'test') { console.log('test', task.fullName) } else { // task is TaskSuite console.log('suite', task.name) } } **WARNING** Note that `suite.children` will only iterate the first level of nesting, it won't go deeper. If you need to iterate over all tests or suites, use [`children.allTests()`](test-collection#alltests) or [`children.allSuites()`](test-collection#allsuites). If you need to iterate over everything, use recursive function: function visit(collection: TestCollection) { for (const task of collection) { if (task.type === 'suite') { // report a suite visit(task.children) } else { // report a test } } } ## ok function ok(): boolean Checks if the suite has any failed tests. This will also return `false` if suite failed during collection. In that case, check the `errors()` for thrown errors. ## state function state(): TestSuiteState Checks the running state of the suite. Possible return values: * **pending** : the tests in this suite did not finish running yet. * **failed** : this suite has failed tests or they couldn't be collected. If `errors()` is not empty, it means the suite failed to collect tests. * **passed** : every test inside this suite has passed. * **skipped** : this suite was skipped during collection. **WARNING** Note that [test module](test-module) also has a `state` method that returns the same values, but it can also return an additional `queued` state if the module wasn't executed yet. ## errors function errors(): TestError[] Errors that happened outside of the test run during collection, like syntax errors. import { describe } from 'vitest' describe('collection failed', () => { throw new Error('a custom error') }) **WARNING** Note that errors are serialized into simple objects: `instanceof Error` will always return `false`. # Vitest Vitest instance requires the current test mode. It can be either: * `test` when running runtime tests * `benchmark` when running benchmarks experimental New in Vitest 3 Vitest 3 is one step closer to stabilising the public API. To achieve that, we deprecated and removed some of the previously public methods on the `Vitest` class. These APIs were made private: * `configOverride` (use `setGlobalTestNamePattern` or `enableSnapshotUpdate`) * `coverageProvider` * `filenamePattern` * `runningPromise` * `closingPromise` * `isCancelling` * `coreWorkspaceProject` * `resolvedProjects` * `_browserLastPort` * `_options` * `reporters` * `vitenode` * `runner` * `pool` * `setServer` * `_initBrowserServers` * `rerunTask` * `changeProjectName` * `changeNamePattern` * `changeFilenamePattern` * `rerunFailed` * `_createRootProject` (renamed to `_ensureRootProject`, but still private) * `filterTestsBySource` (this was moved to the new internal `vitest.specifications` instance) * `runFiles` (use `runTestSpecifications` instead) * `onAfterSetServer` These APIs were deprecated: * `invalidates` * `changedTests` (use `onFilterWatchedSpecification` instead) * `server` (use `vite` instead) * `getProjectsByTestFile` (use `getModuleSpecifications` instead) * `getFileWorkspaceSpecs` (use `getModuleSpecifications` instead) * `getModuleProjects` (filter by `this.projects` yourself) * `updateLastChanged` (renamed to `invalidateFile`) * `globTestSpecs` (use `globTestSpecifications` instead) * `globTestFiles` (use `globTestSpecifications` instead) * `listFile` (use `getRelevantTestSpecifications` instead) ## mode ### test Test mode will only call functions inside `test` or `it`, and throws an error when `bench` is encountered. This mode uses `include` and `exclude` options in the config to find test files. ### benchmark experimental Benchmark mode calls `bench` functions and throws an error, when it encounters `test` or `it`. This mode uses `benchmark.include` and `benchmark.exclude` options in the config to find benchmark files. ## config The root (or global) config. If workspace feature is enabled, projects will reference this as `globalConfig`. **WARNING** This is Vitest config, it doesn't extend _Vite_ config. It only has resolved values from the `test` property. ## vite This is a global [`ViteDevServer`](https://vite.dev/guide/api- javascript#vitedevserver). ## state experimental **WARNING** Public `state` is an experimental API (except `vitest.state.getReportedEntity`). Breaking changes might not follow SemVer, please pin Vitest's version when using it. Global state stores information about the current tests. It uses the same API from `@vitest/runner` by default, but we recommend using the [Reported Tasks API](../reporters#reported-tasks) instead by calling `state.getReportedEntity()` on the `@vitest/runner` API: const task = vitest.state.idMap.get(taskId) // old API const testCase = vitest.state.getReportedEntity(task) // new API In the future, the old API won't be exposed anymore. ## snapshot The global snapshot manager. Vitest keeps track of all snapshots using the `snapshot.add` method. You can get the latest summary of snapshots via the `vitest.snapshot.summay` property. ## cache Cache manager that stores information about latest test results and test file stats. In Vitest itself this is only used by the default sequencer to sort tests. ## projects An array of [test projects](test-project) that belong to the user's workspace. If the user did not specify a custom workspace, the workspace will only have a root project. Vitest will ensure that there is always at least one project in the workspace. If the user specifies a non-existent `--project` name, Vitest will throw an error. ## getRootProject function getRootProject(): TestProject This returns the root test project. The root project generally doesn't run any tests and is not included in `vitest.projects` unless the user explicitly includes the root config in their workspace, or the workspace is not defined at all. The primary goal of the root project is to setup the global config. In fact, `rootProject.config` references `rootProject.globalConfig` and `vitest.config` directly: rootProject.config === rootProject.globalConfig === rootProject.vitest.config ## provide function provide( key: T, value: ProvidedContext[T], ): void Vitest exposes `provide` method which is a shorthand for `vitest.getRootProject().provide`. With this method you can pass down values from the main thread to tests. All values are checked with `structuredClone` before they are stored, but the values themselves are not cloned. To recieve the values in the test, you need to import `inject` method from `vitest` entrypont: import { inject } from 'vitest' const port = inject('wsPort') // 3000 For better type safety, we encourage you to augment the type of `ProvidedContext`: import { createVitest } from 'vitest/node' const vitest = await createVitest('test', { watch: false, }) vitest.provide('wsPort', 3000) declare module 'vitest' { export interface ProvidedContext { wsPort: number } } **WARNING** Technically, `provide` is a method of [`TestProject`](test-project), so it is limited to the specific project. However, all projects inherit the values from the core project which makes `vitest.provide` universal way of passing down values to tests. ## getProvidedContext function getProvidedContext(): ProvidedContext This returns the root context object. This is a shorthand for `vitest.getRootProject().getProvidedContext`. ## getProjectByName function getProjectByName(name: string): TestProject This method returns the project by its name. Simillar to calling `vitest.projects.find`. **WARNING** In case the project doesn't exist, this method will return the root project - make sure to check the names again if the project you are looking for is the one returned. If user didn't customize a name, the Vitest will assign an empty string as a name. ## globTestSpecifications function globTestSpecifications( filters?: string[], ): Promise This method constructs new [test specifications](test-specification) by collecting every test in all projects with [`project.globTestFiles`](test- project#globtestfiles). It accepts string filters to match the test files - these are the same filters that [CLI supports](../../guide/filtering#cli). This method automatically caches all test specifications. When you call `getModuleSpecifications` next time, it will return the same specifications unless `clearSpecificationsCache` was called before that. **WARNING** As of Vitest 3, it's possible to have multiple test specifications with the same module ID (file path) if `poolMatchGlob` has several pools or if `typecheck` is enabled. This possibility will be removed in Vitest 4. const specifications = await vitest.globTestSpecifications(['my-filter']) // [TestSpecification{ moduleId: '/tests/my-filter.test.ts' }] console.log(specifications) ## getRelevantTestSpecifications function getRelevantTestSpecifications( filters?: string[] ): Promise This method resolves every test specification by calling [`project.globTestFiles`](test-project#globtestfiles). It accepts string filters to match the test files - these are the same filters that [CLI supports](../../guide/filtering#cli). If `--changed` flag was specified, the list will be filtered to include only files that changed. `getRelevantTestSpecifications` doesn't run any test files. **WARNING** This method can be slow because it needs to filter `--changed` flags. Do not use it if you just need a list of test files. * If you need to get the list of specifications for known test files, use `getModuleSpecifications` instead. * If you need to get the list of all possible test files, use `globTestSpecifications`. ## mergeReports function mergeReports(directory?: string): Promise Merge reports from multiple runs located in the specified directory (value from `--merge-reports` if not specified). This value can also be set on `config.mergeReports` (by default, it will read `.vitest-reports` folder). Note that the `directory` will always be resolved relative to the working directory. This method is called automatically by [`startVitest`](../guide/tests) if `config.mergeReports` is set. ## collect function collect(filters?: string[]): Promise Execute test files without running test callbacks. `collect` returns unhandled errors and an array of [test modules](test-module). It accepts string filters to match the test files - these are the same filters that [CLI supports](../../guide/filtering#cli). This method resolves tests specifications based on the config `include`, `exclude`, and `includeSource` values. Read more at [`project.globTestFiles`](test-project#globtestfiles). If `--changed` flag was specified, the list will be filtered to include only files that changed. **WARNING** Note that Vitest doesn't use static analysis to collect tests. Vitest will run every test file in isolation, just like it runs regular tests. This makes this method very slow, unless you disable isolation before collecting tests. ## start function start(filters?: string[]): Promise Initialize reporters, the coverage provider, and run tests. This method accepts string filters to match the test files - these are the same filters that [CLI supports](../../guide/filtering#cli). **WARNING** This method should not be called if `vitest.init()` is also invoked. Use `runTestSpecifications` or `rerunTestSpecifications` instead if you need to run tests after Vitest was inititalised. This method is called automatically by [`startVitest`](../guide/tests) if `config.mergeReports` and `config.standalone` are not set. ## init function init(): Promise Initialize reporters and the coverage provider. This method doesn't run any tests. If the `--watch` flag is provided, Vitest will still run changed tests even if this method was not called. Internally, this method is called only if [`--standalone`](../../guide/cli#standalone) flag is enabled. **WARNING** This method should not be called if `vitest.start()` is also invoked. This method is called automatically by [`startVitest`](../guide/tests) if `config.standalone` is set. ## getModuleSpecifications function getModuleSpecifications(moduleId: string): TestSpecification[] Returns a list of test specifications related to the module ID. The ID should already be resolved to an absolute file path. If ID doesn't match `include` or `includeSource` patterns, the returned array will be empty. This method can return already cached specifications based on the `moduleId` and `pool`. But note that [`project.createSpecification`](test- project#createspecification) always returns a new instance and it's not cached automatically. However, specifications are automatically cached when `runTestSpecifications` is called. **WARNING** As of Vitest 3, this method uses a cache to check if the file is a test. To make sure that the cache is not empty, call `globTestSpecifications` at least once. ## clearSpecificationsCache function clearSpecificationsCache(moduleId?: string): void Vitest automatically caches test specifications for each file when `globTestSpecifications` or `runTestSpecifications` is called. This method clears the cache for the given file or the whole cache alltogether depending on the first argument. ## runTestSpecifications function runTestSpecifications( specifications: TestSpecification[], allTestsRun = false ): Promise This method runs every test based on the received [specifications](test- specification). The second argument, `allTestsRun`, is used by the coverage provider to determine if it needs to instrument coverage on _every_ file in the root (this only matters if coverage is enabled and `coverage.all` is set to `true`). **WARNING** This method doesn't trigger `onWatcherRerun`, `onWatcherStart` and `onTestsRerun` callbacks. If you are rerunning tests based on the file change, consider using `rerunTestSpecifications` instead. ## rerunTestSpecifications function rerunTestSpecifications( specifications: TestSpecification[], allTestsRun = false ): Promise This method emits `reporter.onWatcherRerun` and `onTestsRerun` events, then it runs tests with `runTestSpecifications`. If there were no errors in the main process, it will emit `reporter.onWatcherStart` event. ## updateSnapshot function updateSnapshot(files?: string[]): Promise Update snapshots in specified files. If no files are provided, it will update files with failed tests and obsolete snapshots. ## collectTests function collectTests( specifications: TestSpecification[] ): Promise Execute test files without running test callbacks. `collectTests` returns unhandled errors and an array of [test modules](test-module). This method works exactly the same as `collect`, but you need to provide test specifications yourself. **WARNING** Note that Vitest doesn't use static analysis to collect tests. Vitest will run every test file in isolation, just like it runs regular tests. This makes this method very slow, unless you disable isolation before collecting tests. ## cancelCurrentRun function cancelCurrentRun(reason: CancelReason): Promise This method will gracefully cancel all ongoing tests. It will wait for started tests to finish running and will not run tests that were scheduled to run but haven't started yet. ## setGlobalTestNamePattern function setGlobalTestNamePattern(pattern: string | RegExp): void This methods overrides the global [test name pattern](../../config/index#testnamepattern). **WARNING** This method doesn't start running any tests. To run tests with updated pattern, call `runTestSpecifications`. ## resetGlobalTestNamePattern function resetGlobalTestNamePattern(): void This methods resets the [test name pattern](../../config/index#testnamepattern). It means Vitest won't skip any tests now. **WARNING** This method doesn't start running any tests. To run tests without a pattern, call `runTestSpecifications`. ## enableSnapshotUpdate function enableSnapshotUpdate(): void Enable the mode that allows updating snapshots when running tests. Every test that runs after this method is called will update snapshots. To disable the mode, call `resetSnapshotUpdate`. **WARNING** This method doesn't start running any tests. To update snapshots, run tests with `runTestSpecifications`. ## resetSnapshotUpdate function resetSnapshotUpdate(): void Disable the mode that allows updating snapshots when running tests. This method doesn't start running any tests. ## invalidateFile function invalidateFile(filepath: string): void This method invalidates the file in the cache of every project. It is mostly useful if you rely on your own watcher because Vite's cache persist in memory. **DANGER** If you disable Vitest's watcher but keep Vitest running, it is important to manually clear the cache with this method because there is no way to disable the cache. This method will also invalidate file's importers. ## import function import(moduleId: string): Promise Import a file using Vite module runner. The file will be transformed by Vite with the global config and executed in a separate context. Note that `moduleId` will be relative to the `config.root`. **DANGER** `project.import` reuses Vite's module graph, so importing the same module using a regular import will return a different module: import * as staticExample from './example.js' const dynamicExample = await vitest.import('./example.js') dynamicExample !== staticExample // ✅ **INFO** Internally, Vitest uses this method to import global setups, custom coverage providers, workspace file, and custom reporters, meaning all of them share the same module graph as long as they belong to the same Vite server. ## close function close(): Promise Closes all projects and their associated resources. This can only be called once; the closing promise is cached until the server restarts. ## exit function exit(force = false): Promise Closes all projects and exit the process. If `force` is set to `true`, the process will exit immediately after closing the projects. This method will also forcefuly call `process.exit()` if the process is still active after [`config.teardownTimeout`](../../config/index#teardowntimeout) milliseconds. ## shouldKeepServer function shouldKeepServer(): boolean This method will return `true` if the server should be kept running after the tests are done. This usually means that the `watch` mode was enabled. ## onServerRestart function onServerRestart(fn: OnServerRestartHandler): void Register a handler that will be called when the server is restarted due to a config change. ## onCancel function onCancel(fn: (reason: CancelReason) => Awaitable): void Register a handler that will be called when the test run is cancelled with `vitest.cancelCurrentRun`. ## onClose function onClose(fn: () => Awaitable): void Register a handler that will be called when the server is closed. ## onTestsRerun function onTestsRerun(fn: OnTestsRerunHandler): void Register a handler that will be called when the tests are rerunning. The tests can rerun when `rerunTestSpecifications` is called manually or when a file is changed and the built-in watcher schedules a rerun. ## onFilterWatchedSpecification function onFilterWatchedSpecification( fn: (specification: TestSpecification) => boolean ): void Register a handler that will be called when a file is changed. This callback should return `true` or `false`, indicating whether the test file needs to be rerun. With this method, you can hook into the default watcher logic to delay or discard tests that the user doesn't want to keep track of at the moment: const continuesTests: string[] = [] myCustomWrapper.onContinuesRunEnabled(testItem => continuesTests.push(item.fsPath) ) vitest.onFilterWatchedSpecification(specification => continuesTests.includes(specification.moduleId) ) Vitest can create different specifications for the same file depending on the `pool` or `locations` options, so do not rely on the reference. Vitest can also return cached specification from `vitest.getModuleSpecifications` \- the cache is based on the `moduleId` and `pool`. Note that [`project.createSpecification`](test-project#createspecification) always returns a new instance. # Running Tests **WARNING** This guide explains how to use the advanced API to run tests via a Node.js script. If you just want to [run tests](../../guide/index), you probably don't need this. It is primarily used by library authors. Breaking changes might not follow SemVer, please pin Vitest's version when using the experimental API. Vitest exposes two methods to initiate Vitest: * `startVitest` initiates Vitest, validates the packages are installed and runs tests immediately * `createVitest` only initiates Vitest and doesn't run any tests ## `startVitest` import { startVitest } from 'vitest/node' const vitest = await startVitest( 'test', [], // CLI filters {}, // override test config {}, // override Vite config {}, // custom Vitest options ) const testModules = vitest.state.getTestModules() for (const testModule of testModules) { console.log(testModule.moduleId, testModule.ok() ? 'passed' : 'failed') } **TIP** [`TestModule`](../api/test-module), [`TestSuite`](../api/test-suite) and [`TestCase`](../api/test-case) APIs are not experimental and follow SemVer since Vitest 2.1. ## `createVitest` Creates a [Vitest](../api/vitest) instances without running tests. `createVitest` method doesn't validate that required packages are installed. It also doesn't respect `config.standalone` or `config.mergeReports`. Vitest won't be closed automatically even if `watch` is disabled. import { createVitest } from 'vitest/node' const vitest = await createVitest( 'test', {}, // override test config {}, // override Vite config {}, // custom Vitest options ) // called when `vitest.cancelCurrentRun()` is invoked vitest.onCancel(() => {}) // called during `vitest.close()` call vitest.onClose(() => {}) // called when Vitest reruns test files vitest.onTestsRerun((files) => {}) try { // this will set process.exitCode to 1 if tests failed, // and won't close the process automatically await vitest.start(['my-filter']) } catch (err) { // this can throw // "FilesNotFoundError" if no files were found // "GitNotFoundError" with `--changed` and repository is not initialized } finally { await vitest.close() } If you intend to keep the `Vitest` instance, make sure to at least call [`init`](../api/vitest#init). This will initialise reporters and the coverage provider, but won't run any tests. It is also recommended to enable the `watch` mode even if you don't intend to use the Vitest watcher, but want to keep the instance running. Vitest relies on this flag for some of its features to work correctly in a continous process. After reporters are initialised, use [`runTestSpecifications`](../api/vitest#runtestspecifications) or [`rerunTestSpecifications`](../api/vitest#reruntestspecifications) to run tests if manual run is required: watcher.on('change', async (file) => { const specifications = vitest.getModuleSpecifications(file) if (specifications.length) { vitest.invalidateFile(file) // you can use runTestSpecifications if "reporter.onWatcher*" hooks // should not be invoked await vitest.rerunTestSpecifications(specifications) } }) **WARNING** The example above shows a potential usecase if you disable the default watcher behaviour. By default, Vitest already reruns tests if files change. Also note that `getModuleSpecifications` will not resolve test files unless they were already processed by `globTestSpecifications`. If the file was just created, use `project.matchesGlobPattern` instead: watcher.on('add', async (file) => { const specifications = [] for (const project of vitest.projects) { if (project.matchesGlobPattern(file)) { specifications.push(project.createSpecification(file)) } } if (specifications.length) { await vitest.rerunTestSpecifications(specifications) } }) In cases where you need to disable the watcher, you can pass down `server.watch: null` since Vite 5.3 or `server.watch: { ignored: ['*/*'] }` to a Vite config: await createVitest( 'test', {}, { plugins: [ { name: 'stop-watcher', async configureServer(server) { await server.watcher.close() } } ], server: { watch: null, }, } ) # Task Metadata **WARNING** Vitest exposes experimental private API. Breaking changes might not follow SemVer, please pin Vitest's version when using it. If you are developing a custom reporter or using Vitest Node.js API, you might find it useful to pass data from tests that are being executed in various contexts to your reporter or custom Vitest handler. To accomplish this, relying on the [test context](../guide/test-context) is not feasible since it cannot be serialized. However, with Vitest, you can utilize the `meta` property available on every task (suite or test) to share data between your tests and the Node.js process. It's important to note that this communication is one-way only, as the `meta` property can only be modified from within the test context. Any changes made within the Node.js context will not be visible in your tests. You can populate `meta` property on test context or inside `beforeAll`/`afterAll` hooks for suite tasks. afterAll((suite) => { suite.meta.done = true }) test('custom', ({ task }) => { task.meta.custom = 'some-custom-handler' }) Once a test is completed, Vitest will send a task including the result and `meta` to the Node.js process using RPC, and then report it in `onTestCaseResult` and other hooks that have access to tasks. To process this test case, you can utilize the `onTestCaseResult` method available in your reporter implementation: custom-reporter.js import type { Reporter, TestCase, TestModule } from 'vitest/node' export default { onTestCaseResult(testCase: TestCase) { // custom === 'some-custom-handler' ✅ const { custom } = testCase.meta() }, onTestRunEnd(testModule: TestModule) { testModule.meta().done === true testModule.children.at(0).meta().custom === 'some-custom-handler' } } satisfies Reporter **BEWARE** Vitest uses different methods to communicate with the Node.js process. * If Vitest runs tests inside worker threads, it will send data via [message port](https://developer.mozilla.org/en-US/docs/Web/API/MessagePort) * If Vitest uses child process, the data will be send as a serialized Buffer via [`process.send`](https://nodejs.org/api/process.html#processsendmessage-sendhandle-options-callback) API * If Vitest runs tests in the browser, the data will be stringified using [flatted](https://www.npmjs.com/package/flatted) package This property is also present on every test in the `json` reporter, so make sure that data can be serialized into JSON. Also, make sure you serialize [Error properties](https://developer.mozilla.org/en- US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#error_types) before you set them. You can also get this information from Vitest state when tests finished running: const vitest = await createVitest('test') const { testModules } = await vitest.start() const testModule = testModules[0] testModule.meta().done === true testModule.children.at(0).meta().custom === 'some-custom-handler' It's also possible to extend type definitions when using TypeScript: declare module 'vitest' { interface TaskMeta { done?: boolean custom?: string } } # Custom Pool **WARNING** This is an advanced and very low-level API. If you just want to [run tests](../guide/index), you probably don't need this. It is primarily used by library authors. Vitest runs tests in pools. By default, there are several pools: * `threads` to run tests using `node:worker_threads` (isolation is provided with a new worker context) * `forks` to run tests using `node:child_process` (isolation is provided with a new `child_process.fork` process) * `vmThreads` to run tests using `node:worker_threads` (but isolation is provided with `vm` module instead of a new worker context) * `browser` to run tests using browser providers * `typescript` to run typechecking on tests You can provide your own pool by specifying a file path: vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { // will run every file with a custom pool by default pool: './my-custom-pool.ts', // you can provide options using `poolOptions` object poolOptions: { myCustomPool: { customProperty: true, }, }, }, }) If you need to run tests in different pools, use the [workspace](../guide/workspace) feature: vitest.config.ts export default defineConfig({ test: { workspace: [ { extends: true, test: { pool: 'threads', }, }, ], }, }) **INFO** The `workspace` field was introduced in Vitest 3. To define a workspace in [Vitest 2](https://v2.vitest.dev/), create a separate `vitest.workspace.ts` file. ## API The file specified in `pool` option should export a function (can be async) that accepts `Vitest` interface as its first option. This function needs to return an object matching `ProcessPool` interface: import type { ProcessPool, TestSpecification } from 'vitest/node' export interface ProcessPool { name: string runTests: (files: TestSpecification[], invalidates?: string[]) => Promise collectTests: (files: TestSpecification[], invalidates?: string[]) => Promise close?: () => Promise } The function is called only once (unless the server config was updated), and it's generally a good idea to initialize everything you need for tests inside that function and reuse it when `runTests` is called. Vitest calls `runTest` when new tests are scheduled to run. It will not call it if `files` is empty. The first argument is an array of [TestSpecifications](api/test-specification). Files are sorted using [`sequencer`](../config/index#sequence-sequencer) before `runTests` is called. It's possible (but unlikely) to have the same file twice, but it will always have a different project - this is implemented via [`vitest.workspace.ts`](../guide/workspace) configuration. Vitest will wait until `runTests` is executed before finishing a run (i.e., it will emit [`onFinished`](reporters) only after `runTests` is resolved). If you are using a custom pool, you will have to provide test files and their results yourself - you can reference [`vitest.state`](https://github.com/vitest- dev/vitest/blob/main/packages/vitest/src/node/state.ts) for that (most important are `collectFiles` and `updateTasks`). Vitest uses `startTests` function from `@vitest/runner` package to do that. Vitest will call `collectTests` if `vitest.collect` is called or `vitest list` is invoked via a CLI command. It works the same way as `runTests`, but you don't have to run test callbacks, only report their tasks by calling `vitest.state.collectFiles(files)`. To communicate between different processes, you can create methods object using `createMethodsRPC` from `vitest/node`, and use any form of communication that you prefer. For example, to use WebSockets with `birpc` you can write something like this: import { createBirpc } from 'birpc' import { parse, stringify } from 'flatted' import { createMethodsRPC, TestProject } from 'vitest/node' function createRpc(project: TestProject, wss: WebSocketServer) { return createBirpc( createMethodsRPC(project), { post: msg => wss.send(msg), on: fn => wss.on('message', fn), serialize: stringify, deserialize: parse, }, ) } You can see a simple example of a pool made from scratch that doesn't run tests but marks them as collected in [pool/custom- pool.ts](https://github.com/vitest- dev/vitest/blob/main/test/cli/fixtures/custom-pool/pool/custom-pool.ts). # Extending Reporters **WARNING** This is an advanced API. If you just want to configure built-in reporters, read the ["Reporters"](../guide/reporters) guide. You can import reporters from `vitest/reporters` and extend them to create your custom reporters. ## Extending Built-in Reporters In general, you don't need to create your reporter from scratch. `vitest` comes with several default reporting programs that you can extend. import { DefaultReporter } from 'vitest/reporters' export default class MyDefaultReporter extends DefaultReporter { // do something } Of course, you can create your reporter from scratch. Just extend the `BaseReporter` class and implement the methods you need. And here is an example of a custom reporter: custom-reporter.js import { BaseReporter } from 'vitest/reporters' export default class CustomReporter extends BaseReporter { onCollected() { const files = this.ctx.state.getFiles(this.watchFilters) this.reportTestSummary(files) } } Or implement the `Reporter` interface: custom-reporter.js import type { Reporter } from 'vitest/node' export default class CustomReporter implements Reporter { onCollected() { // print something } } Then you can use your custom reporter in the `vitest.config.ts` file: vitest.config.ts import { defineConfig } from 'vitest/config' import CustomReporter from './custom-reporter.js' export default defineConfig({ test: { reporters: [new CustomReporter()], }, }) ## Reported Tasks Instead of using the tasks that reporters receive, it is recommended to use the Reported Tasks API instead. You can get access to this API by calling `vitest.state.getReportedEntity(runnerTask)`: import type { , , , } from 'vitest/node' class implements { private !: (: ) { this. = } (: []) { for (const of ) { // note that the old task implementation uses "file" instead of "module" const = this...() as for (const of .) { .('finished', ., .) } } } } ## Exported Reporters `vitest` comes with a few [built-in reporters](../guide/reporters) that you can use out of the box. ### Built-in reporters: 1. `BasicReporter` 2. `DefaultReporter` 3. `DotReporter` 4. `JsonReporter` 5. `VerboseReporter` 6. `TapReporter` 7. `JUnitReporter` 8. `TapFlatReporter` 9. `HangingProcessReporter` ### Base Abstract reporters: 1. `BaseReporter` ### Interface reporters: 1. `Reporter` # Runner API **WARNING** This is advanced API. If you just want to [run tests](../guide/index), you probably don't need this. It is primarily used by library authors. You can specify a path to your test runner with the `runner` option in your configuration file. This file should have a default export with a class constructor implementing these methods: export interface VitestRunner { /** * First thing that's getting called before actually collecting and running tests. */ onBeforeCollect?: (paths: string[]) => unknown /** * Called after collecting tests and before "onBeforeRun". */ onCollected?: (files: File[]) => unknown /** * Called when test runner should cancel next test runs. * Runner should listen for this method and mark tests and suites as skipped in * "onBeforeRunSuite" and "onBeforeRunTask" when called. */ onCancel?: (reason: CancelReason) => unknown /** * Called before running a single test. Doesn't have "result" yet. */ onBeforeRunTask?: (test: TaskPopulated) => unknown /** * Called before actually running the test function. Already has "result" with "state" and "startTime". */ onBeforeTryTask?: (test: TaskPopulated, options: { retry: number; repeats: number }) => unknown /** * Called after result and state are set. */ onAfterRunTask?: (test: TaskPopulated) => unknown /** * Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws. */ onAfterTryTask?: (test: TaskPopulated, options: { retry: number; repeats: number }) => unknown /** * Called before running a single suite. Doesn't have "result" yet. */ onBeforeRunSuite?: (suite: Suite) => unknown /** * Called after running a single suite. Has state and result. */ onAfterRunSuite?: (suite: Suite) => unknown /** * If defined, will be called instead of usual Vitest suite partition and handling. * "before" and "after" hooks will not be ignored. */ runSuite?: (suite: Suite) => Promise /** * If defined, will be called instead of usual Vitest handling. Useful, if you have your custom test function. * "before" and "after" hooks will not be ignored. */ runTask?: (test: TaskPopulated) => Promise /** * Called, when a task is updated. The same as "onTaskUpdate" in a reporter, but this is running in the same thread as tests. */ onTaskUpdate?: (task: [string, TaskResult | undefined, TaskMeta | undefined][]) => Promise /** * Called before running all tests in collected paths. */ onBeforeRunFiles?: (files: File[]) => unknown /** * Called right after running all tests in collected paths. */ onAfterRunFiles?: (files: File[]) => unknown /** * Called when new context for a test is defined. Useful, if you want to add custom properties to the context. * If you only want to define custom context with a runner, consider using "beforeAll" in "setupFiles" instead. */ extendTaskContext?: (context: TestContext) => TestContext /** * Called when certain files are imported. Can be called in two situations: to collect tests and to import setup files. */ importFile: (filepath: string, source: VitestRunnerImportSource) => unknown /** * Function that is called when the runner attempts to get the value when `test.extend` is used with `{ injected: true }` */ injectValue?: (key: string) => unknown /** * Publicly available configuration. */ config: VitestRunnerConfig /** * The name of the current pool. Can affect how stack trace is inferred on the server side. */ pool?: string } When initiating this class, Vitest passes down Vitest config, - you should expose it as a `config` property: runner.ts import type { RunnerTestFile } from 'vitest' import type { VitestRunner, VitestRunnerConfig } from 'vitest/suite' import { VitestTestRunner } from 'vitest/runners' class CustomRunner extends VitestTestRunner implements VitestRunner { public config: VitestRunnerConfig constructor(config: VitestRunnerConfig) { this.config = config } onAfterRunFiles(files: RunnerTestFile[]) { console.log('finished running', files) } } export default CustomRunner **WARNING** Vitest also injects an instance of `ViteNodeRunner` as `__vitest_executor` property. You can use it to process files in `importFile` method (this is default behavior of `TestRunner` and `BenchmarkRunner`). `ViteNodeRunner` exposes `executeId` method, which is used to import test files in a Vite-friendly environment. Meaning, it will resolve imports and transform file content at runtime so that Node can understand it: export default class Runner { async importFile(filepath: string) { await this.__vitest_executor.executeId(filepath) } } **WARNING** If you don't have a custom runner or didn't define `runTest` method, Vitest will try to retrieve a task automatically. If you didn't add a function with `setFn`, it will fail. **TIP** Snapshot support and some other features depend on the runner. If you don't want to lose it, you can extend your runner from `VitestTestRunner` imported from `vitest/runners`. It also exposes `BenchmarkNodeRunner`, if you want to extend benchmark functionality. ## Tasks **WARNING** The "Runner Tasks API" is experimental and should primarily be used only in the test runtime. Vitest also exposes the ["Reported Tasks API"](api/test- module), which should be preferred when working in the main thread (inside the reporter, for example). The team is currently discussing if "Runner Tasks" should be replaced by "Reported Tasks" in the future. Suites and tests are called `tasks` internally. Vitest runner initiates a `File` task before collecting any tests - this is a superset of `Suite` with a few additional properties. It is available on every task (including `File`) as a `file` property. interface File extends Suite { /** * The name of the pool that the file belongs to. * @default 'forks' */ pool?: string /** * The path to the file in UNIX format. */ filepath: string /** * The name of the workspace project the file belongs to. */ projectName: string | undefined /** * The time it took to collect all tests in the file. * This time also includes importing all the file dependencies. */ collectDuration?: number /** * The time it took to import the setup file. */ setupDuration?: number } Every suite has a `tasks` property that is populated during collection phase. It is useful to traverse the task tree from the top down. interface Suite extends TaskBase { type: 'suite' /** * File task. It's the root task of the file. */ file: File /** * An array of tasks that are part of the suite. */ tasks: Task[] } Every task has a `suite` property that references a suite it is located in. If `test` or `describe` are initiated at the top level, they will not have a `suite` property (it will **not** be equal to `file`!). `File` also never has a `suite` property. It is useful to travers the tasks from the bottom up. interface Test extends TaskBase { type: 'test' /** * Test context that will be passed to the test function. */ context: TestContext & ExtraContext /** * File task. It's the root task of the file. */ file: File /** * Whether the task was skipped by calling `t.skip()`. */ pending?: boolean /** * Whether the task should succeed if it fails. If the task fails, it will be marked as passed. */ fails?: boolean /** * Store promises (from async expects) to wait for them before finishing the test */ promises?: Promise[] } Every task can have a `result` field. Suites can only have this field if an error thrown within a suite callback or `beforeAll`/`afterAll` callbacks prevents them from collecting tests. Tests always have this field after their callbacks are called - the `state` and `errors` fields are present depending on the outcome. If an error was thrown in `beforeEach` or `afterEach` callbacks, the thrown error will be present in `task.result.errors`. export interface TaskResult { /** * State of the task. Inherits the `task.mode` during collection. * When the task has finished, it will be changed to `pass` or `fail`. * - **pass**: task ran successfully * - **fail**: task failed */ state: TaskState /** * Errors that occurred during the task execution. It is possible to have several errors * if `expect.soft()` failed multiple times. */ errors?: ErrorWithDiff[] /** * How long in milliseconds the task took to run. */ duration?: number /** * Time in milliseconds when the task started running. */ startTime?: number /** * Heap size in bytes after the task finished. * Only available if `logHeapUsage` option is set and `process.memoryUsage` is defined. */ heap?: number /** * State of related to this task hooks. Useful during reporting. */ hooks?: Partial> /** * The amount of times the task was retried. The task is retried only if it * failed and `retry` option is set. */ retryCount?: number /** * The amount of times the task was repeated. The task is repeated only if * `repeats` option is set. This number also contains `retryCount`. */ repeatCount?: number } ## Your Task Function Vitest exposes `createTaskCollector` utility to create your own `test` method. It behaves the same way as a test, but calls a custom method during collection. A task is an object that is part of a suite. It is automatically added to the current suite with a `suite.task` method: custom.js import { createTaskCollector, getCurrentSuite } from 'vitest/suite' export { afterAll, beforeAll, describe } from 'vitest' // this function will be called during collection phase: // don't call function handler here, add it to suite tasks // with "getCurrentSuite().task()" method // note: createTaskCollector provides support for "todo"/"each"/... export const myCustomTask = createTaskCollector( function (name, fn, timeout) { getCurrentSuite().task(name, { ...this, // so "todo"/"skip"/... is tracked correctly meta: { customPropertyToDifferentiateTask: true }, handler: fn, timeout, }) } ) tasks.test.js import { afterAll, beforeAll, describe, myCustomTask } from './custom.js' import { gardener } from './gardener.js' describe('take care of the garden', () => { beforeAll(() => { gardener.putWorkingClothes() }) myCustomTask('weed the grass', () => { gardener.weedTheGrass() }) myCustomTask.todo('mow the lawn', () => { gardener.mowerTheLawn() }) myCustomTask('water flowers', () => { gardener.waterFlowers() }) afterAll(() => { gardener.goHome() }) }) vitest ./garden/tasks.test.js # assertType **WARNING** During runtime this function doesn't do anything. To [enable typechecking](../guide/testing-types#run-typechecking), don't forget to pass down `--typecheck` flag. * **Type:** `(value: T): void` You can use this function as an alternative for [`expectTypeOf`](expect- typeof) to easily assert that the argument type is equal to the generic provided. import { assertType } from 'vitest' function concat(a: string, b: string): string function concat(a: number, b: number): number function concat(a: string | number, b: string | number): string | number assertType(concat('a', 'b')) assertType(concat(1, 2)) // @ts-expect-error wrong types assertType(concat('a', 2)) # assert Vitest reexports the `assert` method from [`chai`](https://www.chaijs.com/api/assert/) for verifying invariants. ## assert * **Type:** `(expression: any, message?: string) => asserts expression` Assert that the given `expression` is truthy, otherwise the assertion fails. import { assert, test } from 'vitest' test('assert', () => { assert('foo' !== 'bar', 'foo should not be equal to bar') }) ## fail * **Type:** * `(message?: string) => never` * `(actual: T, expected: T, message?: string, operator?: string) => never` Force an assertion failure. import { assert, test } from 'vitest' test('assert.fail', () => { assert.fail('error message on failure') assert.fail('foo', 'bar', 'foo is not bar', '===') }) ## isOk * **Type:** `(value: T, message?: string) => void` * **Alias** `ok` Assert that the given `value` is truthy. import { assert, test } from 'vitest' test('assert.isOk', () => { assert.isOk('foo', 'every truthy is ok') assert.isOk(false, 'this will fail since false is not truthy') }) ## isNotOk * **Type:** `(value: T, message?: string) => void` * **Alias** `notOk` Assert that the given `value` is falsy. import { assert, test } from 'vitest' test('assert.isNotOk', () => { assert.isNotOk('foo', 'this will fail, every truthy is not ok') assert.isNotOk(false, 'this will pass since false is falsy') }) ## equal * **Type:** `(actual: T, expected: T, message?: string) => void` Asserts non-strict equality (==) of `actual` and `expected`. import { assert, test } from 'vitest' test('assert.equal', () => { assert.equal(Math.sqrt(4), '2') }) ## notEqual * **Type:** `(actual: T, expected: T, message?: string) => void` Asserts non-strict inequality (!=) of `actual` and `expected`. import { assert, test } from 'vitest' test('assert.equal', () => { assert.notEqual(Math.sqrt(4), 3) }) ## strictEqual * **Type:** `(actual: T, expected: T, message?: string) => void` Asserts strict equality (===) of `actual` and `expected`. import { assert, test } from 'vitest' test('assert.strictEqual', () => { assert.strictEqual(Math.sqrt(4), 2) }) ## deepEqual * **Type:** `(actual: T, expected: T, message?: string) => void` Asserts that `actual` is deeply equal to `expected`. import { assert, test } from 'vitest' test('assert.deepEqual', () => { assert.deepEqual({ color: 'green' }, { color: 'green' }) }) ## notDeepEqual * **Type:** `(actual: T, expected: T, message?: string) => void` Assert that `actual` is not deeply equal to `expected`. import { assert, test } from 'vitest' test('assert.notDeepEqual', () => { assert.notDeepEqual({ color: 'green' }, { color: 'red' }) }) ## isAbove * **Type:** `(valueToCheck: number, valueToBeAbove: number, message?: string) => void` Assert that `valueToCheck` is strictly greater than (>) `valueToBeAbove`. import { assert, test } from 'vitest' test('assert.isAbove', () => { assert.isAbove(5, 2, '5 is strictly greater than 2') }) ## isAtLeast * **Type:** `(valueToCheck: number, valueToBeAtLeast: number, message?: string) => void` Assert that `valueToCheck` is greater than or equal to (>=) `valueToBeAtLeast`. import { assert, test } from 'vitest' test('assert.isAtLeast', () => { assert.isAtLeast(5, 2, '5 is greater or equal to 2') assert.isAtLeast(3, 3, '3 is greater or equal to 3') }) ## isBelow * **Type:** `(valueToCheck: number, valueToBeBelow: number, message?: string) => void` Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow`. import { assert, test } from 'vitest' test('assert.isBelow', () => { assert.isBelow(3, 6, '3 is strictly less than 6') }) ## isAtMost * **Type:** `(valueToCheck: number, valueToBeAtMost: number, message?: string) => void` Asserts `valueToCheck` is less than or equal to (<=) `valueToBeAtMost`. import { assert, test } from 'vitest' test('assert.isAtMost', () => { assert.isAtMost(3, 6, '3 is less than or equal to 6') assert.isAtMost(4, 4, '4 is less than or equal to 4') }) ## isTrue * **Type:** `(value: T, message?: string) => void` Asserts that `value` is true. import { assert, test } from 'vitest' const testPassed = true test('assert.isTrue', () => { assert.isTrue(testPassed) }) ## isNotTrue * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not true. import { assert, test } from 'vitest' const testPassed = 'ok' test('assert.isNotTrue', () => { assert.isNotTrue(testPassed) }) ## isFalse * **Type:** `(value: T, message?: string) => void` Asserts that `value` is false. import { assert, test } from 'vitest' const testPassed = false test('assert.isFalse', () => { assert.isFalse(testPassed) }) ## isNotFalse * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not false. import { assert, test } from 'vitest' const testPassed = 'no' test('assert.isNotFalse', () => { assert.isNotFalse(testPassed) }) ## isNull * **Type:** `(value: T, message?: string) => void` Asserts that `value` is null. import { assert, test } from 'vitest' const error = null test('assert.isNull', () => { assert.isNull(error, 'error is null') }) ## isNotNull * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not null. import { assert, test } from 'vitest' const error = { message: 'error was occurred' } test('assert.isNotNull', () => { assert.isNotNull(error, 'error is not null but object') }) ## isNaN * **Type:** `(value: T, message?: string) => void` Asserts that `value` is NaN. import { assert, test } from 'vitest' const calculation = 1 * 'vitest' test('assert.isNaN', () => { assert.isNaN(calculation, '1 * "vitest" is NaN') }) ## isNotNaN * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not NaN. import { assert, test } from 'vitest' const calculation = 1 * 2 test('assert.isNotNaN', () => { assert.isNotNaN(calculation, '1 * 2 is Not NaN but 2') }) ## exists * **Type:** `(value: T, message?: string) => void` Asserts that `value` is neither null nor undefined. import { assert, test } from 'vitest' const name = 'foo' test('assert.exists', () => { assert.exists(name, 'foo is neither null nor undefined') }) ## notExists * **Type:** `(value: T, message?: string) => void` Asserts that `value` is either null nor undefined. import { assert, test } from 'vitest' const foo = null const bar = undefined test('assert.notExists', () => { assert.notExists(foo, 'foo is null so not exist') assert.notExists(bar, 'bar is undefined so not exist') }) ## isUndefined * **Type:** `(value: T, message?: string) => void` Asserts that `value` is undefined. import { assert, test } from 'vitest' const name = undefined test('assert.isUndefined', () => { assert.isUndefined(name, 'name is undefined') }) ## isDefined * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not undefined. import { assert, test } from 'vitest' const name = 'foo' test('assert.isDefined', () => { assert.isDefined(name, 'name is not undefined') }) ## isFunction * **Type:** `(value: T, message?: string) => void` * **Alias:** `isCallable` Asserts that `value` is a function. import { assert, test } from 'vitest' function name() { return 'foo' }; test('assert.isFunction', () => { assert.isFunction(name, 'name is function') }) ## isNotFunction * **Type:** `(value: T, message?: string) => void` * **Alias:** `isNotCallable` Asserts that `value` is not a function. import { assert, test } from 'vitest' const name = 'foo' test('assert.isNotFunction', () => { assert.isNotFunction(name, 'name is not function but string') }) ## isObject * **Type:** `(value: T, message?: string) => void` Asserts that `value` is an object of type Object (as revealed by Object.prototype.toString). The assertion does not match subclassed objects. import { assert, test } from 'vitest' const someThing = { color: 'red', shape: 'circle' } test('assert.isObject', () => { assert.isObject(someThing, 'someThing is object') }) ## isNotObject * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not an object of type Object (as revealed by Object.prototype.toString). The assertion does not match subclassed objects. import { assert, test } from 'vitest' const someThing = 'redCircle' test('assert.isNotObject', () => { assert.isNotObject(someThing, 'someThing is not object but string') }) ## isArray * **Type:** `(value: T, message?: string) => void` Asserts that `value` is an array. import { assert, test } from 'vitest' const color = ['red', 'green', 'yellow'] test('assert.isArray', () => { assert.isArray(color, 'color is array') }) ## isNotArray * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not an array. import { assert, test } from 'vitest' const color = 'red' test('assert.isNotArray', () => { assert.isNotArray(color, 'color is not array but string') }) ## isString * **Type:** `(value: T, message?: string) => void` Asserts that `value` is a string. import { assert, test } from 'vitest' const color = 'red' test('assert.isString', () => { assert.isString(color, 'color is string') }) ## isNotString * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not a string. import { assert, test } from 'vitest' const color = ['red', 'green', 'yellow'] test('assert.isNotString', () => { assert.isNotString(color, 'color is not string but array') }) ## isNumber * **Type:** `(value: T, message?: string) => void` Asserts that `value` is a number. import { assert, test } from 'vitest' const colors = 3 test('assert.isNumber', () => { assert.isNumber(colors, 'colors is number') }) ## isNotNumber * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not a number. import { assert, test } from 'vitest' const colors = '3 colors' test('assert.isNotNumber', () => { assert.isNotNumber(colors, 'colors is not number but strings') }) ## isFinite * **Type:** `(value: T, message?: string) => void` Asserts that `value` is a finite number (not NaN, Infinity). import { assert, test } from 'vitest' const colors = 3 test('assert.isFinite', () => { assert.isFinite(colors, 'colors is number not NaN or Infinity') }) ## isBoolean * **Type:** `(value: T, message?: string) => void` Asserts that `value` is a boolean. import { assert, test } from 'vitest' const isReady = true test('assert.isBoolean', () => { assert.isBoolean(isReady, 'isReady is a boolean') }) ## isNotBoolean * **Type:** `(value: T, message?: string) => void` Asserts that `value` is not a boolean. import { assert, test } from 'vitest' const isReady = 'sure' test('assert.isBoolean', () => { assert.isBoolean(isReady, 'isReady is not a boolean but string') }) ## typeOf * **Type:** `(value: T, name: string, message?: string) => void` Asserts that `value`’s type is `name`, as determined by Object.prototype.toString. import { assert, test } from 'vitest' test('assert.typeOf', () => { assert.typeOf({ color: 'red' }, 'object', 'we have an object') assert.typeOf(['red', 'green'], 'array', 'we have an array') assert.typeOf('red', 'string', 'we have a string') assert.typeOf(/red/, 'regexp', 'we have a regular expression') assert.typeOf(null, 'null', 'we have a null') assert.typeOf(undefined, 'undefined', 'we have an undefined') }) ## notTypeOf * **Type:** `(value: T, name: string, message?: string) => void` Asserts that `value`’s type is not `name`, as determined by Object.prototype.toString. import { assert, test } from 'vitest' test('assert.notTypeOf', () => { assert.notTypeOf('red', 'number', '"red" is not a number') }) ## instanceOf * **Type:** `(value: T, constructor: Function, message?: string) => void` Asserts that `value` is an instance of `constructor`. import { assert, test } from 'vitest' function Person(name) { this.name = name } const foo = new Person('foo') class Tea { constructor(name) { this.name = name } } const coffee = new Tea('coffee') test('assert.instanceOf', () => { assert.instanceOf(foo, Person, 'foo is an instance of Person') assert.instanceOf(coffee, Tea, 'coffee is an instance of Tea') }) ## notInstanceOf * **Type:** `(value: T, constructor: Function, message?: string) => void` Asserts that `value` is not an instance of `constructor`. import { assert, test } from 'vitest' function Person(name) { this.name = name } const foo = new Person('foo') class Tea { constructor(name) { this.name = name } } const coffee = new Tea('coffee') test('assert.instanceOf', () => { assert.instanceOf(foo, Tea, 'foo is not an instance of Tea') }) ## include * **Type:** * `(haystack: string, needle: string, message?: string) => void` * `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` * `(haystack: WeakSet, needle: T, message?: string) => void` * `(haystack: T, needle: Partial, message?: string) => void` Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a value in an array, a substring in a string, or a subset of properties in an object. import { assert, test } from 'vitest' test('assert.include', () => { assert.include([1, 2, 3], 2, 'array contains value') assert.include('foobar', 'foo', 'string contains substring') assert.include({ foo: 'bar', hello: 'universe' }, { foo: 'bar' }, 'object contains property') }) ## notInclude * **Type:** * `(haystack: string, needle: string, message?: string) => void` * `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` * `(haystack: WeakSet, needle: T, message?: string) => void` * `(haystack: T, needle: Partial, message?: string) => void` Asserts that `haystack` does not include `needle`. It can be used to assert the absence of a value in an array, a substring in a string, or a subset of properties in an object. import { assert, test } from 'vitest' test('assert.notInclude', () => { assert.notInclude([1, 2, 3], 4, 'array doesn\'t contain 4') assert.notInclude('foobar', 'baz', 'foobar doesn\'t contain baz') assert.notInclude({ foo: 'bar', hello: 'universe' }, { foo: 'baz' }, 'object doesn\'t contain property') }) ## deepInclude * **Type:** * `(haystack: string, needle: string, message?: string) => void` * `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` * `(haystack: T, needle: T extends WeakSet ? never : Partial, message?: string) => void` Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a value in an array or a subset of properties in an object. Deep equality is used. import { assert, test } from 'vitest' const obj1 = { a: 1 } const obj2 = { b: 2 } test('assert.deepInclude', () => { assert.deepInclude([obj1, obj2], { a: 1 }) assert.deepInclude({ foo: obj1, bar: obj2 }, { foo: { a: 1 } }) }) ## notDeepInclude * **Type:** * `(haystack: string, needle: string, message?: string) => void` * `(haystack: readonly T[] | ReadonlySet | ReadonlyMap, needle: T, message?: string) => void` * `(haystack: T, needle: T extends WeakSet ? never : Partial, message?: string) => void` Asserts that `haystack` does not include `needle`. It can be used to assert the absence of a value in an array or a subset of properties in an object. Deep equality is used. import { assert, test } from 'vitest' const obj1 = { a: 1 } const obj2 = { b: 2 } test('assert.notDeepInclude', () => { assert.notDeepInclude([obj1, obj2], { a: 10 }) assert.notDeepInclude({ foo: obj1, bar: obj2 }, { foo: { a: 10 } }) }) ## nestedInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object. Enables the use of dot- and bracket- notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. import { assert, test } from 'vitest' test('assert.nestedInclude', () => { assert.nestedInclude({ '.a': { b: 'x' } }, { '\\.a.[b]': 'x' }) assert.nestedInclude({ a: { '[b]': 'x' } }, { 'a.\\[b\\]': 'x' }) }) ## notNestedInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` does not include `needle`. Can be used to assert the inclusion of a subset of properties in an object. Enables the use of dot- and bracket-notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. import { assert, test } from 'vitest' test('assert.nestedInclude', () => { assert.notNestedInclude({ '.a': { b: 'x' } }, { '\\.a.b': 'y' }) assert.notNestedInclude({ a: { '[b]': 'x' } }, { 'a.\\[b\\]': 'y' }) }) ## deepNestedInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object while checking for deep equality. Enables the use of dot- and bracket-notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. import { assert, test } from 'vitest' test('assert.deepNestedInclude', () => { assert.deepNestedInclude({ a: { b: [{ x: 1 }] } }, { 'a.b[0]': { x: 1 } }) assert.deepNestedInclude({ '.a': { '[b]': { x: 1 } } }, { '\\.a.\\[b\\]': { x: 1 } }) }) ## notDeepNestedInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` not includes `needle`. Can be used to assert the absence of a subset of properties in an object while checking for deep equality. Enables the use of dot- and bracket-notation for referencing nested properties. ‘[]’ and ‘.’ in property names can be escaped using double backslashes. import { assert, test } from 'vitest' test('assert.notDeepNestedInclude', () => { assert.notDeepNestedInclude({ a: { b: [{ x: 1 }] } }, { 'a.b[0]': { y: 1 } }) assert.notDeepNestedInclude({ '.a': { '[b]': { x: 1 } } }, { '\\.a.\\[b\\]': { y: 2 } }) }) ## ownInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object while ignoring inherited properties. import { assert, test } from 'vitest' test('assert.ownInclude', () => { assert.ownInclude({ a: 1 }, { a: 1 }) }) ## notOwnInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` includes `needle`. Can be used to assert the absence of a subset of properties in an object while ignoring inherited properties. import { assert, test } from 'vitest' const obj1 = { b: 2 } const obj2 = object.create(obj1) obj2.a = 1 test('assert.notOwnInclude', () => { assert.notOwnInclude(obj2, { b: 2 }) }) ## deepOwnInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` includes `needle`. Can be used to assert the inclusion of a subset of properties in an object while ignoring inherited properties and checking for deep equality. import { assert, test } from 'vitest' test('assert.deepOwnInclude', () => { assert.deepOwnInclude({ a: { b: 2 } }, { a: { b: 2 } }) }) ## notDeepOwnInclude * **Type:** `(haystack: any, needle: any, message?: string) => void` Asserts that `haystack` not includes `needle`. Can be used to assert the absence of a subset of properties in an object while ignoring inherited properties and checking for deep equality. import { assert, test } from 'vitest' test('assert.notDeepOwnInclude', () => { assert.notDeepOwnInclude({ a: { b: 2 } }, { a: { c: 3 } }) }) ## match * **Type:** `(value: string, regexp: RegExp, message?: string) => void` Asserts that `value` matches the regular expression `regexp`. import { assert, test } from 'vitest' test('assert.match', () => { assert.match('foobar', /^foo/, 'regexp matches') }) ## notMatch * **Type:** `(value: string, regexp: RegExp, message?: string) => void` Asserts that `value` does not matches the regular expression `regexp`. import { assert, test } from 'vitest' test('assert.notMatch', () => { assert.notMatch('foobar', /^foo/, 'regexp does not match') }) ## property * **Type:** `(object: T, property: string, message?: string) => void` Asserts that `object` has a direct or inherited property named by `property` import { assert, test } from 'vitest' test('assert.property', () => { assert.property({ tea: { green: 'matcha' } }, 'tea') assert.property({ tea: { green: 'matcha' } }, 'toString') }) ## notProperty * **Type:** `(object: T, property: string, message?: string) => void` Asserts that `object` does not have a direct or inherited property named by `property` import { assert, test } from 'vitest' test('assert.notProperty', () => { assert.notProperty({ tea: { green: 'matcha' } }, 'coffee') }) ## propertyVal * **Type:** `(object: T, property: string, value: V, message?: string) => void` Asserts that `object` has a direct or inherited property named by `property` with a value given by `value`. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.notPropertyVal', () => { assert.propertyVal({ tea: 'is good' }, 'tea', 'is good') }) ## notPropertyVal * **Type:** `(object: T, property: string, value: V, message?: string) => void` Asserts that `object` does not have a direct or inherited property named by `property` with a value given by `value`. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.notPropertyVal', () => { assert.notPropertyVal({ tea: 'is good' }, 'tea', 'is bad') assert.notPropertyVal({ tea: 'is good' }, 'coffee', 'is good') }) ## deepPropertyVal * **Type:** `(object: T, property: string, value: V, message?: string) => void` Asserts that `object` has a direct or inherited property named by `property` with a value given by `value`. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.deepPropertyVal', () => { assert.deepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'matcha' }) }) ## notDeepPropertyVal * **Type:** `(object: T, property: string, value: V, message?: string) => void` Asserts that `object` does not have a direct or inherited property named by `property` with a value given by `value`. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.deepPropertyVal', () => { assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { black: 'matcha' }) assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'tea', { green: 'oolong' }) assert.notDeepPropertyVal({ tea: { green: 'matcha' } }, 'coffee', { green: 'matcha' }) }) ## nestedProperty * **Type:** `(object: T, property: string, message?: string) => void` Asserts that `object` has a direct or inherited property named by `property`, which can be a string using dot- and bracket-notation for nested reference. import { assert, test } from 'vitest' test('assert.deepPropertyVal', () => { assert.nestedProperty({ tea: { green: 'matcha' } }, 'tea.green') }) ## notNestedProperty * **Type:** `(object: T, property: string, message?: string) => void` Asserts that `object` does not have a direct or inherited property named by `property`, which can be a string using dot- and bracket-notation for nested reference. import { assert, test } from 'vitest' test('assert.deepPropertyVal', () => { assert.notNestedProperty({ tea: { green: 'matcha' } }, 'tea.oolong') }) ## nestedPropertyVal * **Type:** `(object: T, property: string, value: any, message?: string) => void` Asserts that `object` has a property named by `property` with value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.nestedPropertyVal', () => { assert.nestedPropertyVal({ tea: { green: 'matcha' } }, 'tea.green', 'matcha') }) ## notNestedPropertyVal * **Type:** `(object: T, property: string, value: any, message?: string) => void` Asserts that `object` does not have a property named by `property` with value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.notNestedPropertyVal', () => { assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'tea.green', 'konacha') assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'coffee.green', 'matcha') }) ## deepNestedPropertyVal * **Type:** `(object: T, property: string, value: any, message?: string) => void` Asserts that `object` has a property named by `property` with a value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a deep equality check (===). import { assert, test } from 'vitest' test('assert.notNestedPropertyVal', () => { assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'tea.green', 'konacha') assert.notNestedPropertyVal({ tea: { green: 'matcha' } }, 'coffee.green', 'matcha') }) ## notDeepNestedPropertyVal * **Type:** `(object: T, property: string, value: any, message?: string) => void` Asserts that `object` does not have a property named by `property` with value given by `value`. `property` can use dot- and bracket-notation for nested reference. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.notDeepNestedPropertyVal', () => { assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { oolong: 'yum' }) assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.green', { matcha: 'yuck' }) assert.notDeepNestedPropertyVal({ tea: { green: { matcha: 'yum' } } }, 'tea.black', { matcha: 'yum' }) }) ## lengthOf * **Type:** `(object: T, length: number, message?: string) => void` Asserts that `object` has a `length` or `size` with the expected value. import { assert, test } from 'vitest' test('assert.lengthOf', () => { assert.lengthOf([1, 2, 3], 3, 'array has length of 3') assert.lengthOf('foobar', 6, 'string has length of 6') assert.lengthOf(new Set([1, 2, 3]), 3, 'set has size of 3') assert.lengthOf(new Map([['a', 1], ['b', 2], ['c', 3]]), 3, 'map has size of 3') }) ## hasAnyKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` has at least one of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.hasAnyKeys', () => { assert.hasAnyKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'iDontExist', 'baz']) assert.hasAnyKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, iDontExist: 99, baz: 1337 }) assert.hasAnyKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value']]), [{ foo: 1 }, 'key']) assert.hasAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey']), [{ foo: 'bar' }, 'anotherKey']) }) ## hasAllKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` has all and only all of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.hasAllKeys', () => { assert.hasAllKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'bar', 'baz']) assert.hasAllKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, bar: 99, baz: 1337 }) assert.hasAllKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value']]), [{ foo: 1 }, 'key']) assert.hasAllKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }, 'anotherKey'])) }) ## containsAllKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` has all of the `keys` provided but may have more keys not listed. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.containsAllKeys', () => { assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'baz']) assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, ['foo', 'bar', 'baz']) assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, baz: 1337 }) assert.containsAllKeys({ foo: 1, bar: 2, baz: 3 }, { foo: 30, bar: 99, baz: 1337 }) assert.containsAllKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value']]), [{ foo: 1 }]) assert.containsAllKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value']]), [{ foo: 1 }, 'key']) assert.containsAllKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }])) assert.containsAllKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ foo: 'bar' }, 'anotherKey'])) }) ## doesNotHaveAnyKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` has none of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.doesNotHaveAnyKeys', () => { assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, ['one', 'two', 'example']) assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, { one: 1, two: 2, example: 'foo' }) assert.doesNotHaveAnyKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value']]), [{ one: 'two' }, 'example']) assert.doesNotHaveAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey'], [{ one: 'two' }, 'example'])) }) ## doesNotHaveAllKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` does not have at least one of the `keys` provided. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.hasAnyKeys', () => { assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, ['one', 'two', 'example']) assert.doesNotHaveAnyKeys({ foo: 1, bar: 2, baz: 3 }, { one: 1, two: 2, example: 'foo' }) assert.doesNotHaveAnyKeys(new Map([[{ foo: 1 }, 'bar'], ['key', 'value']]), [{ one: 'two' }, 'example']) assert.doesNotHaveAnyKeys(new Set([{ foo: 'bar' }, 'anotherKey']), [{ one: 'two' }, 'example']) }) ## hasAnyDeepKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` has at least one of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.hasAnyDeepKeys', () => { assert.hasAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2]]), { one: 'one' }) assert.hasAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2]]), [{ one: 'one' }, { two: 'two' }]) assert.hasAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo']]), [{ one: 'one' }, { two: 'two' }]) assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { one: 'one' }) assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { three: 'three' }]) assert.hasAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { two: 'two' }]) }) ## hasAllDeepKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` has all and only all of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.hasAnyDeepKeys', () => { assert.hasAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne']]), { one: 'one' }) assert.hasAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo']]), [{ one: 'one' }, { two: 'two' }]) assert.hasAllDeepKeys(new Set([{ one: 'one' }]), { one: 'one' }) assert.hasAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { two: 'two' }]) }) ## containsAllDeepKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` contains all of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.containsAllDeepKeys', () => { assert.containsAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2]]), { one: 'one' }) assert.containsAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo']]), [{ one: 'one' }, { two: 'two' }]) assert.containsAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { one: 'one' }) assert.containsAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { two: 'two' }]) }) ## doesNotHaveAnyDeepKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` has none of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.doesNotHaveAnyDeepKeys', () => { assert.doesNotHaveAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2]]), { thisDoesNot: 'exist' }) assert.doesNotHaveAnyDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo']]), [{ twenty: 'twenty' }, { fifty: 'fifty' }]) assert.doesNotHaveAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { twenty: 'twenty' }) assert.doesNotHaveAnyDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ twenty: 'twenty' }, { fifty: 'fifty' }]) }) ## doesNotHaveAllDeepKeys * **Type:** `(object: T, keys: Array | { [key: string]: any }, message?: string) => void` Asserts that `object` does not have at least one of the `keys` provided. Since Sets and Maps can have objects as keys you can use this assertion to perform a deep comparison. You can also provide a single object instead of a keys array and its keys will be used as the expected set of keys. import { assert, test } from 'vitest' test('assert.doesNotHaveAllDeepKeys', () => { assert.doesNotHaveAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [1, 2]]), { thisDoesNot: 'exist' }) assert.doesNotHaveAllDeepKeys(new Map([[{ one: 'one' }, 'valueOne'], [{ two: 'two' }, 'valueTwo']]), [{ twenty: 'twenty' }, { one: 'one' }]) assert.doesNotHaveAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), { twenty: 'twenty' }) assert.doesNotHaveAllDeepKeys(new Set([{ one: 'one' }, { two: 'two' }]), [{ one: 'one' }, { fifty: 'fifty' }]) }) ## throws * **Type:** * `(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string) => void` * `(fn: () => void, errorLike?: ErrorConstructor | Error | null, errMsgMatcher?: RegExp | string | null, message?: string) => void` * **Alias:** * `throw` * `Throw` If `errorLike` is an Error constructor, asserts that `fn` will throw an error that is an instance of `errorLike`. If errorLike is an Error instance, asserts that the error thrown is the same instance as `errorLike`. If `errMsgMatcher` is provided, it also asserts that the error thrown will have a message matching `errMsgMatcher`. import { assert, test } from 'vitest' test('assert.throws', () => { assert.throws(fn, 'Error thrown must have this msg') assert.throws(fn, /Error thrown must have a msg that matches this/) assert.throws(fn, ReferenceError) assert.throws(fn, errorInstance) assert.throws(fn, ReferenceError, 'Error thrown must be a ReferenceError and have this msg') assert.throws(fn, errorInstance, 'Error thrown must be the same errorInstance and have this msg') assert.throws(fn, ReferenceError, /Error thrown must be a ReferenceError and match this/) assert.throws(fn, errorInstance, /Error thrown must be the same errorInstance and match this/) }) ## doesNotThrow * **Type:** `(fn: () => void, errMsgMatcher?: RegExp | string, ignored?: any, message?: string) => void` * **Type:** `(fn: () => void, errorLike?: ErrorConstructor | Error | null, errMsgMatcher?: RegExp | string | null, message?: string) => void` If `errorLike` is an Error constructor, asserts that `fn` will not throw an error that is an instance of `errorLike`. If errorLike is an Error instance, asserts that the error thrown is not the same instance as `errorLike`. If `errMsgMatcher` is provided, it also asserts that the error thrown will not have a message matching `errMsgMatcher`. import { assert, test } from 'vitest' test('assert.doesNotThrow', () => { assert.doesNotThrow(fn, 'Any Error thrown must not have this message') assert.doesNotThrow(fn, /Any Error thrown must not match this/) assert.doesNotThrow(fn, Error) assert.doesNotThrow(fn, errorInstance) assert.doesNotThrow(fn, Error, 'Error must not have this message') assert.doesNotThrow(fn, errorInstance, 'Error must not have this message') assert.doesNotThrow(fn, Error, /Error must not match this/) assert.doesNotThrow(fn, errorInstance, /Error must not match this/) }) ## operator * **Type:** `(val1: OperatorComparable, operator: Operator, val2: OperatorComparable, message?: string) => void` Compare `val1` and `val2` using `operator`. import { assert, test } from 'vitest' test('assert.operator', () => { assert.operator(1, '<', 2, 'everything is ok') }) ## closeTo * **Type:** `(actual: number, expected: number, delta: number, message?: string) => void` * **Alias:** `approximately` Asserts that the `actual` is equal `expected`, to within a +/- `delta` range. import { assert, test } from 'vitest' test('assert.closeTo', () => { assert.closeTo(1.5, 1, 0.5, 'numbers are close') }) ## sameMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` have the same members in any order. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.sameMembers', () => { assert.sameMembers([1, 2, 3], [2, 1, 3], 'same members') }) ## notSameMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` don't have the same members in any order. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.sameMembers', () => { assert.notSameMembers([1, 2, 3], [5, 1, 3], 'not same members') }) ## sameDeepMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` have the same members in any order. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.sameDeepMembers', () => { assert.sameDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members') }) ## notSameDeepMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` don’t have the same members in any order. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.sameDeepMembers', () => { assert.sameDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members') }) ## sameOrderedMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` have the same members in the same order. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.sameOrderedMembers', () => { assert.sameOrderedMembers([1, 2, 3], [1, 2, 3], 'same ordered members') }) ## notSameOrderedMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` have the same members in the same order. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.notSameOrderedMembers', () => { assert.notSameOrderedMembers([1, 2, 3], [2, 1, 3], 'not same ordered members') }) ## sameDeepOrderedMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` have the same members in the same order. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.sameDeepOrderedMembers', () => { assert.sameDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { b: 2 }, { c: 3 }], 'same deep ordered members') }) ## notSameDeepOrderedMembers * **Type:** `(set1: T[], set2: T[], message?: string) => void` Asserts that `set1` and `set2` don’t have the same members in the same order. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.notSameDeepOrderedMembers', () => { assert.notSameDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { b: 2 }, { z: 5 }], 'not same deep ordered members') assert.notSameDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { c: 3 }], 'not same deep ordered members') }) ## includeMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` is included in `superset` in any order. Uses a strict equality check (===). Duplicates are ignored. import { assert, test } from 'vitest' test('assert.includeMembers', () => { assert.includeMembers([1, 2, 3], [2, 1, 2], 'include members') }) ## notIncludeMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` isn't included in `superset` in any order. Uses a strict equality check (===). Duplicates are ignored. import { assert, test } from 'vitest' test('assert.notIncludeMembers', () => { assert.notIncludeMembers([1, 2, 3], [5, 1], 'not include members') }) ## includeDeepMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` is included in `superset` in any order. Uses a deep equality check. Duplicates are ignored. import { assert, test } from 'vitest' test('assert.includeDeepMembers', () => { assert.includeDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }, { b: 2 }], 'include deep members') }) ## notIncludeDeepMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` isn’t included in `superset` in any order. Uses a deep equality check. Duplicates are ignored. import { assert, test } from 'vitest' test('assert.notIncludeDeepMembers', () => { assert.notIncludeDeepMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { f: 5 }], 'not include deep members') }) ## includeOrderedMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` is included in `superset` in the same order beginning with the first element in `superset`. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.includeOrderedMembers', () => { assert.includeOrderedMembers([1, 2, 3], [1, 2], 'include ordered members') }) ## notIncludeOrderedMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` isn't included in `superset` in the same order beginning with the first element in `superset`. Uses a strict equality check (===). import { assert, test } from 'vitest' test('assert.notIncludeOrderedMembers', () => { assert.notIncludeOrderedMembers([1, 2, 3], [2, 1], 'not include ordered members') assert.notIncludeOrderedMembers([1, 2, 3], [2, 3], 'not include ordered members') }) ## includeDeepOrderedMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` is included in `superset` in the same order beginning with the first element in `superset`. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.includeDeepOrderedMembers', () => { assert.includeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { b: 2 }], 'include deep ordered members') }) ## notIncludeDeepOrderedMembers * **Type:** `(superset: T[], subset: T[], message?: string) => void` Asserts that `subset` isn’t included in `superset` in the same order beginning with the first element in superset. Uses a deep equality check. import { assert, test } from 'vitest' test('assert.includeDeepOrderedMembers', () => { assert.notIncludeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ a: 1 }, { f: 5 }], 'not include deep ordered members') assert.notIncludeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { a: 1 }], 'not include deep ordered members') assert.notIncludeDeepOrderedMembers([{ a: 1 }, { b: 2 }, { c: 3 }], [{ b: 2 }, { c: 3 }], 'not include deep ordered members') }) ## oneOf * **Type:** `(inList: T, list: T[], message?: string) => void` Asserts that non-object, non-array value `inList` appears in the flat array `list`. import { assert, test } from 'vitest' test('assert.oneOf', () => { assert.oneOf(1, [2, 1], 'Not found in list') }) ## changes * **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` Asserts that a `modifier` changes the `object` of a `property`. import { assert, test } from 'vitest' test('assert.changes', () => { const obj = { val: 10 } function fn() { obj.val = 22 }; assert.changes(fn, obj, 'val') }) ## changesBy * **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` Asserts that a `modifier` changes the `object` of a `property` by a `change`. import { assert, test } from 'vitest' test('assert.changesBy', () => { const obj = { val: 10 } function fn() { obj.val += 2 }; assert.changesBy(fn, obj, 'val', 2) }) ## doesNotChange * **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` Asserts that a `modifier` does not changes the `object` of a `property`. import { assert, test } from 'vitest' test('assert.doesNotChange', () => { const obj = { val: 10 } function fn() { obj.val += 2 }; assert.doesNotChange(fn, obj, 'val', 2) }) ## changesButNotBy * **Type:** `(modifier: Function, object: T, property: string, change:number, message?: string) => void` Asserts that a `modifier` does not change the `object` of a `property` or of a `modifier` return value by a `change`. import { assert, test } from 'vitest' test('assert.changesButNotBy', () => { const obj = { val: 10 } function fn() { obj.val += 10 }; assert.changesButNotBy(fn, obj, 'val', 5) }) ## increases * **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` Asserts that a `modifier` increases a numeric `object`'s `property`. import { assert, test } from 'vitest' test('assert.increases', () => { const obj = { val: 10 } function fn() { obj.val = 13 }; assert.increases(fn, obj, 'val') }) ## increasesBy * **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` Asserts that a `modifier` increases a numeric `object`'s `property` or a `modifier` return value by an `change`. import { assert, test } from 'vitest' test('assert.increasesBy', () => { const obj = { val: 10 } function fn() { obj.val += 10 }; assert.increasesBy(fn, obj, 'val', 10) }) ## doesNotIncrease * **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` Asserts that a `modifier` does not increases a numeric `object`'s `property`. import { assert, test } from 'vitest' test('assert.doesNotIncrease', () => { const obj = { val: 10 } function fn() { obj.val = 8 } assert.doesNotIncrease(fn, obj, 'val') }) ## increasesButNotBy * **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` Asserts that a `modifier` does not increases a numeric `object`'s `property` or a `modifier` return value by an `change`. import { assert, test } from 'vitest' test('assert.increasesButNotBy', () => { const obj = { val: 10 } function fn() { obj.val += 15 }; assert.increasesButNotBy(fn, obj, 'val', 10) }) ## decreases * **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` Asserts that a `modifier` decreases a numeric `object`'s `property`. import { assert, test } from 'vitest' test('assert.decreases', () => { const obj = { val: 10 } function fn() { obj.val = 5 }; assert.decreases(fn, obj, 'val') }) ## decreasesBy * **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` Asserts that a `modifier` decreases a numeric `object`'s `property` or a `modifier` return value by a `change`. import { assert, test } from 'vitest' test('assert.decreasesBy', () => { const obj = { val: 10 } function fn() { obj.val -= 5 }; assert.decreasesBy(fn, obj, 'val', 5) }) ## doesNotDecrease * **Type:** `(modifier: Function, object: T, property: string, message?: string) => void` Asserts that a `modifier` dose not decrease a numeric `object`'s `property`. import { assert, test } from 'vitest' test('assert.doesNotDecrease', () => { const obj = { val: 10 } function fn() { obj.val = 15 } assert.doesNotDecrease(fn, obj, 'val') }) ## doesNotDecreaseBy * **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` Asserts that a `modifier` does not decrease a numeric `object`'s `property` or a `modifier` return value by a `change`. import { assert, test } from 'vitest' test('assert.doesNotDecreaseBy', () => { const obj = { val: 10 } function fn() { obj.val = 5 }; assert.doesNotDecreaseBy(fn, obj, 'val', 1) }) ## decreasesButNotBy * **Type:** `(modifier: Function, object: T, property: string, change: number, message?: string) => void` Asserts that a `modifier` does not decrease a numeric `object`'s `property` or a `modifier` return value by a `change`. import { assert, test } from 'vitest' test('assert.decreasesButNotBy', () => { const obj = { val: 10 } function fn() { obj.val = 5 }; assert.decreasesButNotBy(fn, obj, 'val', 1) }) ## ifError * **Type:** `(object: T, message?: string) => void` Asserts if `object` is not a false value, and throws if it is a true value. This is added to allow for chai to be a drop-in replacement for Node’s assert class. import { assert, test } from 'vitest' test('assert.ifError', () => { const err = new Error('I am a custom error') assert.ifError(err) // Rethrows err! }) ## isExtensible * **Type:** `(object: T, message?: string) => void` * **Alias:** `extensible` Asserts that `object` is extensible (can have new properties added to it). import { assert, test } from 'vitest' test('assert.isExtensible', () => { assert.isExtensible({}) }) ## isNotExtensible * **Type:** `(object: T, message?: string) => void` * **Alias:** `notExtensible` Asserts that `object` is not extensible (can not have new properties added to it). import { assert, test } from 'vitest' test('assert.isNotExtensible', () => { const nonExtensibleObject = Object.preventExtensions({}) const sealedObject = Object.seal({}) const frozenObject = Object.freeze({}) assert.isNotExtensible(nonExtensibleObject) assert.isNotExtensible(sealedObject) assert.isNotExtensible(frozenObject) }) ## isSealed * **Type:** `(object: T, message?: string) => void` * **Alias:** `sealed` Asserts that `object` is sealed (cannot have new properties added to it and its existing properties cannot be removed). import { assert, test } from 'vitest' test('assert.isSealed', () => { const sealedObject = Object.seal({}) const frozenObject = Object.seal({}) assert.isSealed(sealedObject) assert.isSealed(frozenObject) }) ## isNotSealed * **Type:** `(object: T, message?: string) => void` * **Alias:** `notSealed` Asserts that `object` is not sealed (can have new properties added to it and its existing properties can be removed). import { assert, test } from 'vitest' test('assert.isNotSealed', () => { assert.isNotSealed({}) }) ## isFrozen * **Type:** `(object: T, message?: string) => void` * **Alias:** `frozen` Asserts that object is frozen (cannot have new properties added to it and its existing properties cannot be modified). import { assert, test } from 'vitest' test('assert.isFrozen', () => { const frozenObject = Object.freeze({}) assert.frozen(frozenObject) }) ## isNotFrozen * **Type:** `(object: T, message?: string) => void` * **Alias:** `notFrozen` Asserts that `object` is not frozen (can have new properties added to it and its existing properties can be modified). import { assert, test } from 'vitest' test('assert.isNotFrozen', () => { assert.isNotFrozen({}) }) ## isEmpty * **Type:** `(target: T, message?: string) => void` * **Alias:** `empty` Asserts that the `target` does not contain any values. For arrays and strings, it checks the length property. For Map and Set instances, it checks the size property. For non-function objects, it gets the count of its own enumerable string keys. import { assert, test } from 'vitest' test('assert.isEmpty', () => { assert.isEmpty([]) assert.isEmpty('') assert.isEmpty(new Map()) assert.isEmpty({}) }) ## isNotEmpty * **Type:** `(object: T, message?: string) => void` * **Alias:** `notEmpty` Asserts that the `target` contains values. For arrays and strings, it checks the length property. For Map and Set instances, it checks the size property. For non-function objects, it gets the count of its own enumerable string keys. import { assert, test } from 'vitest' test('assert.isNotEmpty', () => { assert.isNotEmpty([1, 2]) assert.isNotEmpty('34') assert.isNotEmpty(new Set([5, 6])) assert.isNotEmpty({ key: 7 }) }) # expectTypeOf **WARNING** During runtime this function doesn't do anything. To [enable typechecking](../guide/testing-types#run-typechecking), don't forget to pass down `--typecheck` flag. * **Type:** `(a: unknown) => ExpectTypeOf` ## not * **Type:** `ExpectTypeOf` You can negate all assertions, using `.not` property. ## toEqualTypeOf * **Type:** `(expected: T) => void` This matcher will check if the types are fully equal to each other. This matcher will not fail if two objects have different values, but the same type. It will fail however if an object is missing a property. import { expectTypeOf } from 'vitest' expectTypeOf({ a: 1 }).toEqualTypeOf<{ a: number }>() expectTypeOf({ a: 1 }).toEqualTypeOf({ a: 1 }) expectTypeOf({ a: 1 }).toEqualTypeOf({ a: 2 }) expectTypeOf({ a: 1, b: 1 }).not.toEqualTypeOf<{ a: number }>() ## toMatchTypeOf * **Type:** `(expected: T) => void` This matcher checks if expect type extends provided type. It is different from `toEqual` and is more similar to [expect's](expect) `toMatchObject()`. With this matcher, you can check if an object “matches” a type. import { expectTypeOf } from 'vitest' expectTypeOf({ a: 1, b: 1 }).toMatchTypeOf({ a: 1 }) expectTypeOf().toMatchTypeOf() expectTypeOf().not.toMatchTypeOf() ## extract * **Type:** `ExpectTypeOf` You can use `.extract` to narrow down types for further testing. import { expectTypeOf } from 'vitest' type ResponsiveProp = T | T[] | { xs?: T; sm?: T; md?: T } interface CSSProperties { margin?: string; padding?: string } function getResponsiveProp(_props: T): ResponsiveProp { return {} } const cssProperties: CSSProperties = { margin: '1px', padding: '2px' } expectTypeOf(getResponsiveProp(cssProperties)) .extract<{ xs?: any }>() // extracts the last type from a union .toEqualTypeOf<{ xs?: CSSProperties; sm?: CSSProperties; md?: CSSProperties }>() expectTypeOf(getResponsiveProp(cssProperties)) .extract() // extracts an array from a union .toEqualTypeOf() **WARNING** If no type is found in the union, `.extract` will return `never`. ## exclude * **Type:** `ExpectTypeOf` You can use `.exclude` to remove types from a union for further testing. import { expectTypeOf } from 'vitest' type ResponsiveProp = T | T[] | { xs?: T; sm?: T; md?: T } interface CSSProperties { margin?: string; padding?: string } function getResponsiveProp(_props: T): ResponsiveProp { return {} } const cssProperties: CSSProperties = { margin: '1px', padding: '2px' } expectTypeOf(getResponsiveProp(cssProperties)) .exclude() .exclude<{ xs?: unknown }>() // or just .exclude() .toEqualTypeOf() **WARNING** If no type is found in the union, `.exclude` will return `never`. ## returns * **Type:** `ExpectTypeOf` You can use `.returns` to extract return value of a function type. import { expectTypeOf } from 'vitest' expectTypeOf(() => {}).returns.toBeVoid() expectTypeOf((a: number) => [a, a]).returns.toEqualTypeOf([1, 2]) **WARNING** If used on a non-function type, it will return `never`, so you won't be able to chain it with other matchers. ## parameters * **Type:** `ExpectTypeOf` You can extract function arguments with `.parameters` to perform assertions on its value. Parameters are returned as an array. import { expectTypeOf } from 'vitest' type NoParam = () => void type HasParam = (s: string) => void expectTypeOf().parameters.toEqualTypeOf<[]>() expectTypeOf().parameters.toEqualTypeOf<[string]>() **WARNING** If used on a non-function type, it will return `never`, so you won't be able to chain it with other matchers. **TIP** You can also use `.toBeCallableWith` matcher as a more expressive assertion. ## parameter * **Type:** `(nth: number) => ExpectTypeOf` You can extract a certain function argument with `.parameter(number)` call to perform other assertions on it. import { expectTypeOf } from 'vitest' function foo(a: number, b: string) { return [a, b] } expectTypeOf(foo).parameter(0).toBeNumber() expectTypeOf(foo).parameter(1).toBeString() **WARNING** If used on a non-function type, it will return `never`, so you won't be able to chain it with other matchers. ## constructorParameters * **Type:** `ExpectTypeOf` You can extract constructor parameters as an array of values and perform assertions on them with this method. import { expectTypeOf } from 'vitest' expectTypeOf(Date).constructorParameters.toEqualTypeOf<[] | [string | number | Date]>() **WARNING** If used on a non-function type, it will return `never`, so you won't be able to chain it with other matchers. **TIP** You can also use `.toBeConstructibleWith` matcher as a more expressive assertion. ## instance * **Type:** `ExpectTypeOf` This property gives access to matchers that can be performed on an instance of the provided class. import { expectTypeOf } from 'vitest' expectTypeOf(Date).instance.toHaveProperty('toISOString') **WARNING** If used on a non-function type, it will return `never`, so you won't be able to chain it with other matchers. ## items * **Type:** `ExpectTypeOf` You can get array item type with `.items` to perform further assertions. import { expectTypeOf } from 'vitest' expectTypeOf([1, 2, 3]).items.toEqualTypeOf() expectTypeOf([1, 2, 3]).items.not.toEqualTypeOf() ## resolves * **Type:** `ExpectTypeOf` This matcher extracts resolved value of a `Promise`, so you can perform other assertions on it. import { expectTypeOf } from 'vitest' async function asyncFunc() { return 123 } expectTypeOf(asyncFunc).returns.resolves.toBeNumber() expectTypeOf(Promise.resolve('string')).resolves.toBeString() **WARNING** If used on a non-promise type, it will return `never`, so you won't be able to chain it with other matchers. ## guards * **Type:** `ExpectTypeOf` This matcher extracts guard value (e.g., `v is number`), so you can perform assertions on it. import { expectTypeOf } from 'vitest' function isString(v: any): v is string { return typeof v === 'string' } expectTypeOf(isString).guards.toBeString() **WARNING** Returns `never`, if the value is not a guard function, so you won't be able to chain it with other matchers. ## asserts * **Type:** `ExpectTypeOf` This matcher extracts assert value (e.g., `assert v is number`), so you can perform assertions on it. import { expectTypeOf } from 'vitest' function assertNumber(v: any): asserts v is number { if (typeof v !== 'number') { throw new TypeError('Nope !') } } expectTypeOf(assertNumber).asserts.toBeNumber() **WARNING** Returns `never`, if the value is not an assert function, so you won't be able to chain it with other matchers. ## toBeAny * **Type:** `() => void` With this matcher you can check, if provided type is `any` type. If the type is too specific, the test will fail. import { expectTypeOf } from 'vitest' expectTypeOf().toBeAny() expectTypeOf({} as any).toBeAny() expectTypeOf('string').not.toBeAny() ## toBeUnknown * **Type:** `() => void` This matcher checks, if provided type is `unknown` type. import { expectTypeOf } from 'vitest' expectTypeOf().toBeUnknown() expectTypeOf({} as unknown).toBeUnknown() expectTypeOf('string').not.toBeUnknown() ## toBeNever * **Type:** `() => void` This matcher checks, if provided type is a `never` type. import { expectTypeOf } from 'vitest' expectTypeOf().toBeNever() expectTypeOf((): never => {}).returns.toBeNever() ## toBeFunction * **Type:** `() => void` This matcher checks, if provided type is a `function`. import { expectTypeOf } from 'vitest' expectTypeOf(42).not.toBeFunction() expectTypeOf((): never => {}).toBeFunction() ## toBeObject * **Type:** `() => void` This matcher checks, if provided type is an `object`. import { expectTypeOf } from 'vitest' expectTypeOf(42).not.toBeObject() expectTypeOf({}).toBeObject() ## toBeArray * **Type:** `() => void` This matcher checks, if provided type is `Array`. import { expectTypeOf } from 'vitest' expectTypeOf(42).not.toBeArray() expectTypeOf([]).toBeArray() expectTypeOf([1, 2]).toBeArray() expectTypeOf([{}, 42]).toBeArray() ## toBeString * **Type:** `() => void` This matcher checks, if provided type is a `string`. import { expectTypeOf } from 'vitest' expectTypeOf(42).not.toBeString() expectTypeOf('').toBeString() expectTypeOf('a').toBeString() ## toBeBoolean * **Type:** `() => void` This matcher checks, if provided type is `boolean`. import { expectTypeOf } from 'vitest' expectTypeOf(42).not.toBeBoolean() expectTypeOf(true).toBeBoolean() expectTypeOf().toBeBoolean() ## toBeVoid * **Type:** `() => void` This matcher checks, if provided type is `void`. import { expectTypeOf } from 'vitest' expectTypeOf(() => {}).returns.toBeVoid() expectTypeOf().toBeVoid() ## toBeSymbol * **Type:** `() => void` This matcher checks, if provided type is a `symbol`. import { expectTypeOf } from 'vitest' expectTypeOf(Symbol(1)).toBeSymbol() expectTypeOf().toBeSymbol() ## toBeNull * **Type:** `() => void` This matcher checks, if provided type is `null`. import { expectTypeOf } from 'vitest' expectTypeOf(null).toBeNull() expectTypeOf().toBeNull() expectTypeOf(undefined).not.toBeNull() ## toBeUndefined * **Type:** `() => void` This matcher checks, if provided type is `undefined`. import { expectTypeOf } from 'vitest' expectTypeOf(undefined).toBeUndefined() expectTypeOf().toBeUndefined() expectTypeOf(null).not.toBeUndefined() ## toBeNullable * **Type:** `() => void` This matcher checks, if you can use `null` or `undefined` with provided type. import { expectTypeOf } from 'vitest' expectTypeOf().toBeNullable() expectTypeOf().toBeNullable() expectTypeOf().toBeNullable() ## toBeCallableWith * **Type:** `() => void` This matcher ensures you can call provided function with a set of parameters. import { expectTypeOf } from 'vitest' type NoParam = () => void type HasParam = (s: string) => void expectTypeOf().toBeCallableWith() expectTypeOf().toBeCallableWith('some string') **WARNING** If used on a non-function type, it will return `never`, so you won't be able to chain it with other matchers. ## toBeConstructibleWith * **Type:** `() => void` This matcher ensures you can create a new instance with a set of constructor parameters. import { expectTypeOf } from 'vitest' expectTypeOf(Date).toBeConstructibleWith(new Date()) expectTypeOf(Date).toBeConstructibleWith('01-01-2000') **WARNING** If used on a non-function type, it will return `never`, so you won't be able to chain it with other matchers. ## toHaveProperty * **Type:** `(property: K) => ExpectTypeOf` This matcher checks if a property exists on the provided object. If it exists, it also returns the same set of matchers for the type of this property, so you can chain assertions one after another. import { expectTypeOf } from 'vitest' const obj = { a: 1, b: '' } expectTypeOf(obj).toHaveProperty('a') expectTypeOf(obj).not.toHaveProperty('c') expectTypeOf(obj).toHaveProperty('a').toBeNumber() expectTypeOf(obj).toHaveProperty('b').toBeString() expectTypeOf(obj).toHaveProperty('a').not.toBeString() # expect The following types are used in the type signatures below type Awaitable = T | PromiseLike `expect` is used to create assertions. In this context `assertions` are functions that can be called to assert a statement. Vitest provides `chai` assertions by default and also `Jest` compatible assertions built on top of `chai`. For example, this code asserts that an `input` value is equal to `2`. If it's not, the assertion will throw an error, and the test will fail. import { } from 'vitest' const = .(4) ()..(2) // chai API ().(2) // jest API Technically this example doesn't use [`test`](index#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](index). Also, `expect` can be used statically to access matcher functions, described later, and more. **WARNING** `expect` has no effect on testing types, if the expression doesn't have a type error. If you want to use Vitest as [type checker](../guide/testing-types), use [`expectTypeOf`](expect-typeof) or [`assertType`](assert-type). ## soft * **Type:** `ExpectStatic & (actual: any) => Assertions` `expect.soft` functions similarly to `expect`, but instead of terminating the test execution upon a failed assertion, it continues running and marks the failure as a test failure. All errors encountered during the test will be displayed until the test is completed. import { expect, test } from 'vitest' test('expect.soft test', () => { expect.soft(1 + 1).toBe(3) // mark the test as fail and continue expect.soft(1 + 2).toBe(4) // mark the test as fail and continue }) // reporter will report both errors at the end of the run It can also be used with `expect`. if `expect` assertion fails, the test will be terminated and all errors will be displayed. import { expect, test } from 'vitest' test('expect.soft test', () => { expect.soft(1 + 1).toBe(3) // mark the test as fail and continue expect(1 + 2).toBe(4) // failed and terminate the test, all previous errors will be output expect.soft(1 + 3).toBe(5) // do not run }) **WARNING** `expect.soft` can only be used inside the [`test`](index#test) function. ## poll interface ExpectPoll extends ExpectStatic { (actual: () => T, options: { interval; timeout; message }): Promise> } `expect.poll` reruns the _assertion_ until it is succeeded. You can configure how many times Vitest should rerun the `expect.poll` callback by setting `interval` and `timeout` options. If an error is thrown inside the `expect.poll` callback, Vitest will retry again until the timeout runs out. import { expect, test } from 'vitest' test('element exists', async () => { asyncInjectElement() await expect.poll(() => document.querySelector('.element')).toBeTruthy() }) **WARNING** `expect.poll` makes every assertion asynchronous, so you need to await it. Since Vitest 3, if you forget to await it, the test will fail with a warning to do so. `expect.poll` doesn't work with several matchers: * Snapshot matchers are not supported because they will always succeed. If your condition is flaky, consider using [`vi.waitFor`](vi#vi-waitfor) instead to resolve it first: import { expect, vi } from 'vitest' const flakyValue = await vi.waitFor(() => getFlakyValue()) expect(flakyValue).toMatchSnapshot() * `.resolves` and `.rejects` are not supported. `expect.poll` already awaits the condition if it's asynchronous. * `toThrow` and its aliases are not supported because the `expect.poll` condition is always resolved before the matcher gets the value ## not Using `not` will negate the assertion. For example, this code asserts that an `input` value is not equal to `2`. If it's equal, the assertion will throw an error, and the test will fail. import { expect, test } from 'vitest' const input = Math.sqrt(16) expect(input).not.to.equal(2) // chai API expect(input).not.toBe(2) // jest API ## toBe * **Type:** `(value: any) => Awaitable` `toBe` can be used to assert if primitives are equal or that objects share the same reference. It is equivalent of calling `expect(Object.is(3, 3)).toBe(true)`. If the objects are not the same, but you want to check if their structures are identical, you can use `toEqual`. For example, the code below checks if the trader has 13 apples. import { expect, test } from 'vitest' const stock = { type: 'apples', count: 13, } test('stock has 13 apples', () => { expect(stock.type).toBe('apples') expect(stock.count).toBe(13) }) test('stocks are the same', () => { const refStock = stock // same reference expect(stock).toBe(refStock) }) Try not to use `toBe` with floating-point numbers. Since JavaScript rounds them, `0.1 + 0.2` is not strictly `0.3`. To reliably assert floating-point numbers, use `toBeCloseTo` assertion. ## toBeCloseTo * **Type:** `(value: number, numDigits?: number) => Awaitable` Use `toBeCloseTo` to compare floating-point numbers. The optional `numDigits` argument limits the number of digits to check _after_ the decimal point. For example: import { expect, test } from 'vitest' test.fails('decimals are not equal in javascript', () => { expect(0.2 + 0.1).toBe(0.3) // 0.2 + 0.1 is 0.30000000000000004 }) test('decimals are rounded to 5 after the point', () => { // 0.2 + 0.1 is 0.30000 | "000000000004" removed expect(0.2 + 0.1).toBeCloseTo(0.3, 5) // nothing from 0.30000000000000004 is removed expect(0.2 + 0.1).not.toBeCloseTo(0.3, 50) }) ## toBeDefined * **Type:** `() => Awaitable` `toBeDefined` asserts that the value is not equal to `undefined`. Useful use case would be to check if function _returned_ anything. import { expect, test } from 'vitest' function getApples() { return 3 } test('function returned something', () => { expect(getApples()).toBeDefined() }) ## toBeUndefined * **Type:** `() => Awaitable` Opposite of `toBeDefined`, `toBeUndefined` asserts that the value _is_ equal to `undefined`. Useful use case would be to check if function hasn't _returned_ anything. import { expect, test } from 'vitest' function getApplesFromStock(stock: string) { if (stock === 'Bill') { return 13 } } test('mary doesn\'t have a stock', () => { expect(getApplesFromStock('Mary')).toBeUndefined() }) ## toBeTruthy * **Type:** `() => Awaitable` `toBeTruthy` asserts that the value is true when converted to boolean. Useful if you don't care for the value, but just want to know it can be converted to `true`. For example, having this code you don't care for the return value of `stocks.getInfo` \- it maybe a complex object, a string, or anything else. The code will still work. import { Stocks } from './stocks.js' const stocks = new Stocks() stocks.sync('Bill') if (stocks.getInfo('Bill')) { stocks.sell('apples', 'Bill') } So if you want to test that `stocks.getInfo` will be truthy, you could write: import { expect, test } from 'vitest' import { Stocks } from './stocks.js' const stocks = new Stocks() test('if we know Bill stock, sell apples to him', () => { stocks.sync('Bill') expect(stocks.getInfo('Bill')).toBeTruthy() }) Everything in JavaScript is truthy, except `false`, `null`, `undefined`, `NaN`, `0`, `-0`, `0n`, `""` and `document.all`. ## toBeFalsy * **Type:** `() => Awaitable` `toBeFalsy` asserts that the value is false when converted to boolean. Useful if you don't care for the value, but just want to know if it can be converted to `false`. For example, having this code you don't care for the return value of `stocks.stockFailed` \- it may return any falsy value, but the code will still work. import { Stocks } from './stocks.js' const stocks = new Stocks() stocks.sync('Bill') if (!stocks.stockFailed('Bill')) { stocks.sell('apples', 'Bill') } So if you want to test that `stocks.stockFailed` will be falsy, you could write: import { expect, test } from 'vitest' import { Stocks } from './stocks.js' const stocks = new Stocks() test('if Bill stock hasn\'t failed, sell apples to him', () => { stocks.syncStocks('Bill') expect(stocks.stockFailed('Bill')).toBeFalsy() }) Everything in JavaScript is truthy, except `false`, `null`, `undefined`, `NaN`, `0`, `-0`, `0n`, `""` and `document.all`. ## toBeNull * **Type:** `() => Awaitable` `toBeNull` simply asserts if something is `null`. Alias for `.toBe(null)`. import { expect, test } from 'vitest' function apples() { return null } test('we don\'t have apples', () => { expect(apples()).toBeNull() }) ## toBeNaN * **Type:** `() => Awaitable` `toBeNaN` simply asserts if something is `NaN`. Alias for `.toBe(NaN)`. import { expect, test } from 'vitest' let i = 0 function getApplesCount() { i++ return i > 1 ? Number.NaN : i } test('getApplesCount has some unusual side effects...', () => { expect(getApplesCount()).not.toBeNaN() expect(getApplesCount()).toBeNaN() }) ## toBeOneOf * **Type:** `(sample: Array) => any` `toBeOneOf` asserts if a value matches any of the values in the provided array. import { expect, test } from 'vitest' test('fruit is one of the allowed values', () => { expect(fruit).toBeOneOf(['apple', 'banana', 'orange']) }) The asymmetric matcher is particularly useful when testing optional properties that could be either `null` or `undefined`: test('optional properties can be null or undefined', () => { const user = { firstName: 'John', middleName: undefined, lastName: 'Doe' } expect(user).toEqual({ firstName: expect.any(String), middleName: expect.toBeOneOf([expect.any(String), undefined]), lastName: expect.any(String), }) }) **TIP** You can use `expect.not` with this matcher to ensure a value does NOT match any of the provided options. ## toBeTypeOf * **Type:** `(c: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => Awaitable` `toBeTypeOf` asserts if an actual value is of type of received type. import { expect, test } from 'vitest' const actual = 'stock' test('stock is type of string', () => { expect(actual).toBeTypeOf('string') }) ## toBeInstanceOf * **Type:** `(c: any) => Awaitable` `toBeInstanceOf` asserts if an actual value is instance of received class. import { expect, test } from 'vitest' import { Stocks } from './stocks.js' const stocks = new Stocks() test('stocks are instance of Stocks', () => { expect(stocks).toBeInstanceOf(Stocks) }) ## toBeGreaterThan * **Type:** `(n: number | bigint) => Awaitable` `toBeGreaterThan` asserts if actual value is greater than received one. Equal values will fail the test. import { expect, test } from 'vitest' import { getApples } from './stocks.js' test('have more then 10 apples', () => { expect(getApples()).toBeGreaterThan(10) }) ## toBeGreaterThanOrEqual * **Type:** `(n: number | bigint) => Awaitable` `toBeGreaterThanOrEqual` asserts if actual value is greater than received one or equal to it. import { expect, test } from 'vitest' import { getApples } from './stocks.js' test('have 11 apples or more', () => { expect(getApples()).toBeGreaterThanOrEqual(11) }) ## toBeLessThan * **Type:** `(n: number | bigint) => Awaitable` `toBeLessThan` asserts if actual value is less than received one. Equal values will fail the test. import { expect, test } from 'vitest' import { getApples } from './stocks.js' test('have less then 20 apples', () => { expect(getApples()).toBeLessThan(20) }) ## toBeLessThanOrEqual * **Type:** `(n: number | bigint) => Awaitable` `toBeLessThanOrEqual` asserts if actual value is less than received one or equal to it. import { expect, test } from 'vitest' import { getApples } from './stocks.js' test('have 11 apples or less', () => { expect(getApples()).toBeLessThanOrEqual(11) }) ## toEqual * **Type:** `(received: any) => Awaitable` `toEqual` asserts if actual value is equal to received one or has the same structure, if it is an object (compares them recursively). You can see the difference between `toEqual` and `toBe` in this example: import { expect, test } from 'vitest' const stockBill = { type: 'apples', count: 13, } const stockMary = { type: 'apples', count: 13, } test('stocks have the same properties', () => { expect(stockBill).toEqual(stockMary) }) test('stocks are not the same', () => { expect(stockBill).not.toBe(stockMary) }) **WARNING** For `Error` objects, non-enumerable properties such as `name`, `message`, `cause` and `AggregateError.errors` are also compared. For `Error.cause`, the comparison is done asymmetrically: // success expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi')) // fail expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' })) To test if something was thrown, use `toThrowError` assertion. ## toStrictEqual * **Type:** `(received: any) => Awaitable` `toStrictEqual` asserts if the actual value is equal to the received one or has the same structure if it is an object (compares them recursively), and of the same type. Differences from `.toEqual`: * Keys with `undefined` properties are checked. e.g. `{a: undefined, b: 2}` does not match `{b: 2}` when using `.toStrictEqual`. * Array sparseness is checked. e.g. `[, 1]` does not match `[undefined, 1]` when using `.toStrictEqual`. * Object types are checked to be equal. e.g. A class instance with fields `a` and` b` will not equal a literal object with fields `a` and `b`. import { expect, test } from 'vitest' class Stock { constructor(type) { this.type = type } } test('structurally the same, but semantically different', () => { expect(new Stock('apples')).toEqual({ type: 'apples' }) expect(new Stock('apples')).not.toStrictEqual({ type: 'apples' }) }) ## toContain * **Type:** `(received: string) => Awaitable` `toContain` asserts if the actual value is in an array. `toContain` can also check whether a string is a substring of another string. If you are running tests in a browser-like environment, this assertion can also check if class is contained in a `classList`, or an element is inside another one. import { expect, test } from 'vitest' import { getAllFruits } from './stocks.js' test('the fruit list contains orange', () => { expect(getAllFruits()).toContain('orange') const element = document.querySelector('#el') // element has a class expect(element.classList).toContain('flex') // element is inside another one expect(document.querySelector('#wrapper')).toContain(element) }) ## toContainEqual * **Type:** `(received: any) => Awaitable` `toContainEqual` asserts if an item with a specific structure and values is contained in an array. It works like `toEqual` inside for each element. import { expect, test } from 'vitest' import { getFruitStock } from './stocks.js' test('apple available', () => { expect(getFruitStock()).toContainEqual({ fruit: 'apple', count: 5 }) }) ## toHaveLength * **Type:** `(received: number) => Awaitable` `toHaveLength` asserts if an object has a `.length` property and it is set to a certain numeric value. import { expect, test } from 'vitest' test('toHaveLength', () => { expect('abc').toHaveLength(3) expect([1, 2, 3]).toHaveLength(3) expect('').not.toHaveLength(3) // doesn't have .length of 3 expect({ length: 3 }).toHaveLength(3) }) ## toHaveProperty * **Type:** `(key: any, received?: any) => Awaitable` `toHaveProperty` asserts if a property at provided reference `key` exists for an object. You can provide an optional value argument also known as deep equality, like the `toEqual` matcher to compare the received property value. import { expect, test } from 'vitest' const invoice = { 'isActive': true, 'P.O': '12345', 'customer': { first_name: 'John', last_name: 'Doe', location: 'China', }, 'total_amount': 5000, 'items': [ { type: 'apples', quantity: 10, }, { type: 'oranges', quantity: 5, }, ], } test('John Doe Invoice', () => { expect(invoice).toHaveProperty('isActive') // assert that the key exists expect(invoice).toHaveProperty('total_amount', 5000) // assert that the key exists and the value is equal expect(invoice).not.toHaveProperty('account') // assert that this key does not exist // Deep referencing using dot notation expect(invoice).toHaveProperty('customer.first_name') expect(invoice).toHaveProperty('customer.last_name', 'Doe') expect(invoice).not.toHaveProperty('customer.location', 'India') // Deep referencing using an array containing the key expect(invoice).toHaveProperty('items[0].type', 'apples') expect(invoice).toHaveProperty('items.0.type', 'apples') // dot notation also works // Deep referencing using an array containing the keyPath expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples') expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples') // string notation also works // Wrap your key in an array to avoid the key from being parsed as a deep reference expect(invoice).toHaveProperty(['P.O'], '12345') }) ## toMatch * **Type:** `(received: string | regexp) => Awaitable` `toMatch` asserts if a string matches a regular expression or a string. import { expect, test } from 'vitest' test('top fruits', () => { expect('top fruits include apple, orange and grape').toMatch(/apple/) expect('applefruits').toMatch('fruit') // toMatch also accepts a string }) ## toMatchObject * **Type:** `(received: object | array) => Awaitable` `toMatchObject` asserts if an object matches a subset of the properties of an object. You can also pass an array of objects. This is useful if you want to check that two arrays match in their number of elements, as opposed to `arrayContaining`, which allows for extra elements in the received array. import { expect, test } from 'vitest' const johnInvoice = { isActive: true, customer: { first_name: 'John', last_name: 'Doe', location: 'China', }, total_amount: 5000, items: [ { type: 'apples', quantity: 10, }, { type: 'oranges', quantity: 5, }, ], } const johnDetails = { customer: { first_name: 'John', last_name: 'Doe', location: 'China', }, } test('invoice has john personal details', () => { expect(johnInvoice).toMatchObject(johnDetails) }) test('the number of elements must match exactly', () => { // Assert that an array of object matches expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([ { foo: 'bar' }, { baz: 1 }, ]) }) ## toThrowError * **Type:** `(received: any) => Awaitable` * **Alias:** `toThrow` `toThrowError` asserts if a function throws an error when it is called. You can provide an optional argument to test that a specific error is thrown: * `RegExp`: error message matches the pattern * `string`: error message includes the substring * `Error`, `AsymmetricMatcher`: compare with a received object similar to `toEqual(received)` **TIP** You must wrap the code in a function, otherwise the error will not be caught, and test will fail. This does not apply for async calls as rejects correctly unwraps the promise: test('expect rejects toThrow', async ({ expect }) => { const promise = Promise.reject(new Error('Test')) await expect(promise).rejects.toThrowError() }) For example, if we want to test that `getFruitStock('pineapples')` throws, we could write: import { expect, test } from 'vitest' function getFruitStock(type: string) { if (type === 'pineapples') { throw new Error('Pineapples are not in stock') } // Do some other stuff } test('throws on pineapples', () => { // Test that the error message says "stock" somewhere: these are equivalent expect(() => getFruitStock('pineapples')).toThrowError(/stock/) expect(() => getFruitStock('pineapples')).toThrowError('stock') // Test the exact error message expect(() => getFruitStock('pineapples')).toThrowError( /^Pineapples are not in stock$/, ) expect(() => getFruitStock('pineapples')).toThrowError( new Error('Pineapples are not in stock'), ) expect(() => getFruitStock('pineapples')).toThrowError(expect.objectContaining({ message: 'Pineapples are not in stock', })) }) **TIP** To test async functions, use in combination with rejects. function getAsyncFruitStock() { return Promise.reject(new Error('empty')) } test('throws on pineapples', async () => { await expect(() => getAsyncFruitStock()).rejects.toThrowError('empty') }) ## toMatchSnapshot * **Type:** `(shape?: Partial | string, message?: string) => void` This ensures that a value matches the most recent snapshot. You can provide an optional `hint` string argument that is appended to the test name. Although Vitest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate multiple snapshots in a single it or test block. Vitest sorts snapshots by name in the corresponding `.snap` file. **TIP** When a snapshot mismatches and causes the test to fail, if the mismatch is expected, you can press `u` key to update the snapshot once. Or you can pass `-u` or `--update` CLI options to make Vitest always update the tests. import { expect, test } from 'vitest' test('matches snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) } expect(data).toMatchSnapshot() }) You can also provide a shape of an object, if you are testing just a shape of an object, and don't need it to be 100% compatible: import { expect, test } from 'vitest' test('matches snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) } expect(data).toMatchSnapshot({ foo: expect.any(Set) }) }) ## toMatchInlineSnapshot * **Type:** `(shape?: Partial | string, snapshot?: string, message?: string) => void` This ensures that a value matches the most recent snapshot. Vitest adds and updates the inlineSnapshot string argument to the matcher in the test file (instead of an external `.snap` file). import { expect, test } from 'vitest' test('matches inline snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) } // Vitest will update following content when updating the snapshot expect(data).toMatchInlineSnapshot(` { "foo": Set { "bar", "snapshot", }, } `) }) You can also provide a shape of an object, if you are testing just a shape of an object, and don't need it to be 100% compatible: import { expect, test } from 'vitest' test('matches snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) } expect(data).toMatchInlineSnapshot( { foo: expect.any(Set) }, ` { "foo": Any, } ` ) }) ## toMatchFileSnapshot * **Type:** `(filepath: string, message?: string) => Promise` Compare or update the snapshot with the content of a file explicitly specified (instead of the `.snap` file). import { expect, it } from 'vitest' it('render basic', async () => { const result = renderHTML(h('div', { class: 'foo' })) await expect(result).toMatchFileSnapshot('./test/basic.output.html') }) Note that since file system operation is async, you need to use `await` with `toMatchFileSnapshot()`. If `await` is not used, Vitest treats it like `expect.soft`, meaning the code after the statement will continue to run even if the snapshot mismatches. After the test finishes, Vitest will check the snapshot and fail if there is a mismatch. ## toThrowErrorMatchingSnapshot * **Type:** `(message?: string) => void` The same as `toMatchSnapshot`, but expects the same value as `toThrowError`. ## toThrowErrorMatchingInlineSnapshot * **Type:** `(snapshot?: string, message?: string) => void` The same as `toMatchInlineSnapshot`, but expects the same value as `toThrowError`. ## toHaveBeenCalled * **Type:** `() => Awaitable` This assertion is useful for testing that a function has been called. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' const market = { buy(subject: string, amount: number) { // ... }, } test('spy function', () => { const buySpy = vi.spyOn(market, 'buy') expect(buySpy).not.toHaveBeenCalled() market.buy('apples', 10) expect(buySpy).toHaveBeenCalled() }) ## toHaveBeenCalledTimes * **Type** : `(amount: number) => Awaitable` This assertion checks if a function was called a certain amount of times. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' const market = { buy(subject: string, amount: number) { // ... }, } test('spy function called two times', () => { const buySpy = vi.spyOn(market, 'buy') market.buy('apples', 10) market.buy('apples', 20) expect(buySpy).toHaveBeenCalledTimes(2) }) ## toHaveBeenCalledWith * **Type** : `(...args: any[]) => Awaitable` This assertion checks if a function was called at least once with certain parameters. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' const market = { buy(subject: string, amount: number) { // ... }, } test('spy function', () => { const buySpy = vi.spyOn(market, 'buy') market.buy('apples', 10) market.buy('apples', 20) expect(buySpy).toHaveBeenCalledWith('apples', 10) expect(buySpy).toHaveBeenCalledWith('apples', 20) }) ## toHaveBeenCalledBefore 3.0.0+ * **Type** : `(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable` This assertion checks if a `Mock` was called before another `Mock`. test('calls mock1 before mock2', () => { const mock1 = vi.fn() const mock2 = vi.fn() mock1() mock2() mock1() expect(mock1).toHaveBeenCalledBefore(mock2) }) ## toHaveBeenCalledAfter 3.0.0+ * **Type** : `(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable` This assertion checks if a `Mock` was called after another `Mock`. test('calls mock1 after mock2', () => { const mock1 = vi.fn() const mock2 = vi.fn() mock2() mock1() mock2() expect(mock1).toHaveBeenCalledAfter(mock2) }) ## toHaveBeenCalledExactlyOnceWith 3.0.0+ * **Type** : `(...args: any[]) => Awaitable` This assertion checks if a function was called exactly once and with certain parameters. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' const market = { buy(subject: string, amount: number) { // ... }, } test('spy function', () => { const buySpy = vi.spyOn(market, 'buy') market.buy('apples', 10) expect(buySpy).toHaveBeenCalledExactlyOnceWith('apples', 10) }) ## toHaveBeenLastCalledWith * **Type** : `(...args: any[]) => Awaitable` This assertion checks if a function was called with certain parameters at its last invocation. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' const market = { buy(subject: string, amount: number) { // ... }, } test('spy function', () => { const buySpy = vi.spyOn(market, 'buy') market.buy('apples', 10) market.buy('apples', 20) expect(buySpy).not.toHaveBeenLastCalledWith('apples', 10) expect(buySpy).toHaveBeenLastCalledWith('apples', 20) }) ## toHaveBeenNthCalledWith * **Type** : `(time: number, ...args: any[]) => Awaitable` This assertion checks if a function was called with certain parameters at the certain time. The count starts at 1. So, to check the second entry, you would write `.toHaveBeenNthCalledWith(2, ...)`. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' const market = { buy(subject: string, amount: number) { // ... }, } test('first call of spy function called with right params', () => { const buySpy = vi.spyOn(market, 'buy') market.buy('apples', 10) market.buy('apples', 20) expect(buySpy).toHaveBeenNthCalledWith(1, 'apples', 10) }) ## toHaveReturned * **Type** : `() => Awaitable` This assertion checks if a function has successfully returned a value at least once (i.e., did not throw an error). Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' function getApplesPrice(amount: number) { const PRICE = 10 return amount * PRICE } test('spy function returned a value', () => { const getPriceSpy = vi.fn(getApplesPrice) const price = getPriceSpy(10) expect(price).toBe(100) expect(getPriceSpy).toHaveReturned() }) ## toHaveReturnedTimes * **Type** : `(amount: number) => Awaitable` This assertion checks if a function has successfully returned a value an exact amount of times (i.e., did not throw an error). Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' test('spy function returns a value two times', () => { const sell = vi.fn((product: string) => ({ product })) sell('apples') sell('bananas') expect(sell).toHaveReturnedTimes(2) }) ## toHaveReturnedWith * **Type** : `(returnValue: any) => Awaitable` You can call this assertion to check if a function has successfully returned a value with certain parameters at least once. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' test('spy function returns a product', () => { const sell = vi.fn((product: string) => ({ product })) sell('apples') expect(sell).toHaveReturnedWith({ product: 'apples' }) }) ## toHaveLastReturnedWith * **Type** : `(returnValue: any) => Awaitable` You can call this assertion to check if a function has successfully returned a certain value when it was last invoked. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' test('spy function returns bananas on a last call', () => { const sell = vi.fn((product: string) => ({ product })) sell('apples') sell('bananas') expect(sell).toHaveLastReturnedWith({ product: 'bananas' }) }) ## toHaveNthReturnedWith * **Type** : `(time: number, returnValue: any) => Awaitable` You can call this assertion to check if a function has successfully returned a value with certain parameters on a certain call. Requires a spy function to be passed to `expect`. import { expect, test, vi } from 'vitest' test('spy function returns bananas on second call', () => { const sell = vi.fn((product: string) => ({ product })) sell('apples') sell('bananas') expect(sell).toHaveNthReturnedWith(2, { product: 'bananas' }) }) ## toHaveResolved * **Type** : `() => Awaitable` This assertion checks if a function has successfully resolved a value at least once (i.e., did not reject). Requires a spy function to be passed to `expect`. If the function returned a promise, but it was not resolved yet, this will fail. import { expect, test, vi } from 'vitest' import db from './db/apples.js' async function getApplesPrice(amount: number) { return amount * await db.get('price') } test('spy function resolved a value', async () => { const getPriceSpy = vi.fn(getApplesPrice) const price = await getPriceSpy(10) expect(price).toBe(100) expect(getPriceSpy).toHaveResolved() }) ## toHaveResolvedTimes * **Type** : `(amount: number) => Awaitable` This assertion checks if a function has successfully resolved a value an exact amount of times (i.e., did not reject). Requires a spy function to be passed to `expect`. This will only count resolved promises. If the function returned a promise, but it was not resolved yet, it will not be counted. import { expect, test, vi } from 'vitest' test('spy function resolved a value two times', async () => { const sell = vi.fn((product: string) => Promise.resolve({ product })) await sell('apples') await sell('bananas') expect(sell).toHaveResolvedTimes(2) }) ## toHaveResolvedWith * **Type** : `(returnValue: any) => Awaitable` You can call this assertion to check if a function has successfully resolved a certain value at least once. Requires a spy function to be passed to `expect`. If the function returned a promise, but it was not resolved yet, this will fail. import { expect, test, vi } from 'vitest' test('spy function resolved a product', async () => { const sell = vi.fn((product: string) => Promise.resolve({ product })) await sell('apples') expect(sell).toHaveResolvedWith({ product: 'apples' }) }) ## toHaveLastResolvedWith * **Type** : `(returnValue: any) => Awaitable` You can call this assertion to check if a function has successfully resolved a certain value when it was last invoked. Requires a spy function to be passed to `expect`. If the function returned a promise, but it was not resolved yet, this will fail. import { expect, test, vi } from 'vitest' test('spy function resolves bananas on a last call', async () => { const sell = vi.fn((product: string) => Promise.resolve({ product })) await sell('apples') await sell('bananas') expect(sell).toHaveLastResolvedWith({ product: 'bananas' }) }) ## toHaveNthResolvedWith * **Type** : `(time: number, returnValue: any) => Awaitable` You can call this assertion to check if a function has successfully resolved a certain value on a specific invocation. Requires a spy function to be passed to `expect`. If the function returned a promise, but it was not resolved yet, this will fail. import { expect, test, vi } from 'vitest' test('spy function returns bananas on second call', async () => { const sell = vi.fn((product: string) => Promise.resolve({ product })) await sell('apples') await sell('bananas') expect(sell).toHaveNthResolvedWith(2, { product: 'bananas' }) }) ## toSatisfy * **Type:** `(predicate: (value: any) => boolean) => Awaitable` This assertion checks if a value satisfies a certain predicate. import { describe, expect, it } from 'vitest' const isOdd = (value: number) => value % 2 !== 0 describe('toSatisfy()', () => { it('pass with 0', () => { expect(1).toSatisfy(isOdd) }) it('pass with negation', () => { expect(2).not.toSatisfy(isOdd) }) }) ## resolves * **Type:** `Promisify` `resolves` is intended to remove boilerplate when asserting asynchronous code. Use it to unwrap value from the pending promise and assert its value with usual assertions. If the promise rejects, the assertion will fail. It returns the same `Assertions` object, but all matchers now return `Promise`, so you would need to `await` it. Also works with `chai` assertions. For example, if you have a function, that makes an API call and returns some data, you may use this code to assert its return value: import { expect, test } from 'vitest' async function buyApples() { return fetch('/buy/apples').then(r => r.json()) } test('buyApples returns new stock id', async () => { // toEqual returns a promise now, so you HAVE to await it await expect(buyApples()).resolves.toEqual({ id: 1 }) // jest API await expect(buyApples()).resolves.to.equal({ id: 1 }) // chai API }) **WARNING** If the assertion is not awaited, then you will have a false-positive test that will pass every time. To make sure that assertions are actually called, you may use `expect.assertions(number)`. Since Vitest 3, if a method is not awaited, Vitest will show a warning at the end of the test. In Vitest 4, the test will be marked as "failed" if the assertion is not awaited. ## rejects * **Type:** `Promisify` `rejects` is intended to remove boilerplate when asserting asynchronous code. Use it to unwrap reason why the promise was rejected, and assert its value with usual assertions. If the promise successfully resolves, the assertion will fail. It returns the same `Assertions` object, but all matchers now return `Promise`, so you would need to `await` it. Also works with `chai` assertions. For example, if you have a function that fails when you call it, you may use this code to assert the reason: import { expect, test } from 'vitest' async function buyApples(id) { if (!id) { throw new Error('no id') } } test('buyApples throws an error when no id provided', async () => { // toThrow returns a promise now, so you HAVE to await it await expect(buyApples()).rejects.toThrow('no id') }) **WARNING** If the assertion is not awaited, then you will have a false-positive test that will pass every time. To make sure that assertions were actually called, you can use `expect.assertions(number)`. Since Vitest 3, if a method is not awaited, Vitest will show a warning at the end of the test. In Vitest 4, the test will be marked as "failed" if the assertion is not awaited. ## expect.assertions * **Type:** `(count: number) => void` After the test has passed or failed verify that a certain number of assertions was called during a test. A useful case would be to check if an asynchronous code was called. For example, if we have a function that asynchronously calls two matchers, we can assert that they were actually called. import { expect, test } from 'vitest' async function doAsync(...cbs) { await Promise.all( cbs.map((cb, index) => cb({ index })), ) } test('all assertions are called', async () => { expect.assertions(2) function callback1(data) { expect(data).toBeTruthy() } function callback2(data) { expect(data).toBeTruthy() } await doAsync(callback1, callback2) }) **WARNING** When using `assertions` with async concurrent tests, `expect` from the local [Test Context](../guide/test-context) must be used to ensure the right test is detected. ## expect.hasAssertions * **Type:** `() => void` After the test has passed or failed verify that at least one assertion was called during a test. A useful case would be to check if an asynchronous code was called. For example, if you have a code that calls a callback, we can make an assertion inside a callback, but the test will always pass if we don't check if an assertion was called. import { expect, test } from 'vitest' import { db } from './db.js' const cbs = [] function onSelect(cb) { cbs.push(cb) } // after selecting from db, we call all callbacks function select(id) { return db.select({ id }).then((data) => { return Promise.all( cbs.map(cb => cb(data)), ) }) } test('callback was called', async () => { expect.hasAssertions() onSelect((data) => { // should be called on select expect(data).toBeTruthy() }) // if not awaited, test will fail // if you don't have expect.hasAssertions(), test will pass await select(3) }) ## expect.unreachable * **Type:** `(message?: string) => never` This method is used to assert that a line should never be reached. For example, if we want to test that `build()` throws due to receiving directories having no `src` folder, and also handle each error separately, we could do this: import { expect, test } from 'vitest' async function build(dir) { if (dir.includes('no-src')) { throw new Error(`${dir}/src does not exist`) } } const errorDirs = [ 'no-src-folder', // ... ] test.each(errorDirs)('build fails with "%s"', async (dir) => { try { await build(dir) expect.unreachable('Should not pass build') } catch (err: any) { expect(err).toBeInstanceOf(Error) expect(err.stack).toContain('build') switch (dir) { case 'no-src-folder': expect(err.message).toBe(`${dir}/src does not exist`) break default: // to exhaust all error tests expect.unreachable('All error test must be handled') break } } }) ## expect.anything * **Type:** `() => any` This asymmetric matcher, when used with equality check, will always return `true`. Useful, if you just want to be sure that the property exist. import { expect, test } from 'vitest' test('object has "apples" key', () => { expect({ apples: 22 }).toEqual({ apples: expect.anything() }) }) ## expect.any * **Type:** `(constructor: unknown) => any` This asymmetric matcher, when used with an equality check, will return `true` only if the value is an instance of a specified constructor. Useful, if you have a value that is generated each time, and you only want to know that it exists with a proper type. import { expect, test } from 'vitest' import { generateId } from './generators.js' test('"id" is a number', () => { expect({ id: generateId() }).toEqual({ id: expect.any(Number) }) }) ## expect.closeTo * **Type:** `(expected: any, precision?: number) => any` `expect.closeTo` is useful when comparing floating point numbers in object properties or array item. If you need to compare a number, please use `.toBeCloseTo` instead. The optional `precision` argument limits the number of digits to check **after** the decimal point. For the default value `2`, the test criterion is `Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2)`. For example, this test passes with a precision of 5 digits: test('compare float in object properties', () => { expect({ title: '0.1 + 0.2', sum: 0.1 + 0.2, }).toEqual({ title: '0.1 + 0.2', sum: expect.closeTo(0.3, 5), }) }) ## expect.arrayContaining * **Type:** `(expected: T[]) => any` When used with an equality check, this asymmetric matcher will return `true` if the value is an array and contains specified items. import { expect, test } from 'vitest' test('basket includes fuji', () => { const basket = { varieties: [ 'Empire', 'Fuji', 'Gala', ], count: 3 } expect(basket).toEqual({ count: 3, varieties: expect.arrayContaining(['Fuji']) }) }) **TIP** You can use `expect.not` with this matcher to negate the expected value. ## expect.objectContaining * **Type:** `(expected: any) => any` When used with an equality check, this asymmetric matcher will return `true` if the value has a similar shape. import { expect, test } from 'vitest' test('basket has empire apples', () => { const basket = { varieties: [ { name: 'Empire', count: 1, } ], } expect(basket).toEqual({ varieties: [ expect.objectContaining({ name: 'Empire' }), ] }) }) **TIP** You can use `expect.not` with this matcher to negate the expected value. ## expect.stringContaining * **Type:** `(expected: any) => any` When used with an equality check, this asymmetric matcher will return `true` if the value is a string and contains a specified substring. import { expect, test } from 'vitest' test('variety has "Emp" in its name', () => { const variety = { name: 'Empire', count: 1, } expect(variety).toEqual({ name: expect.stringContaining('Emp'), count: 1, }) }) **TIP** You can use `expect.not` with this matcher to negate the expected value. ## expect.stringMatching * **Type:** `(expected: any) => any` When used with an equality check, this asymmetric matcher will return `true` if the value is a string and contains a specified substring or if the string matches a regular expression. import { expect, test } from 'vitest' test('variety ends with "re"', () => { const variety = { name: 'Empire', count: 1, } expect(variety).toEqual({ name: expect.stringMatching(/re$/), count: 1, }) }) **TIP** You can use `expect.not` with this matcher to negate the expected value. ## expect.addSnapshotSerializer * **Type:** `(plugin: PrettyFormatPlugin) => void` This method adds custom serializers that are called when creating a snapshot. This is an advanced feature - if you want to know more, please read a [guide on custom serializers](../guide/snapshot#custom-serializer). If you are adding custom serializers, you should call this method inside [`setupFiles`](../config/index#setupfiles). This will affect every snapshot. **TIP** If you previously used Vue CLI with Jest, you might want to install [jest- serializer-vue](https://www.npmjs.com/package/jest-serializer-vue). Otherwise, your snapshots will be wrapped in a string, which cases `"` to be escaped. ## expect.extend * **Type:** `(matchers: MatchersObject) => void` You can extend default matchers with your own. This function is used to extend the matchers object with custom matchers. When you define matchers that way, you also create asymmetric matchers that can be used like `expect.stringContaining`. import { expect, test } from 'vitest' test('custom matchers', () => { expect.extend({ toBeFoo: (received, expected) => { if (received !== 'foo') { return { message: () => `expected ${received} to be foo`, pass: false, } } }, }) expect('foo').toBeFoo() expect({ foo: 'foo' }).toEqual({ foo: expect.toBeFoo() }) }) **TIP** If you want your matchers to appear in every test, you should call this method inside [`setupFiles`](../config/index#setupfiles). This function is compatible with Jest's `expect.extend`, so any library that uses it to create custom matchers will work with Vitest. If you are using TypeScript, since Vitest 0.31.0 you can extend default `Assertion` interface in an ambient declaration file (e.g: `vitest.d.ts`) with the code below: interface CustomMatchers { toBeFoo: () => R } declare module 'vitest' { interface Assertion extends CustomMatchers {} interface AsymmetricMatchersContaining extends CustomMatchers {} } **WARNING** Don't forget to include the ambient declaration file in your `tsconfig.json`. **TIP** If you want to know more, checkout [guide on extending matchers](../guide/extending-matchers). ## expect.addEqualityTesters * **Type:** `(tester: Array) => void` You can use this method to define custom testers, which are methods used by matchers, to test if two objects are equal. It is compatible with Jest's `expect.addEqualityTesters`. import { expect, test } from 'vitest' class AnagramComparator { public word: string constructor(word: string) { this.word = word } equals(other: AnagramComparator): boolean { const cleanStr1 = this.word.replace(/ /g, '').toLowerCase() const cleanStr2 = other.word.replace(/ /g, '').toLowerCase() const sortedStr1 = cleanStr1.split('').sort().join('') const sortedStr2 = cleanStr2.split('').sort().join('') return sortedStr1 === sortedStr2 } } function isAnagramComparator(a: unknown): a is AnagramComparator { return a instanceof AnagramComparator } function areAnagramsEqual(a: unknown, b: unknown): boolean | undefined { const isAAnagramComparator = isAnagramComparator(a) const isBAnagramComparator = isAnagramComparator(b) if (isAAnagramComparator && isBAnagramComparator) { return a.equals(b) } else if (isAAnagramComparator === isBAnagramComparator) { return undefined } else { return false } } expect.addEqualityTesters([areAnagramsEqual]) test('custom equality tester', () => { expect(new AnagramComparator('listen')).toEqual(new AnagramComparator('silent')) }) # Test API Reference The following types are used in the type signatures below 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 } 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). Most options support both dot-syntax and object-syntax allowing you to use whatever style you prefer. dot-syntaxobject-syntax import { test } from 'vitest' test.skip('skipped test', () => { // some logic that fails right now }) import { test } from 'vitest' test('skipped test', { skip: true }, () => { // some logic that fails right now }) ## 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/index#testtimeout) import { expect, test } from 'vitest' test('should work as expected', () => { expect(Math.sqrt(4)).toBe(2) }) ### 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#test-extend) for more information. 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. 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: import { assert, test } from 'vitest' test('skipped test', (context) => { context.skip() // 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. 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. 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/index#testtimeout). 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). 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: 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) to ensure the right test is detected. 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. 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. // 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. 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` 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 * `%#`: index of the test case * `%%`: single percent sign ('%') 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 with `$` prefix, if you are using objects as arguments: 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 You can also access Object attributes with `.`, if you are using objects as arguments: 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 Starting from Vitest 0.25.3, you can also use template string table. * First row should be column names, separated by `|`; * One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. 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/index#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 of `test.each` to provide [`TestContext`](../guide/test-context). The difference from `test.each` is how array case is provided in the arguments. Other non array case (including template string usage) works exactly same. // `each` spreads array case test.each([ [1, 1, 2], [1, 2, 3], [2, 1, 3], ])('add(%i, %i) -> %i', (a, b, expected) => { expect(a + b).toBe(expected) }) // `for` doesn't spread array case test.for([ [1, 1, 2], [1, 2, 3], [2, 1, 3], ])('add(%i, %i) -> %i', ([a, b, expected]) => { expect(a + b).toBe(expected) }) 2nd argument is [`TestContext`](../guide/test-context) and it can be used for concurrent snapshot, for example, 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 [`tinybench`](https://github.com/tinylibs/tinybench) library under the hood, inheriting all its options that can be used as a third argument. import { bench } from 'vitest' bench('normal sorting', () => { const x = [1, 5, 4, 2, 3] x.sort((a, b) => { return a - b }) }, { time: 1000 }) 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 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. 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. 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. 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. // 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) }) }) // 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: 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. 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. 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. 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 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 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: 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. 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. 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/index#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. 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. // 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` which simplifies argument types and aligns with `test.for`. Use `describe.each` if you have more than one test that depends on the same data. 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) }) }) Starting from Vitest 0.25.3, you can also use template string table. * First row should be column names, separated by `|`; * One or more subsequent rows of data supplied as template literal expressions using `${value}` syntax. 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. // `each` spreads array case describe.each([ [1, 1, 2], [1, 2, 3], [2, 1, 3], ])('add(%i, %i) -> %i', (a, b, expected) => { 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]) => { 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. import { beforeEach } from 'vitest' beforeEach(async () => { // Clear mocks and add some testing data after 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`). 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. 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** Vitest 1.3.0 added `onTestFinished` hook. You can call it 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. 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`). 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. 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 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`. 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: 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: // 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/index#sequence-hooks) option. Note that this hook is not called if test was skipped with a dynamic `ctx.skip()` call: test('skipped dynamically', (t) => { onTestFinished(() => {}) // not called t.skip() }) ### 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. import { onTestFailed, test } from 'vitest' test('performs a query', () => { const db = connectDb() onTestFailed((e) => { console.log(e.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: import { test } from 'vitest' test.concurrent('performs a query', ({ onTestFailed }) => { const db = connectDb() onTestFailed((result) => { console.log(result.errors) }) db.query('SELECT * FROM users') }) # Mock Functions You can create a mock function to track its execution with `vi.fn` method. If you want to track a method on an already created object, you can use `vi.spyOn` method: import { vi } from 'vitest' const fn = vi.fn() fn('hello world') fn.mock.calls[0] === ['hello world'] const market = { getApples: () => 100 } const getApplesSpy = vi.spyOn(market, 'getApples') market.getApples() getApplesSpy.mock.calls.length === 1 You should use mock assertions (e.g., [`toHaveBeenCalled`](expect#tohavebeencalled)) on [`expect`](expect) to assert mock result. This API reference describes available properties and methods to manipulate mock behavior. **TIP** The custom function implementation in the types below is marked with a generic ``. ## getMockImplementation function getMockImplementation(): T | undefined Returns current mock implementation if there is one. If the mock was created with [`vi.fn`](vi#vi-fn), it will use the provided method as the mock implementation. If the mock was created with [`vi.spyOn`](vi#vi-spyon), it will return `undefined` unless a custom implementation is provided. ## getMockName function getMockName(): string Use it to return the name assigned to the mock with the `.mockName(name)` method. By default, it will return `vi.fn()`. ## mockClear function mockClear(): MockInstance Clears all information about every call. After calling it, all properties on `.mock` will return to their initial state. This method does not reset implementations. It is useful for cleaning up mocks between different assertions. To automatically call this method before each test, enable the [`clearMocks`](../config/index#clearmocks) setting in the configuration. ## mockName function mockName(name: string): MockInstance Sets the internal mock name. This is useful for identifying the mock when an assertion fails. ## mockImplementation function mockImplementation(fn: T): MockInstance Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function. const mockFn = vi.fn().mockImplementation((apples: number) => apples + 1) // or: vi.fn(apples => apples + 1); const NelliesBucket = mockFn(0) const BobsBucket = mockFn(1) NelliesBucket === 1 // true BobsBucket === 2 // true mockFn.mock.calls[0][0] === 0 // true mockFn.mock.calls[1][0] === 1 // true ## mockImplementationOnce function mockImplementationOnce(fn: T): MockInstance Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function. This method can be chained to produce different results for multiple function calls. const myMockFn = vi .fn() .mockImplementationOnce(() => true) // 1st call .mockImplementationOnce(() => false) // 2nd call myMockFn() // 1st call: true myMockFn() // 2nd call: false When the mocked function runs out of implementations, it will invoke the default implementation set with `vi.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if they were called: const myMockFn = vi .fn(() => 'default') .mockImplementationOnce(() => 'first call') .mockImplementationOnce(() => 'second call') // 'first call', 'second call', 'default', 'default' console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()) ## withImplementation function withImplementation( fn: T, cb: () => void ): MockInstance function withImplementation( fn: T, cb: () => Promise ): Promise> Overrides the original mock implementation temporarily while the callback is being executed. const myMockFn = vi.fn(() => 'original') myMockFn.withImplementation(() => 'temp', () => { myMockFn() // 'temp' }) myMockFn() // 'original' Can be used with an asynchronous callback. The method has to be awaited to use the original implementation afterward. test('async callback', () => { const myMockFn = vi.fn(() => 'original') // We await this call since the callback is async await myMockFn.withImplementation( () => 'temp', async () => { myMockFn() // 'temp' }, ) myMockFn() // 'original' }) Note that this method takes precedence over the `mockImplementationOnce`. ## mockRejectedValue function mockRejectedValue(value: unknown): MockInstance Accepts an error that will be rejected when async function is called. const asyncMock = vi.fn().mockRejectedValue(new Error('Async error')) await asyncMock() // throws Error<'Async error'> ## mockRejectedValueOnce function mockRejectedValueOnce(value: unknown): MockInstance Accepts a value that will be rejected during the next function call. If chained, each consecutive call will reject the specified value. const asyncMock = vi .fn() .mockResolvedValueOnce('first call') .mockRejectedValueOnce(new Error('Async error')) await asyncMock() // 'first call' await asyncMock() // throws Error<'Async error'> ## mockReset function mockReset(): MockInstance Does what `mockClear` does and resets inner implementation to the original function. This also resets all "once" implementations. Note that resetting a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`. resetting a mock from `vi.fn(impl)` will restore implementation to `impl`. This is useful when you want to reset a mock to its original state. To automatically call this method before each test, enable the [`mockReset`](../config/index#mockreset) setting in the configuration. ## mockRestore function mockRestore(): MockInstance Does what `mockReset` does and restores original descriptors of spied-on objects. Note that restoring a mock from `vi.fn()` will set implementation to an empty function that returns `undefined`. Restoring a mock from `vi.fn(impl)` will restore implementation to `impl`. To automatically call this method before each test, enable the [`restoreMocks`](../config/index#restoremocks) setting in the configuration. ## mockResolvedValue function mockResolvedValue(value: Awaited>): MockInstance Accepts a value that will be resolved when the async function is called. TypeScript will only accept values that match the return type of the original function. const asyncMock = vi.fn().mockResolvedValue(42) await asyncMock() // 42 ## mockResolvedValueOnce function mockResolvedValueOnce(value: Awaited>): MockInstance Accepts a value that will be resolved during the next function call. TypeScript will only accept values that match the return type of the original function. If chained, each consecutive call will resolve the specified value. const asyncMock = vi .fn() .mockResolvedValue('default') .mockResolvedValueOnce('first call') .mockResolvedValueOnce('second call') await asyncMock() // first call await asyncMock() // second call await asyncMock() // default await asyncMock() // default ## mockReturnThis function mockReturnThis(): MockInstance Use this if you need to return the `this` context from the method without invoking the actual implementation. This is a shorthand for: spy.mockImplementation(function () { return this }) ## mockReturnValue function mockReturnValue(value: ReturnType): MockInstance Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function. const mock = vi.fn() mock.mockReturnValue(42) mock() // 42 mock.mockReturnValue(43) mock() // 43 ## mockReturnValueOnce function mockReturnValueOnce(value: ReturnType): MockInstance Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function. When the mocked function runs out of implementations, it will invoke the default implementation set with `vi.fn(() => defaultValue)` or `.mockImplementation(() => defaultValue)` if they were called: const myMockFn = vi .fn() .mockReturnValue('default') .mockReturnValueOnce('first call') .mockReturnValueOnce('second call') // 'first call', 'second call', 'default', 'default' console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()) ## mock.calls const calls: Parameters[] This is an array containing all arguments for each call. One item of the array is the arguments of that call. const fn = vi.fn() fn('arg1', 'arg2') fn('arg3') fn.mock.calls === [ ['arg1', 'arg2'], // first call ['arg3'], // second call ] ## mock.lastCall const lastCall: Parameters | undefined This contains the arguments of the last call. If mock wasn't called, it will return `undefined`. ## mock.results interface MockResultReturn { type: 'return' /** * The value that was returned from the function. * If function returned a Promise, then this will be a resolved value. */ value: T } interface MockResultIncomplete { type: 'incomplete' value: undefined } interface MockResultThrow { type: 'throw' /** * An error that was thrown during function execution. */ value: any } type MockResult = | MockResultReturn | MockResultThrow | MockResultIncomplete const results: MockResult>[] This is an array containing all values that were `returned` from the function. One item of the array is an object with properties `type` and `value`. Available types are: * `'return'` \- function returned without throwing. * `'throw'` \- function threw a value. The `value` property contains the returned value or thrown error. If the function returned a `Promise`, then `result` will always be `'return'` even if the promise was rejected. const fn = vi.fn() .mockReturnValueOnce('result') .mockImplementationOnce(() => { throw new Error('thrown error') }) const result = fn() // returned 'result' try { fn() // threw Error } catch {} fn.mock.results === [ // first result { type: 'return', value: 'result', }, // last result { type: 'throw', value: Error, }, ] ## mock.settledResults interface MockSettledResultFulfilled { type: 'fulfilled' value: T } interface MockSettledResultRejected { type: 'rejected' value: any } export type MockSettledResult = | MockSettledResultFulfilled | MockSettledResultRejected const settledResults: MockSettledResult>>[] An array containing all values that were `resolved` or `rejected` from the function. This array will be empty if the function was never resolved or rejected. const fn = vi.fn().mockResolvedValueOnce('result') const result = fn() fn.mock.settledResults === [] await result fn.mock.settledResults === [ { type: 'fulfilled', value: 'result', }, ] ## mock.invocationCallOrder const invocationCallOrder: number[] This property returns the order of the mock function's execution. It is an array of numbers that are shared between all defined mocks. const fn1 = vi.fn() const fn2 = vi.fn() fn1() fn2() fn1() fn1.mock.invocationCallOrder === [1, 3] fn2.mock.invocationCallOrder === [2] ## mock.contexts const contexts: ThisParameterType[] This property is an array of `this` values used during each call to the mock function. const fn = vi.fn() const context = {} fn.apply(context) fn.call(context) fn.mock.contexts[0] === context fn.mock.contexts[1] === context ## mock.instances const instances: ReturnType[] This property is an array containing all instances that were created when the mock was called with the `new` keyword. Note that this is an actual context (`this`) of the function, not a return value. **WARNING** If mock was instantiated with `new MyClass()`, then `mock.instances` will be an array with one value: const MyClass = vi.fn() const a = new MyClass() MyClass.mock.instances[0] === a If you return a value from constructor, it will not be in `instances` array, but instead inside `results`: const Spy = vi.fn(() => ({ method: vi.fn() })) const a = new Spy() Spy.mock.instances[0] !== a Spy.mock.results[0] === a # Vi Vitest provides utility functions to help you out through its `vi` helper. You can access it globally (when [globals configuration](../config/index#globals) is enabled), or import it from `vitest` directly: import { vi } from 'vitest' ## Mock Modules This section describes the API that you can use when [mocking a module](../guide/mocking#modules). Beware that Vitest doesn't support mocking modules imported using `require()`. ### vi.mock * **Type** : `(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void` * **Type** : `(path: Promise, factory?: MockOptions | ((importOriginal: () => T) => T | Promise)) => void` Substitutes all imported modules from provided `path` with another module. You can use configured Vite aliases inside a path. The call to `vi.mock` is hoisted, so it doesn't matter where you call it. It will always be executed before all imports. If you need to reference some variables outside of its scope, you can define them inside `vi.hoisted` and reference them inside `vi.mock`. **WARNING** `vi.mock` works only for modules that were imported with the `import` keyword. It doesn't work with `require`. In order to hoist `vi.mock`, Vitest statically analyzes your files. It indicates that `vi` that was not directly imported from the `vitest` package (for example, from some utility file) cannot be used. Use `vi.mock` with `vi` imported from `vitest`, or enable [`globals`](../config/index#globals) config option. Vitest will not mock modules that were imported inside a [setup file](../config/index#setupfiles) because they are cached by the time a test file is running. You can call `vi.resetModules()` inside `vi.hoisted` to clear all module caches before running a test file. If the `factory` function is defined, all imports will return its result. Vitest calls factory only once and caches results for all subsequent imports until `vi.unmock` or `vi.doUnmock` is called. Unlike in `jest`, the factory can be asynchronous. You can use `vi.importActual` or a helper with the factory passed in as the first argument, and get the original module inside. You can also provide an object with a `spy` property instead of a factory function. If `spy` is `true`, then Vitest will automock the module as usual, but it won't override the implementation of exports. This is useful if you just want to assert that the exported method was called correctly by another method. import { calculator } from './src/calculator.ts' vi.mock('./src/calculator.ts', { spy: true }) // calls the original implementation, // but allows asserting the behaviour later const result = calculator(1, 2) expect(result).toBe(3) expect(calculator).toHaveBeenCalledWith(1, 2) expect(calculator).toHaveReturned(3) Vitest also supports a module promise instead of a string in the `vi.mock` and `vi.doMock` methods for better IDE support. When the file is moved, the path will be updated, and `importOriginal` inherits the type automatically. Using this signature will also enforce factory return type to be compatible with the original module (keeping exports optional). .(import('./path/to/module.js'), async () => { const = await () // type is inferred return { ..., // replace some exports : .(), } }) Under the hood, Vitest still operates on a string and not a module object. If you are using TypeScript with `paths` aliases configured in `tsconfig.json` however, the compiler won't be able to correctly resolve import types. In order to make it work, make sure to replace all aliased imports, with their corresponding relative paths. Eg. use `import('./path/to/module.js')` instead of `import('@/module')`. **WARNING** `vi.mock` is hoisted (in other words, _moved_) to **top of the file**. It means that whenever you write it (be it inside `beforeEach` or `test`), it will actually be called before that. This also means that you cannot use any variables inside the factory that are defined outside the factory. If you need to use variables inside the factory, try `vi.doMock`. It works the same way but isn't hoisted. Beware that it only mocks subsequent imports. You can also reference variables defined by `vi.hoisted` method if it was declared before `vi.mock`: import { namedExport } from './path/to/module.js' const mocks = vi.hoisted(() => { return { namedExport: vi.fn(), } }) vi.mock('./path/to/module.js', () => { return { namedExport: mocks.namedExport, } }) vi.mocked(namedExport).mockReturnValue(100) expect(namedExport()).toBe(100) expect(namedExport).toBe(mocks.namedExport) **WARNING** If you are mocking a module with default export, you will need to provide a `default` key within the returned factory function object. This is an ES module-specific caveat; therefore, `jest` documentation may differ as `jest` uses CommonJS modules. For example, vi.mock('./path/to/module.js', () => { return { default: { myDefaultKey: vi.fn() }, namedExport: vi.fn(), // etc... } }) If there is a `__mocks__` folder alongside a file that you are mocking, and the factory is not provided, Vitest will try to find a file with the same name in the `__mocks__` subfolder and use it as an actual module. If you are mocking a dependency, Vitest will try to find a `__mocks__` folder in the [root](../config/index#root) of the project (default is `process.cwd()`). You can tell Vitest where the dependencies are located through the [`deps.moduleDirectories`](../config/index#deps-moduledirectories) config option. For example, you have this file structure: - __mocks__ - axios.js - src __mocks__ - increment.js - increment.js - tests - increment.test.js If you call `vi.mock` in a test file without a factory or options provided, it will find a file in the `__mocks__` folder to use as a module: increment.test.js import { vi } from 'vitest' // axios is a default export from `__mocks__/axios.js` import axios from 'axios' // increment is a named export from `src/__mocks__/increment.js` import { increment } from '../increment.js' vi.mock('axios') vi.mock('../increment.js') axios.get(`/apples/${increment(1)}`) **WARNING** Beware that if you don't call `vi.mock`, modules **are not** mocked automatically. To replicate Jest's automocking behaviour, you can call `vi.mock` for each required module inside [`setupFiles`](../config/index#setupfiles). If there is no `__mocks__` folder or a factory provided, Vitest will import the original module and auto-mock all its exports. For the rules applied, see [algorithm](../guide/mocking#automocking-algorithm). ### vi.doMock * **Type** : `(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void` * **Type** : `(path: Promise, factory?: MockOptions | ((importOriginal: () => T) => T | Promise)) => void` The same as `vi.mock`, but it's not hoisted to the top of the file, so you can reference variables in the global file scope. The next [dynamic import](https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Operators/import) of the module will be mocked. **WARNING** This will not mock modules that were imported before this was called. Don't forget that all static imports in ESM are always [hoisted](https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Statements/import#hoisting), so putting this before static import will not force it to be called before the import: vi.doMock('./increment.js') // this will be called _after_ the import statement import { increment } from './increment.js' increment.js export function increment(number) { return number + 1 } increment.test.js import { beforeEach, test } from 'vitest' import { increment } from './increment.js' // the module is not mocked, because vi.doMock is not called yet increment(1) === 2 let mockedIncrement = 100 beforeEach(() => { // you can access variables inside a factory vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement })) }) test('importing the next module imports mocked one', async () => { // original import WAS NOT MOCKED, because vi.doMock is evaluated AFTER imports expect(increment(1)).toBe(2) const { increment: mockedIncrement } = await import('./increment.js') // new dynamic import returns mocked module expect(mockedIncrement(1)).toBe(101) expect(mockedIncrement(1)).toBe(102) expect(mockedIncrement(1)).toBe(103) }) ### vi.mocked * **Type** : `(obj: T, deep?: boolean) => MaybeMockedDeep` * **Type** : `(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep` Type helper for TypeScript. Just returns the object that was passed. When `partial` is `true` it will expect a `Partial` as a return value. By default, this will only make TypeScript believe that the first level values are mocked. You can pass down `{ deep: true }` as a second argument to tell TypeScript that the whole object is mocked, if it actually is. example.ts export function add(x: number, y: number): number { return x + y } export function fetchSomething(): Promise { return fetch('https://vitest.dev/') } example.test.ts import * as example from './example' vi.mock('./example') test('1 + 1 equals 10', async () => { vi.mocked(example.add).mockReturnValue(10) expect(example.add(1, 1)).toBe(10) }) test('mock return value with only partially correct typing', async () => { vi.mocked(example.fetchSomething).mockResolvedValue(new Response('hello')) vi.mocked(example.fetchSomething, { partial: true }).mockResolvedValue({ ok: false }) // vi.mocked(example.someFn).mockResolvedValue({ ok: false }) // this is a type error }) ### vi.importActual * **Type** : `(path: string) => Promise` Imports module, bypassing all checks if it should be mocked. Can be useful if you want to mock module partially. vi.mock('./example.js', async () => { const originalModule = await vi.importActual('./example.js') return { ...originalModule, get: vi.fn() } }) ### vi.importMock * **Type** : `(path: string) => Promise>` Imports a module with all of its properties (including nested properties) mocked. Follows the same rules that `vi.mock` does. For the rules applied, see [algorithm](../guide/mocking#automocking-algorithm). ### vi.unmock * **Type** : `(path: string | Promise) => void` Removes module from the mocked registry. All calls to import will return the original module even if it was mocked before. This call is hoisted to the top of the file, so it will only unmock modules that were defined in `setupFiles`, for example. ### vi.doUnmock * **Type** : `(path: string | Promise) => void` The same as `vi.unmock`, but is not hoisted to the top of the file. The next import of the module will import the original module instead of the mock. This will not unmock previously imported modules. increment.js export function increment(number) { return number + 1 } increment.test.js import { increment } from './increment.js' // increment is already mocked, because vi.mock is hoisted increment(1) === 100 // this is hoisted, and factory is called before the import on line 1 vi.mock('./increment.js', () => ({ increment: () => 100 })) // all calls are mocked, and `increment` always returns 100 increment(1) === 100 increment(30) === 100 // this is not hoisted, so other import will return unmocked module vi.doUnmock('./increment.js') // this STILL returns 100, because `vi.doUnmock` doesn't reevaluate a module increment(1) === 100 increment(30) === 100 // the next import is unmocked, now `increment` is the original function that returns count + 1 const { increment: unmockedIncrement } = await import('./increment.js') unmockedIncrement(1) === 2 unmockedIncrement(30) === 31 ### vi.resetModules * **Type** : `() => Vitest` Resets modules registry by clearing the cache of all modules. This allows modules to be reevaluated when reimported. Top-level imports cannot be re- evaluated. Might be useful to isolate modules where local state conflicts between tests. import { vi } from 'vitest' import { data } from './data.js' // Will not get reevaluated beforeEach test beforeEach(() => { vi.resetModules() }) test('change state', async () => { const mod = await import('./some/path.js') // Will get reevaluated mod.changeLocalState('new value') expect(mod.getLocalState()).toBe('new value') }) test('module has old state', async () => { const mod = await import('./some/path.js') // Will get reevaluated expect(mod.getLocalState()).toBe('old value') }) **WARNING** Does not reset mocks registry. To clear mocks registry, use `vi.unmock` or `vi.doUnmock`. ### vi.dynamicImportSettled Wait for all imports to load. Useful, if you have a synchronous call that starts importing a module that you cannot wait otherwise. import { expect, test } from 'vitest' // cannot track import because Promise is not returned function renderComponent() { import('./component.js').then(({ render }) => { render() }) } test('operations are resolved', async () => { renderComponent() await vi.dynamicImportSettled() expect(document.querySelector('.component')).not.toBeNull() }) **TIP** If during a dynamic import another dynamic import is initiated, this method will wait until all of them are resolved. This method will also wait for the next `setTimeout` tick after the import is resolved so all synchronous operations should be completed by the time it's resolved. ## Mocking Functions and Objects This section describes how to work with [method mocks](mock) and replace environmental and global variables. ### vi.fn * **Type:** `(fn?: Function) => Mock` Creates a spy on a function, though can be initiated without one. Every time a function is invoked, it stores its call arguments, returns, and instances. Also, you can manipulate its behavior with [methods](mock). If no function is given, mock will return `undefined`, when invoked. const getApples = vi.fn(() => 0) getApples() expect(getApples).toHaveBeenCalled() expect(getApples).toHaveReturnedWith(0) getApples.mockReturnValueOnce(5) const res = getApples() expect(res).toBe(5) expect(getApples).toHaveNthReturnedWith(2, 5) ### vi.isMockFunction * **Type:** `(fn: Function) => boolean` Checks that a given parameter is a mock function. If you are using TypeScript, it will also narrow down its type. ### vi.clearAllMocks Calls [`.mockClear()`](mock#mockclear) on all spies. This will clear mock history without affecting mock implementations. ### vi.resetAllMocks Calls [`.mockReset()`](mock#mockreset) on all spies. This will clear mock history and reset each mock's implementation to its original. ### vi.restoreAllMocks Calls [`.mockRestore()`](mock#mockrestore) on all spies. This will clear mock history, restore all original mock implementations, and restore original descriptors of spied-on objects. ### vi.spyOn * **Type:** `(object: T, method: K, accessType?: 'get' | 'set') => MockInstance` Creates a spy on a method or getter/setter of an object similar to `vi.fn()`. It returns a [mock function](mock). let apples = 0 const cart = { getApples: () => 42, } const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples) apples = 1 expect(cart.getApples()).toBe(1) expect(spy).toHaveBeenCalled() expect(spy).toHaveReturnedWith(1) **TIP** You can call `vi.restoreAllMocks` inside [`afterEach`](index#aftereach) (or enable [`test.restoreMocks`](../config/index#restoreMocks)) to restore all methods to their original implementations. 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: const cart = { getApples: () => 42, } const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10) console.log(cart.getApples()) // 10 vi.restoreAllMocks() console.log(cart.getApples()) // 42 spy.mockReturnValue(10) console.log(cart.getApples()) // still 42! **TIP** It is not possible to spy on exported methods in [Browser Mode](../guide/browser/index). Instead, you can spy on every exported method by calling `vi.mock("./file-path.js", { spy: true })`. This will mock every export but keep its implementation intact, allowing you to assert if the method was called correctly. import { calculator } from './src/calculator.ts' vi.mock('./src/calculator.ts', { spy: true }) calculator(1, 2) expect(calculator).toHaveBeenCalledWith(1, 2) expect(calculator).toHaveReturned(3) And while it is possible to spy on exports in `jsdom` or other Node.js environments, this might change in the future. ### vi.stubEnv * **Type:** `(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest` Changes the value of environmental variable on `process.env` and `import.meta.env`. You can restore its value by calling `vi.unstubAllEnvs`. import { vi } from 'vitest' // `process.env.NODE_ENV` and `import.meta.env.NODE_ENV` // are "development" before calling "vi.stubEnv" vi.stubEnv('NODE_ENV', 'production') process.env.NODE_ENV === 'production' import.meta.env.NODE_ENV === 'production' vi.stubEnv('NODE_ENV', undefined) process.env.NODE_ENV === undefined import.meta.env.NODE_ENV === undefined // doesn't change other envs import.meta.env.MODE === 'development' **TIP** You can also change the value by simply assigning it, but you won't be able to use `vi.unstubAllEnvs` to restore previous value: import.meta.env.MODE = 'test' ### vi.unstubAllEnvs * **Type:** `() => Vitest` Restores all `import.meta.env` and `process.env` values that were changed with `vi.stubEnv`. When it's called for the first time, Vitest remembers the original value and will store it, until `unstubAllEnvs` is called again. import { vi } from 'vitest' // `process.env.NODE_ENV` and `import.meta.env.NODE_ENV` // are "development" before calling stubEnv vi.stubEnv('NODE_ENV', 'production') process.env.NODE_ENV === 'production' import.meta.env.NODE_ENV === 'production' vi.stubEnv('NODE_ENV', 'staging') process.env.NODE_ENV === 'staging' import.meta.env.NODE_ENV === 'staging' vi.unstubAllEnvs() // restores to the value that were stored before the first "stubEnv" call process.env.NODE_ENV === 'development' import.meta.env.NODE_ENV === 'development' ### vi.stubGlobal * **Type:** `(name: string | number | symbol, value: unknown) => Vitest` Changes the value of global variable. You can restore its original value by calling `vi.unstubAllGlobals`. import { vi } from 'vitest' // `innerWidth` is "0" before calling stubGlobal vi.stubGlobal('innerWidth', 100) innerWidth === 100 globalThis.innerWidth === 100 // if you are using jsdom or happy-dom window.innerWidth === 100 **TIP** You can also change the value by simply assigning it to `globalThis` or `window` (if you are using `jsdom` or `happy-dom` environment), but you won't be able to use `vi.unstubAllGlobals` to restore original value: globalThis.innerWidth = 100 // if you are using jsdom or happy-dom window.innerWidth = 100 ### vi.unstubAllGlobals * **Type:** `() => Vitest` Restores all global values on `globalThis`/`global` (and `window`/`top`/`self`/`parent`, if you are using `jsdom` or `happy-dom` environment) that were changed with `vi.stubGlobal`. When it's called for the first time, Vitest remembers the original value and will store it, until `unstubAllGlobals` is called again. import { vi } from 'vitest' const Mock = vi.fn() // IntersectionObserver is "undefined" before calling "stubGlobal" vi.stubGlobal('IntersectionObserver', Mock) IntersectionObserver === Mock global.IntersectionObserver === Mock globalThis.IntersectionObserver === Mock // if you are using jsdom or happy-dom window.IntersectionObserver === Mock vi.unstubAllGlobals() globalThis.IntersectionObserver === undefined 'IntersectionObserver' in globalThis === false // throws ReferenceError, because it's not defined IntersectionObserver === undefined ## Fake Timers This sections describes how to work with [fake timers](../guide/mocking#timers). ### vi.advanceTimersByTime * **Type:** `(ms: number) => Vitest` This method will invoke every initiated timer until the specified number of milliseconds is passed or the queue is empty - whatever comes first. let i = 0 setInterval(() => console.log(++i), 50) vi.advanceTimersByTime(150) // log: 1 // log: 2 // log: 3 ### vi.advanceTimersByTimeAsync * **Type:** `(ms: number) => Promise` This method will invoke every initiated timer until the specified number of milliseconds is passed or the queue is empty - whatever comes first. This will include asynchronously set timers. let i = 0 setInterval(() => Promise.resolve().then(() => console.log(++i)), 50) await vi.advanceTimersByTimeAsync(150) // log: 1 // log: 2 // log: 3 ### vi.advanceTimersToNextTimer * **Type:** `() => Vitest` Will call next available timer. Useful to make assertions between each timer call. You can chain call it to manage timers by yourself. let i = 0 setInterval(() => console.log(++i), 50) vi.advanceTimersToNextTimer() // log: 1 .advanceTimersToNextTimer() // log: 2 .advanceTimersToNextTimer() // log: 3 ### vi.advanceTimersToNextTimerAsync * **Type:** `() => Promise` Will call next available timer and wait until it's resolved if it was set asynchronously. Useful to make assertions between each timer call. let i = 0 setInterval(() => Promise.resolve().then(() => console.log(++i)), 50) await vi.advanceTimersToNextTimerAsync() // log: 1 expect(console.log).toHaveBeenCalledWith(1) await vi.advanceTimersToNextTimerAsync() // log: 2 await vi.advanceTimersToNextTimerAsync() // log: 3 ### vi.advanceTimersToNextFrame 2.1.0+ * **Type:** `() => Vitest` Similar to [`vi.advanceTimersByTime`](vi#vi-advancetimersbytime), but will advance timers by the milliseconds needed to execute callbacks currently scheduled with `requestAnimationFrame`. let frameRendered = false requestAnimationFrame(() => { frameRendered = true }) vi.advanceTimersToNextFrame() expect(frameRendered).toBe(true) ### vi.getTimerCount * **Type:** `() => number` Get the number of waiting timers. ### vi.clearAllTimers Removes all timers that are scheduled to run. These timers will never run in the future. ### vi.getMockedSystemTime * **Type** : `() => Date | null` Returns mocked current date. If date is not mocked the method will return `null`. ### vi.getRealSystemTime * **Type** : `() => number` When using `vi.useFakeTimers`, `Date.now` calls are mocked. If you need to get real time in milliseconds, you can call this function. ### vi.runAllTicks * **Type:** `() => Vitest` Calls every microtask that was queued by `process.nextTick`. This will also run all microtasks scheduled by themselves. ### vi.runAllTimers * **Type:** `() => Vitest` This method will invoke every initiated timer until the timer queue is empty. It means that every timer called during `runAllTimers` will be fired. If you have an infinite interval, it will throw after 10 000 tries (can be configured with [`fakeTimers.loopLimit`](../config/index#faketimers-looplimit)). let i = 0 setTimeout(() => console.log(++i)) const interval = setInterval(() => { console.log(++i) if (i === 3) { clearInterval(interval) } }, 50) vi.runAllTimers() // log: 1 // log: 2 // log: 3 ### vi.runAllTimersAsync * **Type:** `() => Promise` This method will asynchronously invoke every initiated timer until the timer queue is empty. It means that every timer called during `runAllTimersAsync` will be fired even asynchronous timers. If you have an infinite interval, it will throw after 10 000 tries (can be configured with [`fakeTimers.loopLimit`](../config/index#faketimers-looplimit)). setTimeout(async () => { console.log(await Promise.resolve('result')) }, 100) await vi.runAllTimersAsync() // log: result ### vi.runOnlyPendingTimers * **Type:** `() => Vitest` This method will call every timer that was initiated after `vi.useFakeTimers` call. It will not fire any timer that was initiated during its call. let i = 0 setInterval(() => console.log(++i), 50) vi.runOnlyPendingTimers() // log: 1 ### vi.runOnlyPendingTimersAsync * **Type:** `() => Promise` This method will asynchronously call every timer that was initiated after `vi.useFakeTimers` call, even asynchronous ones. It will not fire any timer that was initiated during its call. setTimeout(() => { console.log(1) }, 100) setTimeout(() => { Promise.resolve().then(() => { console.log(2) setInterval(() => { console.log(3) }, 40) }) }, 10) await vi.runOnlyPendingTimersAsync() // log: 2 // log: 3 // log: 3 // log: 1 ### vi.setSystemTime * **Type** : `(date: string | number | Date) => void` If fake timers are enabled, this method simulates a user changing the system clock (will affect date related API like `hrtime`, `performance.now` or `new Date()`) - however, it will not fire any timers. If fake timers are not enabled, this method will only mock `Date.*` calls. Useful if you need to test anything that depends on the current date - for example [Luxon](https://github.com/moment/luxon/) calls inside your code. Accepts the same string and number arguments as the `Date`. const date = new Date(1998, 11, 19) vi.useFakeTimers() vi.setSystemTime(date) expect(Date.now()).toBe(date.valueOf()) vi.useRealTimers() ### vi.useFakeTimers * **Type:** `(config?: FakeTimerInstallOpts) => Vitest` To enable mocking timers, you need to call this method. It will wrap all further calls to timers (such as `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `setImmediate`, `clearImmediate`, and `Date`) until `vi.useRealTimers()` is called. Mocking `nextTick` is not supported when running Vitest inside `node:child_process` by using `--pool=forks`. NodeJS uses `process.nextTick` internally in `node:child_process` and hangs when it is mocked. Mocking `nextTick` is supported when running Vitest with `--pool=threads`. The implementation is based internally on [`@sinonjs/fake- timers`](https://github.com/sinonjs/fake-timers). **TIP** `vi.useFakeTimers()` does not automatically mock `process.nextTick` and `queueMicrotask`. But you can enable it by specifying the option in `toFake` argument: `vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })`. ### vi.isFakeTimers * **Type:** `() => boolean` Returns `true` if fake timers are enabled. ### vi.useRealTimers * **Type:** `() => Vitest` When timers are run out, you may call this method to return mocked timers to its original implementations. All timers that were scheduled before will be discarded. ## Miscellaneous A set of useful helper functions that Vitest provides. ### vi.waitFor * **Type:** `(callback: WaitForCallback, options?: number | WaitForOptions) => Promise` Wait for the callback to execute successfully. If the callback throws an error or returns a rejected promise it will continue to wait until it succeeds or times out. If options is set to a number, the effect is equivalent to setting `{ timeout: options }`. This is very useful when you need to wait for some asynchronous action to complete, for example, when you start a server and need to wait for it to start. import { expect, test, vi } from 'vitest' import { createServer } from './server.js' test('Server started successfully', async () => { const server = createServer() await vi.waitFor( () => { if (!server.isReady) { throw new Error('Server not started') } console.log('Server started') }, { timeout: 500, // default is 1000 interval: 20, // default is 50 } ) expect(server.isReady).toBe(true) }) It also works for asynchronous callbacks // @vitest-environment jsdom import { expect, test, vi } from 'vitest' import { getDOMElementAsync, populateDOMAsync } from './dom.js' test('Element exists in a DOM', async () => { // start populating DOM populateDOMAsync() const element = await vi.waitFor(async () => { // try to get the element until it exists const element = await getDOMElementAsync() as HTMLElement | null expect(element).toBeTruthy() expect(element.dataset.initialized).toBeTruthy() return element }, { timeout: 500, // default is 1000 interval: 20, // default is 50 }) expect(element).toBeInstanceOf(HTMLElement) }) If `vi.useFakeTimers` is used, `vi.waitFor` automatically calls `vi.advanceTimersByTime(interval)` in every check callback. ### vi.waitUntil * **Type:** `(callback: WaitUntilCallback, options?: number | WaitUntilOptions) => Promise` This is similar to `vi.waitFor`, but if the callback throws any errors, execution is immediately interrupted and an error message is received. If the callback returns falsy value, the next check will continue until truthy value is returned. This is useful when you need to wait for something to exist before taking the next step. Look at the example below. We can use `vi.waitUntil` to wait for the element to appear on the page, and then we can do something with the element. import { expect, test, vi } from 'vitest' test('Element render correctly', async () => { const element = await vi.waitUntil( () => document.querySelector('.element'), { timeout: 500, // default is 1000 interval: 20, // default is 50 } ) // do something with the element expect(element.querySelector('.element-child')).toBeTruthy() }) ### vi.hoisted * **Type** : `(factory: () => T) => T` All static `import` statements in ES modules are hoisted to the top of the file, so any code that is defined before the imports will actually be executed after imports are evaluated. However, it can be useful to invoke some side effects like mocking dates before importing a module. To bypass this limitation, you can rewrite static imports into dynamic ones like this: callFunctionWithSideEffect() - import { value } from './some/module.js' + const { value } = await import('./some/module.js') When running `vitest`, you can do this automatically by using `vi.hoisted` method. Under the hood, Vitest will convert static imports into dynamic ones with preserved live-bindings. - callFunctionWithSideEffect() import { value } from './some/module.js' + vi.hoisted(() => callFunctionWithSideEffect()) **IMPORTS ARE NOT AVAILABLE** Running code before the imports means that you cannot access imported variables because they are not defined yet: import { value } from './some/module.js' vi.hoisted(() => { value }) // throws an error // This code will produce an error: Cannot access '__vi_import_0__' before initialization If you need to access a variable from another module inside of `vi.hoisted`, use dynamic import: await vi.hoisted(async () => { const { value } = await import('./some/module.js') }) However, it is discourage to import anything inside of `vi.hoisted` because imports are already hoisted - if you need to execute something before the tests are running, just execute it in the imported module itself. This method returns the value that was returned from the factory. You can use that value in your `vi.mock` factories if you need easy access to locally defined variables: import { expect, vi } from 'vitest' import { originalMethod } from './path/to/module.js' const { mockedMethod } = vi.hoisted(() => { return { mockedMethod: vi.fn() } }) vi.mock('./path/to/module.js', () => { return { originalMethod: mockedMethod } }) mockedMethod.mockReturnValue(100) expect(originalMethod()).toBe(100) Note that this method can also be called asynchronously even if your environment doesn't support top-level await: const json = await vi.hoisted(async () => { const response = await fetch('https://jsonplaceholder.typicode.com/posts') return response.json() }) ### vi.setConfig * **Type** : `RuntimeConfig` Updates config for the current test file. This method supports only config options that will affect the current test file: vi.setConfig({ allowOnly: true, testTimeout: 10_000, hookTimeout: 10_000, clearMocks: true, restoreMocks: true, fakeTimers: { now: new Date(2021, 11, 19), // supports the whole object }, maxConcurrency: 10, sequence: { hooks: 'stack' // supports only "sequence.hooks" } }) ### vi.resetConfig * **Type** : `RuntimeConfig` If `vi.setConfig` was called before, this will reset config to the original state. # Configuring Vitest If you are using Vite and have a `vite.config` file, Vitest will read it to match with the plugins and setup as your Vite app. If you want to have a different configuration for testing or your main app doesn't rely on Vite specifically, you could either: * Create `vitest.config.ts`, which will have the higher priority and will **override** the configuration from `vite.config.ts` (Vitest supports all conventional JS and TS extensions, but doesn't support `json`) - it means all options in your `vite.config` will be **ignored** * Pass `--config` option to CLI, e.g. `vitest --config ./path/to/vitest.config.ts` * Use `process.env.VITEST` or `mode` property on `defineConfig` (will be set to `test`/`benchmark` if not overridden with `--mode`) to conditionally apply different configuration in `vite.config.ts` To configure `vitest` itself, add `test` property in your Vite config. You'll also need to add a reference to Vitest types using a [triple slash command](https://www.typescriptlang.org/docs/handbook/triple-slash- directives.html#-reference-types-) at the top of your config file, if you are importing `defineConfig` from `vite` itself. Open Config Examples Using `defineConfig` from `vite` you should follow this: vite.config.js /// import { defineConfig } from 'vite' export default defineConfig({ test: { // ... Specify options here. }, }) The `` will stop working in Vitest 4, but you can already start migrating to `vitest/config`: vite.config.js /// import { defineConfig } from 'vite' export default defineConfig({ test: { // ... Specify options here. }, }) Using `defineConfig` from `vitest/config` you should follow this: vitest.config.js import { defineConfig } from 'vitest/config' export default defineConfig({ test: { // ... Specify options here. }, }) You can retrieve Vitest's default options to expand them if needed: vitest.config.js import { configDefaults, defineConfig } from 'vitest/config' export default defineConfig({ test: { exclude: [...configDefaults.exclude, 'packages/template/*'], }, }) When using a separate `vitest.config.js`, you can also extend Vite's options from another config file if needed: vitest.config.js import { defineConfig, mergeConfig } from 'vitest/config' import viteConfig from './vite.config' export default mergeConfig(viteConfig, defineConfig({ test: { exclude: ['packages/template/*'], }, })) If your Vite config is defined as a function, you can define the config like this: vitest.config.js import { defineConfig, mergeConfig } from 'vitest/config' import viteConfig from './vite.config' export default defineConfig(configEnv => mergeConfig( viteConfig(configEnv), defineConfig({ test: { exclude: ['packages/template/*'], }, }) )) **WARNING** _All listed options_ on this page are located within a `test` property inside the configuration: vitest.config.js export default defineConfig({ test: { exclude: [], }, }) Since Vitest uses Vite config, you can also use any configuration option from [Vite](https://vitejs.dev/config/). For example, `define` to define global variables, or `resolve.alias` to define aliases - these options should be defined on the top level, _not_ within a `test` property. Configuration options that are not supported inside a [workspace](../guide/workspace) project config have * sign next to them. ### include * **Type:** `string[]` * **Default:** `['**/*.{test,spec}.?(c|m)[jt]s?(x)']` * **CLI:** `vitest [...include]`, `vitest **/*.test.js` A list of glob patterns that match your test files. **NOTE** When using coverage, Vitest automatically adds test files `include` patterns to coverage's default `exclude` patterns. See `coverage.exclude`. ### exclude * **Type:** `string[]` * **Default:** `['**/node_modules/**', '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**', '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*']` * **CLI:** `vitest --exclude "**/excluded-file"` A list of glob patterns that should be excluded from your test files. **WARNING** This option does not affect coverage. If you need to remove certain files from the coverage report, use `coverage.exclude`. This is the only option that doesn't override your configuration if you provide it with a CLI flag. All glob patterns added via `--exclude` flag will be added to the config's `exclude`. ### includeSource * **Type:** `string[]` * **Default:** `[]` Include globs for in-source test files. When defined, Vitest will run all matched files with `import.meta.vitest` inside. ### name * **Type:** `string` Assign a custom name to the test project or Vitest process. The name will be visible in the CLI and available in the Node.js API via [`project.name`](../advanced/api/test-project#name). ### server * **Type:** `{ sourcemap?, deps?, ... }` Vite-Node server options. #### server.sourcemap * **Type:** `'inline' | boolean` * **Default:** `'inline'` Inject inline source map to modules. #### server.debug * **Type:** `{ dumpModules?, loadDumppedModules? }` Vite-Node debugger options. #### server.debug.dumpModules * **Type:** `boolean | string` Dump the transformed module to filesystem. Passing a string will dump to the specified path. #### server.debug.loadDumppedModules * **Type:** `boolean` Read dumped module from filesystem whenever exists. Useful for debugging by modifying the dump result from the filesystem. #### server.deps * **Type:** `{ external?, inline?, ... }` Handling for dependencies resolution. #### server.deps.external * **Type:** `(string | RegExp)[]` * **Default:** `[/\/node_modules\//]` Externalize means that Vite will bypass the package to the native Node. Externalized dependencies will not be applied to Vite's transformers and resolvers, so they do not support HMR on reload. By default, all packages inside `node_modules` are externalized. These options support package names as they are written in `node_modules` or specified inside `deps.moduleDirectories`. For example, package `@company/some-name` located inside `packages/some-name` should be specified as `some-name`, and `packages` should be included in `deps.moduleDirectories`. Basically, Vitest always checks the file path, not the actual package name. If regexp is used, Vitest calls it on the _file path_ , not the package name. #### server.deps.inline * **Type:** `(string | RegExp)[] | true` * **Default:** `[]` Vite will process inlined modules. This could be helpful to handle packages that ship `.js` in ESM format (that Node can't handle). If `true`, every dependency will be inlined. All dependencies, specified in [`ssr.noExternal`](https://vitejs.dev/guide/ssr.html#ssr-externals) will be inlined by default. #### server.deps.fallbackCJS * **Type** `boolean` * **Default:** `false` When a dependency is a valid ESM package, try to guess the cjs version based on the path. This might be helpful, if a dependency has the wrong ESM file. This might potentially cause some misalignment if a package has different logic in ESM and CJS mode. #### server.deps.cacheDir * **Type** `string` * **Default** : `'node_modules/.vite'` Directory to save cache files. ### deps * **Type:** `{ optimizer?, ... }` Handling for dependencies resolution. #### deps.optimizer * **Type:** `{ ssr?, web? }` * **See also:** [Dep Optimization Options](https://vitejs.dev/config/dep-optimization-options.html) Enable dependency optimization. If you have a lot of tests, this might improve their performance. When Vitest encounters the external library listed in `include`, it will be bundled into a single file using esbuild and imported as a whole module. This is good for several reasons: * Importing packages with a lot of imports is expensive. By bundling them into one file we can save a lot of time * Importing UI libraries is expensive because they are not meant to run inside Node.js * Your `alias` configuration is now respected inside bundled packages * Code in your tests is running closer to how it's running in the browser Be aware that only packages in `deps.optimizer?.[mode].include` option are bundled (some plugins populate this automatically, like Svelte). You can read more about available options in [Vite](https://vitejs.dev/config/dep- optimization-options.html) docs (Vitest doesn't support `disable` and `noDiscovery` options). By default, Vitest uses `optimizer.web` for `jsdom` and `happy-dom` environments, and `optimizer.ssr` for `node` and `edge` environments, but it is configurable by `transformMode`. This options also inherits your `optimizeDeps` configuration (for web Vitest will extend `optimizeDeps`, for ssr - `ssr.optimizeDeps`). If you redefine `include`/`exclude` option in `deps.optimizer` it will extend your `optimizeDeps` when running tests. Vitest automatically removes the same options from `include`, if they are listed in `exclude`. **TIP** You will not be able to edit your `node_modules` code for debugging, since the code is actually located in your `cacheDir` or `test.cache.dir` directory. If you want to debug with `console.log` statements, edit it directly or force rebundling with `deps.optimizer?.[mode].force` option. #### deps.optimizer.{mode}.enabled * **Type:** `boolean` * **Default:** `false` Enable dependency optimization. #### deps.web * **Type:** `{ transformAssets?, ... }` Options that are applied to external files when transform mode is set to `web`. By default, `jsdom` and `happy-dom` use `web` mode, while `node` and `edge` environments use `ssr` transform mode, so these options will have no affect on files inside those environments. Usually, files inside `node_modules` are externalized, but these options also affect files in `server.deps.external`. #### deps.web.transformAssets * **Type:** `boolean` * **Default:** `true` Should Vitest process assets (.png, .svg, .jpg, etc) files and resolve them like Vite does in the browser. This module will have a default export equal to the path to the asset, if no query is specified. **WARNING** At the moment, this option only works with `vmThreads` and `vmForks` pools. #### deps.web.transformCss * **Type:** `boolean` * **Default:** `true` Should Vitest process CSS (.css, .scss, .sass, etc) files and resolve them like Vite does in the browser. If CSS files are disabled with `css` options, this option will just silence `ERR_UNKNOWN_FILE_EXTENSION` errors. **WARNING** At the moment, this option only works with `vmThreads` and `vmForks` pools. #### deps.web.transformGlobPattern * **Type:** `RegExp | RegExp[]` * **Default:** `[]` Regexp pattern to match external files that should be transformed. By default, files inside `node_modules` are externalized and not transformed, unless it's CSS or an asset, and corresponding option is not disabled. **WARNING** At the moment, this option only works with `vmThreads` and `vmForks` pools. #### deps.interopDefault * **Type:** `boolean` * **Default:** `true` Interpret CJS module's default as named exports. Some dependencies only bundle CJS modules and don't use named exports that Node.js can statically analyze when a package is imported using `import` syntax instead of `require`. When importing such dependencies in Node environment using named exports, you will see this error: import { read } from 'fs-jetpack'; ^^^^ SyntaxError: Named export 'read' not found. The requested module 'fs-jetpack' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export. Vitest doesn't do static analysis, and cannot fail before your running code, so you will most likely see this error when running tests, if this feature is disabled: TypeError: createAsyncThunk is not a function TypeError: default is not a function By default, Vitest assumes you are using a bundler to bypass this and will not fail, but you can disable this behaviour manually, if you code is not processed. #### deps.moduleDirectories * **Type:** `string[]` * **Default** : `['node_modules']` A list of directories that should be treated as module directories. This config option affects the behavior of [`vi.mock`](../api/vi#vi-mock): when no factory is provided and the path of what you are mocking matches one of the `moduleDirectories` values, Vitest will try to resolve the mock by looking for a `__mocks__` folder in the root of the project. This option will also affect if a file should be treated as a module when externalizing dependencies. By default, Vitest imports external modules with native Node.js bypassing Vite transformation step. Setting this option will _override_ the default, if you wish to still search `node_modules` for packages include it along with any other options: import { defineConfig } from 'vitest/config' export default defineConfig({ test: { deps: { moduleDirectories: ['node_modules', path.resolve('../../packages')], } }, }) ### runner * **Type** : `VitestRunnerConstructor` * **Default** : `node`, when running tests, or `benchmark`, when running benchmarks Path to a custom test runner. This is an advanced feature and should be used with custom library runners. You can read more about it in [the documentation](../advanced/runner). ### benchmark * **Type:** `{ include?, exclude?, ... }` Options used when running `vitest bench`. #### benchmark.include * **Type:** `string[]` * **Default:** `['**/*.{bench,benchmark}.?(c|m)[jt]s?(x)']` Include globs for benchmark test files #### benchmark.exclude * **Type:** `string[]` * **Default:** `['node_modules', 'dist', '.idea', '.git', '.cache']` Exclude globs for benchmark test files #### benchmark.includeSource * **Type:** `string[]` * **Default:** `[]` Include globs for in-source benchmark test files. This option is similar to `includeSource`. When defined, Vitest will run all matched files with `import.meta.vitest` inside. #### benchmark.reporters * **Type:** `Arrayable` * **Default:** `'default'` Custom reporter for output. Can contain one or more built-in report names, reporter instances, and/or paths to custom reporters. #### benchmark.outputFile Deprecated in favor of `benchmark.outputJson`. #### benchmark.outputJson * **Type:** `string | undefined` * **Default:** `undefined` A file path to store the benchmark result, which can be used for `--compare` option later. For example: # save main branch's result git checkout main vitest bench --outputJson main.json # change a branch and compare against main git checkout feature vitest bench --compare main.json #### benchmark.compare * **Type:** `string | undefined` * **Default:** `undefined` A file path to a previous benchmark result to compare against current runs. ### alias * **Type:** `Record | Array<{ find: string | RegExp, replacement: string, customResolver?: ResolverFunction | ResolverObject }>` Define custom aliases when running inside tests. They will be merged with aliases from `resolve.alias`. **WARNING** Vitest uses Vite SSR primitives to run tests which has [certain pitfalls](https://vitejs.dev/guide/ssr.html#ssr-externals). 1. Aliases affect only modules imported directly with an `import` keyword by an inlined module (all source code is inlined by default). 2. Vitest does not support aliasing `require` calls. 3. If you are aliasing an external dependency (e.g., `react` -> `preact`), you may want to alias the actual `node_modules` packages instead to make it work for externalized dependencies. Both [Yarn](https://classic.yarnpkg.com/en/docs/cli/add/#toc-yarn-add-alias) and [pnpm](https://pnpm.io/aliases/) support aliasing via the `npm:` prefix. ### globals * **Type:** `boolean` * **Default:** `false` * **CLI:** `--globals`, `--globals=false` By default, `vitest` does not provide global APIs for explicitness. If you prefer to use the APIs globally like Jest, you can pass the `--globals` option to CLI or add `globals: true` in the config. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { globals: true, }, }) To get TypeScript working with the global APIs, add `vitest/globals` to the `types` field in your `tsconfig.json` tsconfig.json { "compilerOptions": { "types": ["vitest/globals"] } } If you are already using [`unplugin-auto- import`](https://github.com/antfu/unplugin-auto-import) in your project, you can also use it directly for auto importing those APIs. vitest.config.js import { defineConfig } from 'vitest/config' import AutoImport from 'unplugin-auto-import/vite' export default defineConfig({ plugins: [ AutoImport({ imports: ['vitest'], dts: true, // generate TypeScript declaration }), ], }) ### environment * **Type:** `'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' | string` * **Default:** `'node'` * **CLI:** `--environment=` The environment that will be used for testing. The default environment in Vitest is a Node.js environment. If you are building a web application, you can use browser-like environment through either [`jsdom`](https://github.com/jsdom/jsdom) or [`happy- dom`](https://github.com/capricorn86/happy-dom) instead. If you are building edge functions, you can use [`edge-runtime`](https://edge- runtime.vercel.app/packages/vm) environment **TIP** You can also use [Browser Mode](../guide/browser/index) to run integration or unit tests in the browser without mocking the environment. By adding a `@vitest-environment` docblock or comment at the top of the file, you can specify another environment to be used for all tests in that file: Docblock style: /** * @vitest-environment jsdom */ test('use jsdom in this test file', () => { const element = document.createElement('div') expect(element).not.toBeNull() }) Comment style: // @vitest-environment happy-dom test('use happy-dom in this test file', () => { const element = document.createElement('div') expect(element).not.toBeNull() }) For compatibility with Jest, there is also a `@jest-environment`: /** * @jest-environment jsdom */ test('use jsdom in this test file', () => { const element = document.createElement('div') expect(element).not.toBeNull() }) If you are running Vitest with `--isolate=false` flag, your tests will be run in this order: `node`, `jsdom`, `happy-dom`, `edge-runtime`, `custom environments`. Meaning, that every test with the same environment is grouped, but is still running sequentially. Starting from 0.23.0, you can also define custom environment. When non-builtin environment is used, Vitest will try to load package `vitest- environment-${name}`. That package should export an object with the shape of `Environment`: environment.js import type { Environment } from 'vitest' export default { name: 'custom', transformMode: 'ssr', setup() { // custom setup return { teardown() { // called after all tests with this env have been run } } } } Vitest also exposes `builtinEnvironments` through `vitest/environments` entry, in case you just want to extend it. You can read more about extending environments in [our guide](../guide/environment). **TIP** jsdom environment exposes `jsdom` global variable equal to the current [JSDOM](https://github.com/jsdom/jsdom) instance. If you want TypeScript to recognize it, you can add `vitest/jsdom` to your `tsconfig.json` when you use this environment: tsconfig.json { "compilerOptions": { "types": ["vitest/jsdom"] } } ### environmentOptions * **Type:** `Record<'jsdom' | string, unknown>` * **Default:** `{}` These options are passed down to `setup` method of current `environment`. By default, you can configure only JSDOM options, if you are using it as your test environment. ### environmentMatchGlobs * **Type:** `[string, EnvironmentName][]` * **Default:** `[]` **DEPRECATED** This API was deprecated in Vitest 3. Use [workspace](../guide/workspace) to define different configurations instead. export default defineConfig({ test: { environmentMatchGlobs: [ ['./*.jsdom.test.ts', 'jsdom'], ], workspace: [ { extends: true, test: { environment: 'jsdom', }, }, ], }, }) Automatically assign environment based on globs. The first match will be used. For example: import { defineConfig } from 'vitest/config' export default defineConfig({ test: { environmentMatchGlobs: [ // all tests in tests/dom will run in jsdom ['tests/dom/**', 'jsdom'], // all tests in tests/ with .edge.test.ts will run in edge-runtime ['**\/*.edge.test.ts', 'edge-runtime'], // ... ] } }) ### poolMatchGlobs * **Type:** `[string, 'threads' | 'forks' | 'vmThreads' | 'vmForks' | 'typescript'][]` * **Default:** `[]` **DEPRECATED** This API was deprecated in Vitest 3. Use [workspace](../guide/workspace) to define different configurations instead: export default defineConfig({ test: { poolMatchGlobs: [ ['./*.threads.test.ts', 'threads'], ], workspace: [ { test: { extends: true, pool: 'threads', }, }, ], }, }) Automatically assign pool in which tests will run based on globs. The first match will be used. For example: import { defineConfig } from 'vitest/config' export default defineConfig({ test: { poolMatchGlobs: [ // all tests in "worker-specific" directory will run inside a worker as if you enabled `--pool=threads` for them, ['**/tests/worker-specific/**', 'threads'], // run all tests in "browser" directory in an actual browser ['**/tests/browser/**', 'browser'], // all other tests will run based on "browser.enabled" and "threads" options, if you didn't specify other globs // ... ] } }) ### update * * **Type:** `boolean` * **Default:** `false` * **CLI:** `-u`, `--update`, `--update=false` Update snapshot files. This will update all changed snapshots and delete obsolete ones. ### watch * * **Type:** `boolean` * **Default:** `!process.env.CI` * **CLI:** `-w`, `--watch`, `--watch=false` Enable watch mode ### root * **Type:** `string` * **CLI:** `-r `, `--root=` Project root ### dir * **Type:** `string` * **CLI:** `--dir=` * **Default:** same as `root` Base directory to scan for the test files. You can specify this option to speed up test discovery if your root covers the whole project ### reporters * * **Type:** `Reporter | Reporter[]` * **Default:** `'default'` * **CLI:** `--reporter=`, `--reporter= --reporter=` Custom [reporters](../guide/reporters) for output. Reporters can be [a Reporter instance](https://github.com/vitest- dev/vitest/blob/main/packages/vitest/src/node/types/reporter.ts), a string to select built-in reporters, or a path to a custom implementation (e.g. `'./path/to/reporter.ts'`, `'@scope/reporter'`). ### outputFile * * **Type:** `string | Record` * **CLI:** `--outputFile=`, `--outputFile.json=./path` Write test results to a file when the `--reporter=json`, `--reporter=html` or `--reporter=junit` option is also specified. By providing an object instead of a string you can define individual outputs when using multiple reporters. ### pool * * **Type:** `'threads' | 'forks' | 'vmThreads' | 'vmForks'` * **Default:** `'forks'` * **CLI:** `--pool=threads` Pool used to run tests in. #### threads * Enable multi-threading using [tinypool](https://github.com/tinylibs/tinypool) (a lightweight fork of [Piscina](https://github.com/piscinajs/piscina)). When using threads you are unable to use process related APIs such as `process.chdir()`. Some libraries written in native languages, such as Prisma, `bcrypt` and `canvas`, have problems when running in multiple threads and run into segfaults. In these cases it is advised to use `forks` pool instead. #### forks * Similar as `threads` pool but uses `child_process` instead of `worker_threads` via [tinypool](https://github.com/tinylibs/tinypool). Communication between tests and main process is not as fast as with `threads` pool. Process related APIs such as `process.chdir()` are available in `forks` pool. #### vmThreads * Run tests using [VM context](https://nodejs.org/api/vm.html) (inside a sandboxed environment) in a `threads` pool. This makes tests run faster, but the VM module is unstable when running [ESM code](https://github.com/nodejs/node/issues/37648). Your tests will [leak memory](https://github.com/nodejs/node/issues/33439) \- to battle that, consider manually editing `poolOptions.vmThreads.memoryLimit` value. **WARNING** Running code in a sandbox has some advantages (faster tests), but also comes with a number of disadvantages. * The globals within native modules, such as (`fs`, `path`, etc), differ from the globals present in your test environment. As a result, any error thrown by these native modules will reference a different Error constructor compared to the one used in your code: try { fs.writeFileSync('/doesnt exist') } catch (err) { console.log(err instanceof Error) // false } * Importing ES modules caches them indefinitely which introduces memory leaks if you have a lot of contexts (test files). There is no API in Node.js that clears that cache. * Accessing globals [takes longer](https://github.com/nodejs/node/issues/31658) in a sandbox environment. Please, be aware of these issues when using this option. Vitest team cannot fix any of the issues on our side. #### vmForks * Similar as `vmThreads` pool but uses `child_process` instead of `worker_threads` via [tinypool](https://github.com/tinylibs/tinypool). Communication between tests and the main process is not as fast as with `vmThreads` pool. Process related APIs such as `process.chdir()` are available in `vmForks` pool. Please be aware that this pool has the same pitfalls listed in `vmThreads`. ### poolOptions * * **Type:** `Record<'threads' | 'forks' | 'vmThreads' | 'vmForks', {}>` * **Default:** `{}` #### poolOptions.threads Options for `threads` pool. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { poolOptions: { threads: { // Threads related options here } } } }) ##### poolOptions.threads.maxThreads * * **Type:** `number | string` * **Default:** _available CPUs_ Maximum number or percentage of threads. You can also use `VITEST_MAX_THREADS` environment variable. ##### poolOptions.threads.minThreads * * **Type:** `number | string` * **Default:** _available CPUs_ Minimum number or percentage of threads. You can also use `VITEST_MIN_THREADS` environment variable. ##### poolOptions.threads.singleThread * **Type:** `boolean` * **Default:** `false` Run all tests with the same environment inside a single worker thread. This will disable built-in module isolation (your source code or inlined code will still be reevaluated for each test), but can improve test performance. **WARNING** Even though this option will force tests to run one after another, this option is different from Jest's `--runInBand`. Vitest uses workers not only for running tests in parallel, but also to provide isolation. By disabling this option, your tests will run sequentially, but in the same global context, so you must provide isolation yourself. This might cause all sorts of issues, if you are relying on global state (frontend frameworks usually do) or your code relies on environment to be defined separately for each test. But can be a speed boost for your tests (up to 3 times faster), that don't necessarily rely on global state or can easily bypass that. ##### poolOptions.threads.useAtomics * * **Type:** `boolean` * **Default:** `false` Use Atomics to synchronize threads. This can improve performance in some cases, but might cause segfault in older Node versions. ##### poolOptions.threads.isolate * **Type:** `boolean` * **Default:** `true` Isolate environment for each test file. ##### poolOptions.threads.execArgv * * **Type:** `string[]` * **Default:** `[]` Pass additional arguments to `node` in the threads. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information. **WARNING** Be careful when using, it as some options may crash worker, e.g. --prof, --title. See . #### poolOptions.forks Options for `forks` pool. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { poolOptions: { forks: { // Forks related options here } } } }) ##### poolOptions.forks.maxForks * * **Type:** `number | string` * **Default:** _available CPUs_ Maximum number or percentage of forks. You can also use `VITEST_MAX_FORKS` environment variable. ##### poolOptions.forks.minForks * * **Type:** `number | string` * **Default:** _available CPUs_ Minimum number or percentage of forks. You can also use `VITEST_MIN_FORKS` environment variable. ##### poolOptions.forks.isolate * **Type:** `boolean` * **Default:** `true` Isolate environment for each test file. ##### poolOptions.forks.singleFork * **Type:** `boolean` * **Default:** `false` Run all tests with the same environment inside a single child process. This will disable built-in module isolation (your source code or inlined code will still be reevaluated for each test), but can improve test performance. **WARNING** Even though this option will force tests to run one after another, this option is different from Jest's `--runInBand`. Vitest uses child processes not only for running tests in parallel, but also to provide isolation. By disabling this option, your tests will run sequentially, but in the same global context, so you must provide isolation yourself. This might cause all sorts of issues, if you are relying on global state (frontend frameworks usually do) or your code relies on environment to be defined separately for each test. But can be a speed boost for your tests (up to 3 times faster), that don't necessarily rely on global state or can easily bypass that. ##### poolOptions.forks.execArgv * * **Type:** `string[]` * **Default:** `[]` Pass additional arguments to `node` process in the child processes. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information. **WARNING** Be careful when using, it as some options may crash worker, e.g. --prof, --title. See . #### poolOptions.vmThreads Options for `vmThreads` pool. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { poolOptions: { vmThreads: { // VM threads related options here } } } }) ##### poolOptions.vmThreads.maxThreads * * **Type:** `number | string` * **Default:** _available CPUs_ Maximum number or percentage of threads. You can also use `VITEST_MAX_THREADS` environment variable. ##### poolOptions.vmThreads.minThreads * * **Type:** `number | string` * **Default:** _available CPUs_ Minimum number or percentage of threads. You can also use `VITEST_MIN_THREADS` environment variable. ##### poolOptions.vmThreads.memoryLimit * * **Type:** `string | number` * **Default:** `1 / CPU Cores` Specifies the memory limit for workers before they are recycled. This value heavily depends on your environment, so it's better to specify it manually instead of relying on the default. **TIP** The implementation is based on Jest's [`workerIdleMemoryLimit`](https://jestjs.io/docs/configuration#workeridlememorylimit- numberstring). The limit can be specified in a number of different ways and whatever the result is `Math.floor` is used to turn it into an integer value: * `<= 1` \- The value is assumed to be a percentage of system memory. So 0.5 sets the memory limit of the worker to half of the total system memory * `\> 1` \- Assumed to be a fixed byte value. Because of the previous rule if you wanted a value of 1 byte (I don't know why) you could use 1.1. * With units * `50%` \- As above, a percentage of total system memory * `100KB`, `65MB`, etc - With units to denote a fixed memory limit. * `K` / `KB` \- Kilobytes (x1000) * `KiB` \- Kibibytes (x1024) * `M` / `MB` \- Megabytes * `MiB` \- Mebibytes * `G` / `GB` \- Gigabytes * `GiB` \- Gibibytes **WARNING** Percentage based memory limit [does not work on Linux CircleCI](https://github.com/jestjs/jest/issues/11956#issuecomment-1212925677) workers due to incorrect system memory being reported. ##### poolOptions.vmThreads.useAtomics * * **Type:** `boolean` * **Default:** `false` Use Atomics to synchronize threads. This can improve performance in some cases, but might cause segfault in older Node versions. ##### poolOptions.vmThreads.execArgv * * **Type:** `string[]` * **Default:** `[]` Pass additional arguments to `node` process in the VM context. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information. **WARNING** Be careful when using, it as some options may crash worker, e.g. --prof, --title. See . #### poolOptions.vmForks * Options for `vmForks` pool. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { poolOptions: { vmForks: { // VM forks related options here } } } }) ##### poolOptions.vmForks.maxForks * * **Type:** `number | string` * **Default:** _available CPUs_ Maximum number or percentage of forks. You can also use `VITEST_MAX_FORKS` environment variable. ##### poolOptions.vmForks.minForks * * **Type:** `number | string` * **Default:** _available CPUs_ Minimum number or percentage of forks. You can also use `VITEST_MIN_FORKS` environment variable. ##### poolOptions.vmForks.memoryLimit * * **Type:** `string | number` * **Default:** `1 / CPU Cores` Specifies the memory limit for workers before they are recycled. This value heavily depends on your environment, so it's better to specify it manually instead of relying on the default. How the value is calculated is described in `poolOptions.vmThreads.memoryLimit` ##### poolOptions.vmForks.execArgv * * **Type:** `string[]` * **Default:** `[]` Pass additional arguments to `node` process in the VM context. See [Command-line API | Node.js](https://nodejs.org/docs/latest/api/cli.html) for more information. **WARNING** Be careful when using, it as some options may crash worker, e.g. --prof, --title. See . ### fileParallelism * * **Type:** `boolean` * **Default:** `true` * **CLI:** `--no-file-parallelism`, `--fileParallelism=false` Should all test files run in parallel. Setting this to `false` will override `maxWorkers` and `minWorkers` options 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/index#describe-concurrent) or via a config. ### maxWorkers * * **Type:** `number | string` Maximum number or percentage of workers to run tests in. `poolOptions.{threads,vmThreads}.maxThreads`/`poolOptions.forks.maxForks` has higher priority. ### minWorkers * * **Type:** `number | string` Minimum number or percentage of workers to run tests in. `poolOptions.{threads,vmThreads}.minThreads`/`poolOptions.forks.minForks` has higher priority. ### testTimeout * **Type:** `number` * **Default:** `5_000` in Node.js, `15_000` if `browser.enabled` is `true` * **CLI:** `--test-timeout=5000`, `--testTimeout=5000` Default timeout of a test in milliseconds. Use `0` to disable timeout completely. ### hookTimeout * **Type:** `number` * **Default:** `10_000` in Node.js, `30_000` if `browser.enabled` is `true` * **CLI:** `--hook-timeout=10000`, `--hookTimeout=10000` Default timeout of a hook in milliseconds. Use `0` to disable timeout completely. ### teardownTimeout * * **Type:** `number` * **Default:** `10000` * **CLI:** `--teardown-timeout=5000`, `--teardownTimeout=5000` Default timeout to wait for close when Vitest shuts down, in milliseconds ### silent * * **Type:** `boolean` * **Default:** `false` * **CLI:** `--silent`, `--silent=false` Silent console output from tests ### setupFiles * **Type:** `string | string[]` Path to setup files. They will be run before each test file. **INFO** Editing a setup file will automatically trigger a rerun of all tests. You can use `process.env.VITEST_POOL_ID` (integer-like string) inside to distinguish between threads. **TIP** Note, that if you are running `--isolate=false`, this setup file will be run in the same global scope multiple times. Meaning, that you are accessing the same global object before each test, so make sure you are not doing the same thing more than you need. For example, you may rely on a global variable: import { config } from '@some-testing-lib' if (!globalThis.defined) { config.plugins = [myCoolPlugin] computeHeavyThing() globalThis.defined = true } // hooks are reset before each suite afterEach(() => { cleanup() }) globalThis.resetBeforeEachTest = true ### provide 2.1.0+ * **Type:** `Partial` Define values that can be accessed inside your tests using `inject` method. vitest.config.jsapi.test.js import { defineConfig } from 'vitest/config' export default defineConfig({ test: { provide: { API_KEY: '123', }, }, }) import { expect, inject, test } from 'vitest' test('api key is defined', () => { expect(inject('API_KEY')).toBe('123') }) **WARNING** Properties have to be strings and values need to be [serializable](https://developer.mozilla.org/en- US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types) because this object will be transferred between different processes. **TIP** If you are using TypeScript, you will need to augment `ProvidedContext` type for type safe access: vitest.shims.d.ts declare module 'vitest' { export interface ProvidedContext { API_KEY: string } } // mark this file as a module so augmentation works correctly export {} ### globalSetup * **Type:** `string | string[]` Path to global setup files, relative to project root. A global setup file can either export named functions `setup` and `teardown` or a `default` function that returns a teardown function ([example](https://github.com/vitest-dev/vitest/blob/main/test/global- setup/vitest.config.ts)). **INFO** Multiple globalSetup files are possible. setup and teardown are executed sequentially with teardown in reverse order. **WARNING** Global setup runs only if there is at least one running test. This means that global setup might start running during watch mode after test file is changed (the test file will wait for global setup to finish before running). Beware that the global setup is running in a different global scope, so your tests don't have access to variables defined here. However, you can pass down serializable data to tests via `provide` method: example.test.jsglobalSetup.ts 3.0.0+ globalSetup.ts 2.0.0+ import { inject } from 'vitest' inject('wsPort') === 3000 import type { TestProject } from 'vitest/node' export default function setup(project: TestProject) { project.provide('wsPort', 3000) } declare module 'vitest' { export interface ProvidedContext { wsPort: number } } import type { GlobalSetupContext } from 'vitest/node' export default function setup({ provide }: GlobalSetupContext) { provide('wsPort', 3000) } declare module 'vitest' { export interface ProvidedContext { wsPort: number } } Since Vitest 3, you can define a custom callback function to be called when Vitest reruns tests. If the function is asynchronous, the runner will wait for it to complete before executing tests. Note that you cannot destruct the `project` like `{ onTestsRerun }` because it relies on the context. globalSetup.ts import type { TestProject } from 'vitest/node' export default function setup(project: TestProject) { project.onTestsRerun(async () => { await restartDb() }) } ### forceRerunTriggers * * **Type** : `string[]` * **Default:** `['**/package.json/**', '**/vitest.config.*/**', '**/vite.config.*/**']` Glob pattern of file paths that will trigger the whole suite rerun. When paired with the `--changed` argument will run the whole test suite if the trigger is found in the git diff. Useful if you are testing calling CLI commands, because Vite cannot construct a module graph: test('execute a script', async () => { // Vitest cannot rerun this test, if content of `dist/index.js` changes await execa('node', ['dist/index.js']) }) **TIP** Make sure that your files are not excluded by [`server.watch.ignored`](https://vitejs.dev/config/server-options.html#server- watch). ### coverage * You can use [`v8`](https://v8.dev/blog/javascript-code-coverage), [`istanbul`](https://istanbul.js.org/) or [a custom coverage solution](../guide/coverage#custom-coverage-provider) for coverage collection. You can provide coverage options to CLI with dot notation: npx vitest --coverage.enabled --coverage.provider=istanbul --coverage.all **WARNING** If you are using coverage options with dot notation, don't forget to specify `--coverage.enabled`. Do not provide a single `--coverage` option in that case. #### coverage.provider * **Type:** `'v8' | 'istanbul' | 'custom'` * **Default:** `'v8'` * **CLI:** `--coverage.provider=` Use `provider` to select the tool for coverage collection. #### coverage.enabled * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.enabled`, `--coverage.enabled=false` Enables coverage collection. Can be overridden using `--coverage` CLI option. #### coverage.include * **Type:** `string[]` * **Default:** `['**']` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.include=`, `--coverage.include= --coverage.include=` List of files included in coverage as glob patterns #### coverage.extension * **Type:** `string | string[]` * **Default:** `['.js', '.cjs', '.mjs', '.ts', '.mts', '.tsx', '.jsx', '.vue', '.svelte', '.marko', '.astro']` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.extension=`, `--coverage.extension= --coverage.extension=` #### coverage.exclude * **Type:** `string[]` * **Default:** [ 'coverage/**', 'dist/**', '**/node_modules/**', '**/[.]**', 'packages/*/test?(s)/**', '**/*.d.ts', '**/virtual:*', '**/__x00__*', '**/\x00*', 'cypress/**', 'test?(s)/**', 'test?(-*).?(c|m)[jt]s?(x)', '**/*{.,-}{test,spec,bench,benchmark}?(-d).?(c|m)[jt]s?(x)', '**/__tests__/**', '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*', '**/vitest.{workspace,projects}.[jt]s?(on)', '**/.{eslint,mocha,prettier}rc.{?(c|m)js,yml}', ] * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.exclude=`, `--coverage.exclude= --coverage.exclude=` List of files excluded from coverage as glob patterns. This option overrides all default options. Extend the default options when adding new patterns to ignore: import { coverageConfigDefaults, defineConfig } from 'vitest/config' export default defineConfig({ test: { coverage: { exclude: ['**/custom-pattern/**', ...coverageConfigDefaults.exclude] }, }, }) **NOTE** Vitest automatically adds test files `include` patterns to the `coverage.exclude`. It's not possible to show coverage of test files. #### coverage.all * **Type:** `boolean` * **Default:** `true` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.all`, `--coverage.all=false` Whether to include all files, including the untested ones into report. #### coverage.clean * **Type:** `boolean` * **Default:** `true` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.clean`, `--coverage.clean=false` Clean coverage results before running tests #### coverage.cleanOnRerun * **Type:** `boolean` * **Default:** `true` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.cleanOnRerun`, `--coverage.cleanOnRerun=false` Clean coverage report on watch rerun. Set to `false` to preserve coverage results from previous run in watch mode. #### coverage.reportsDirectory * **Type:** `string` * **Default:** `'./coverage'` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.reportsDirectory=` **WARNING** Vitest will delete this directory before running tests if `coverage.clean` is enabled (default value). Directory to write coverage report to. To preview the coverage report in the output of [HTML reporter](../guide/reporters#html-reporter), this option must be set as a sub- directory of the html report directory (for example `./html/coverage`). #### coverage.reporter * **Type:** `string | string[] | [string, {}][]` * **Default:** `['text', 'html', 'clover', 'json']` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.reporter=`, `--coverage.reporter= --coverage.reporter=` Coverage reporters to use. See [istanbul documentation](https://istanbul.js.org/docs/advanced/alternative-reporters/) for detailed list of all reporters. See [`@types/istanbul- reporter`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/276d95e4304b3670eaf6e8e5a7ea9e265a14e338/types/istanbul- reports/index.d.ts) for details about reporter specific options. The reporter has three different types: * A single reporter: `{ reporter: 'html' }` * Multiple reporters without options: `{ reporter: ['html', 'json'] }` * A single or multiple reporters with reporter options: { reporter: [ ['lcov', { 'projectRoot': './src' }], ['json', { 'file': 'coverage.json' }], ['text'] ] } You can also pass custom coverage reporters. See [Guide - Custom Coverage Reporter](../guide/coverage#custom-coverage-reporter) for more information. { reporter: [ // Specify reporter using name of the NPM package '@vitest/custom-coverage-reporter', ['@vitest/custom-coverage-reporter', { someOption: true }], // Specify reporter using local path '/absolute/path/to/custom-reporter.cjs', ['/absolute/path/to/custom-reporter.cjs', { someOption: true }], ] } You can check your coverage report in Vitest UI: check [Vitest UI Coverage](../guide/coverage#vitest-ui) for more details. #### coverage.reportOnFailure * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.reportOnFailure`, `--coverage.reportOnFailure=false` Generate coverage report even when tests fail. #### coverage.allowExternal * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.allowExternal`, `--coverage.allowExternal=false` Collect coverage of files outside the project `root`. #### coverage.excludeAfterRemap 2.1.0+ * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.excludeAfterRemap`, `--coverage.excludeAfterRemap=false` Apply exclusions again after coverage has been remapped to original sources. This is useful when your source files are transpiled and may contain source maps of non-source files. Use this option when you are seeing files that show up in report even if they match your `coverage.exclude` patterns. #### coverage.skipFull * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.skipFull`, `--coverage.skipFull=false` Do not show files with 100% statement, branch, and function coverage. #### coverage.thresholds Options for coverage thresholds. If a threshold is set to a positive number, it will be interpreted as the minimum percentage of coverage required. For example, setting the lines threshold to `90` means that 90% of lines must be covered. If a threshold is set to a negative number, it will be treated as the maximum number of uncovered items allowed. For example, setting the lines threshold to `-10` means that no more than 10 lines may be uncovered. { coverage: { thresholds: { // Requires 90% function coverage functions: 90, // Require that no more than 10 lines are uncovered lines: -10, } } } ##### coverage.thresholds.lines * **Type:** `number` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.thresholds.lines=` Global threshold for lines. ##### coverage.thresholds.functions * **Type:** `number` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.thresholds.functions=` Global threshold for functions. ##### coverage.thresholds.branches * **Type:** `number` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.thresholds.branches=` Global threshold for branches. ##### coverage.thresholds.statements * **Type:** `number` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.thresholds.statements=` Global threshold for statements. ##### coverage.thresholds.perFile * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.thresholds.perFile`, `--coverage.thresholds.perFile=false` Check thresholds per file. ##### coverage.thresholds.autoUpdate * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.thresholds.autoUpdate=` Update all threshold values `lines`, `functions`, `branches` and `statements` to configuration file when current coverage is better than the configured thresholds. This option helps to maintain thresholds when coverage is improved. ##### coverage.thresholds.100 * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.thresholds.100`, `--coverage.thresholds.100=false` Sets global thresholds to 100. Shortcut for `--coverage.thresholds.lines 100 --coverage.thresholds.functions 100 --coverage.thresholds.branches 100 --coverage.thresholds.statements 100`. ##### coverage.thresholds[glob-pattern] * **Type:** `{ statements?: number functions?: number branches?: number lines?: number }` * **Default:** `undefined` * **Available for providers:** `'v8' | 'istanbul'` Sets thresholds for files matching the glob pattern. **NOTE** Vitest counts all files, including those covered by glob-patterns, into the global coverage thresholds. This is different from Jest behavior. { coverage: { thresholds: { // Thresholds for all files functions: 95, branches: 70, // Thresholds for matching glob pattern 'src/utils/**.ts': { statements: 95, functions: 90, branches: 85, lines: 80, }, // Files matching this pattern will only have lines thresholds set. // Global thresholds are not inherited. '**/math.ts': { lines: 100, } } } } ##### coverage.thresholds[glob-pattern].100 2.1.0+ * **Type:** `boolean` * **Default:** `false` * **Available for providers:** `'v8' | 'istanbul'` Sets thresholds to 100 for files matching the glob pattern. { coverage: { thresholds: { // Thresholds for all files functions: 95, branches: 70, // Thresholds for matching glob pattern 'src/utils/**.ts': { 100: true }, '**/math.ts': { 100: true } } } } #### coverage.ignoreEmptyLines * **Type:** `boolean` * **Default:** `true` (`false` in v1) * **Available for providers:** `'v8'` * **CLI:** `--coverage.ignoreEmptyLines=` Ignore empty lines, comments and other non-runtime code, e.g. Typescript types. This option works only if the used compiler removes comments and other non- runtime code from the transpiled code. By default Vite uses ESBuild which removes comments and Typescript types from `.ts`, `.tsx` and `.jsx` files. If you want to apply ESBuild to other files as well, define them in [`esbuild` options](https://vitejs.dev/config/shared-options.html#esbuild): import { defineConfig } from 'vitest/config' export default defineConfig({ esbuild: { // Transpile all files with ESBuild to remove comments from code coverage. // Required for `test.coverage.ignoreEmptyLines` to work: include: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.ts', '**/*.tsx'], }, test: { coverage: { provider: 'v8', ignoreEmptyLines: true, }, }, }) #### coverage.ignoreClassMethods * **Type:** `string[]` * **Default:** `[]` * **Available for providers:** `'istanbul'` * **CLI:** `--coverage.ignoreClassMethods=` Set to array of class method names to ignore for coverage. See [istanbul documentation](https://github.com/istanbuljs/nyc#ignoring-methods) for more information. #### coverage.watermarks * **Type:** { statements?: [number, number], functions?: [number, number], branches?: [number, number], lines?: [number, number] } * **Default:** { statements: [50, 80], functions: [50, 80], branches: [50, 80], lines: [50, 80] } * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.watermarks.statements=50,80`, `--coverage.watermarks.branches=50,80` Watermarks for statements, lines, branches and functions. See [istanbul documentation](https://github.com/istanbuljs/nyc#high-and-low-watermarks) for more information. #### coverage.processingConcurrency * **Type:** `boolean` * **Default:** `Math.min(20, os.availableParallelism?.() ?? os.cpus().length)` * **Available for providers:** `'v8' | 'istanbul'` * **CLI:** `--coverage.processingConcurrency=` Concurrency limit used when processing the coverage results. #### coverage.customProviderModule * **Type:** `string` * **Available for providers:** `'custom'` * **CLI:** `--coverage.customProviderModule=` Specifies the module name or path for the custom coverage provider module. See [Guide - Custom Coverage Provider](../guide/coverage#custom-coverage-provider) for more information. ### testNamePattern * * **Type** `string | RegExp` * **CLI:** `-t `, `--testNamePattern=`, `--test-name-pattern=` Run tests with full names matching the pattern. If you add `OnlyRunThis` to this property, tests not containing the word `OnlyRunThis` in the test name will be skipped. import { expect, test } from 'vitest' // run test('OnlyRunThis', () => { expect(true).toBe(true) }) // skipped test('doNotRun', () => { expect(true).toBe(true) }) ### open * * **Type:** `boolean` * **Default:** `!process.env.CI` * **CLI:** `--open`, `--open=false` Open Vitest UI (WIP) ### api * **Type:** `boolean | number` * **Default:** `false` * **CLI:** `--api`, `--api.port`, `--api.host`, `--api.strictPort` Listen to port and serve API. When set to true, the default port is 51204 ### browser experimental * **Default:** `{ enabled: false }` * **CLI:** `--browser=`, `--browser.name=chrome --browser.headless` Configuration for running browser tests. Please, refer to the ["Browser Config Reference"](../guide/browser/config) article. **WARNING** This is an experimental feature. Breaking changes might not follow SemVer, please pin Vitest's version when using it. ### clearMocks * **Type:** `boolean` * **Default:** `false` Will call [`.mockClear()`](../api/mock#mockclear) on all spies before each test. This will clear mock history without affecting mock implementations. ### mockReset * **Type:** `boolean` * **Default:** `false` Will call [`.mockReset()`](../api/mock#mockreset) on all spies before each test. This will clear mock history and reset each implementation to its original. ### restoreMocks * **Type:** `boolean` * **Default:** `false` Will call [`.mockRestore()`](../api/mock#mockrestore) on all spies before each test. This will clear mock history, restore each implementation to its original, and restore original descriptors of spied-on objects.. ### unstubEnvs * **Type:** `boolean` * **Default:** `false` Will call [`vi.unstubAllEnvs`](../api/vi#vi-unstuballenvs) before each test. ### unstubGlobals * **Type:** `boolean` * **Default:** `false` Will call [`vi.unstubAllGlobals`](../api/vi#vi-unstuballglobals) before each test. ### testTransformMode * **Type:** `{ web?, ssr? }` Determine the transform method for all modules imported inside a test that matches the glob pattern. By default, relies on the environment. For example, tests with JSDOM environment will process all files with `ssr: false` flag and tests with Node environment process all modules with `ssr: true`. #### testTransformMode.ssr * **Type:** `string[]` * **Default:** `[]` Use SSR transform pipeline for all modules inside specified tests. Vite plugins will receive `ssr: true` flag when processing those files. #### testTransformMode.web * **Type:** `string[]` * **Default:** `[]` First do a normal transform pipeline (targeting browser), then do a SSR rewrite to run the code in Node. Vite plugins will receive `ssr: false` flag when processing those files. ### snapshotFormat * * **Type:** `PrettyFormatOptions` Format options for snapshot testing. These options are passed down to [`pretty-format`](https://www.npmjs.com/package/pretty-format). **TIP** Beware that `plugins` field on this object will be ignored. If you need to extend snapshot serializer via pretty-format plugins, please, use [`expect.addSnapshotSerializer`](../api/expect#expect- addsnapshotserializer) API or snapshotSerializers option. ### snapshotSerializers * * **Type:** `string[]` * **Default:** `[]` A list of paths to snapshot serializer modules for snapshot testing, useful if you want add custom snapshot serializers. See [Custom Serializer](../guide/snapshot#custom-serializer) for more information. ### resolveSnapshotPath * * **Type** : `(testPath: string, snapExtension: string, context: { config: SerializedConfig }) => string` * **Default** : stores snapshot files in `__snapshots__` directory Overrides default snapshot path. For example, to store snapshots next to test files: import { defineConfig } from 'vitest/config' export default defineConfig({ test: { resolveSnapshotPath: (testPath, snapExtension) => testPath + snapExtension, }, }) ### allowOnly * **Type** : `boolean` * **Default** : `!process.env.CI` * **CLI:** `--allowOnly`, `--allowOnly=false` Allow tests and suites that are marked as only. ### dangerouslyIgnoreUnhandledErrors * * **Type** : `boolean` * **Default** : `false` * **CLI:** `--dangerouslyIgnoreUnhandledErrors` `--dangerouslyIgnoreUnhandledErrors=false` Ignore any unhandled errors that occur. ### passWithNoTests * * **Type** : `boolean` * **Default** : `false` * **CLI:** `--passWithNoTests`, `--passWithNoTests=false` Vitest will not fail, if no tests will be found. ### logHeapUsage * **Type** : `boolean` * **Default** : `false` * **CLI:** `--logHeapUsage`, `--logHeapUsage=false` Show heap usage after each test. Useful for debugging memory leaks. ### css * **Type** : `boolean | { include?, exclude?, modules? }` Configure if CSS should be processed. When excluded, CSS files will be replaced with empty strings to bypass the subsequent processing. CSS Modules will return a proxy to not affect runtime. #### css.include * **Type** : `RegExp | RegExp[]` * **Default** : `[]` RegExp pattern for files that should return actual CSS and will be processed by Vite pipeline. **TIP** To process all CSS files, use `/.+/`. #### css.exclude * **Type** : `RegExp | RegExp[]` * **Default** : `[]` RegExp pattern for files that will return an empty CSS file. #### css.modules * **Type** : `{ classNameStrategy? }` * **Default** : `{}` #### css.modules.classNameStrategy * **Type** : `'stable' | 'scoped' | 'non-scoped'` * **Default** : `'stable'` If you decide to process CSS files, you can configure if class names inside CSS modules should be scoped. You can choose one of the options: * `stable`: class names will be generated as `_${name}_${hashedFilename}`, which means that generated class will stay the same, if CSS content is changed, but will change, if the name of the file is modified, or file is moved to another folder. This setting is useful, if you use snapshot feature. * `scoped`: class names will be generated as usual, respecting `css.modules.generateScopedName` method, if you have one and CSS processing is enabled. By default, filename will be generated as `_${name}_${hash}`, where hash includes filename and content of the file. * `non-scoped`: class names will not be hashed. **WARNING** By default, Vitest exports a proxy, bypassing CSS Modules processing. If you rely on CSS properties on your classes, you have to enable CSS processing using `include` option. ### maxConcurrency * **Type** : `number` * **Default** : `5` * **CLI** : `--max-concurrency=10`, `--maxConcurrency=10` A number of tests that are allowed to run at the same time marked with `test.concurrent`. Test above this limit will be queued to run when available slot appears. ### cache * * **Type** : `false` * **CLI** : `--no-cache`, `--cache=false` Use this option if you want to disable the cache feature. At the moment Vitest stores cache for test results to run the longer and failed tests first. The cache directory is controlled by the Vite's [`cacheDir`](https://vitejs.dev/config/shared-options.html#cachedir) option: import { defineConfig } from 'vitest/config' export default defineConfig({ cacheDir: 'custom-folder/.vitest' }) You can limit the directory only for Vitest by using `process.env.VITEST`: import { defineConfig } from 'vitest/config' export default defineConfig({ cacheDir: process.env.VITEST ? 'custom-folder/.vitest' : undefined }) ### sequence * **Type** : `{ sequencer?, shuffle?, seed?, hooks?, setupFiles? }` Options for how tests should be sorted. You can provide sequence options to CLI with dot notation: npx vitest --sequence.shuffle --sequence.seed=1000 #### sequence.sequencer * * **Type** : `TestSequencerConstructor` * **Default** : `BaseSequencer` A custom class that defines methods for sharding and sorting. You can extend `BaseSequencer` from `vitest/node`, if you only need to redefine one of the `sort` and `shard` methods, but both should exist. Sharding is happening before sorting, and only if `--shard` option is provided. #### sequence.shuffle * **Type** : `boolean | { files?, tests? }` * **Default** : `false` * **CLI** : `--sequence.shuffle`, `--sequence.shuffle=false` If you want files and tests to run randomly, you can enable it with this option, or CLI argument [`--sequence.shuffle`](../guide/cli). Vitest usually uses cache to sort tests, so long running tests start earlier - this makes tests run faster. If your files and tests will run in random order you will lose this performance improvement, but it may be useful to track tests that accidentally depend on another run previously. #### sequence.shuffle.files * **Type** : `boolean` * **Default** : `false` * **CLI** : `--sequence.shuffle.files`, `--sequence.shuffle.files=false` Whether to randomize files, be aware that long running tests will not start earlier if you enable this option. #### sequence.shuffle.tests * **Type** : `boolean` * **Default** : `false` * **CLI** : `--sequence.shuffle.tests`, `--sequence.shuffle.tests=false` Whether to randomize tests. #### sequence.concurrent * **Type** : `boolean` * **Default** : `false` * **CLI** : `--sequence.concurrent`, `--sequence.concurrent=false` If you want tests to run in parallel, you can enable it with this option, or CLI argument [`--sequence.concurrent`](../guide/cli). #### sequence.seed * * **Type** : `number` * **Default** : `Date.now()` * **CLI** : `--sequence.seed=1000` Sets the randomization seed, if tests are running in random order. #### sequence.hooks * **Type** : `'stack' | 'list' | 'parallel'` * **Default** : `'stack'` * **CLI** : `--sequence.hooks=` Changes the order in which hooks are executed. * `stack` will order "after" hooks in reverse order, "before" hooks will run in the order they were defined * `list` will order all hooks in the order they are defined * `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/index#ontestfinished). It is always called in reverse order. #### sequence.setupFiles * **Type** : `'list' | 'parallel'` * **Default** : `'parallel'` * **CLI** : `--sequence.setupFiles=` Changes the order in which setup files are executed. * `list` will run setup files in the order they are defined * `parallel` will run setup files in parallel ### typecheck Options for configuring [typechecking](../guide/testing-types) test environment. #### typecheck.enabled * **Type** : `boolean` * **Default** : `false` * **CLI** : `--typecheck`, `--typecheck.enabled` Enable typechecking alongside your regular tests. #### typecheck.only * **Type** : `boolean` * **Default** : `false` * **CLI** : `--typecheck.only` Run only typecheck tests, when typechecking is enabled. When using CLI, this option will automatically enable typechecking. #### typecheck.checker * **Type** : `'tsc' | 'vue-tsc' | string` * **Default** : `tsc` What tools to use for type checking. Vitest will spawn a process with certain parameters for easier parsing, depending on the type. Checker should implement the same output format as `tsc`. You need to have a package installed to use typechecker: * `tsc` requires `typescript` package * `vue-tsc` requires `vue-tsc` package You can also pass down a path to custom binary or command name that produces the same output as `tsc --noEmit --pretty false`. #### typecheck.include * **Type** : `string[]` * **Default** : `['**/*.{test,spec}-d.?(c|m)[jt]s?(x)']` Glob pattern for files that should be treated as test files #### typecheck.exclude * **Type** : `string[]` * **Default** : `['**/node_modules/**', '**/dist/**', '**/cypress/**', '**/.{idea,git,cache,output,temp}/**']` Glob pattern for files that should not be treated as test files #### typecheck.allowJs * **Type** : `boolean` * **Default** : `false` Check JS files that have `@ts-check` comment. If you have it enabled in tsconfig, this will not overwrite it. #### typecheck.ignoreSourceErrors * **Type** : `boolean` * **Default** : `false` Do not fail, if Vitest found errors outside the test files. This will not show you non-test errors at all. By default, if Vitest finds source error, it will fail test suite. #### typecheck.tsconfig * **Type** : `string` * **Default** : _tries to find closest tsconfig.json_ Path to custom tsconfig, relative to the project root. ### slowTestThreshold * * **Type** : `number` * **Default** : `300` * **CLI** : `--slow-test-threshold=`, `--slowTestThreshold=` The number of milliseconds after which a test or suite is considered slow and reported as such in the results. ### chaiConfig * **Type:** `{ includeStack?, showDiff?, truncateThreshold? }` * **Default:** `{ includeStack: false, showDiff: true, truncateThreshold: 40 }` Equivalent to [Chai config](https://github.com/chaijs/chai/blob/4.x.x/lib/chai/config.js). #### chaiConfig.includeStack * **Type:** `boolean` * **Default:** `false` Influences whether stack trace is included in Assertion error message. Default of false suppresses stack trace in the error message. #### chaiConfig.showDiff * **Type:** `boolean` * **Default:** `true` Influences whether or not the `showDiff` flag should be included in the thrown AssertionErrors. `false` will always be `false`; `true` will be true when the assertion has requested a diff to be shown. #### chaiConfig.truncateThreshold * **Type:** `number` * **Default:** `40` Sets length threshold for actual and expected values in assertion errors. If this threshold is exceeded, for example for large data structures, the value is replaced with something like `[ Array(3) ]` or `{ Object (prop1, prop2) }`. Set it to `0` if you want to disable truncating altogether. This config option affects truncating values in `test.each` titles and inside the assertion error message. ### bail * **Type:** `number` * **Default:** `0` * **CLI** : `--bail=` Stop test execution when given number of tests have failed. By default Vitest will run all of your test cases even if some of them fail. This may not be desired for CI builds where you are only interested in 100% successful builds and would like to stop test execution as early as possible when test failures occur. The `bail` option can be used to speed up CI runs by preventing it from running more tests when failures have occurred. ### retry * **Type:** `number` * **Default:** `0` * **CLI:** `--retry=` Retry the test specific number of times if it fails. ### onConsoleLog * * **Type** : `(log: string, type: 'stdout' | 'stderr') => boolean | void` Custom handler for `console.log` in tests. If you return `false`, Vitest will not print the log to the console. Can be useful for filtering out logs from third-party libraries. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { onConsoleLog(log: string, type: 'stdout' | 'stderr'): boolean | void { return !(log === 'message from third party library' && type === 'stdout') }, }, }) ### onStackTrace * * **Type** : `(error: Error, frame: ParsedStack) => boolean | void` Apply a filtering function to each frame of each stack trace when handling errors. The first argument, `error`, is an object with the same properties as a standard `Error`, but it is not an actual instance. Can be useful for filtering out stack trace frames from third-party libraries. import type { ParsedStack } from 'vitest' import { defineConfig } from 'vitest/config' export default defineConfig({ test: { onStackTrace(error: Error, { file }: ParsedStack): boolean | void { // If we've encountered a ReferenceError, show the whole stack. if (error.name === 'ReferenceError') { return } // Reject all frames from third party libraries. if (file.includes('node_modules')) { return false } }, }, }) ### diff * **Type:** `string` * **CLI:** `--diff=` `DiffOptions` object or a path to a module which exports `DiffOptions`. Useful if you want to customize diff display. For example, as a config object: import { defineConfig } from 'vitest/config' import c from 'picocolors' export default defineConfig({ test: { diff: { aIndicator: c.bold('--'), bIndicator: c.bold('++'), omitAnnotationLines: true, }, }, }) Or as a module: vitest.config.jsvitest.diff.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { diff: './vitest.diff.ts', }, }) import type { DiffOptions } from 'vitest' import c from 'picocolors' export default { aIndicator: c.bold('--'), bIndicator: c.bold('++'), omitAnnotationLines: true, } satisfies DiffOptions #### diff.expand * **Type** : `boolean` * **Default** : `true` * **CLI:** `--diff.expand=false` Expand all common lines. #### diff.truncateThreshold * **Type** : `number` * **Default** : `0` * **CLI:** `--diff.truncateThreshold=` The maximum length of diff result to be displayed. Diffs above this threshold will be truncated. Truncation won't take effect with default value 0. #### diff.truncateAnnotation * **Type** : `string` * **Default** : `'... Diff result is truncated'` * **CLI:** `--diff.truncateAnnotation=` Annotation that is output at the end of diff result if it's truncated. #### diff.truncateAnnotationColor * **Type** : `DiffOptionsColor = (arg: string) => string` * **Default** : `noColor = (string: string): string => string` Color of truncate annotation, default is output with no color. #### diff.printBasicPrototype * **Type** : `boolean` * **Default** : `false` Print basic prototype `Object` and `Array` in diff output ### fakeTimers * **Type:** `FakeTimerInstallOpts` Options that Vitest will pass down to [`@sinon/fake- timers`](https://www.npmjs.com/package/@sinonjs/fake-timers) when using [`vi.useFakeTimers()`](../api/vi#vi-usefaketimers). #### fakeTimers.now * **Type:** `number | Date` * **Default:** `Date.now()` Installs fake timers with the specified Unix epoch. #### fakeTimers.toFake * **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask')[]` * **Default:** everything available globally except `nextTick` and `queueMicrotask` An array with names of global methods and APIs to fake. To only mock `setTimeout()` and `nextTick()`, specify this property as `['setTimeout', 'nextTick']`. Mocking `nextTick` is not supported when running Vitest inside `node:child_process` by using `--pool=forks`. NodeJS uses `process.nextTick` internally in `node:child_process` and hangs when it is mocked. Mocking `nextTick` is supported when running Vitest with `--pool=threads`. #### fakeTimers.loopLimit * **Type:** `number` * **Default:** `10_000` The maximum number of timers that will be run when calling [`vi.runAllTimers()`](../api/vi#vi-runalltimers). #### fakeTimers.shouldAdvanceTime * **Type:** `boolean` * **Default:** `false` Tells @sinonjs/fake-timers to increment mocked time automatically based on the real system time shift (e.g. the mocked time will be incremented by 20ms for every 20ms change in the real system time). #### fakeTimers.advanceTimeDelta * **Type:** `number` * **Default:** `20` Relevant only when using with `shouldAdvanceTime: true`. increment mocked time by advanceTimeDelta ms every advanceTimeDelta ms change in the real system time. #### fakeTimers.shouldClearNativeTimers * **Type:** `boolean` * **Default:** `true` Tells fake timers to clear "native" (i.e. not fake) timers by delegating to their respective handlers. When disabled, it can lead to potentially unexpected behavior if timers existed prior to starting fake timers session. ### workspace * * **Type:** `string | TestProjectConfiguration` * **CLI:** `--workspace=./file.js` * **Default:** `vitest.{workspace,projects}.{js,ts,json}` close to the config file or root Path to a [workspace](../guide/workspace) config file relative to root. Since Vitest 3, you can also define the workspace array in the root config. If the `workspace` is defined in the config manually, Vitest will ignore the `vitest.workspace` file in the root. ### isolate * **Type:** `boolean` * **Default:** `true` * **CLI:** `--no-isolate`, `--isolate=false` Run tests in an isolated environment. This option has no effect on `vmThreads` and `vmForks` pools. Disabling this option might [improve performance](../guide/improving- performance) if your code doesn't rely on side effects (which is usually true for projects with `node` environment). **TIP** You can disable isolation for specific pools by using `poolOptions` property. ### includeTaskLocation * **Type:** `boolean` * **Default:** `false` Should `location` property be included when Vitest API receives tasks in reporters. If you have a lot of tests, this might cause a small performance regression. The `location` property has `column` and `line` values that correspond to the `test` or `describe` position in the original file. This option will be auto-enabled if you don't disable it explicitly, and you are running Vitest with: * [Vitest UI](../guide/ui) * or using the [Browser Mode](../guide/browser/index) without [headless](../guide/browser/index#headless) mode * or using [HTML Reporter](../guide/reporters#html-reporter) **TIP** This option has no effect if you do not use custom code that relies on this. ### snapshotEnvironment * **Type:** `string` Path to a custom snapshot environment implementation. This is useful if you are running your tests in an environment that doesn't support Node.js APIs. This option doesn't have any effect on a browser runner. This object should have the shape of `SnapshotEnvironment` and is used to resolve and read/write snapshot files: export interface SnapshotEnvironment { getVersion: () => string getHeader: () => string resolvePath: (filepath: string) => Promise resolveRawPath: (testPath: string, rawPath: string) => Promise saveSnapshotFile: (filepath: string, snapshot: string) => Promise readSnapshotFile: (filepath: string) => Promise removeSnapshotFile: (filepath: string) => Promise } You can extend default `VitestSnapshotEnvironment` from `vitest/snapshot` entry point if you need to overwrite only a part of the API. **WARNING** This is a low-level option and should be used only for advanced cases where you don't have access to default Node.js APIs. If you just need to configure snapshots feature, use `snapshotFormat` or `resolveSnapshotPath` options. ### env * **Type:** `Partial` Environment variables available on `process.env` and `import.meta.env` during tests. These variables will not be available in the main process (in `globalSetup`, for example). ### expect * **Type:** `ExpectOptions` #### expect.requireAssertions * **Type:** `boolean` * **Default:** `false` The same as calling [`expect.hasAssertions()`](../api/expect#expect- hasassertions) at the start of every test. This makes sure that no test will pass accidentally. **TIP** This only works with Vitest's `expect`. If you use `assert` or `.should` assertions, they will not count, and your test will fail due to the lack of expect assertions. You can change the value of this by calling `vi.setConfig({ expect: { requireAssertions: false } })`. The config will be applied to every subsequent `expect` call until the `vi.resetConfig` is called manually. #### expect.poll Global configuration options for [`expect.poll`](../api/expect#poll). These are the same options you can pass down to `expect.poll(condition, options)`. ##### expect.poll.interval * **Type:** `number` * **Default:** `50` Polling interval in milliseconds ##### expect.poll.timeout * **Type:** `number` * **Default:** `1000` Polling timeout in milliseconds ### printConsoleTrace * **Type:** `boolean` * **Default:** `false` Always print console traces when calling any `console` method. This is useful for debugging. # Browser Mode Experimental This page provides information about the experimental browser mode feature in the Vitest API, which allows you to run your tests in the browser natively, providing access to browser globals like window and document. This feature is currently under development, and APIs may change in the future. **TIP** If you are looking for documentation for `expect`, `vi` or any general API like workspaces or type testing, refer to the ["Getting Started" guide](index). ## Installation For easier setup, you can use `vitest init browser` command to install required dependencies and create browser configuration. npmyarnpnpmbun npx vitest init browser yarn exec vitest init browser pnpx vitest init browser bunx vitest init browser ### Manual Installation You can also install packages manually. By default, Browser Mode doesn't require any additional E2E provider to run tests locally because it reuses your existing browser. npmyarnpnpmbun npm install -D vitest @vitest/browser yarn add -D vitest @vitest/browser pnpm add -D vitest @vitest/browser bun add -D vitest @vitest/browser **WARNING** However, to run tests in CI you need to install either [`playwright`](https://npmjs.com/package/playwright) or [`webdriverio`](https://www.npmjs.com/package/webdriverio). We also recommend switching to either one of them for testing locally instead of using the default `preview` provider since it relies on simulating events instead of using Chrome DevTools Protocol. If you don't already use one of these tools, we recommend starting with Playwright because it supports parallel execution, which makes your tests run faster. Additionally, Playwright uses [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) which is generally faster than WebDriver. PlaywrightWebdriverIO [Playwright](https://npmjs.com/package/playwright) is a framework for Web Testing and Automation. npmyarnpnpmbun npm install -D vitest @vitest/browser playwright yarn add -D vitest @vitest/browser playwright pnpm add -D vitest @vitest/browser playwright bun add -D vitest @vitest/browser playwright ## Configuration To activate browser mode in your Vitest configuration, you can use the `--browser=name` flag or set the `browser.enabled` field to `true` in your Vitest configuration file. Here is an example configuration using the browser field: vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { provider: 'playwright', // or 'webdriverio' enabled: true, // at least one instance is required instances: [ { browser: 'chromium' }, ], }, } }) **INFO** Vitest assigns port `63315` to avoid conflicts with the development server, allowing you to run both in parallel. You can change that with the [`browser.api`](../config/index#browser-api) option. Since Vitest 2.1.5, the CLI no longer prints the Vite URL automatically. You can press "b" to print the URL when running in watch mode. If you have not used Vite before, make sure you have your framework's plugin installed and specified in the config. Some frameworks might require extra configuration to work - check their Vite related documentation to be sure. reactvuesveltesolidmarko import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import { svelte } from '@sveltejs/vite-plugin-svelte' export default defineConfig({ plugins: [svelte()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import solidPlugin from 'vite-plugin-solid' export default defineConfig({ plugins: [solidPlugin()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import marko from '@marko/vite' export default defineConfig({ plugins: [marko()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) If you need to run some tests using Node-based runner, you can define a [workspace](workspace) file with separate configurations for different testing strategies: vitest.workspace.ts import { defineWorkspace } from 'vitest/config' export default defineWorkspace([ { test: { // an example of file based convention, // you don't have to follow it include: [ 'tests/unit/**/*.{test,spec}.ts', 'tests/**/*.unit.{test,spec}.ts', ], name: 'unit', environment: 'node', }, }, { test: { // an example of file based convention, // you don't have to follow it include: [ 'tests/browser/**/*.{test,spec}.ts', 'tests/**/*.browser.{test,spec}.ts', ], name: 'browser', browser: { enabled: true, instances: [ { browser: 'chromium' }, ], }, }, }, ]) ## Browser Option Types The browser option in Vitest depends on the provider. Vitest will fail, if you pass `--browser` and don't specify its name in the config file. Available options: * `webdriverio` supports these browsers: * `firefox` * `chrome` * `edge` * `safari` * `playwright` supports these browsers: * `firefox` * `webkit` * `chromium` ## TypeScript By default, TypeScript doesn't recognize providers options and extra `expect` properties. If you don't use any providers, make sure the `@vitest/browser/matchers` is referenced somewhere in your tests, [setup file](../config/index#setupfiles) or a [config file](../config/index) to pick up the extra `expect` definitions. If you are using custom providers, make sure to add `@vitest/browser/providers/playwright` or `@vitest/browser/providers/webdriverio` to the same file so TypeScript can pick up definitions for custom options: defaultplaywrightwebdriverio /// /// /// Alternatively, you can also add them to `compilerOptions.types` field in your `tsconfig.json` file. Note that specifying anything in this field will disable [auto loading](https://www.typescriptlang.org/tsconfig/#types) of `@types/*` packages. defaultplaywrightwebdriverio { "compilerOptions": { "types": ["@vitest/browser/matchers"] } } { "compilerOptions": { "types": ["@vitest/browser/providers/playwright"] } } { "compilerOptions": { "types": ["@vitest/browser/providers/webdriverio"] } } ## Browser Compatibility Vitest uses [Vite dev server](https://vitejs.dev/guide/#browser-support) to run your tests, so we only support features specified in the [`esbuild.target`](https://vitejs.dev/config/shared-options.html#esbuild) option (`esnext` by default). By default, Vite targets browsers which support the native [ES Modules](https://caniuse.com/es6-module), native [ESM dynamic import](https://caniuse.com/es6-module-dynamic-import), and [`import.meta`](https://caniuse.com/mdn-javascript_operators_import_meta). On top of that, we utilize [`BroadcastChannel`](https://caniuse.com/?search=BroadcastChannel) to communicate between iframes: * Chrome >=87 * Firefox >=78 * Safari >=15.4 * Edge >=88 ## Running Tests When you specify a browser name in the browser option, Vitest will try to run the specified browser using `preview` by default, and then run the tests there. If you don't want to use `preview`, you can configure the custom browser provider by using `browser.provider` option. To specify a browser using the CLI, use the `--browser` flag followed by the browser name, like this: npx vitest --browser=chrome Or you can provide browser options to CLI with dot notation: npx vitest --browser.headless By default, Vitest will automatically open the browser UI for development. Your tests will run inside an iframe in the center. You can configure the viewport by selecting the preferred dimensions, calling `page.viewport` inside the test, or setting default values in [the config](../config/index#browser- viewport). ## Headless Headless mode is another option available in the browser mode. In headless mode, the browser runs in the background without a user interface, which makes it useful for running automated tests. The headless option in Vitest can be set to a boolean value to enable or disable headless mode. When using headless mode, Vitest won't open the UI automatically. If you want to continue using the UI but have tests run headlessly, you can install the [`@vitest/ui`](ui) package and pass the `--ui` flag when running Vitest. Here's an example configuration enabling headless mode: vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { provider: 'playwright', enabled: true, headless: true, }, } }) You can also set headless mode using the `--browser.headless` flag in the CLI, like this: npx vitest --browser.headless In this case, Vitest will run in headless mode using the Chrome browser. **WARNING** Headless mode is not available by default. You need to use either [`playwright`](https://npmjs.com/package/playwright) or [`webdriverio`](https://www.npmjs.com/package/webdriverio) providers to enable this feature. ## Examples By default, you don't need any external packages to work with the Browser Mode: example.test.js import { expect, test } from 'vitest' import { page } from '@vitest/browser/context' import { render } from './my-render-function.js' test('properly handles form inputs', async () => { render() // mount DOM elements // Asserts initial state. await expect.element(page.getByText('Hi, my name is Alice')).toBeInTheDocument() // Get the input DOM node by querying the associated label. const usernameInput = page.getByLabelText(/username/i) // Type the name into the input. This already validates that the input // is filled correctly, no need to check the value manually. await usernameInput.fill('Bob') await expect.element(page.getByText('Hi, my name is Bob')).toBeInTheDocument() }) However, Vitest also provides packages to render components for several popular frameworks out of the box: * [`vitest-browser-vue`](https://github.com/vitest-dev/vitest-browser-vue) to render [vue](https://vuejs.org) components * [`vitest-browser-svelte`](https://github.com/vitest-dev/vitest-browser-svelte) to render [svelte](https://svelte.dev) components * [`vitest-browser-react`](https://github.com/vitest-dev/vitest-browser-react) to render [react](https://react.dev) components If your framework is not represented, feel free to create your own package - it is a simple wrapper around the framework renderer and `page.elementLocator` API. We will add a link to it on this page. Make sure it has a name starting with `vitest-browser-`. Besides rendering components and locating elements, you will also need to make assertions. Vitest bundles the [`@testing-library/jest- dom`](https://github.com/testing-library/jest-dom) library to provide a wide range of DOM assertions out of the box. Read more at the [Assertions API](browser/assertion-api). import { expect } from 'vitest' import { page } from '@vitest/browser/context' // element is rendered correctly await expect.element(page.getByText('Hello World')).toBeInTheDocument() Vitest exposes a [Context API](browser/context) with a small set of utilities that might be useful to you in tests. For example, if you need to make an interaction, like clicking an element or typing text into an input, you can use `userEvent` from `@vitest/browser/context`. Read more at the [Interactivity API](browser/interactivity-api). import { page, userEvent } from '@vitest/browser/context' await userEvent.fill(page.getByLabelText(/username/i), 'Alice') // or just locator.fill await page.getByLabelText(/username/i).fill('Alice') vuesveltereact import { render } from 'vitest-browser-vue' import Component from './Component.vue' test('properly handles v-model', async () => { const screen = render(Component) // Asserts initial state. await expect.element(screen.getByText('Hi, my name is Alice')).toBeInTheDocument() // Get the input DOM node by querying the associated label. const usernameInput = screen.getByLabelText(/username/i) // Type the name into the input. This already validates that the input // is filled correctly, no need to check the value manually. await usernameInput.fill('Bob') await expect.element(screen.getByText('Hi, my name is Bob')).toBeInTheDocument() }) import { render } from 'vitest-browser-svelte' import { expect, test } from 'vitest' import Greeter from './greeter.svelte' test('greeting appears on click', async () => { const screen = render(Greeter, { name: 'World' }) const button = screen.getByRole('button') await button.click() const greeting = screen.getByText(/hello world/iu) await expect.element(greeting).toBeInTheDocument() }) import { render } from 'vitest-browser-react' import Fetch from './fetch' test('loads and displays greeting', async () => { // Render a React element into the DOM const screen = render() await screen.getByText('Load Greeting').click() // wait before throwing an error if it cannot find an element const heading = screen.getByRole('heading') // assert that the alert message is correct await expect.element(heading).toHaveTextContent('hello there') await expect.element(screen.getByRole('button')).toBeDisabled() }) Vitest doesn't support all frameworks out of the box, but you can use external tools to run tests with these frameworks. We also encourage the community to create their own `vitest-browser` wrappers - if you have one, feel free to add it to the examples above. For unsupported frameworks, we recommend using `testing-library` packages: * [`@testing-library/preact`](https://testing-library.com/docs/preact-testing-library/intro) to render [preact](https://preactjs.com) components * [`@solidjs/testing-library`](https://testing-library.com/docs/solid-testing-library/intro) to render [solid](https://www.solidjs.com) components * [`@marko/testing-library`](https://testing-library.com/docs/marko-testing-library/intro) to render [marko](https://markojs.com) components You can also see more examples in [`browser- examples`](https://github.com/vitest-tests/browser-examples) repository. **WARNING** `testing-library` provides a package `@testing-library/user-event`. We do not recommend using it directly because it simulates events instead of actually triggering them - instead, use [`userEvent`](browser/interactivity-api) imported from `@vitest/browser/context` that uses Chrome DevTools Protocol or Webdriver (depending on the provider) under the hood. preactsolidmarko // based on @testing-library/preact example // https://testing-library.com/docs/preact-testing-library/example import { h } from 'preact' import { page } from '@vitest/browser/context' import { render } from '@testing-library/preact' import HiddenMessage from '../hidden-message' test('shows the children when the checkbox is checked', async () => { const testMessage = 'Test Message' const { baseElement } = render( {testMessage}, ) const screen = page.elementLocator(baseElement) // .query() will return the element or null if it cannot be found. // .element() will return the element or throw an error if it cannot be found. expect(screen.getByText(testMessage).query()).not.toBeInTheDocument() // The queries can accept a regex to make your selectors more // resilient to content tweaks and changes. await screen.getByLabelText(/show/i).click() await expect.element(screen.getByText(testMessage)).toBeInTheDocument() }) // baed on @testing-library/solid API // https://testing-library.com/docs/solid-testing-library/api import { render } from '@testing-library/solid' it('uses params', async () => { const App = () => ( <> (

Id: {useParams()?.id}

)} />

Start

} /> ) const { baseElement } = render(() => , { location: 'ids/1234' }) const screen = page.elementLocator(baseElement) await expect.screen(screen.getByText('Id: 1234')).toBeInTheDocument() }) // baed on @testing-library/marko API // https://testing-library.com/docs/marko-testing-library/api import { render, screen } from '@marko/testing-library' import Greeting from './greeting.marko' test('renders a message', async () => { const { baseElement } = await render(Greeting, { name: 'Marko' }) const screen = page.elementLocator(baseElement) await expect.element(screen.getByText(/Marko/)).toBeInTheDocument() expect(container.firstChild).toMatchInlineSnapshot(`

Hello, Marko!

`) }) ## Limitations ### Thread Blocking Dialogs When using Vitest Browser, it's important to note that thread blocking dialogs like `alert` or `confirm` cannot be used natively. This is because they block the web page, which means Vitest cannot continue communicating with the page, causing the execution to hang. In such situations, Vitest provides default mocks with default returned values for these APIs. This ensures that if the user accidentally uses synchronous popup web APIs, the execution would not hang. However, it's still recommended for the user to mock these web APIs for better experience. Read more in [Mocking](mocking). # Assertion API Vitest bundles the [`@testing-library/jest-dom`](https://github.com/testing- library/jest-dom) library to provide a wide range of DOM assertions out of the box. For detailed documentation, you can read the `jest-dom` readme: * [`toBeDisabled`](https://github.com/testing-library/jest-dom#toBeDisabled) * [`toBeEnabled`](https://github.com/testing-library/jest-dom#toBeEnabled) * [`toBeEmptyDOMElement`](https://github.com/testing-library/jest-dom#toBeEmptyDOMElement) * [`toBeInTheDocument`](https://github.com/testing-library/jest-dom#toBeInTheDocument) * [`toBeInvalid`](https://github.com/testing-library/jest-dom#toBeInvalid) * [`toBeRequired`](https://github.com/testing-library/jest-dom#toBeRequired) * [`toBeValid`](https://github.com/testing-library/jest-dom#toBeValid) * [`toBeVisible`](https://github.com/testing-library/jest-dom#toBeVisible) * [`toContainElement`](https://github.com/testing-library/jest-dom#toContainElement) * [`toContainHTML`](https://github.com/testing-library/jest-dom#toContainHTML) * [`toHaveAccessibleDescription`](https://github.com/testing-library/jest-dom#toHaveAccessibleDescription) * [`toHaveAccessibleErrorMessage`](https://github.com/testing-library/jest-dom#toHaveAccessibleErrorMessage) * [`toHaveAccessibleName`](https://github.com/testing-library/jest-dom#toHaveAccessibleName) * [`toHaveAttribute`](https://github.com/testing-library/jest-dom#toHaveAttribute) * [`toHaveClass`](https://github.com/testing-library/jest-dom#toHaveClass) * [`toHaveFocus`](https://github.com/testing-library/jest-dom#toHaveFocus) * [`toHaveFormValues`](https://github.com/testing-library/jest-dom#toHaveFormValues) * [`toHaveStyle`](https://github.com/testing-library/jest-dom#toHaveStyle) * [`toHaveTextContent`](https://github.com/testing-library/jest-dom#toHaveTextContent) * [`toHaveValue`](https://github.com/testing-library/jest-dom#toHaveValue) * [`toHaveDisplayValue`](https://github.com/testing-library/jest-dom#toHaveDisplayValue) * [`toBeChecked`](https://github.com/testing-library/jest-dom#toBeChecked) * [`toBePartiallyChecked`](https://github.com/testing-library/jest-dom#toBePartiallyChecked) * [`toHaveRole`](https://github.com/testing-library/jest-dom#toHaveRole) * [`toHaveErrorMessage`](https://github.com/testing-library/jest-dom#toHaveErrorMessage) If you are using [TypeScript](index#typescript) or want to have correct type hints in `expect`, make sure you have either `@vitest/browser/providers/playwright` or `@vitest/browser/providers/webdriverio` referenced in your [setup file](../../config/index#setupfile) or a [config file](../../config/index) depending on the provider you use. If you use the default `preview` provider, you can specify `@vitest/browser/matchers` instead. previewplaywrightwebdriverio /// /// /// Tests in the browser might fail inconsistently due to their asynchronous nature. Because of this, it is important to have a way to guarantee that assertions succeed even if the condition is delayed (by a timeout, network request, or animation, for example). For this purpose, Vitest provides retriable assertions out of the box via the [`expect.poll`](../../api/expect#poll) and `expect.element` APIs: import { expect, test } from 'vitest' import { page } from '@vitest/browser/context' test('error banner is rendered', async () => { triggerError() // @testing-library provides queries with built-in retry-ability // It will try to find the banner until it's rendered const banner = page.getByRole('alert', { name: /error/i, }) // Vitest provides `expect.element` with built-in retry-ability // It will check `element.textContent` until it's equal to "Error!" await expect.element(banner).toHaveTextContent('Error!') }) **TIP** `expect.element` is a shorthand for `expect.poll(() => element)` and works in exactly the same way. `toHaveTextContent` and all other [`@testing-library/jest- dom`](https://github.com/testing-library/jest-dom) assertions are still available on a regular `expect` without a built-in retry-ability mechanism: // will fail immediately if .textContent is not `'Error!'` expect(banner).toHaveTextContent('Error!') # Commands Command is a function that invokes another function on the server and passes down the result back to the browser. Vitest exposes several built-in commands you can use in your browser tests. ## Built-in Commands ### Files Handling You can use `readFile`, `writeFile` and `removeFile` API to handle files inside your browser tests. All paths are resolved relative to the test file even if they are called in a helper function located in another file. By default, Vitest uses `utf-8` encoding but you can override it with options. **TIP** This API follows [`server.fs`](https://vitejs.dev/config/server- options.html#server-fs-allow) limitations for security reasons. import { server } from '@vitest/browser/context' const { readFile, writeFile, removeFile } = server.commands it('handles files', async () => { const file = './test.txt' await writeFile(file, 'hello world') const content = await readFile(file) expect(content).toBe('hello world') await removeFile(file) }) ## CDP Session Vitest exposes access to raw Chrome Devtools Protocol via the `cdp` method exported from `@vitest/browser/context`. It is mostly useful to library authors to build tools on top of it. import { cdp } from '@vitest/browser/context' const input = document.createElement('input') document.body.appendChild(input) input.focus() await cdp().send('Input.dispatchKeyEvent', { type: 'keyDown', text: 'a', }) expect(input).toHaveValue('a') **WARNING** CDP session works only with `playwright` provider and only when using `chromium` browser. You can read more about it in playwright's [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession) documentation. ## Custom Commands You can also add your own commands via [`browser.commands`](config#browser- commands) config option. If you develop a library, you can provide them via a `config` hook inside a plugin: import type { Plugin } from 'vitest/config' import type { BrowserCommand } from 'vitest/node' const myCustomCommand: BrowserCommand<[arg1: string, arg2: string]> = ({ testPath, provider }, arg1, arg2) => { if (provider.name === 'playwright') { console.log(testPath, arg1, arg2) return { someValue: true } } throw new Error(`provider ${provider.name} is not supported`) } export default function BrowserCommands(): Plugin { return { name: 'vitest:custom-commands', config() { return { test: { browser: { commands: { myCustomCommand, } } } } } } } Then you can call it inside your test by importing it from `@vitest/browser/context`: import { commands } from '@vitest/browser/context' import { expect, test } from 'vitest' test('custom command works correctly', async () => { const result = await commands.myCustomCommand('test1', 'test2') expect(result).toEqual({ someValue: true }) }) // if you are using TypeScript, you can augment the module declare module '@vitest/browser/context' { interface BrowserCommands { myCustomCommand: (arg1: string, arg2: string) => Promise<{ someValue: true }> } } **WARNING** Custom functions will override built-in ones if they have the same name. ### Custom `playwright` commands Vitest exposes several `playwright` specific properties on the command context. * `page` references the full page that contains the test iframe. This is the orchestrator HTML and you most likely shouldn't touch it to not break things. * `frame` is an async method that will resolve tester [`Frame`](https://playwright.dev/docs/api/class-frame). It has a similar API to the `page`, but it doesn't support certain methods. If you need to query an element, you should prefer using `context.iframe` instead because it is more stable and faster. * `iframe` is a [`FrameLocator`](https://playwright.dev/docs/api/class-framelocator) that should be used to query other elements on the page. * `context` refers to the unique [BrowserContext](https://playwright.dev/docs/api/class-browsercontext). import { BrowserCommand } from 'vitest/node' export const myCommand: BrowserCommand<[string, number]> = async ( ctx, arg1: string, arg2: number ) => { if (ctx.provider.name === 'playwright') { const element = await ctx.iframe.findByRole('alert') const screenshot = await element.screenshot() // do something with the screenshot return difference } } **TIP** If you are using TypeScript, don't forget to reference `@vitest/browser/providers/playwright` in your [setup file](../../config/index#setupfile) or a [config file](../../config/index) to get autocompletion in the config and in `userEvent` and `page` options: /// ### Custom `webdriverio` commands Vitest exposes some `webdriverio` specific properties on the context object. * `browser` is the `WebdriverIO.Browser` API. Vitest automatically switches the `webdriver` context to the test iframe by calling `browser.switchToFrame` before the command is called, so `$` and `$$` methods refer to the elements inside the iframe, not in the orchestrator, but non-webdriver APIs will still refer to the parent frame context. **TIP** If you are using TypeScript, don't forget to reference `@vitest/browser/providers/webdriverio` in your [setup file](../../config/index#setupfile) or a [config file](../../config/index) to get autocompletion: /// # Browser Config Reference You can change the browser configuration by updating the `test.browser` field in your [config file](../../config/index). An example of a simple config file: vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium', setupFile: './chromium-setup.js', }, ], }, }, }) Please, refer to the ["Config Reference"](../../config/index) article for different config examples. **WARNING** _All listed options_ on this page are located within a `test` property inside the configuration: vitest.config.js export default defineConfig({ test: { browser: {}, }, }) ## browser.enabled * **Type:** `boolean` * **Default:** `false` * **CLI:** `--browser`, `--browser.enabled=false` Run all tests inside a browser by default. Note that `--browser` only works if you have at least one `browser.instances` item. ## browser.instances * **Type:** `BrowserConfig` * **Default:** `[{ browser: name }]` Defines multiple browser setups. Every config has to have at least a `browser` field. The config supports your providers configurations: * [Configuring Playwright](playwright) * [Configuring WebdriverIO](webdriverio) **TIP** To have a better type safety when using built-in providers, you should reference one of these types (for provider that you are using) in your [config file](../../config/index): /// /// In addition to that, you can also specify most of the [project options](../../config/index) (not marked with a * icon) and some of the `browser` options like `browser.testerHtmlPath`. **WARNING** Every browser config inherits options from the root config: vitest.config.ts export default defineConfig({ test: { setupFile: ['./root-setup-file.js'], browser: { enabled: true, testerHtmlPath: './custom-path.html', instances: [ { // will have both setup files: "root" and "browser" setupFile: ['./browser-setup-file.js'], // implicitly has "testerHtmlPath" from the root config // // testerHtmlPath: './custom-path.html', // }, ], }, }, }) During development, Vitest supports only one non-headless configuration. You can limit the headed project yourself by specifying `headless: false` in the config, or by providing the `--browser.headless=false` flag, or by filtering projects with `--project=chromium` flag. For more examples, refer to the ["Multiple Setups" guide](multiple-setups). List of available `browser` options: * `browser.headless` * `browser.locators` * `browser.viewport` * `browser.testerHtmlPath` * `browser.screenshotDirectory` * `browser.screenshotFailures` By default, Vitest creates an array with a single element which uses the `browser.name` field as a `browser`. Note that this behaviour will be removed with Vitest 4. Under the hood, Vitest transforms these instances into separate [test projects](../../advanced/api/test-project) sharing a single Vite server for better caching performance. ## browser.name deprecated * **Type:** `string` * **CLI:** `--browser=safari` **DANGER** This API is deprecated an will be removed in Vitest 4. Please, use `browser.instances` option instead. Run all tests in a specific browser. Possible options in different providers: * `webdriverio`: `firefox`, `chrome`, `edge`, `safari` * `playwright`: `firefox`, `webkit`, `chromium` * custom: any string that will be passed to the provider ## browser.headless * **Type:** `boolean` * **Default:** `process.env.CI` * **CLI:** `--browser.headless`, `--browser.headless=false` Run the browser in a `headless` mode. If you are running Vitest in CI, it will be enabled by default. ## browser.isolate * **Type:** `boolean` * **Default:** `true` * **CLI:** `--browser.isolate`, `--browser.isolate=false` Run every test in a separate iframe. ## browser.testerHtmlPath * **Type:** `string` A path to the HTML entry point. Can be relative to the root of the project. This file will be processed with [`transformIndexHtml`](https://vite.dev/guide/api-plugin#transformindexhtml) hook. ## browser.api * **Type:** `number | { port?, strictPort?, host? }` * **Default:** `63315` * **CLI:** `--browser.api=63315`, `--browser.api.port=1234, --browser.api.host=example.com` Configure options for Vite server that serves code in the browser. Does not affect `test.api` option. By default, Vitest assigns port `63315` to avoid conflicts with the development server, allowing you to run both in parallel. ## browser.provider experimental * **Type:** `'webdriverio' | 'playwright' | 'preview' | string` * **Default:** `'preview'` * **CLI:** `--browser.provider=playwright` **ADVANCED API** The provider API is highly experimental and can change between patches. If you just need to run tests in a browser, use the `browser.instances` option instead. Path to a provider that will be used when running browser tests. Vitest provides three providers which are `preview` (default), `webdriverio` and `playwright`. Custom providers should be exported using `default` export and have this shape: export interface BrowserProvider { name: string supportsParallelism: boolean getSupportedBrowsers: () => readonly string[] beforeCommand?: (command: string, args: unknown[]) => Awaitable afterCommand?: (command: string, args: unknown[]) => Awaitable getCommandsContext: (sessionId: string) => Record openPage: (sessionId: string, url: string, beforeNavigate?: () => Promise) => Promise getCDPSession?: (sessionId: string) => Promise close: () => Awaitable initialize( ctx: TestProject, options: BrowserProviderInitializationOptions ): Awaitable } ## browser.providerOptions deprecated * **Type:** `BrowserProviderOptions` **DANGER** This API is deprecated an will be removed in Vitest 4. Please, use `browser.instances` option instead. Options that will be passed down to provider when calling `provider.initialize`. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { providerOptions: { launch: { devtools: true, }, }, }, }, }) **TIP** To have a better type safety when using built-in providers, you should reference one of these types (for provider that you are using) in your [config file](../../config/index): /// /// ## browser.ui * **Type:** `boolean` * **Default:** `!isCI` * **CLI:** `--browser.ui=false` Should Vitest UI be injected into the page. By default, injects UI iframe during development. ## browser.viewport * **Type:** `{ width, height }` * **Default:** `414x896` Default iframe's viewport. ## browser.locators Options for built-in [browser locators](locators). ### browser.locators.testIdAttribute * **Type:** `string` * **Default:** `data-testid` Attribute used to find elements with `getByTestId` locator. ## browser.screenshotDirectory * **Type:** `string` * **Default:** `__screenshots__` in the test file directory Path to the screenshots directory relative to the `root`. ## browser.screenshotFailures * **Type:** `boolean` * **Default:** `!browser.ui` Should Vitest take screenshots if the test fails. ## browser.orchestratorScripts * **Type:** `BrowserScript[]` * **Default:** `[]` Custom scripts that should be injected into the orchestrator HTML before test iframes are initiated. This HTML document only sets up iframes and doesn't actually import your code. The script `src` and `content` will be processed by Vite plugins. Script should be provided in the following shape: export interface BrowserScript { /** * If "content" is provided and type is "module", this will be its identifier. * * If you are using TypeScript, you can add `.ts` extension here for example. * @default `injected-${index}.js` */ id?: string /** * JavaScript content to be injected. This string is processed by Vite plugins if type is "module". * * You can use `id` to give Vite a hint about the file extension. */ content?: string /** * Path to the script. This value is resolved by Vite so it can be a node module or a file path. */ src?: string /** * If the script should be loaded asynchronously. */ async?: boolean /** * Script type. * @default 'module' */ type?: string } ## browser.testerScripts * **Type:** `BrowserScript[]` * **Default:** `[]` **DANGER** This API is deprecated an will be removed in Vitest 4. Please, use `browser.testerHtmlPath` field instead. Custom scripts that should be injected into the tester HTML before the tests environment is initiated. This is useful to inject polyfills required for Vitest browser implementation. It is recommended to use `setupFiles` in almost all cases instead of this. The script `src` and `content` will be processed by Vite plugins. ## browser.commands * **Type:** `Record` * **Default:** `{ readFile, writeFile, ... }` Custom [commands](commands) that can be imported during browser tests from `@vitest/browser/commands`. ## browser.connectTimeout * **Type:** `number` * **Default:** `60_000` The timeout in milliseconds. If connection to the browser takes longer, the test suite will fail. **INFO** This is the time it should take for the browser to establish the WebSocket connection with the Vitest server. In normal circumstances, this timeout should never be reached. # Browser Config Reference You can change the browser configuration by updating the `test.browser` field in your [config file](../../../config/index). An example of a simple config file: vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium', setupFile: './chromium-setup.js', }, ], }, }, }) Please, refer to the ["Config Reference"](../../../config/index) article for different config examples. **WARNING** _All listed options_ on this page are located within a `test` property inside the configuration: vitest.config.js export default defineConfig({ test: { browser: {}, }, }) ## browser.enabled * **Type:** `boolean` * **Default:** `false` * **CLI:** `--browser`, `--browser.enabled=false` Run all tests inside a browser by default. Note that `--browser` only works if you have at least one `browser.instances` item. ## browser.instances * **Type:** `BrowserConfig` * **Default:** `[{ browser: name }]` Defines multiple browser setups. Every config has to have at least a `browser` field. The config supports your providers configurations: * [Configuring Playwright](../playwright) * [Configuring WebdriverIO](../webdriverio) **TIP** To have a better type safety when using built-in providers, you should reference one of these types (for provider that you are using) in your [config file](../../../config/index): /// /// In addition to that, you can also specify most of the [project options](../../../config/index) (not marked with a * icon) and some of the `browser` options like `browser.testerHtmlPath`. **WARNING** Every browser config inherits options from the root config: vitest.config.ts export default defineConfig({ test: { setupFile: ['./root-setup-file.js'], browser: { enabled: true, testerHtmlPath: './custom-path.html', instances: [ { // will have both setup files: "root" and "browser" setupFile: ['./browser-setup-file.js'], // implicitly has "testerHtmlPath" from the root config // // testerHtmlPath: './custom-path.html', // }, ], }, }, }) During development, Vitest supports only one non-headless configuration. You can limit the headed project yourself by specifying `headless: false` in the config, or by providing the `--browser.headless=false` flag, or by filtering projects with `--project=chromium` flag. For more examples, refer to the ["Multiple Setups" guide](../multiple-setups). List of available `browser` options: * `browser.headless` * `browser.locators` * `browser.viewport` * `browser.testerHtmlPath` * `browser.screenshotDirectory` * `browser.screenshotFailures` By default, Vitest creates an array with a single element which uses the `browser.name` field as a `browser`. Note that this behaviour will be removed with Vitest 4. Under the hood, Vitest transforms these instances into separate [test projects](../../../advanced/api/test-project) sharing a single Vite server for better caching performance. ## browser.name deprecated * **Type:** `string` * **CLI:** `--browser=safari` **DANGER** This API is deprecated an will be removed in Vitest 4. Please, use `browser.instances` option instead. Run all tests in a specific browser. Possible options in different providers: * `webdriverio`: `firefox`, `chrome`, `edge`, `safari` * `playwright`: `firefox`, `webkit`, `chromium` * custom: any string that will be passed to the provider ## browser.headless * **Type:** `boolean` * **Default:** `process.env.CI` * **CLI:** `--browser.headless`, `--browser.headless=false` Run the browser in a `headless` mode. If you are running Vitest in CI, it will be enabled by default. ## browser.isolate * **Type:** `boolean` * **Default:** `true` * **CLI:** `--browser.isolate`, `--browser.isolate=false` Run every test in a separate iframe. ## browser.testerHtmlPath * **Type:** `string` A path to the HTML entry point. Can be relative to the root of the project. This file will be processed with [`transformIndexHtml`](https://vite.dev/guide/api-plugin#transformindexhtml) hook. ## browser.api * **Type:** `number | { port?, strictPort?, host? }` * **Default:** `63315` * **CLI:** `--browser.api=63315`, `--browser.api.port=1234, --browser.api.host=example.com` Configure options for Vite server that serves code in the browser. Does not affect `test.api` option. By default, Vitest assigns port `63315` to avoid conflicts with the development server, allowing you to run both in parallel. ## browser.provider experimental * **Type:** `'webdriverio' | 'playwright' | 'preview' | string` * **Default:** `'preview'` * **CLI:** `--browser.provider=playwright` **ADVANCED API** The provider API is highly experimental and can change between patches. If you just need to run tests in a browser, use the `browser.instances` option instead. Path to a provider that will be used when running browser tests. Vitest provides three providers which are `preview` (default), `webdriverio` and `playwright`. Custom providers should be exported using `default` export and have this shape: export interface BrowserProvider { name: string supportsParallelism: boolean getSupportedBrowsers: () => readonly string[] beforeCommand?: (command: string, args: unknown[]) => Awaitable afterCommand?: (command: string, args: unknown[]) => Awaitable getCommandsContext: (sessionId: string) => Record openPage: (sessionId: string, url: string, beforeNavigate?: () => Promise) => Promise getCDPSession?: (sessionId: string) => Promise close: () => Awaitable initialize( ctx: TestProject, options: BrowserProviderInitializationOptions ): Awaitable } ## browser.providerOptions deprecated * **Type:** `BrowserProviderOptions` **DANGER** This API is deprecated an will be removed in Vitest 4. Please, use `browser.instances` option instead. Options that will be passed down to provider when calling `provider.initialize`. import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { providerOptions: { launch: { devtools: true, }, }, }, }, }) **TIP** To have a better type safety when using built-in providers, you should reference one of these types (for provider that you are using) in your [config file](../../../config/index): /// /// ## browser.ui * **Type:** `boolean` * **Default:** `!isCI` * **CLI:** `--browser.ui=false` Should Vitest UI be injected into the page. By default, injects UI iframe during development. ## browser.viewport * **Type:** `{ width, height }` * **Default:** `414x896` Default iframe's viewport. ## browser.locators Options for built-in [browser locators](../locators). ### browser.locators.testIdAttribute * **Type:** `string` * **Default:** `data-testid` Attribute used to find elements with `getByTestId` locator. ## browser.screenshotDirectory * **Type:** `string` * **Default:** `__screenshots__` in the test file directory Path to the screenshots directory relative to the `root`. ## browser.screenshotFailures * **Type:** `boolean` * **Default:** `!browser.ui` Should Vitest take screenshots if the test fails. ## browser.orchestratorScripts * **Type:** `BrowserScript[]` * **Default:** `[]` Custom scripts that should be injected into the orchestrator HTML before test iframes are initiated. This HTML document only sets up iframes and doesn't actually import your code. The script `src` and `content` will be processed by Vite plugins. Script should be provided in the following shape: export interface BrowserScript { /** * If "content" is provided and type is "module", this will be its identifier. * * If you are using TypeScript, you can add `.ts` extension here for example. * @default `injected-${index}.js` */ id?: string /** * JavaScript content to be injected. This string is processed by Vite plugins if type is "module". * * You can use `id` to give Vite a hint about the file extension. */ content?: string /** * Path to the script. This value is resolved by Vite so it can be a node module or a file path. */ src?: string /** * If the script should be loaded asynchronously. */ async?: boolean /** * Script type. * @default 'module' */ type?: string } ## browser.testerScripts * **Type:** `BrowserScript[]` * **Default:** `[]` **DANGER** This API is deprecated an will be removed in Vitest 4. Please, use `browser.testerHtmlPath` field instead. Custom scripts that should be injected into the tester HTML before the tests environment is initiated. This is useful to inject polyfills required for Vitest browser implementation. It is recommended to use `setupFiles` in almost all cases instead of this. The script `src` and `content` will be processed by Vite plugins. ## browser.commands * **Type:** `Record` * **Default:** `{ readFile, writeFile, ... }` Custom [commands](../commands) that can be imported during browser tests from `@vitest/browser/commands`. ## browser.connectTimeout * **Type:** `number` * **Default:** `60_000` The timeout in milliseconds. If connection to the browser takes longer, the test suite will fail. **INFO** This is the time it should take for the browser to establish the WebSocket connection with the Vitest server. In normal circumstances, this timeout should never be reached. # Context API Vitest exposes a context module via `@vitest/browser/context` entry point. As of 2.0, it exposes a small set of utilities that might be useful to you in tests. ## `userEvent` **TIP** The `userEvent` API is explained in detail at [Interactivity API](interactivity-api). /** * Handler for user interactions. The support is implemented by the browser provider (`playwright` or `webdriverio`). * If used with `preview` provider, fallbacks to simulated events via `@testing-library/user-event`. * @experimental */ export const userEvent: { setup: () => UserEvent cleanup: () => Promise click: (element: Element, options?: UserEventClickOptions) => Promise dblClick: (element: Element, options?: UserEventDoubleClickOptions) => Promise tripleClick: (element: Element, options?: UserEventTripleClickOptions) => Promise selectOptions: ( element: Element, values: HTMLElement | HTMLElement[] | string | string[], options?: UserEventSelectOptions, ) => Promise keyboard: (text: string) => Promise type: (element: Element, text: string, options?: UserEventTypeOptions) => Promise clear: (element: Element) => Promise tab: (options?: UserEventTabOptions) => Promise hover: (element: Element, options?: UserEventHoverOptions) => Promise unhover: (element: Element, options?: UserEventHoverOptions) => Promise fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise dragAndDrop: (source: Element, target: Element, options?: UserEventDragAndDropOptions) => Promise } ## `commands` **TIP** This API is explained in detail at [Commands API](commands). /** * Available commands for the browser. * A shortcut to `server.commands`. */ export const commands: BrowserCommands ## `page` The `page` export provides utilities to interact with the current `page`. **WARNING** While it exposes some utilities from Playwright's `page`, it is not the same object. Since the browser context is evaluated in the browser, your tests don't have access to Playwright's `page` because it runs on the server. Use [Commands API](commands) if you need to have access to Playwright's `page` object. export const page: { /** * Change the size of iframe's viewport. */ viewport(width: number, height: number): Promise /** * Make a screenshot of the test iframe or a specific element. * @returns Path to the screenshot file or path and base64. */ screenshot(options: Omit & { base64: true }): Promise<{ path: string base64: string }> screenshot(options?: ScreenshotOptions): Promise /** * Extend default `page` object with custom methods. */ extend(methods: Partial): BrowserPage /** * Wrap an HTML element in a `Locator`. When querying for elements, the search will always return this element. */ elementLocator(element: Element): Locator /** * Locator APIs. See its documentation for more details. */ getByRole(role: ARIARole | string, options?: LocatorByRoleOptions): Locator getByLabelText(text: string | RegExp, options?: LocatorOptions): Locator getByTestId(text: string | RegExp): Locator getByAltText(text: string | RegExp, options?: LocatorOptions): Locator getByPlaceholder(text: string | RegExp, options?: LocatorOptions): Locator getByText(text: string | RegExp, options?: LocatorOptions): Locator getByTitle(text: string | RegExp, options?: LocatorOptions): Locator } **TIP** The `getBy*` API is explained at [Locators API](locators). ## `cdp` The `cdp` export returns the current Chrome DevTools Protocol session. It is mostly useful to library authors to build tools on top of it. **WARNING** CDP session works only with `playwright` provider and only when using `chromium` browser. You can read more about it in playwright's [`CDPSession`](https://playwright.dev/docs/api/class-cdpsession) documentation. export const cdp: () => CDPSession ## `server` The `server` export represents the Node.js environment where the Vitest server is running. It is mostly useful for debugging or limiting your tests based on the environment. export const server: { /** * Platform the Vitest server is running on. * The same as calling `process.platform` on the server. */ platform: Platform /** * Runtime version of the Vitest server. * The same as calling `process.version` on the server. */ version: string /** * Name of the browser provider. */ provider: string /** * Name of the current browser. */ browser: string /** * Available commands for the browser. */ commands: BrowserCommands /** * Serialized test config. */ config: SerializedConfig } # Browser Mode Experimental This page provides information about the experimental browser mode feature in the Vitest API, which allows you to run your tests in the browser natively, providing access to browser globals like window and document. This feature is currently under development, and APIs may change in the future. **TIP** If you are looking for documentation for `expect`, `vi` or any general API like workspaces or type testing, refer to the ["Getting Started" guide](../index). ## Installation For easier setup, you can use `vitest init browser` command to install required dependencies and create browser configuration. npmyarnpnpmbun npx vitest init browser yarn exec vitest init browser pnpx vitest init browser bunx vitest init browser ### Manual Installation You can also install packages manually. By default, Browser Mode doesn't require any additional E2E provider to run tests locally because it reuses your existing browser. npmyarnpnpmbun npm install -D vitest @vitest/browser yarn add -D vitest @vitest/browser pnpm add -D vitest @vitest/browser bun add -D vitest @vitest/browser **WARNING** However, to run tests in CI you need to install either [`playwright`](https://npmjs.com/package/playwright) or [`webdriverio`](https://www.npmjs.com/package/webdriverio). We also recommend switching to either one of them for testing locally instead of using the default `preview` provider since it relies on simulating events instead of using Chrome DevTools Protocol. If you don't already use one of these tools, we recommend starting with Playwright because it supports parallel execution, which makes your tests run faster. Additionally, Playwright uses [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) which is generally faster than WebDriver. PlaywrightWebdriverIO [Playwright](https://npmjs.com/package/playwright) is a framework for Web Testing and Automation. npmyarnpnpmbun npm install -D vitest @vitest/browser playwright yarn add -D vitest @vitest/browser playwright pnpm add -D vitest @vitest/browser playwright bun add -D vitest @vitest/browser playwright ## Configuration To activate browser mode in your Vitest configuration, you can use the `--browser=name` flag or set the `browser.enabled` field to `true` in your Vitest configuration file. Here is an example configuration using the browser field: vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { provider: 'playwright', // or 'webdriverio' enabled: true, // at least one instance is required instances: [ { browser: 'chromium' }, ], }, } }) **INFO** Vitest assigns port `63315` to avoid conflicts with the development server, allowing you to run both in parallel. You can change that with the [`browser.api`](../../config/index#browser-api) option. Since Vitest 2.1.5, the CLI no longer prints the Vite URL automatically. You can press "b" to print the URL when running in watch mode. If you have not used Vite before, make sure you have your framework's plugin installed and specified in the config. Some frameworks might require extra configuration to work - check their Vite related documentation to be sure. reactvuesveltesolidmarko import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import { svelte } from '@sveltejs/vite-plugin-svelte' export default defineConfig({ plugins: [svelte()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import solidPlugin from 'vite-plugin-solid' export default defineConfig({ plugins: [solidPlugin()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) import { defineConfig } from 'vitest/config' import marko from '@marko/vite' export default defineConfig({ plugins: [marko()], test: { browser: { enabled: true, provider: 'playwright', instances: [ { browser: 'chromium' }, ], } } }) If you need to run some tests using Node-based runner, you can define a [workspace](../workspace) file with separate configurations for different testing strategies: vitest.workspace.ts import { defineWorkspace } from 'vitest/config' export default defineWorkspace([ { test: { // an example of file based convention, // you don't have to follow it include: [ 'tests/unit/**/*.{test,spec}.ts', 'tests/**/*.unit.{test,spec}.ts', ], name: 'unit', environment: 'node', }, }, { test: { // an example of file based convention, // you don't have to follow it include: [ 'tests/browser/**/*.{test,spec}.ts', 'tests/**/*.browser.{test,spec}.ts', ], name: 'browser', browser: { enabled: true, instances: [ { browser: 'chromium' }, ], }, }, }, ]) ## Browser Option Types The browser option in Vitest depends on the provider. Vitest will fail, if you pass `--browser` and don't specify its name in the config file. Available options: * `webdriverio` supports these browsers: * `firefox` * `chrome` * `edge` * `safari` * `playwright` supports these browsers: * `firefox` * `webkit` * `chromium` ## TypeScript By default, TypeScript doesn't recognize providers options and extra `expect` properties. If you don't use any providers, make sure the `@vitest/browser/matchers` is referenced somewhere in your tests, [setup file](../../config/index#setupfiles) or a [config file](../../config/index) to pick up the extra `expect` definitions. If you are using custom providers, make sure to add `@vitest/browser/providers/playwright` or `@vitest/browser/providers/webdriverio` to the same file so TypeScript can pick up definitions for custom options: defaultplaywrightwebdriverio /// /// /// Alternatively, you can also add them to `compilerOptions.types` field in your `tsconfig.json` file. Note that specifying anything in this field will disable [auto loading](https://www.typescriptlang.org/tsconfig/#types) of `@types/*` packages. defaultplaywrightwebdriverio { "compilerOptions": { "types": ["@vitest/browser/matchers"] } } { "compilerOptions": { "types": ["@vitest/browser/providers/playwright"] } } { "compilerOptions": { "types": ["@vitest/browser/providers/webdriverio"] } } ## Browser Compatibility Vitest uses [Vite dev server](https://vitejs.dev/guide/#browser-support) to run your tests, so we only support features specified in the [`esbuild.target`](https://vitejs.dev/config/shared-options.html#esbuild) option (`esnext` by default). By default, Vite targets browsers which support the native [ES Modules](https://caniuse.com/es6-module), native [ESM dynamic import](https://caniuse.com/es6-module-dynamic-import), and [`import.meta`](https://caniuse.com/mdn-javascript_operators_import_meta). On top of that, we utilize [`BroadcastChannel`](https://caniuse.com/?search=BroadcastChannel) to communicate between iframes: * Chrome >=87 * Firefox >=78 * Safari >=15.4 * Edge >=88 ## Running Tests When you specify a browser name in the browser option, Vitest will try to run the specified browser using `preview` by default, and then run the tests there. If you don't want to use `preview`, you can configure the custom browser provider by using `browser.provider` option. To specify a browser using the CLI, use the `--browser` flag followed by the browser name, like this: npx vitest --browser=chrome Or you can provide browser options to CLI with dot notation: npx vitest --browser.headless By default, Vitest will automatically open the browser UI for development. Your tests will run inside an iframe in the center. You can configure the viewport by selecting the preferred dimensions, calling `page.viewport` inside the test, or setting default values in [the config](../../config/index#browser-viewport). ## Headless Headless mode is another option available in the browser mode. In headless mode, the browser runs in the background without a user interface, which makes it useful for running automated tests. The headless option in Vitest can be set to a boolean value to enable or disable headless mode. When using headless mode, Vitest won't open the UI automatically. If you want to continue using the UI but have tests run headlessly, you can install the [`@vitest/ui`](../ui) package and pass the `--ui` flag when running Vitest. Here's an example configuration enabling headless mode: vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { provider: 'playwright', enabled: true, headless: true, }, } }) You can also set headless mode using the `--browser.headless` flag in the CLI, like this: npx vitest --browser.headless In this case, Vitest will run in headless mode using the Chrome browser. **WARNING** Headless mode is not available by default. You need to use either [`playwright`](https://npmjs.com/package/playwright) or [`webdriverio`](https://www.npmjs.com/package/webdriverio) providers to enable this feature. ## Examples By default, you don't need any external packages to work with the Browser Mode: example.test.js import { expect, test } from 'vitest' import { page } from '@vitest/browser/context' import { render } from './my-render-function.js' test('properly handles form inputs', async () => { render() // mount DOM elements // Asserts initial state. await expect.element(page.getByText('Hi, my name is Alice')).toBeInTheDocument() // Get the input DOM node by querying the associated label. const usernameInput = page.getByLabelText(/username/i) // Type the name into the input. This already validates that the input // is filled correctly, no need to check the value manually. await usernameInput.fill('Bob') await expect.element(page.getByText('Hi, my name is Bob')).toBeInTheDocument() }) However, Vitest also provides packages to render components for several popular frameworks out of the box: * [`vitest-browser-vue`](https://github.com/vitest-dev/vitest-browser-vue) to render [vue](https://vuejs.org) components * [`vitest-browser-svelte`](https://github.com/vitest-dev/vitest-browser-svelte) to render [svelte](https://svelte.dev) components * [`vitest-browser-react`](https://github.com/vitest-dev/vitest-browser-react) to render [react](https://react.dev) components If your framework is not represented, feel free to create your own package - it is a simple wrapper around the framework renderer and `page.elementLocator` API. We will add a link to it on this page. Make sure it has a name starting with `vitest-browser-`. Besides rendering components and locating elements, you will also need to make assertions. Vitest bundles the [`@testing-library/jest- dom`](https://github.com/testing-library/jest-dom) library to provide a wide range of DOM assertions out of the box. Read more at the [Assertions API](assertion-api). import { expect } from 'vitest' import { page } from '@vitest/browser/context' // element is rendered correctly await expect.element(page.getByText('Hello World')).toBeInTheDocument() Vitest exposes a [Context API](context) with a small set of utilities that might be useful to you in tests. For example, if you need to make an interaction, like clicking an element or typing text into an input, you can use `userEvent` from `@vitest/browser/context`. Read more at the [Interactivity API](interactivity-api). import { page, userEvent } from '@vitest/browser/context' await userEvent.fill(page.getByLabelText(/username/i), 'Alice') // or just locator.fill await page.getByLabelText(/username/i).fill('Alice') vuesveltereact import { render } from 'vitest-browser-vue' import Component from './Component.vue' test('properly handles v-model', async () => { const screen = render(Component) // Asserts initial state. await expect.element(screen.getByText('Hi, my name is Alice')).toBeInTheDocument() // Get the input DOM node by querying the associated label. const usernameInput = screen.getByLabelText(/username/i) // Type the name into the input. This already validates that the input // is filled correctly, no need to check the value manually. await usernameInput.fill('Bob') await expect.element(screen.getByText('Hi, my name is Bob')).toBeInTheDocument() }) import { render } from 'vitest-browser-svelte' import { expect, test } from 'vitest' import Greeter from './greeter.svelte' test('greeting appears on click', async () => { const screen = render(Greeter, { name: 'World' }) const button = screen.getByRole('button') await button.click() const greeting = screen.getByText(/hello world/iu) await expect.element(greeting).toBeInTheDocument() }) import { render } from 'vitest-browser-react' import Fetch from './fetch' test('loads and displays greeting', async () => { // Render a React element into the DOM const screen = render() await screen.getByText('Load Greeting').click() // wait before throwing an error if it cannot find an element const heading = screen.getByRole('heading') // assert that the alert message is correct await expect.element(heading).toHaveTextContent('hello there') await expect.element(screen.getByRole('button')).toBeDisabled() }) Vitest doesn't support all frameworks out of the box, but you can use external tools to run tests with these frameworks. We also encourage the community to create their own `vitest-browser` wrappers - if you have one, feel free to add it to the examples above. For unsupported frameworks, we recommend using `testing-library` packages: * [`@testing-library/preact`](https://testing-library.com/docs/preact-testing-library/intro) to render [preact](https://preactjs.com) components * [`@solidjs/testing-library`](https://testing-library.com/docs/solid-testing-library/intro) to render [solid](https://www.solidjs.com) components * [`@marko/testing-library`](https://testing-library.com/docs/marko-testing-library/intro) to render [marko](https://markojs.com) components You can also see more examples in [`browser- examples`](https://github.com/vitest-tests/browser-examples) repository. **WARNING** `testing-library` provides a package `@testing-library/user-event`. We do not recommend using it directly because it simulates events instead of actually triggering them - instead, use [`userEvent`](interactivity-api) imported from `@vitest/browser/context` that uses Chrome DevTools Protocol or Webdriver (depending on the provider) under the hood. preactsolidmarko // based on @testing-library/preact example // https://testing-library.com/docs/preact-testing-library/example import { h } from 'preact' import { page } from '@vitest/browser/context' import { render } from '@testing-library/preact' import HiddenMessage from '../hidden-message' test('shows the children when the checkbox is checked', async () => { const testMessage = 'Test Message' const { baseElement } = render( {testMessage}, ) const screen = page.elementLocator(baseElement) // .query() will return the element or null if it cannot be found. // .element() will return the element or throw an error if it cannot be found. expect(screen.getByText(testMessage).query()).not.toBeInTheDocument() // The queries can accept a regex to make your selectors more // resilient to content tweaks and changes. await screen.getByLabelText(/show/i).click() await expect.element(screen.getByText(testMessage)).toBeInTheDocument() }) // baed on @testing-library/solid API // https://testing-library.com/docs/solid-testing-library/api import { render } from '@testing-library/solid' it('uses params', async () => { const App = () => ( <> (

Id: {useParams()?.id}

)} />

Start

} /> ) const { baseElement } = render(() => , { location: 'ids/1234' }) const screen = page.elementLocator(baseElement) await expect.screen(screen.getByText('Id: 1234')).toBeInTheDocument() }) // baed on @testing-library/marko API // https://testing-library.com/docs/marko-testing-library/api import { render, screen } from '@marko/testing-library' import Greeting from './greeting.marko' test('renders a message', async () => { const { baseElement } = await render(Greeting, { name: 'Marko' }) const screen = page.elementLocator(baseElement) await expect.element(screen.getByText(/Marko/)).toBeInTheDocument() expect(container.firstChild).toMatchInlineSnapshot(`

Hello, Marko!

`) }) ## Limitations ### Thread Blocking Dialogs When using Vitest Browser, it's important to note that thread blocking dialogs like `alert` or `confirm` cannot be used natively. This is because they block the web page, which means Vitest cannot continue communicating with the page, causing the execution to hang. In such situations, Vitest provides default mocks with default returned values for these APIs. This ensures that if the user accidentally uses synchronous popup web APIs, the execution would not hang. However, it's still recommended for the user to mock these web APIs for better experience. Read more in [Mocking](../mocking). # Interactivity API Vitest implements a subset of [`@testing-library/user-event`](https://testing- library.com/docs/user-event/intro) APIs using [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) or [webdriver](https://www.w3.org/TR/webdriver/) instead of faking events which makes the browser behaviour more reliable and consistent with how users interact with a page. import { userEvent } from '@vitest/browser/context' await userEvent.click(document.querySelector('.button')) Almost every `userEvent` method inherits its provider options. To see all available options in your IDE, add `webdriver` or `playwright` types (depending on your provider) to your [setup file](../../config/index#setupfile) or a [config file](../../config/index) (depending on what is in `included` in your `tsconfig.json`): playwrightwebdriverio /// /// ## userEvent.setup function setup(): UserEvent Creates a new user event instance. This is useful if you need to keep the state of keyboard to press and release buttons correctly. **WARNING** Unlike `@testing-library/user-event`, the default `userEvent` instance from `@vitest/browser/context` is created once, not every time its methods are called! You can see the difference in how it works in this snippet: import { userEvent as vitestUserEvent } from '@vitest/browser/context' import { userEvent as originalUserEvent } from '@testing-library/user-event' await vitestUserEvent.keyboard('{Shift}') // press shift without releasing await vitestUserEvent.keyboard('{/Shift}') // releases shift await originalUserEvent.keyboard('{Shift}') // press shift without releasing await originalUserEvent.keyboard('{/Shift}') // DID NOT release shift because the state is different This behaviour is more useful because we do not emulate the keyboard, we actually press the Shift, so keeping the original behaviour would cause unexpected issues when typing in the field. ## userEvent.click function click( element: Element | Locator, options?: UserEventClickOptions, ): Promise Click on an element. Inherits provider's options. Please refer to your provider's documentation for detailed explanation about how this method works. import { page, userEvent } from '@vitest/browser/context' test('clicks on an element', async () => { const logo = page.getByRole('img', { name: /logo/ }) await userEvent.click(logo) // or you can access it directly on the locator await logo.click() }) References: * [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click) * [WebdriverIO `element.click` API](https://webdriver.io/docs/api/element/click/) * [testing-library `click` API](https://testing-library.com/docs/user-event/convenience/#click) ## userEvent.dblClick function dblClick( element: Element | Locator, options?: UserEventDoubleClickOptions, ): Promise Triggers a double click event on an element. Please refer to your provider's documentation for detailed explanation about how this method works. import { page, userEvent } from '@vitest/browser/context' test('triggers a double click on an element', async () => { const logo = page.getByRole('img', { name: /logo/ }) await userEvent.dblClick(logo) // or you can access it directly on the locator await logo.dblClick() }) References: * [Playwright `locator.dblclick` API](https://playwright.dev/docs/api/class-locator#locator-dblclick) * [WebdriverIO `element.doubleClick` API](https://webdriver.io/docs/api/element/doubleClick/) * [testing-library `dblClick` API](https://testing-library.com/docs/user-event/convenience/#dblClick) ## userEvent.tripleClick function tripleClick( element: Element | Locator, options?: UserEventTripleClickOptions, ): Promise Triggers a triple click event on an element. Since there is no `tripleclick` in browser api, this method will fire three click events in a row, and so you must check [click event detail](https://developer.mozilla.org/en- US/docs/Web/API/Element/click_event#usage_notes) to filter the event: `evt.detail === 3`. Please refer to your provider's documentation for detailed explanation about how this method works. import { page, userEvent } from '@vitest/browser/context' test('triggers a triple click on an element', async () => { const logo = page.getByRole('img', { name: /logo/ }) let tripleClickFired = false logo.addEventListener('click', (evt) => { if (evt.detail === 3) { tripleClickFired = true } }) await userEvent.tripleClick(logo) // or you can access it directly on the locator await logo.tripleClick() expect(tripleClickFired).toBe(true) }) References: * [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click): implemented via `click` with `clickCount: 3` . * [WebdriverIO `browser.action` API](https://webdriver.io/docs/api/browser/action/): implemented via actions api with `move` plus three `down + up + pause` events in a row * [testing-library `tripleClick` API](https://testing-library.com/docs/user-event/convenience/#tripleClick) ## userEvent.fill function fill( element: Element | Locator, text: string, ): Promise Set a value to the `input/textarea/conteneditable` field. This will remove any existing text in the input before setting the new value. import { page, userEvent } from '@vitest/browser/context' test('update input', async () => { const input = page.getByRole('input') await userEvent.fill(input, 'foo') // input.value == foo await userEvent.fill(input, '{{a[[') // input.value == {{a[[ await userEvent.fill(input, '{Shift}') // input.value == {Shift} // or you can access it directly on the locator await input.fill('foo') // input.value == foo }) This methods focuses the element, fills it and triggers an `input` event after filling. You can use an empty string to clear the field. **TIP** This API is faster than using `userEvent.type` or `userEvent.keyboard`, but it **doesn't support** [user-event `keyboard` syntax](https://testing- library.com/docs/user-event/keyboard) (e.g., `{Shift}{selectall}`). We recommend using this API over `userEvent.type` in situations when you don't need to enter special characters or have granular control over keypress events. References: * [Playwright `locator.fill` API](https://playwright.dev/docs/api/class-locator#locator-fill) * [WebdriverIO `element.setValue` API](https://webdriver.io/docs/api/element/setValue) * [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type) ## userEvent.keyboard function keyboard(text: string): Promise The `userEvent.keyboard` allows you to trigger keyboard strokes. If any input has a focus, it will type characters into that input. Otherwise, it will trigger keyboard events on the currently focused element (`document.body` if there are no focused elements). This API supports [user-event `keyboard` syntax](https://testing- library.com/docs/user-event/keyboard). import { userEvent } from '@vitest/browser/context' test('trigger keystrokes', async () => { await userEvent.keyboard('foo') // translates to: f, o, o await userEvent.keyboard('{{a[[') // translates to: {, a, [ await userEvent.keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, o await userEvent.keyboard('{a>5}') // press a without releasing it and trigger 5 keydown await userEvent.keyboard('{a>5/}') // press a for 5 keydown and then release it }) References: * [Playwright `Keyboard` API](https://playwright.dev/docs/api/class-keyboard) * [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source) * [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type) ## userEvent.tab function tab(options?: UserEventTabOptions): Promise Sends a `Tab` key event. This is a shorthand for `userEvent.keyboard('{tab}')`. import { page, userEvent } from '@vitest/browser/context' test('tab works', async () => { const [input1, input2] = page.getByRole('input').elements() expect(input1).toHaveFocus() await userEvent.tab() expect(input2).toHaveFocus() await userEvent.tab({ shift: true }) expect(input1).toHaveFocus() }) References: * [Playwright `Keyboard` API](https://playwright.dev/docs/api/class-keyboard) * [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source) * [testing-library `tab` API](https://testing-library.com/docs/user-event/convenience/#tab) ## userEvent.type function type( element: Element | Locator, text: string, options?: UserEventTypeOptions, ): Promise **WARNING** If you don't rely on [special characters](https://testing- library.com/docs/user-event/keyboard) (e.g., `{shift}` or `{selectall}`), it is recommended to use `userEvent.fill` instead for better performance. The `type` method implements `@testing-library/user-event`'s [`type`](https://testing-library.com/docs/user-event/utility/#type) utility built on top of [`keyboard`](https://testing-library.com/docs/user- event/keyboard) API. This function allows you to type characters into an input/textarea/conteneditable element. It supports [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard). If you just need to press characters without an input, use `userEvent.keyboard` API. import { page, userEvent } from '@vitest/browser/context' test('update input', async () => { const input = page.getByRole('input') await userEvent.type(input, 'foo') // input.value == foo await userEvent.type(input, '{{a[[') // input.value == foo{a[ await userEvent.type(input, '{Shift}') // input.value == foo{a[ }) **INFO** Vitest doesn't expose `.type` method on the locator like `input.type` because it exists only for compatibility with the `userEvent` library. Consider using `.fill` instead as it is faster. References: * [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press) * [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source) * [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type) ## userEvent.clear function clear(element: Element | Locator): Promise This method clears the input element content. import { page, userEvent } from '@vitest/browser/context' test('clears input', async () => { const input = page.getByRole('input') await userEvent.fill(input, 'foo') expect(input).toHaveValue('foo') await userEvent.clear(input) // or you can access it directly on the locator await input.clear() expect(input).toHaveValue('') }) References: * [Playwright `locator.clear` API](https://playwright.dev/docs/api/class-locator#locator-clear) * [WebdriverIO `element.clearValue` API](https://webdriver.io/docs/api/element/clearValue) * [testing-library `clear` API](https://testing-library.com/docs/user-event/utility/#clear) ## userEvent.selectOptions function selectOptions( element: Element | Locator, values: | HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[], options?: UserEventSelectOptions, ): Promise The `userEvent.selectOptions` allows selecting a value in a `
You can locate each element by its implicit role: await expect.element( page.getByRole('heading', { name: 'Sign up' }) ).toBeVisible() await page.getByRole('textbox', { name: 'Login' }).fill('admin') await page.getByRole('textbox', { name: 'Password' }).fill('admin') await page.getByRole('button', { name: /submit/i }).click() **WARNING** Roles are matched by string equality, without inheriting from the ARIA role hierarchy. As a result, querying a superclass role like `checkbox` will not include elements with a subclass role like `switch`. By default, many semantic elements in HTML have a role; for example, `` has the "radio" role. Non-semantic elements in HTML do not have a role; `
` and `` without added semantics return `null`. The `role` attribute can provide semantics. Providing roles via `role` or `aria-*` attributes to built-in elements that already have an implicit role is **highly discouraged** by ARIA guidelines. ##### Options * `exact: boolean` Whether the `name` is matched exactly: case-sensitive and whole-string. Disabled by default. This option is ignored if `name` is a regular expression. Note that exact match still trims whitespace. page.getByRole('button', { name: 'hello world' }) // ✅ page.getByRole('button', { name: 'hello world', exact: true }) // ❌ page.getByRole('button', { name: 'Hello World', exact: true }) // ✅ * `checked: boolean` Should checked elements (set by `aria-checked` or ``) be included or not. By default, the filter is not applied. See [`aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked) for more information <> page.getByRole('button', { name: 'Click Me!' }) // ✅ page.getByRole('button', { name: 'click me!' }) // ✅ page.getByRole('button', { name: 'Click Me?' }) // ❌ * `pressed: boolean` Should pressed elements be included or not. By default, the filter is not applied. See [`aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed) for more information page.getByRole('button', { pressed: true }) // ✅ page.getByRole('button', { pressed: false }) // ❌ * `selected: boolean` Should selected elements be included or not. By default, the filter is not applied. See [`aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected) for more information page.getByRole('button', { selected: true }) // ✅ page.getByRole('button', { selected: false }) // ❌ ##### See also * [List of ARIA roles at MDN](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles) * [List of ARIA roles at w3.org](https://www.w3.org/TR/wai-aria-1.2/#role_definitions) * [testing-library's `ByRole`](https://testing-library.com/docs/queries/byrole/) ## getByAltText function getByAltText( text: string | RegExp, options?: LocatorOptions, ): Locator Creates a locator capable of finding an element with an `alt` attribute that matches the text. Unlike testing-library's implementation, Vitest will match any element that has a matching `alt` attribute. Incredibles 2 Poster page.getByAltText(/incredibles.*? poster/i) // ✅ page.getByAltText('non existing alt text') // ❌ #### Options * `exact: boolean` Whether the `text` is matched exactly: case-sensitive and whole-string. Disabled by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace. #### See also * [testing-library's `ByAltText`](https://testing-library.com/docs/queries/byalttext/) ## getByLabelText function getByLabelText( text: string | RegExp, options?: LocatorOptions, ): Locator Creates a locator capable of finding an element that has an associated label. The `page.getByLabelText('Username')` locator will find every input in the example bellow: // for/htmlFor relationship between label and form element id // The aria-labelledby attribute with form elements // Wrapper labels // Wrapper labels where the label text is in another child element // aria-label attributes // Take care because this is not a label that users can see on the page, // so the purpose of your input must be obvious to visual users. #### Options * `exact: boolean` Whether the `text` is matched exactly: case-sensitive and whole-string. Disabled by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace. #### See also * [testing-library's `ByLabelText`](https://testing-library.com/docs/queries/bylabeltext/) ## getByPlaceholder function getByPlaceholder( text: string | RegExp, options?: LocatorOptions, ): Locator Creates a locator capable of finding an element that has the specified `placeholder` attribute. Vitest will match any element that has a matching `placeholder` attribute, not just `input`. page.getByPlaceholder('Username') // ✅ page.getByPlaceholder('not found') // ❌ **WARNING** It is generally better to rely on a label using `getByLabelText` than a placeholder. #### Options * `exact: boolean` Whether the `text` is matched exactly: case-sensitive and whole-string. Disabled by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace. #### See also * [testing-library's `ByPlaceholderText`](https://testing-library.com/docs/queries/byplaceholdertext/) ## getByText function getByText( text: string | RegExp, options?: LocatorOptions, ): Locator Creates a locator capable of finding an element that contains the specified text. The text will be matched against TextNode's [`nodeValue`](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeValue) or input's value if the type is `button` or `reset`. Matching by text always normalizes whitespace, even with exact match. For example, it turns multiple spaces into one, turns line breaks into spaces and ignores leading and trailing whitespace. About ℹ️ page.getByText(/about/i) // ✅ page.getByText('about', { exact: true }) // ❌ **TIP** This locator is useful for locating non-interactive elements. If you need to locate an interactive element, like a button or an input, prefer `getByRole`. #### Options * `exact: boolean` Whether the `text` is matched exactly: case-sensitive and whole-string. Disabled by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace. #### See also * [testing-library's `ByText`](https://testing-library.com/docs/queries/bytext/) ## getByTitle function getByTitle( text: string | RegExp, options?: LocatorOptions, ): Locator Creates a locator capable of finding an element that has the specified `title` attribute. Unlike testing-library's `getByTitle`, Vitest cannot find `title` elements within an SVG. page.getByTitle('Delete') // ✅ page.getByTitle('Create') // ❌ #### Options * `exact: boolean` Whether the `text` is matched exactly: case-sensitive and whole-string. Disabled by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace. #### See also * [testing-library's `ByTitle`](https://testing-library.com/docs/queries/bytitle/) ## getByTestId function getByTestId(text: string | RegExp): Locator Creates a locator capable of finding an element that matches the specified test id attribute. You can configure the attribute name with [`browser.locators.testIdAttribute`](config#browser-locators-testidattribute).
page.getByTestId('custom-element') // ✅ page.getByTestId('non-existing-element') // ❌ **WARNING** It is recommended to use this only after the other locators don't work for your use case. Using `data-testid` attributes does not resemble how your software is used and should be avoided if possible. #### Options * `exact: boolean` Whether the `text` is matched exactly: case-sensetive and whole-string. Disabled by default. This option is ignored if `text` is a regular expression. Note that exact match still trims whitespace. #### See also * [testing-library's `ByTestId`](https://testing-library.com/docs/queries/bytestid/) ## nth function nth(index: number): Locator This method returns a new locator that matches only a specific index within a multi-element query result. It's zero based, `nth(0)` selects the first element. Unlike `elements()[n]`, the `nth` locator will be retried until the element is present.
page.getByRole('textbox').nth(0) // ✅ page.getByRole('textbox').nth(4) // ❌ **TIP** Before resorting to `nth`, you may find it useful to use chained locators to narrow down your search. Sometimes there is no better way to distinguish than by element position; although this can lead to flake, it's better than nothing. page.getByLabel('two').getByRole('input') // ✅ better alternative to page.getByRole('textbox').nth(3) page.getByLabel('one').getByRole('input') // ❌ too ambiguous page.getByLabel('one').getByRole('input').nth(1) // ✅ pragmatic compromise ## first function first(): Locator This method returns a new locator that matches only the first index of a multi-element query result. It is sugar for `nth(0)`. page.getByRole('textbox').first() // ✅ ## last function last(): Locator This method returns a new locator that matches only the last index of a multi- element query result. It is sugar for `nth(-1)`. page.getByRole('textbox').last() // ✅ ## Methods All methods are asynchronous and must be awaited. Since Vitest 3, tests will fail if a method is not awaited. ### click function click(options?: UserEventClickOptions): Promise Click on an element. You can use the options to set the cursor position. import { page } from '@vitest/browser/context' await page.getByRole('img', { name: 'Rose' }).click() * [See more at `userEvent.click`](interactivity-api#userevent-click) ### dblClick function dblClick(options?: UserEventDoubleClickOptions): Promise Triggers a double click event on an element. You can use the options to set the cursor position. import { page } from '@vitest/browser/context' await page.getByRole('img', { name: 'Rose' }).dblClick() * [See more at `userEvent.dblClick`](interactivity-api#userevent-dblclick) ### tripleClick function tripleClick(options?: UserEventTripleClickOptions): Promise Triggers a triple click event on an element. Since there is no `tripleclick` in browser api, this method will fire three click events in a row. import { page } from '@vitest/browser/context' await page.getByRole('img', { name: 'Rose' }).tripleClick() * [See more at `userEvent.tripleClick`](interactivity-api#userevent-tripleclick) ### clear function clear(): Promise Clears the input element content. import { page } from '@vitest/browser/context' await page.getByRole('textbox', { name: 'Full Name' }).clear() * [See more at `userEvent.clear`](interactivity-api#userevent-clear) ### hover function hover(options?: UserEventHoverOptions): Promise Moves the cursor position to the selected element. import { page } from '@vitest/browser/context' await page.getByRole('img', { name: 'Rose' }).hover() * [See more at `userEvent.hover`](interactivity-api#userevent-hover) ### unhover function unhover(options?: UserEventHoverOptions): Promise This works the same as `locator.hover`, but moves the cursor to the `document.body` element instead. import { page } from '@vitest/browser/context' await page.getByRole('img', { name: 'Rose' }).unhover() * [See more at `userEvent.unhover`](interactivity-api#userevent-unhover) ### fill function fill(text: string, options?: UserEventFillOptions): Promise Sets the value of the current `input`, `textarea` or `conteneditable` element. import { page } from '@vitest/browser/context' await page.getByRole('input', { name: 'Full Name' }).fill('Mr. Bean') * [See more at `userEvent.fill`](interactivity-api#userevent-fill) ### dropTo function dropTo( target: Locator, options?: UserEventDragAndDropOptions, ): Promise Drags the current element to the target location. import { page } from '@vitest/browser/context' const paris = page.getByText('Paris') const france = page.getByText('France') await paris.dropTo(france) * [See more at `userEvent.dragAndDrop`](interactivity-api#userevent-draganddrop) ### selectOptions function selectOptions( values: | HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[], options?: UserEventSelectOptions, ): Promise Choose one or more values from a `