diff --git a/README.md b/README.md index 772748a0..228ad8af 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,31 @@ c8 node foo.js The above example will output coverage metrics for `foo.js`. +## CLI Options / Configuration + +c8 can be configured via command-line flags, a `c8` section in `package.json`, or a JSON configuration file on disk. + +A configuration file can be specified by passing its path on the command line with `--config` or `-c`. If no config option is provided, c8 searches for files named `.c8rc`, `.c8rc.json`, `.nycrc`, or `.nycrc.json`, starting from +`cwd` and walking up the filesystem tree. + +When using `package.json` configuration or a dedicated configuration file, omit the `--` prefix from the long-form of the desired command-line option. + +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` | `['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 | `boolean` | `false` | +| `--src` | see [section below](#checking-for-full-source-coverage-using---all) for more info | `Array` | `[process.cwd()]`| +| `-n`, `--include` | see [section below](#checking-for-full-source-coverage-using---all) for more info | `Array` | `[]` (include all files) | +| `-x`, `--exclude` | see [section below](#checking-for-full-source-coverage-using---all) for more info | `Array` | [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 @@ -23,8 +48,9 @@ project that are flexed in production but not in your tests, your coverage numbe 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 diff --git a/lib/parse-args.js b/lib/parse-args.js index b778cb98..42797139 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -5,65 +5,100 @@ 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', { + 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' }) @@ -71,6 +106,11 @@ function buildYargs (withCommands = false) { 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' @@ -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) { diff --git a/test/fixtures/config/.c8rc.json b/test/fixtures/config/.c8rc.json new file mode 100644 index 00000000..1d481b7f --- /dev/null +++ b/test/fixtures/config/.c8rc.json @@ -0,0 +1,4 @@ +{ + "temp-directory": "./foo", + "lines": 101 +} diff --git a/test/integration.js_10.snap b/test/integration.js_10.snap index d9f4c957..c68884e8 100644 --- a/test/integration.js_10.snap +++ b/test/integration.js_10.snap @@ -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 | @@ -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%) " `; @@ -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 @@ -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%) " `; @@ -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 | @@ -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 | @@ -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%) " `; diff --git a/test/parse-args.js b/test/parse-args.js index e742a4d2..57271a05 100644 --- a/test/parse-args.js +++ b/test/parse-args.js @@ -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']) + 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) + }) + }) })