-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(coverage): custom providers to work inside worker threads (#2817)
- Loading branch information
1 parent
94247f1
commit 81604bc
Showing
15 changed files
with
274 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,47 @@ | ||
import { importModule } from 'local-pkg' | ||
import type { CoverageOptions, CoverageProvider, CoverageProviderModule } from '../types' | ||
|
||
export const CoverageProviderMap = { | ||
interface Loader { | ||
executeId: (id: string) => Promise<{ default: CoverageProviderModule }> | ||
} | ||
|
||
export const CoverageProviderMap: Record<string, string> = { | ||
c8: '@vitest/coverage-c8', | ||
istanbul: '@vitest/coverage-istanbul', | ||
} | ||
|
||
export async function resolveCoverageProvider(provider: NonNullable<CoverageOptions['provider']>) { | ||
if (typeof provider === 'string') { | ||
const pkg = CoverageProviderMap[provider] | ||
if (!pkg) | ||
throw new Error(`Unknown coverage provider: ${provider}`) | ||
return await importModule<CoverageProviderModule>(pkg) | ||
async function resolveCoverageProviderModule(options: CoverageOptions & Required<Pick<CoverageOptions, 'provider'>>, loader: Loader) { | ||
const provider = options.provider | ||
|
||
if (provider === 'c8' || provider === 'istanbul') | ||
return await importModule<CoverageProviderModule>(CoverageProviderMap[provider]) | ||
|
||
let customProviderModule | ||
|
||
try { | ||
customProviderModule = await loader.executeId(options.customProviderModule) | ||
} | ||
else { | ||
return provider | ||
catch (error) { | ||
throw new Error(`Failed to load custom CoverageProviderModule from ${options.customProviderModule}`, { cause: error }) | ||
} | ||
|
||
if (customProviderModule.default == null) | ||
throw new Error(`Custom CoverageProviderModule loaded from ${options.customProviderModule} was not the default export`) | ||
|
||
return customProviderModule.default | ||
} | ||
|
||
export async function getCoverageProvider(options?: CoverageOptions): Promise<CoverageProvider | null> { | ||
if (options?.enabled && options?.provider) { | ||
const { getProvider } = await resolveCoverageProvider(options.provider) | ||
export async function getCoverageProvider(options: CoverageOptions, loader: Loader): Promise<CoverageProvider | null> { | ||
if (options.enabled && options.provider) { | ||
const { getProvider } = await resolveCoverageProviderModule(options, loader) | ||
return await getProvider() | ||
} | ||
return null | ||
} | ||
|
||
export async function takeCoverageInsideWorker(options: CoverageOptions) { | ||
export async function takeCoverageInsideWorker(options: CoverageOptions, loader: Loader) { | ||
if (options.enabled && options.provider) { | ||
const { takeCoverage } = await resolveCoverageProvider(options.provider) | ||
const { takeCoverage } = await resolveCoverageProviderModule(options, loader) | ||
return await takeCoverage?.() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
test/coverage-test/coverage-report-tests/__snapshots__/custom.report.test.ts.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Vitest Snapshot v1 | ||
|
||
exports[`custom json report 1`] = ` | ||
{ | ||
"calls": [ | ||
"initialized with context", | ||
"resolveOptions", | ||
"clean with force", | ||
"onBeforeFilesRun", | ||
"onAfterSuiteRun with {\\"coverage\\":{\\"customCoverage\\":\\"Coverage report passed from workers to main thread\\"}}", | ||
"reportCoverage with {\\"allTestsRun\\":true}", | ||
], | ||
"transformedFiles": [ | ||
"<process-cwd>/src/Counter/Counter.component.ts", | ||
"<process-cwd>/src/Counter/Counter.vue", | ||
"<process-cwd>/src/Counter/index.ts", | ||
"<process-cwd>/src/Defined.vue", | ||
"<process-cwd>/src/Hello.vue", | ||
"<process-cwd>/src/another-setup.ts", | ||
"<process-cwd>/src/implicitElse.ts", | ||
"<process-cwd>/src/importEnv.ts", | ||
"<process-cwd>/src/index.mts", | ||
"<process-cwd>/src/utils.ts", | ||
], | ||
} | ||
`; |
12 changes: 12 additions & 0 deletions
12
test/coverage-test/coverage-report-tests/custom.report.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* | ||
* Custom coverage provider specific test cases | ||
*/ | ||
|
||
import { readFileSync } from 'fs' | ||
import { expect, test } from 'vitest' | ||
|
||
test('custom json report', async () => { | ||
const report = readFileSync('./coverage/custom-coverage-provider-report.json', 'utf-8') | ||
|
||
expect(JSON.parse(report)).toMatchSnapshot() | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs' | ||
import type { AfterSuiteRunMeta, CoverageProvider, CoverageProviderModule, ReportContext, ResolvedCoverageOptions, Vitest } from 'vitest' | ||
|
||
import { normalizeFilename } from './coverage-report-tests/utils' | ||
|
||
const CustomCoverageProviderModule: CoverageProviderModule = { | ||
getProvider(): CoverageProvider { | ||
return new CustomCoverageProvider() | ||
}, | ||
|
||
takeCoverage() { | ||
return { customCoverage: 'Coverage report passed from workers to main thread' } | ||
}, | ||
} | ||
|
||
/** | ||
* 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() | ||
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`) | ||
} | ||
|
||
onBeforeFilesRun() { | ||
this.calls.add('onBeforeFilesRun') | ||
} | ||
|
||
onAfterSuiteRun(meta: AfterSuiteRunMeta) { | ||
this.calls.add(`onAfterSuiteRun with ${JSON.stringify(meta)}`) | ||
} | ||
|
||
reportCoverage(reportContext?: ReportContext) { | ||
this.calls.add(`reportCoverage with ${JSON.stringify(reportContext)}`) | ||
|
||
const jsonReport = JSON.stringify({ | ||
calls: Array.from(this.calls.values()), | ||
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 (/\/src\//.test(filename)) | ||
this.transformedFiles.add(filename) | ||
|
||
return { code } | ||
} | ||
|
||
resolveOptions(): ResolvedCoverageOptions { | ||
this.calls.add('resolveOptions') | ||
|
||
return this.options | ||
} | ||
} | ||
|
||
export default CustomCoverageProviderModule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.