Skip to content

Commit c692f76

Browse files
authored
refactor(coverage): move common parts to vitest package (#5236)
1 parent 09c1c76 commit c692f76

File tree

3 files changed

+65
-120
lines changed

3 files changed

+65
-120
lines changed

packages/coverage-istanbul/src/provider.ts

Lines changed: 4 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { AfterSuiteRunMeta, CoverageIstanbulOptions, CoverageProvider, Repo
44
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config'
55
import { BaseCoverageProvider } from 'vitest/coverage'
66
import c from 'picocolors'
7-
import type { ProxifiedModule } from 'magicast'
87
import { parseModule } from 'magicast'
98
import createDebug from 'debug'
109
import libReport from 'istanbul-lib-report'
@@ -172,7 +171,7 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
172171
for (const filenames of [coveragePerProject.ssr, coveragePerProject.web]) {
173172
const coverageMapByTransformMode = libCoverage.createCoverageMap({})
174173

175-
for (const chunk of toSlices(filenames, this.options.processingConcurrency)) {
174+
for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
176175
if (debug.enabled) {
177176
index += chunk.length
178177
debug('Covered files %d/%d', index, total)
@@ -206,7 +205,7 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
206205
watermarks: this.options.watermarks,
207206
})
208207

209-
if (hasTerminalReporter(this.options.reporter))
208+
if (this.hasTerminalReporter(this.options.reporter))
210209
this.ctx.logger.log(c.blue(' % ') + c.dim('Coverage report from ') + c.yellow(this.name))
211210

212211
for (const reporter of this.options.reporter) {
@@ -240,10 +239,8 @@ export class IstanbulCoverageProvider extends BaseCoverageProvider implements Co
240239
this.updateThresholds({
241240
thresholds: resolvedThresholds,
242241
perFile: this.options.thresholds.perFile,
243-
configurationFile: {
244-
write: () => writeFileSync(configFilePath, configModule.generate().code, 'utf-8'),
245-
read: () => resolveConfig(configModule),
246-
},
242+
configurationFile: configModule,
243+
onUpdate: () => writeFileSync(configFilePath, configModule.generate().code, 'utf-8'),
247244
})
248245
}
249246
}
@@ -338,53 +335,3 @@ function isEmptyCoverageRange(range: libCoverage.Range) {
338335
|| range.end.column === undefined
339336
)
340337
}
341-
342-
function hasTerminalReporter(reporters: Options['reporter']) {
343-
return reporters.some(([reporter]) =>
344-
reporter === 'text'
345-
|| reporter === 'text-summary'
346-
|| reporter === 'text-lcov'
347-
|| reporter === 'teamcity')
348-
}
349-
350-
function toSlices<T>(array: T[], size: number): T[][] {
351-
return array.reduce<T[][]>((chunks, item) => {
352-
const index = Math.max(0, chunks.length - 1)
353-
const lastChunk = chunks[index] || []
354-
chunks[index] = lastChunk
355-
356-
if (lastChunk.length >= size)
357-
chunks.push([item])
358-
359-
else
360-
lastChunk.push(item)
361-
362-
return chunks
363-
}, [])
364-
}
365-
366-
function resolveConfig(configModule: ProxifiedModule<any>) {
367-
const mod = configModule.exports.default
368-
369-
try {
370-
// Check for "export default { test: {...} }"
371-
if (mod.$type === 'object')
372-
return mod
373-
374-
if (mod.$type === 'function-call') {
375-
// "export default defineConfig({ test: {...} })"
376-
if (mod.$args[0].$type === 'object')
377-
return mod.$args[0]
378-
379-
// "export default defineConfig(() => ({ test: {...} }))"
380-
if (mod.$args[0].$type === 'arrow-function-expression' && mod.$args[0].$body.$type === 'object')
381-
return mod.$args[0].$body
382-
}
383-
}
384-
catch (error) {
385-
// Reduce magicast's verbose errors to readable ones
386-
throw new Error(error instanceof Error ? error.message : String(error))
387-
}
388-
389-
throw new Error('Failed to update coverage thresholds. Configuration file is too complex.')
390-
}

packages/coverage-v8/src/provider.ts

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import type { CoverageMap } from 'istanbul-lib-coverage'
99
import libCoverage from 'istanbul-lib-coverage'
1010
import libSourceMaps from 'istanbul-lib-source-maps'
1111
import MagicString from 'magic-string'
12-
import type { ProxifiedModule } from 'magicast'
1312
import { parseModule } from 'magicast'
1413
import remapping from '@ampproject/remapping'
1514
import { normalize, resolve } from 'pathe'
@@ -162,7 +161,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
162161
for (const [transformMode, filenames] of Object.entries(coveragePerProject) as [AfterSuiteRunMeta['transformMode'], Filename[]][]) {
163162
let merged: RawCoverage = { result: [] }
164163

165-
for (const chunk of toSlices(filenames, this.options.processingConcurrency)) {
164+
for (const chunk of this.toSlices(filenames, this.options.processingConcurrency)) {
166165
if (debug.enabled) {
167166
index += chunk.length
168167
debug('Covered files %d/%d', index, total)
@@ -198,7 +197,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
198197
watermarks: this.options.watermarks,
199198
})
200199

201-
if (hasTerminalReporter(this.options.reporter))
200+
if (this.hasTerminalReporter(this.options.reporter))
202201
this.ctx.logger.log(c.blue(' % ') + c.dim('Coverage report from ') + c.yellow(this.name))
203202

204203
for (const reporter of this.options.reporter) {
@@ -232,10 +231,8 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
232231
this.updateThresholds({
233232
thresholds: resolvedThresholds,
234233
perFile: this.options.thresholds.perFile,
235-
configurationFile: {
236-
write: () => writeFileSync(configFilePath, configModule.generate().code, 'utf-8'),
237-
read: () => resolveConfig(configModule),
238-
},
234+
configurationFile: configModule,
235+
onUpdate: () => writeFileSync(configFilePath, configModule.generate().code, 'utf-8'),
239236
})
240237
}
241238
}
@@ -255,7 +252,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
255252
let merged: RawCoverage = { result: [] }
256253
let index = 0
257254

258-
for (const chunk of toSlices(uncoveredFiles, this.options.processingConcurrency)) {
255+
for (const chunk of this.toSlices(uncoveredFiles, this.options.processingConcurrency)) {
259256
if (debug.enabled) {
260257
index += chunk.length
261258
debug('Uncovered files %d/%d', index, uncoveredFiles.length)
@@ -334,7 +331,7 @@ export class V8CoverageProvider extends BaseCoverageProvider implements Coverage
334331
const coverageMap = libCoverage.createCoverageMap({})
335332
let index = 0
336333

337-
for (const chunk of toSlices(scriptCoverages, this.options.processingConcurrency)) {
334+
for (const chunk of this.toSlices(scriptCoverages, this.options.processingConcurrency)) {
338335
if (debug.enabled) {
339336
index += chunk.length
340337
debug('Converting %d/%d', index, scriptCoverages.length)
@@ -410,53 +407,3 @@ function normalizeTransformResults(fetchCache: Map<string, { result: FetchResult
410407

411408
return normalized
412409
}
413-
414-
function hasTerminalReporter(reporters: Options['reporter']) {
415-
return reporters.some(([reporter]) =>
416-
reporter === 'text'
417-
|| reporter === 'text-summary'
418-
|| reporter === 'text-lcov'
419-
|| reporter === 'teamcity')
420-
}
421-
422-
function toSlices<T>(array: T[], size: number): T[][] {
423-
return array.reduce<T[][]>((chunks, item) => {
424-
const index = Math.max(0, chunks.length - 1)
425-
const lastChunk = chunks[index] || []
426-
chunks[index] = lastChunk
427-
428-
if (lastChunk.length >= size)
429-
chunks.push([item])
430-
431-
else
432-
lastChunk.push(item)
433-
434-
return chunks
435-
}, [])
436-
}
437-
438-
function resolveConfig(configModule: ProxifiedModule<any>) {
439-
const mod = configModule.exports.default
440-
441-
try {
442-
// Check for "export default { test: {...} }"
443-
if (mod.$type === 'object')
444-
return mod
445-
446-
if (mod.$type === 'function-call') {
447-
// "export default defineConfig({ test: {...} })"
448-
if (mod.$args[0].$type === 'object')
449-
return mod.$args[0]
450-
451-
// "export default defineConfig(() => ({ test: {...} }))"
452-
if (mod.$args[0].$type === 'arrow-function-expression' && mod.$args[0].$body.$type === 'object')
453-
return mod.$args[0].$body
454-
}
455-
}
456-
catch (error) {
457-
// Reduce magicast's verbose errors to readable ones
458-
throw new Error(error instanceof Error ? error.message : String(error))
459-
}
460-
461-
throw new Error('Failed to update coverage thresholds. Configuration file is too complex.')
462-
}

packages/vitest/src/utils/coverage.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ export class BaseCoverageProvider {
1818
/**
1919
* Check if current coverage is above configured thresholds and bump the thresholds if needed
2020
*/
21-
updateThresholds({ thresholds: allThresholds, perFile, configurationFile }: {
21+
updateThresholds({ thresholds: allThresholds, perFile, configurationFile, onUpdate }: {
2222
thresholds: ResolvedThreshold[]
2323
perFile?: boolean
24-
configurationFile: { read: () => unknown; write: () => void }
24+
configurationFile: unknown // ProxifiedModule from magicast
25+
onUpdate: () => void
2526
}) {
2627
let updatedThresholds = false
2728

28-
const config = configurationFile.read()
29+
const config = resolveConfig(configurationFile)
2930
assertConfigurationModule(config)
3031

3132
for (const { coverageMap, thresholds, name } of allThresholds) {
@@ -63,7 +64,7 @@ export class BaseCoverageProvider {
6364
if (updatedThresholds) {
6465
// eslint-disable-next-line no-console
6566
console.log('Updating thresholds to configuration file. You may want to push with updated coverage thresholds.')
66-
configurationFile.write()
67+
onUpdate()
6768
}
6869
}
6970

@@ -200,6 +201,30 @@ export class BaseCoverageProvider {
200201

201202
return resolvedReporters
202203
}
204+
205+
hasTerminalReporter(reporters: ResolvedCoverageOptions['reporter']) {
206+
return reporters.some(([reporter]) =>
207+
reporter === 'text'
208+
|| reporter === 'text-summary'
209+
|| reporter === 'text-lcov'
210+
|| reporter === 'teamcity')
211+
}
212+
213+
toSlices<T>(array: T[], size: number): T[][] {
214+
return array.reduce<T[][]>((chunks, item) => {
215+
const index = Math.max(0, chunks.length - 1)
216+
const lastChunk = chunks[index] || []
217+
chunks[index] = lastChunk
218+
219+
if (lastChunk.length >= size)
220+
chunks.push([item])
221+
222+
else
223+
lastChunk.push(item)
224+
225+
return chunks
226+
}, [])
227+
}
203228
}
204229

205230
/**
@@ -228,3 +253,29 @@ function assertConfigurationModule(config: unknown): asserts config is { test: {
228253
throw new Error(`Unable to parse thresholds from configuration file: ${message}`)
229254
}
230255
}
256+
257+
function resolveConfig(configModule: any) {
258+
const mod = configModule.exports.default
259+
260+
try {
261+
// Check for "export default { test: {...} }"
262+
if (mod.$type === 'object')
263+
return mod
264+
265+
if (mod.$type === 'function-call') {
266+
// "export default defineConfig({ test: {...} })"
267+
if (mod.$args[0].$type === 'object')
268+
return mod.$args[0]
269+
270+
// "export default defineConfig(() => ({ test: {...} }))"
271+
if (mod.$args[0].$type === 'arrow-function-expression' && mod.$args[0].$body.$type === 'object')
272+
return mod.$args[0].$body
273+
}
274+
}
275+
catch (error) {
276+
// Reduce magicast's verbose errors to readable ones
277+
throw new Error(error instanceof Error ? error.message : String(error))
278+
}
279+
280+
throw new Error('Failed to update coverage thresholds. Configuration file is too complex.')
281+
}

0 commit comments

Comments
 (0)