[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix(coverage): prevent filtering out virtual files before remapping to sources (#8860)

authored by

Ari Perkkiö and committed by
GitHub
(Oct 30, 2025, 3:24 PM +0100) e3b77755 c57511be

+60 -26
+10 -5
packages/coverage-istanbul/src/provider.ts
··· 2 2 import type { Instrumenter } from 'istanbul-lib-instrument' 3 3 import type { ProxifiedModule } from 'magicast' 4 4 import type { CoverageProvider, ReportContext, ResolvedCoverageOptions, Vite, Vitest } from 'vitest/node' 5 - import { promises as fs } from 'node:fs' 5 + import { existsSync, promises as fs } from 'node:fs' 6 6 // @ts-expect-error missing types 7 7 import { defaults as istanbulDefaults } from '@istanbuljs/schema' 8 8 import createDebug from 'debug' ··· 15 15 import c from 'tinyrainbow' 16 16 import { BaseCoverageProvider } from 'vitest/coverage' 17 17 import { isCSSRequest } from 'vitest/node' 18 - 19 18 import { version } from '../package.json' with { type: 'json' } 20 19 import { COVERAGE_STORE_KEY } from './constants' 21 20 ··· 118 117 coverageMap.merge(await transformCoverage(uncoveredCoverage)) 119 118 } 120 119 121 - if (this.options.excludeAfterRemap) { 122 - coverageMap.filter(filename => this.isIncluded(filename)) 123 - } 120 + coverageMap.filter((filename) => { 121 + const exists = existsSync(filename) 122 + 123 + if (this.options.excludeAfterRemap) { 124 + return exists && this.isIncluded(filename) 125 + } 126 + 127 + return exists 128 + }) 124 129 125 130 if (debug.enabled) { 126 131 debug('Generate coverage total time %d ms', (performance.now() - start!).toFixed())
+10 -4
packages/coverage-v8/src/provider.ts
··· 2 2 import type { ProxifiedModule } from 'magicast' 3 3 import type { Profiler } from 'node:inspector' 4 4 import type { CoverageProvider, ReportContext, ResolvedCoverageOptions, TestProject, Vite, Vitest } from 'vitest/node' 5 - import { promises as fs } from 'node:fs' 5 + import { existsSync, promises as fs } from 'node:fs' 6 6 import { fileURLToPath } from 'node:url' 7 7 // @ts-expect-error -- untyped 8 8 import { mergeProcessCovs } from '@bcoe/v8-coverage' ··· 86 86 coverageMap.merge(await transformCoverage(untestedCoverage)) 87 87 } 88 88 89 - if (this.options.excludeAfterRemap) { 90 - coverageMap.filter(filename => this.isIncluded(filename)) 91 - } 89 + coverageMap.filter((filename) => { 90 + const exists = existsSync(filename) 91 + 92 + if (this.options.excludeAfterRemap) { 93 + return exists && this.isIncluded(filename) 94 + } 95 + 96 + return exists 97 + }) 92 98 93 99 if (debug.enabled) { 94 100 debug(`Generate coverage total time ${(performance.now() - start!).toFixed()} ms`)
+12
test/coverage-test/test/virtual-files.test.ts
··· 26 26 // Vitest browser 27 27 expect(file).not.toContain('\x00') 28 28 } 29 + 30 + expect(files).toContain('<process-cwd>/fixtures/src/math.ts') 31 + 32 + const fileCoverage = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/math.ts') 33 + expect(fileCoverage).toMatchInlineSnapshot(` 34 + { 35 + "branches": "0/0 (100%)", 36 + "functions": "1/4 (25%)", 37 + "lines": "1/4 (25%)", 38 + "statements": "1/4 (25%)", 39 + } 40 + `) 29 41 })
+5 -12
packages/vitest/src/node/coverage.ts
··· 6 6 import type { AfterSuiteRunMeta } from '../types/general' 7 7 import { existsSync, promises as fs, readdirSync, writeFileSync } from 'node:fs' 8 8 import path from 'node:path' 9 - import { cleanUrl, slash } from '@vitest/utils/helpers' 9 + import { slash } from '@vitest/utils/helpers' 10 10 import { relative, resolve } from 'pathe' 11 11 import pm from 'picomatch' 12 12 import { glob } from 'tinyglobby' ··· 161 161 // By default `coverage.include` matches all files, except "coverage.exclude" 162 162 const glob = this.options.include || '**' 163 163 164 - let included = roots.some((root) => { 165 - const options: pm.PicomatchOptions = { 166 - contains: true, 167 - dot: true, 168 - cwd: root, 169 - ignore: this.options.exclude, 170 - } 171 - 172 - return pm.isMatch(filename, glob, options) 164 + const included = pm.isMatch(filename, glob, { 165 + contains: true, 166 + dot: true, 167 + ignore: this.options.exclude, 173 168 }) 174 - 175 - included &&= existsSync(cleanUrl(filename)) 176 169 177 170 this.globCache.set(filename, included) 178 171
+15 -1
test/coverage-test/fixtures/configs/vitest.config.virtual-files.ts
··· 1 + import { readFileSync } from 'node:fs' 2 + import { resolve } from 'node:path' 1 3 import { Plugin, defineConfig, mergeConfig } from 'vitest/config' 4 + import { transformWithEsbuild } from 'vite' 2 5 3 6 import base from './vitest.config' 4 7 ··· 22 25 if (id === '\0vitest-custom-virtual-file-2') { 23 26 return 'src/\0vitest-custom-virtual-file-2.ts' 24 27 } 28 + 29 + if (id.includes('vitest-custom-virtual:math')) { 30 + return resolve(import.meta.dirname, "../src/vitest-custom-virtual:math") 31 + } 25 32 }, 26 33 load(id) { 27 34 if (id === 'src/virtual:vitest-custom-virtual-file-1.ts') { ··· 38 45 export default virtualFile; 39 46 ` 40 47 } 48 + 49 + if(id.includes("vitest-custom-virtual:math")) { 50 + const filename = resolve(import.meta.dirname, "../src/math.ts"); 51 + const sources = readFileSync(filename, "utf8") 52 + 53 + return transformWithEsbuild(sources, filename) 54 + } 41 55 }, 42 56 } 43 - } 57 + }
+4 -2
test/coverage-test/fixtures/src/virtual-files.ts
··· 1 1 // @ts-expect-error -- untyped virtual file provided by custom plugin 2 2 import virtualFile1 from 'virtual:vitest-custom-virtual-file-1' 3 3 4 - 5 4 // @ts-expect-error -- untyped virtual file provided by custom plugin 6 5 import virtualFile2 from '\0vitest-custom-virtual-file-2' 7 6 7 + // @ts-expect-error -- untyped virtual file provided by custom plugin 8 + import * as virtualMath from 'vitest-custom-virtual:math' 9 + 8 10 export function getVirtualFileImports() { 9 - return { virtualFile1, virtualFile2 } 11 + return { virtualFile1, virtualFile2, virtualMath } 10 12 }
+4 -2
test/coverage-test/fixtures/test/virtual-files-fixture.test.ts
··· 2 2 import { getVirtualFileImports} from '../src/virtual-files' 3 3 4 4 test("verify virtual files work", () => { 5 - const {virtualFile1, virtualFile2} = getVirtualFileImports() 5 + const {virtualFile1, virtualFile2, virtualMath} = getVirtualFileImports() 6 6 7 7 expect(virtualFile1).toBe('This file should be excluded from coverage report #1') 8 8 expect(virtualFile2).toBe('This file should be excluded from coverage report #2') 9 9 10 - }) 10 + expect(virtualMath).toHaveProperty('sum') 11 + expect(virtualMath.sum(50, 65)).toBe(115) 12 + })