vitest/test/coverage-test/fixtures/custom-provider.ts

122 lines
3.5 KiB
TypeScript

import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
import { normalize, resolve, sep } from 'node:path'
import { AfterSuiteRunMeta } from 'vitest'
import type { CoverageProvider, CoverageProviderModule, ReportContext, ResolvedCoverageOptions, Vitest } from 'vitest/node'
const CustomCoverageProviderModule: CoverageProviderModule = {
getProvider(): CoverageProvider {
return new CustomCoverageProvider()
},
takeCoverage() {
// @ts-expect-error -- untyped
globalThis.CUSTOM_PROVIDER_TAKE_COVERAGE = true
// @ts-expect-error -- untyped
if (!globalThis.CUSTOM_PROVIDER_START_COVERAGE) {
throw new Error('takeCoverage was called before startCoverage!')
}
return { customCoverage: 'Coverage report passed from workers to main thread' }
},
startCoverage() {
// @ts-expect-error -- untyped
globalThis.CUSTOM_PROVIDER_START_COVERAGE = true
},
stopCoverage() {
// @ts-expect-error -- untyped
if (!globalThis.CUSTOM_PROVIDER_START_COVERAGE) {
throw new Error('stopCoverage was called before startCoverage!')
}
// @ts-expect-error -- untyped
if (!globalThis.CUSTOM_PROVIDER_TAKE_COVERAGE) {
throw new Error('stopCoverage was called before takeCoverage!')
}
},
}
/**
* Provider that simply keeps track of the functions that were called
*/
class CustomCoverageProvider implements CoverageProvider {
name = 'custom-coverage-provider'
options!: ResolvedCoverageOptions
calls: Set<string> = new Set()
coverageReports: Set<string> = new Set()
transformedFiles: Set<string> = new Set()
initialize(ctx: Vitest) {
this.options = ctx.config.coverage
this.calls.add(`initialized ${ctx ? 'with' : 'without'} context`)
}
clean(force?: boolean) {
this.calls.add(`clean ${force ? 'with' : 'without'} force`)
}
onAfterSuiteRun(meta: AfterSuiteRunMeta) {
// Do not include coverage info here, as order of tests is not guaranteed
this.calls.add('onAfterSuiteRun')
// Keep coverage info separate from calls and ignore its order
this.coverageReports.add(JSON.stringify({
...meta,
// Project name keeps changing so let's simply check that its present
projectName: meta.projectName && typeof meta.projectName === 'string',
}))
}
generateCoverage(_reportContext: ReportContext) {
return {}
}
reportCoverage(coverage: unknown, reportContext?: ReportContext) {
this.calls.add(`reportCoverage with ${JSON.stringify(reportContext)}`)
const jsonReport = JSON.stringify({
calls: Array.from(this.calls.values()),
coverageReports: Array.from(this.coverageReports.values()).sort(),
transformedFiles: Array.from(this.transformedFiles.values()).sort(),
}, null, 2)
if (existsSync('./coverage')) {
rmSync('./coverage', { maxRetries: 10, recursive: true })
}
mkdirSync('./coverage')
writeFileSync('./coverage/custom-coverage-provider-report.json', jsonReport, 'utf-8')
}
onFileTransform(code: string, id: string) {
const filename = normalizeFilename(id).split('?')[0]
if (/\/fixtures\/src\//.test(filename)) {
this.transformedFiles.add(filename)
}
return { code }
}
resolveOptions(): ResolvedCoverageOptions {
this.calls.add('resolveOptions')
return this.options
}
}
export default CustomCoverageProviderModule
function normalizeFilename(filename: string) {
return normalize(filename)
.replace(normalize(process.cwd()), '<process-cwd>')
.replace(normalize(resolve(process.cwd(), '../../')), '<project-root>')
.replaceAll(sep, "/")
}