Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add --config option and documentation on options and configs #308

Merged
merged 9 commits into from
Jul 10, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,42 @@ c8 node foo.js

The above example will output coverage metrics for `foo.js`.

## CLI Options / Configuration

c8 can be configured via command-line options.

Options can also be specified in the `c8` section of `package.json`, and/or in a JSON configuration file by passing its
path on the command line with `--config` or `-c`. When using `package.json` configuration or a dedicated configuration
file, omit the `--` prefix from the long-form of the desired command-line option. If a configuration file is not
given on the command-line, c8 searches for files named `.c8rc`, `.c8rc.json`, `.nycrc`, or `.nycrc.json`, starting from
`cwd` and walking up the filesystem tree.
clemyan marked this conversation as resolved.
Show resolved Hide resolved

Here is a list of common options. Run `c8 --help` for the full list and documentation.

| Option | Description | Type | Default |
| ------ | ----------- | ---- | ------- |
| `-c`, `--config` | path to JSON configuration file | `string` | See above |
| `-r`, `--reporter` | coverage reporter(s) to use | `Array<string>` | `['text']` |
| `-o`, `--reports-dir`, `--report-dir` | directory where coverage reports will be output to | `string` | `./coverage` |
| `--all` | see [section below](#checking-for-full-source-coverage-using---all) for more info | `Array<string>` | `['**']`|
| `--src` | see [section below](#checking-for-full-source-coverage-using---all) for more info | `Array<string>` | `[process.cwd()]`|
| `-n`, `--include` | see [section below](#checking-for-full-source-coverage-using---all) for more info | `Array<string>` | `[]`|
| `-x`, `--exclude` | see [section below](#checking-for-full-source-coverage-using---all) for more info | `Array<string>` | [list](https://github.com/istanbuljs/schema/blob/master/default-exclude.js) |
| `--skip-full` | do not show files with 100% statement, branch, and function coverage | `boolean` | `false` |
| `--check-coverage` | check whether coverage is within thresholds provided | `boolean` | `false` |
| `--temp-directory` | directory V8 coverage data is written to and read from | `string` | `process.env.NODE_V8_COVERAGE` |
| `--clean` | should temp files be deleted before script execution | `boolean` | `true` |

## Checking for "full" source coverage using `--all`

By default v8 will only give us coverage for files that were loaded by the engine. If there are source files in your
project that are flexed in production but not in your tests, your coverage numbers will not reflect this. For example,
if your project's `main.js` loads `a.js` and `b.js` but your unit tests only load `a.js` your total coverage
could show as `100%` for `a.js` when in fact both `main.js` and `b.js` are uncovered.

By supplying `--all` to c8, all files in `cwd` that pass the `--include` and `--exclude` flag checks, will be loaded into the
report. If any of those files remain uncovered they will be factored into the report with a default of 0% coverage.
By supplying `--all` to c8, all files in directories specified with `--src` (defaults to `cwd`) that pass the `--include`
and `--exclude` flag checks, will be loaded into the report. If any of those files remain uncovered they will be factored
into the report with a default of 0% coverage.

## c8 report

Expand Down
77 changes: 47 additions & 30 deletions lib/parse-args.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,112 @@ const Yargs = require('yargs/yargs')
const parser = require('yargs-parser')
const { resolve } = require('path')

const configPath = findUp.sync(['.c8rc', '.c8rc.json', '.nycrc', '.nycrc.json'])
const config = configPath ? JSON.parse(readFileSync(configPath)) : {}

function buildYargs (withCommands = false) {
const yargs = Yargs([])
.usage('$0 [opts] [script] [opts]')
.options('config', {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this was missing, thank you for adding.

alias: 'c',
config: true,
describe: 'path to JSON configuration file',
configParser: (path) => JSON.parse(readFileSync(path)),
default: () => findUp.sync(['.c8rc', '.c8rc.json', '.nycrc', '.nycrc.json'])
})
.option('reporter', {
alias: 'r',
group: 'Reporting options',
describe: 'coverage reporter(s) to use',
default: 'text'
})
.option('reports-dir', {
alias: ['o', 'report-dir'],
group: 'Reporting options',
describe: 'directory where coverage reports will be output to',
default: './coverage'
})
.options('all', {
default: false,
type: 'boolean',
group: 'Reporting options',
describe: 'supplying --all will cause c8 to consider all src files in the current working directory ' +
'when the determining coverage. Respects include/exclude.'
})
.options('src', {
default: undefined,
type: 'string',
group: 'Reporting options',
describe: 'supplying --src will override cwd as the default location where --all looks for src files. --src can be ' +
'supplied multiple times and each directory will be included. This allows for workspaces spanning multiple projects'
})
.option('include', {
alias: 'n',
default: [],
group: 'Reporting options',
describe: 'a list of specific files that should be covered (glob patterns are supported)'
})
.option('exclude', {
alias: 'x',
default: defaultExclude,
group: 'Reporting options',
describe: 'a list of specific files and directories that should be excluded from coverage (glob patterns are supported)'
})
.option('exclude-after-remap', {
alias: 'a',
type: 'boolean',
default: false,
group: 'Reporting options',
describe: 'apply exclude logic to files after they are remapped by a source-map'
})
.option('include', {
alias: 'n',
default: [],
describe: 'a list of specific files that should be covered (glob patterns are supported)'
.options('skip-full', {
default: false,
type: 'boolean',
group: 'Reporting options',
describe: 'do not show files with 100% statement, branch, and function coverage'
})
.option('check-coverage', {
default: false,
type: 'boolean',
group: 'Coverage thresholds',
description: 'check whether coverage is within thresholds provided'
})
.option('branches', {
default: 0,
group: 'Coverage thresholds',
description: 'what % of branches must be covered?',
type: 'number'
})
.option('functions', {
default: 0,
group: 'Coverage thresholds',
description: 'what % of functions must be covered?',
type: 'number'
})
.option('lines', {
default: 90,
group: 'Coverage thresholds',
description: 'what % of lines must be covered?',
type: 'number'
})
.option('statements', {
default: 0,
group: 'Coverage thresholds',
description: 'what % of statements must be covered?',
type: 'number'
})
.option('per-file', {
default: false,
group: 'Coverage thresholds',
description: 'check thresholds per file',
type: 'boolean'
})
.option('temp-directory', {
describe: 'directory V8 coverage data is written to and read from',
default: process.env.NODE_V8_COVERAGE
})
.option('clean', {
default: true,
type: 'boolean',
describe: 'should temp files be deleted before script execution'
})
.option('resolve', {
default: '',
describe: 'resolve paths to alternate base directory'
Expand All @@ -84,36 +124,13 @@ function buildYargs (withCommands = false) {
type: 'boolean',
describe: 'omit any paths that are not absolute, e.g., internal/net.js'
})
.option('clean', {
default: true,
type: 'boolean',
describe: 'should temp files be deleted before script execution'
})
.options('all', {
default: false,
type: 'boolean',
describe: 'supplying --all will cause c8 to consider all src files in the current working directory ' +
'when the determining coverage. Respects include/exclude.'
})
.options('allowExternal', {
default: false,
type: 'boolean',
describe: 'supplying --allowExternal will cause c8 to allow files from outside of your cwd. This applies both to ' +
'files discovered in coverage temp files and also src files discovered if using the --all flag.'
})
.options('src', {
default: undefined,
type: 'string',
describe: 'supplying --src will override cwd as the default location where --all looks for src files. --src can be ' +
'supplied multiple times and each directory will be included. This allows for workspaces spanning multiple projects'
})
.options('skip-full', {
default: false,
type: 'boolean',
describe: 'do not show files with 100% statement, branch, and function coverage'
})
.pkgConf('c8')
.config(config)
.demandCommand(1)
.check((argv) => {
if (!argv.tempDirectory) {
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/config/.c8rc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"temp-directory": "./foo",
"lines": 101
}
40 changes: 20 additions & 20 deletions test/integration.js_10.snap
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,12 @@ hey
--------------------------|---------|----------|---------|---------|--------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------------|---------|----------|---------|---------|--------------------------------
All files | 73.12 | 58.02 | 60.53 | 73.12 |
All files | 73.7 | 59.76 | 62.5 | 73.7 |
bin | 78.85 | 60 | 66.67 | 78.85 |
c8.js | 78.85 | 60 | 66.67 | 78.85 | 22,27-29,32-33,41-43,50-51
lib | 76.27 | 51.79 | 69.57 | 76.27 |
lib | 76.94 | 54.39 | 72 | 76.94 |
is-cjs-esm-bridge.js | 90 | 25 | 100 | 90 | 9
parse-args.js | 96.39 | 45.45 | 100 | 96.39 | 120-121,129-130,143-144
parse-args.js | 96.72 | 58.33 | 100 | 96.72 | 137-138,146-147,160-161
report.js | 75.16 | 58.33 | 78.57 | 75.16 | ...247,274-275,281-283,304-309
source-map-from-file.js | 45 | 100 | 0 | 45 | 39-50,52-67,69-77,81-98
lib/commands | 45.65 | 75 | 16.67 | 45.65 |
Expand All @@ -167,9 +167,9 @@ All files | 73.12 | 58.02 | 60.53 | 73.12 |
async.js | 100 | 100 | 100 | 100 |
normal.js | 75 | 66.67 | 33.33 | 75 | 14-16,18-20
--------------------------|---------|----------|---------|---------|--------------------------------
,ERROR: Coverage for lines (73.12%) does not meet global threshold (101%)
ERROR: Coverage for branches (58.02%) does not meet global threshold (82%)
ERROR: Coverage for statements (73.12%) does not meet global threshold (95%)
,ERROR: Coverage for lines (73.7%) does not meet global threshold (101%)
ERROR: Coverage for branches (59.76%) does not meet global threshold (82%)
ERROR: Coverage for statements (73.7%) does not meet global threshold (95%)
"
`;

Expand All @@ -185,8 +185,8 @@ ERROR: Coverage for statements (93.55%) does not meet threshold (95%) for lib/co
ERROR: Coverage for lines (90%) does not meet threshold (101%) for lib/is-cjs-esm-bridge.js
ERROR: Coverage for branches (25%) does not meet threshold (82%) for lib/is-cjs-esm-bridge.js
ERROR: Coverage for statements (90%) does not meet threshold (95%) for lib/is-cjs-esm-bridge.js
ERROR: Coverage for lines (96.39%) does not meet threshold (101%) for lib/parse-args.js
ERROR: Coverage for branches (45.45%) does not meet threshold (82%) for lib/parse-args.js
ERROR: Coverage for lines (96.72%) does not meet threshold (101%) for lib/parse-args.js
ERROR: Coverage for branches (58.33%) does not meet threshold (82%) for lib/parse-args.js
ERROR: Coverage for lines (75.16%) does not meet threshold (101%) for lib/report.js
ERROR: Coverage for branches (58.33%) does not meet threshold (82%) for lib/report.js
ERROR: Coverage for statements (75.16%) does not meet threshold (95%) for lib/report.js
Expand All @@ -202,9 +202,9 @@ ERROR: Coverage for statements (75%) does not meet threshold (95%) for test/fixt
exports[`c8 check-coverage exits with 0 if coverage within threshold 1`] = `",,"`;

exports[`c8 check-coverage exits with 1 if coverage is below threshold 1`] = `
",,ERROR: Coverage for lines (73.12%) does not meet global threshold (101%)
ERROR: Coverage for branches (58.02%) does not meet global threshold (82%)
ERROR: Coverage for statements (73.12%) does not meet global threshold (95%)
",,ERROR: Coverage for lines (73.7%) does not meet global threshold (101%)
ERROR: Coverage for branches (59.76%) does not meet global threshold (82%)
ERROR: Coverage for statements (73.7%) does not meet global threshold (95%)
"
`;

Expand Down Expand Up @@ -287,12 +287,12 @@ exports[`c8 report generates report from existing temporary files 1`] = `
",--------------------------|---------|----------|---------|---------|--------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------------|---------|----------|---------|---------|--------------------------------
All files | 73.12 | 58.02 | 60.53 | 73.12 |
All files | 73.7 | 59.76 | 62.5 | 73.7 |
bin | 78.85 | 60 | 66.67 | 78.85 |
c8.js | 78.85 | 60 | 66.67 | 78.85 | 22,27-29,32-33,41-43,50-51
lib | 76.27 | 51.79 | 69.57 | 76.27 |
lib | 76.94 | 54.39 | 72 | 76.94 |
is-cjs-esm-bridge.js | 90 | 25 | 100 | 90 | 9
parse-args.js | 96.39 | 45.45 | 100 | 96.39 | 120-121,129-130,143-144
parse-args.js | 96.72 | 58.33 | 100 | 96.72 | 137-138,146-147,160-161
report.js | 75.16 | 58.33 | 78.57 | 75.16 | ...247,274-275,281-283,304-309
source-map-from-file.js | 45 | 100 | 0 | 45 | 39-50,52-67,69-77,81-98
lib/commands | 45.65 | 75 | 16.67 | 45.65 |
Expand All @@ -309,12 +309,12 @@ exports[`c8 report supports --check-coverage, when generating reports 1`] = `
",--------------------------|---------|----------|---------|---------|--------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------------|---------|----------|---------|---------|--------------------------------
All files | 73.12 | 58.02 | 60.53 | 73.12 |
All files | 73.7 | 59.76 | 62.5 | 73.7 |
bin | 78.85 | 60 | 66.67 | 78.85 |
c8.js | 78.85 | 60 | 66.67 | 78.85 | 22,27-29,32-33,41-43,50-51
lib | 76.27 | 51.79 | 69.57 | 76.27 |
lib | 76.94 | 54.39 | 72 | 76.94 |
is-cjs-esm-bridge.js | 90 | 25 | 100 | 90 | 9
parse-args.js | 96.39 | 45.45 | 100 | 96.39 | 120-121,129-130,143-144
parse-args.js | 96.72 | 58.33 | 100 | 96.72 | 137-138,146-147,160-161
report.js | 75.16 | 58.33 | 78.57 | 75.16 | ...247,274-275,281-283,304-309
source-map-from-file.js | 45 | 100 | 0 | 45 | 39-50,52-67,69-77,81-98
lib/commands | 45.65 | 75 | 16.67 | 45.65 |
Expand All @@ -324,9 +324,9 @@ All files | 73.12 | 58.02 | 60.53 | 73.12 |
async.js | 100 | 100 | 100 | 100 |
normal.js | 75 | 66.67 | 33.33 | 75 | 14-16,18-20
--------------------------|---------|----------|---------|---------|--------------------------------
,ERROR: Coverage for lines (73.12%) does not meet global threshold (101%)
ERROR: Coverage for branches (58.02%) does not meet global threshold (82%)
ERROR: Coverage for statements (73.12%) does not meet global threshold (95%)
,ERROR: Coverage for lines (73.7%) does not meet global threshold (101%)
ERROR: Coverage for branches (59.76%) does not meet global threshold (82%)
ERROR: Coverage for statements (73.7%) does not meet global threshold (95%)
"
`;

Expand Down
21 changes: 21 additions & 0 deletions test/parse-args.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,25 @@ describe('parse-args', () => {
process.env.NODE_V8_COVERAGE = NODE_V8_COVERAGE
})
})

describe('--config', () => {
it('should resolve to .nycrc at cwd', () => {
const argv = buildYargs().parse(['node', 'c8', 'my-app'])
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the tests 😄

argv.lines.should.be.equal(95)
})
it('should use config file specified in --config', () => {
const argv = buildYargs().parse(['node', 'c8', '--config', require.resolve('./fixtures/config/.c8rc.json')])
argv.lines.should.be.equal(101)
argv.tempDirectory.should.be.equal('./foo')
})
it('should have -c as an alias', () => {
const argv = buildYargs().parse(['node', 'c8', '-c', require.resolve('./fixtures/config/.c8rc.json')])
argv.lines.should.be.equal(101)
argv.tempDirectory.should.be.equal('./foo')
})
it('should respect options on the command line over config file', () => {
const argv = buildYargs().parse(['node', 'c8', '--lines', '100', '--config', require.resolve('./fixtures/config/.c8rc.json')])
argv.lines.should.be.equal(100)
})
})
})