diff --git a/packages/coverage-istanbul/src/provider.ts b/packages/coverage-istanbul/src/provider.ts index 08af60ee9f9e..25596de38031 100644 --- a/packages/coverage-istanbul/src/provider.ts +++ b/packages/coverage-istanbul/src/provider.ts @@ -108,6 +108,8 @@ export class IstanbulCoverageProvider implements CoverageProvider { if (this.options.all) await this.includeUntestedFiles(mergedCoverage) + includeImplicitElseBranches(mergedCoverage) + const sourceMapStore = libSourceMaps.createSourceMapStore() const coverageMap: CoverageMap = await sourceMapStore.transformCoverage(mergedCoverage) @@ -238,3 +240,44 @@ function resolveIstanbulOptions(options: CoverageIstanbulOptions, root: string) function removeQueryParameters(filename: string) { return filename.split('?')[0] } + +/** + * Work-around for #1887 and #2239 while waiting for https://github.com/istanbuljs/istanbuljs/pull/706 + * + * Goes through all files in the coverage map and checks if branchMap's have + * if-statements with implicit else. When finds one, copies source location of + * the if-statement into the else statement. + */ +function includeImplicitElseBranches(coverageMap: CoverageMap) { + for (const file of coverageMap.files()) { + const fileCoverage = coverageMap.fileCoverageFor(file) + + for (const branchMap of Object.values(fileCoverage.branchMap)) { + if (branchMap.type === 'if') { + const lastIndex = branchMap.locations.length - 1 + + if (lastIndex > 0) { + const elseLocation = branchMap.locations[lastIndex] + + if (elseLocation && isEmptyCoverageRange(elseLocation)) { + const ifLocation = branchMap.locations[0] + + elseLocation.start = { ...ifLocation.start } + elseLocation.end = { ...ifLocation.end } + } + } + } + } + } +} + +function isEmptyCoverageRange(range: libCoverage.Range) { + return ( + range.start === undefined + || range.start.line === undefined + || range.start.column === undefined + || range.end === undefined + || range.end.line === undefined + || range.end.column === undefined + ) +} diff --git a/test/coverage-test/coverage-test/coverage.istanbul.test.ts b/test/coverage-test/coverage-test/coverage.istanbul.test.ts index 46780cd9e5c7..1f02b9748866 100644 --- a/test/coverage-test/coverage-test/coverage.istanbul.test.ts +++ b/test/coverage-test/coverage-test/coverage.istanbul.test.ts @@ -39,3 +39,13 @@ test('files should not contain query parameters', () => { expect(files).toContain('Counter.component.ts.html') expect(files).not.toContain('Counter.component.ts?vue&type=script&src=true&lang.ts.html') }) + +test('implicit else is included in branch count', async () => { + // @ts-expect-error -- generated file + const { default: coverageMap } = await import('./coverage/coverage-final.json') + + const fileCoverage = coverageMap[resolve('./src/implicitElse.ts')] + + expect(fileCoverage.b).toHaveProperty('0') + expect(fileCoverage.b['0']).toHaveLength(2) +}) diff --git a/test/coverage-test/src/implicitElse.ts b/test/coverage-test/src/implicitElse.ts new file mode 100644 index 000000000000..013c4dfe5efe --- /dev/null +++ b/test/coverage-test/src/implicitElse.ts @@ -0,0 +1,8 @@ +export function implicitElse(condition: boolean) { + let a = 1 + + if (condition) + a = 2 + + return a +} diff --git a/test/coverage-test/test/coverage.test.ts b/test/coverage-test/test/coverage.test.ts index 3fc0c95b2524..ecf60dd6ca84 100644 --- a/test/coverage-test/test/coverage.test.ts +++ b/test/coverage-test/test/coverage.test.ts @@ -1,6 +1,11 @@ import { expect, test } from 'vitest' import { pythagoras } from '../src' +import { implicitElse } from '../src/implicitElse' test('Math.sqrt()', async () => { expect(pythagoras(3, 4)).toBe(5) }) + +test('implicit else', () => { + expect(implicitElse(true)).toBe(2) +}) diff --git a/test/coverage-test/vitest.config.ts b/test/coverage-test/vitest.config.ts index 036738b8691c..4244cc0d6801 100644 --- a/test/coverage-test/vitest.config.ts +++ b/test/coverage-test/vitest.config.ts @@ -20,7 +20,7 @@ export default defineConfig({ enabled: true, clean: true, all: true, - reporter: ['html', 'text', 'lcov'], + reporter: ['html', 'text', 'lcov', 'json'], }, }, })