Skip to content

Commit

Permalink
refactor: internal coverage typings (#2713)
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Jan 21, 2023
1 parent 5df522f commit 1503e15
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 39 deletions.
40 changes: 28 additions & 12 deletions packages/coverage-c8/src/provider.ts
Expand Up @@ -4,20 +4,25 @@ import type { Profiler } from 'inspector'
import { takeCoverage } from 'v8'
import { extname, resolve } from 'pathe'
import type { RawSourceMap } from 'vite-node'
import { configDefaults } from 'vitest/config'
import { coverageConfigDefaults } from 'vitest/config'
// eslint-disable-next-line no-restricted-imports
import type { CoverageC8Options, CoverageProvider, ReportContext, ResolvedCoverageOptions } from 'vitest'
import type { Vitest } from 'vitest/node'
import type { Report } from 'c8'
// @ts-expect-error missing types
import createReport from 'c8/lib/report.js'
// @ts-expect-error missing types
import { checkCoverages } from 'c8/lib/commands/check-coverage.js'

type Options =
& ResolvedCoverageOptions<'c8'>
& { tempDirectory: string }

export class C8CoverageProvider implements CoverageProvider {
name = 'c8'

ctx!: Vitest
options!: ResolvedCoverageOptions & { provider: 'c8' }
options!: Options

initialize(ctx: Vitest) {
this.ctx = ctx
Expand Down Expand Up @@ -47,7 +52,7 @@ export class C8CoverageProvider implements CoverageProvider {
async reportCoverage({ allTestsRun }: ReportContext = {}) {
takeCoverage()

const options = {
const options: ConstructorParameters<typeof Report>[0] = {
...this.options,
all: this.options.all && allTestsRun,
}
Expand Down Expand Up @@ -151,10 +156,26 @@ export class C8CoverageProvider implements CoverageProvider {
await fs.rm(this.options.tempDirectory, { recursive: true, force: true, maxRetries: 10 })
}
}
function resolveC8Options(options: CoverageC8Options, root: string) {
const resolved = {
...configDefaults.coverage,
...options as any,

function resolveC8Options(options: CoverageC8Options, root: string): Options {
const reportsDirectory = resolve(root, options.reportsDirectory || coverageConfigDefaults.reportsDirectory)
const reporter = options.reporter || coverageConfigDefaults.reporter

const resolved: Options = {
...coverageConfigDefaults,

// Provider specific defaults
excludeNodeModules: true,
allowExternal: false,

// User's options
...options,

// Resolved fields
provider: 'c8',
tempDirectory: process.env.NODE_V8_COVERAGE || resolve(reportsDirectory, 'tmp'),
reporter: Array.isArray(reporter) ? reporter : [reporter],
reportsDirectory,
}

if (options['100']) {
Expand All @@ -164,10 +185,5 @@ function resolveC8Options(options: CoverageC8Options, root: string) {
resolved.statements = 100
}

resolved.reporter = resolved.reporter || []
resolved.reporter = Array.isArray(resolved.reporter) ? resolved.reporter : [resolved.reporter]
resolved.reportsDirectory = resolve(root, resolved.reportsDirectory)
resolved.tempDirectory = process.env.NODE_V8_COVERAGE || resolve(resolved.reportsDirectory, 'tmp')

return resolved
}
28 changes: 17 additions & 11 deletions packages/coverage-istanbul/src/provider.ts
Expand Up @@ -3,7 +3,7 @@ import { existsSync, promises as fs } from 'fs'
import { relative, resolve } from 'pathe'
import type { TransformPluginContext } from 'rollup'
import type { AfterSuiteRunMeta, CoverageIstanbulOptions, CoverageProvider, ReportContext, ResolvedCoverageOptions, Vitest } from 'vitest'
import { configDefaults, defaultExclude, defaultInclude } from 'vitest/config'
import { coverageConfigDefaults, defaultExclude, defaultInclude } from 'vitest/config'
import libReport from 'istanbul-lib-report'
import reports from 'istanbul-reports'
import type { CoverageMap } from 'istanbul-lib-coverage'
Expand All @@ -14,6 +14,8 @@ import { type Instrumenter, createInstrumenter } from 'istanbul-lib-instrument'
import _TestExclude from 'test-exclude'
import { COVERAGE_STORE_KEY } from './constants'

type Options = ResolvedCoverageOptions<'istanbul'>

type Threshold = 'lines' | 'functions' | 'statements' | 'branches'

interface TestExclude {
Expand All @@ -33,7 +35,7 @@ export class IstanbulCoverageProvider implements CoverageProvider {
name = 'istanbul'

ctx!: Vitest
options!: ResolvedCoverageOptions & CoverageIstanbulOptions & { provider: 'istanbul' }
options!: Options
instrumenter!: Instrumenter
testExclude!: InstanceType<TestExclude>

Expand Down Expand Up @@ -70,7 +72,7 @@ export class IstanbulCoverageProvider implements CoverageProvider {
})
}

resolveOptions(): ResolvedCoverageOptions {
resolveOptions() {
return this.options
}

Expand Down Expand Up @@ -121,7 +123,7 @@ export class IstanbulCoverageProvider implements CoverageProvider {
})

for (const reporter of this.options.reporter) {
reports.create(reporter as any, {
reports.create(reporter, {
skipFull: this.options.skipFull,
projectRoot: this.ctx.config.root,
}).execute(context)
Expand Down Expand Up @@ -217,19 +219,23 @@ export class IstanbulCoverageProvider implements CoverageProvider {
}
}

function resolveIstanbulOptions(options: CoverageIstanbulOptions, root: string) {
const reportsDirectory = resolve(root, options.reportsDirectory || configDefaults.coverage.reportsDirectory!)
function resolveIstanbulOptions(options: CoverageIstanbulOptions, root: string): Options {
const reportsDirectory = resolve(root, options.reportsDirectory || coverageConfigDefaults.reportsDirectory)
const reporter = options.reporter || coverageConfigDefaults.reporter

const resolved: Options = {
...coverageConfigDefaults,

const resolved = {
...configDefaults.coverage,
// User's options
...options,

// Resolved fields
provider: 'istanbul',
reportsDirectory,
tempDirectory: resolve(reportsDirectory, 'tmp'),
reporter: Array.isArray(options.reporter) ? options.reporter : [options.reporter],
reporter: Array.isArray(reporter) ? reporter : [reporter],
}

return resolved as ResolvedCoverageOptions & { provider: 'istanbul' }
return resolved
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/config.ts
Expand Up @@ -5,7 +5,7 @@ export interface UserConfig extends ViteUserConfig {
}

// will import vitest declare test in module 'vite'
export { configDefaults, defaultInclude, defaultExclude } from './defaults'
export { configDefaults, defaultInclude, defaultExclude, coverageConfigDefaults } from './defaults'

export type { ConfigEnv }
export type UserConfigFn = (env: ConfigEnv) => UserConfig | Promise<UserConfig>
Expand Down
8 changes: 3 additions & 5 deletions packages/vitest/src/defaults.ts
Expand Up @@ -24,21 +24,19 @@ const defaultCoverageExcludes = [
'**/.{eslint,mocha,prettier}rc.{js,cjs,yml}',
]

const coverageConfigDefaults = {
all: false,
// These are the generic defaults for coverage. Providers may also set some provider speficic defaults.
export const coverageConfigDefaults: ResolvedCoverageOptions = {
provider: 'c8',
enabled: false,
clean: true,
cleanOnRerun: true,
reportsDirectory: './coverage',
excludeNodeModules: true,
exclude: defaultCoverageExcludes,
reporter: ['text', 'html', 'clover', 'json'],
allowExternal: false,
// default extensions used by c8, plus '.vue' and '.svelte'
// see https://github.com/istanbuljs/schema/blob/master/default-extension.js
extension: ['.js', '.cjs', '.mjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.vue', '.svelte'],
} as ResolvedCoverageOptions
}

export const fakeTimersDefaults = {
loopLimit: 10_000,
Expand Down
32 changes: 24 additions & 8 deletions packages/vitest/src/types/coverage.ts
Expand Up @@ -53,14 +53,30 @@ export type CoverageReporter =
| 'text-summary'
| 'text'

export type CoverageOptions =
| BaseCoverageOptions & { provider?: null | CoverageProviderModule }
| CoverageC8Options & { provider?: 'c8' }
| CoverageIstanbulOptions & { provider?: 'istanbul' }

export type ResolvedCoverageOptions =
& { tempDirectory: string }
& Required<CoverageOptions>
type Provider = 'c8' | 'istanbul' | CoverageProviderModule | undefined

export type CoverageOptions<T extends Provider = Provider> =
T extends CoverageProviderModule ? ({ provider: T } & BaseCoverageOptions) :
T extends 'istanbul' ? ({ provider: T } & CoverageIstanbulOptions) :
({ provider?: T } & CoverageC8Options)

/** Fields that have default values. Internally these will always be defined. */
type FieldsWithDefaultValues =
| 'enabled'
| 'clean'
| 'cleanOnRerun'
| 'reportsDirectory'
| 'exclude'
| 'extension'
| 'reporter'

export type ResolvedCoverageOptions<T extends Provider = Provider> =
& CoverageOptions<T>
& Required<Pick<CoverageOptions<T>, FieldsWithDefaultValues>>
// Resolved fields which may have different typings as public configuration API has
& {
reporter: CoverageReporter[]
}

export interface BaseCoverageOptions {
/**
Expand Down
12 changes: 10 additions & 2 deletions test/coverage-test/test/configuration-options.test-d.ts
Expand Up @@ -21,8 +21,16 @@ test('providers, custom', () => {
return {
name: 'custom-provider',
initialize(_: Vitest) {},
resolveOptions() {
return {} as ResolvedCoverageOptions
resolveOptions(): ResolvedCoverageOptions {
return {
clean: true,
cleanOnRerun: true,
enabled: true,
exclude: ['string'],
extension: ['string'],
reporter: ['html', 'json'],
reportsDirectory: 'string',
}
},
clean(_: boolean) {},
onBeforeFilesRun() {},
Expand Down

0 comments on commit 1503e15

Please sign in to comment.