Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(coverage): v8 provider to pick source maps without url query params #3784

Merged
merged 2 commits into from Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 26 additions & 10 deletions packages/coverage-v8/src/provider.ts
Expand Up @@ -13,7 +13,8 @@ import remapping from '@ampproject/remapping'
import { normalize, resolve } from 'pathe'
import c from 'picocolors'
import { provider } from 'std-env'
import type { EncodedSourceMap } from 'vite-node'
import { cleanUrl } from 'vite-node/utils'
import type { EncodedSourceMap, FetchResult } from 'vite-node'
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config'
import { BaseCoverageProvider } from 'vitest/coverage'
import type { AfterSuiteRunMeta, CoverageProvider, CoverageV8Options, ReportContext, ResolvedCoverageOptions } from 'vitest'
Expand All @@ -36,6 +37,7 @@ interface TestExclude {
}

type Options = ResolvedCoverageOptions<'v8'>
type TransformResults = Map<string, FetchResult>

// TODO: vite-node should export this
const WRAPPER_LENGTH = 185
Expand Down Expand Up @@ -99,18 +101,19 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
if (provider === 'stackblitz')
this.ctx.logger.log(c.blue(' % ') + c.yellow('@vitest/coverage-v8 does not work on Stackblitz. Report will be empty.'))

const transformResults = normalizeTransformResults(this.ctx.projects.map(project => project.vitenode.fetchCache))
const merged = mergeProcessCovs(this.coverages)
const scriptCoverages = merged.result.filter(result => this.testExclude.shouldInstrument(fileURLToPath(result.url)))

if (this.options.all && allTestsRun) {
const coveredFiles = Array.from(scriptCoverages.map(r => r.url))
const untestedFiles = await this.getUntestedFiles(coveredFiles)
const untestedFiles = await this.getUntestedFiles(coveredFiles, transformResults)

scriptCoverages.push(...untestedFiles)
}

const converted = await Promise.all(scriptCoverages.map(async ({ url, functions }) => {
const sources = await this.getSources(url, functions)
const sources = await this.getSources(url, transformResults, functions)

// 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
Expand Down Expand Up @@ -177,14 +180,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
}
}

private async getUntestedFiles(testedFiles: string[]): Promise<Profiler.ScriptCoverage[]> {
private async getUntestedFiles(testedFiles: string[], transformResults: TransformResults): Promise<Profiler.ScriptCoverage[]> {
const includedFiles = await this.testExclude.glob(this.ctx.config.root)
const uncoveredFiles = includedFiles
.map(file => pathToFileURL(resolve(this.ctx.config.root, file)))
.filter(file => !testedFiles.includes(file.href))

return await Promise.all(uncoveredFiles.map(async (uncoveredFile) => {
const { source } = await this.getSources(uncoveredFile.href)
const { source } = await this.getSources(uncoveredFile.href, transformResults)

return {
url: uncoveredFile.href,
Expand All @@ -204,16 +207,14 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
}))
}

private async getSources(url: string, functions: Profiler.FunctionCoverage[] = []): Promise<{
private async getSources(url: string, transformResults: TransformResults, functions: Profiler.FunctionCoverage[] = []): Promise<{
source: string
originalSource?: string
sourceMap?: { sourcemap: EncodedSourceMap }
}> {
const filePath = normalize(fileURLToPath(url))
const transformResult = this.ctx.projects
.map(project => project.vitenode.fetchCache.get(filePath)?.result)
.filter(Boolean)
.shift()

const transformResult = transformResults.get(filePath)

const map = transformResult?.map
const code = transformResult?.code
Expand Down Expand Up @@ -277,3 +278,18 @@ function findLongestFunctionLength(functions: Profiler.FunctionCoverage[]) {
return Math.max(previous, maxEndOffset)
}, 0)
}

function normalizeTransformResults(fetchCaches: Map<string, { result: FetchResult }>[]) {
const normalized: TransformResults = new Map()

for (const fetchCache of fetchCaches) {
for (const [key, value] of fetchCache.entries()) {
const cleanEntry = cleanUrl(key)

if (!normalized.has(cleanEntry))
normalized.set(cleanEntry, value.result)
}
}

return normalized
}
Expand Up @@ -704,12 +704,11 @@ exports[`v8 json report 1`] = `
"0": 1,
"1": 1,
"2": 1,
"3": 1,
},
"statementMap": {
"0": {
"end": {
"column": 50,
"column": 38,
"line": 1,
},
"start": {
Expand All @@ -719,7 +718,7 @@ exports[`v8 json report 1`] = `
},
"1": {
"end": {
"column": 38,
"column": 0,
"line": 2,
},
"start": {
Expand All @@ -729,24 +728,14 @@ exports[`v8 json report 1`] = `
},
"2": {
"end": {
"column": 0,
"column": 21,
"line": 3,
},
"start": {
"column": 0,
"line": 3,
},
},
"3": {
"end": {
"column": 39,
"line": 4,
},
"start": {
"column": 0,
"line": 4,
},
},
},
},
"<process-cwd>/src/Defined.vue": {
Expand Down
3 changes: 1 addition & 2 deletions test/coverage-test/src/Counter/index.ts
@@ -1,4 +1,3 @@
import CounterComponent from './Counter.component'
import CounterVue from './Counter.vue'

export { CounterComponent, CounterVue }
export { CounterVue }