diff --git a/README.md b/README.md index 7cd6e90..61c5ca0 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Note the use of `incremental: true`, which speed up compilation massively. * `--no-typescript` or `-T`, disable automatic TypeScript compilation if `tsconfig.json` is found. * `--post-compile` or `-P`, the path to a file that will be executed after each typescript compilation. * `--check-coverage`, enables c8 check coverage; default is false +* `--coverage-html`, generates c8 html report; default is false ### Check coverage options * `--lines`, set the lines threshold when check coverage is active; default is 100 * `--functions`, set the functions threshold when check coverage is active; default is 100 @@ -143,6 +144,7 @@ The current supported options are: this configuration object, coverage reporting will be enabled. - `check-coverage` (boolean): Set to `true` to enable coverage checking. Omit to disable coverage checking. + - `coverage-html` (boolean): Set to `true` to generate an HTML coverage report. - `branches` (number): Define the percentage of acceptable coverage for branches. Default: 100. - `functions` (number): Define the percentage of acceptable coverage for diff --git a/borp.js b/borp.js index 305544c..07e839b 100755 --- a/borp.js +++ b/borp.js @@ -42,6 +42,7 @@ Options: -P, --post-compile Execute file after TypeScript compilation -r, --reporter Set reporter (can be used multiple times, default: spec) --check-coverage Enable coverage threshold checking + --coverage-html Generate HTML coverage report --lines Set lines coverage threshold (default: 100) --branches Set branches coverage threshold (default: 100) --functions Set functions coverage threshold (default: 100) @@ -82,6 +83,7 @@ const optionsConfig = { multiple: true }, 'check-coverage': { type: 'boolean' }, + 'coverage-html': { type: 'boolean' }, lines: { type: 'string', default: '100' }, branches: { type: 'string', default: '100' }, functions: { type: 'string', default: '100' }, @@ -225,11 +227,23 @@ try { exclude = exclude.map((file) => posix.join(localPrefix, file)) } const nycrc = await findUp(['.c8rc', '.c8rc.json', '.nycrc', '.nycrc.json'], { cwd: config.cwd }) + const nycrcConfig = nycrc ? JSON.parse(await readFile(nycrc, 'utf8')) : {} + const configuredReporters = Array.isArray(nycrcConfig.reporter) + ? nycrcConfig.reporter + : typeof nycrcConfig.reporter === 'string' && nycrcConfig.reporter.length > 0 + ? [nycrcConfig.reporter] + : ['text'] + + const coverageReporters = args.values['coverage-html'] + ? Array.from(new Set([...configuredReporters, 'html'])) + : configuredReporters + + delete nycrcConfig.reporter const report = Report({ - reporter: ['text'], + reporter: coverageReporters, tempDirectory: covDir, exclude, - ...nycrc && JSON.parse(await readFile(nycrc, 'utf8')) + ...nycrcConfig }) if (args.values['check-coverage']) { diff --git a/fixtures/conf/cov-html.yaml b/fixtures/conf/cov-html.yaml new file mode 100644 index 0000000..eda810d --- /dev/null +++ b/fixtures/conf/cov-html.yaml @@ -0,0 +1,2 @@ +coverage: + coverage-html: true diff --git a/lib/conf.js b/lib/conf.js index ecaef88..9dc88d8 100644 --- a/lib/conf.js +++ b/lib/conf.js @@ -67,6 +67,10 @@ async function loadConfig () { result.push('--check-coverage') continue } + if (option === 'coverage-html' && value === true) { + result.push('--coverage-html') + continue + } if (allowed.includes(option) === false) continue if (isNaN(parseInt(value, 10)) === true) continue result.push(`--${option}=${value}`) diff --git a/test/cli-help.test.js b/test/cli-help.test.js index fdcdf80..6573247 100644 --- a/test/cli-help.test.js +++ b/test/cli-help.test.js @@ -23,6 +23,7 @@ test('invalid option shows help text', { skip: isWindows }, async () => { strictEqual(error.stderr.includes('Usage: borp [options] [files...]'), true, 'Should show usage line') strictEqual(error.stderr.includes('--help'), true, 'Should show help option') strictEqual(error.stderr.includes('--coverage'), true, 'Should show coverage option') + strictEqual(error.stderr.includes('--coverage-html'), true, 'Should show html option') strictEqual(error.stderr.includes('Examples:'), true, 'Should show examples section') return true }) @@ -74,6 +75,7 @@ test('--help option shows help text and exits successfully', { skip: isWindows } strictEqual(stdout.includes('Usage: borp [options] [files...]'), true, 'Should show usage line') strictEqual(stdout.includes('--help'), true, 'Should show help option') strictEqual(stdout.includes('--coverage'), true, 'Should show coverage option') + strictEqual(stdout.includes('--coverage-html'), true, 'Should show html option') strictEqual(stdout.includes('Examples:'), true, 'Should show examples section') strictEqual(stdout.includes('borp --coverage'), true, 'Should show coverage example') }) diff --git a/test/config.test.js b/test/config.test.js index d0b0a92..ad86aed 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -58,3 +58,16 @@ test('adds coverage options', async () => { true ) }) + +test('adds coverage html', async () => { + const cwd = join(import.meta.url, '..', 'fixtures', 'ts-esm') + const { stderr } = await execa('node', [borp], { + cwd, + env: { + BORP_CONF_FILE: path.join(confFilesDir, 'cov-html.yaml') + } + }) + + strictEqual(stderr.includes('--coverage'), true) + strictEqual(stderr.includes('--coverage-html'), true) +}) diff --git a/test/coverage.test.js b/test/coverage.test.js index 87be321..2988395 100644 --- a/test/coverage.test.js +++ b/test/coverage.test.js @@ -2,6 +2,8 @@ import { test } from 'node:test' import { match, doesNotMatch, fail, equal, AssertionError } from 'node:assert' import { execa } from 'execa' import { join } from 'desm' +import { mkdtemp, rm, cp, access } from 'node:fs/promises' +import path from 'node:path' delete process.env.GITHUB_ACTION const borp = join(import.meta.url, '..', 'borp.js') @@ -19,6 +21,29 @@ test('coverage', async () => { match(res.stdout, /add\.ts/) }) +test('coverage supports html reporter', async () => { + const repoRoot = path.dirname(borp) + const tmpDir = await mkdtemp(path.join(repoRoot, '.test-coverage-html-')) + const fixtureDir = join(import.meta.url, '..', 'fixtures', 'ts-esm') + const cwd = path.join(tmpDir, 'project') + + try { + await cp(fixtureDir, cwd, { recursive: true }) + const res = await execa('node', [ + borp, + '--coverage', + '--coverage-html' + ], { + cwd + }) + + match(res.stdout, /% Stmts/) + await access(path.join(cwd, 'coverage', 'index.html')) + } finally { + await rm(tmpDir, { recursive: true, force: true }) + } +}) + test('coverage excludes', async () => { const res = await execa('node', [ borp,