Skip to content

Commit 67b744e

Browse files
authored
Merge branch 'main' into fix-9642-merge-reports-reporter-errors
2 parents 56dc266 + 001e458 commit 67b744e

29 files changed

Lines changed: 630 additions & 527 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ jobs:
104104
with:
105105
node-version: ${{ matrix.node_version }}
106106

107-
- uses: browser-actions/setup-chrome@b94431e051d1c52dcbe9a7092a4f10f827795416 # v2.1.0
107+
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
108108

109109
- name: Install
110110
run: pnpm i
@@ -150,7 +150,7 @@ jobs:
150150
with:
151151
node-version: ${{ matrix.node_version }}
152152

153-
- uses: browser-actions/setup-chrome@b94431e051d1c52dcbe9a7092a4f10f827795416 # v2.1.0
153+
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
154154

155155
- name: Install
156156
run: pnpm i
@@ -186,8 +186,8 @@ jobs:
186186
with:
187187
node-version: ${{ matrix.node_version }}
188188

189-
- uses: browser-actions/setup-chrome@b94431e051d1c52dcbe9a7092a4f10f827795416 # v2.1.0
190-
- uses: browser-actions/setup-firefox@5914774dda97099441f02628f8d46411fcfbd208 # v1.7.0
189+
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
190+
- uses: browser-actions/setup-firefox@fcf821c621167805dd63a29662bd7cb5676c81a8 # v1.7.1
191191

192192
- name: Install
193193
run: pnpm i
@@ -219,7 +219,7 @@ jobs:
219219
with:
220220
node-version: 22
221221

222-
- uses: browser-actions/setup-chrome@b94431e051d1c52dcbe9a7092a4f10f827795416 # v2.1.0
222+
- uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
223223

224224
- name: Install
225225
run: |

docs/guide/browser/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ Community packages are available for other frameworks:
402402

