import type { ExtensionTestFileSpecification, ExtensionTestSpecification, VitestWorkerRPC, WorkerWSEventEmitter, } from 'vitest-vscode-shared' import type { TestModule, TestSpecification, Vitest as VitestCore } from 'vitest/node' export class ExtensionWorkerRunner { private rpc!: VitestWorkerRPC constructor( public readonly vitest: VitestCore, private readonly debug = false, private emitter: WorkerWSEventEmitter, ) {} async getFiles(): Promise { this.vitest.clearSpecificationsCache() const specifications = await this.vitest.globTestSpecifications() return specifications.map((spec) => { const config = spec.project.config return [ spec.moduleId, { project: spec.project.name, pool: config.pool, browser: config.browser?.enabled ? { provider: config.browser.provider?.name || 'preview', name: config.browser.name, webRoot: config.root, } : undefined, }, ] }) } async collectTests(testFiles: ExtensionTestSpecification[]): Promise { const specifications = await this.resolveTestSpecifications(testFiles) await this.collectSpecifications(specifications) } public async collectSpecifications(specifications: TestSpecification[]): Promise { const testModules = await this.parseSpecifications(specifications) const promises = testModules.map((module) => // TODO: fix "as any" this.rpc.onCollected((module as any).task, true), ) await Promise.all(promises) } private parseSpecifications(specifications: TestSpecification[]): Promise { if (this.vitest.parseSpecifications) { return this.vitest.parseSpecifications(specifications) } return this.vitest.experimental_parseSpecifications(specifications) } initRpc(rpc: VitestWorkerRPC) { this.rpc = rpc } cancelRun(): Promise { return this.vitest.cancelCurrentRun('keyboard-input') } async runTests( filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string, ): Promise { const currentTestNamePattern = this.getGlobalTestNamePattern() if (testNamePattern) { this.vitest.setGlobalTestNamePattern(testNamePattern) } if (this.vitest.config.coverage.enabled) { await this.vitest.enableCoverage() } if (!filesOrDirectories || this.isOnlyDirectories(filesOrDirectories)) { const specifications = await this.vitest.getRelevantTestSpecifications( filesOrDirectories || [], ) await this.vitest.rerunTestSpecifications(specifications, true) } else { const specifications = await this.resolveTestSpecifications(filesOrDirectories) await this.vitest.rerunTestSpecifications(specifications, false) } // debugger never runs in watch mode if (this.debug) { await this.vitest.close() this.emitter.close() } if (currentTestNamePattern) { this.vitest.setGlobalTestNamePattern(currentTestNamePattern) } else { this.vitest.resetGlobalTestNamePattern() } } // TODO: use vitest.getGlobalTestNamePattern when merged private getGlobalTestNamePattern(): RegExp | undefined { if ((this.vitest as any).configOverride.testNamePattern != null) { return (this.vitest as any).configOverride.testNamePattern } return this.vitest.config.testNamePattern } async resolveTestSpecifications( files: ExtensionTestSpecification[], ): Promise { const specifications: TestSpecification[] = [] files.forEach((file) => { const [projectName, filepath, options] = file const project = this.vitest.getProjectByName(projectName) 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 } async updateSnapshots( filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string, ): Promise { const currentTestNamePattern = this.getGlobalTestNamePattern() this.vitest.enableSnapshotUpdate() try { return await this.runTests(filesOrDirectories, testNamePattern) } finally { if (currentTestNamePattern) { this.vitest.setGlobalTestNamePattern(currentTestNamePattern) } else { this.vitest.resetSnapshotUpdate() } } } private isOnlyDirectories( filesOrDirectories: ExtensionTestSpecification[] | string[], ): filesOrDirectories is string[] { return typeof filesOrDirectories[0] === 'string' } }