diff --git a/packages/coverage-istanbul/src/provider.ts b/packages/coverage-istanbul/src/provider.ts index 480a54d47b775..84e4f77514eb0 100644 --- a/packages/coverage-istanbul/src/provider.ts +++ b/packages/coverage-istanbul/src/provider.ts @@ -386,6 +386,11 @@ export class IstanbulCoverageProvider resolve(this.ctx.config.root, file), ) + const viteNodeServers = [ + this.ctx.vitenode, + ...this.ctx.projects.map(project => project.vitenode), + ] + if (this.ctx.config.changed) { includedFiles = (this.ctx.config.related || []).filter(file => includedFiles.includes(file), @@ -396,16 +401,29 @@ export class IstanbulCoverageProvider .filter(file => !coveredFiles.includes(file)) .sort() - const cacheKey = new Date().getTime() const coverageMap = libCoverage.createCoverageMap({}) + // Make sure file is not served from cache so that instrumenter loads up requested file coverage + const cacheKey = new Date().getTime() + // Note that these cannot be run parallel as synchronous instrumenter.lastFileCoverage // returns the coverage of the last transformed file for (const [index, filename] of uncoveredFiles.entries()) { debug('Uncovered file %s %d/%d', filename, index, uncoveredFiles.length) - // Make sure file is not served from cache so that instrumenter loads up requested file coverage - await this.ctx.vitenode.transformRequest(`${filename}?v=${cacheKey}`) + for (const vitenode of viteNodeServers) { + try { + await vitenode.transformRequest(`${filename}?v=${cacheKey}`) + continue + } + catch (error) { + // Try transforming with next ViteNode server, unless last + if (viteNodeServers.indexOf(vitenode) === viteNodeServers.length - 1) { + throw error + } + } + } + const lastCoverage = this.instrumenter.lastFileCoverage() coverageMap.addFileCoverage(lastCoverage) } diff --git a/test/coverage-test/fixtures/configs/vitest.workspace.multi-transforms.ts b/test/coverage-test/fixtures/configs/vitest.workspace.multi-transforms.ts new file mode 100644 index 0000000000000..956a61258a685 --- /dev/null +++ b/test/coverage-test/fixtures/configs/vitest.workspace.multi-transforms.ts @@ -0,0 +1,48 @@ +import { Plugin, defineWorkspace } from "vitest/config"; +import MagicString from "magic-string"; +import { readFileSync } from "fs"; + +export default defineWorkspace([ + { + test: { + name: 'normal', + include: ['fixtures/test/math.test.ts'] + }, + }, + { + test: { + name: 'special', + include: ['fixtures/test/custom-syntax.test.ts'] + }, + plugins: [customFilePlugin()] + } +]) + +function customFilePlugin(): Plugin { + return { + name: 'load-custom-files', + load(id) { + const filename = id.split("?")[0] + + if(filename.endsWith(".custom")) { + const content = readFileSync(filename, 'utf8') + + const s = new MagicString(content) + s.replaceAll('', ` +function covered() { + return "Custom file loaded!" +}`.trim()); + + s.replaceAll('', ` +function uncovered() { + return "This should be uncovered!" +}`.trim()); + + s.replaceAll('', 'export default covered()'); + s.replaceAll('', 'export default uncovered()'); + + return { code: s.toString(), map: s.generateMap({ hires: 'boundary'})} + } + }, + } +} diff --git a/test/coverage-test/fixtures/src/covered.custom b/test/coverage-test/fixtures/src/covered.custom new file mode 100644 index 0000000000000..095b52aa44c8f --- /dev/null +++ b/test/coverage-test/fixtures/src/covered.custom @@ -0,0 +1,5 @@ + + + + + diff --git a/test/coverage-test/fixtures/src/uncovered.custom b/test/coverage-test/fixtures/src/uncovered.custom new file mode 100644 index 0000000000000..f4e16be2968b7 --- /dev/null +++ b/test/coverage-test/fixtures/src/uncovered.custom @@ -0,0 +1,3 @@ + + + diff --git a/test/coverage-test/fixtures/test/custom-syntax.test.ts b/test/coverage-test/fixtures/test/custom-syntax.test.ts new file mode 100644 index 0000000000000..d4b08f589fd41 --- /dev/null +++ b/test/coverage-test/fixtures/test/custom-syntax.test.ts @@ -0,0 +1,8 @@ +import { expect, test } from 'vitest' + +// @ts-expect-error -- untyped +import output from '../src/covered.custom' + +test('custom file loads fine', () => { + expect(output).toMatch('Custom file loaded!') +}) diff --git a/test/coverage-test/test/all.test.ts b/test/coverage-test/test/all.test.ts index 063072e1f3dbf..c6bb23bf88e21 100644 --- a/test/coverage-test/test/all.test.ts +++ b/test/coverage-test/test/all.test.ts @@ -4,7 +4,7 @@ import { readCoverageMap, runVitest, test } from '../utils' test('{ all: true } includes uncovered files', async () => { await runVitest({ include: ['fixtures/test/**'], - exclude: ['**/virtual-files-**'], + exclude: ['**/virtual-files-**', '**/custom-syntax**'], coverage: { include: ['fixtures/src/**'], all: true, @@ -25,7 +25,7 @@ test('{ all: true } includes uncovered files', async () => { test('{ all: false } excludes uncovered files', async () => { await runVitest({ include: ['fixtures/test/**'], - exclude: ['**/virtual-files-**'], + exclude: ['**/virtual-files-**', '**/custom-syntax**'], coverage: { include: ['fixtures/src/**'], all: false, diff --git a/test/coverage-test/test/changed.test.ts b/test/coverage-test/test/changed.test.ts index 33ed43d6d139f..55db075df6d01 100644 --- a/test/coverage-test/test/changed.test.ts +++ b/test/coverage-test/test/changed.test.ts @@ -29,6 +29,7 @@ afterAll(() => { test('{ changed: "HEAD" }', async () => { await runVitest({ include: ['fixtures/test/**'], + exclude: ['**/custom-syntax**'], changed: 'HEAD', coverage: { include: ['fixtures/src/**'], diff --git a/test/coverage-test/test/workspace.multi-transform.test.ts b/test/coverage-test/test/workspace.multi-transform.test.ts new file mode 100644 index 0000000000000..d8aa54a8bef0b --- /dev/null +++ b/test/coverage-test/test/workspace.multi-transform.test.ts @@ -0,0 +1,25 @@ +import { expect } from 'vitest' +import { readCoverageMap, runVitest, test } from '../utils' + +test('{ all: true } includes uncovered files that require custom transform', async () => { + await runVitest({ + workspace: 'fixtures/configs/vitest.workspace.multi-transforms.ts', + coverage: { + all: true, + extension: ['.ts', '.custom'], + reporter: 'json', + include: ['**/*.custom', '**/math.ts'], + }, + }) + + const coverageMap = await readCoverageMap() + const files = coverageMap.files() + + expect(files).toMatchInlineSnapshot(` + [ + "/fixtures/src/covered.custom", + "/fixtures/src/math.ts", + "/fixtures/src/uncovered.custom", + ] + `) +})