From 994151ae5eb278b96b3bb6201f9199e04327d997 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 21 Jan 2019 14:43:32 -0800 Subject: [PATCH 1/5] feat: support non-0 exit code if coverage thresholds not met --- README.md | 24 ++++++++ bin/c8.js | 41 ++++--------- lib/commands/check-coverage.js | 77 ++++++++++++++++++++++++ lib/commands/report.js | 25 ++++++++ lib/parse-args.js | 105 ++++++++++++++++++--------------- lib/report.js | 7 +-- package-lock.json | 104 +++++++++++++++++++++----------- package.json | 2 +- test/integration.js.snap | 15 +++-- test/parse-args.js | 6 +- 10 files changed, 282 insertions(+), 124 deletions(-) create mode 100644 lib/commands/check-coverage.js create mode 100644 lib/commands/report.js diff --git a/README.md b/README.md index 5eefc5fa..afc8c741 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,30 @@ The above example will output coverage metrics for `foo.js`. run `c8 report` to regenerate reports after `c8` has already been run. +## Checking coverage + +nyc can fail tests if coverage falls below a threshold. +After running your tests with nyc, simply run: + +```shell +nyc check-coverage --lines 95 --functions 95 --branches 95 +``` + +nyc also accepts a `--check-coverage` shorthand, which can be used to +both run tests and check that coverage falls within the threshold provided: + +```shell +nyc --check-coverage --lines 100 npm test +``` + +The above check fails if coverage falls below 100%. + +To check thresholds on a per-file basis run: + +```shell +nyc check-coverage --lines 95 --per-file +``` + ## Supported Node.js Versions c8 uses diff --git a/bin/c8.js b/bin/c8.js index 6498be03..35b3e26e 100755 --- a/bin/c8.js +++ b/bin/c8.js @@ -2,48 +2,33 @@ 'use strict' const fs = require('fs') -const util = require('util') - const foreground = require('foreground-child') -const report = require('../lib/report') +const { outputReport } = require('../lib/commands/report') +const { promisify } = require('util') const rimraf = require('rimraf') const { + buildYargs, hideInstrumenteeArgs, - hideInstrumenterArgs, - yargs + hideInstrumenterArgs } = require('../lib/parse-args') const instrumenterArgs = hideInstrumenteeArgs() -let argv = yargs.parse(instrumenterArgs) - -const _p = util.promisify - -function outputReport () { - report({ - include: argv.include, - exclude: argv.exclude, - reporter: Array.isArray(argv.reporter) ? argv.reporter : [argv.reporter], - tempDirectory: argv.tempDirectory, - watermarks: argv.watermarks, - resolve: argv.resolve, - omitRelative: argv.omitRelative, - wrapperLength: argv.wrapperLength - }) -} +let argv = buildYargs().parse(instrumenterArgs) -(async function run () { - if (argv._[0] === 'report') { - argv = yargs.parse(process.argv) // support flag arguments after "report". - outputReport() +;(async function run () { + if ([ + 'check-coverage', 'report' + ].indexOf(argv._[0]) !== -1) { + argv = buildYargs(true).parse(process.argv.slice(2)) } else { if (argv.clean) { - await _p(rimraf)(argv.tempDirectory) - await _p(fs.mkdir)(argv.tempDirectory, { recursive: true }) + await promisify(rimraf)(argv.tempDirectory) + await promisify(fs.mkdir)(argv.tempDirectory, { recursive: true }) } process.env.NODE_V8_COVERAGE = argv.tempDirectory foreground(hideInstrumenterArgs(argv), () => { - outputReport() + outputReport(argv) }) } })() diff --git a/lib/commands/check-coverage.js b/lib/commands/check-coverage.js new file mode 100644 index 00000000..df60684a --- /dev/null +++ b/lib/commands/check-coverage.js @@ -0,0 +1,77 @@ +const Report = require('../report') + +exports.command = 'check-coverage' + +exports.describe = 'check whether coverage is within thresholds provided' + +exports.builder = function (yargs) { + yargs + .option('branches', { + default: 0, + description: 'what % of branches must be covered?' + }) + .option('functions', { + default: 0, + description: 'what % of functions must be covered?' + }) + .option('lines', { + default: 90, + description: 'what % of lines must be covered?' + }) + .option('statements', { + default: 0, + description: 'what % of statements must be covered?' + }) + .option('per-file', { + default: false, + description: 'check thresholds per file' + }) + .example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided") +} + +exports.handler = function (argv) { + const report = Report({ + include: argv.include, + exclude: argv.exclude, + reporter: Array.isArray(argv.reporter) ? argv.reporter : [argv.reporter], + tempDirectory: argv.tempDirectory, + watermarks: argv.watermarks, + resolve: argv.resolve, + omitRelative: argv.omitRelative, + wrapperLength: argv.wrapperLength + }) + const thresholds = { + lines: argv.lines, + functions: argv.functions, + branches: argv.branches, + statements: argv.statements + } + const map = report.getCoverageMapFromAllCoverageFiles() + checkCoverages(map, thresholds, argv.perFile) +} + +function checkCoverages (map, thresholds, perFile) { + if (perFile) { + map.files().forEach(file => { + // ERROR: Coverage for lines (90.12%) does not meet threshold (120%) for index.js + checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file) + }) + } else { + // ERROR: Coverage for lines (90.12%) does not meet global threshold (120%) + checkCoverage(map.getCoverageSummary(), thresholds) + } +} + +function checkCoverage (summary, thresholds, file) { + Object.keys(thresholds).forEach(key => { + const coverage = summary[key].pct + if (coverage < thresholds[key]) { + process.exitCode = 1 + if (file) { + console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + file) + } else { + console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet global threshold (' + thresholds[key] + '%)') + } + } + }) +} diff --git a/lib/commands/report.js b/lib/commands/report.js new file mode 100644 index 00000000..f3fffbd9 --- /dev/null +++ b/lib/commands/report.js @@ -0,0 +1,25 @@ +const Report = require('../report') + +exports.command = 'report' + +exports.describe = 'read V8 coverage data from temp and output report' + +exports.builder = function (yargs) {} + +exports.handler = function (argv) { + exports.outputReport(argv) +} + +exports.outputReport = function (argv) { + const report = Report({ + include: argv.include, + exclude: argv.exclude, + reporter: Array.isArray(argv.reporter) ? argv.reporter : [argv.reporter], + tempDirectory: argv.tempDirectory, + watermarks: argv.watermarks, + resolve: argv.resolve, + omitRelative: argv.omitRelative, + wrapperLength: argv.wrapperLength + }) + report.run() +} diff --git a/lib/parse-args.js b/lib/parse-args.js index d2bd84c1..62f9af98 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -1,56 +1,69 @@ const Exclude = require('test-exclude') const findUp = require('find-up') const { readFileSync } = require('fs') -const yargs = require('yargs') +const Yargs = require('yargs/yargs') const parser = require('yargs-parser') const configPath = findUp.sync(['.c8rc', '.c8rc.json']) const config = configPath ? JSON.parse(readFileSync(configPath)) : {} -yargs() - .usage('$0 [opts] [script] [opts]') - .option('reporter', { - alias: 'r', - describe: 'coverage reporter(s) to use', - default: 'text' - }) - .option('exclude', { - alias: 'x', - default: Exclude.defaultExclude, - describe: 'a list of specific files and directories that should be excluded from coverage (glob patterns are supported)' - }) - .option('include', { - alias: 'n', - default: [], - describe: 'a list of specific files that should be covered (glob patterns are supported)' - }) - .option('temp-directory', { - default: './coverage/tmp', - describe: 'directory V8 coverage data is written to and read from' - }) - .option('resolve', { - default: '', - describe: 'resolve paths to alternate base directory' - }) - .option('wrapper-length', { - describe: 'how many bytes is the wrapper prefix on executed JavaScript', - type: 'number' - }) - .option('omit-relative', { - default: true, - 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' - }) - .command('report', 'read V8 coverage data from temp and output report') - .pkgConf('c8') - .config(config) - .demandCommand(1) - .epilog('visit https://git.io/vHysA for list of available reporters') +function buildYargs (withCommands = false) { + const yargs = Yargs([]) + .usage('$0 [opts] [script] [opts]') + .option('reporter', { + alias: 'r', + describe: 'coverage reporter(s) to use', + default: 'text' + }) + .option('exclude', { + alias: 'x', + default: Exclude.defaultExclude, + describe: 'a list of specific files and directories that should be excluded from coverage (glob patterns are supported)' + }) + .option('include', { + alias: 'n', + default: [], + describe: 'a list of specific files that should be covered (glob patterns are supported)' + }) + .option('temp-directory', { + default: './coverage/tmp', + describe: 'directory V8 coverage data is written to and read from' + }) + .option('resolve', { + default: '', + describe: 'resolve paths to alternate base directory' + }) + .option('wrapper-length', { + describe: 'how many bytes is the wrapper prefix on executed JavaScript', + type: 'number' + }) + .option('omit-relative', { + default: true, + 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' + }) + .pkgConf('c8') + .config(config) + .demandCommand(1) + .epilog('visit https://git.io/vHysA for list of available reporters') + + const checkCoverage = require('./commands/check-coverage') + const report = require('./commands/report') + if (withCommands) { + yargs.command(checkCoverage) + yargs.command(report) + } else { + yargs.command(checkCoverage.command, checkCoverage.describe) + yargs.command(report.command, report.describe) + } + + return yargs +} function hideInstrumenterArgs (yargv) { var argv = process.argv.slice(1) @@ -76,7 +89,7 @@ function hideInstrumenteeArgs () { } module.exports = { - yargs, + buildYargs, hideInstrumenterArgs, hideInstrumenteeArgs } diff --git a/lib/report.js b/lib/report.js index abfd65a9..7db228b6 100644 --- a/lib/report.js +++ b/lib/report.js @@ -32,7 +32,7 @@ class Report { this.wrapperLength = wrapperLength } run () { - const map = this._getCoverageMapFromAllCoverageFiles() + const map = this.getCoverageMapFromAllCoverageFiles() var context = libReport.createContext({ dir: './coverage', watermarks: this.watermarks @@ -45,7 +45,7 @@ class Report { }) } - _getCoverageMapFromAllCoverageFiles () { + getCoverageMapFromAllCoverageFiles () { const v8ProcessCov = this._getMergedProcessCov() const map = libCoverage.createCoverageMap({}) @@ -138,6 +138,5 @@ class Report { } module.exports = function (opts) { - const report = new Report(opts) - report.run() + return new Report(opts) } diff --git a/package-lock.json b/package-lock.json index 052292e7..fa8c3ed2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,7 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -1181,6 +1182,14 @@ "safer-buffer": "^2.1.0" } }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", @@ -1888,7 +1897,8 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true }, "getpass": { "version": "0.1.7", @@ -2190,7 +2200,8 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "optional": true }, "is-builtin-module": { "version": "1.0.0", @@ -2495,6 +2506,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "optional": true, "requires": { "is-buffer": "^1.1.5" } @@ -2597,7 +2609,8 @@ "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "optional": true }, "loose-envify": { "version": "1.4.0", @@ -2643,9 +2656,9 @@ } }, "map-age-cleaner": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", - "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "requires": { "p-defer": "^1.0.0" } @@ -3331,6 +3344,15 @@ "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -3455,7 +3477,8 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "optional": true }, "repeating": { "version": "2.0.1", @@ -4266,11 +4289,6 @@ "mkdirp": "^0.5.1" } }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==" - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", @@ -4288,12 +4306,12 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "requires": { "cliui": "^4.0.0", - "decamelize": "^2.0.0", + "decamelize": "^1.2.0", "find-up": "^3.0.0", "get-caller-file": "^1.0.1", "os-locale": "^3.0.0", @@ -4303,7 +4321,7 @@ "string-width": "^2.0.0", "which-module": "^2.0.0", "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" + "yargs-parser": "^11.1.1" }, "dependencies": { "ansi-regex": { @@ -4311,6 +4329,11 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + }, "cliui": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", @@ -4333,21 +4356,13 @@ "which": "^1.2.9" } }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "requires": { - "xregexp": "4.0.0" - } - }, "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "requires": { "cross-spawn": "^6.0.0", - "get-stream": "^3.0.0", + "get-stream": "^4.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", "p-finally": "^1.0.0", @@ -4355,6 +4370,14 @@ "strip-eof": "^1.0.0" } }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, "invert-kv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", @@ -4379,19 +4402,19 @@ } }, "os-locale": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", - "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "requires": { - "execa": "^0.10.0", + "execa": "^1.0.0", "lcid": "^2.0.0", "mem": "^4.0.0" } }, "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, "strip-ansi": { "version": "4.0.0", @@ -4400,6 +4423,15 @@ "requires": { "ansi-regex": "^3.0.0" } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } } } }, diff --git a/package.json b/package.json index 0381298b..4413c28e 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "test-exclude": "^5.0.0", "uuid": "^3.3.2", "v8-to-istanbul": "^2.0.2", - "yargs": "^12.0.2", + "yargs": "^12.0.5", "yargs-parser": "^10.1.0" }, "devDependencies": { diff --git a/test/integration.js.snap b/test/integration.js.snap index d7394a14..6259c092 100644 --- a/test/integration.js.snap +++ b/test/integration.js.snap @@ -8,12 +8,15 @@ second --------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | --------------------|----------|----------|----------|----------|-------------------| -All files | 94.4 | 71.43 | 95 | 94.4 | | - bin | 88 | 62.5 | 100 | 88 | | - c8.js | 88 | 62.5 | 100 | 88 | 36,40,47,48,49,50 | - lib | 95.59 | 61.29 | 100 | 95.59 | | - parse-args.js | 97.59 | 44.44 | 100 | 97.59 | 59,60 | - report.js | 94.44 | 68.18 | 100 | 94.44 |... 01,127,128,129 | +All files | 80 | 70.31 | 74.07 | 80 | | + bin | 85.71 | 66.67 | 100 | 85.71 | | + c8.js | 85.71 | 66.67 | 100 | 85.71 | 25,32,33,34,35 | + lib | 94.98 | 57.14 | 100 | 94.98 | | + parse-args.js | 95.83 | 41.67 | 100 | 95.83 | 58,59,72,73 | + report.js | 94.41 | 65.22 | 100 | 94.41 |... 01,127,128,129 | + lib/commands | 34.62 | 83.33 | 14.29 | 34.62 | | + check-coverage.js | 15.38 | 100 | 0 | 15.38 |... 73,74,75,76,77 | + report.js | 92.31 | 75 | 33.33 | 92.31 | 10,11 | test/fixtures | 95.16 | 94.12 | 85.71 | 95.16 | | async.js | 100 | 100 | 100 | 100 | | multiple-spawn.js | 100 | 100 | 100 | 100 | | diff --git a/test/parse-args.js b/test/parse-args.js index 43743e21..04f29194 100644 --- a/test/parse-args.js +++ b/test/parse-args.js @@ -1,9 +1,9 @@ /* global describe, it */ const { + buildYargs, hideInstrumenteeArgs, - hideInstrumenterArgs, - yargs + hideInstrumenterArgs } = require('../lib/parse-args') describe('parse-args', () => { @@ -18,7 +18,7 @@ describe('parse-args', () => { describe('hideInstrumenterArgs', () => { it('hides arguments passed to c8 bin', () => { process.argv = ['node', 'c8', '--foo=99', 'my-app', '--help'] - const argv = yargs.parse(hideInstrumenteeArgs()) + const argv = buildYargs().parse(hideInstrumenteeArgs()) const instrumenteeArgs = hideInstrumenterArgs(argv) instrumenteeArgs.should.eql(['my-app', '--help']) }) From 6bbcf31dc0aae4fe85b16569f89a270e842880e4 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 21 Jan 2019 14:47:49 -0800 Subject: [PATCH 2/5] doc: switch nyc to c8 --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index afc8c741..f01c9772 100644 --- a/README.md +++ b/README.md @@ -22,18 +22,18 @@ run `c8 report` to regenerate reports after `c8` has already been run. ## Checking coverage -nyc can fail tests if coverage falls below a threshold. -After running your tests with nyc, simply run: +c8 can fail tests if coverage falls below a threshold. +After running your tests with c8, simply run: ```shell -nyc check-coverage --lines 95 --functions 95 --branches 95 +c8 check-coverage --lines 95 --functions 95 --branches 95 ``` -nyc also accepts a `--check-coverage` shorthand, which can be used to +c8 also accepts a `--check-coverage` shorthand, which can be used to both run tests and check that coverage falls within the threshold provided: ```shell -nyc --check-coverage --lines 100 npm test +c8 --check-coverage --lines 100 npm test ``` The above check fails if coverage falls below 100%. @@ -41,7 +41,7 @@ The above check fails if coverage falls below 100%. To check thresholds on a per-file basis run: ```shell -nyc check-coverage --lines 95 --per-file +c8 check-coverage --lines 95 --per-file ``` ## Supported Node.js Versions From 868d586fdd2f8efdfe7e555a1b7a03f69d0ea7b2 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 21 Jan 2019 17:06:39 -0800 Subject: [PATCH 3/5] finish implementing threshold behavior --- bin/c8.js | 4 +- lib/commands/check-coverage.js | 34 +++-------------- lib/commands/report.js | 3 +- lib/parse-args.js | 25 ++++++++++++ lib/report.js | 9 ++++- test/fixtures/normal.js | 4 ++ test/integration.js | 52 ++++++++++++++++++++++++- test/integration.js.snap | 69 +++++++++++++++++++++++++++------- 8 files changed, 154 insertions(+), 46 deletions(-) diff --git a/bin/c8.js b/bin/c8.js index 35b3e26e..495dd9c3 100755 --- a/bin/c8.js +++ b/bin/c8.js @@ -4,6 +4,7 @@ const fs = require('fs') const foreground = require('foreground-child') const { outputReport } = require('../lib/commands/report') +const { checkCoverages } = require('../lib/commands/check-coverage') const { promisify } = require('util') const rimraf = require('rimraf') const { @@ -28,7 +29,8 @@ let argv = buildYargs().parse(instrumenterArgs) process.env.NODE_V8_COVERAGE = argv.tempDirectory foreground(hideInstrumenterArgs(argv), () => { - outputReport(argv) + const report = outputReport(argv) + if (argv.checkCoverage) checkCoverages(argv, report) }) } })() diff --git a/lib/commands/check-coverage.js b/lib/commands/check-coverage.js index df60684a..6fd5c872 100644 --- a/lib/commands/check-coverage.js +++ b/lib/commands/check-coverage.js @@ -6,27 +6,7 @@ exports.describe = 'check whether coverage is within thresholds provided' exports.builder = function (yargs) { yargs - .option('branches', { - default: 0, - description: 'what % of branches must be covered?' - }) - .option('functions', { - default: 0, - description: 'what % of functions must be covered?' - }) - .option('lines', { - default: 90, - description: 'what % of lines must be covered?' - }) - .option('statements', { - default: 0, - description: 'what % of statements must be covered?' - }) - .option('per-file', { - default: false, - description: 'check thresholds per file' - }) - .example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided") + .example('$0 check-coverage --lines 95', "check whether the JSON in c8's output folder meets the thresholds provided") } exports.handler = function (argv) { @@ -40,6 +20,10 @@ exports.handler = function (argv) { omitRelative: argv.omitRelative, wrapperLength: argv.wrapperLength }) + exports.checkCoverages(argv, report) +} + +exports.checkCoverages = function (argv, report) { const thresholds = { lines: argv.lines, functions: argv.functions, @@ -47,17 +31,11 @@ exports.handler = function (argv) { statements: argv.statements } const map = report.getCoverageMapFromAllCoverageFiles() - checkCoverages(map, thresholds, argv.perFile) -} - -function checkCoverages (map, thresholds, perFile) { - if (perFile) { + if (argv.perFile) { map.files().forEach(file => { - // ERROR: Coverage for lines (90.12%) does not meet threshold (120%) for index.js checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file) }) } else { - // ERROR: Coverage for lines (90.12%) does not meet global threshold (120%) checkCoverage(map.getCoverageSummary(), thresholds) } } diff --git a/lib/commands/report.js b/lib/commands/report.js index f3fffbd9..9aa283ed 100644 --- a/lib/commands/report.js +++ b/lib/commands/report.js @@ -4,8 +4,6 @@ exports.command = 'report' exports.describe = 'read V8 coverage data from temp and output report' -exports.builder = function (yargs) {} - exports.handler = function (argv) { exports.outputReport(argv) } @@ -22,4 +20,5 @@ exports.outputReport = function (argv) { wrapperLength: argv.wrapperLength }) report.run() + return report } diff --git a/lib/parse-args.js b/lib/parse-args.js index 62f9af98..ee4f1955 100644 --- a/lib/parse-args.js +++ b/lib/parse-args.js @@ -25,6 +25,31 @@ function buildYargs (withCommands = false) { default: [], describe: 'a list of specific files that should be covered (glob patterns are supported)' }) + .option('check-coverage', { + default: false, + type: 'boolean', + description: 'check whether coverage is within thresholds provided' + }) + .option('branches', { + default: 0, + description: 'what % of branches must be covered?' + }) + .option('functions', { + default: 0, + description: 'what % of functions must be covered?' + }) + .option('lines', { + default: 90, + description: 'what % of lines must be covered?' + }) + .option('statements', { + default: 0, + description: 'what % of statements must be covered?' + }) + .option('per-file', { + default: false, + description: 'check thresholds per file' + }) .option('temp-directory', { default: './coverage/tmp', describe: 'directory V8 coverage data is written to and read from' diff --git a/lib/report.js b/lib/report.js index 7db228b6..160b0c7e 100644 --- a/lib/report.js +++ b/lib/report.js @@ -46,6 +46,12 @@ class Report { } getCoverageMapFromAllCoverageFiles () { + // the merge process can be very expensive, and it's often the case that + // check-coverage is called immediately after a report. We memoize the + // result from getCoverageMapFromAllCoverageFiles() to address this + // use-case. + if (this._allCoverageFiles) return this._allCoverageFiles + const v8ProcessCov = this._getMergedProcessCov() const map = libCoverage.createCoverageMap({}) @@ -61,7 +67,8 @@ class Report { } } - return map + this._allCoverageFiles = map + return this._allCoverageFiles } /** diff --git a/test/fixtures/normal.js b/test/fixtures/normal.js index 226db158..ad544629 100644 --- a/test/fixtures/normal.js +++ b/test/fixtures/normal.js @@ -15,6 +15,10 @@ function missed () { } +function missed2 () { + +} + apple() apple() apple() diff --git a/test/integration.js b/test/integration.js index dd91bc15..b7671659 100644 --- a/test/integration.js +++ b/test/integration.js @@ -34,7 +34,7 @@ describe('c8', () => { output.toString('utf8').should.matchSnapshot() }) - it('omit-relative can be set to false', () => { + it('allows relative files to be included', () => { const { output } = spawnSync(nodePath, [ c8Path, '--exclude="test/*.js"', @@ -47,4 +47,54 @@ describe('c8', () => { /Error: ENOENT: no such file or directory.*loaders\.js/ ) }) + + describe('check-coverage', () => { + it('exits with 0 if coverage within threshold', () => { + const { output, status } = spawnSync(nodePath, [ + c8Path, + 'check-coverage', + '--exclude="test/*.js"', + '--lines=80' + ]) + status.should.equal(0) + output.toString('utf8').should.matchSnapshot() + }) + + it('allows threshold to be applied on per-file basis', () => { + const { output, status } = spawnSync(nodePath, [ + c8Path, + 'check-coverage', + '--exclude="test/*.js"', + '--lines=80', + '--per-file' + ]) + status.should.equal(1) + output.toString('utf8').should.matchSnapshot() + }) + + it('exits with 1 if coverage is below threshold', () => { + const { output, status } = spawnSync(nodePath, [ + c8Path, + 'check-coverage', + '--exclude="test/*.js"', + '--lines=101' + ]) + status.should.equal(1) + output.toString('utf8').should.matchSnapshot() + }) + + it('allows --check-coverage when executing script', () => { + const { output, status } = spawnSync(nodePath, [ + c8Path, + '--exclude="test/*.js"', + '--clean=false', + '--lines=101', + '--check-coverage', + nodePath, + require.resolve('./fixtures/normal') + ]) + status.should.equal(1) + output.toString('utf8').should.matchSnapshot() + }) + }) }) diff --git a/test/integration.js.snap b/test/integration.js.snap index 6259c092..774dfb9e 100644 --- a/test/integration.js.snap +++ b/test/integration.js.snap @@ -1,5 +1,48 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`c8 check-coverage allows --check-coverage when executing script 1`] = ` +",hey +i am a line of code +what +hey +what +hey +what +hey +--------------------|----------|----------|----------|----------|-------------------| +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | +--------------------|----------|----------|----------|----------|-------------------| +All files | 95.38 | 75.61 | 89.66 | 95.38 | | + bin | 86.49 | 71.43 | 100 | 86.49 | | + c8.js | 86.49 | 71.43 | 100 | 86.49 | 26,34,35,36,37 | + lib | 97.05 | 65 | 100 | 97.05 | | + parse-args.js | 98.35 | 53.85 | 100 | 98.35 | 97,98 | + report.js | 96 | 70.37 | 100 | 96 |... 08,134,135,136 | + lib/commands | 97.53 | 83.33 | 87.5 | 97.53 | | + check-coverage.js | 100 | 92.31 | 100 | 100 | 16 | + report.js | 92 | 60 | 50 | 92 | 8,9 | + test/fixtures | 90.91 | 94.12 | 75 | 90.91 | | + async.js | 100 | 100 | 100 | 100 | | + multiple-spawn.js | 100 | 100 | 100 | 100 | | + normal.js | 76 | 75 | 33.33 | 76 | 14,15,16,18,19,20 | + subprocess.js | 100 | 100 | 100 | 100 | | +--------------------|----------|----------|----------|----------|-------------------| +,ERROR: Coverage for lines (95.38%) does not meet global threshold (101%) +" +`; + +exports[`c8 check-coverage allows threshold to be applied on per-file basis 1`] = ` +",,ERROR: Coverage for lines (76%) does not meet threshold (80%) for /Users/benjamincoe/bcoe/c8/test/fixtures/normal.js +" +`; + +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 (94.95%) does not meet global threshold (101%) +" +`; + exports[`c8 merges reports from subprocesses together 1`] = ` ",first @@ -8,19 +51,19 @@ second --------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | --------------------|----------|----------|----------|----------|-------------------| -All files | 80 | 70.31 | 74.07 | 80 | | - bin | 85.71 | 66.67 | 100 | 85.71 | | - c8.js | 85.71 | 66.67 | 100 | 85.71 | 25,32,33,34,35 | - lib | 94.98 | 57.14 | 100 | 94.98 | | - parse-args.js | 95.83 | 41.67 | 100 | 95.83 | 58,59,72,73 | - report.js | 94.41 | 65.22 | 100 | 94.41 |... 01,127,128,129 | - lib/commands | 34.62 | 83.33 | 14.29 | 34.62 | | - check-coverage.js | 15.38 | 100 | 0 | 15.38 |... 73,74,75,76,77 | - report.js | 92.31 | 75 | 33.33 | 92.31 | 10,11 | - test/fixtures | 95.16 | 94.12 | 85.71 | 95.16 | | +All files | 85.05 | 67.16 | 74.07 | 85.05 | | + bin | 86.49 | 57.14 | 100 | 86.49 | | + c8.js | 86.49 | 57.14 | 100 | 86.49 | 26,34,35,36,37 | + lib | 95.57 | 55.56 | 100 | 95.57 | | + parse-args.js | 96.69 | 41.67 | 100 | 96.69 | 83,84,97,98 | + report.js | 94.67 | 62.5 | 100 | 94.67 |... 08,134,135,136 | + lib/commands | 44.44 | 71.43 | 16.67 | 44.44 | | + check-coverage.js | 23.21 | 100 | 0 | 23.21 |... 51,52,53,54,55 | + report.js | 92 | 60 | 50 | 92 | 8,9 | + test/fixtures | 90.91 | 94.12 | 75 | 90.91 | | async.js | 100 | 100 | 100 | 100 | | multiple-spawn.js | 100 | 100 | 100 | 100 | | - normal.js | 85.71 | 75 | 50 | 85.71 | 14,15,16 | + normal.js | 76 | 75 | 33.33 | 76 | 14,15,16,18,19,20 | subprocess.js | 100 | 100 | 100 | 100 | | --------------------|----------|----------|----------|----------|-------------------| ," @@ -38,9 +81,9 @@ hey -----------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -----------|----------|----------|----------|----------|-------------------| -All files | 91.18 | 88.89 | 80 | 91.18 | | +All files | 84.21 | 88.89 | 66.67 | 84.21 | | async.js | 100 | 100 | 100 | 100 | | - normal.js | 85.71 | 75 | 50 | 85.71 | 14,15,16 | + normal.js | 76 | 75 | 33.33 | 76 | 14,15,16,18,19,20 | -----------|----------|----------|----------|----------|-------------------| ," `; From 5055f18f7ff4d72a64eb6f92faf97c6f1dbbe291 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 21 Jan 2019 17:13:01 -0800 Subject: [PATCH 4/5] chore: make paths relative --- lib/commands/check-coverage.js | 3 ++- test/integration.js.snap | 18 +++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/commands/check-coverage.js b/lib/commands/check-coverage.js index 6fd5c872..6a3a468c 100644 --- a/lib/commands/check-coverage.js +++ b/lib/commands/check-coverage.js @@ -1,3 +1,4 @@ +const { relative } = require('path') const Report = require('../report') exports.command = 'check-coverage' @@ -46,7 +47,7 @@ function checkCoverage (summary, thresholds, file) { if (coverage < thresholds[key]) { process.exitCode = 1 if (file) { - console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + file) + console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + relative('./', file)) } else { console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet global threshold (' + thresholds[key] + '%)') } diff --git a/test/integration.js.snap b/test/integration.js.snap index 774dfb9e..08208d1c 100644 --- a/test/integration.js.snap +++ b/test/integration.js.snap @@ -12,14 +12,14 @@ hey --------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | --------------------|----------|----------|----------|----------|-------------------| -All files | 95.38 | 75.61 | 89.66 | 95.38 | | +All files | 95.39 | 75.61 | 89.66 | 95.39 | | bin | 86.49 | 71.43 | 100 | 86.49 | | c8.js | 86.49 | 71.43 | 100 | 86.49 | 26,34,35,36,37 | lib | 97.05 | 65 | 100 | 97.05 | | parse-args.js | 98.35 | 53.85 | 100 | 98.35 | 97,98 | report.js | 96 | 70.37 | 100 | 96 |... 08,134,135,136 | - lib/commands | 97.53 | 83.33 | 87.5 | 97.53 | | - check-coverage.js | 100 | 92.31 | 100 | 100 | 16 | + lib/commands | 97.56 | 83.33 | 87.5 | 97.56 | | + check-coverage.js | 100 | 92.31 | 100 | 100 | 17 | report.js | 92 | 60 | 50 | 92 | 8,9 | test/fixtures | 90.91 | 94.12 | 75 | 90.91 | | async.js | 100 | 100 | 100 | 100 | | @@ -27,19 +27,19 @@ All files | 95.38 | 75.61 | 89.66 | 95.38 | normal.js | 76 | 75 | 33.33 | 76 | 14,15,16,18,19,20 | subprocess.js | 100 | 100 | 100 | 100 | | --------------------|----------|----------|----------|----------|-------------------| -,ERROR: Coverage for lines (95.38%) does not meet global threshold (101%) +,ERROR: Coverage for lines (95.39%) does not meet global threshold (101%) " `; exports[`c8 check-coverage allows threshold to be applied on per-file basis 1`] = ` -",,ERROR: Coverage for lines (76%) does not meet threshold (80%) for /Users/benjamincoe/bcoe/c8/test/fixtures/normal.js +",,ERROR: Coverage for lines (76%) does not meet threshold (80%) for test/fixtures/normal.js " `; 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 (94.95%) does not meet global threshold (101%) +",,ERROR: Coverage for lines (94.96%) does not meet global threshold (101%) " `; @@ -51,14 +51,14 @@ second --------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | --------------------|----------|----------|----------|----------|-------------------| -All files | 85.05 | 67.16 | 74.07 | 85.05 | | +All files | 85.09 | 67.16 | 74.07 | 85.09 | | bin | 86.49 | 57.14 | 100 | 86.49 | | c8.js | 86.49 | 57.14 | 100 | 86.49 | 26,34,35,36,37 | lib | 95.57 | 55.56 | 100 | 95.57 | | parse-args.js | 96.69 | 41.67 | 100 | 96.69 | 83,84,97,98 | report.js | 94.67 | 62.5 | 100 | 94.67 |... 08,134,135,136 | - lib/commands | 44.44 | 71.43 | 16.67 | 44.44 | | - check-coverage.js | 23.21 | 100 | 0 | 23.21 |... 51,52,53,54,55 | + lib/commands | 45.12 | 71.43 | 16.67 | 45.12 | | + check-coverage.js | 24.56 | 100 | 0 | 24.56 |... 52,53,54,55,56 | report.js | 92 | 60 | 50 | 92 | 8,9 | test/fixtures | 90.91 | 94.12 | 75 | 90.91 | | async.js | 100 | 100 | 100 | 100 | | From 5193b1f3765de6fa889946d9567ca207a19d15d3 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 21 Jan 2019 17:21:41 -0800 Subject: [PATCH 5/5] attempt to fix Windows paths --- bin/c8.js | 2 +- lib/commands/check-coverage.js | 5 ++++- test/integration.js.snap | 19 ++++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/bin/c8.js b/bin/c8.js index 495dd9c3..7a64fa60 100755 --- a/bin/c8.js +++ b/bin/c8.js @@ -26,8 +26,8 @@ let argv = buildYargs().parse(instrumenterArgs) await promisify(rimraf)(argv.tempDirectory) await promisify(fs.mkdir)(argv.tempDirectory, { recursive: true }) } - process.env.NODE_V8_COVERAGE = argv.tempDirectory + process.env.NODE_V8_COVERAGE = argv.tempDirectory foreground(hideInstrumenterArgs(argv), () => { const report = outputReport(argv) if (argv.checkCoverage) checkCoverages(argv, report) diff --git a/lib/commands/check-coverage.js b/lib/commands/check-coverage.js index 6a3a468c..136618f5 100644 --- a/lib/commands/check-coverage.js +++ b/lib/commands/check-coverage.js @@ -47,7 +47,10 @@ function checkCoverage (summary, thresholds, file) { if (coverage < thresholds[key]) { process.exitCode = 1 if (file) { - console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + relative('./', file)) + console.error( + 'ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + + relative('./', file).replace(/\\/g, '/') // standardize path for Windows. + ) } else { console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet global threshold (' + thresholds[key] + '%)') } diff --git a/test/integration.js.snap b/test/integration.js.snap index 08208d1c..0a21127d 100644 --- a/test/integration.js.snap +++ b/test/integration.js.snap @@ -12,14 +12,14 @@ hey --------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | --------------------|----------|----------|----------|----------|-------------------| -All files | 95.39 | 75.61 | 89.66 | 95.39 | | +All files | 95.42 | 75.9 | 89.66 | 95.42 | | bin | 86.49 | 71.43 | 100 | 86.49 | | c8.js | 86.49 | 71.43 | 100 | 86.49 | 26,34,35,36,37 | lib | 97.05 | 65 | 100 | 97.05 | | parse-args.js | 98.35 | 53.85 | 100 | 98.35 | 97,98 | report.js | 96 | 70.37 | 100 | 96 |... 08,134,135,136 | - lib/commands | 97.56 | 83.33 | 87.5 | 97.56 | | - check-coverage.js | 100 | 92.31 | 100 | 100 | 17 | + lib/commands | 97.65 | 84.21 | 87.5 | 97.65 | | + check-coverage.js | 100 | 92.86 | 100 | 100 | 17 | report.js | 92 | 60 | 50 | 92 | 8,9 | test/fixtures | 90.91 | 94.12 | 75 | 90.91 | | async.js | 100 | 100 | 100 | 100 | | @@ -27,19 +27,20 @@ All files | 95.39 | 75.61 | 89.66 | 95.39 | normal.js | 76 | 75 | 33.33 | 76 | 14,15,16,18,19,20 | subprocess.js | 100 | 100 | 100 | 100 | | --------------------|----------|----------|----------|----------|-------------------| -,ERROR: Coverage for lines (95.39%) does not meet global threshold (101%) +,ERROR: Coverage for lines (95.42%) does not meet global threshold (101%) " `; exports[`c8 check-coverage allows threshold to be applied on per-file basis 1`] = ` -",,ERROR: Coverage for lines (76%) does not meet threshold (80%) for test/fixtures/normal.js +",,ERROR: Coverage for lines (78.33%) does not meet threshold (80%) for lib/commands/check-coverage.js +ERROR: Coverage for lines (76%) does not meet threshold (80%) for test/fixtures/normal.js " `; 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 (94.96%) does not meet global threshold (101%) +",,ERROR: Coverage for lines (94.99%) does not meet global threshold (101%) " `; @@ -51,14 +52,14 @@ second --------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | --------------------|----------|----------|----------|----------|-------------------| -All files | 85.09 | 67.16 | 74.07 | 85.09 | | +All files | 84.53 | 67.16 | 74.07 | 84.53 | | bin | 86.49 | 57.14 | 100 | 86.49 | | c8.js | 86.49 | 57.14 | 100 | 86.49 | 26,34,35,36,37 | lib | 95.57 | 55.56 | 100 | 95.57 | | parse-args.js | 96.69 | 41.67 | 100 | 96.69 | 83,84,97,98 | report.js | 94.67 | 62.5 | 100 | 94.67 |... 08,134,135,136 | - lib/commands | 45.12 | 71.43 | 16.67 | 45.12 | | - check-coverage.js | 24.56 | 100 | 0 | 24.56 |... 52,53,54,55,56 | + lib/commands | 43.53 | 71.43 | 16.67 | 43.53 | | + check-coverage.js | 23.33 | 100 | 0 | 23.33 |... 55,56,57,58,59 | report.js | 92 | 60 | 50 | 92 | 8,9 | test/fixtures | 90.91 | 94.12 | 75 | 90.91 | | async.js | 100 | 100 | 100 | 100 | |