Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ test/cli/fixtures/browser-multiple/basic-*
# exclude static html reporter folder
test/browser/html/
test/core/html/
.vitest-attachments
.vitest-attachments
explainFiles.txt
2 changes: 1 addition & 1 deletion docs/advanced/runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export interface TaskResult {
* Errors that occurred during the task execution. It is possible to have several errors
* if `expect.soft()` failed multiple times.
*/
errors?: ErrorWithDiff[]
errors?: TestError[]
/**
* How long in milliseconds the task took to run.
*/
Expand Down
56 changes: 11 additions & 45 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ When using coverage, Vitest automatically adds test files `include` patterns to
### 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"`
- **Default:** `['**/node_modules/**', '**/.git/**']`
- **CLI:** `vitest --exclude "**/excluded-file" --exclude "*/other-files/*.js"`

A list of glob patterns that should be excluded from your test files.

Expand Down Expand Up @@ -1628,51 +1628,11 @@ Sets thresholds to 100 for files matching the glob pattern.
}
```

#### coverage.ignoreEmptyLines

- **Type:** `boolean`
- **Default:** `true` (`false` in v1)
- **Available for providers:** `'v8'`
- **CLI:** `--coverage.ignoreEmptyLines=<boolean>`

Ignore empty lines, comments and other non-runtime code, e.g. Typescript types. Requires `experimentalAstAwareRemapping: false`.

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):

```ts
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.experimentalAstAwareRemapping

- **Type:** `boolean`
- **Default:** `false`
- **Available for providers:** `'v8'`
- **CLI:** `--coverage.experimentalAstAwareRemapping=<boolean>`

Remap coverage with experimental AST based analysis. Provides more accurate results compared to default mode.

#### coverage.ignoreClassMethods

- **Type:** `string[]`
- **Default:** `[]`
- **Available for providers:** `'istanbul'`
- **Available for providers:** `'v8' | 'istanbul'`
- **CLI:** `--coverage.ignoreClassMethods=<method>`

Set to array of class method names to ignore for coverage.
Expand Down Expand Up @@ -2273,9 +2233,15 @@ Retry the test specific number of times if it fails.

### onConsoleLog<NonProjectOption />

- **Type**: `(log: string, type: 'stdout' | 'stderr') => boolean | void`
```ts
function onConsoleLog(
log: string,
type: 'stdout' | 'stderr',
entity: TestModule | TestSuite | TestCase | undefined,
): boolean | void
```

Custom handler for `console.log` in tests. If you return `false`, Vitest will not print the log to the console.
Custom handler for `console` methods in tests. If you return `false`, Vitest will not print the log to the console. Note that Vitest ignores all other falsy values.

Can be useful for filtering out logs from third-party libraries.

Expand Down
3 changes: 1 addition & 2 deletions docs/guide/coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,8 @@ Please refer to the type definition for more details.

Both coverage providers have their own ways how to ignore code from coverage reports:

