Skip to content

Commit

Permalink
fix(coverage): v8 to ignore type-only files
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Mar 3, 2024
1 parent 14ee16d commit 10265d5
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 7 deletions.
1 change: 1 addition & 0 deletions packages/coverage-v8/rollup.config.js
Expand Up @@ -19,6 +19,7 @@ const external = [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
'node:inspector',
'vite',
/^@?vitest(\/|$)/,
]

Expand Down
40 changes: 33 additions & 7 deletions packages/coverage-v8/src/provider.ts
@@ -1,6 +1,7 @@
import { existsSync, promises as fs, writeFileSync } from 'node:fs'
import type { Profiler } from 'node:inspector'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { parseAstAsync } from 'vite'
import v8ToIstanbul from 'v8-to-istanbul'
import { mergeProcessCovs } from '@bcoe/v8-coverage'
import libReport from 'istanbul-lib-report'
Expand Down Expand Up @@ -260,7 +261,13 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
}

const coverages = await Promise.all(chunk.map(async (filename) => {
const { source } = await this.getSources(filename.href, transformResults)
const transformResult = await this.ctx.vitenode.transformRequest(filename.pathname)

// Ignore empty files, e.g. files that contain only typescript types and no runtime code
if (transformResult && await isFileEmpty(transformResult.code))
return null

const { originalSource } = await this.getSources(filename.href, transformResults)

const coverage = {
url: filename.href,
Expand All @@ -269,7 +276,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
functions: [{
ranges: [{
startOffset: 0,
endOffset: source.length,
endOffset: originalSource.length,
count: 0,
}],
isBlockCoverage: true,
Expand All @@ -281,15 +288,18 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
return { result: [coverage] }
}))

merged = mergeProcessCovs([merged, ...coverages])
merged = mergeProcessCovs([
merged,
...coverages.filter((cov): cov is NonNullable<typeof cov> => cov != null),
])
}

return merged
}

private async getSources(url: string, transformResults: TransformResults, functions: Profiler.FunctionCoverage[] = []): Promise<{
source: string
originalSource?: string
originalSource: string
sourceMap?: { sourcemap: EncodedSourceMap }
}> {
const filePath = normalize(fileURLToPath(url))
Expand All @@ -302,12 +312,16 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
// If file does not exist construct a dummy source for it.
// These can be files that were generated dynamically during the test run and were removed after it.
const length = findLongestFunctionLength(functions)
return '.'.repeat(length)
return '/'.repeat(length)
})

// These can be uncovered files included by "all: true" or files that are loaded outside vite-node
if (!map)
return { source: code || sourcesContent }
if (!map) {
return {
source: code || sourcesContent,
originalSource: sourcesContent,
}
}

return {
originalSource: sourcesContent,
Expand Down Expand Up @@ -411,3 +425,15 @@ function normalizeTransformResults(fetchCache: Map<string, { result: FetchResult

return normalized
}

async function isFileEmpty(code: string) {
try {
const ast = await parseAstAsync(code, { allowReturnOutsideFunction: true })
return ast.body.length === 0
}
catch (_) {
// Ignore possible parsing errors
}

return false
}
3 changes: 3 additions & 0 deletions test/coverage-test/src/types.ts
@@ -0,0 +1,3 @@
export type First = 'This' | 'file' | 'should'

export type Second = 'be' | 'excluded' | 'from' | 'report'

0 comments on commit 10265d5

Please sign in to comment.