From 3309d21048b14b9eefe4d2536448fca294bec42e Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 17 Apr 2026 08:10:25 +0200 Subject: [PATCH 1/4] fix: don't filter tests when running in a folder --- packages/extension/src/runner.ts | 49 ++++++++++++++++++++++++++------ packages/shared/src/index.ts | 10 ++++++- packages/worker/src/runner.ts | 7 +++-- packages/worker/src/watcher.ts | 23 ++++++++++----- 4 files changed, 70 insertions(+), 19 deletions(-) diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 15e63fcc..3d5d7789 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -14,7 +14,7 @@ import * as vscode from 'vscode' import { getConfig } from './config' import { coverageContext } from './coverage' import { log } from './log' -import { getTestData, TestCase, TestFile, TestFolder } from './testTreeData' +import { getTestData, TestCase, TestFile, TestFolder, TestSuite } from './testTreeData' import { getErrorMessage, showVitestError } from './utils' export class TestRunner extends vscode.Disposable { @@ -376,7 +376,7 @@ export class ContinuousTestRunner extends TestRunner { log.info('[RUNNER]', 'Watching all test files') } else { const files = getTestFiles(include) - const testNamePatern = formatTestPattern(include) + const testNamePatern = formatContinuousTestPattern(include) await this.handle.rpc.watchTests(files, testNamePatern) log.info( '[RUNNER]', @@ -600,7 +600,7 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe ] } const testSpecs: ExtensionTestSpecification[] = [] - const testFiles = new Set() + const testFiles = new Map() for (const test of tests) { const fsPath = normalize(test.uri!.fsPath) const data = getTestData(test) @@ -608,19 +608,38 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe if (data instanceof TestFolder) continue const project = data instanceof TestFile ? data.project : data.file.project const key = `${project}\0${fsPath}` - if (testFiles.has(key)) continue - testFiles.add(key) - testSpecs.push([project, fsPath]) + if (testFiles.has(key)) { + const specification = testFiles.get(key)! + if (data instanceof TestCase || data instanceof TestSuite) { + const options = specification[2]! + if (options.testNamePattern === '.+') { + options.testNamePattern = data.getTestNamePattern() + } else { + options.testNamePattern += `|${data.getTestNamePattern()}` + } + } + continue + } + const specification: ExtensionTestSpecification = [ + project, + fsPath, + { + // run every test by default + testNamePattern: data instanceof TestFile ? '.+' : data.getTestNamePattern(), + }, + ] + testFiles.set(key, specification) + testSpecs.push(specification) } return testSpecs } -function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] = []) { +function formatContinuousTestPattern(tests: readonly vscode.TestItem[], patterns: string[] = []) { for (const test of tests) { const data = getTestData(test)! // file or a folder, try to include every test in there if (!('getTestNamePattern' in data)) { - formatTestPattern( + formatContinuousTestPattern( Array.from(test.children, (t) => t[1]), patterns, ) @@ -632,11 +651,23 @@ function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] return patterns.join('|') } +function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] = []) { + for (const test of tests) { + const data = getTestData(test)! + if (!('getTestNamePattern' in data)) { + return + } + patterns.push(data.getTestNamePattern()) + } + if (!patterns.length) return undefined + return patterns.join('|') +} + function formatTestOutput(output: string) { return output.replace(/(?' + if (!items?.length) return '' return items.map((p) => `"${p.label}"`).join(', ') } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index d23e0611..63dd8c6c 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -11,7 +11,15 @@ export { normalizeDriveLetter, } from './utils' -export type ExtensionTestSpecification = [project: string, file: string] +export type ExtensionTestSpecificationOptions = { + testNamePattern?: string +} + +export type ExtensionTestSpecification = [ + project: string, + file: string, + options?: ExtensionTestSpecificationOptions, +] export interface ExtensionTestFileMetadata { project: string diff --git a/packages/worker/src/runner.ts b/packages/worker/src/runner.ts index 35c5e39f..0b85716f 100644 --- a/packages/worker/src/runner.ts +++ b/packages/worker/src/runner.ts @@ -106,9 +106,12 @@ export class ExtensionWorkerRunner { ): Promise { const specifications: TestSpecification[] = [] files.forEach((file) => { - const [projectName, filepath] = file + const [projectName, filepath, options] = file const project = this.vitest.getProjectByName(projectName) - specifications.push(project.createSpecification(filepath)) + const specification = project.createSpecification(filepath) + // @ts-expect-error testNamePattern is readonly and supported only in v4.1 + specification.testNamePattern = options?.testNamePattern + specifications.push(specification) }) return specifications } diff --git a/packages/worker/src/watcher.ts b/packages/worker/src/watcher.ts index 6a4e6b1c..3276a69c 100644 --- a/packages/worker/src/watcher.ts +++ b/packages/worker/src/watcher.ts @@ -1,4 +1,7 @@ -import type { ExtensionTestSpecification } from 'vitest-vscode-shared' +import type { + ExtensionTestSpecification, + ExtensionTestSpecificationOptions, +} from 'vitest-vscode-shared' import type { TestSpecification, Vitest } from 'vitest/node' import type { ExtensionWorkerRunner } from './runner' import { createQueuedHandler } from 'vitest-vscode-shared' @@ -6,7 +9,7 @@ import { createQueuedHandler } from 'vitest-vscode-shared' export class ExtensionWorkerWatcher { private enabled = false private trackingEveryFile = false - private trackedTestItems: Record = {} + private trackedTestItems: Record> = {} private trackedDirectories: string[] = [] constructor( @@ -42,11 +45,17 @@ export class ExtensionWorkerWatcher { const project = specification.project.name const files = this.trackedTestItems[project] - if (!files?.length) { + if (!files?.size) { return false } - return files.includes(specification.moduleId) + const options = files.get(specification.moduleId) + if (options) { + // @ts-expect-error testNamePattern is readonly and available only in v4.1 + specification.testNamePattern = options.testNamePattern + return true + } + return false } trackTestItems(filesOrDirectories: ExtensionTestSpecification[] | string[]) { @@ -54,11 +63,11 @@ export class ExtensionWorkerWatcher { if (typeof filesOrDirectories[0] === 'string') { this.trackedDirectories = filesOrDirectories as string[] } else { - for (const [project, file] of filesOrDirectories) { + for (const [project, file, options] of filesOrDirectories as ExtensionTestSpecification[]) { if (!this.trackedTestItems[project]) { - this.trackedTestItems[project] = [] + this.trackedTestItems[project] = new Map() } - this.trackedTestItems[project].push(file) + this.trackedTestItems[project].set(file, options || {}) } } } From 9a0d908b96abab3e543a4da232dd2f2fbb519e4c Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 17 Apr 2026 08:12:49 +0200 Subject: [PATCH 2/4] chore: log project/pattern --- packages/extension/src/runner.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 3d5d7789..ae9bdac3 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -212,7 +212,16 @@ export class TestRunner extends vscode.Disposable { else log.info( `Running ${files.length} file(s):`, - files.map((f) => this.relative(f)), + files.map((f) => { + if (typeof f === 'string') + return this.relative(f) + const parts = [this.relative(f)] + if (f[0]) + parts.push(`[${f[0]}]`) + if (f[2]?.testNamePattern) + parts.push(`(${f[2].testNamePattern})`) + return parts.join(' ') + }), ) await runTests(files, testNamePatern) } From 904d42d15c22c83b22b239c5150b072dd033404c Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 17 Apr 2026 08:14:44 +0200 Subject: [PATCH 3/4] chore: lint --- packages/worker-legacy/src/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/worker-legacy/src/worker.ts b/packages/worker-legacy/src/worker.ts index 98d5d0c4..87e22d12 100644 --- a/packages/worker-legacy/src/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -70,7 +70,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return (this.vitest as any).getCoreWorkspaceProject() } - public async collectTests(files: [projectName: string, filepath: string][]) { + public async collectTests(files: ExtensionTestSpecification[]) { const specifications: [project: WorkspaceProject, filepath: string][] = [] for (const [projectName, filepath] of files) { From 393885f6af8d6d9975fcc2460be3123b4789d0a7 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 17 Apr 2026 08:15:43 +0200 Subject: [PATCH 4/4] chore: lint --- packages/extension/src/runner.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index ae9bdac3..c49764af 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -213,13 +213,10 @@ export class TestRunner extends vscode.Disposable { log.info( `Running ${files.length} file(s):`, files.map((f) => { - if (typeof f === 'string') - return this.relative(f) + if (typeof f === 'string') return this.relative(f) const parts = [this.relative(f)] - if (f[0]) - parts.push(`[${f[0]}]`) - if (f[2]?.testNamePattern) - parts.push(`(${f[2].testNamePattern})`) + if (f[0]) parts.push(`[${f[0]}]`) + if (f[2]?.testNamePattern) parts.push(`(${f[2].testNamePattern})`) return parts.join(' ') }), )