Skip to content

Commit b165b7a

Browse files
authored
feat: support Vitest 4 (#650)
1 parent 6e34fcd commit b165b7a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+2186
-592
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ jobs:
3737
- name: test-e2e
3838
run: pnpm test-e2e --retry 2
3939

40+
- name: vitest 3.2
41+
run: node ./scripts/lower-vitest-version.js
42+
- run: pnpm install --no-frozen-lockfile
43+
44+
- name: test-e2e-legacy
45+
run: pnpm test-e2e --retry 2
46+
4047
- uses: actions/upload-artifact@v4
4148
if: always()
4249
with:

.vscode/launch.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@
1515
],
1616
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
1717
},
18+
{
19+
"name": "Run Extension Basic V4 Sample",
20+
"type": "extensionHost",
21+
"request": "launch",
22+
"args": [
23+
"--extensionDevelopmentPath=${workspaceFolder}",
24+
"${workspaceFolder}/samples/basic-v4"
25+
],
26+
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
27+
},
1828
{
1929
"name": "Run Extension Browser Sample",
2030
"type": "extensionHost",

eslint.config.mjs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,6 @@ export default antfu(
5252
'antfu/no-import-dist': 'off',
5353
},
5454
},
55-
{
56-
files: [`packages/${GLOB_SRC}`],
57-
rules: {
58-
'no-restricted-imports': [
59-
'error',
60-
{
61-
paths: ['vitest', 'path'],
62-
},
63-
],
64-
},
65-
},
6655
{
6756
// these files define vitest as peer dependency
6857
files: [`packages/{coverage-*,ui,browser,web-worker}/${GLOB_SRC}`],

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,26 @@
313313
"which": "^4.0.0",
314314
"ws": "^8.16.0"
315315
},
316+
"pnpm": {
317+
"overrides": {
318+
"@vitest/browser": "^4.0.0-beta.8",
319+
"@vitest/coverage": "^4.0.0-beta.8",
320+
"@vitest/runner": "^4.0.0-beta.8",
321+
"vitest": "^4.0.0-beta.8",
322+
"vitest-vscode-extension>@vitest/browser": "^3.2.0",
323+
"vitest-vscode-extension>@vitest/coverage": "^3.2.0",
324+
"vitest-vscode-extension>@vitest/runner": "^3.2.0",
325+
"vitest-vscode-extension>vitest": "^3.2.0",
326+
"vitest-vscode-shared>@vitest/browser": "^3.2.0",
327+
"vitest-vscode-shared>@vitest/coverage": "^3.2.0",
328+
"vitest-vscode-shared>@vitest/runner": "^3.2.0",
329+
"vitest-vscode-shared>vitest": "^3.2.0",
330+
"vitest-vscode-worker-legacy>@vitest/browser": "^3.2.0",
331+
"vitest-vscode-worker-legacy>@vitest/coverage": "^3.2.0",
332+
"vitest-vscode-worker-legacy>@vitest/runner": "^3.2.0",
333+
"vitest-vscode-worker-legacy>vitest": "^3.2.0"
334+
}
335+
},
316336
"lint-staged": {
317337
"*.{js,ts,tsx,vue,md}": [
318338
"eslint --fix"

packages/extension/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "vitest-vscode-extension",
3+
"version": "0.0.0",
4+
"private": true,
5+
"dependencies": {
6+
"vitest": "$vitest",
7+
"vitest-vscode-shared": "workspace:*"
8+
}
9+
}

src/api.ts renamed to packages/extension/src/api.ts

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { VitestPackage } from './api/pkg'
2-
import type { ExtensionWorkerEvents, SerializedTestSpecification, VitestRPC } from './api/rpc'
2+
import type { ExtensionWorkerEvents, VitestExtensionRPC } from './api/rpc'
33
import type { ExtensionWorkerProcess } from './api/types'
44
import { dirname, isAbsolute } from 'node:path'
55
import { normalize, relative } from 'pathe'
6+
import { createQueuedHandler, type ExtensionTestSpecification } from 'vitest-vscode-shared'
67
import * as vscode from 'vscode'
78
import { createVitestProcess } from './api/child_process'
89
import { createVitestTerminalProcess } from './api/terminal'
@@ -110,11 +111,11 @@ export class VitestFolderAPI {
110111
return this.pkg
111112
}
112113

113-
async runFiles(specs?: SerializedTestSpecification[] | string[], testNamePatern?: string) {
114+
async runFiles(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) {
114115
await this.meta.rpc.runTests(normalizeSpecs(specs), testNamePatern)
115116
}
116117

117-
async updateSnapshots(specs?: SerializedTestSpecification[] | string[], testNamePatern?: string) {
118+
async updateSnapshots(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) {
118119
await this.meta.rpc.updateSnapshots(normalizeSpecs(specs), testNamePatern)
119120
}
120121

@@ -197,7 +198,7 @@ export class VitestFolderAPI {
197198
await this.meta.rpc.disableCoverage()
198199
}
199200

200-
async watchTests(files?: SerializedTestSpecification[] | string[], testNamePattern?: string) {
201+
async watchTests(files?: ExtensionTestSpecification[] | string[], testNamePattern?: string) {
201202
await this.meta.rpc.watchTests(normalizeSpecs(files), testNamePattern)
202203
}
203204

@@ -207,10 +208,9 @@ export class VitestFolderAPI {
207208

208209
onConsoleLog = this.createHandler('onConsoleLog')
209210
onTaskUpdate = this.createHandler('onTaskUpdate')
210-
onFinished = this.createHandler('onFinished')
211+
onTestRunEnd = this.createHandler('onTestRunEnd')
212+
onTestRunStart = this.createHandler('onTestRunStart')
211213
onCollected = this.createHandler('onCollected')
212-
onWatcherStart = this.createHandler('onWatcherStart')
213-
onWatcherRerun = this.createHandler('onWatcherRerun')
214214

215215
clearListeners(name?: Exclude<keyof ResolvedMeta['handlers'], 'clearListeners' | 'removeListener'>) {
216216
if (name)
@@ -230,28 +230,6 @@ export class VitestFolderAPI {
230230
}
231231
}
232232

233-
function createQueuedHandler<T>(resolver: (value: T[]) => Promise<void>) {
234-
const cached = new Set<T>()
235-
let promise: Promise<void> | null = null
236-
let timer: NodeJS.Timeout | null = null
237-
return (value: T) => {
238-
cached.add(value)
239-
if (timer) {
240-
clearTimeout(timer)
241-
}
242-
timer = setTimeout(() => {
243-
if (promise) {
244-
return
245-
}
246-
const values = Array.from(cached)
247-
cached.clear()
248-
promise = resolver(values).finally(() => {
249-
promise = null
250-
})
251-
}, 50)
252-
}
253-
}
254-
255233
export async function resolveVitestAPI(workspaceConfigs: VitestPackage[], configs: VitestPackage[]) {
256234
const usedConfigs = new Set<string>()
257235
const workspacePromises = workspaceConfigs.map(pkg => createVitestFolderAPI(usedConfigs, pkg))
@@ -402,7 +380,7 @@ async function createVitestFolderAPI(usedConfigs: Set<string>, pkg: VitestPackag
402380
}
403381

404382
export interface ResolvedMeta {
405-
rpc: VitestRPC
383+
rpc: VitestExtensionRPC
406384
process: ExtensionWorkerProcess
407385
workspaceSource: string | false
408386
pkg: VitestPackage
@@ -411,23 +389,22 @@ export interface ResolvedMeta {
411389
onStdout: (listener: (log: string) => void) => void
412390
onConsoleLog: (listener: ExtensionWorkerEvents['onConsoleLog']) => void
413391
onTaskUpdate: (listener: ExtensionWorkerEvents['onTaskUpdate']) => void
414-
onFinished: (listener: ExtensionWorkerEvents['onFinished']) => void
392+
onTestRunEnd: (listener: ExtensionWorkerEvents['onTestRunEnd']) => void
393+
onTestRunStart: (listener: ExtensionWorkerEvents['onTestRunStart']) => void
415394
onCollected: (listener: ExtensionWorkerEvents['onCollected']) => void
416-
onWatcherStart: (listener: ExtensionWorkerEvents['onWatcherStart']) => void
417-
onWatcherRerun: (listener: ExtensionWorkerEvents['onWatcherRerun']) => void
418395
clearListeners: () => void
419396
removeListener: (name: string, listener: any) => void
420397
}
421398
}
422399

423-
function normalizeSpecs(specs?: string[] | SerializedTestSpecification[]) {
400+
function normalizeSpecs(specs?: string[] | ExtensionTestSpecification[]) {
424401
if (!specs) {
425402
return specs
426403
}
427404
return specs.map((spec) => {
428405
if (typeof spec === 'string') {
429406
return normalize(spec)
430407
}
431-
return [spec[0], normalize(spec[1])] as SerializedTestSpecification
432-
}) as string[] | SerializedTestSpecification[]
408+
return [spec[0], normalize(spec[1])] as ExtensionTestSpecification
409+
}) as string[] | ExtensionTestSpecification[]
433410
}
File renamed without changes.

src/api/resolve.ts renamed to packages/extension/src/api/resolve.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type * as vscode from 'vscode'
22
import { findUpSync } from 'find-up'
33
import { dirname, resolve } from 'pathe'
4+
import { normalizeDriveLetter } from 'vitest-vscode-shared'
45
import { getConfig } from '../config'
5-
import { normalizeDriveLetter } from '../worker/utils'
66

77
const _require = require
88

packages/extension/src/api/rpc.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from 'vitest-vscode-shared'
2+
import { stripVTControlCharacters } from 'node:util'
3+
import v8 from 'node:v8'
4+
import { createBirpc } from 'birpc'
5+
import { log } from '../log'
6+
7+
export type {
8+
ExtensionWorkerEvents,
9+
ExtensionWorkerTransport,
10+
VitestExtensionRPC,
11+
VitestWorkerRPC,
12+
} from 'vitest-vscode-shared'
13+
14+
function createHandler<T extends (...args: any) => any>() {
15+
const handlers: T[] = []
16+
return {
17+
handlers,
18+
register: (listener: any) => handlers.push(listener),
19+
trigger: (...data: any) => handlers.forEach(handler => handler(...data)),
20+
clear: () => handlers.length = 0,
21+
remove: (listener: T) => {
22+
const index = handlers.indexOf(listener)
23+
if (index !== -1)
24+
handlers.splice(index, 1)
25+
},
26+
}
27+
}
28+
29+
export function createRpcOptions() {
30+
const handlers = {
31+
onConsoleLog: createHandler<ExtensionWorkerEvents['onConsoleLog']>(),
32+
onTaskUpdate: createHandler<ExtensionWorkerEvents['onTaskUpdate']>(),
33+
onCollected: createHandler<ExtensionWorkerEvents['onCollected']>(),
34+
onTestRunStart: createHandler<ExtensionWorkerEvents['onTestRunStart']>(),
35+
onTestRunEnd: createHandler<ExtensionWorkerEvents['onTestRunEnd']>(),
36+
}
37+
38+
const events: Omit<ExtensionWorkerEvents, 'onReady' | 'onError'> = {
39+
onConsoleLog: handlers.onConsoleLog.trigger,
40+
onTestRunEnd: handlers.onTestRunEnd.trigger,
41+
onTaskUpdate: handlers.onTaskUpdate.trigger,
42+
onCollected: handlers.onCollected.trigger,
43+
onTestRunStart: handlers.onTestRunStart.trigger,
44+
onProcessLog(type, message) {
45+
log.worker(type === 'stderr' ? 'error' : 'info', stripVTControlCharacters(message))
46+
},
47+
}
48+
49+
return {
50+
events,
51+
handlers: {
52+
onConsoleLog: handlers.onConsoleLog.register,
53+
onTaskUpdate: handlers.onTaskUpdate.register,
54+
onTestRunEnd: handlers.onTestRunEnd.register,
55+
onCollected: handlers.onCollected.register,
56+
onTestRunStart: handlers.onTestRunStart.register,
57+
removeListener(name: string, listener: any) {
58+
handlers[name as 'onCollected']?.remove(listener)
59+
},
60+
clearListeners() {
61+
for (const name in handlers)
62+
handlers[name as 'onCollected']?.clear()
63+
},
64+
},
65+
}
66+
}
67+
68+
export function createVitestRpc(options: {
69+
on: (listener: (message: any) => void) => void
70+
send: (message: any) => void
71+
}) {
72+
const { events, handlers } = createRpcOptions()
73+
74+
const api = createBirpc<ExtensionWorkerTransport, ExtensionWorkerEvents>(
75+
events,
76+
{
77+
timeout: -1,
78+
bind: 'functions',
79+
on(listener) {
80+
options.on(listener)
81+
},
82+
post(message) {
83+
options.send(message)
84+
},
85+
serialize: v8.serialize,
86+
deserialize: v => v8.deserialize(Buffer.from(v) as any),
87+
},
88+
)
89+
90+
return {
91+
api,
92+
handlers,
93+
}
94+
}

0 commit comments

Comments
 (0)