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
57 changes: 47 additions & 10 deletions packages/extension/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -212,7 +212,13 @@ 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)
}
Expand Down Expand Up @@ -376,7 +382,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]',
Expand Down Expand Up @@ -600,27 +606,46 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe
]
}
const testSpecs: ExtensionTestSpecification[] = []
const testFiles = new Set<string>()
const testFiles = new Map<string, ExtensionTestSpecification>()
for (const test of tests) {
const fsPath = normalize(test.uri!.fsPath)
const data = getTestData(test)
// just to type guard, actually not possible to have
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,
)
Expand All @@ -632,11 +657,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(/(?<!\r)\n/g, '\r\n')
}

function labelTestItems(items: readonly vscode.TestItem[] | undefined) {
if (!items) return '<all tests>'
if (!items?.length) return '<all tests>'
return items.map((p) => `"${p.label}"`).join(', ')
}
10 changes: 9 additions & 1 deletion packages/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/worker-legacy/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
7 changes: 5 additions & 2 deletions packages/worker/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,12 @@ export class ExtensionWorkerRunner {
): Promise<TestSpecification[]> {
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
}
Expand Down
23 changes: 16 additions & 7 deletions packages/worker/src/watcher.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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'

export class ExtensionWorkerWatcher {
private enabled = false
private trackingEveryFile = false
private trackedTestItems: Record<string, string[]> = {}
private trackedTestItems: Record<string, Map<string, ExtensionTestSpecificationOptions>> = {}
private trackedDirectories: string[] = []

constructor(
Expand Down Expand Up @@ -42,23 +45,29 @@ 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[]) {
this.enabled = true
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 || {})
}
}
}
Expand Down
Loading