diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 000000000..c0a1a4b41 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,194 @@ +var arrify = require('arrify') +var path = require('path') +var pkgUp = require('pkg-up') +var testExclude = require('test-exclude') +var Yargs = require('yargs/yargs') + +// load config from a cascade of sources: +// * command line arguments. +// * package.json. +// * .nycrc (coming soon) +function Config (argv, cwd) { + cwd = cwd || process.env.NYC_CWD || process.cwd() + var pkgPath = pkgUp.sync(cwd) + + if (pkgPath) { + cwd = path.dirname(pkgPath) + } + + var config = Config.buildYargs(cwd) + .default({ + cwd: cwd + }) + .parse(argv || []) + + // post-hoc, we convert several of the + // configuration settings to arrays, providing + // a consistent contract to index.js. + config.require = arrify(config.require) + config.extension = arrify(config.extension) + config.exclude = arrify(config.exclude) + config.include = arrify(config.include) + config.cwd = cwd + + return config +} + +// build a yargs object, omitting any settings +// that would cause the application to exit early. +Config.buildYargs = function (cwd) { + return Yargs([]) + .usage('$0 [command] [options]\n\nrun your tests with the nyc bin to instrument them with coverage') + .command('report', 'run coverage report for .nyc_output', function (yargs) { + return yargs + .usage('$0 report [options]') + .option('reporter', { + alias: 'r', + describe: 'coverage reporter(s) to use', + default: 'text' + }) + .option('report-dir', { + describe: 'directory to output coverage reports in', + default: 'coverage' + }) + .option('temp-directory', { + describe: 'directory from which coverage JSON files are read', + default: './.nyc_output' + }) + .option('show-process-tree', { + describe: 'display the tree of spawned processes', + default: false, + type: 'boolean' + }) + .example('$0 report --reporter=lcov', 'output an HTML lcov report to ./coverage') + }) + .command('check-coverage', 'check whether coverage is within thresholds provided', function (yargs) { + return yargs + .usage('$0 check-coverage [options]') + .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?' + }) + .example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided") + }) + .option('reporter', { + alias: 'r', + describe: 'coverage reporter(s) to use', + default: 'text' + }) + .option('report-dir', { + describe: 'directory to output coverage reports in', + default: 'coverage' + }) + .option('silent', { + alias: 's', + default: false, + type: 'boolean', + describe: "don't output a report after tests finish running" + }) + .option('all', { + alias: 'a', + default: false, + type: 'boolean', + describe: 'whether or not to instrument all files of the project (not just the ones touched by your test suite)' + }) + .option('exclude', { + alias: 'x', + default: testExclude.defaultExclude, + describe: 'a list of specific files and directories that should be excluded from coverage, glob patterns are supported, node_modules is always excluded' + }) + .option('include', { + alias: 'n', + default: [], + describe: 'a list of specific files that should be covered, glob patterns are supported' + }) + .option('require', { + alias: 'i', + default: [], + describe: 'a list of additional modules that nyc should attempt to require in its subprocess, e.g., babel-register, babel-polyfill.' + }) + .option('cache', { + alias: 'c', + default: false, + type: 'boolean', + describe: 'cache instrumentation results for improved performance' + }) + .option('extension', { + alias: 'e', + default: [], + describe: 'a list of extensions that nyc should handle in addition to .js' + }) + .option('check-coverage', { + type: 'boolean', + default: false, + describe: '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('source-map', { + default: true, + type: 'boolean', + description: 'should nyc detect and handle source maps?' + }) + .option('instrument', { + default: true, + type: 'boolean', + description: 'should nyc handle instrumentation?' + }) + .option('hook-run-in-context', { + default: true, + type: 'boolean', + description: 'should nyc wrap vm.runInThisContext?' + }) + .option('show-process-tree', { + describe: 'display the tree of spawned processes', + default: false, + type: 'boolean' + }) + .pkgConf('nyc', cwd || process.cwd()) + .example('$0 npm test', 'instrument your tests with coverage') + .example('$0 --require babel-core/register npm test', 'instrument your tests with coverage and babel') + .example('$0 report --reporter=text-lcov', 'output lcov report after running your tests') + .epilog('visit https://git.io/voHar for list of available reporters') + .boolean('help') + .boolean('h') + .boolean('version') +} + +// decorate yargs with all the actions +// that would make it exit: help, version, command. +Config.decorateYargs = function (yargs) { + return yargs + .help('h') + .alias('h', 'help') + .version() + .command(require('../lib/commands/instrument')) +} + +module.exports = Config diff --git a/package.json b/package.json index 34ad66882..c14dbc3e1 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,9 @@ "glob": "^7.0.6", "istanbul-lib-coverage": "^1.0.0", "istanbul-lib-hook": "^1.0.0-alpha.4", - "istanbul-lib-instrument": "^1.1.1", + "istanbul-lib-instrument": "^1.1.3", "istanbul-lib-report": "^1.0.0-alpha.3", - "istanbul-lib-source-maps": "^1.0.0", + "istanbul-lib-source-maps": "^1.0.1", "istanbul-reports": "^1.0.0-alpha.8", "md5-hex": "^1.2.0", "micromatch": "^2.3.11", @@ -93,7 +93,7 @@ "pkg-up": "^1.0.0", "resolve-from": "^2.0.0", "rimraf": "^2.5.4", - "signal-exit": "^3.0.0", + "signal-exit": "^3.0.1", "spawn-wrap": "^1.2.4", "test-exclude": "^2.1.2", "yargs": "^5.0.0", diff --git a/test/fixtures/check-instrumented.js b/test/fixtures/check-instrumented.js index 4e91f940d..b36853cbb 100644 --- a/test/fixtures/check-instrumented.js +++ b/test/fixtures/check-instrumented.js @@ -3,5 +3,5 @@ function probe () {} // When instrumented there will be references to variables like // __cov_pwkoI2PYHp3LJXkn_erl1Q in the probe() source. module.exports = function () { - return /\b__cov_\B/.test(probe + '') + return /\bcov_\B/.test(probe + '') } diff --git a/test/fixtures/conf-multiple-extensions/check-instrumented.es6 b/test/fixtures/conf-multiple-extensions/check-instrumented.es6 index 4e91f940d..b36853cbb 100644 --- a/test/fixtures/conf-multiple-extensions/check-instrumented.es6 +++ b/test/fixtures/conf-multiple-extensions/check-instrumented.es6 @@ -3,5 +3,5 @@ function probe () {} // When instrumented there will be references to variables like // __cov_pwkoI2PYHp3LJXkn_erl1Q in the probe() source. module.exports = function () { - return /\b__cov_\B/.test(probe + '') + return /\bcov_\B/.test(probe + '') } diff --git a/test/fixtures/conf-multiple-extensions/check-instrumented.foo.bar b/test/fixtures/conf-multiple-extensions/check-instrumented.foo.bar index 4e91f940d..b36853cbb 100644 --- a/test/fixtures/conf-multiple-extensions/check-instrumented.foo.bar +++ b/test/fixtures/conf-multiple-extensions/check-instrumented.foo.bar @@ -3,5 +3,5 @@ function probe () {} // When instrumented there will be references to variables like // __cov_pwkoI2PYHp3LJXkn_erl1Q in the probe() source. module.exports = function () { - return /\b__cov_\B/.test(probe + '') + return /\bcov_\B/.test(probe + '') } diff --git a/test/src/nyc-test.js b/test/src/nyc-test.js index 9657d6a6d..1fb06d683 100644 --- a/test/src/nyc-test.js +++ b/test/src/nyc-test.js @@ -66,13 +66,11 @@ describe('nyc', function () { describe('config', function () { it("loads 'exclude' patterns from package.json#nyc", function () { var nyc = new NYC(configUtil.loadConfig([], path.resolve(__dirname, '../fixtures'))) - nyc.exclude.exclude.length.should.eql(4) }) it("loads 'extension' patterns from package.json#nyc", function () { var nyc = new NYC(configUtil.loadConfig([], path.resolve(__dirname, '../fixtures/conf-multiple-extensions'))) - nyc.extensions.length.should.eql(3) }) @@ -93,7 +91,6 @@ describe('nyc', function () { it("ignores 'exclude' option if it's falsy", function () { var nyc1 = new NYC(configUtil.loadConfig([], path.resolve(__dirname, '../fixtures/conf-empty'))) - nyc1.exclude.exclude.length.should.eql(7) }) @@ -317,8 +314,8 @@ describe('nyc', function () { var report = reports[0][notLoadedPath] reports.length.should.equal(1) + report.s['0'].should.equal(0) report.s['1'].should.equal(0) - report.s['2'].should.equal(0) return done() }) @@ -338,12 +335,12 @@ describe('nyc', function () { reports.length.should.equal(1) var report1 = reports[0][notLoadedPath1] + report1.s['0'].should.equal(0) report1.s['1'].should.equal(0) - report1.s['2'].should.equal(0) var report2 = reports[0][notLoadedPath2] + report2.s['0'].should.equal(0) report2.s['1'].should.equal(0) - report2.s['2'].should.equal(0) return done() }) @@ -364,8 +361,8 @@ describe('nyc', function () { var report = reports[0][notLoadedPath] reports.length.should.equal(1) + report.s['0'].should.equal(1) report.s['1'].should.equal(1) - report.s['2'].should.equal(1) return done() }) @@ -378,7 +375,6 @@ describe('nyc', function () { ) var nyc = (new NYC(configUtil.loadConfig(['--require', './test/fixtures/transpile-hook'], fixtures))) - nyc.reset() nyc.addAllFiles() @@ -389,7 +385,7 @@ describe('nyc', function () { var report = reports[0][needsTranspilePath] reports.length.should.equal(1) - report.s['1'].should.equal(0) + report.s['0'].should.equal(0) fs.unlinkSync(needsTranspilePath) return done() @@ -418,7 +414,7 @@ describe('nyc', function () { var report = reports[0][needsTranspilePath] reports.length.should.equal(1) - report.s['1'].should.equal(0) + report.s['0'].should.equal(0) fs.unlinkSync(needsTranspilePath) return done()