diff --git a/README.md b/README.md index c81727a02711..68c196a86567 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - rolldown logo + Vitest logo
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index c5bbc2a3f67f..6ffb9ed8ea57 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -63,6 +63,15 @@ export default ({ mode }: { mode: string }) => { ['link', { rel: 'me', href: 'https://m.webtoo.ls/@vitest' }], ['link', { rel: 'mask-icon', href: '/logo.svg', color: '#ffffff' }], ['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' }], + [ + 'script', + { + 'src': 'https://cdn.usefathom.com/script.js', + 'data-site': 'BEAFAKYG', + 'data-spa': 'auto', + 'defer': '', + }, + ], ], lastUpdated: true, vite: { diff --git a/docs/.vitepress/scripts/transformHead.ts b/docs/.vitepress/scripts/transformHead.ts index ac0a014d21c1..63b48a294315 100644 --- a/docs/.vitepress/scripts/transformHead.ts +++ b/docs/.vitepress/scripts/transformHead.ts @@ -13,13 +13,6 @@ export async function transformHead({ pageData }: TransformContext): Promise3.2.0] +```ts import 'vitest' -interface CustomMatchers { - toBeFoo: () => R -} - declare module 'vitest' { - interface Matchers extends CustomMatchers {} -} -``` -```ts [3.0.0] -import 'vitest' - -interface CustomMatchers { - toBeFoo: () => R -} - -declare module 'vitest' { - interface Assertion extends CustomMatchers {} - interface AsymmetricMatchersContaining extends CustomMatchers {} + interface Matchers { + toBeFoo: () => R + } } ``` -::: ::: tip -Since Vitest 3.2, you can extend the `Matchers` interface to have type-safe assertions in `expect.extend`, `expect().*`, and `expect.*` methods at the same time. Previously, you had to define separate interfaces for each of them. +Importing `vitest` makes TypeScript think this is an ES module file, type declaration won't work without it. ::: +Extending the `Matchers` interface will add a type to `expect.extend`, `expect().*`, and `expect.*` methods at the same time. + ::: warning Don't forget to include the ambient declaration file in your `tsconfig.json`. ::: @@ -62,7 +48,7 @@ Don't forget to include the ambient declaration file in your `tsconfig.json`. The return value of a matcher should be compatible with the following interface: ```ts -interface ExpectationResult { +interface MatcherResult { pass: boolean message: () => string // If you pass these, they will automatically appear inside a diff when @@ -73,7 +59,7 @@ interface ExpectationResult { ``` ::: warning -If you create an asynchronous matcher, don't forget to `await` the result (`await expect('foo').toBeFoo()`) in the test itself:: +If you create an asynchronous matcher, don't forget to `await` the result (`await expect('foo').toBeFoo()`) in the test itself: ```ts expect.extend({ @@ -86,13 +72,46 @@ await expect().toBeAsyncAssertion() ``` ::: -The first argument inside a matcher's function is the received value (the one inside `expect(received)`). The rest are arguments passed directly to the matcher. +The first argument inside a matcher's function is the received value (the one inside `expect(received)`). The rest are arguments passed directly to the matcher. Since version 4.1, Vitest exposes several types that can be used by your custom matcher: + +```ts +import type { + // the function type + Matcher, + // the return value + MatcherResult, + // state available as `this` + MatcherState, +} from 'vitest' +import { expect } from 'vitest' + +// a simple matcher, using "function" to have access to "this" +const customMatcher: Matcher = function (received) { + // ... +} + +// a matcher with arguments +const customMatcher: Matcher = function (received, arg1, arg2) { + // ... +} + +// a matcher with custom annotations +function customMatcher(this: MatcherState, received: unknown, arg1: unknown, arg2: unknown): MatcherResult { + // ... + return { + pass: false, + message: () => 'something went wrong!', + } +} + +expect.extend({ customMatcher }) +``` Matcher function has access to `this` context with the following properties: ### `isNot` -Returns true, if matcher was called on `not` (`expect(received).not.toBeFoo()`). +Returns true, if matcher was called on `not` (`expect(received).not.toBeFoo()`). You do not need to respect it, Vitest will reverse the value of `pass` automatically. ### `promise` @@ -112,7 +131,7 @@ This contains a set of utility functions that you can use to display messages. Full name of the current test (including describe block). -### `task` 4.0.11 {#task} +### `task` 4.1.0 {#task} Contains a reference to [the `Test` runner task](/api/advanced/runner#tasks) when available. @@ -122,4 +141,16 @@ When using the global `expect` with concurrent tests, `this.task` is `undefined` ### `testPath` -Path to the current test. +File path to the current test. + +### `environment` + +The name of the current [`environment`](/config/environment) (for example, `jsdom`). + +### `soft` + +Was assertion called as a [`soft`](/api/expect#soft) one. You don't need to respect it, Vitest will always catch the error. + +::: tip +These are not all of the available properties, only the most useful ones. The other state values are used by Vitest internally. +::: diff --git a/docs/guide/ide.md b/docs/guide/ide.md index 0a9cb35dc3f6..eaa502346ef8 100644 --- a/docs/guide/ide.md +++ b/docs/guide/ide.md @@ -2,12 +2,17 @@ title: IDE Integrations | Guide --- + + # IDE Integrations ## VS Code Official {#vs-code}

