Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ jobs:
- name: test-e2e
run: pnpm test-e2e --retry 2

- name: vitest 3.2
run: node ./scripts/lower-vitest-version.js
- run: pnpm install --no-frozen-lockfile

- name: test-e2e-legacy
run: pnpm test-e2e --retry 2

- uses: actions/upload-artifact@v4
if: always()
with:
Expand Down
10 changes: 10 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
},
{
"name": "Run Extension Basic V4 Sample",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"${workspaceFolder}/samples/basic-v4"
],
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
},
{
"name": "Run Extension Browser Sample",
"type": "extensionHost",
Expand Down
11 changes: 0 additions & 11 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,6 @@ export default antfu(
'antfu/no-import-dist': 'off',
},
},
{
files: [`packages/${GLOB_SRC}`],
rules: {
'no-restricted-imports': [
'error',
{
paths: ['vitest', 'path'],
},
],
},
},
{
// these files define vitest as peer dependency
files: [`packages/{coverage-*,ui,browser,web-worker}/${GLOB_SRC}`],
Expand Down
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,26 @@
"which": "^4.0.0",
"ws": "^8.16.0"
},
"pnpm": {
"overrides": {
"@vitest/browser": "^4.0.0-beta.8",
"@vitest/coverage": "^4.0.0-beta.8",
"@vitest/runner": "^4.0.0-beta.8",
"vitest": "^4.0.0-beta.8",
"vitest-vscode-extension>@vitest/browser": "^3.2.0",
"vitest-vscode-extension>@vitest/coverage": "^3.2.0",
"vitest-vscode-extension>@vitest/runner": "^3.2.0",
"vitest-vscode-extension>vitest": "^3.2.0",
"vitest-vscode-shared>@vitest/browser": "^3.2.0",
"vitest-vscode-shared>@vitest/coverage": "^3.2.0",
"vitest-vscode-shared>@vitest/runner": "^3.2.0",
"vitest-vscode-shared>vitest": "^3.2.0",
"vitest-vscode-worker-legacy>@vitest/browser": "^3.2.0",
"vitest-vscode-worker-legacy>@vitest/coverage": "^3.2.0",
"vitest-vscode-worker-legacy>@vitest/runner": "^3.2.0",
"vitest-vscode-worker-legacy>vitest": "^3.2.0"
}
},
"lint-staged": {
"*.{js,ts,tsx,vue,md}": [
"eslint --fix"
Expand Down
9 changes: 9 additions & 0 deletions packages/extension/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "vitest-vscode-extension",
"version": "0.0.0",
"private": true,
"dependencies": {
"vitest": "$vitest",
"vitest-vscode-shared": "workspace:*"
}
}
49 changes: 13 additions & 36 deletions src/api.ts → packages/extension/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { VitestPackage } from './api/pkg'
import type { ExtensionWorkerEvents, SerializedTestSpecification, VitestRPC } from './api/rpc'
import type { ExtensionWorkerEvents, VitestExtensionRPC } from './api/rpc'
import type { ExtensionWorkerProcess } from './api/types'
import { dirname, isAbsolute } from 'node:path'
import { normalize, relative } from 'pathe'
import { createQueuedHandler, type ExtensionTestSpecification } from 'vitest-vscode-shared'
import * as vscode from 'vscode'
import { createVitestProcess } from './api/child_process'
import { createVitestTerminalProcess } from './api/terminal'
Expand Down Expand Up @@ -110,11 +111,11 @@ export class VitestFolderAPI {
return this.pkg
}

async runFiles(specs?: SerializedTestSpecification[] | string[], testNamePatern?: string) {
async runFiles(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) {
await this.meta.rpc.runTests(normalizeSpecs(specs), testNamePatern)
}

async updateSnapshots(specs?: SerializedTestSpecification[] | string[], testNamePatern?: string) {
async updateSnapshots(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) {
await this.meta.rpc.updateSnapshots(normalizeSpecs(specs), testNamePatern)
}

Expand Down Expand Up @@ -197,7 +198,7 @@ export class VitestFolderAPI {
await this.meta.rpc.disableCoverage()
}

async watchTests(files?: SerializedTestSpecification[] | string[], testNamePattern?: string) {
async watchTests(files?: ExtensionTestSpecification[] | string[], testNamePattern?: string) {
await this.meta.rpc.watchTests(normalizeSpecs(files), testNamePattern)
}

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

onConsoleLog = this.createHandler('onConsoleLog')
onTaskUpdate = this.createHandler('onTaskUpdate')
onFinished = this.createHandler('onFinished')
onTestRunEnd = this.createHandler('onTestRunEnd')
onTestRunStart = this.createHandler('onTestRunStart')
onCollected = this.createHandler('onCollected')
onWatcherStart = this.createHandler('onWatcherStart')
onWatcherRerun = this.createHandler('onWatcherRerun')

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

function createQueuedHandler<T>(resolver: (value: T[]) => Promise<void>) {
const cached = new Set<T>()
let promise: Promise<void> | null = null
let timer: NodeJS.Timeout | null = null
return (value: T) => {
cached.add(value)
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
if (promise) {
return
}
const values = Array.from(cached)
cached.clear()
promise = resolver(values).finally(() => {
promise = null
})
}, 50)
}
}

export async function resolveVitestAPI(workspaceConfigs: VitestPackage[], configs: VitestPackage[]) {
const usedConfigs = new Set<string>()
const workspacePromises = workspaceConfigs.map(pkg => createVitestFolderAPI(usedConfigs, pkg))
Expand Down Expand Up @@ -402,7 +380,7 @@ async function createVitestFolderAPI(usedConfigs: Set<string>, pkg: VitestPackag
}

export interface ResolvedMeta {
rpc: VitestRPC
rpc: VitestExtensionRPC
process: ExtensionWorkerProcess
workspaceSource: string | false
pkg: VitestPackage
Expand All @@ -411,23 +389,22 @@ export interface ResolvedMeta {
onStdout: (listener: (log: string) => void) => void
onConsoleLog: (listener: ExtensionWorkerEvents['onConsoleLog']) => void
onTaskUpdate: (listener: ExtensionWorkerEvents['onTaskUpdate']) => void
onFinished: (listener: ExtensionWorkerEvents['onFinished']) => void
onTestRunEnd: (listener: ExtensionWorkerEvents['onTestRunEnd']) => void
onTestRunStart: (listener: ExtensionWorkerEvents['onTestRunStart']) => void
onCollected: (listener: ExtensionWorkerEvents['onCollected']) => void
onWatcherStart: (listener: ExtensionWorkerEvents['onWatcherStart']) => void
onWatcherRerun: (listener: ExtensionWorkerEvents['onWatcherRerun']) => void
clearListeners: () => void
removeListener: (name: string, listener: any) => void
}
}

function normalizeSpecs(specs?: string[] | SerializedTestSpecification[]) {
function normalizeSpecs(specs?: string[] | ExtensionTestSpecification[]) {
if (!specs) {
return specs
}
return specs.map((spec) => {
if (typeof spec === 'string') {
return normalize(spec)
}
return [spec[0], normalize(spec[1])] as SerializedTestSpecification
}) as string[] | SerializedTestSpecification[]
return [spec[0], normalize(spec[1])] as ExtensionTestSpecification
}) as string[] | ExtensionTestSpecification[]
}
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type * as vscode from 'vscode'
import { findUpSync } from 'find-up'
import { dirname, resolve } from 'pathe'
import { normalizeDriveLetter } from 'vitest-vscode-shared'
import { getConfig } from '../config'
import { normalizeDriveLetter } from '../worker/utils'

const _require = require

Expand Down
94 changes: 94 additions & 0 deletions packages/extension/src/api/rpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from 'vitest-vscode-shared'
import { stripVTControlCharacters } from 'node:util'
import v8 from 'node:v8'
import { createBirpc } from 'birpc'
import { log } from '../log'

export type {
ExtensionWorkerEvents,
ExtensionWorkerTransport,
VitestExtensionRPC,
VitestWorkerRPC,
} from 'vitest-vscode-shared'

function createHandler<T extends (...args: any) => any>() {
const handlers: T[] = []
return {
handlers,
register: (listener: any) => handlers.push(listener),
trigger: (...data: any) => handlers.forEach(handler => handler(...data)),
clear: () => handlers.length = 0,
remove: (listener: T) => {
const index = handlers.indexOf(listener)
if (index !== -1)
handlers.splice(index, 1)
},
}
}

export function createRpcOptions() {
const handlers = {
onConsoleLog: createHandler<ExtensionWorkerEvents['onConsoleLog']>(),
onTaskUpdate: createHandler<ExtensionWorkerEvents['onTaskUpdate']>(),
onCollected: createHandler<ExtensionWorkerEvents['onCollected']>(),
onTestRunStart: createHandler<ExtensionWorkerEvents['onTestRunStart']>(),
onTestRunEnd: createHandler<ExtensionWorkerEvents['onTestRunEnd']>(),
}

const events: Omit<ExtensionWorkerEvents, 'onReady' | 'onError'> = {
onConsoleLog: handlers.onConsoleLog.trigger,
onTestRunEnd: handlers.onTestRunEnd.trigger,
onTaskUpdate: handlers.onTaskUpdate.trigger,
onCollected: handlers.onCollected.trigger,
onTestRunStart: handlers.onTestRunStart.trigger,
onProcessLog(type, message) {
log.worker(type === 'stderr' ? 'error' : 'info', stripVTControlCharacters(message))
},
}

return {
events,
handlers: {
onConsoleLog: handlers.onConsoleLog.register,
onTaskUpdate: handlers.onTaskUpdate.register,
onTestRunEnd: handlers.onTestRunEnd.register,
onCollected: handlers.onCollected.register,
onTestRunStart: handlers.onTestRunStart.register,
removeListener(name: string, listener: any) {
handlers[name as 'onCollected']?.remove(listener)
},
clearListeners() {
for (const name in handlers)
handlers[name as 'onCollected']?.clear()
},
},
}
}

export function createVitestRpc(options: {
on: (listener: (message: any) => void) => void
send: (message: any) => void
}) {
const { events, handlers } = createRpcOptions()

const api = createBirpc<ExtensionWorkerTransport, ExtensionWorkerEvents>(
events,
{
timeout: -1,
bind: 'functions',
on(listener) {
options.on(listener)
},
post(message) {
options.send(message)
},
serialize: v8.serialize,
deserialize: v => v8.deserialize(Buffer.from(v) as any),
},
)

return {
api,
handlers,
}
}
File renamed without changes.
File renamed without changes.
5 changes: 4 additions & 1 deletion src/api/ws.ts → packages/extension/src/api/ws.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { WorkerEvent, WorkerRunnerOptions } from 'vitest-vscode-shared'
import type { WebSocket, WebSocketServer } from 'ws'
import type { ResolvedMeta } from '../api'
import type { WorkerEvent, WorkerRunnerOptions } from '../worker/types'
import type { VitestPackage } from './pkg'
import { pathToFileURL } from 'node:url'
import { gte } from 'semver'
import { getConfig } from '../config'
import { finalCoverageFileName, setupFilePath } from '../constants'
import { log } from '../log'
import { createVitestRpc } from './rpc'

Expand Down Expand Up @@ -131,6 +132,8 @@ export function onWsConnection(
pnpLoader: pnpLoader && gte(process.version, '18.19.0')
? pathToFileURL(pnpLoader).toString()
: undefined,
setupFilePath,
finalCoverageFileName,
},
debug,
astCollect: getConfig(pkg.folder).experimentalStaticAstCollect,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading