From 10b89713d2593ce562676455cc29e18a27fd0cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Fri, 5 Apr 2024 13:59:56 +0300 Subject: [PATCH] feat(coverage): v8 to ignore empty lines, comments, types (#5457) --- docs/config/index.md | 46 ++ docs/guide/coverage.md | 5 + package.json | 3 +- packages/coverage-v8/package.json | 4 +- packages/coverage-v8/src/provider.ts | 12 +- packages/vitest/src/defaults.ts | 1 + packages/vitest/src/types/coverage.ts | 7 +- patches/v8-to-istanbul@9.2.0.patch | 156 +++++ pnpm-lock.yaml | 17 +- .../istanbul.report.test.ts.snap | 338 ++++++++-- .../__snapshots__/v8.report.test.ts.snap | 612 +++++++++++++++--- .../coverage-report-tests/changed.test.ts | 2 + .../coverage-report-tests/empty-lines.test.ts | 108 ++++ .../option-tests/empty-lines.test.ts | 7 + test/coverage-test/src/empty-lines.ts | 29 + test/coverage-test/src/untested-file.ts | 16 +- test/coverage-test/testing-options.mjs | 20 + 17 files changed, 1227 insertions(+), 156 deletions(-) create mode 100644 patches/v8-to-istanbul@9.2.0.patch create mode 100644 test/coverage-test/coverage-report-tests/empty-lines.test.ts create mode 100644 test/coverage-test/option-tests/empty-lines.test.ts create mode 100644 test/coverage-test/src/empty-lines.ts diff --git a/docs/config/index.md b/docs/config/index.md index c0d73e06e1ae..9b79bef818b7 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -1103,6 +1103,20 @@ List of files included in coverage as glob patterns List of files excluded from coverage as glob patterns. +This option overrides all default options. Extend the default options when adding new patterns to ignore: + +```ts +import { coverageConfigDefaults, defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + coverage: { + exclude: ['**/custom-pattern/**', ...coverageConfigDefaults.exclude] + }, + }, +}) +``` + #### coverage.all - **Type:** `boolean` @@ -1320,6 +1334,38 @@ Sets thresholds for files matching the glob pattern. } ``` +#### coverage.ignoreEmptyLines + +- **Type:** `boolean` +- **Default:** `false` +- **Available for providers:** `'v8'` +- **CLI:** `--coverage.ignoreEmptyLines=` + +Ignore empty lines, comments and other non-runtime code, e.g. Typescript types. + +This option works only if the used compiler removes comments and other non-runtime code from the transpiled code. +By default Vite uses ESBuild which removes comments and Typescript types from `.ts`, `.tsx` and `.jsx` files. + +If you want to apply ESBuild to other files as well, define them in [`esbuild` options](https://vitejs.dev/config/shared-options.html#esbuild): + +```ts +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + esbuild: { + // Transpile all files with ESBuild to remove comments from code coverage. + // Required for `test.coverage.ignoreEmptyLines` to work: + include: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.ts', '**/*.tsx'], + }, + test: { + coverage: { + provider: 'v8', + ignoreEmptyLines: true, + }, + }, +}) +``` + #### coverage.ignoreClassMethods - **Type:** `string[]` diff --git a/docs/guide/coverage.md b/docs/guide/coverage.md index f81fbbb4ec9c..9e5c12fba91f 100644 --- a/docs/guide/coverage.md +++ b/docs/guide/coverage.md @@ -43,6 +43,11 @@ npm i -D @vitest/coverage-istanbul ## Coverage Setup +:::tip +It's recommended to always define [`coverage.include`](https://vitest.dev/config/#coverage-include) in your configuration file. +This helps Vitest to reduce the amount of files picked by [`coverage.all`](https://vitest.dev/config/#coverage-all). +::: + To test with coverage enabled, you can pass the `--coverage` flag in CLI. By default, reporter `['text', 'html', 'clover', 'json']` will be used. diff --git a/package.json b/package.json index a2f527c5c5ab..eb4f6072ef0b 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,8 @@ "@types/chai@4.3.6": "patches/@types__chai@4.3.6.patch", "@sinonjs/fake-timers@11.1.0": "patches/@sinonjs__fake-timers@11.1.0.patch", "cac@6.7.14": "patches/cac@6.7.14.patch", - "@types/sinonjs__fake-timers@8.1.5": "patches/@types__sinonjs__fake-timers@8.1.5.patch" + "@types/sinonjs__fake-timers@8.1.5": "patches/@types__sinonjs__fake-timers@8.1.5.patch", + "v8-to-istanbul@9.2.0": "patches/v8-to-istanbul@9.2.0.patch" } }, "simple-git-hooks": { diff --git a/packages/coverage-v8/package.json b/packages/coverage-v8/package.json index 33cf11cae857..2b961d62e944 100644 --- a/packages/coverage-v8/package.json +++ b/packages/coverage-v8/package.json @@ -56,8 +56,7 @@ "picocolors": "^1.0.0", "std-env": "^3.5.0", "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" + "test-exclude": "^6.0.0" }, "devDependencies": { "@types/debug": "^4.1.12", @@ -66,6 +65,7 @@ "@types/istanbul-lib-source-maps": "^4.0.4", "@types/istanbul-reports": "^3.0.4", "pathe": "^1.1.1", + "v8-to-istanbul": "^9.2.0", "vite-node": "workspace:*", "vitest": "workspace:*" } diff --git a/packages/coverage-v8/src/provider.ts b/packages/coverage-v8/src/provider.ts index 3f43c74ec131..be1805020c46 100644 --- a/packages/coverage-v8/src/provider.ts +++ b/packages/coverage-v8/src/provider.ts @@ -266,14 +266,12 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage } const coverages = await Promise.all(chunk.map(async (filename) => { - const transformResult = await this.ctx.vitenode.transformRequest(filename.pathname).catch(() => {}) + const { originalSource, source } = await this.getSources(filename.href, transformResults) // Ignore empty files, e.g. files that contain only typescript types and no runtime code - if (transformResult && stripLiteral(transformResult.code).trim() === '') + if (source && stripLiteral(source).trim() === '') return null - const { originalSource } = await this.getSources(filename.href, transformResults) - const coverage = { url: filename.href, scriptId: '0', @@ -309,9 +307,9 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage }> { const filePath = normalize(fileURLToPath(url)) - const transformResult = transformResults.get(filePath) + const transformResult = transformResults.get(filePath) || await this.ctx.vitenode.transformRequest(filePath).catch(() => {}) - const map = transformResult?.map + const map = transformResult?.map as (EncodedSourceMap | undefined) const code = transformResult?.code const sourcesContent = map?.sourcesContent?.[0] || await fs.readFile(filePath, 'utf-8').catch(() => { // If file does not exist construct a dummy source for it. @@ -367,7 +365,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage // If no source map was found from vite-node we can assume this file was not run in the wrapper const wrapperLength = sources.sourceMap ? WRAPPER_LENGTH : 0 - const converter = v8ToIstanbul(url, wrapperLength, sources) + const converter = v8ToIstanbul(url, wrapperLength, sources, undefined, this.options.ignoreEmptyLines) await converter.load() converter.applyCoverage(functions) diff --git a/packages/vitest/src/defaults.ts b/packages/vitest/src/defaults.ts index 44d21c4a350f..ae678886bf75 100644 --- a/packages/vitest/src/defaults.ts +++ b/packages/vitest/src/defaults.ts @@ -43,6 +43,7 @@ export const coverageConfigDefaults: ResolvedCoverageOptions = { reporter: [['text', {}], ['html', {}], ['clover', {}], ['json', {}]], extension: ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte', '.marko'], allowExternal: false, + ignoreEmptyLines: false, processingConcurrency: Math.min(20, os.availableParallelism?.() ?? os.cpus().length), } diff --git a/packages/vitest/src/types/coverage.ts b/packages/vitest/src/types/coverage.ts index 69425c9d7195..beddea8f79c8 100644 --- a/packages/vitest/src/types/coverage.ts +++ b/packages/vitest/src/types/coverage.ts @@ -233,7 +233,12 @@ export interface CoverageIstanbulOptions extends BaseCoverageOptions { ignoreClassMethods?: string[] } -export interface CoverageV8Options extends BaseCoverageOptions {} +export interface CoverageV8Options extends BaseCoverageOptions { + /** + * Ignore empty lines, comments and other non-runtime code, e.g. Typescript types + */ + ignoreEmptyLines?: boolean +} export interface CustomProviderOptions extends Pick { /** Name of the module or path to a file to load the custom provider from */ diff --git a/patches/v8-to-istanbul@9.2.0.patch b/patches/v8-to-istanbul@9.2.0.patch new file mode 100644 index 000000000000..b29d2545987c --- /dev/null +++ b/patches/v8-to-istanbul@9.2.0.patch @@ -0,0 +1,156 @@ +diff --git a/CHANGELOG.md b/CHANGELOG.md +deleted file mode 100644 +index 4f7e3bc8d1bba4feb51044ff9eb77b41f972f957..0000000000000000000000000000000000000000 +diff --git a/index.d.ts b/index.d.ts +index ee7b286844f2bf96357218166e26e1c338f774cf..657531b7c75f43e9a4e957dd1f10797e44da5bb1 100644 +--- a/index.d.ts ++++ b/index.d.ts +@@ -1,5 +1,7 @@ + /// + ++// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 ++ + import { Profiler } from 'inspector' + import { CoverageMapData } from 'istanbul-lib-coverage' + import { SourceMapInput } from '@jridgewell/trace-mapping' +@@ -20,6 +22,6 @@ declare class V8ToIstanbul { + toIstanbul(): CoverageMapData + } + +-declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources, excludePath?: (path: string) => boolean): V8ToIstanbul ++declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources, excludePath?: (path: string) => boolean, excludeEmptyLines?: boolean): V8ToIstanbul + + export = v8ToIstanbul +diff --git a/index.js b/index.js +index 4db27a7d84324d0e6605c5506e3eee5665ddfeb0..7bfb839634b1e3c54efedc3c270d82edc4167a64 100644 +--- a/index.js ++++ b/index.js +@@ -1,5 +1,6 @@ ++// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 + const V8ToIstanbul = require('./lib/v8-to-istanbul') + +-module.exports = function (path, wrapperLength, sources, excludePath) { +- return new V8ToIstanbul(path, wrapperLength, sources, excludePath) ++module.exports = function (path, wrapperLength, sources, excludePath, excludeEmptyLines) { ++ return new V8ToIstanbul(path, wrapperLength, sources, excludePath, excludeEmptyLines) + } +diff --git a/lib/source.js b/lib/source.js +index d8ebc215f6ad83d472abafe976935acfe5c61b04..021fd2aed1f73ebb4adc449ce6e96f2d89c295a5 100644 +--- a/lib/source.js ++++ b/lib/source.js +@@ -1,23 +1,32 @@ ++// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 + const CovLine = require('./line') + const { sliceRange } = require('./range') +-const { originalPositionFor, generatedPositionFor, GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND } = require('@jridgewell/trace-mapping') ++const { originalPositionFor, generatedPositionFor, eachMapping, GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND } = require('@jridgewell/trace-mapping') + + module.exports = class CovSource { +- constructor (sourceRaw, wrapperLength) { ++ constructor (sourceRaw, wrapperLength, traceMap) { + sourceRaw = sourceRaw ? sourceRaw.trimEnd() : '' + this.lines = [] + this.eof = sourceRaw.length + this.shebangLength = getShebangLength(sourceRaw) + this.wrapperLength = wrapperLength - this.shebangLength +- this._buildLines(sourceRaw) ++ this._buildLines(sourceRaw, traceMap) + } + +- _buildLines (source) { ++ _buildLines (source, traceMap) { + let position = 0 + let ignoreCount = 0 + let ignoreAll = false ++ const linesToCover = traceMap && this._parseLinesToCover(traceMap) ++ + for (const [i, lineStr] of source.split(/(?<=\r?\n)/u).entries()) { +- const line = new CovLine(i + 1, position, lineStr) ++ const lineNumber = i + 1 ++ const line = new CovLine(lineNumber, position, lineStr) ++ ++ if (linesToCover && !linesToCover.has(lineNumber)) { ++ line.ignore = true ++ } ++ + if (ignoreCount > 0) { + line.ignore = true + ignoreCount-- +@@ -125,6 +134,18 @@ module.exports = class CovSource { + if (this.lines[line - 1] === undefined) return this.eof + return Math.min(this.lines[line - 1].startCol + relCol, this.lines[line - 1].endCol) + } ++ ++ _parseLinesToCover (traceMap) { ++ const linesToCover = new Set() ++ ++ eachMapping(traceMap, (mapping) => { ++ if (mapping.originalLine !== null) { ++ linesToCover.add(mapping.originalLine) ++ } ++ }) ++ ++ return linesToCover ++ } + } + + // this implementation is pulled over from istanbul-lib-sourcemap: +diff --git a/lib/v8-to-istanbul.js b/lib/v8-to-istanbul.js +index 3616437b00658861dc5a8910c64d1449e9fdf467..c1e0c0ae19984480e408713d1691fa174a7c4c1f 100644 +--- a/lib/v8-to-istanbul.js ++++ b/lib/v8-to-istanbul.js +@@ -1,3 +1,4 @@ ++// Patch applied: https://github.com/istanbuljs/v8-to-istanbul/pull/244 + const assert = require('assert') + const convertSourceMap = require('convert-source-map') + const util = require('util') +@@ -25,12 +26,13 @@ const isNode8 = /^v8\./.test(process.version) + const cjsWrapperLength = isOlderNode10 ? require('module').wrapper[0].length : 0 + + module.exports = class V8ToIstanbul { +- constructor (scriptPath, wrapperLength, sources, excludePath) { ++ constructor (scriptPath, wrapperLength, sources, excludePath, excludeEmptyLines) { + assert(typeof scriptPath === 'string', 'scriptPath must be a string') + assert(!isNode8, 'This module does not support node 8 or lower, please upgrade to node 10') + this.path = parsePath(scriptPath) + this.wrapperLength = wrapperLength === undefined ? cjsWrapperLength : wrapperLength + this.excludePath = excludePath || (() => false) ++ this.excludeEmptyLines = excludeEmptyLines === true + this.sources = sources || {} + this.generatedLines = [] + this.branches = {} +@@ -58,8 +60,8 @@ module.exports = class V8ToIstanbul { + if (!this.sourceMap.sourcesContent) { + this.sourceMap.sourcesContent = await this.sourcesContentFromSources() + } +- this.covSources = this.sourceMap.sourcesContent.map((rawSource, i) => ({ source: new CovSource(rawSource, this.wrapperLength), path: this.sourceMap.sources[i] })) +- this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength) ++ this.covSources = this.sourceMap.sourcesContent.map((rawSource, i) => ({ source: new CovSource(rawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null), path: this.sourceMap.sources[i] })) ++ this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null) + } else { + const candidatePath = this.rawSourceMap.sourcemap.sources.length >= 1 ? this.rawSourceMap.sourcemap.sources[0] : this.rawSourceMap.sourcemap.file + this.path = this._resolveSource(this.rawSourceMap, candidatePath || this.path) +@@ -82,8 +84,8 @@ module.exports = class V8ToIstanbul { + // We fallback to reading the original source from disk. + originalRawSource = await readFile(this.path, 'utf8') + } +- this.covSources = [{ source: new CovSource(originalRawSource, this.wrapperLength), path: this.path }] +- this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength) ++ this.covSources = [{ source: new CovSource(originalRawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null), path: this.path }] ++ this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength, this.excludeEmptyLines ? this.sourceMap : null) + } + } else { + this.covSources = [{ source: new CovSource(rawSource, this.wrapperLength), path: this.path }] +@@ -281,8 +283,10 @@ module.exports = class V8ToIstanbul { + s: {} + } + source.lines.forEach((line, index) => { +- statements.statementMap[`${index}`] = line.toIstanbul() +- statements.s[`${index}`] = line.ignore ? 1 : line.count ++ if (!line.ignore) { ++ statements.statementMap[`${index}`] = line.toIstanbul() ++ statements.s[`${index}`] = line.count ++ } + }) + return statements + } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a8021fcce35..7394d57ffaba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,6 +22,9 @@ patchedDependencies: cac@6.7.14: hash: slh3cigivjjjktoa42g2agwaem path: patches/cac@6.7.14.patch + v8-to-istanbul@9.2.0: + hash: zm2cjmgndzbmnuve7zu5emyu7i + path: patches/v8-to-istanbul@9.2.0.patch importers: @@ -1063,9 +1066,6 @@ importers: test-exclude: specifier: ^6.0.0 version: 6.0.0 - v8-to-istanbul: - specifier: ^9.2.0 - version: 9.2.0 devDependencies: '@types/debug': specifier: ^4.1.12 @@ -1085,6 +1085,9 @@ importers: pathe: specifier: ^1.1.1 version: 1.1.1 + v8-to-istanbul: + specifier: ^9.2.0 + version: 9.2.0(patch_hash=zm2cjmgndzbmnuve7zu5emyu7i) vite-node: specifier: workspace:* version: link:../vite-node @@ -6180,6 +6183,7 @@ packages: dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + dev: true /@jridgewell/trace-mapping@0.3.22: resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} @@ -9619,6 +9623,7 @@ packages: /@types/istanbul-lib-coverage@2.0.6: resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + dev: true /@types/istanbul-lib-instrument@1.7.7: resolution: {integrity: sha512-xyV3cVIhVuVAT7wyJXT+lliOo4b7BpfsnfCDy0/nuuAX+J7RB2rWNDV/7hkmv62vwqAD2XQ2Fgt6OwWGWEYfvg==} @@ -12968,7 +12973,7 @@ packages: istanbul-reports: 3.1.6 rimraf: 3.0.2 test-exclude: 6.0.0 - v8-to-istanbul: 9.2.0 + v8-to-istanbul: 9.2.0(patch_hash=zm2cjmgndzbmnuve7zu5emyu7i) yargs: 16.2.0 yargs-parser: 20.2.9 dev: true @@ -26536,13 +26541,15 @@ packages: source-map: 0.7.4 dev: true - /v8-to-istanbul@9.2.0: + /v8-to-istanbul@9.2.0(patch_hash=zm2cjmgndzbmnuve7zu5emyu7i): resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} engines: {node: '>=10.12.0'} dependencies: '@jridgewell/trace-mapping': 0.3.20 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + dev: true + patched: true /validate-html-nesting@1.2.2: resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} diff --git a/test/coverage-test/coverage-report-tests/__snapshots__/istanbul.report.test.ts.snap b/test/coverage-test/coverage-report-tests/__snapshots__/istanbul.report.test.ts.snap index 4ce5f2804de4..ec7ed93f4a90 100644 --- a/test/coverage-test/coverage-report-tests/__snapshots__/istanbul.report.test.ts.snap +++ b/test/coverage-test/coverage-report-tests/__snapshots__/istanbul.report.test.ts.snap @@ -1134,6 +1134,256 @@ exports[`istanbul json report 1`] = ` }, }, }, + "/src/empty-lines.ts": { + "b": { + "0": [ + 0, + 0, + ], + "1": [ + 0, + 0, + ], + "2": [ + 0, + 0, + ], + "3": [ + 0, + 0, + ], + }, + "branchMap": { + "0": { + "loc": { + "end": { + "column": null, + "line": 11, + }, + "start": { + "column": 2, + "line": 8, + }, + }, + "locations": [ + { + "end": { + "column": null, + "line": 11, + }, + "start": { + "column": 2, + "line": 8, + }, + }, + { + "end": { + "column": null, + "line": 11, + }, + "start": { + "column": 2, + "line": 8, + }, + }, + ], + "type": "if", + }, + "1": { + "loc": { + "end": { + "column": 26, + "line": 8, + }, + "start": { + "column": 6, + "line": 8, + }, + }, + "locations": [ + { + "end": { + "column": 17, + "line": 8, + }, + "start": { + "column": 6, + "line": 8, + }, + }, + { + "end": { + "column": 26, + "line": 8, + }, + "start": { + "column": 17, + "line": 8, + }, + }, + ], + "type": "binary-expr", + }, + "2": { + "loc": { + "end": { + "column": null, + "line": 18, + }, + "start": { + "column": 2, + "line": 15, + }, + }, + "locations": [ + { + "end": { + "column": null, + "line": 18, + }, + "start": { + "column": 2, + "line": 15, + }, + }, + { + "end": { + "column": null, + "line": 18, + }, + "start": { + "column": 2, + "line": 15, + }, + }, + ], + "type": "if", + }, + "3": { + "loc": { + "end": { + "column": 26, + "line": 15, + }, + "start": { + "column": 6, + "line": 15, + }, + }, + "locations": [ + { + "end": { + "column": 17, + "line": 15, + }, + "start": { + "column": 6, + "line": 15, + }, + }, + { + "end": { + "column": 26, + "line": 15, + }, + "start": { + "column": 17, + "line": 15, + }, + }, + ], + "type": "binary-expr", + }, + }, + "f": { + "0": 0, + }, + "fnMap": { + "0": { + "decl": { + "end": { + "column": 20, + "line": 2, + }, + "start": { + "column": 16, + "line": 2, + }, + }, + "loc": { + "end": { + "column": null, + "line": 29, + }, + "start": { + "column": 42, + "line": 2, + }, + }, + "name": "add", + }, + }, + "path": "/src/empty-lines.ts", + "s": { + "0": 0, + "1": 0, + "2": 0, + "3": 0, + "4": 0, + }, + "statementMap": { + "0": { + "end": { + "column": null, + "line": 11, + }, + "start": { + "column": 2, + "line": 8, + }, + }, + "1": { + "end": { + "column": null, + "line": 10, + }, + "start": { + "column": 4, + "line": 10, + }, + }, + "2": { + "end": { + "column": null, + "line": 18, + }, + "start": { + "column": 2, + "line": 15, + }, + }, + "3": { + "end": { + "column": null, + "line": 17, + }, + "start": { + "column": 4, + "line": 17, + }, + }, + "4": { + "end": { + "column": null, + "line": 28, + }, + "start": { + "column": 2, + "line": 28, + }, + }, + }, + }, "/src/file-to-change.ts": { "b": {}, "branchMap": {}, @@ -2702,32 +2952,32 @@ exports[`istanbul json report 1`] = ` "loc": { "end": { "column": null, - "line": 24, + "line": 36, }, "start": { "column": 2, - "line": 21, + "line": 33, }, }, "locations": [ { "end": { "column": null, - "line": 24, + "line": 36, }, "start": { "column": 2, - "line": 21, + "line": 33, }, }, { "end": { "column": null, - "line": 24, + "line": 36, }, "start": { "column": 2, - "line": 21, + "line": 33, }, }, ], @@ -2737,32 +2987,32 @@ exports[`istanbul json report 1`] = ` "loc": { "end": { "column": null, - "line": 29, + "line": 42, }, "start": { "column": 2, - "line": 26, + "line": 39, }, }, "locations": [ { "end": { "column": null, - "line": 29, + "line": 42, }, "start": { "column": 2, - "line": 26, + "line": 39, }, }, { "end": { "column": null, - "line": 29, + "line": 42, }, "start": { "column": 2, - "line": 26, + "line": 39, }, }, ], @@ -2780,21 +3030,21 @@ exports[`istanbul json report 1`] = ` "decl": { "end": { "column": 39, - "line": 6, + "line": 8, }, "start": { "column": 24, - "line": 6, + "line": 8, }, }, "loc": { "end": { "column": null, - "line": 8, + "line": 10, }, "start": { "column": 39, - "line": 6, + "line": 8, }, }, "name": "untestedFile", @@ -2803,21 +3053,21 @@ exports[`istanbul json report 1`] = ` "decl": { "end": { "column": 13, - "line": 10, + "line": 12, }, "start": { "column": 9, - "line": 10, + "line": 12, }, }, "loc": { "end": { "column": null, - "line": 13, + "line": 15, }, "start": { "column": 35, - "line": 10, + "line": 12, }, }, "name": "add", @@ -2826,21 +3076,21 @@ exports[`istanbul json report 1`] = ` "decl": { "end": { "column": 18, - "line": 15, + "line": 19, }, "start": { "column": 9, - "line": 15, + "line": 19, }, }, "loc": { "end": { "column": null, - "line": 18, + "line": 22, }, "start": { "column": 40, - "line": 15, + "line": 19, }, }, "name": "multiply", @@ -2849,21 +3099,21 @@ exports[`istanbul json report 1`] = ` "decl": { "end": { "column": 21, - "line": 20, + "line": 24, }, "start": { "column": 16, - "line": 20, + "line": 24, }, }, "loc": { "end": { "column": null, - "line": 33, + "line": 47, }, "start": { "column": 64, - "line": 20, + "line": 24, }, }, "name": "math", @@ -2884,81 +3134,81 @@ exports[`istanbul json report 1`] = ` "0": { "end": { "column": null, - "line": 7, + "line": 9, }, "start": { "column": 2, - "line": 7, + "line": 9, }, }, "1": { "end": { "column": null, - "line": 12, + "line": 14, }, "start": { "column": 2, - "line": 12, + "line": 14, }, }, "2": { "end": { "column": null, - "line": 17, + "line": 21, }, "start": { "column": 2, - "line": 17, + "line": 21, }, }, "3": { "end": { "column": null, - "line": 24, + "line": 36, }, "start": { "column": 2, - "line": 21, + "line": 33, }, }, "4": { "end": { "column": null, - "line": 23, + "line": 35, }, "start": { "column": 4, - "line": 23, + "line": 35, }, }, "5": { "end": { "column": null, - "line": 29, + "line": 42, }, "start": { "column": 2, - "line": 26, + "line": 39, }, }, "6": { "end": { "column": null, - "line": 28, + "line": 41, }, "start": { "column": 4, - "line": 28, + "line": 41, }, }, "7": { "end": { "column": null, - "line": 32, + "line": 46, }, "start": { "column": 2, - "line": 32, + "line": 46, }, }, }, diff --git a/test/coverage-test/coverage-report-tests/__snapshots__/v8.report.test.ts.snap b/test/coverage-test/coverage-report-tests/__snapshots__/v8.report.test.ts.snap index cd0ff16fdc7d..326c2b7f6b93 100644 --- a/test/coverage-test/coverage-report-tests/__snapshots__/v8.report.test.ts.snap +++ b/test/coverage-test/coverage-report-tests/__snapshots__/v8.report.test.ts.snap @@ -2777,6 +2777,395 @@ exports[`v8 json report 1`] = ` }, }, }, + "/src/empty-lines.ts": { + "all": true, + "b": { + "0": [ + 0, + ], + }, + "branchMap": { + "0": { + "line": 1, + "loc": { + "end": { + "column": 1, + "line": 29, + }, + "start": { + "column": 478, + "line": 1, + }, + }, + "locations": [ + { + "end": { + "column": 1, + "line": 29, + }, + "start": { + "column": 478, + "line": 1, + }, + }, + ], + "type": "branch", + }, + }, + "f": { + "0": 0, + }, + "fnMap": { + "0": { + "decl": { + "end": { + "column": 1, + "line": 29, + }, + "start": { + "column": 478, + "line": 1, + }, + }, + "line": 1, + "loc": { + "end": { + "column": 1, + "line": 29, + }, + "start": { + "column": 478, + "line": 1, + }, + }, + "name": "(empty-report)", + }, + }, + "path": "/src/empty-lines.ts", + "s": { + "0": 0, + "1": 0, + "10": 0, + "11": 0, + "12": 0, + "13": 0, + "14": 0, + "15": 0, + "16": 0, + "17": 0, + "18": 0, + "19": 0, + "2": 0, + "20": 0, + "21": 0, + "22": 0, + "23": 0, + "24": 0, + "25": 0, + "26": 0, + "27": 0, + "28": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0, + "7": 0, + "8": 0, + "9": 0, + }, + "statementMap": { + "0": { + "end": { + "column": 65, + "line": 1, + }, + "start": { + "column": 0, + "line": 1, + }, + }, + "1": { + "end": { + "column": 43, + "line": 2, + }, + "start": { + "column": 0, + "line": 2, + }, + }, + "10": { + "end": { + "column": 3, + "line": 11, + }, + "start": { + "column": 0, + "line": 11, + }, + }, + "11": { + "end": { + "column": 0, + "line": 12, + }, + "start": { + "column": 0, + "line": 12, + }, + }, + "12": { + "end": { + "column": 32, + "line": 13, + }, + "start": { + "column": 0, + "line": 13, + }, + }, + "13": { + "end": { + "column": 0, + "line": 14, + }, + "start": { + "column": 0, + "line": 14, + }, + }, + "14": { + "end": { + "column": 27, + "line": 15, + }, + "start": { + "column": 0, + "line": 15, + }, + }, + "15": { + "end": { + "column": 38, + "line": 16, + }, + "start": { + "column": 0, + "line": 16, + }, + }, + "16": { + "end": { + "column": 12, + "line": 17, + }, + "start": { + "column": 0, + "line": 17, + }, + }, + "17": { + "end": { + "column": 3, + "line": 18, + }, + "start": { + "column": 0, + "line": 18, + }, + }, + "18": { + "end": { + "column": 0, + "line": 19, + }, + "start": { + "column": 0, + "line": 19, + }, + }, + "19": { + "end": { + "column": 33, + "line": 20, + }, + "start": { + "column": 0, + "line": 20, + }, + }, + "2": { + "end": { + "column": 5, + "line": 3, + }, + "start": { + "column": 0, + "line": 3, + }, + }, + "20": { + "end": { + "column": 13, + "line": 21, + }, + "start": { + "column": 0, + "line": 21, + }, + }, + "21": { + "end": { + "column": 11, + "line": 22, + }, + "start": { + "column": 0, + "line": 22, + }, + }, + "22": { + "end": { + "column": 22, + "line": 23, + }, + "start": { + "column": 0, + "line": 23, + }, + }, + "23": { + "end": { + "column": 7, + "line": 24, + }, + "start": { + "column": 0, + "line": 24, + }, + }, + "24": { + "end": { + "column": 5, + "line": 25, + }, + "start": { + "column": 0, + "line": 25, + }, + }, + "25": { + "end": { + "column": 3, + "line": 26, + }, + "start": { + "column": 0, + "line": 26, + }, + }, + "26": { + "end": { + "column": 0, + "line": 27, + }, + "start": { + "column": 0, + "line": 27, + }, + }, + "27": { + "end": { + "column": 14, + "line": 28, + }, + "start": { + "column": 0, + "line": 28, + }, + }, + "28": { + "end": { + "column": 1, + "line": 29, + }, + "start": { + "column": 0, + "line": 29, + }, + }, + "3": { + "end": { + "column": 10, + "line": 4, + }, + "start": { + "column": 0, + "line": 4, + }, + }, + "4": { + "end": { + "column": 9, + "line": 5, + }, + "start": { + "column": 0, + "line": 5, + }, + }, + "5": { + "end": { + "column": 12, + "line": 6, + }, + "start": { + "column": 0, + "line": 6, + }, + }, + "6": { + "end": { + "column": 5, + "line": 7, + }, + "start": { + "column": 0, + "line": 7, + }, + }, + "7": { + "end": { + "column": 27, + "line": 8, + }, + "start": { + "column": 0, + "line": 8, + }, + }, + "8": { + "end": { + "column": 38, + "line": 9, + }, + "start": { + "column": 0, + "line": 9, + }, + }, + "9": { + "end": { + "column": 12, + "line": 10, + }, + "start": { + "column": 0, + "line": 10, + }, + }, + }, + }, "/src/file-to-change.ts": { "all": true, "b": { @@ -2793,7 +3182,7 @@ exports[`v8 json report 1`] = ` "line": 7, }, "start": { - "column": 0, + "column": 133, "line": 1, }, }, @@ -2804,7 +3193,7 @@ exports[`v8 json report 1`] = ` "line": 7, }, "start": { - "column": 0, + "column": 133, "line": 1, }, }, @@ -2823,7 +3212,7 @@ exports[`v8 json report 1`] = ` "line": 7, }, "start": { - "column": 0, + "column": 133, "line": 1, }, }, @@ -2834,7 +3223,7 @@ exports[`v8 json report 1`] = ` "line": 7, }, "start": { - "column": 0, + "column": 133, "line": 1, }, }, @@ -5306,10 +5695,10 @@ exports[`v8 json report 1`] = ` "loc": { "end": { "column": 1, - "line": 33, + "line": 47, }, "start": { - "column": 0, + "column": 984, "line": 1, }, }, @@ -5317,10 +5706,10 @@ exports[`v8 json report 1`] = ` { "end": { "column": 1, - "line": 33, + "line": 47, }, "start": { - "column": 0, + "column": 984, "line": 1, }, }, @@ -5336,10 +5725,10 @@ exports[`v8 json report 1`] = ` "decl": { "end": { "column": 1, - "line": 33, + "line": 47, }, "start": { - "column": 0, + "column": 984, "line": 1, }, }, @@ -5347,10 +5736,10 @@ exports[`v8 json report 1`] = ` "loc": { "end": { "column": 1, - "line": 33, + "line": 47, }, "start": { - "column": 0, + "column": 984, "line": 1, }, }, @@ -5386,7 +5775,15 @@ exports[`v8 json report 1`] = ` "30": 0, "31": 0, "32": 0, + "33": 0, + "34": 0, + "35": 0, + "36": 0, "4": 0, + "43": 0, + "44": 0, + "45": 0, + "46": 0, "5": 0, "6": 0, "7": 0, @@ -5416,7 +5813,7 @@ exports[`v8 json report 1`] = ` }, "10": { "end": { - "column": 36, + "column": 0, "line": 11, }, "start": { @@ -5426,7 +5823,7 @@ exports[`v8 json report 1`] = ` }, "11": { "end": { - "column": 14, + "column": 36, "line": 12, }, "start": { @@ -5436,7 +5833,7 @@ exports[`v8 json report 1`] = ` }, "12": { "end": { - "column": 1, + "column": 36, "line": 13, }, "start": { @@ -5446,7 +5843,7 @@ exports[`v8 json report 1`] = ` }, "13": { "end": { - "column": 0, + "column": 14, "line": 14, }, "start": { @@ -5456,7 +5853,7 @@ exports[`v8 json report 1`] = ` }, "14": { "end": { - "column": 41, + "column": 1, "line": 15, }, "start": { @@ -5466,7 +5863,7 @@ exports[`v8 json report 1`] = ` }, "15": { "end": { - "column": 36, + "column": 0, "line": 16, }, "start": { @@ -5476,7 +5873,7 @@ exports[`v8 json report 1`] = ` }, "16": { "end": { - "column": 14, + "column": 30, "line": 17, }, "start": { @@ -5486,7 +5883,7 @@ exports[`v8 json report 1`] = ` }, "17": { "end": { - "column": 1, + "column": 0, "line": 18, }, "start": { @@ -5496,7 +5893,7 @@ exports[`v8 json report 1`] = ` }, "18": { "end": { - "column": 0, + "column": 41, "line": 19, }, "start": { @@ -5506,7 +5903,7 @@ exports[`v8 json report 1`] = ` }, "19": { "end": { - "column": 65, + "column": 36, "line": 20, }, "start": { @@ -5526,7 +5923,7 @@ exports[`v8 json report 1`] = ` }, "20": { "end": { - "column": 25, + "column": 14, "line": 21, }, "start": { @@ -5536,7 +5933,7 @@ exports[`v8 json report 1`] = ` }, "21": { "end": { - "column": 38, + "column": 1, "line": 22, }, "start": { @@ -5546,7 +5943,7 @@ exports[`v8 json report 1`] = ` }, "22": { "end": { - "column": 25, + "column": 0, "line": 23, }, "start": { @@ -5556,7 +5953,7 @@ exports[`v8 json report 1`] = ` }, "23": { "end": { - "column": 3, + "column": 65, "line": 24, }, "start": { @@ -5566,7 +5963,7 @@ exports[`v8 json report 1`] = ` }, "24": { "end": { - "column": 0, + "column": 33, "line": 25, }, "start": { @@ -5576,7 +5973,7 @@ exports[`v8 json report 1`] = ` }, "25": { "end": { - "column": 25, + "column": 13, "line": 26, }, "start": { @@ -5586,7 +5983,7 @@ exports[`v8 json report 1`] = ` }, "26": { "end": { - "column": 38, + "column": 11, "line": 27, }, "start": { @@ -5596,7 +5993,7 @@ exports[`v8 json report 1`] = ` }, "27": { "end": { - "column": 20, + "column": 22, "line": 28, }, "start": { @@ -5606,7 +6003,7 @@ exports[`v8 json report 1`] = ` }, "28": { "end": { - "column": 3, + "column": 7, "line": 29, }, "start": { @@ -5616,7 +6013,7 @@ exports[`v8 json report 1`] = ` }, "29": { "end": { - "column": 0, + "column": 5, "line": 30, }, "start": { @@ -5636,7 +6033,7 @@ exports[`v8 json report 1`] = ` }, "30": { "end": { - "column": 36, + "column": 3, "line": 31, }, "start": { @@ -5646,7 +6043,7 @@ exports[`v8 json report 1`] = ` }, "31": { "end": { - "column": 41, + "column": 0, "line": 32, }, "start": { @@ -5656,7 +6053,7 @@ exports[`v8 json report 1`] = ` }, "32": { "end": { - "column": 1, + "column": 25, "line": 33, }, "start": { @@ -5664,6 +6061,46 @@ exports[`v8 json report 1`] = ` "line": 33, }, }, + "33": { + "end": { + "column": 38, + "line": 34, + }, + "start": { + "column": 0, + "line": 34, + }, + }, + "34": { + "end": { + "column": 25, + "line": 35, + }, + "start": { + "column": 0, + "line": 35, + }, + }, + "35": { + "end": { + "column": 3, + "line": 36, + }, + "start": { + "column": 0, + "line": 36, + }, + }, + "36": { + "end": { + "column": 0, + "line": 37, + }, + "start": { + "column": 0, + "line": 37, + }, + }, "4": { "end": { "column": 0, @@ -5674,9 +6111,49 @@ exports[`v8 json report 1`] = ` "line": 5, }, }, + "43": { + "end": { + "column": 0, + "line": 44, + }, + "start": { + "column": 0, + "line": 44, + }, + }, + "44": { + "end": { + "column": 36, + "line": 45, + }, + "start": { + "column": 0, + "line": 45, + }, + }, + "45": { + "end": { + "column": 41, + "line": 46, + }, + "start": { + "column": 0, + "line": 46, + }, + }, + "46": { + "end": { + "column": 1, + "line": 47, + }, + "start": { + "column": 0, + "line": 47, + }, + }, "5": { "end": { - "column": 40, + "column": 65, "line": 6, }, "start": { @@ -5686,7 +6163,7 @@ exports[`v8 json report 1`] = ` }, "6": { "end": { - "column": 72, + "column": 0, "line": 7, }, "start": { @@ -5696,7 +6173,7 @@ exports[`v8 json report 1`] = ` }, "7": { "end": { - "column": 1, + "column": 40, "line": 8, }, "start": { @@ -5706,7 +6183,7 @@ exports[`v8 json report 1`] = ` }, "8": { "end": { - "column": 0, + "column": 72, "line": 9, }, "start": { @@ -5716,7 +6193,7 @@ exports[`v8 json report 1`] = ` }, "9": { "end": { - "column": 36, + "column": 1, "line": 10, }, "start": { @@ -6022,11 +6499,6 @@ exports[`v8 json report 1`] = ` "22": 0, "23": 0, "24": 1, - "25": 1, - "26": 1, - "27": 1, - "28": 1, - "29": 1, "3": 1, "4": 1, "5": 2, @@ -6216,56 +6688,6 @@ exports[`v8 json report 1`] = ` "line": 25, }, }, - "25": { - "end": { - "column": 22, - "line": 26, - }, - "start": { - "column": 0, - "line": 26, - }, - }, - "26": { - "end": { - "column": 39, - "line": 27, - }, - "start": { - "column": 0, - "line": 27, - }, - }, - "27": { - "end": { - "column": 35, - "line": 28, - }, - "start": { - "column": 0, - "line": 28, - }, - }, - "28": { - "end": { - "column": 62, - "line": 29, - }, - "start": { - "column": 0, - "line": 29, - }, - }, - "29": { - "end": { - "column": 1, - "line": 30, - }, - "start": { - "column": 0, - "line": 30, - }, - }, "3": { "end": { "column": 0, diff --git a/test/coverage-test/coverage-report-tests/changed.test.ts b/test/coverage-test/coverage-report-tests/changed.test.ts index 20f4602512b2..07481c2dafe5 100644 --- a/test/coverage-test/coverage-report-tests/changed.test.ts +++ b/test/coverage-test/coverage-report-tests/changed.test.ts @@ -7,6 +7,8 @@ test('report contains only the changed files', async () => { const coverageJson = await readCoverageJson('./coverage/coverage-final.json') const coverageMap = libCoverage.createCoverageMap(coverageJson as any) + // Note that this test may fail if you have new files in "vitest/test/coverage-test/src" + // and have not yet committed those expect(coverageMap.files()).toMatchInlineSnapshot(` [ "/src/file-to-change.ts", diff --git a/test/coverage-test/coverage-report-tests/empty-lines.test.ts b/test/coverage-test/coverage-report-tests/empty-lines.test.ts new file mode 100644 index 000000000000..dd405e2971f7 --- /dev/null +++ b/test/coverage-test/coverage-report-tests/empty-lines.test.ts @@ -0,0 +1,108 @@ +import { beforeAll, expect, test } from 'vitest' +import libCoverage from 'istanbul-lib-coverage' + +import { readCoverageJson } from './utils' + +type CoveredLine = 1 +type UncoveredLine = 0 +type IgnoredLine = undefined + +// Key is 1-based line number +type LineCoverage = Record + +let coveredFileLines: LineCoverage +let uncoveredFileLines: LineCoverage + +beforeAll(async () => { + const coverageJson = await readCoverageJson('./coverage/coverage-final.json') + const coverageMap = libCoverage.createCoverageMap(coverageJson as any) + + coveredFileLines = coverageMap.fileCoverageFor('/src/empty-lines.ts').getLineCoverage() as typeof coveredFileLines + uncoveredFileLines = coverageMap.fileCoverageFor('/src/untested-file.ts').getLineCoverage() as typeof uncoveredFileLines +}) + +test('empty lines are ignored', async () => { + expect(coveredFileLines[12]).toBe(undefined) + expect(coveredFileLines[14]).toBe(undefined) + expect(coveredFileLines[19]).toBe(undefined) + expect(coveredFileLines[27]).toBe(undefined) + expect(coveredFileLines[30]).toBe(undefined) + + expect(uncoveredFileLines[5]).toBe(undefined) + expect(uncoveredFileLines[7]).toBe(undefined) +}) + +test('comments are ignored', async () => { + expect(coveredFileLines[1]).toBe(undefined) + expect(coveredFileLines[3]).toBe(undefined) + expect(coveredFileLines[4]).toBe(undefined) + expect(coveredFileLines[5]).toBe(undefined) + expect(coveredFileLines[6]).toBe(undefined) + expect(coveredFileLines[7]).toBe(undefined) + expect(coveredFileLines[9]).toBe(undefined) + expect(coveredFileLines[16]).toBe(undefined) + + expect(uncoveredFileLines[1]).toBe(undefined) + expect(uncoveredFileLines[2]).toBe(undefined) + expect(uncoveredFileLines[3]).toBe(undefined) + expect(uncoveredFileLines[4]).toBe(undefined) + expect(uncoveredFileLines[6]).toBe(undefined) + expect(uncoveredFileLines[13]).toBe(undefined) + expect(uncoveredFileLines[20]).toBe(undefined) + expect(uncoveredFileLines[34]).toBe(undefined) + expect(uncoveredFileLines[45]).toBe(undefined) +}) + +test('ignore hints are ignored', () => { + expect(uncoveredFileLines[38]).toBe(undefined) + expect(uncoveredFileLines[39]).toBe(undefined) + expect(uncoveredFileLines[40]).toBe(undefined) + expect(uncoveredFileLines[41]).toBe(undefined) + expect(uncoveredFileLines[42]).toBe(undefined) + expect(uncoveredFileLines[43]).toBe(undefined) +}) + +test('typescript types are ignored', () => { + expect(coveredFileLines[13]).toBe(undefined) + expect(coveredFileLines[20]).toBe(undefined) + expect(coveredFileLines[21]).toBe(undefined) + expect(coveredFileLines[22]).toBe(undefined) + expect(coveredFileLines[23]).toBe(undefined) + expect(coveredFileLines[24]).toBe(undefined) + expect(coveredFileLines[25]).toBe(undefined) + expect(coveredFileLines[26]).toBe(undefined) + + expect(uncoveredFileLines[17]).toBe(undefined) + expect(uncoveredFileLines[25]).toBe(undefined) + expect(uncoveredFileLines[26]).toBe(undefined) + expect(uncoveredFileLines[27]).toBe(undefined) + expect(uncoveredFileLines[28]).toBe(undefined) + expect(uncoveredFileLines[29]).toBe(undefined) + expect(uncoveredFileLines[30]).toBe(undefined) + expect(uncoveredFileLines[31]).toBe(undefined) +}) + +test('runtime code is not ignored', () => { + // Covered + expect(coveredFileLines[2]).toBe(1) + expect(coveredFileLines[8]).toBe(1) + expect(coveredFileLines[15]).toBe(1) + expect(coveredFileLines[28]).toBe(1) + + // Uncovered + expect(coveredFileLines[10]).toBe(0) + expect(coveredFileLines[17]).toBe(0) + + // Uncovered + expect(uncoveredFileLines[8]).toBe(0) + expect(uncoveredFileLines[9]).toBe(0) + expect(uncoveredFileLines[10]).toBe(0) + expect(uncoveredFileLines[12]).toBe(0) + expect(uncoveredFileLines[14]).toBe(0) + expect(uncoveredFileLines[19]).toBe(0) + expect(uncoveredFileLines[21]).toBe(0) + expect(uncoveredFileLines[24]).toBe(0) + expect(uncoveredFileLines[33]).toBe(0) + expect(uncoveredFileLines[35]).toBe(0) + expect(uncoveredFileLines[46]).toBe(0) +}) diff --git a/test/coverage-test/option-tests/empty-lines.test.ts b/test/coverage-test/option-tests/empty-lines.test.ts new file mode 100644 index 000000000000..2bb4da02326e --- /dev/null +++ b/test/coverage-test/option-tests/empty-lines.test.ts @@ -0,0 +1,7 @@ +import { expect, test } from 'vitest' + +import { add } from '../src/empty-lines' + +test('cover some lines', () => { + expect(add(10, 20)).toBe(30) +}) diff --git a/test/coverage-test/src/empty-lines.ts b/test/coverage-test/src/empty-lines.ts new file mode 100644 index 000000000000..acd18efd2793 --- /dev/null +++ b/test/coverage-test/src/empty-lines.ts @@ -0,0 +1,29 @@ +/* eslint-disable unused-imports/no-unused-vars -- intentional */ +export function add(a: number, b: number) { + /** + * Multi + * line + * comment + */ + if (a === 2 && b === 3) { + // This line should NOT be covered + return 5 + } + + type TypescriptTypings = 1 | 2 + + if (a === 1 && b === 1) { + // This line should NOT be covered + return 2 + } + + interface MoreCompileTimeCode { + should: { + be: { + excluded: true + } + } + } + + return a + b +} diff --git a/test/coverage-test/src/untested-file.ts b/test/coverage-test/src/untested-file.ts index 9b6d86042cc0..cd744e7e9d13 100644 --- a/test/coverage-test/src/untested-file.ts +++ b/test/coverage-test/src/untested-file.ts @@ -3,6 +3,8 @@ * if sourcemaps are off. */ +/* eslint-disable unused-imports/no-unused-vars -- intentional */ + export default function untestedFile() { return 'This file should end up in report when {"all": true} is given' } @@ -12,21 +14,33 @@ function add(a: number, b: number) { return a + b } +type TypescriptTypings = 1 | 2 + function multiply(a: number, b: number) { // This line should NOT be covered return a * b } export function math(a: number, b: number, operator: '*' | '+') { + interface MoreCompileTimeCode { + should: { + be: { + excluded: true + } + } + } + if (operator === '*') { // This line should NOT be covered return multiply(a, b) } + /* v8 ignore start */ if (operator === '+') { - // This line should NOT be covered + // This line should be excluded return add(a, b) } + /* v8 ignore stop */ // This line should NOT be covered throw new Error('Unsupported operator') diff --git a/test/coverage-test/testing-options.mjs b/test/coverage-test/testing-options.mjs index 45bd0d773e6c..7967097703cd 100644 --- a/test/coverage-test/testing-options.mjs +++ b/test/coverage-test/testing-options.mjs @@ -90,10 +90,30 @@ const testCases = [ rmSync('./src/new-uncovered-file.ts') }, }, + { + testConfig: { + name: 'ignore empty lines', + include: ['option-tests/empty-lines.test.ts'], + coverage: { + provider: 'v8', + reporter: 'json', + ignoreEmptyLines: true, + all: true, + include: ['src/empty-lines.ts', 'src/untested-file.ts'], + }, + }, + assertionConfig: { + include: ['coverage-report-tests/empty-lines.test.ts'], + }, + }, ] for (const provider of ['v8', 'istanbul']) { for (const { after, before, testConfig, assertionConfig } of testCases) { + // Test config may specify which provider the test is for + if (testConfig.coverage?.provider && testConfig.coverage.provider !== provider) + continue + await before?.() // Run test case