- +

[GitHub](https://github.com/vitest-dev/vscode) | [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=vitest.explorer) @@ -19,7 +24,7 @@ title: IDE Integrations | Guide WebStorm, PhpStorm, IntelliJ IDEA Ultimate, and other JetBrains IDEs come with built-in support for Vitest.

- +

[WebStorm Help](https://www.jetbrains.com/help/webstorm/vitest.html) | [IntelliJ IDEA Ultimate Help](https://www.jetbrains.com/help/idea/vitest.html) | [PhpStorm Help](https://www.jetbrains.com/help/phpstorm/vitest.html) @@ -33,7 +38,7 @@ Created by [The Wallaby Team](https://wallabyjs.com) [Wallaby.js](https://wallabyjs.com) runs your Vitest tests immediately as you type, highlighting results in your IDE right next to your code.

- + Vitest + Wallaby logos

[VS Code](https://marketplace.visualstudio.com/items?itemName=WallabyJs.wallaby-vscode) | [JetBrains](https://plugins.jetbrains.com/plugin/15742-wallaby) | diff --git a/docs/package.json b/docs/package.json index 1071400c366f..741600cc1214 100644 --- a/docs/package.json +++ b/docs/package.json @@ -32,7 +32,7 @@ "tinyglobby": "catalog:", "unocss": "catalog:", "vite": "^6.3.5", - "vite-plugin-pwa": "^0.21.2", + "vite-plugin-pwa": "^1.2.0", "vitepress": "2.0.0-alpha.15", "vitepress-plugin-group-icons": "^1.6.5", "vitepress-plugin-llms": "^1.10.0", diff --git a/docs/public/ide/vitest-jb-dark.png b/docs/public/ide/vitest-jb-dark.png new file mode 100644 index 000000000000..340f7f06f4b9 Binary files /dev/null and b/docs/public/ide/vitest-jb-dark.png differ diff --git a/docs/public/ide/vitest-jb-light.png b/docs/public/ide/vitest-jb-light.png new file mode 100644 index 000000000000..d9dc2dadfe4f Binary files /dev/null and b/docs/public/ide/vitest-jb-light.png differ diff --git a/docs/public/ide/vitest-wallaby-dark.png b/docs/public/ide/vitest-wallaby-dark.png new file mode 100644 index 000000000000..7a0bdd799584 Binary files /dev/null and b/docs/public/ide/vitest-wallaby-dark.png differ diff --git a/docs/public/ide/vitest-wallaby-light.png b/docs/public/ide/vitest-wallaby-light.png new file mode 100644 index 000000000000..a4646a258c8f Binary files /dev/null and b/docs/public/ide/vitest-wallaby-light.png differ diff --git a/docs/public/logo-shadow.svg b/docs/public/logo-shadow.svg deleted file mode 100644 index e5b59bb8220d..000000000000 --- a/docs/public/logo-shadow.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/public/vite.svg b/docs/public/vite.svg deleted file mode 100644 index de4aeddc12bd..000000000000 --- a/docs/public/vite.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/docs/public/voidzero.svg b/docs/public/voidzero.svg deleted file mode 100644 index 1bdd69a30fa3..000000000000 --- a/docs/public/voidzero.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts index 4e9928d8751f..18f0bf577625 100644 --- a/packages/expect/src/types.ts +++ b/packages/expect/src/types.ts @@ -47,7 +47,13 @@ export interface MatcherState { customTesters: Array assertionCalls: number currentTestName?: string + /** + * @deprecated exists only in types + */ dontThrow?: () => void + /** + * @deprecated exists only in types + */ error?: Error equals: ( a: unknown, @@ -55,15 +61,19 @@ export interface MatcherState { customTesters?: Array, strictCheck?: boolean, ) => boolean + /** + * @deprecated exists only in types + */ expand?: boolean expectedAssertionsNumber?: number | null expectedAssertionsNumberErrorGen?: (() => Error) | null isExpectingAssertions?: boolean isExpectingAssertionsError?: Error | null isNot: boolean - // environment: VitestEnvironment promise: string - // snapshotState: SnapshotState + /** + * @deprecated exists only in types + */ suppressedErrors: Array testPath?: string utils: ReturnType & { diff --git a/packages/vitest/src/node/ast-collect.ts b/packages/vitest/src/node/ast-collect.ts index 31b37793ff92..4914aa3c5d1e 100644 --- a/packages/vitest/src/node/ast-collect.ts +++ b/packages/vitest/src/node/ast-collect.ts @@ -338,6 +338,7 @@ function createFileTask( file, tasks: [], mode, + each: definition.dynamic, name: definition.name, fullName: createTaskName([latestSuite.fullName, definition.name]), fullTestName: createTaskName([latestSuite.fullTestName, definition.name]), @@ -357,6 +358,7 @@ function createFileTask( id: '', suite: latestSuite, file, + each: definition.dynamic, mode, context: {} as any, // not used on the server name: definition.name, diff --git a/packages/vitest/src/public/index.ts b/packages/vitest/src/public/index.ts index 6ee9bdbdab38..4becb8f98761 100644 --- a/packages/vitest/src/public/index.ts +++ b/packages/vitest/src/public/index.ts @@ -92,7 +92,10 @@ export type { ExpectPollOptions, ExpectStatic, JestAssertion, + RawMatcherFn as Matcher, + ExpectationResult as MatcherResult, Matchers, + MatcherState, } from '@vitest/expect' export { afterAll, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b9c58f981f6..fb3b1918ccde 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -284,7 +284,7 @@ importers: version: 1.0.2 '@vite-pwa/vitepress': specifier: ^1.1.0 - version: 1.1.0(@vite-pwa/assets-generator@1.0.2)(vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0)) + version: 1.1.0(@vite-pwa/assets-generator@1.0.2)(vite-plugin-pwa@1.2.0(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0)) '@vitejs/plugin-vue': specifier: 'catalog:' version: 6.0.3(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3)) @@ -304,8 +304,8 @@ importers: specifier: 7.1.5 version: 7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) vite-plugin-pwa: - specifier: ^0.21.2 - version: 0.21.2(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) + specifier: ^1.2.0 + version: 1.2.0(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) vitepress: specifier: 2.0.0-alpha.15 version: 2.0.0-alpha.15(@types/node@24.10.7)(axios@1.13.2)(change-case@5.4.4)(jiti@2.6.1)(lightningcss@1.30.2)(oxc-minify@0.108.0)(postcss@8.5.6)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) @@ -5819,15 +5819,6 @@ packages: supports-color: optional: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -9390,14 +9381,14 @@ packages: vue-router: optional: true - vite-plugin-pwa@0.21.2: - resolution: {integrity: sha512-vFhH6Waw8itNu37hWUJxL50q+CBbNcMVzsKaYHQVrfxTt3ihk3PeLO22SbiP1UNWzcEPaTQv+YVxe4G0KOjAkg==} + vite-plugin-pwa@1.2.0: + resolution: {integrity: sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw==} engines: {node: '>=16.0.0'} peerDependencies: - '@vite-pwa/assets-generator': ^0.2.6 + '@vite-pwa/assets-generator': ^1.0.0 vite: 7.1.5 - workbox-build: ^7.3.0 - workbox-window: ^7.3.0 + workbox-build: ^7.4.0 + workbox-window: ^7.4.0 peerDependenciesMeta: '@vite-pwa/assets-generator': optional: true @@ -12869,9 +12860,9 @@ snapshots: sharp-ico: 0.1.5 unconfig: 7.3.3 - '@vite-pwa/vitepress@1.1.0(@vite-pwa/assets-generator@1.0.2)(vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0))': + '@vite-pwa/vitepress@1.1.0(@vite-pwa/assets-generator@1.0.2)(vite-plugin-pwa@1.2.0(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0))': dependencies: - vite-plugin-pwa: 0.21.2(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) + vite-plugin-pwa: 1.2.0(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) optionalDependencies: '@vite-pwa/assets-generator': 1.0.2 @@ -13955,10 +13946,6 @@ snapshots: dependencies: ms: 2.0.0 - debug@4.4.1: - dependencies: - ms: 2.1.3 - debug@4.4.3: dependencies: ms: 2.1.3 @@ -18195,9 +18182,9 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): + vite-plugin-pwa@1.2.0(@vite-pwa/assets-generator@1.0.2)(vite@7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.1.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): dependencies: - debug: 4.4.1 + debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.15 vite: 7.1.5(@types/node@24.10.7)(jiti@2.6.1)(lightningcss@1.30.2)(sass-embedded@1.97.2)(sass@1.97.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) diff --git a/test/cli/test/static-collect.test.ts b/test/cli/test/static-collect.test.ts index b804356583c6..859bd9318e7b 100644 --- a/test/cli/test/static-collect.test.ts +++ b/test/cli/test/static-collect.test.ts @@ -1,6 +1,6 @@ import type { CliOptions, TestCase, TestModule, TestSuite } from 'vitest/node' import { expect, test } from 'vitest' -import { createVitest } from 'vitest/node' +import { createVitest, rolldownVersion } from 'vitest/node' test('correctly collects a simple test', async () => { const testModule = await collectTests(` @@ -575,6 +575,7 @@ test('collects tests with each modifier', async () => { "each tests": { "barTest with each %i": { "dynamic": true, + "each": true, "errors": [], "fullName": "each tests > barTest with each %i", "id": "-1732721377_0_2-dynamic", @@ -584,6 +585,7 @@ test('collects tests with each modifier', async () => { }, "test with each %i": { "dynamic": true, + "each": true, "errors": [], "fullName": "each tests > test with each %i", "id": "-1732721377_0_0-dynamic", @@ -593,6 +595,7 @@ test('collects tests with each modifier', async () => { }, "testFoo with each %i": { "dynamic": true, + "each": true, "errors": [], "fullName": "each tests > testFoo with each %i", "id": "-1732721377_0_1-dynamic", @@ -812,15 +815,22 @@ function testTree(module: TestModule | TestSuite, tree: any = {}) { } function testItem(testCase: TestCase) { + let location: string | undefined + if (testCase.location) { + // rolldown's column is moved by 1 when using test.each/test.for + const column = rolldownVersion && testCase.options.each + ? testCase.location.column - 1 + : testCase.location.column + location = `${testCase.location.line}:${column}` + } return { id: testCase.id, - location: testCase.location - ? `${testCase.location.line}:${testCase.location.column}` - : undefined, + location, mode: testCase.options.mode, fullName: testCase.fullName, state: testCase.result().state, errors: testCase.result().errors || [], ...(testCase.task.dynamic ? { dynamic: true } : {}), + ...(testCase.options.each ? { each: true } : {}), } }