403403
- [`vitest-browser-lit`](https://github.com/EskiMojo14/vitest-browser-lit) to render [lit](https://lit.dev) components
404404
- [`vitest-browser-preact`](https://github.com/JoviDeCroock/vitest-browser-preact) to render [preact](https://preactjs.com) components
405-
- [`vitest-browser-qwik`](https://github.com/kunai-consulting/vitest-browser-qwik) to render [qwik](https://qwik.dev) components
405+
- [`vitest-browser-qwik`](https://github.com/QwikDev/vitest-browser-qwik) to render [qwik](https://qwik.dev) components
406406

407407
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-`.
408408

docs/guide/migration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@ outline: deep
55

66
# Migration Guide
77

8+
[Migrating to Vitest 3.0](https://v3.vitest.dev/guide/migration) | [Migrating to Vitest 2.0](https://v2.vitest.dev/guide/migration)
9+
810
## Migrating to Vitest 4.0 {#vitest-4}
911

12+
::: warning Prerequisites
13+
Vitest 4.0 requires **Vite >= 6.0.0** and **Node.js >= 20.0.0**. Before proceeding
14+
with any other migration steps, ensure your environment meets these requirements.
15+
Running Vitest 4.0 on older versions of Vite or Node.js is not supported and may
16+
result in unexpected errors.
17+
:::
18+
1019
### V8 Code Coverage Major Changes
1120

1221
Vitest's V8 code coverage provider is now using more accurate coverage result remapping logic.

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"@vite-pwa/assets-generator": "^1.0.2",
2828
"@vite-pwa/vitepress": "^1.1.0",
2929
"@vitejs/plugin-vue": "catalog:",
30-
"@voidzero-dev/vitepress-theme": "^4.5.0",
30+
"@voidzero-dev/vitepress-theme": "^4.5.1",
3131
"https-localhost": "^4.7.1",
3232
"tinyglobby": "catalog:",
3333
"unocss": "catalog:",

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": "module",
44
"version": "4.1.0-beta.4",
55
"private": true,
6-
"packageManager": "pnpm@10.30.1",
6+
"packageManager": "pnpm@10.30.2",
77
"description": "Next generation testing framework powered by Vite",
88
"engines": {
99
"node": "^20.0.0 || ^22.0.0 || >=24.0.0"
@@ -58,7 +58,7 @@
5858
"magic-string": "^0.30.21",
5959
"pathe": "^2.0.3",
6060
"premove": "^4.0.0",
61-
"rollup": "^4.57.1",
61+
"rollup": "^4.59.0",
6262
"rollup-plugin-dts": "^6.3.0",
6363
"rollup-plugin-license": "^3.7.0",
6464
"tinyglobby": "catalog:",

packages/browser/src/client/tester/context.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent
5151
setup() {
5252
return createUserEvent()
5353
},
54-
async cleanup() {
54+
cleanup() {
5555
// avoid cleanup rpc call if there is nothing to cleanup
5656
if (!keyboard.unreleased.length) {
57-
return
57+
return Promise.resolve()
5858
}
5959
return ensureAwaited(async (error) => {
6060
await triggerCommand('__vitest_cleanup', [keyboard], error)
@@ -100,7 +100,7 @@ export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent
100100
},
101101

102102
// testing-library user-event
103-
async type(element, text, options) {
103+
type(element, text, options) {
104104
return ensureAwaited(async (error) => {
105105
const selector = await convertToSelector(element, options)
106106
const { unreleased } = await triggerCommand<{ unreleased: string[] }>(
@@ -118,7 +118,7 @@ export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent
118118
tab(options = {}) {
119119
return ensureAwaited(error => triggerCommand('__vitest_tab', [options], error))
120120
},
121-
async keyboard(text) {
121+
keyboard(text) {
122122
return ensureAwaited(async (error) => {
123123
const { unreleased } = await triggerCommand<{ unreleased: string[] }>(
124124
'__vitest_keyboard',
@@ -128,14 +128,14 @@ export function createUserEvent(__tl_user_event_base__?: TestingLibraryUserEvent
128128
keyboard.unreleased = unreleased
129129
})
130130
},
131-
async copy() {
132-
await userEvent.keyboard(`{${modifier}>}{c}{/${modifier}}`)
131+
copy() {
132+
return userEvent.keyboard(`{${modifier}>}{c}{/${modifier}}`)
133133
},
134-
async cut() {
135-
await userEvent.keyboard(`{${modifier}>}{x}{/${modifier}}`)
134+
cut() {
135+
return userEvent.keyboard(`{${modifier}>}{x}{/${modifier}}`)
136136
},
137-
async paste() {
138-
await userEvent.keyboard(`{${modifier}>}{v}{/${modifier}}`)
137+
paste() {
138+
return userEvent.keyboard(`{${modifier}>}{v}{/${modifier}}`)
139139
},
140140
}
141141
return userEvent

packages/browser/src/client/tester/locators/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export abstract class Locator {
135135
return this.triggerCommand<void>('__vitest_fill', this.selector, text, options)
136136
}
137137

138-
public async upload(files: string | string[] | File | File[], options?: UserEventUploadOptions): Promise<void> {
138+
public upload(files: string | string[] | File | File[], options?: UserEventUploadOptions): Promise<void> {
139139
return ensureAwaited(async (error) => {
140140
const filesPromise = (Array.isArray(files) ? files : [files]).map(async (file) => {
141141
if (typeof file === 'string') {

packages/browser/src/node/projectParent.ts

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import type {
1111
Vitest,
1212
} from 'vitest/node'
1313
import type { BrowserServerState } from './state'
14-
import { readFileSync } from 'node:fs'
1514
import { readFile } from 'node:fs/promises'
1615
import { parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map'
17-
import { dirname, join, resolve } from 'pathe'
16+
import { extractSourcemapFromFile } from '@vitest/utils/source-map/node'
17+
import { join, resolve } from 'pathe'
1818
import { BrowserServerCDPHandler } from './cdp'
1919
import builtinCommands from './commands/index'
2020
import { distRoot } from './constants'
@@ -61,19 +61,17 @@ export class ParentBrowserProject {
6161
if (this.sourceMapCache.has(id)) {
6262
return this.sourceMapCache.get(id)
6363
}
64+
6465
const result = this.vite.moduleGraph.getModuleById(id)?.transformResult
65-
// this can happen for bundled dependencies in node_modules/.vite
66+
// handle non-inline source map such as pre-bundled deps in node_modules/.vite
6667
if (result && !result.map) {
67-
const sourceMapUrl = this.retrieveSourceMapURL(result.code)
68-
if (!sourceMapUrl) {
69-
return null
70-
}
71-
const filepathDir = dirname(id)
72-
const sourceMapPath = resolve(filepathDir, sourceMapUrl)
73-
const map = JSON.parse(readFileSync(sourceMapPath, 'utf-8'))
74-
this.sourceMapCache.set(id, map)
75-
return map
68+
const filePath = id.split('?')[0]
69+
const extracted = extractSourcemapFromFile(result.code, filePath)
70+
this.sourceMapCache.set(id, extracted?.map)
71+
return extracted?.map
7672
}
73+
74+
this.sourceMapCache.set(id, result?.map)
7775
return result?.map
7876
},
7977
getUrlId: (id) => {
@@ -262,20 +260,4 @@ export class ParentBrowserProject {
262260
const decodedTestFile = decodeURIComponent(testFile)
263261
return { sessionId, testFile: decodedTestFile }
264262
}
265-
266-
private retrieveSourceMapURL(source: string): string | null {
267-
const re
268-
= /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm
269-
// Keep executing the search to find the *last* sourceMappingURL to avoid
270-
// picking up sourceMappingURLs from comments, strings, etc.
271-
let lastMatch, match
272-
// eslint-disable-next-line no-cond-assign
273-
while ((match = re.exec(source))) {
274-
lastMatch = match
275-
}
276-
if (!lastMatch) {
277-
return null
278-
}
279-
return lastMatch[1]
280-
}
281263
}

packages/browser/src/node/rpc.ts

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import type { WebSocket } from 'ws'
66
import type { WebSocketBrowserEvents, WebSocketBrowserHandlers } from '../types'
77
import type { ParentBrowserProject } from './projectParent'
88
import type { BrowserServerState } from './state'
9-
import { existsSync, promises as fs, readFileSync } from 'node:fs'
9+
import { existsSync, promises as fs } from 'node:fs'
1010
import { AutomockedModule, AutospiedModule, ManualMockedModule, RedirectedModule } from '@vitest/mocker'
1111
import { ServerMockResolver } from '@vitest/mocker/node'
12+
import { extractSourcemapFromFile } from '@vitest/utils/source-map/node'
1213
import { createBirpc } from 'birpc'
1314
import { parse, stringify } from 'flatted'
14-
import { dirname, join, resolve } from 'pathe'
15+
import { dirname, join } from 'pathe'
1516
import { createDebugger, isFileLoadingAllowed, isValidApiRequest } from 'vitest/node'
1617
import { WebSocketServer } from 'ws'
1718

@@ -244,21 +245,11 @@ export function setupBrowserRpc(globalServer: ParentBrowserProject, defaultMocke
244245
getBrowserFileSourceMap(id) {
245246
const mod = globalServer.vite.moduleGraph.getModuleById(id)
246247
const result = mod?.transformResult
247-
// this can happen for bundled dependencies in node_modules/.vite
248+
// handle non-inline source map such as pre-bundled deps in node_modules/.vite
248249
if (result && !result.map) {
249-
const sourceMapUrl = retrieveSourceMapURL(result.code)
250-
if (!sourceMapUrl) {
251-
return null
252-
}
253-
const filepathDir = dirname(id)
254-
const sourceMapPath = resolve(filepathDir, sourceMapUrl)
255-
try {
256-
const map = JSON.parse(readFileSync(sourceMapPath, 'utf-8'))
257-
return map
258-
}
259-
catch {
260-
return null
261-
}
250+
const filePath = id.split('?')[0]
251+
const extracted = extractSourcemapFromFile(result.code, filePath)
252+
return extracted?.map
262253
}
263254
return result?.map
264255
},
@@ -410,21 +401,6 @@ export function setupBrowserRpc(globalServer: ParentBrowserProject, defaultMocke
410401
}
411402
}
412403

413-
function retrieveSourceMapURL(source: string): string | null {
414-
const re = /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm
415-
// keep executing the search to find the *last* sourceMappingURL to avoid
416-
// picking up sourceMappingURLs from comments, strings, etc.
417-
let lastMatch, match
418-
// eslint-disable-next-line no-cond-assign
419-
while ((match = re.exec(source))) {
420-
lastMatch = match
421-
}
422-
if (!lastMatch) {
423-
return null
424-
}
425-
return lastMatch[1]
426-
}
427-
428404
// Serialization support utils.
429405
function cloneByOwnProperties(value: any) {
430406
// Clones the value's properties into a new Object. The simpler approach of

packages/runner/src/fixture.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export type UserFixtures = Record<string, unknown>
1818
export type FixtureRegistrations = Map<string, TestFixtureItem>
1919

2020
export class TestFixtures {
21-
private _suiteContexts: WeakMap<Suite | symbol, /* context object */ Record<string, unknown>>
21+
private _suiteContexts: WeakMap<Suite | { type: 'worker' }, /* context object */ Record<string, unknown>>
2222
private _overrides = new WeakMap<Suite, FixtureRegistrations>()
2323
private _registrations: FixtureRegistrations
2424

@@ -34,7 +34,7 @@ export class TestFixtures {
3434

3535
private static _fixtureOptionKeys: string[] = ['auto', 'injected', 'scope']
3636
private static _fixtureScopes: string[] = ['test', 'file', 'worker']
37-
private static _workerContextSymbol = Symbol('workerContext')
37+
private static _workerContextSuite = { type: 'worker' } as const
3838

3939
static clearDefinitions(): void {
4040
TestFixtures._definitions.length = 0
@@ -103,10 +103,10 @@ export class TestFixtures {
103103
}
104104

105105
getWorkerContext(): Record<string, any> {
106-
if (!this._suiteContexts.has(TestFixtures._workerContextSymbol)) {
107-
this._suiteContexts.set(TestFixtures._workerContextSymbol, Object.create(null))
106+
if (!this._suiteContexts.has(TestFixtures._workerContextSuite)) {
107+
this._suiteContexts.set(TestFixtures._workerContextSuite, Object.create(null))
108108
}
109-
return this._suiteContexts.get(TestFixtures._workerContextSymbol)!
109+
return this._suiteContexts.get(TestFixtures._workerContextSuite)!
110110
}
111111

112112
private parseUserFixtures(

0 commit comments

Comments
 (0)