- [`v8`](https://github.com/istanbuljs/v8-to-istanbul#ignoring-uncovered-lines)
- [`v8`](https://github.com/AriPerkkio/ast-v8-to-istanbul?tab=readme-ov-file#ignoring-code)
- [`istanbul`](https://github.com/istanbuljs/nyc#parsing-hints-ignoring-lines)
- `v8` with [`experimentalAstAwareRemapping: true`](https://vitest.dev/config/#coverage-experimentalAstAwareRemapping) see [ast-v8-to-istanbul | Ignoring code](https://github.com/AriPerkkio/ast-v8-to-istanbul?tab=readme-ov-file#ignoring-code)

When using TypeScript the source codes are transpiled using `esbuild`, which strips all comments from the source codes ([esbuild#516](https://github.com/evanw/esbuild/issues/516)).
Comments which are considered as [legal comments](https://esbuild.github.io/api/#legal-comments) are preserved.
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/improving-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ export default defineConfig({
```
:::

## Limiting directory search

You can limit the working directory when Vitest searches for files using [`test.dir`](/config/#test-dir) option. This should make the search faster if you have unrelated folders and files in the root directory.

## Pool

By default Vitest runs tests in `pool: 'forks'`. While `'forks'` pool is better for compatibility issues ([hanging process](/guide/common-errors.html#failed-to-terminate-worker) and [segfaults](/guide/common-errors.html#segfaults-and-native-code-errors)), it may be slightly slower than `pool: 'threads'` in larger projects.
Expand Down
15 changes: 15 additions & 0 deletions docs/guide/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,21 @@ export default defineConfig({
})
```

### V8 Code Coverage Major Changes

Vitest's V8 code coverage provider is now using more accurate coverage result remapping logic.
It is expected for users to see changes in their coverage reports when updating from Vitest v3.

In the past Vitest used [`v8-to-istanbul`](https://github.com/istanbuljs/v8-to-istanbul) for remapping V8 coverage results into your source files.
This method wasn't very accurate and provided plenty of false positives in the coverage reports.
We've now developed a new package that utilizes AST based analysis for the V8 coverage.
This allows V8 reports to be as accurate as `@vitest/coverage-istanbul` reports.

- Coverage ignore hints have updated. See [Coverage | Ignoring Code](/guide/coverage.html#ignoring-code).
- `coverage.ignoreEmptyLines` is removed. Lines without runtime code are no longer included in reports.
- `coverage.experimentalAstAwareRemapping` is removed. This option is now enabled by default, and is the only supported remapping method.
- `coverage.ignoreClassMethods` is now supported by V8 provider too.

### Removed options `coverage.all` and `coverage.extensions`

In previous versions Vitest included all uncovered files in coverage report by default.
Expand Down
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"tinyglobby": "catalog:",
"unocss": "catalog:",
"unplugin-vue-components": "catalog:",
"vite": "^5.2.8",
"vite": "^6.3.5",
"vite-plugin-pwa": "^0.21.2",
"vitepress": "2.0.0-alpha.6",
"vitepress-plugin-group-icons": "^1.6.0",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@vitest/monorepo",
"type": "module",
"version": "3.2.4",
"version": "4.0.0-beta.1",
"private": true,
"packageManager": "pnpm@10.12.1",
"description": "Next generation testing framework powered by Vite",
Expand All @@ -27,6 +27,7 @@
"test:ci": "CI=true pnpm -r --reporter-hide-prefix --stream --sequential --filter '@vitest/test-*' --filter !test-browser run test",
"test:examples": "CI=true pnpm -r --reporter-hide-prefix --stream --filter '@vitest/example-*' run test",
"test:ecosystem-ci": "ECOSYSTEM_CI=true pnpm test:ci",
"typebuild": "tsx ./scripts/explain-types.ts",
"typecheck": "tsc -p tsconfig.check.json --noEmit",
"typecheck:why": "tsc -p tsconfig.check.json --noEmit --explainFiles > explainTypes.txt",
"ui:build": "vite build packages/ui",
Expand Down Expand Up @@ -86,7 +87,6 @@
"@sinonjs/fake-timers@14.0.0": "patches/@sinonjs__fake-timers@14.0.0.patch",
"cac@6.7.14": "patches/cac@6.7.14.patch",
"@types/sinonjs__fake-timers@8.1.5": "patches/@types__sinonjs__fake-timers@8.1.5.patch",
"v8-to-istanbul@9.3.0": "patches/v8-to-istanbul@9.3.0.patch",
"acorn@8.11.3": "patches/acorn@8.11.3.patch"
},
"onlyBuiltDependencies": [
Expand Down
4 changes: 3 additions & 1 deletion packages/browser/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@vitest/browser",
"type": "module",
"version": "3.2.4",
"version": "4.0.0-beta.1",
"description": "Browser running for Vitest",
"license": "MIT",
"funding": "https://opencollective.com/vitest",
Expand Down Expand Up @@ -64,6 +64,8 @@
"providers"
],
"scripts": {
"typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",
"typecheck:why": "tsc -p ./src/client/tsconfig.json --noEmit --explainFiles > explainTypes.txt",
"build": "rimraf dist && pnpm build:node && pnpm build:client",
"build:client": "vite build src/client",
"build:node": "rollup -c",
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ModuleMocker } from '@vitest/mocker/browser'
import type { CancelReason } from '@vitest/runner'
import type { BirpcReturn } from 'birpc'
import type { WebSocketBrowserEvents, WebSocketBrowserHandlers } from '../node/types'
import type { WebSocketBrowserEvents, WebSocketBrowserHandlers } from '../types'
import type { IframeOrchestrator } from './orchestrator'
import { createBirpc } from 'birpc'
import { parse, stringify } from 'flatted'
Expand Down
28 changes: 19 additions & 9 deletions packages/browser/src/client/tester/context.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import type { Options as TestingLibraryOptions, UserEvent as TestingLibraryUserEvent } from '@testing-library/user-event'
import type { RunnerTask } from 'vitest'
import type {
Options as TestingLibraryOptions,
UserEvent as TestingLibraryUserEvent,
} from '@testing-library/user-event'
import type {
BrowserLocators,
BrowserPage,
Locator,
UserEvent,
} from '../../../context'
} from '@vitest/browser/context'
import type { RunnerTask } from 'vitest'
import type { IframeViewportEvent } from '../client'
import type { BrowserRunnerState } from '../utils'
import type { Locator as LocatorAPI } from './locators/index'
Expand Down Expand Up @@ -289,12 +292,19 @@ export const page: BrowserPage = {
const name
= options.path || `${taskName.replace(/[^a-z0-9]/gi, '-')}-${number}.png`

return ensureAwaited(error => triggerCommand('__vitest_screenshot', [name, processTimeoutOptions({
...options,
element: options.element
? convertToSelector(options.element)
: undefined,
})], error))
return ensureAwaited(error => triggerCommand(
'__vitest_screenshot',
[
name,
processTimeoutOptions({
...options,
element: options.element
? convertToSelector(options.element)
: undefined,
} as any /** TODO */),
],
error,
))
},
getByRole() {
throw new Error(`Method "getByRole" is not implemented in the "${provider}" provider.`)
Expand Down
10 changes: 9 additions & 1 deletion packages/browser/src/client/tester/locators/playwright.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import type { UserEventClearOptions, UserEventClickOptions, UserEventDragAndDropOptions, UserEventFillOptions, UserEventHoverOptions, UserEventSelectOptions, UserEventUploadOptions } from '@vitest/browser/context'
import type {
UserEventClearOptions,
UserEventClickOptions,
UserEventDragAndDropOptions,
UserEventFillOptions,
UserEventHoverOptions,
UserEventSelectOptions,
UserEventUploadOptions,
} from '@vitest/browser/context'
import { page, server } from '@vitest/browser/context'
import {
getByAltTextSelector,
Expand Down
7 changes: 6 additions & 1 deletion packages/browser/src/client/tester/locators/webdriverio.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { UserEventClickOptions, UserEventDragAndDropOptions, UserEventHoverOptions, UserEventSelectOptions } from '@vitest/browser/context'
import type {
UserEventClickOptions,
UserEventDragAndDropOptions,
UserEventHoverOptions,
UserEventSelectOptions,
} from '@vitest/browser/context'
import { page, server } from '@vitest/browser/context'
import {
getByAltTextSelector,
Expand Down
9 changes: 1 addition & 8 deletions packages/browser/src/client/tester/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,21 @@ import { getSafeTimers } from 'vitest/internal/browser'
const { get } = Reflect

function withSafeTimers(getTimers: typeof getSafeTimers, fn: () => void) {
const { setTimeout, clearTimeout, setImmediate, clearImmediate }
= getTimers()
const { setTimeout, clearTimeout } = getTimers()

const currentSetTimeout = globalThis.setTimeout
const currentClearTimeout = globalThis.clearTimeout
const currentSetImmediate = globalThis.setImmediate
const currentClearImmediate = globalThis.clearImmediate

try {
globalThis.setTimeout = setTimeout
globalThis.clearTimeout = clearTimeout
globalThis.setImmediate = setImmediate
globalThis.clearImmediate = clearImmediate

const result = fn()
return result
}
finally {
globalThis.setTimeout = currentSetTimeout
globalThis.clearTimeout = currentClearTimeout
globalThis.setImmediate = currentSetImmediate
globalThis.clearImmediate = currentClearImmediate
}
}

Expand Down
7 changes: 3 additions & 4 deletions packages/browser/src/client/tester/runner.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { CancelReason, File, Suite, Task, TaskEventPack, TaskResultPack, Test, TestAnnotation, VitestRunner } from '@vitest/runner'
import type { SerializedConfig, TestExecutionMethod, WorkerGlobalState } from 'vitest'
import type { VitestExecutor } from 'vitest/execute'
import type { VitestBrowserClientMocker } from './mocker'
import { globalChannel, onCancel } from '@vitest/browser/client'
import { page, userEvent } from '@vitest/browser/context'
Expand Down Expand Up @@ -77,7 +76,7 @@ export function createBrowserRunner(
if (this.config.browser.screenshotFailures && document.body.clientHeight > 0 && task.result?.state === 'fail') {
const screenshot = await page.screenshot({
timeout: this.config.browser.providerOptions?.actionTimeout ?? 5_000,
}).catch((err) => {
} as any /** TODO */).catch((err) => {
console.error('[vitest] Failed to take a screenshot', err)
})
if (screenshot) {
Expand Down Expand Up @@ -239,8 +238,8 @@ export async function initiateRunner(
})

const [diffOptions] = await Promise.all([
loadDiffConfig(config, executor as unknown as VitestExecutor),
loadSnapshotSerializers(config, executor as unknown as VitestExecutor),
loadDiffConfig(config, executor as any),
loadSnapshotSerializers(config, executor as any),
])
runner.config.diffOptions = diffOptions
getWorkerState().onFilterStackTrace = (stack: string) => {
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/client/tester/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const state: WorkerGlobalState = {
},
onCleanup: fn => getBrowserState().cleanups.push(fn),
moduleCache: getBrowserState().moduleCache,
moduleExecutionInfo: new Map(),
rpc: null as any,
durations: {
environment: 0,
Expand Down
21 changes: 21 additions & 0 deletions packages/browser/src/client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"lib": [
"dom",
"esnext",
"DOM.Iterable"
],
"types": ["vite/client"],
"noEmit": true
},
"include": [
"./**/*.ts",
"../types.ts",
"../../matchers.d.ts",
"../../../vitest/src/integrations/chai/chai-subset.d.ts"
],
"exclude": [
"./vite.config.ts"
]
}
2 changes: 1 addition & 1 deletion packages/browser/src/client/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function ensureAwaited<T>(promise: (error?: Error) => Promise<T>): Promis
export interface BrowserRunnerState {
files: string[]
runningFiles: string[]
moduleCache: WorkerGlobalState['moduleCache']
moduleCache: Map<string, any>
config: SerializedConfig
provider: string
runner: VitestRunner
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/node/cdp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { CDPSession } from 'vitest/node'
import type { WebSocketBrowserRPC } from './types'
import type { WebSocketBrowserRPC } from '../types'

export class BrowserServerCDPHandler {
private listenerIds: Record<string, string[]> = {}
Expand Down
2 changes: 1 addition & 1 deletion packages/browser/src/node/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ export default (parentServer: ParentBrowserProject, base = '/'): Plugin[] => {
}
const s = new MagicString(code, { filename })
s.prepend(
`import.meta.vitest = __vitest_index__;\n`,
`Object.defineProperty(import.meta, 'vitest', { get() { return typeof __vitest_worker__ !== 'undefined' && __vitest_worker__.filepath === "${filename.replace(/"/g, '\\"')}" ? __vitest_index__ : undefined } });\n`,
)
return {
code: s.toString(),
Expand Down
Loading