[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.

feat!(coverage): v8 to support only AST based remapping (#8064)

authored by

Ari Perkkiö and committed by
GitHub
(Jun 18, 2025, 12:29 PM +0300) 176133ed 41a111c3

+355 -1177
-1
package.json
··· 86 86 "@sinonjs/fake-timers@14.0.0": "patches/@sinonjs__fake-timers@14.0.0.patch", 87 87 "cac@6.7.14": "patches/cac@6.7.14.patch", 88 88 "@types/sinonjs__fake-timers@8.1.5": "patches/@types__sinonjs__fake-timers@8.1.5.patch", 89 - "v8-to-istanbul@9.3.0": "patches/v8-to-istanbul@9.3.0.patch", 90 89 "acorn@8.11.3": "patches/acorn@8.11.3.patch" 91 90 }, 92 91 "onlyBuiltDependencies": [
-16
pnpm-lock.yaml
··· 143 143 cac@6.7.14: 144 144 hash: a8f0f3517a47ce716ed90c0cfe6ae382ab763b021a664ada2a608477d0621588 145 145 path: patches/cac@6.7.14.patch 146 - v8-to-istanbul@9.3.0: 147 - hash: fc8eccce7f8e7c27b0d0b1c63e93ff3adaa8c05c33d5603bacf58ea6d12951f3 148 - path: patches/v8-to-istanbul@9.3.0.patch 149 146 150 147 importers: 151 148 ··· 633 630 pathe: 634 631 specifier: 'catalog:' 635 632 version: 2.0.3 636 - v8-to-istanbul: 637 - specifier: ^9.3.0 638 - version: 9.3.0(patch_hash=fc8eccce7f8e7c27b0d0b1c63e93ff3adaa8c05c33d5603bacf58ea6d12951f3) 639 633 vite-node: 640 634 specifier: workspace:* 641 635 version: link:../vite-node ··· 8433 8427 utils-merge@1.0.1: 8434 8428 resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 8435 8429 engines: {node: '>= 0.4.0'} 8436 - 8437 - v8-to-istanbul@9.3.0: 8438 - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} 8439 - engines: {node: '>=10.12.0'} 8440 8430 8441 8431 varint@6.0.0: 8442 8432 resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} ··· 16595 16585 util-deprecate@1.0.2: {} 16596 16586 16597 16587 utils-merge@1.0.1: {} 16598 - 16599 - v8-to-istanbul@9.3.0(patch_hash=fc8eccce7f8e7c27b0d0b1c63e93ff3adaa8c05c33d5603bacf58ea6d12951f3): 16600 - dependencies: 16601 - '@jridgewell/trace-mapping': 0.3.25 16602 - '@types/istanbul-lib-coverage': 2.0.6 16603 - convert-source-map: 2.0.0 16604 16588 16605 16589 varint@6.0.0: {} 16606 16590
-174
patches/v8-to-istanbul@9.3.0.patch
··· 1 - diff --git a/CHANGELOG.md b/CHANGELOG.md 2 - deleted file mode 100644 3 - index 4f7e3bc8d1bba4feb51044ff9eb77b41f972f957..0000000000000000000000000000000000000000 4 - diff --git a/index.d.ts b/index.d.ts 5 - index ee7b286844f2bf96357218166e26e1c338f774cf..657531b7c75f43e9a4e957dd1f10797e44da5bb1 100644 6 - --- a/index.d.ts 7 - +++ b/index.d.ts 8 - @@ -1,5 +1,7 @@ 9 - /// <reference types="node" /> 10 - 11 - +// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 12 - + 13 - import { Profiler } from 'inspector' 14 - import { CoverageMapData } from 'istanbul-lib-coverage' 15 - import { SourceMapInput } from '@jridgewell/trace-mapping' 16 - @@ -20,6 +22,6 @@ declare class V8ToIstanbul { 17 - toIstanbul(): CoverageMapData 18 - } 19 - 20 - -declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources, excludePath?: (path: string) => boolean): V8ToIstanbul 21 - +declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources, excludePath?: (path: string) => boolean, excludeEmptyLines?: boolean): V8ToIstanbul 22 - 23 - export = v8ToIstanbul 24 - diff --git a/index.js b/index.js 25 - index 4db27a7d84324d0e6605c5506e3eee5665ddfeb0..7bfb839634b1e3c54efedc3c270d82edc4167a64 100644 26 - --- a/index.js 27 - +++ b/index.js 28 - @@ -1,5 +1,6 @@ 29 - +// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 30 - const V8ToIstanbul = require('./lib/v8-to-istanbul') 31 - 32 - -module.exports = function (path, wrapperLength, sources, excludePath) { 33 - - return new V8ToIstanbul(path, wrapperLength, sources, excludePath) 34 - +module.exports = function (path, wrapperLength, sources, excludePath, excludeEmptyLines) { 35 - + return new V8ToIstanbul(path, wrapperLength, sources, excludePath, excludeEmptyLines) 36 - } 37 - diff --git a/lib/source.js b/lib/source.js 38 - index d8ebc215f6ad83d472abafe976935acfe5c61b04..021fd2aed1f73ebb4adc449ce6e96f2d89c295a5 100644 39 - --- a/lib/source.js 40 - +++ b/lib/source.js 41 - @@ -1,23 +1,32 @@ 42 - +// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 43 - const CovLine = require('./line') 44 - const { sliceRange } = require('./range') 45 - -const { originalPositionFor, generatedPositionFor, GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND } = require('@jridgewell/trace-mapping') 46 - +const { originalPositionFor, generatedPositionFor, eachMapping, GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND } = require('@jridgewell/trace-mapping') 47 - 48 - module.exports = class CovSource { 49 - - constructor (sourceRaw, wrapperLength) { 50 - + constructor (sourceRaw, wrapperLength, traceMap) { 51 - sourceRaw = sourceRaw ? sourceRaw.trimEnd() : '' 52 - this.lines = [] 53 - this.eof = sourceRaw.length 54 - this.shebangLength = getShebangLength(sourceRaw) 55 - this.wrapperLength = wrapperLength - this.shebangLength 56 - - this._buildLines(sourceRaw) 57 - + this._buildLines(sourceRaw, traceMap) 58 - } 59 - 60 - - _buildLines (source) { 61 - + _buildLines (source, traceMap) { 62 - let position = 0 63 - let ignoreCount = 0 64 - let ignoreAll = false 65 - + const linesToCover = traceMap && this._parseLinesToCover(traceMap) 66 - + 67 - for (const [i, lineStr] of source.split(/(?<=\r?\n)/u).entries()) { 68 - - const line = new CovLine(i + 1, position, lineStr) 69 - + const lineNumber = i + 1 70 - + const line = new CovLine(lineNumber, position, lineStr) 71 - + 72 - + if (linesToCover && !linesToCover.has(lineNumber)) { 73 - + line.ignore = true 74 - + } 75 - + 76 - if (ignoreCount > 0) { 77 - line.ignore = true 78 - ignoreCount-- 79 - @@ -125,6 +134,18 @@ module.exports = class CovSource { 80 - if (this.lines[line - 1] === undefined) return this.eof 81 - return Math.min(this.lines[line - 1].startCol + relCol, this.lines[line - 1].endCol) 82 - } 83 - + 84 - + _parseLinesToCover (traceMap) { 85 - + const linesToCover = new Set() 86 - + 87 - + eachMapping(traceMap, (mapping) => { 88 - + if (mapping.originalLine !== null) { 89 - + linesToCover.add(mapping.originalLine) 90 - + } 91 - + }) 92 - + 93 - + return linesToCover 94 - + } 95 - } 96 - 97 - // this implementation is pulled over from istanbul-lib-sourcemap: 98 - diff --git a/lib/v8-to-istanbul.js b/lib/v8-to-istanbul.js 99 - index 3616437b00658861dc5a8910c64d1449e9fdf467..4642ca4818ce982e2f186abe4289793768e7cdf9 100644 100 - --- a/lib/v8-to-istanbul.js 101 - +++ b/lib/v8-to-istanbul.js 102 - @@ -1,3 +1,4 @@ 103 - +// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 104 - const assert = require('assert') 105 - const convertSourceMap = require('convert-source-map') 106 - const util = require('util') 107 - @@ -8,14 +9,9 @@ const CovBranch = require('./branch') 108 - const CovFunction = require('./function') 109 - const CovSource = require('./source') 110 - const { sliceRange } = require('./range') 111 - -const compatError = Error(`requires Node.js ${require('../package.json').engines.node}`) 112 - -const { readFileSync } = require('fs') 113 - -let readFile = () => { throw compatError } 114 - -try { 115 - - readFile = require('fs').promises.readFile 116 - -} catch (_err) { 117 - - // most likely we're on an older version of Node.js. 118 - -} 119 - +const { readFileSync, promises } = require('fs') 120 - +const readFile = promises.readFile 121 - + 122 - const { TraceMap } = require('@jridgewell/trace-mapping') 123 - const isOlderNode10 = /^v10\.(([0-9]\.)|(1[0-5]\.))/u.test(process.version) 124 - const isNode8 = /^v8\./.test(process.version) 125 - @@ -25,12 +21,13 @@ const isNode8 = /^v8\./.test(process.version) 126 - const cjsWrapperLength = isOlderNode10 ? require('module').wrapper[0].length : 0 127 - 128 - module.exports = class V8ToIstanbul { 129 - - constructor (scriptPath, wrapperLength, sources, excludePath) { 130 - + constructor (scriptPath, wrapperLength, sources, excludePath, excludeEmptyLines) { 131 - assert(typeof scriptPath === 'string', 'scriptPath must be a string') 132 - assert(!isNode8, 'This module does not support node 8 or lower, please upgrade to node 10') 133 - this.path = parsePath(scriptPath) 134 - this.wrapperLength = wrapperLength === undefined ? cjsWrapperLength : wrapperLength 135 - this.excludePath = excludePath || (() => false) 136 - + this.excludeEmptyLines = excludeEmptyLines === true 137 - this.sources = sources || {} 138 - this.generatedLines = [] 139 - this.branches = {} 140 - @@ -58,8 +55,8 @@ module.exports = class V8ToIstanbul { 141 - if (!this.sourceMap.sourcesContent) { 142 - this.sourceMap.sourcesContent = await this.sourcesContentFromSources() 143 - } 144 - - this.covSources = this.sourceMap.sourcesContent.map((rawSource, i) => ({ source: new CovSource(rawSource, this.wrapperLength), path: this.sourceMap.sources[i] })) 145 - - this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength) 146 - + this.covSources = this.sourceMap.sourcesContent.map((rawSource, i) => ({ source: new CovSource(rawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null), path: this.sourceMap.sources[i] })) 147 - + this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null) 148 - } else { 149 - const candidatePath = this.rawSourceMap.sourcemap.sources.length >= 1 ? this.rawSourceMap.sourcemap.sources[0] : this.rawSourceMap.sourcemap.file 150 - this.path = this._resolveSource(this.rawSourceMap, candidatePath || this.path) 151 - @@ -82,8 +79,8 @@ module.exports = class V8ToIstanbul { 152 - // We fallback to reading the original source from disk. 153 - originalRawSource = await readFile(this.path, 'utf8') 154 - } 155 - - this.covSources = [{ source: new CovSource(originalRawSource, this.wrapperLength), path: this.path }] 156 - - this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength) 157 - + this.covSources = [{ source: new CovSource(originalRawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null), path: this.path }] 158 - + this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null) 159 - } 160 - } else { 161 - this.covSources = [{ source: new CovSource(rawSource, this.wrapperLength), path: this.path }] 162 - @@ -281,8 +278,10 @@ module.exports = class V8ToIstanbul { 163 - s: {} 164 - } 165 - source.lines.forEach((line, index) => { 166 - - statements.statementMap[`${index}`] = line.toIstanbul() 167 - - statements.s[`${index}`] = line.ignore ? 1 : line.count 168 - + if (!line.ignore) { 169 - + statements.statementMap[`${index}`] = line.toIstanbul() 170 - + statements.s[`${index}`] = line.count 171 - + } 172 - }) 173 - return statements 174 - }
+1 -41
docs/config/index.md
··· 1628 1628 } 1629 1629 ``` 1630 1630 1631 - #### coverage.ignoreEmptyLines 1632 - 1633 - - **Type:** `boolean` 1634 - - **Default:** `true` (`false` in v1) 1635 - - **Available for providers:** `'v8'` 1636 - - **CLI:** `--coverage.ignoreEmptyLines=<boolean>` 1637 - 1638 - Ignore empty lines, comments and other non-runtime code, e.g. Typescript types. Requires `experimentalAstAwareRemapping: false`. 1639 - 1640 - This option works only if the used compiler removes comments and other non-runtime code from the transpiled code. 1641 - By default Vite uses ESBuild which removes comments and Typescript types from `.ts`, `.tsx` and `.jsx` files. 1642 - 1643 - If you want to apply ESBuild to other files as well, define them in [`esbuild` options](https://vitejs.dev/config/shared-options.html#esbuild): 1644 - 1645 - ```ts 1646 - import { defineConfig } from 'vitest/config' 1647 - 1648 - export default defineConfig({ 1649 - esbuild: { 1650 - // Transpile all files with ESBuild to remove comments from code coverage. 1651 - // Required for `test.coverage.ignoreEmptyLines` to work: 1652 - include: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.ts', '**/*.tsx'], 1653 - }, 1654 - test: { 1655 - coverage: { 1656 - provider: 'v8', 1657 - ignoreEmptyLines: true, 1658 - }, 1659 - }, 1660 - }) 1661 - ``` 1662 - #### coverage.experimentalAstAwareRemapping 1663 - 1664 - - **Type:** `boolean` 1665 - - **Default:** `false` 1666 - - **Available for providers:** `'v8'` 1667 - - **CLI:** `--coverage.experimentalAstAwareRemapping=<boolean>` 1668 - 1669 - Remap coverage with experimental AST based analysis. Provides more accurate results compared to default mode. 1670 - 1671 1631 #### coverage.ignoreClassMethods 1672 1632 1673 1633 - **Type:** `string[]` 1674 1634 - **Default:** `[]` 1675 - - **Available for providers:** `'istanbul'` 1635 + - **Available for providers:** `'v8' | 'istanbul'` 1676 1636 - **CLI:** `--coverage.ignoreClassMethods=<method>` 1677 1637 1678 1638 Set to array of class method names to ignore for coverage.
+1 -2
docs/guide/coverage.md
··· 336 336 337 337 Both coverage providers have their own ways how to ignore code from coverage reports: 338 338 339 - - [`v8`](https://github.com/istanbuljs/v8-to-istanbul#ignoring-uncovered-lines) 339 + - [`v8`](https://github.com/AriPerkkio/ast-v8-to-istanbul?tab=readme-ov-file#ignoring-code) 340 340 - [`istanbul`](https://github.com/istanbuljs/nyc#parsing-hints-ignoring-lines) 341 - - `v8` with [`experimentalAstAwareRemapping: true`](https://vitest.dev/config/#coverage-experimentalAstAwareRemapping) see [ast-v8-to-istanbul | Ignoring code](https://github.com/AriPerkkio/ast-v8-to-istanbul?tab=readme-ov-file#ignoring-code) 342 341 343 342 When using TypeScript the source codes are transpiled using `esbuild`, which strips all comments from the source codes ([esbuild#516](https://github.com/evanw/esbuild/issues/516)). 344 343 Comments which are considered as [legal comments](https://esbuild.github.io/api/#legal-comments) are preserved.
+15
docs/guide/migration.md
··· 21 21 }) 22 22 ``` 23 23 24 + ### V8 Code Coverage Major Changes 25 + 26 + Vitest's V8 code coverage provider is now using more accurate coverage result remapping logic. 27 + It is expected for users to see changes in their coverage reports when updating from Vitest v3. 28 + 29 + In the past Vitest used [`v8-to-istanbul`](https://github.com/istanbuljs/v8-to-istanbul) for remapping V8 coverage results into your source files. 30 + This method wasn't very accurate and provided plenty of false positives in the coverage reports. 31 + We've now developed a new package that utilizes AST based analysis for the V8 coverage. 32 + This allows V8 reports to be as accurate as `@vitest/coverage-istanbul` reports. 33 + 34 + - Coverage ignore hints have updated. See [Coverage | Ignoring Code](/guide/coverage.html#ignoring-code). 35 + - `coverage.ignoreEmptyLines` is removed. Lines without runtime code are no longer included in reports. 36 + - `coverage.experimentalAstAwareRemapping` is removed. This option is now enabled by default, and is the only supported remapping method. 37 + - `coverage.ignoreClassMethods` is now supported by V8 provider too. 38 + 24 39 ### Removed options `coverage.all` and `coverage.extensions` 25 40 26 41 In previous versions Vitest included all uncovered files in coverage report by default.
-1
packages/coverage-v8/package.json
··· 75 75 "@types/istanbul-reports": "catalog:", 76 76 "@vitest/browser": "workspace:*", 77 77 "pathe": "catalog:", 78 - "v8-to-istanbul": "^9.3.0", 79 78 "vite-node": "workspace:*", 80 79 "vitest": "workspace:*" 81 80 }
+1 -6
test/coverage-test/utils.ts
··· 46 46 enabled: true, 47 47 reporter: [], 48 48 ...config.coverage, 49 - provider: provider === 'v8-ast-aware' ? 'v8' : provider, 50 - experimentalAstAwareRemapping: provider === 'v8-ast-aware', 49 + provider, 51 50 customProviderModule: provider === 'custom' ? 'fixtures/custom-provider' : undefined, 52 51 }, 53 52 browser: { ··· 108 107 109 108 export function isV8Provider() { 110 109 return process.env.COVERAGE_PROVIDER === 'v8' 111 - } 112 - 113 - export function isExperimentalV8Provider() { 114 - return process.env.COVERAGE_PROVIDER === 'v8-ast-aware' 115 110 } 116 111 117 112 export function isBrowser() {
+1 -20
test/coverage-test/vitest.workspace.custom.ts
··· 31 31 }, 32 32 }, 33 33 34 - // Test cases for experimental AST aware v8-provider 35 - { 36 - test: { 37 - ...config.test, 38 - name: 'v8-ast-aware', 39 - env: { COVERAGE_PROVIDER: 'v8-ast-aware' }, 40 - 41 - // Intentionally run Istanbul tests too 42 - include: [GENERIC_TESTS, ISTANBUL_TESTS, V8_TESTS], 43 - exclude: [ 44 - UNIT_TESTS, 45 - CUSTOM_TESTS, 46 - BROWSER_TESTS, 47 - // Not using original v8-to-istanbul that has patch applied: github.com/istanbuljs/v8-to-istanbul/pull/244 48 - 'test/empty-lines.v8.test.ts', 49 - ], 50 - }, 51 - }, 52 - 53 34 // Test cases for istanbul-provider 54 35 { 55 36 test: { ··· 108 89 test: { 109 90 ...config.test, 110 91 name: { label: 'v8-browser', color: 'red' }, 111 - env: { COVERAGE_PROVIDER: 'v8-ast-aware', COVERAGE_BROWSER: 'true' }, 92 + env: { COVERAGE_PROVIDER: 'v8', COVERAGE_BROWSER: 'true' }, 112 93 include: [ 113 94 BROWSER_TESTS, 114 95
+117 -195
packages/coverage-v8/src/provider.ts
··· 6 6 import type { CoverageProvider, ReportContext, ResolvedCoverageOptions, TestProject, Vitest } from 'vitest/node' 7 7 import { promises as fs } from 'node:fs' 8 8 import { fileURLToPath, pathToFileURL } from 'node:url' 9 - import remapping from '@ampproject/remapping' 10 9 // @ts-expect-error -- untyped 11 10 import { mergeProcessCovs } from '@bcoe/v8-coverage' 12 11 import astV8ToIstanbul from 'ast-v8-to-istanbul' ··· 15 14 import libReport from 'istanbul-lib-report' 16 15 import libSourceMaps from 'istanbul-lib-source-maps' 17 16 import reports from 'istanbul-reports' 18 - import MagicString from 'magic-string' 19 17 import { parseModule } from 'magicast' 20 18 import { normalize } from 'pathe' 21 19 import { provider } from 'std-env' 22 20 import c from 'tinyrainbow' 23 - import v8ToIstanbul from 'v8-to-istanbul' 24 21 import { cleanUrl } from 'vite-node/utils' 25 22 26 23 import { BaseCoverageProvider } from 'vitest/coverage' ··· 34 31 type TransformResults = Map<string, FetchResult> 35 32 interface RawCoverage { result: ScriptCoverageWithOffset[] } 36 33 37 - // Note that this needs to match the line ending as well 38 - const VITE_EXPORTS_LINE_PATTERN 39 - = /Object\.defineProperty\(__vite_ssr_exports__.*\n/g 40 - const DECORATOR_METADATA_PATTERN 41 - = /_ts_metadata\("design:paramtypes", \[[^\]]*\]\),*/g 42 34 const FILE_PROTOCOL = 'file://' 43 35 44 36 const debug = createDebug('vitest:coverage') ··· 188 180 transform, 189 181 ) 190 182 191 - coverageMap.merge(await this.v8ToIstanbul( 183 + coverageMap.merge(await this.remapCoverage( 192 184 url.href, 193 185 0, 194 186 sources, 195 - [{ 196 - ranges: [ 197 - { 198 - startOffset: 0, 199 - endOffset: sources.originalSource.length, 200 - count: 0, 201 - }, 202 - ], 203 - isBlockCoverage: true, 204 - // This is magical value that indicates an empty report: https://github.com/istanbuljs/v8-to-istanbul/blob/fca5e6a9e6ef38a9cdc3a178d5a6cf9ef82e6cab/lib/v8-to-istanbul.js#LL131C40-L131C40 205 - functionName: '(empty-report)', 206 - }], 187 + [], 207 188 )) 208 189 209 190 if (debug.enabled) { ··· 219 200 return coverageMap 220 201 } 221 202 222 - private async v8ToIstanbul(filename: string, wrapperLength: number, sources: Awaited<ReturnType<typeof this.getSources>>, functions: Profiler.FunctionCoverage[]) { 223 - if (this.options.experimentalAstAwareRemapping) { 224 - let ast 225 - try { 226 - ast = await parseAstAsync(sources.source) 227 - } 228 - catch (error) { 229 - this.ctx.logger.error(`Failed to parse ${filename}. Excluding it from coverage.\n`, error) 230 - return {} 231 - } 232 - 233 - return await astV8ToIstanbul({ 234 - code: sources.source, 235 - sourceMap: sources.sourceMap?.sourcemap, 236 - ast, 237 - coverage: { functions, url: filename }, 238 - ignoreClassMethods: this.options.ignoreClassMethods, 239 - wrapperLength, 240 - ignoreNode: (node, type) => { 241 - // SSR transformed imports 242 - if ( 243 - type === 'statement' 244 - && node.type === 'VariableDeclarator' 245 - && node.id.type === 'Identifier' 246 - && node.id.name.startsWith('__vite_ssr_import_') 247 - ) { 248 - return true 249 - } 250 - 251 - // SSR transformed exports vite@>6.3.5 252 - if ( 253 - type === 'statement' 254 - && node.type === 'ExpressionStatement' 255 - && node.expression.type === 'AssignmentExpression' 256 - && node.expression.left.type === 'MemberExpression' 257 - && node.expression.left.object.type === 'Identifier' 258 - && node.expression.left.object.name === '__vite_ssr_exports__' 259 - ) { 260 - return true 261 - } 262 - 263 - // SSR transformed exports vite@^6.3.5 264 - if ( 265 - type === 'statement' 266 - && node.type === 'VariableDeclarator' 267 - && node.id.type === 'Identifier' 268 - && node.id.name === '__vite_ssr_export_default__' 269 - ) { 270 - return true 271 - } 272 - 273 - // in-source test with "if (import.meta.vitest)" 274 - if ( 275 - (type === 'branch' || type === 'statement') 276 - && node.type === 'IfStatement' 277 - && node.test.type === 'MemberExpression' 278 - && node.test.property.type === 'Identifier' 279 - && node.test.property.name === 'vitest' 280 - ) { 281 - // SSR 282 - if ( 283 - node.test.object.type === 'Identifier' 284 - && node.test.object.name === '__vite_ssr_import_meta__' 285 - ) { 286 - return 'ignore-this-and-nested-nodes' 287 - } 288 - 289 - // Web 290 - if ( 291 - node.test.object.type === 'MetaProperty' 292 - && node.test.object.meta.name === 'import' 293 - && node.test.object.property.name === 'meta' 294 - ) { 295 - return 'ignore-this-and-nested-nodes' 296 - } 297 - } 298 - 299 - // Browser mode's "import.meta.env =" 300 - if ( 301 - type === 'statement' 302 - && node.type === 'ExpressionStatement' 303 - && node.expression.type === 'AssignmentExpression' 304 - && node.expression.left.type === 'MemberExpression' 305 - && node.expression.left.object.type === 'MetaProperty' 306 - && node.expression.left.object.meta.name === 'import' 307 - && node.expression.left.object.property.name === 'meta' 308 - && node.expression.left.property.type === 'Identifier' 309 - && node.expression.left.property.name === 'env') { 310 - return true 311 - } 312 - }, 313 - }, 314 - ) 315 - } 316 - 317 - const converter = v8ToIstanbul( 318 - filename, 319 - wrapperLength, 320 - sources, 321 - undefined, 322 - this.options.ignoreEmptyLines, 323 - ) 324 - await converter.load() 203 + private async remapCoverage(filename: string, wrapperLength: number, result: Awaited<ReturnType<typeof this.getSources>>, functions: Profiler.FunctionCoverage[]) { 204 + let ast 325 205 326 206 try { 327 - converter.applyCoverage(functions) 207 + ast = await parseAstAsync(result.code) 328 208 } 329 209 catch (error) { 330 - this.ctx.logger.error(`Failed to convert coverage for ${filename}.\n`, error) 210 + this.ctx.logger.error(`Failed to parse ${filename}. Excluding it from coverage.\n`, error) 211 + return {} 331 212 } 332 213 333 - return converter.toIstanbul() 214 + return await astV8ToIstanbul({ 215 + code: result.code, 216 + sourceMap: result.map, 217 + ast, 218 + coverage: { functions, url: filename }, 219 + ignoreClassMethods: this.options.ignoreClassMethods, 220 + wrapperLength, 221 + ignoreNode: (node, type) => { 222 + // SSR transformed imports 223 + if ( 224 + type === 'statement' 225 + && node.type === 'VariableDeclarator' 226 + && node.id.type === 'Identifier' 227 + && node.id.name.startsWith('__vite_ssr_import_') 228 + ) { 229 + return true 230 + } 231 + 232 + // SSR transformed exports vite@>6.3.5 233 + if ( 234 + type === 'statement' 235 + && node.type === 'ExpressionStatement' 236 + && node.expression.type === 'AssignmentExpression' 237 + && node.expression.left.type === 'MemberExpression' 238 + && node.expression.left.object.type === 'Identifier' 239 + && node.expression.left.object.name === '__vite_ssr_exports__' 240 + ) { 241 + return true 242 + } 243 + 244 + // SSR transformed exports vite@^6.3.5 245 + if ( 246 + type === 'statement' 247 + && node.type === 'VariableDeclarator' 248 + && node.id.type === 'Identifier' 249 + && node.id.name === '__vite_ssr_export_default__' 250 + ) { 251 + return true 252 + } 253 + 254 + // in-source test with "if (import.meta.vitest)" 255 + if ( 256 + (type === 'branch' || type === 'statement') 257 + && node.type === 'IfStatement' 258 + && node.test.type === 'MemberExpression' 259 + && node.test.property.type === 'Identifier' 260 + && node.test.property.name === 'vitest' 261 + ) { 262 + // SSR 263 + if ( 264 + node.test.object.type === 'Identifier' 265 + && node.test.object.name === '__vite_ssr_import_meta__' 266 + ) { 267 + return 'ignore-this-and-nested-nodes' 268 + } 269 + 270 + // Web 271 + if ( 272 + node.test.object.type === 'MetaProperty' 273 + && node.test.object.meta.name === 'import' 274 + && node.test.object.property.name === 'meta' 275 + ) { 276 + return 'ignore-this-and-nested-nodes' 277 + } 278 + } 279 + 280 + // Browser mode's "import.meta.env =" 281 + if ( 282 + type === 'statement' 283 + && node.type === 'ExpressionStatement' 284 + && node.expression.type === 'AssignmentExpression' 285 + && node.expression.left.type === 'MemberExpression' 286 + && node.expression.left.object.type === 'MetaProperty' 287 + && node.expression.left.object.meta.name === 'import' 288 + && node.expression.left.object.property.name === 'meta' 289 + && node.expression.left.property.type === 'Identifier' 290 + && node.expression.left.property.name === 'env') { 291 + return true 292 + } 293 + 294 + // SWC's decorators 295 + if ( 296 + type === 'statement' 297 + && node.type === 'ExpressionStatement' 298 + && node.expression.type === 'CallExpression' 299 + && node.expression.callee.type === 'Identifier' 300 + && node.expression.callee.name === '_ts_decorate') { 301 + return 'ignore-this-and-nested-nodes' 302 + } 303 + }, 304 + }, 305 + ) 334 306 } 335 307 336 308 private async getSources<TransformResult extends (FetchResult | Awaited<ReturnType<typeof this.ctx.vitenode.transformRequest>>)>( ··· 339 311 onTransform: (filepath: string) => Promise<TransformResult>, 340 312 functions: Profiler.FunctionCoverage[] = [], 341 313 ): Promise<{ 342 - source: string 343 - originalSource: string 344 - sourceMap?: { sourcemap: EncodedSourceMap } 314 + code: string 315 + map?: EncodedSourceMap 345 316 }> { 346 317 const filePath = normalize(fileURLToPath(url)) 347 318 ··· 353 324 354 325 const map = transformResult?.map as EncodedSourceMap | undefined 355 326 const code = transformResult?.code 356 - const sourcesContent = map?.sourcesContent || [] 357 327 358 - if (!sourcesContent[0]) { 359 - sourcesContent[0] = await fs.readFile(filePath, 'utf-8').catch(() => { 328 + if (!code) { 329 + const original = await fs.readFile(filePath, 'utf-8').catch(() => { 360 330 // If file does not exist construct a dummy source for it. 361 331 // These can be files that were generated dynamically during the test run and were removed after it. 362 332 const length = findLongestFunctionLength(functions) 363 333 return '/'.repeat(length) 364 334 }) 335 + 336 + return { code: original } 365 337 } 366 338 367 - // These can be uncovered files picked by "coverage.include" or files that are loaded outside vite-node 368 - if (!map) { 369 - return { 370 - source: code || sourcesContent[0], 371 - originalSource: sourcesContent[0], 339 + // Vue needs special handling for "map.sources" 340 + if (map) { 341 + map.sources ||= [] 342 + 343 + map.sources = map.sources 344 + .filter(source => source != null) 345 + .map(source => new URL(source, url).href) 346 + 347 + if (map.sources.length === 0) { 348 + map.sources.push(url) 372 349 } 373 350 } 374 351 375 - const sources = (map.sources || []) 376 - .filter(source => source != null) 377 - .map(source => new URL(source, url).href) 378 - 379 - if (sources.length === 0) { 380 - sources.push(url) 381 - } 382 - 383 - return { 384 - originalSource: sourcesContent[0], 385 - source: code || sourcesContent[0], 386 - sourceMap: { 387 - sourcemap: excludeGeneratedCode(code, { 388 - ...map, 389 - version: 3, 390 - sources, 391 - sourcesContent, 392 - }), 393 - }, 394 - } 352 + return { code, map } 395 353 } 396 354 397 355 private async convertCoverage( ··· 464 422 functions, 465 423 ) 466 424 467 - coverageMap.merge(await this.v8ToIstanbul( 425 + coverageMap.merge(await this.remapCoverage( 468 426 url, 469 427 startOffset, 470 428 sources, ··· 489 447 async function transformCoverage(coverageMap: CoverageMap) { 490 448 const sourceMapStore = libSourceMaps.createSourceMapStore() 491 449 return await sourceMapStore.transformCoverage(coverageMap) 492 - } 493 - 494 - /** 495 - * Remove generated code from the source maps: 496 - * - Vite's export helpers: e.g. `Object.defineProperty(__vite_ssr_exports__, "sum", { enumerable: true, configurable: true, get(){ return sum }});` 497 - * - SWC's decorator metadata: e.g. `_ts_metadata("design:paramtypes", [\ntypeof Request === "undefined" ? Object : Request\n]),` 498 - */ 499 - function excludeGeneratedCode( 500 - source: string | undefined, 501 - map: EncodedSourceMap, 502 - ) { 503 - if (!source) { 504 - return map 505 - } 506 - 507 - if ( 508 - !source.match(VITE_EXPORTS_LINE_PATTERN) 509 - && !source.match(DECORATOR_METADATA_PATTERN) 510 - ) { 511 - return map 512 - } 513 - 514 - const trimmed = new MagicString(source) 515 - trimmed.replaceAll(VITE_EXPORTS_LINE_PATTERN, '\n') 516 - trimmed.replaceAll(DECORATOR_METADATA_PATTERN, match => 517 - '\n'.repeat(match.split('\n').length - 1)) 518 - 519 - const trimmedMap = trimmed.generateMap({ hires: 'boundary' }) 520 - 521 - // A merged source map where the first one excludes generated parts 522 - const combinedMap = remapping( 523 - [{ ...trimmedMap, version: 3 }, map], 524 - () => null, 525 - ) 526 - 527 - return combinedMap as EncodedSourceMap 528 450 } 529 451 530 452 /**
-1
packages/vitest/src/defaults.ts
··· 44 44 ], 45 45 allowExternal: false, 46 46 excludeAfterRemap: false, 47 - ignoreEmptyLines: true, 48 47 processingConcurrency: Math.min( 49 48 20, 50 49 os.availableParallelism?.() ?? os.cpus().length,
+1 -1
test/config/test/failures.test.ts
··· 394 394 }) 395 395 396 396 test('boolean flag 100 should not crash CLI', async () => { 397 - let { stderr } = await runVitestCli('--coverage.enabled', '--coverage.thresholds.100', '--coverage.include=fixtures/coverage-test') 397 + let { stderr } = await runVitestCli('--coverage.enabled', '--coverage.thresholds.100', '--coverage.include=fixtures/coverage-test', '--passWithNoTests') 398 398 // non-zero coverage shows up, which is non-deterministic, so strip it. 399 399 stderr = stderr.replace(/\([0-9.]+%\) does/g, '(0%) does') 400 400
+17 -37
test/coverage-test/test/bundled-sources.test.ts
··· 1 1 import libCoverage from 'istanbul-lib-coverage' 2 2 import { expect } from 'vitest' 3 3 import * as transpiled from '../fixtures/src/pre-bundle/bundle.js' 4 - import { coverageTest, formatSummary, isV8Provider, normalizeURL, readCoverageJson, runVitest, test } from '../utils.js' 4 + import { coverageTest, formatSummary, normalizeURL, readCoverageJson, runVitest, test } from '../utils.js' 5 5 6 6 test('bundled code with source maps to originals', async () => { 7 7 await runVitest({ ··· 31 31 [second.path]: formatSummary(second.toSummary()), 32 32 } 33 33 34 - if (isV8Provider()) { 35 - expect(summary).toMatchInlineSnapshot(` 36 - { 37 - "<process-cwd>/fixtures/src/pre-bundle/first.ts": { 38 - "branches": "1/1 (100%)", 39 - "functions": "1/2 (50%)", 40 - "lines": "4/6 (66.66%)", 41 - "statements": "4/6 (66.66%)", 42 - }, 43 - "<process-cwd>/fixtures/src/pre-bundle/second.ts": { 44 - "branches": "1/1 (100%)", 45 - "functions": "1/2 (50%)", 46 - "lines": "4/6 (66.66%)", 47 - "statements": "4/6 (66.66%)", 48 - }, 49 - } 50 - `) 51 - } 52 - else { 53 - expect(summary).toMatchInlineSnapshot(` 54 - { 55 - "<process-cwd>/fixtures/src/pre-bundle/first.ts": { 56 - "branches": "0/0 (100%)", 57 - "functions": "1/2 (50%)", 58 - "lines": "1/2 (50%)", 59 - "statements": "1/2 (50%)", 60 - }, 61 - "<process-cwd>/fixtures/src/pre-bundle/second.ts": { 62 - "branches": "0/0 (100%)", 63 - "functions": "1/2 (50%)", 64 - "lines": "1/2 (50%)", 65 - "statements": "1/2 (50%)", 66 - }, 67 - } 68 - `) 69 - } 34 + expect(summary).toMatchInlineSnapshot(` 35 + { 36 + "<process-cwd>/fixtures/src/pre-bundle/first.ts": { 37 + "branches": "0/0 (100%)", 38 + "functions": "1/2 (50%)", 39 + "lines": "1/2 (50%)", 40 + "statements": "1/2 (50%)", 41 + }, 42 + "<process-cwd>/fixtures/src/pre-bundle/second.ts": { 43 + "branches": "0/0 (100%)", 44 + "functions": "1/2 (50%)", 45 + "lines": "1/2 (50%)", 46 + "statements": "1/2 (50%)", 47 + }, 48 + } 49 + `) 70 50 }) 71 51 72 52 coverageTest('run bundled sources', () => {
+17 -37
test/coverage-test/test/changed.test.ts
··· 1 1 import { readFileSync, rmSync, writeFileSync } from 'node:fs' 2 2 import { resolve } from 'node:path' 3 3 import { beforeAll, expect } from 'vitest' 4 - import { isV8Provider, readCoverageMap, runVitest, test } from '../utils' 4 + import { readCoverageMap, runVitest, test } from '../utils' 5 5 6 6 // Note that this test may fail if you have new files in "vitest/test/coverage/src" 7 7 // and have not yet committed those ··· 51 51 const uncoveredFile = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/new-uncovered-file.ts') 52 52 const changedFile = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/file-to-change.ts') 53 53 54 - if (isV8Provider()) { 55 - expect([uncoveredFile, changedFile]).toMatchInlineSnapshot(` 56 - { 57 - "<process-cwd>/fixtures/src/file-to-change.ts": { 58 - "branches": "1/1 (100%)", 59 - "functions": "1/2 (50%)", 60 - "lines": "4/6 (66.66%)", 61 - "statements": "4/6 (66.66%)", 62 - }, 63 - "<process-cwd>/fixtures/src/new-uncovered-file.ts": { 64 - "branches": "1/1 (100%)", 65 - "functions": "1/1 (100%)", 66 - "lines": "0/3 (0%)", 67 - "statements": "0/3 (0%)", 68 - }, 69 - } 70 - `) 71 - } 72 - else { 73 - expect([uncoveredFile, changedFile]).toMatchInlineSnapshot(` 74 - { 75 - "<process-cwd>/fixtures/src/file-to-change.ts": { 76 - "branches": "0/0 (100%)", 77 - "functions": "1/2 (50%)", 78 - "lines": "1/2 (50%)", 79 - "statements": "1/2 (50%)", 80 - }, 81 - "<process-cwd>/fixtures/src/new-uncovered-file.ts": { 82 - "branches": "0/0 (100%)", 83 - "functions": "0/1 (0%)", 84 - "lines": "0/1 (0%)", 85 - "statements": "0/1 (0%)", 86 - }, 87 - } 88 - `) 89 - } 54 + expect([uncoveredFile, changedFile]).toMatchInlineSnapshot(` 55 + { 56 + "<process-cwd>/fixtures/src/file-to-change.ts": { 57 + "branches": "0/0 (100%)", 58 + "functions": "1/2 (50%)", 59 + "lines": "1/2 (50%)", 60 + "statements": "1/2 (50%)", 61 + }, 62 + "<process-cwd>/fixtures/src/new-uncovered-file.ts": { 63 + "branches": "0/0 (100%)", 64 + "functions": "0/1 (0%)", 65 + "lines": "0/1 (0%)", 66 + "statements": "0/1 (0%)", 67 + }, 68 + } 69 + `) 90 70 }, SKIP)
-17
test/coverage-test/test/configuration-options.test-d.ts
··· 72 72 }) 73 73 }) 74 74 75 - test('provider specific options, v8', () => { 76 - assertType<Coverage>({ 77 - provider: 'v8', 78 - experimentalAstAwareRemapping: true, 79 - }) 80 - }) 81 - 82 - test('provider specific options, istanbul', () => { 83 - assertType<Coverage>({ 84 - provider: 'istanbul', 85 - ignoreClassMethods: ['string'], 86 - 87 - // @ts-expect-error -- v8 specific error 88 - experimentalAstAwareRemapping: true, 89 - }) 90 - }) 91 - 92 75 test('provider specific options, custom', () => { 93 76 assertType<Coverage>({ 94 77 provider: 'custom',
+3 -10
test/coverage-test/test/decorators.test.ts
··· 1 1 import { expect } from 'vitest' 2 2 import { DecoratorsTester } from '../fixtures/src/decorators' 3 - import { coverageTest, isV8Provider, normalizeURL, readCoverageMap, runVitest, test } from '../utils' 3 + import { coverageTest, normalizeURL, readCoverageMap, runVitest, test } from '../utils' 4 4 5 5 test('decorators generated metadata is ignored', async () => { 6 6 await runVitest({ ··· 14 14 const lineCoverage = fileCoverage.getLineCoverage() 15 15 const branchCoverage = fileCoverage.getBranchCoverageByLine() 16 16 17 - // Decorator should not be uncovered - on V8 this is marked as covered, on Istanbul it's excluded from report 18 - if (isV8Provider()) { 19 - expect(lineCoverage['4']).toBe(1) 20 - expect(branchCoverage['4'].coverage).toBe(100) 21 - } 22 - else { 23 - expect(lineCoverage['4']).toBeUndefined() 24 - expect(branchCoverage['4']).toBeUndefined() 25 - } 17 + expect(lineCoverage['4']).toBeUndefined() 18 + expect(branchCoverage['4']).toBeUndefined() 26 19 27 20 // Covered branch should be marked correctly 28 21 expect(lineCoverage['7']).toBe(1)
-184
test/coverage-test/test/empty-lines.v8.test.ts
··· 1 - import { beforeAll, expect } from 'vitest' 2 - import { add } from '../fixtures/src/empty-lines' 3 - import { coverageTest, describe, normalizeURL, readCoverageMap, runVitest, test } from '../utils' 4 - 5 - type CoveredLine = 1 6 - type UncoveredLine = 0 7 - type IgnoredLine = undefined 8 - 9 - // Key is 1-based line number 10 - type LineCoverage = Record<number, CoveredLine | UncoveredLine | IgnoredLine> 11 - 12 - describe('include empty lines', () => { 13 - let coveredFileLines: LineCoverage 14 - let uncoveredFileLines: LineCoverage 15 - 16 - beforeAll(async () => { 17 - await runVitest({ 18 - include: [normalizeURL(import.meta.url)], 19 - coverage: { 20 - reporter: 'json', 21 - ignoreEmptyLines: false, 22 - include: [ 23 - '**/fixtures/src/empty-lines.ts', 24 - '**/fixtures/src/untested-file.ts', 25 - '**/fixtures/src/types-only.ts', 26 - ], 27 - }, 28 - }) 29 - 30 - ;({ coveredFileLines, uncoveredFileLines } = await readCoverage()) 31 - }) 32 - 33 - test('lines are included', async () => { 34 - for (const line of range(29)) { 35 - expect(coveredFileLines[line], `Line #${line}`).not.toBe(undefined) 36 - expect(coveredFileLines[line], `Line #${line}`).toBeTypeOf('number') 37 - } 38 - 39 - for (const lines of [range(37), range(4, { base: 44 })]) { 40 - for (const line of lines) { 41 - expect(uncoveredFileLines[line], `Line #${line}`).not.toBe(undefined) 42 - expect(uncoveredFileLines[line], `Line #${line}`).toBeTypeOf('number') 43 - } 44 - } 45 - }) 46 - 47 - test('lines with ignore hints are ignored', () => { 48 - for (const line of range(6, { base: 38 })) { 49 - expect(uncoveredFileLines[line], `Line #${line}`).toBe(undefined) 50 - } 51 - }) 52 - }) 53 - 54 - describe('ignore empty lines', () => { 55 - let coveredFileLines: LineCoverage 56 - let uncoveredFileLines: LineCoverage 57 - let typesOnlyFileLines: LineCoverage 58 - 59 - beforeAll(async () => { 60 - await runVitest({ 61 - include: [normalizeURL(import.meta.url)], 62 - coverage: { 63 - reporter: 'json', 64 - include: [ 65 - '**/fixtures/src/empty-lines.ts', 66 - '**/fixtures/src/untested-file.ts', 67 - '**/fixtures/src/types-only.ts', 68 - ], 69 - }, 70 - }) 71 - 72 - ;({ coveredFileLines, uncoveredFileLines, typesOnlyFileLines } = await readCoverage()) 73 - }) 74 - 75 - test('file containing only types has no uncovered lines', () => { 76 - expect(typesOnlyFileLines[1]).toBe(undefined) 77 - expect(typesOnlyFileLines[2]).toBe(undefined) 78 - expect(typesOnlyFileLines[3]).toBe(undefined) 79 - }) 80 - 81 - test('empty lines are ignored', async () => { 82 - expect(coveredFileLines[12]).toBe(undefined) 83 - expect(coveredFileLines[14]).toBe(undefined) 84 - expect(coveredFileLines[19]).toBe(undefined) 85 - expect(coveredFileLines[27]).toBe(undefined) 86 - expect(coveredFileLines[30]).toBe(undefined) 87 - 88 - expect(uncoveredFileLines[5]).toBe(undefined) 89 - expect(uncoveredFileLines[7]).toBe(undefined) 90 - }) 91 - 92 - test('comments are ignored', async () => { 93 - expect(coveredFileLines[1]).toBe(undefined) 94 - expect(coveredFileLines[3]).toBe(undefined) 95 - expect(coveredFileLines[4]).toBe(undefined) 96 - expect(coveredFileLines[5]).toBe(undefined) 97 - expect(coveredFileLines[6]).toBe(undefined) 98 - expect(coveredFileLines[7]).toBe(undefined) 99 - expect(coveredFileLines[9]).toBe(undefined) 100 - expect(coveredFileLines[16]).toBe(undefined) 101 - 102 - expect(uncoveredFileLines[1]).toBe(undefined) 103 - expect(uncoveredFileLines[2]).toBe(undefined) 104 - expect(uncoveredFileLines[3]).toBe(undefined) 105 - expect(uncoveredFileLines[4]).toBe(undefined) 106 - expect(uncoveredFileLines[6]).toBe(undefined) 107 - expect(uncoveredFileLines[13]).toBe(undefined) 108 - expect(uncoveredFileLines[20]).toBe(undefined) 109 - expect(uncoveredFileLines[34]).toBe(undefined) 110 - expect(uncoveredFileLines[45]).toBe(undefined) 111 - }) 112 - 113 - test('ignore hints are ignored', () => { 114 - expect(uncoveredFileLines[38]).toBe(undefined) 115 - expect(uncoveredFileLines[39]).toBe(undefined) 116 - expect(uncoveredFileLines[40]).toBe(undefined) 117 - expect(uncoveredFileLines[41]).toBe(undefined) 118 - expect(uncoveredFileLines[42]).toBe(undefined) 119 - expect(uncoveredFileLines[43]).toBe(undefined) 120 - }) 121 - 122 - test('typescript types are ignored', () => { 123 - expect(coveredFileLines[13]).toBe(undefined) 124 - expect(coveredFileLines[20]).toBe(undefined) 125 - expect(coveredFileLines[21]).toBe(undefined) 126 - expect(coveredFileLines[22]).toBe(undefined) 127 - expect(coveredFileLines[23]).toBe(undefined) 128 - expect(coveredFileLines[24]).toBe(undefined) 129 - expect(coveredFileLines[25]).toBe(undefined) 130 - expect(coveredFileLines[26]).toBe(undefined) 131 - 132 - expect(uncoveredFileLines[17]).toBe(undefined) 133 - expect(uncoveredFileLines[25]).toBe(undefined) 134 - expect(uncoveredFileLines[26]).toBe(undefined) 135 - expect(uncoveredFileLines[27]).toBe(undefined) 136 - expect(uncoveredFileLines[28]).toBe(undefined) 137 - expect(uncoveredFileLines[29]).toBe(undefined) 138 - expect(uncoveredFileLines[30]).toBe(undefined) 139 - expect(uncoveredFileLines[31]).toBe(undefined) 140 - }) 141 - 142 - test('runtime code is not ignored', () => { 143 - // Covered 144 - expect(coveredFileLines[2]).toBe(1) 145 - expect(coveredFileLines[8]).toBe(1) 146 - expect(coveredFileLines[15]).toBe(1) 147 - expect(coveredFileLines[28]).toBe(1) 148 - 149 - // Uncovered 150 - expect(coveredFileLines[10]).toBe(0) 151 - expect(coveredFileLines[17]).toBe(0) 152 - 153 - // Uncovered 154 - expect(uncoveredFileLines[8]).toBe(0) 155 - expect(uncoveredFileLines[9]).toBe(0) 156 - expect(uncoveredFileLines[10]).toBe(0) 157 - expect(uncoveredFileLines[12]).toBe(0) 158 - expect(uncoveredFileLines[14]).toBe(0) 159 - expect(uncoveredFileLines[19]).toBe(0) 160 - expect(uncoveredFileLines[21]).toBe(0) 161 - expect(uncoveredFileLines[24]).toBe(0) 162 - expect(uncoveredFileLines[33]).toBe(0) 163 - expect(uncoveredFileLines[35]).toBe(0) 164 - expect(uncoveredFileLines[46]).toBe(0) 165 - }) 166 - }) 167 - 168 - coverageTest('cover some lines', () => { 169 - expect(add(10, 20)).toBe(30) 170 - }) 171 - 172 - async function readCoverage() { 173 - const coverageMap = await readCoverageMap() 174 - 175 - const coveredFileLines = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/empty-lines.ts').getLineCoverage() as LineCoverage 176 - const uncoveredFileLines = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/untested-file.ts').getLineCoverage() as LineCoverage 177 - const typesOnlyFileLines = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/types-only.ts').getLineCoverage() as LineCoverage 178 - 179 - return { coveredFileLines, uncoveredFileLines, typesOnlyFileLines } 180 - } 181 - 182 - function range(count: number, options: { base: number } = { base: 1 }) { 183 - return Array.from({ length: count }).fill(0).map((_, i) => options.base + i) 184 - }
+2 -2
test/coverage-test/test/file-outside-vite.test.ts
··· 1 1 import { createRequire } from 'node:module' 2 2 import { expect } from 'vitest' 3 - import { coverageTest, isExperimentalV8Provider, isV8Provider, normalizeURL, readCoverageMap, runVitest, test } from '../utils' 3 + import { coverageTest, isV8Provider, normalizeURL, readCoverageMap, runVitest, test } from '../utils' 4 4 5 5 test('does not crash when file outside Vite is loaded (#5639)', async () => { 6 6 await runVitest({ ··· 11 11 const coverageMap = await readCoverageMap() 12 12 const fileCoverage = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/load-outside-vite.cjs') 13 13 14 - if (isV8Provider() || isExperimentalV8Provider()) { 14 + if (isV8Provider()) { 15 15 expect(fileCoverage).toMatchInlineSnapshot(` 16 16 { 17 17 "branches": "0/0 (100%)",
+1 -5
test/coverage-test/test/ignore-hints.test.ts
··· 4 4 */ 5 5 6 6 import { expect } from 'vitest' 7 - import { isExperimentalV8Provider, isV8Provider, readCoverageMap, runVitest, test } from '../utils' 7 + import { isV8Provider, readCoverageMap, runVitest, test } from '../utils' 8 8 9 9 test('ignore hints work', async () => { 10 10 await runVitest({ ··· 20 20 expect(lines[12]).toBeGreaterThanOrEqual(1) 21 21 22 22 if (isV8Provider()) { 23 - expect(lines[15]).toBeUndefined() 24 - expect(lines[18]).toBeGreaterThanOrEqual(1) 25 - } 26 - else if (isExperimentalV8Provider()) { 27 23 expect(lines[15]).toBeUndefined() 28 24 expect(lines[18]).toBeUndefined() 29 25 }
+9 -26
test/coverage-test/test/in-source.test.ts
··· 1 1 import { expect } from 'vitest' 2 - import { isV8Provider, readCoverageMap, runVitest, test } from '../utils' 2 + import { readCoverageMap, runVitest, test } from '../utils' 3 3 4 4 test('in-source tests work', async () => { 5 5 const { stdout } = await runVitest({ ··· 25 25 // If-branch is not taken - makes sure source maps are correct in in-source testing too 26 26 expect(fileCoverage.getUncoveredLines()).toContain('5') 27 27 28 - if (isV8Provider()) { 29 - expect(fileCoverage).toMatchInlineSnapshot(` 30 - { 31 - "branches": "2/4 (50%)", 32 - "functions": "2/2 (100%)", 33 - "lines": "10/12 (83.33%)", 34 - "statements": "10/12 (83.33%)", 35 - } 36 - `) 37 - } 38 - else { 39 - expect(fileCoverage).toMatchInlineSnapshot(` 40 - { 41 - "branches": "2/4 (50%)", 42 - "functions": "1/1 (100%)", 43 - "lines": "2/3 (66.66%)", 44 - "statements": "2/3 (66.66%)", 45 - } 46 - `) 47 - } 48 - 49 - // v8-to-istanbul cannot exclude whole if-block 50 - if (isV8Provider()) { 51 - return 52 - } 28 + expect(fileCoverage).toMatchInlineSnapshot(` 29 + { 30 + "branches": "2/4 (50%)", 31 + "functions": "1/1 (100%)", 32 + "lines": "2/3 (66.66%)", 33 + "statements": "2/3 (66.66%)", 34 + } 35 + `) 53 36 54 37 // The "customNamedTestFunction" should be excluded by auto-generated ignore hints 55 38 expect(functions).toMatchInlineSnapshot(`
+14 -33
test/coverage-test/test/isolation.test.ts
··· 1 1 import type { TestSpecification } from 'vitest/node' 2 2 import { expect, test } from 'vitest' 3 - import { formatSummary, isV8Provider, readCoverageMap, runVitest } from '../utils' 3 + import { formatSummary, readCoverageMap, runVitest } from '../utils' 4 4 5 5 const pools = ['forks'] 6 6 ··· 39 39 [math.path]: formatSummary(math.toSummary()), 40 40 } 41 41 42 - if (isV8Provider()) { 43 - expect(summary).toStrictEqual({ 44 - '<process-cwd>/fixtures/src/branch.ts': { 45 - branches: '3/3 (100%)', 46 - functions: '1/1 (100%)', 47 - lines: '6/6 (100%)', 48 - statements: '6/6 (100%)', 49 - }, 50 - '<process-cwd>/fixtures/src/math.ts': { 51 - branches: '4/4 (100%)', 52 - functions: '4/4 (100%)', 53 - lines: '12/12 (100%)', 54 - statements: '12/12 (100%)', 55 - }, 56 - }) 57 - } 58 - else { 59 - expect(summary).toStrictEqual({ 60 - '<process-cwd>/fixtures/src/branch.ts': { 61 - branches: '2/2 (100%)', 62 - functions: '1/1 (100%)', 63 - lines: '4/4 (100%)', 64 - statements: '4/4 (100%)', 65 - }, 66 - '<process-cwd>/fixtures/src/math.ts': { 67 - branches: '0/0 (100%)', 68 - functions: '4/4 (100%)', 69 - lines: '4/4 (100%)', 70 - statements: '4/4 (100%)', 71 - }, 42 + expect(summary).toStrictEqual({ 43 + '<process-cwd>/fixtures/src/branch.ts': { 44 + branches: '2/2 (100%)', 45 + functions: '1/1 (100%)', 46 + lines: '4/4 (100%)', 47 + statements: '4/4 (100%)', 72 48 }, 73 - ) 74 - } 49 + '<process-cwd>/fixtures/src/math.ts': { 50 + branches: '0/0 (100%)', 51 + functions: '4/4 (100%)', 52 + lines: '4/4 (100%)', 53 + statements: '4/4 (100%)', 54 + }, 55 + }) 75 56 }) 76 57 } 77 58 }
+10 -23
test/coverage-test/test/on-failure.test.ts
··· 1 1 import { expect } from 'vitest' 2 2 import { sum } from '../fixtures/src/math' 3 - import { captureStdout, coverageTest, isV8Provider, normalizeURL, runVitest, test } from '../utils' 3 + import { captureStdout, coverageTest, normalizeURL, runVitest, test } from '../utils' 4 4 5 5 test('report is not generated when tests fail', async () => { 6 6 const stdout = captureStdout() ··· 29 29 }, 30 30 }, { throwOnError: false }) 31 31 32 - if (isV8Provider()) { 33 - expect(stdout()).toMatchInlineSnapshot(` 34 - "----------|---------|----------|---------|---------|------------------- 35 - File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 36 - ----------|---------|----------|---------|---------|------------------- 37 - All files | 50 | 100 | 25 | 50 | 38 - math.ts | 50 | 100 | 25 | 50 | 6-7,10-11,14-15 39 - ----------|---------|----------|---------|---------|------------------- 40 - " 41 - `) 42 - } 43 - else { 44 - expect(stdout()).toMatchInlineSnapshot(` 45 - "----------|---------|----------|---------|---------|------------------- 46 - File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 47 - ----------|---------|----------|---------|---------|------------------- 48 - All files | 25 | 100 | 25 | 25 | 49 - math.ts | 25 | 100 | 25 | 25 | 6-14 50 - ----------|---------|----------|---------|---------|------------------- 51 - " 52 - `) 53 - } 32 + expect(stdout()).toMatchInlineSnapshot(` 33 + "----------|---------|----------|---------|---------|------------------- 34 + File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 35 + ----------|---------|----------|---------|---------|------------------- 36 + All files | 25 | 100 | 25 | 25 | 37 + math.ts | 25 | 100 | 25 | 25 | 6-14 38 + ----------|---------|----------|---------|---------|------------------- 39 + " 40 + `) 54 41 55 42 expect(exitCode).toBe(1) 56 43 })
+9 -21
test/coverage-test/test/pre-transpiled-source.test.ts
··· 1 1 import libCoverage from 'istanbul-lib-coverage' 2 2 import { expect } from 'vitest' 3 - import { isV8Provider, readCoverageJson, runVitest, test } from '../utils' 3 + import { readCoverageJson, runVitest, test } from '../utils' 4 4 5 5 test('pre-transpiled code with source maps to original (#5341)', async () => { 6 6 await runVitest({ ··· 22 22 23 23 const fileCoverage = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/pre-transpiled/original.ts') 24 24 25 - if (isV8Provider()) { 26 - expect(fileCoverage).toMatchInlineSnapshot(` 27 - { 28 - "branches": "2/4 (50%)", 29 - "functions": "2/2 (100%)", 30 - "lines": "11/17 (64.7%)", 31 - "statements": "11/17 (64.7%)", 32 - } 33 - `) 34 - } 35 - else { 36 - expect(fileCoverage).toMatchInlineSnapshot(` 37 - { 38 - "branches": "3/6 (50%)", 39 - "functions": "2/2 (100%)", 40 - "lines": "6/8 (75%)", 41 - "statements": "6/8 (75%)", 42 - } 43 - `) 44 - } 25 + expect(fileCoverage).toMatchInlineSnapshot(` 26 + { 27 + "branches": "3/6 (50%)", 28 + "functions": "2/2 (100%)", 29 + "lines": "6/8 (75%)", 30 + "statements": "6/8 (75%)", 31 + } 32 + `) 45 33 })
+23 -49
test/coverage-test/test/results-snapshot.test.ts
··· 1 1 import { expect } from 'vitest' 2 - import { isV8Provider, readCoverageMap, runVitest, test } from '../utils' 2 + import { readCoverageMap, runVitest, test } from '../utils' 3 3 4 4 test('coverage results matches snapshot', async () => { 5 5 await runVitest({ ··· 17 17 const coverageMap = await readCoverageMap() 18 18 const fileCoverages = coverageMap.files().map(file => coverageMap.fileCoverageFor(file)) 19 19 20 - if (isV8Provider()) { 21 - expect(fileCoverages).toMatchInlineSnapshot(` 22 - { 23 - "<process-cwd>/fixtures/src/even.ts": { 24 - "branches": "1/1 (100%)", 25 - "functions": "1/2 (50%)", 26 - "lines": "4/6 (66.66%)", 27 - "statements": "4/6 (66.66%)", 28 - }, 29 - "<process-cwd>/fixtures/src/math.ts": { 30 - "branches": "1/1 (100%)", 31 - "functions": "1/4 (25%)", 32 - "lines": "6/12 (50%)", 33 - "statements": "6/12 (50%)", 34 - }, 35 - "<process-cwd>/fixtures/src/untested-file.ts": { 36 - "branches": "1/1 (100%)", 37 - "functions": "1/1 (100%)", 38 - "lines": "0/15 (0%)", 39 - "statements": "0/15 (0%)", 40 - }, 41 - } 42 - `) 43 - } 44 - else { 45 - expect(fileCoverages).toMatchInlineSnapshot(` 46 - { 47 - "<process-cwd>/fixtures/src/even.ts": { 48 - "branches": "0/0 (100%)", 49 - "functions": "1/2 (50%)", 50 - "lines": "1/2 (50%)", 51 - "statements": "1/2 (50%)", 52 - }, 53 - "<process-cwd>/fixtures/src/math.ts": { 54 - "branches": "0/0 (100%)", 55 - "functions": "1/4 (25%)", 56 - "lines": "1/4 (25%)", 57 - "statements": "1/4 (25%)", 58 - }, 59 - "<process-cwd>/fixtures/src/untested-file.ts": { 60 - "branches": "0/4 (0%)", 61 - "functions": "0/4 (0%)", 62 - "lines": "0/8 (0%)", 63 - "statements": "0/8 (0%)", 64 - }, 65 - } 66 - `) 67 - } 20 + expect(fileCoverages).toMatchInlineSnapshot(` 21 + { 22 + "<process-cwd>/fixtures/src/even.ts": { 23 + "branches": "0/0 (100%)", 24 + "functions": "1/2 (50%)", 25 + "lines": "1/2 (50%)", 26 + "statements": "1/2 (50%)", 27 + }, 28 + "<process-cwd>/fixtures/src/math.ts": { 29 + "branches": "0/0 (100%)", 30 + "functions": "1/4 (25%)", 31 + "lines": "1/4 (25%)", 32 + "statements": "1/4 (25%)", 33 + }, 34 + "<process-cwd>/fixtures/src/untested-file.ts": { 35 + "branches": "0/4 (0%)", 36 + "functions": "0/4 (0%)", 37 + "lines": "0/8 (0%)", 38 + "statements": "0/8 (0%)", 39 + }, 40 + } 41 + `) 68 42 })
+9 -21
test/coverage-test/test/setup-files.test.ts
··· 1 1 import { resolve } from 'node:path' 2 2 import { expect } from 'vitest' 3 - import { isV8Provider, readCoverageMap, runVitest, test } from '../utils' 3 + import { readCoverageMap, runVitest, test } from '../utils' 4 4 5 5 test('tests with multiple suites are covered (#3514)', async () => { 6 6 const { stdout } = await runVitest({ ··· 29 29 // Some valid coverage should be reported 30 30 const fileCoverage = coverageMap.fileCoverageFor('<process-cwd>/fixtures/src/math.ts') 31 31 32 - if (isV8Provider()) { 33 - expect(fileCoverage).toMatchInlineSnapshot(` 34 - { 35 - "branches": "1/1 (100%)", 36 - "functions": "1/4 (25%)", 37 - "lines": "6/12 (50%)", 38 - "statements": "6/12 (50%)", 39 - } 40 - `) 41 - } 42 - else { 43 - expect(fileCoverage).toMatchInlineSnapshot(` 44 - { 45 - "branches": "0/0 (100%)", 46 - "functions": "1/4 (25%)", 47 - "lines": "1/4 (25%)", 48 - "statements": "1/4 (25%)", 49 - } 50 - `) 51 - } 32 + expect(fileCoverage).toMatchInlineSnapshot(` 33 + { 34 + "branches": "0/0 (100%)", 35 + "functions": "1/4 (25%)", 36 + "lines": "1/4 (25%)", 37 + "statements": "1/4 (25%)", 38 + } 39 + `) 52 40 })
+22 -52
test/coverage-test/test/threshold-auto-update.test.ts
··· 1 1 import { readFileSync, writeFileSync } from 'node:fs' 2 2 import { expect, onTestFinished } from 'vitest' 3 - import { isV8Provider, runVitest, test } from '../utils' 3 + import { runVitest, test } from '../utils' 4 4 5 5 const config = 'fixtures/configs/vitest.config.thresholds-auto-update.ts' 6 6 ··· 41 41 config, 42 42 }, { throwOnError: false }) 43 43 44 - if (isV8Provider()) { 45 - expect(readConfig()).toMatchInlineSnapshot(` 46 - "import { defineConfig } from 'vitest/config' 44 + expect(readConfig()).toMatchInlineSnapshot(` 45 + "import { defineConfig } from 'vitest/config' 47 46 48 - export default defineConfig({ 49 - test: { 50 - coverage: { 51 - thresholds: { 52 - autoUpdate: true, 47 + export default defineConfig({ 48 + test: { 49 + coverage: { 50 + thresholds: { 51 + autoUpdate: true, 53 52 54 - // Global ones 55 - lines: 55.55, 56 - functions: 33.33, 53 + // Global ones 54 + lines: 33.33, 55 + functions: 33.33, 56 + branches: 100, 57 + statements: -4, 58 + 59 + '**/src/math.ts': { 57 60 branches: 100, 58 - statements: -8, 59 - 60 - '**/src/math.ts': { 61 - branches: 100, 62 - functions: 25, 63 - lines: -6, 64 - statements: -6, 65 - } 61 + functions: 25, 62 + lines: -3, 63 + statements: -3, 66 64 } 67 65 } 68 - }, 69 - })" 70 - `) 71 - } 72 - else { 73 - expect(readConfig()).toMatchInlineSnapshot(` 74 - "import { defineConfig } from 'vitest/config' 75 - 76 - export default defineConfig({ 77 - test: { 78 - coverage: { 79 - thresholds: { 80 - autoUpdate: true, 81 - 82 - // Global ones 83 - lines: 33.33, 84 - functions: 33.33, 85 - branches: 100, 86 - statements: -4, 87 - 88 - '**/src/math.ts': { 89 - branches: 100, 90 - functions: 25, 91 - lines: -3, 92 - statements: -3, 93 - } 94 - } 95 - } 96 - }, 97 - })" 98 - `) 99 - } 66 + } 67 + }, 68 + })" 69 + `) 100 70 }) 101 71 102 72 function readConfig() {
+7 -16
test/coverage-test/test/threshold-failure.test.ts
··· 1 1 import { expect } from 'vitest' 2 2 import { sum } from '../fixtures/src/math' 3 - import { coverageTest, isV8Provider, normalizeURL, runVitest, test } from '../utils' 3 + import { coverageTest, normalizeURL, runVitest, test } from '../utils' 4 4 5 5 test('failing percentage thresholds', async () => { 6 6 const { exitCode, stderr } = await runVitest({ ··· 18 18 }, 19 19 }, { throwOnError: false }) 20 20 21 - const lines = isV8Provider() ? '50%' : '25%' 22 - const statements = isV8Provider() ? '50%' : '25%' 23 - 24 21 expect(exitCode).toBe(1) 25 - expect(stderr).toContain(`ERROR: Coverage for lines (${lines}) does not meet "**/fixtures/src/math.ts" threshold (100%)`) 26 - expect(stderr).toContain(`ERROR: Coverage for statements (${statements}) does not meet "**/fixtures/src/math.ts" threshold (100%)`) 22 + expect(stderr).toContain('ERROR: Coverage for lines (25%) does not meet "**/fixtures/src/math.ts" threshold (100%)') 23 + expect(stderr).toContain('ERROR: Coverage for statements (25%) does not meet "**/fixtures/src/math.ts" threshold (100%)') 27 24 expect(stderr).toContain('ERROR: Coverage for functions (25%) does not meet "**/fixtures/src/math.ts" threshold (100%)') 28 25 }) 29 26 ··· 36 33 '**/fixtures/src/math.ts': { 37 34 branches: -1, 38 35 functions: -2, 39 - lines: -5, 36 + lines: -2, 40 37 statements: -1, 41 38 }, 42 39 }, ··· 45 42 46 43 expect(exitCode).toBe(1) 47 44 48 - if (isV8Provider()) { 49 - expect(stderr).toContain('ERROR: Uncovered lines (6) exceed "**/fixtures/src/math.ts" threshold (5)') 50 - expect(stderr).toContain('ERROR: Uncovered functions (3) exceed "**/fixtures/src/math.ts" threshold (2)') 51 - expect(stderr).toContain('ERROR: Uncovered statements (6) exceed "**/fixtures/src/math.ts" threshold (1)') 52 - } 53 - else { 54 - expect(stderr).toContain('ERROR: Uncovered functions (3) exceed "**/fixtures/src/math.ts" threshold (2)') 55 - expect(stderr).toContain('ERROR: Uncovered statements (3) exceed "**/fixtures/src/math.ts" threshold (1)') 56 - } 45 + expect(stderr).toContain('ERROR: Uncovered functions (3) exceed "**/fixtures/src/math.ts" threshold (2)') 46 + expect(stderr).toContain('ERROR: Uncovered statements (3) exceed "**/fixtures/src/math.ts" threshold (1)') 47 + expect(stderr).toContain('ERROR: Uncovered lines (3) exceed "**/fixtures/src/math.ts" threshold (2)') 57 48 }) 58 49 59 50 coverageTest('cover some lines, but not too much', () => {
+9 -19
test/coverage-test/test/threshold-glob.test.ts
··· 1 1 import { expect } from 'vitest' 2 2 import { isEven, isOdd } from '../fixtures/src/even' 3 3 import { sum } from '../fixtures/src/math' 4 - import { coverageTest, isV8Provider, normalizeURL, runVitest, test } from '../utils' 4 + import { coverageTest, normalizeURL, runVitest, test } from '../utils' 5 5 6 6 test('threshold glob patterns count in global coverage', async () => { 7 7 await runVitest({ ··· 14 14 thresholds: { 15 15 'branches': 100, 16 16 'functions': 50, 17 - 'lines': isV8Provider() ? 66 : 50, 18 - 'statements': isV8Provider() ? 66 : 50, 17 + 'lines': 50, 18 + 'statements': 50, 19 19 20 20 '**/fixtures/src/even.ts': { 21 21 branches: 100, ··· 49 49 50 50 expect(exitCode).toBe(1) 51 51 52 - if (isV8Provider()) { 53 - expect(stderr).toMatchInlineSnapshot(` 54 - "ERROR: Coverage for lines (50%) does not meet "**/fixtures/src/math.ts" threshold (100%) 55 - ERROR: Coverage for functions (25%) does not meet "**/fixtures/src/math.ts" threshold (100%) 56 - ERROR: Coverage for statements (50%) does not meet "**/fixtures/src/math.ts" threshold (100%) 57 - " 58 - `) 59 - } 60 - else { 61 - expect(stderr).toMatchInlineSnapshot(` 62 - "ERROR: Coverage for lines (25%) does not meet "**/fixtures/src/math.ts" threshold (100%) 63 - ERROR: Coverage for functions (25%) does not meet "**/fixtures/src/math.ts" threshold (100%) 64 - ERROR: Coverage for statements (25%) does not meet "**/fixtures/src/math.ts" threshold (100%) 65 - " 66 - `) 67 - } 52 + expect(stderr).toMatchInlineSnapshot(` 53 + "ERROR: Coverage for lines (25%) does not meet "**/fixtures/src/math.ts" threshold (100%) 54 + ERROR: Coverage for functions (25%) does not meet "**/fixtures/src/math.ts" threshold (100%) 55 + ERROR: Coverage for statements (25%) does not meet "**/fixtures/src/math.ts" threshold (100%) 56 + " 57 + `) 68 58 }) 69 59 70 60 coverageTest('cover some lines, but not too much', () => {
+9 -32
test/coverage-test/test/vue.test.ts
··· 1 1 import { readdirSync } from 'node:fs' 2 2 import { resolve } from 'node:path' 3 3 import { beforeAll, expect } from 'vitest' 4 - import { rolldownVersion } from 'vitest/node' 5 - import { isV8Provider, readCoverageMap, runVitest, test } from '../utils' 4 + import { readCoverageMap, runVitest, test } from '../utils' 6 5 7 6 beforeAll(async () => { 8 7 await runVitest({ ··· 24 23 test('coverage results matches snapshot', async () => { 25 24 const coverageMap = await readCoverageMap() 26 25 27 - if (isV8Provider() && !rolldownVersion) { 28 - expect(coverageMap).toMatchInlineSnapshot(` 29 - { 30 - "branches": "5/7 (71.42%)", 31 - "functions": "3/5 (60%)", 32 - "lines": "36/45 (80%)", 33 - "statements": "36/45 (80%)", 34 - } 35 - `) 36 - } 37 - else if (isV8Provider() && rolldownVersion) { 38 - expect(coverageMap).toMatchInlineSnapshot(` 39 - { 40 - "branches": "7/9 (77.77%)", 41 - "functions": "4/6 (66.66%)", 42 - "lines": "36/45 (80%)", 43 - "statements": "36/45 (80%)", 44 - } 45 - `) 46 - } 47 - else { 48 - expect(coverageMap).toMatchInlineSnapshot(` 49 - { 50 - "branches": "6/8 (75%)", 51 - "functions": "5/7 (71.42%)", 52 - "lines": "13/16 (81.25%)", 53 - "statements": "14/17 (82.35%)", 54 - } 55 - `) 56 - } 26 + expect(coverageMap).toMatchInlineSnapshot(` 27 + { 28 + "branches": "6/8 (75%)", 29 + "functions": "5/7 (71.42%)", 30 + "lines": "13/16 (81.25%)", 31 + "statements": "14/17 (82.35%)", 32 + } 33 + `) 57 34 })
+17 -37
test/coverage-test/test/web-worker.test.ts
··· 1 1 import { expect } from 'vitest' 2 - import { formatSummary, isV8Provider, readCoverageMap, runVitest, test } from '../utils' 2 + import { formatSummary, readCoverageMap, runVitest, test } from '../utils' 3 3 4 4 test('web worker coverage is correct', async () => { 5 5 await runVitest({ ··· 28 28 } 29 29 30 30 // Check HTML report if these change unexpectedly 31 - if (isV8Provider()) { 32 - expect(summary).toMatchInlineSnapshot(` 33 - { 34 - "<process-cwd>/fixtures/src/worker-wrapper.ts": { 35 - "branches": "3/3 (100%)", 36 - "functions": "2/4 (50%)", 37 - "lines": "18/22 (81.81%)", 38 - "statements": "18/22 (81.81%)", 39 - }, 40 - "<process-cwd>/fixtures/src/worker.ts": { 41 - "branches": "2/4 (50%)", 42 - "functions": "2/3 (66.66%)", 43 - "lines": "11/19 (57.89%)", 44 - "statements": "11/19 (57.89%)", 45 - }, 46 - } 47 - `) 48 - } 49 - else { 50 - expect(summary).toMatchInlineSnapshot(` 51 - { 52 - "<process-cwd>/fixtures/src/worker-wrapper.ts": { 53 - "branches": "0/0 (100%)", 54 - "functions": "3/5 (60%)", 55 - "lines": "9/11 (81.81%)", 56 - "statements": "9/11 (81.81%)", 57 - }, 58 - "<process-cwd>/fixtures/src/worker.ts": { 59 - "branches": "2/4 (50%)", 60 - "functions": "2/3 (66.66%)", 61 - "lines": "7/12 (58.33%)", 62 - "statements": "7/12 (58.33%)", 63 - }, 64 - } 65 - `) 66 - } 31 + expect(summary).toMatchInlineSnapshot(` 32 + { 33 + "<process-cwd>/fixtures/src/worker-wrapper.ts": { 34 + "branches": "0/0 (100%)", 35 + "functions": "3/5 (60%)", 36 + "lines": "9/11 (81.81%)", 37 + "statements": "9/11 (81.81%)", 38 + }, 39 + "<process-cwd>/fixtures/src/worker.ts": { 40 + "branches": "2/4 (50%)", 41 + "functions": "2/3 (66.66%)", 42 + "lines": "7/12 (58.33%)", 43 + "statements": "7/12 (58.33%)", 44 + }, 45 + } 46 + `) 67 47 })
+35 -73
test/coverage-test/test/workspace.multi-transform.test.ts
··· 1 1 import { expect } from 'vitest' 2 - import { isV8Provider, readCoverageMap, runVitest, test } from '../utils' 2 + import { readCoverageMap, runVitest, test } from '../utils' 3 3 4 4 test('uncovered files that require custom transform', async () => { 5 5 await runVitest({ ··· 26 26 27 27 const fileCoverages = coverageMap.files().map(file => coverageMap.fileCoverageFor(file)) 28 28 29 - if (isV8Provider()) { 30 - expect(fileCoverages).toMatchInlineSnapshot(` 31 - { 32 - "<process-cwd>/fixtures/src/covered.custom-1": { 33 - "branches": "1/1 (100%)", 34 - "functions": "1/2 (50%)", 35 - "lines": "2/3 (66.66%)", 36 - "statements": "2/3 (66.66%)", 37 - }, 38 - "<process-cwd>/fixtures/src/math.ts": { 39 - "branches": "1/1 (100%)", 40 - "functions": "1/4 (25%)", 41 - "lines": "6/12 (50%)", 42 - "statements": "6/12 (50%)", 43 - }, 44 - "<process-cwd>/fixtures/src/uncovered.custom-1": { 45 - "branches": "0/1 (0%)", 46 - "functions": "0/1 (0%)", 47 - "lines": "0/2 (0%)", 48 - "statements": "0/2 (0%)", 49 - }, 50 - "<process-cwd>/fixtures/workspaces/custom-2/src/covered.custom-2": { 51 - "branches": "1/1 (100%)", 52 - "functions": "1/2 (50%)", 53 - "lines": "2/3 (66.66%)", 54 - "statements": "2/3 (66.66%)", 55 - }, 56 - "<process-cwd>/fixtures/workspaces/custom-2/src/uncovered.custom-2": { 57 - "branches": "0/1 (0%)", 58 - "functions": "0/1 (0%)", 59 - "lines": "0/2 (0%)", 60 - "statements": "0/2 (0%)", 61 - }, 62 - } 63 - `) 64 - } 65 - else { 66 - expect(fileCoverages).toMatchInlineSnapshot(` 67 - { 68 - "<process-cwd>/fixtures/src/covered.custom-1": { 69 - "branches": "0/0 (100%)", 70 - "functions": "1/2 (50%)", 71 - "lines": "1/2 (50%)", 72 - "statements": "1/2 (50%)", 73 - }, 74 - "<process-cwd>/fixtures/src/math.ts": { 75 - "branches": "0/0 (100%)", 76 - "functions": "1/4 (25%)", 77 - "lines": "1/4 (25%)", 78 - "statements": "1/4 (25%)", 79 - }, 80 - "<process-cwd>/fixtures/src/uncovered.custom-1": { 81 - "branches": "0/0 (100%)", 82 - "functions": "0/1 (0%)", 83 - "lines": "0/1 (0%)", 84 - "statements": "0/1 (0%)", 85 - }, 86 - "<process-cwd>/fixtures/workspaces/custom-2/src/covered.custom-2": { 87 - "branches": "0/0 (100%)", 88 - "functions": "1/2 (50%)", 89 - "lines": "1/2 (50%)", 90 - "statements": "1/2 (50%)", 91 - }, 92 - "<process-cwd>/fixtures/workspaces/custom-2/src/uncovered.custom-2": { 93 - "branches": "0/0 (100%)", 94 - "functions": "0/1 (0%)", 95 - "lines": "0/1 (0%)", 96 - "statements": "0/1 (0%)", 97 - }, 98 - } 99 - `) 100 - } 29 + expect(fileCoverages).toMatchInlineSnapshot(` 30 + { 31 + "<process-cwd>/fixtures/src/covered.custom-1": { 32 + "branches": "0/0 (100%)", 33 + "functions": "1/2 (50%)", 34 + "lines": "1/2 (50%)", 35 + "statements": "1/2 (50%)", 36 + }, 37 + "<process-cwd>/fixtures/src/math.ts": { 38 + "branches": "0/0 (100%)", 39 + "functions": "1/4 (25%)", 40 + "lines": "1/4 (25%)", 41 + "statements": "1/4 (25%)", 42 + }, 43 + "<process-cwd>/fixtures/src/uncovered.custom-1": { 44 + "branches": "0/0 (100%)", 45 + "functions": "0/1 (0%)", 46 + "lines": "0/1 (0%)", 47 + "statements": "0/1 (0%)", 48 + }, 49 + "<process-cwd>/fixtures/workspaces/custom-2/src/covered.custom-2": { 50 + "branches": "0/0 (100%)", 51 + "functions": "1/2 (50%)", 52 + "lines": "1/2 (50%)", 53 + "statements": "1/2 (50%)", 54 + }, 55 + "<process-cwd>/fixtures/workspaces/custom-2/src/uncovered.custom-2": { 56 + "branches": "0/0 (100%)", 57 + "functions": "0/1 (0%)", 58 + "lines": "0/1 (0%)", 59 + "statements": "0/1 (0%)", 60 + }, 61 + } 62 + `) 101 63 })
+2 -2
test/config/fixtures/coverage-test/example.ts
··· 1 - export function uncovered() { 2 - return 0 1 + export function uncovered(condition: boolean) { 2 + return condition ? 1 : 0 3 3 }
+1 -1
test/coverage-test/fixtures/src/ignore-hints.ts
··· 1 - /* v8 ignore next 4 */ 1 + // padding 2 2 /* istanbul ignore next -- @preserve */ 3 3 export function first() { 4 4 return "First"
+2 -22
packages/vitest/src/node/types/coverage.ts
··· 248 248 * Defaults to `Math.min(20, os.availableParallelism?.() ?? os.cpus().length)` 249 249 */ 250 250 processingConcurrency?: number 251 - } 252 251 253 - export interface CoverageIstanbulOptions extends BaseCoverageOptions { 254 252 /** 255 253 * Set to array of class method names to ignore for coverage 256 254 * ··· 259 257 ignoreClassMethods?: string[] 260 258 } 261 259 262 - export interface CoverageV8Options extends BaseCoverageOptions { 263 - /** 264 - * Ignore empty lines, comments and other non-runtime code, e.g. Typescript types 265 - * - Requires `experimentalAstAwareRemapping: false` 266 - */ 267 - ignoreEmptyLines?: boolean 260 + export interface CoverageIstanbulOptions extends BaseCoverageOptions {} 268 261 269 - /** 270 - * Remap coverage with experimental AST based analysis 271 - * - Provides more accurate results compared to default mode 272 - */ 273 - experimentalAstAwareRemapping?: boolean 274 - 275 - /** 276 - * Set to array of class method names to ignore for coverage. 277 - * - Requires `experimentalAstAwareRemapping: true` 278 - * 279 - * @default [] 280 - */ 281 - ignoreClassMethods?: string[] 282 - } 262 + export interface CoverageV8Options extends BaseCoverageOptions {} 283 263 284 264 export interface CustomProviderOptions 285 265 extends Pick<BaseCoverageOptions, FieldsWithDefaultValues> {