Skip to content

Commit 422ba66

Browse files
authored
fix(coverage): browser mode + coverage.all (#7597)
1 parent 10bbbe9 commit 422ba66

20 files changed

+236
-1961
lines changed

packages/coverage-v8/src/provider.ts

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ export class V8CoverageProvider extends BaseCoverageProvider<ResolvedCoverageOpt
103103
const coveredFiles = coverageMap.files()
104104
const untestedCoverage = await this.getUntestedFiles(coveredFiles)
105105

106-
const converted = await this.convertCoverage(untestedCoverage)
107-
coverageMap.merge(await transformCoverage(converted))
106+
coverageMap.merge(await transformCoverage(untestedCoverage))
108107
}
109108

110109
if (this.options.excludeAfterRemap) {
@@ -158,7 +157,7 @@ export class V8CoverageProvider extends BaseCoverageProvider<ResolvedCoverageOpt
158157
)
159158
}
160159

161-
private async getUntestedFiles(testedFiles: string[]): Promise<RawCoverage> {
160+
private async getUntestedFiles(testedFiles: string[]): Promise<CoverageMap> {
162161
const transformResults = normalizeTransformResults(
163162
this.ctx.vitenode.fetchCache,
164163
)
@@ -179,56 +178,57 @@ export class V8CoverageProvider extends BaseCoverageProvider<ResolvedCoverageOpt
179178
.map(file => pathToFileURL(file))
180179
.filter(file => !testedFiles.includes(file.pathname))
181180

182-
let merged: RawCoverage = { result: [] }
183181
let index = 0
184182

183+
const coverageMap = this.createCoverageMap()
184+
185185
for (const chunk of this.toSlices(uncoveredFiles, this.options.processingConcurrency)) {
186186
if (debug.enabled) {
187187
index += chunk.length
188188
debug('Uncovered files %d/%d', index, uncoveredFiles.length)
189189
}
190190

191-
const coverages = await Promise.all(
192-
chunk.map(async (filename) => {
193-
const { originalSource } = await this.getSources(
194-
filename.href,
195-
transformResults,
196-
transform,
197-
)
191+
await Promise.all(chunk.map(async (filename) => {
192+
const sources = await this.getSources(
193+
filename.href,
194+
transformResults,
195+
transform,
196+
)
197+
198+
const converter = v8ToIstanbul(
199+
filename.href,
200+
0,
201+
sources,
202+
undefined,
203+
this.options.ignoreEmptyLines,
204+
)
198205

199-
const coverage = {
200-
url: filename.href,
201-
scriptId: '0',
202-
// Create a made up function to mark whole file as uncovered. Note that this does not exist in source maps.
203-
functions: [
206+
await converter.load()
207+
208+
try {
209+
// Create a made up function to mark whole file as uncovered. Note that this does not exist in source maps.
210+
converter.applyCoverage([{
211+
ranges: [
204212
{
205-
ranges: [
206-
{
207-
startOffset: 0,
208-
endOffset: originalSource.length,
209-
count: 0,
210-
},
211-
],
212-
isBlockCoverage: true,
213-
// 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
214-
functionName: '(empty-report)',
213+
startOffset: 0,
214+
endOffset: sources.originalSource.length,
215+
count: 0,
215216
},
216217
],
217-
}
218-
219-
return { result: [coverage] }
220-
}),
221-
)
218+
isBlockCoverage: true,
219+
// 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
220+
functionName: '(empty-report)',
221+
}])
222+
}
223+
catch (error) {
224+
this.ctx.logger.error(`Failed to convert coverage for uncovered ${filename.href}.\n`, error)
225+
}
222226

223-
merged = mergeProcessCovs([
224-
merged,
225-
...coverages.filter(
226-
(cov): cov is NonNullable<typeof cov> => cov != null,
227-
),
228-
])
227+
coverageMap.merge(converter.toIstanbul())
228+
}))
229229
}
230230

231-
return merged
231+
return coverageMap
232232
}
233233

234234
private async getSources<TransformResult extends (FetchResult | Awaited<ReturnType<typeof this.ctx.vitenode.transformRequest>>)>(

packages/vite-node/src/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ export class ViteNodeRunner {
535535
}
536536

537537
protected importExternalModule(path: string): Promise<any> {
538-
return import(path)
538+
return import(/* @vite-ignore */ path)
539539
}
540540

541541
/**

packages/vitest/src/node/coverage.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,20 +552,30 @@ export class BaseCoverageProvider<Options extends ResolvedCoverageOptions<'istan
552552
const servers = [
553553
...ctx.projects.map(project => ({
554554
root: project.config.root,
555+
isBrowserEnabled: project.isBrowserEnabled(),
555556
vitenode: project.vitenode,
556557
})),
557558
// Check core last as it will match all files anyway
558-
{ root: ctx.config.root, vitenode: ctx.vitenode },
559+
{ root: ctx.config.root, vitenode: ctx.vitenode, isBrowserEnabled: ctx.getRootProject().isBrowserEnabled() },
559560
]
560561

561562
return async function transformFile(filename: string): Promise<TransformResult | null | undefined> {
562563
let lastError
563564

564-
for (const { root, vitenode } of servers) {
565-
if (!filename.startsWith(root)) {
565+
for (const { root, vitenode, isBrowserEnabled } of servers) {
566+
// On Windows root doesn't start with "/" while filenames do
567+
if (!filename.startsWith(root) && !filename.startsWith(`/${root}`)) {
566568
continue
567569
}
568570

571+
if (isBrowserEnabled) {
572+
const result = await vitenode.transformRequest(filename, undefined, 'web').catch(() => null)
573+
574+
if (result) {
575+
return result
576+
}
577+
}
578+
569579
try {
570580
return await vitenode.transformRequest(filename)
571581
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { join, resolve } from 'node:path'
2+
import { defineConfig } from 'vitest/config'
3+
4+
export default defineConfig({
5+
resolve: {
6+
alias: [
7+
{
8+
find: /fixtures\/src\/conditional/,
9+
replacement: "$1",
10+
customResolver(_, __, options) {
11+
if ('ssr' in options && options.ssr) {
12+
return { id: resolve('fixtures/src/conditional/node.ts') }
13+
}
14+
return { id: resolve('fixtures/src/conditional/browser.ts') }
15+
},
16+
},
17+
],
18+
},
19+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function browser() {
2+
return "This is for browsers only"
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function node() {
2+
return "This is for node only"
3+
}

test/coverage-test/test/__snapshots__/custom-file-covered-1-istanbul.snapshot.json

Lines changed: 0 additions & 83 deletions
This file was deleted.

test/coverage-test/test/__snapshots__/custom-file-covered-1-v8.snapshot.json

Lines changed: 0 additions & 128 deletions
This file was deleted.

0 commit comments

Comments
 (0)