From fd3e9d4a27390713413a659793d5a40b9ecd3302 Mon Sep 17 00:00:00 2001 From: "Trevor D. McKeown" Date: Fri, 2 Feb 2024 10:16:00 -0500 Subject: [PATCH] Additional Configuration File Support (#436) feat: Adding additional configuration file options test: unit test for config file detection test: unit test for config files pass via the cli test: unit test case for early exit of hideInstrumenteeArgs test: unit test for Node.js argument handling fix: bug in spawning of c8 process that dropped coverage significantly --- README.md | 20 +++- lib/parse-args.js | 58 ++++++++++- package-lock.json | 37 +++---- package.json | 1 + test/fixtures/config/.c8.config.cjs | 11 ++ test/fixtures/config/.c8.config.js | 11 ++ test/fixtures/config/.c8.config.py | 11 ++ test/fixtures/config/.c8rc | 10 ++ test/fixtures/config/.c8rc.cjs | 11 ++ test/fixtures/config/.c8rc.js | 11 ++ test/fixtures/config/.c8rc.yaml | 8 ++ test/fixtures/config/.c8rc.yml | 8 ++ test/fixtures/config/.nyc.config.cjs | 12 +++ test/fixtures/config/.nyc.config.js | 12 +++ test/fixtures/config/.nycrc | 7 ++ test/fixtures/config/.nycrc.json | 9 ++ test/fixtures/config/.nycrc.yaml | 7 ++ test/fixtures/config/.nycrc.yml | 7 ++ test/fixtures/config/c8.config.cjs | 12 +++ test/fixtures/config/c8.config.js | 12 +++ test/fixtures/config/nyc.config.cjs | 11 ++ test/fixtures/config/nyc.config.js | 11 ++ test/integration.js.snap | 20 +++- test/parse-args-helper.js | 34 ++++++ test/parse-args.js | 148 ++++++++++++++++++++++++++- 25 files changed, 465 insertions(+), 34 deletions(-) create mode 100644 test/fixtures/config/.c8.config.cjs create mode 100644 test/fixtures/config/.c8.config.js create mode 100644 test/fixtures/config/.c8.config.py create mode 100644 test/fixtures/config/.c8rc create mode 100644 test/fixtures/config/.c8rc.cjs create mode 100644 test/fixtures/config/.c8rc.js create mode 100644 test/fixtures/config/.c8rc.yaml create mode 100644 test/fixtures/config/.c8rc.yml create mode 100644 test/fixtures/config/.nyc.config.cjs create mode 100644 test/fixtures/config/.nyc.config.js create mode 100644 test/fixtures/config/.nycrc create mode 100644 test/fixtures/config/.nycrc.json create mode 100644 test/fixtures/config/.nycrc.yaml create mode 100644 test/fixtures/config/.nycrc.yml create mode 100644 test/fixtures/config/c8.config.cjs create mode 100644 test/fixtures/config/c8.config.js create mode 100644 test/fixtures/config/nyc.config.cjs create mode 100644 test/fixtures/config/nyc.config.js create mode 100644 test/parse-args-helper.js diff --git a/README.md b/README.md index 14e881f5d..96549d95b 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,25 @@ 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. +c8 can be configured via command-line flags, a `c8` section in `package.json`, or a configuration file on disk. When using `package.json` configuration or a dedicated configuration file, omit the `--` prefix from the long-form of the desired command-line option. +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 in the table below starting from `cwd` and walking up the filesystem tree. + +A robust configuration file naming convention is available in an effort to stay compatible with nyc configuration options and ensure dynamic configuration. + +| File name | File Association | +|-----------------------------------------------------------------------------------------------------|--------------------| +| `.c8rc`, `.c8rc.json` | JSON | +| `.c8rc.yml`, `.c8rc.yaml` | YAML | +| `.c8rc.js`, `.c8rc.cjs`, `.c8.config.js`, `.c8.config.cjs`, `c8.config.js`, `c8.config.cjs` | CommonJS export* | +| `.nycrc`, `.nycrc.json` | JSON | +| `.nycrc.yaml`, `.nycrc.yml` | YAML | +| `.nycrc.js`, `.nycrc.cjs`, `nyc.config.js`, `nyc.config.cjs`, `.nyc.config.js`, `.nyc.config.cjs` | CommonJS export* | + +For packages written in ESM module syntax, a static configuration option is supported in JSON or YAML syntax. A dynamic configuration is also supported. These configuration files must be written in CommonJS utilizing one of the .cjs file options in the table above. At the moment ESM syntax is not supported for writing c8 configuration files. This may change in the future, but please note, C8 is written in CommonJS syntax. + Here is a list of common options. Run `c8 --help` for the full list and documentation. | Option | Description | Type | Default | diff --git a/lib/parse-args.js b/lib/parse-args.js index 84ff8e3ed..861ac11e0 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -5,7 +5,7 @@ const { readFileSync } = require('fs') const Yargs = require('yargs/yargs') const { applyExtends } = require('yargs/helpers') const parser = require('yargs-parser') -const { resolve } = require('path') +const { resolve, extname, basename } = require('path') function buildYargs (withCommands = false) { const yargs = Yargs([]) @@ -13,12 +13,12 @@ function buildYargs (withCommands = false) { .options('config', { alias: 'c', config: true, - describe: 'path to JSON configuration file', + describe: 'path to configuration file', configParser: (path) => { - const config = JSON.parse(readFileSync(path)) + const config = loadConfigFile(path) return applyExtends(config, process.cwd(), true) }, - default: () => findUp.sync(['.c8rc', '.c8rc.json', '.nycrc', '.nycrc.json']) + default: () => findUp.sync(getConfigFileNames()) }) .option('reporter', { alias: 'r', @@ -199,6 +199,52 @@ function buildYargs (withCommands = false) { return yargs } +function loadConfigFile (path) { + let config = {} + const jsExts = ['.js', '.cjs'] + const ymlExts = ['.yml', '.yaml'] + const fileName = basename(path) + + const ext = extname(path).toLowerCase() + if (jsExts.includes(ext)) { + config = require(path) + } else if (ymlExts.includes(ext)) { + config = require('js-yaml').load(readFileSync(path, 'utf8')) + } else if (ext === '.json' || fileName.slice(-2) === 'rc') { + config = JSON.parse(readFileSync(path, 'utf8')) + } + + // Should the process die if none of these filename extensions are found? + if (Object.keys(config).length === 0) { + throw new Error(`Unsupported file type ${ext} while reading file ${path}`) + } + + return config +} + +function getConfigFileNames () { + return [ + '.c8rc', + '.c8rc.json', + '.c8rc.yml', + '.c8rc.yaml', + '.c8rc.js', + '.c8rc.cjs', + '.c8.config.js', + '.c8.config.cjs', + 'c8.config.js', + 'c8.config.cjs', + '.nycrc', + '.nycrc.json', + '.nycrc.yml', + '.nycrc.yaml', + '.nyc.config.js', + '.nyc.config.cjs', + 'nyc.config.js', + 'nyc.config.cjs' + ] +} + function hideInstrumenterArgs (yargv) { let argv = process.argv.slice(1) argv = argv.slice(argv.indexOf(yargv._[0])) @@ -225,5 +271,7 @@ function hideInstrumenteeArgs () { module.exports = { buildYargs, hideInstrumenterArgs, - hideInstrumenteeArgs + hideInstrumenteeArgs, + getConfigFileNames, + loadConfigFile } diff --git a/package-lock.json b/package-lock.json index 09fe0ad49..c500ee66c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,12 @@ "istanbul-lib-coverage": "^3.2.0", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.1.6", +<<<<<<< HEAD "test-exclude": "^7.0.1", +======= + "js-yaml": "^4.1.0", + "test-exclude": "^6.0.0", +>>>>>>> dc87eba (Additional Configuration File Support (#436)) "v8-to-istanbul": "^9.0.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1" @@ -328,9 +333,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.10.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", - "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "version": "20.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.7.tgz", + "integrity": "sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -440,8 +445,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", @@ -640,9 +644,9 @@ } }, "node_modules/chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.0.tgz", + "integrity": "sha512-x9cHNq1uvkCdU+5xTkNh5WtgD4e4yDFCsp9jVc7N7qVeKeftv3gO/ZrviX5d+3ZfxdYnZXZYujjRInu1RogU6A==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", @@ -2612,7 +2616,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -5157,9 +5160,9 @@ "dev": true }, "@types/node": { - "version": "20.10.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", - "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "version": "20.10.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.7.tgz", + "integrity": "sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==", "dev": true, "requires": { "undici-types": "~5.26.4" @@ -5242,8 +5245,7 @@ "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-buffer-byte-length": { "version": "1.0.0", @@ -5385,9 +5387,9 @@ "dev": true }, "chai": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", - "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.0.tgz", + "integrity": "sha512-x9cHNq1uvkCdU+5xTkNh5WtgD4e4yDFCsp9jVc7N7qVeKeftv3gO/ZrviX5d+3ZfxdYnZXZYujjRInu1RogU6A==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -6835,7 +6837,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "requires": { "argparse": "^2.0.1" } diff --git a/package.json b/package.json index e9f960a81..3fd76a514 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.1.6", "test-exclude": "^7.0.1", + "js-yaml": "^4.1.0", "v8-to-istanbul": "^9.0.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1" diff --git a/test/fixtures/config/.c8.config.cjs b/test/fixtures/config/.c8.config.cjs new file mode 100644 index 000000000..290e529ad --- /dev/null +++ b/test/fixtures/config/.c8.config.cjs @@ -0,0 +1,11 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "lines": 45, + "branches": "72", + "statements": "65" +} + +module.exports = config diff --git a/test/fixtures/config/.c8.config.js b/test/fixtures/config/.c8.config.js new file mode 100644 index 000000000..4e420f803 --- /dev/null +++ b/test/fixtures/config/.c8.config.js @@ -0,0 +1,11 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "lines": 47, + "branches": "72", + "statements": "65" +} + +module.exports = config diff --git a/test/fixtures/config/.c8.config.py b/test/fixtures/config/.c8.config.py new file mode 100644 index 000000000..290e529ad --- /dev/null +++ b/test/fixtures/config/.c8.config.py @@ -0,0 +1,11 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "lines": 45, + "branches": "72", + "statements": "65" +} + +module.exports = config diff --git a/test/fixtures/config/.c8rc b/test/fixtures/config/.c8rc new file mode 100644 index 000000000..acbcfceed --- /dev/null +++ b/test/fixtures/config/.c8rc @@ -0,0 +1,10 @@ + { + "reporter": [ + "lcov", + "json" + ], + "statements": 29, + "branches": 81, + "lines": 78, + "functions": 93 + } diff --git a/test/fixtures/config/.c8rc.cjs b/test/fixtures/config/.c8rc.cjs new file mode 100644 index 000000000..52218a576 --- /dev/null +++ b/test/fixtures/config/.c8rc.cjs @@ -0,0 +1,11 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "lines": 32, + "branches": "72", + "statements": "65" +} + +module.exports = config diff --git a/test/fixtures/config/.c8rc.js b/test/fixtures/config/.c8rc.js new file mode 100644 index 000000000..b13cb0272 --- /dev/null +++ b/test/fixtures/config/.c8rc.js @@ -0,0 +1,11 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "lines": 22, + "branches": "72", + "statements": "65" +} + +module.exports = config diff --git a/test/fixtures/config/.c8rc.yaml b/test/fixtures/config/.c8rc.yaml new file mode 100644 index 000000000..fb8519bb1 --- /dev/null +++ b/test/fixtures/config/.c8rc.yaml @@ -0,0 +1,8 @@ +--- +reporter: +- html +- text +statements: 83 +branches: 88 +lines: 10 +functions: 12 diff --git a/test/fixtures/config/.c8rc.yml b/test/fixtures/config/.c8rc.yml new file mode 100644 index 000000000..93795b312 --- /dev/null +++ b/test/fixtures/config/.c8rc.yml @@ -0,0 +1,8 @@ +--- +reporter: +- html +- text +statements: 89 +branches: 58 +lines: 69 +functions: 54 diff --git a/test/fixtures/config/.nyc.config.cjs b/test/fixtures/config/.nyc.config.cjs new file mode 100644 index 000000000..cda7b4942 --- /dev/null +++ b/test/fixtures/config/.nyc.config.cjs @@ -0,0 +1,12 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "statements": 54, + "branches": 40, + "lines": 71, + "functions": 0 +} + +module.exports = config diff --git a/test/fixtures/config/.nyc.config.js b/test/fixtures/config/.nyc.config.js new file mode 100644 index 000000000..19ca15250 --- /dev/null +++ b/test/fixtures/config/.nyc.config.js @@ -0,0 +1,12 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "statements": 54, + "branches": 40, + "lines": 85, + "functions": 0 +} + +module.exports = config diff --git a/test/fixtures/config/.nycrc b/test/fixtures/config/.nycrc new file mode 100644 index 000000000..ed44728cb --- /dev/null +++ b/test/fixtures/config/.nycrc @@ -0,0 +1,7 @@ + { + "statements": 95, + "branches": 40, + "lines": 51, + "functions": 89 + } + \ No newline at end of file diff --git a/test/fixtures/config/.nycrc.json b/test/fixtures/config/.nycrc.json new file mode 100644 index 000000000..b9c9a301d --- /dev/null +++ b/test/fixtures/config/.nycrc.json @@ -0,0 +1,9 @@ +{ + "reporter": [ + "html", + "text" + ], + "lines": 96, + "branches": "82", + "statements": "95" +} diff --git a/test/fixtures/config/.nycrc.yaml b/test/fixtures/config/.nycrc.yaml new file mode 100644 index 000000000..c7d8c2ffd --- /dev/null +++ b/test/fixtures/config/.nycrc.yaml @@ -0,0 +1,7 @@ +--- +reporter: +- html +- text +lines: 98 +branches: '82' +statements: '95' diff --git a/test/fixtures/config/.nycrc.yml b/test/fixtures/config/.nycrc.yml new file mode 100644 index 000000000..602b4999e --- /dev/null +++ b/test/fixtures/config/.nycrc.yml @@ -0,0 +1,7 @@ +--- +reporter: +- html +- text +lines: 99 +branches: '82' +statements: '95' diff --git a/test/fixtures/config/c8.config.cjs b/test/fixtures/config/c8.config.cjs new file mode 100644 index 000000000..400266dc5 --- /dev/null +++ b/test/fixtures/config/c8.config.cjs @@ -0,0 +1,12 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "statements": 95, + "branches": 40, + "lines": 51, + "functions": 89 +} + +module.exports = config diff --git a/test/fixtures/config/c8.config.js b/test/fixtures/config/c8.config.js new file mode 100644 index 000000000..e23d4ae5d --- /dev/null +++ b/test/fixtures/config/c8.config.js @@ -0,0 +1,12 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "statements": 54, + "branches": 40, + "lines": 47, + "functions": 0 +} + +module.exports = config diff --git a/test/fixtures/config/nyc.config.cjs b/test/fixtures/config/nyc.config.cjs new file mode 100644 index 000000000..c602be3b5 --- /dev/null +++ b/test/fixtures/config/nyc.config.cjs @@ -0,0 +1,11 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "lines": 94, + "branches": "82", + "statements": "95" +} + +module.exports = config diff --git a/test/fixtures/config/nyc.config.js b/test/fixtures/config/nyc.config.js new file mode 100644 index 000000000..6b03355c5 --- /dev/null +++ b/test/fixtures/config/nyc.config.js @@ -0,0 +1,11 @@ +const config = { + "reporter": [ + "html", + "text" + ], + "lines": 95, + "branches": "82", + "statements": "95" +} + +module.exports = config diff --git a/test/integration.js.snap b/test/integration.js.snap index fdbce7041..d2a318694 100644 --- a/test/integration.js.snap +++ b/test/integration.js.snap @@ -156,7 +156,7 @@ hey ---------------------------------------|---------|----------|---------|---------|------------------------ File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ---------------------------------------|---------|----------|---------|---------|------------------------ -All files | 3.21 | 12.24 | 6.38 | 3.21 | +All files | 3.03 | 11.11 | 5.76 | 3.03 | c8 | 0 | 0 | 0 | 0 | index.js | 0 | 0 | 0 | 0 | 1 c8/bin | 0 | 0 | 0 | 0 | @@ -166,7 +166,7 @@ All files | 3.21 | 12.24 | 6.38 | 3.21 prettify.js | 0 | 0 | 0 | 0 | 1-2 sorter.js | 0 | 0 | 0 | 0 | 1-196 c8/lib | 0 | 0 | 0 | 0 | - parse-args.js | 0 | 0 | 0 | 0 | 1-229 + parse-args.js | 0 | 0 | 0 | 0 | 1-277 report.js | 0 | 0 | 0 | 0 | 1-542 source-map-from-file.js | 0 | 0 | 0 | 0 | 1-100 c8/lib/commands | 0 | 0 | 0 | 0 | @@ -195,6 +195,12 @@ All files | 3.21 | 12.24 | 6.38 | 3.21 main.js | 0 | 0 | 0 | 0 | 1-4 c8/test/fixtures/all/vanilla/dir | 0 | 0 | 0 | 0 | unloaded.js | 0 | 0 | 0 | 0 | 1-5 + c8/test/fixtures/config | 0 | 0 | 0 | 0 | + .c8.config.js | 0 | 0 | 0 | 0 | 1-11 + .c8rc.js | 0 | 0 | 0 | 0 | 1-11 + .nyc.config.js | 0 | 0 | 0 | 0 | 1-12 + c8.config.js | 0 | 0 | 0 | 0 | 1-12 + nyc.config.js | 0 | 0 | 0 | 0 | 1-11 c8/test/fixtures/multidir1 | 0 | 0 | 0 | 0 | file1.js | 0 | 0 | 0 | 0 | 1 c8/test/fixtures/multidir2 | 0 | 0 | 0 | 0 | @@ -522,7 +528,7 @@ hey ---------------------------------------|---------|----------|---------|---------|------------------------ File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ---------------------------------------|---------|----------|---------|---------|------------------------ -All files | 3.21 | 12.24 | 6.38 | 3.21 | +All files | 3.03 | 11.11 | 5.76 | 3.03 | c8 | 0 | 0 | 0 | 0 | index.js | 0 | 0 | 0 | 0 | 1 c8/bin | 0 | 0 | 0 | 0 | @@ -532,7 +538,7 @@ All files | 3.21 | 12.24 | 6.38 | 3.21 prettify.js | 0 | 0 | 0 | 0 | 1-2 sorter.js | 0 | 0 | 0 | 0 | 1-196 c8/lib | 0 | 0 | 0 | 0 | - parse-args.js | 0 | 0 | 0 | 0 | 1-229 + parse-args.js | 0 | 0 | 0 | 0 | 1-277 report.js | 0 | 0 | 0 | 0 | 1-542 source-map-from-file.js | 0 | 0 | 0 | 0 | 1-100 c8/lib/commands | 0 | 0 | 0 | 0 | @@ -561,6 +567,12 @@ All files | 3.21 | 12.24 | 6.38 | 3.21 main.js | 0 | 0 | 0 | 0 | 1-4 c8/test/fixtures/all/vanilla/dir | 0 | 0 | 0 | 0 | unloaded.js | 0 | 0 | 0 | 0 | 1-5 + c8/test/fixtures/config | 0 | 0 | 0 | 0 | + .c8.config.js | 0 | 0 | 0 | 0 | 1-11 + .c8rc.js | 0 | 0 | 0 | 0 | 1-11 + .nyc.config.js | 0 | 0 | 0 | 0 | 1-12 + c8.config.js | 0 | 0 | 0 | 0 | 1-12 + nyc.config.js | 0 | 0 | 0 | 0 | 1-11 c8/test/fixtures/multidir1 | 0 | 0 | 0 | 0 | file1.js | 0 | 0 | 0 | 0 | 1 c8/test/fixtures/multidir2 | 0 | 0 | 0 | 0 | diff --git a/test/parse-args-helper.js b/test/parse-args-helper.js new file mode 100644 index 000000000..93b39d1aa --- /dev/null +++ b/test/parse-args-helper.js @@ -0,0 +1,34 @@ +const { mkdirSync, copyFileSync, rmSync, existsSync } = require('fs') +const { join } = require('path') + +const testPath = './test/fixtures/tmp-config-test' + +const testConfigFile = function (filePath, fileNameLineNumberMap, callback) { + Object.keys(fileNameLineNumberMap).forEach((fileName) => { + const expectedLines = fileNameLineNumberMap[fileName] + callback(fileName, expectedLines) + }) +} + +const beforeTestReadingConfigFile = (configFileName) => { + afterTestReadingConfigFile() + // make the directory tmp-config-test + mkdirSync(testPath) + + // Copy config file in fileName and test/fixtures/normal.js to dir above + copyFileSync('./test/fixtures/normal.js', join(testPath, '/normal.js')) + copyFileSync('./test/fixtures/async.js', join(testPath, '/async.js')) + copyFileSync('./test/fixtures/config/' + configFileName, join(testPath, configFileName)) +} + +const afterTestReadingConfigFile = () => { + if (existsSync(testPath)) { + rmSync(testPath, { recursive: true, force: true }) + } +} + +module.exports = { + testConfigFile: testConfigFile, + beforeTestReadingConfigFile: beforeTestReadingConfigFile, + afterTestReadingConfigFile: afterTestReadingConfigFile +} diff --git a/test/parse-args.js b/test/parse-args.js index fa2d5267a..8e4b5b7cf 100644 --- a/test/parse-args.js +++ b/test/parse-args.js @@ -1,12 +1,31 @@ -/* global describe, it */ +/* global describe, it, beforeEach, after */ + +const { assert } = require('chai') +const { existsSync } = require('fs') +const { resolve, join } = require('path') +const chaiJestSnapshot = require('chai-jest-snapshot') +const { spawnSync } = require('child_process') const { buildYargs, hideInstrumenteeArgs, - hideInstrumenterArgs + hideInstrumenterArgs, + getConfigFileNames, + loadConfigFile } = require('../lib/parse-args') -const { join, resolve } = require('path') +const { + testConfigFile, + beforeTestReadingConfigFile, + afterTestReadingConfigFile +} = require('./parse-args-helper.js') + +require('chai') + .use(chaiJestSnapshot) + .should() + +const c8Path = require.resolve('../bin/c8') +const nodePath = process.execPath describe('parse-args', () => { describe('hideInstrumenteeArgs', () => { @@ -15,6 +34,12 @@ describe('parse-args', () => { const instrumenterArgs = hideInstrumenteeArgs() instrumenterArgs.should.eql(['--foo=99', 'my-app']) }) + + it('test early exit from function if no arguments are passed', () => { + process.argv = [] + const instrumenterArgs = hideInstrumenteeArgs() + instrumenterArgs.length.should.eql(0) + }) }) describe('hideInstrumenterArgs', () => { @@ -25,6 +50,14 @@ describe('parse-args', () => { instrumenteeArgs.should.eql(['my-app', '--help']) argv.tempDirectory.endsWith(join('coverage', 'tmp')).should.be.equal(true) }) + + it('interprets first args after -- as Node.js execArgv', async () => { + const expected = [process.execPath, '--expose-gc', 'index.js'] + process.argv = ['node', 'c8', '--', '--expose-gc', 'index.js'] + const argv = buildYargs().parse(hideInstrumenteeArgs()) + const munged = hideInstrumenterArgs(argv) + munged.should.deep.equal(expected) + }) }) describe('with NODE_V8_COVERAGE already set', () => { @@ -39,6 +72,113 @@ describe('parse-args', () => { }) describe('--config', () => { + it('c8 process should throw an error message if an invalid configuration file name is passed', function () { + const invalidConfig = './fixtures/config/.c8.config.py' + const loadInvalidConfigFile = function (file, callBack) { + try { + callBack(file) + assert.fail('Invalid configuration file loaded') + } catch (error) { + const expectErrorValue = `Error: Unsupported file type .py while reading file ${invalidConfig}` + String(error).should.eql(expectErrorValue) + } + } + + loadInvalidConfigFile(invalidConfig, function (file) { + loadConfigFile(file) + }) + }) + + it('config directory should contain all variations of the config file naming convention', () => { + let count = 0 + const fileMessages = [] + const configFileList = getConfigFileNames() + configFileList.forEach((file) => { + const fullPath = './test/fixtures/config/' + file + if (existsSync(fullPath)) { + count++ + } else { + fileMessages.push(`Missing ${file} from ./test/fixtures/config directory`) + } + }) + + if (count === configFileList.length) { + assert.equal(count, configFileList.length) + } else { + const msg = fileMessages.join(' \n ') + assert.equal(fileMessages.length, 0, msg) + } + }) + + describe('should be able to read config files with .json, .yml, .yaml, .js, .cjs extensions', () => { + const filePath = './fixtures/config/' + const testData = { + c8: { + description: 'c8 variations of config file', + fileNameLineNumberMap: { + '.c8rc.json': 101, + '.c8rc.yml': 69, + '.c8rc.yaml': 10, + 'c8.config.js': 47, + 'c8.config.cjs': 51, + '.c8rc.js': 22, + '.c8rc.cjs': 32, + '.c8.config.js': 47, + '.c8.config.cjs': 45 + } + }, + nyc: { + description: 'nyc variations of config file', + fileNameLineNumberMap: { + '.nycrc': 51, + '.nycrc.json': 96, + '.nycrc.yml': 99, + '.nycrc.yaml': 98, + 'nyc.config.js': 95, + 'nyc.config.cjs': 94, + '.nyc.config.js': 85, + '.nyc.config.cjs': 71 + } + } + } + + Object.keys(testData).forEach(key => { + const { description, fileNameLineNumberMap } = testData[key] + describe(description, function () { + testConfigFile(filePath, fileNameLineNumberMap, function (fileName, expectedLines) { + beforeEach(() => beforeTestReadingConfigFile(fileName)) + it(`should be able to resolve config file ${fileName} with --config flag`, () => { + const configFilePath = join(filePath, fileName) + const argv = buildYargs().parse(['node', 'c8', 'my-app', '--config', require.resolve(`./${configFilePath}`)]) + argv.lines.should.be.equal(expectedLines) + }) + + // skipping for the moment. Need another patch for this test to successfully run + it.skip(`should be able to resolve config file ${fileName} by detection`, function () { + // set the snapshot filename + chaiJestSnapshot.setTestName(`should be able to resolve config file ${fileName} by detection`) + chaiJestSnapshot.setFilename('./test/fixtures/config/snapshots/' + fileName + '.snap') + + // Run V8 in the dir above + const { output } = spawnSync(nodePath, + [ + c8Path, + '--temp-directory=tmp/normal', + '--all', + '--src=./test/fixtures/tmp-config-test', + nodePath, + require.resolve('./fixtures/tmp-config-test/normal.js') + ], + { cwd: './test/fixtures/tmp-config-test' } + ) + output.toString('utf8').should.matchSnapshot() + }) + after(afterTestReadingConfigFile) + }) + }) + }) + }) + it('should resolve to .nycrc at cwd', () => { const argv = buildYargs().parse(['node', 'c8', 'my-app']) argv.lines.should.be.equal(95) @@ -47,11 +187,13 @@ describe('parse-args', () => { 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') + argv.functions.should.be.equal(24) }) 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') + argv.functions.should.be.equal(24) }) 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')])