From 5d5f340d2d400e5d3f8f847c193796e284b1b7d1 Mon Sep 17 00:00:00 2001 From: "Benjamin E. Coe" Date: Fri, 22 Feb 2019 11:52:31 -0800 Subject: [PATCH] chore(test): consolidate on tap test runner, stop running build step (#997) * remove build step from tests * address standard nits * remove forking-tap dependency * increase tap timeout now that we have bigger test files --- build-tests.js | 56 --- package-lock.json | 243 +---------- package.json | 7 +- test/lib/reset-state.js | 23 + test/nyc-index.js | 536 ++++++++++++++++++++++++ test/{nyc-bin.js => nyc-integration.js} | 1 + test/nyc.js | 48 --- test/process-args.js | 1 + 8 files changed, 564 insertions(+), 351 deletions(-) delete mode 100644 build-tests.js create mode 100644 test/lib/reset-state.js create mode 100644 test/nyc-index.js rename test/{nyc-bin.js => nyc-integration.js} (99%) delete mode 100644 test/nyc.js diff --git a/build-tests.js b/build-tests.js deleted file mode 100644 index 3889f8806..000000000 --- a/build-tests.js +++ /dev/null @@ -1,56 +0,0 @@ -'use strict' - -var fs = require('fs') -var path = require('path') -var rimraf = require('rimraf') -var mkdirp = require('make-dir') -var forkingTap = require('forking-tap') -var zeroFill = require('zero-fill') -var sanitizeFilename = require('sanitize-filename') - -// Delete previous files. -process.chdir(__dirname) -rimraf.sync('test/build') -mkdirp.sync(path.join(__dirname, 'test/build')) - -var testDir = path.join(__dirname, 'test/src') -var buildDir = path.join(__dirname, 'test/build') -var originalTestsFilename = path.join(testDir, 'nyc-tap.js') -var originalTestSource = fs.readFileSync(originalTestsFilename, 'utf8') -var individualTests = forkingTap(originalTestSource, { - filename: originalTestsFilename, - attachComment: true -}) - -function writeTest (test, i, enableCache) { - var filename = ['built', zeroFill(3, i)] - .concat(test.nestedName) - .join('-') - - if (enableCache) { - filename += '-cache' - } - - filename += '.js' - - // file names with spaces are legal, but annoying to use w/ CLI commands - filename = filename.replace(/\s/g, '_') - - // istanbul freaks out if the there are `'` characters in the file name - filename = filename.replace(/'/g, '') - - // remove any illegal chars - filename = sanitizeFilename(filename) - - var code = test.code - if (enableCache) { - code = code.replace('var enableCache = false', 'var enableCache = true') - } - - fs.writeFileSync(path.join(buildDir, filename), code) -} - -individualTests.forEach(function (test, i) { - writeTest(test, i * 2, false) - writeTest(test, i * 2 + 1, true) -}) diff --git a/package-lock.json b/package-lock.json index 10f0767a9..8ffc3f54b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -241,12 +241,6 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "ast-types": { - "version": "0.8.15", - "resolved": "http://registry.npmjs.org/ast-types/-/ast-types-0.8.15.tgz", - "integrity": "sha1-ju8IJ/BN/w7IhXupJavj/qYZTlI=", - "dev": true - }, "async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", @@ -367,12 +361,6 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -1989,7 +1977,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -2352,16 +2340,6 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", "dev": true }, - "forking-tap": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/forking-tap/-/forking-tap-0.1.1.tgz", - "integrity": "sha1-1h0sMgrv7+1JEPTOAbCyrtluqx8=", - "dev": true, - "requires": { - "inline-source-map-comment": "^1.0.5", - "recast": "^0.10.39" - } - }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", @@ -2786,12 +2764,6 @@ "is-stream": "^1.0.1" } }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, "hosted-git-info": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", @@ -2854,67 +2826,6 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, - "inline-source-map-comment": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/inline-source-map-comment/-/inline-source-map-comment-1.0.5.tgz", - "integrity": "sha1-UKikTCp5DfrEQbXJTszVRiY1+vY=", - "dev": true, - "requires": { - "chalk": "^1.0.0", - "get-stdin": "^4.0.1", - "minimist": "^1.1.1", - "sum-up": "^1.0.1", - "xtend": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "inquirer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", @@ -2946,14 +2857,6 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "requires": { - "builtin-modules": "^1.0.0" - } - }, "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", @@ -3572,71 +3475,6 @@ } } }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "commander": { - "version": "2.15.1", - "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -5057,12 +4895,6 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", @@ -5170,26 +5002,6 @@ "util-deprecate": "~1.0.1" } }, - "recast": { - "version": "0.10.43", - "resolved": "http://registry.npmjs.org/recast/-/recast-0.10.43.tgz", - "integrity": "sha1-uV1Q9tYHYaX2JS4V2AZ4FoSRzn8=", - "dev": true, - "requires": { - "ast-types": "0.8.15", - "esprima-fb": "~15001.1001.0-dev-harmony-fb", - "private": "~0.1.5", - "source-map": "~0.5.0" - }, - "dependencies": { - "esprima-fb": { - "version": "15001.1001.0-dev-harmony-fb", - "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz", - "integrity": "sha1-Q761fsJujPI3092LM+QlM1d/Jlk=", - "dev": true - } - } - }, "redent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", @@ -5544,7 +5356,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -5908,57 +5720,6 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, - "sum-up": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.3.tgz", - "integrity": "sha1-HGYfZnBX9jvLeHWqFDi8FiUlFW4=", - "dev": true, - "requires": { - "chalk": "^1.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/package.json b/package.json index 326571721..23251247c 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,9 @@ "scripts": { "bundle": "bundle-dependencies update", "posttest": "standard", - "test": "npm run clean && npm run build && npm run instrument && npm run test-integration && npm run test-mocha && npm run report", + "test": "npm run clean && npm run instrument && tap -t360 --no-cov -b ./test/*.js && npm run report", "clean": "rimraf ./.nyc_output ./node_modules/.cache ./.self_coverage ./test/fixtures/.nyc_output ./test/fixtures/node_modules/.cache *covered.js ./lib/*covered.js", - "build": "node ./build-tests", "instrument": "node ./build-self-coverage.js", - "test-integration": "tap -t120 --no-cov -b ./test/build/*.js && mocha --timeout=15000 ./test/nyc-bin.js", - "test-mocha": "node ./bin/nyc --no-clean --silent --temp-dir=./.self_coverage mocha ./test/nyc.js ./test/process-args.js", "report": "node ./bin/nyc --temp-dir ./.self_coverage/ -r text -r lcov report", "release": "standard-version" }, @@ -104,10 +101,8 @@ "bundle-dependencies": "^1.0.2", "chai": "^4.2.0", "coveralls": "^3.0.2", - "forking-tap": "^0.1.1", "is-windows": "^1.0.2", "lodash": "^4.17.11", - "mocha": "^5.2.0", "newline-regex": "^0.2.1", "requirejs": "^2.3.6", "sanitize-filename": "^1.6.1", diff --git a/test/lib/reset-state.js b/test/lib/reset-state.js new file mode 100644 index 000000000..dc73d32da --- /dev/null +++ b/test/lib/reset-state.js @@ -0,0 +1,23 @@ +// reset global state modified by nyc in non-integration tests. +const extensions = Object.assign({}, require.extensions) // eslint-disable-line +const glob = require('glob') +const rimraf = require('rimraf') + +module.exports = function () { + // nuke any temporary files created during test runs. + glob.sync('test/**/*/{.nyc_output,.cache}').forEach(function (path) { + rimraf.sync(path) + }) + // reset Node's require cache. + Object.keys(require.cache).forEach((key) => { + if (key.indexOf('node_modules') === -1) delete require.cache[key] + }) + // reset any custom loaders for extensions, disabling the stack maintained + // by append-transform. + Object.keys(require.extensions).forEach((key) => { // eslint-disable-line + delete require.extensions[key] // eslint-disable-line + if (extensions[key]) require.extensions[key] = extensions[key] // eslint-disable-line + }) + // reset any NYC-specific environment variables that might have been set. + delete process.env.NYC_CWD +} diff --git a/test/nyc-index.js b/test/nyc-index.js new file mode 100644 index 000000000..00f7f8f3d --- /dev/null +++ b/test/nyc-index.js @@ -0,0 +1,536 @@ +/* global describe, it, beforeEach */ + +require('source-map-support').install({ hookRequire: true }) + +const _ = require('lodash') +const ap = require('any-path') +const configUtil = require('../lib/config-util') +const fs = require('fs') + +let NYC +try { + NYC = require('../index.covered.js') +} catch (e) { + NYC = require('../') +} +// we test exit handlers in nyc-integration.js. +NYC.prototype._wrapExit = () => {} + +const path = require('path') +const rimraf = require('rimraf') +const isWindows = require('is-windows')() +const spawn = require('child_process').spawn +const fixtures = path.resolve(__dirname, './fixtures') +const bin = path.resolve(__dirname, '../bin/nyc') +const resetState = require('./lib/reset-state') + +require('chai').should() +require('tap').mochaGlobals() + +describe('nyc', function () { + beforeEach(resetState) + + describe('cwd', function () { + it('sets cwd to process.cwd() if no environment variable is set', function () { + var nyc = new NYC(configUtil.buildYargs().parse()) + + nyc.cwd.should.eql(process.cwd()) + }) + + it('uses NYC_CWD environment variable for cwd if it is set', function () { + process.env.NYC_CWD = path.resolve(__dirname, './fixtures') + var nyc = new NYC(configUtil.buildYargs().parse()) + + nyc.cwd.should.equal(path.resolve(__dirname, './fixtures')) + }) + + it('will look upwards for package.json from cwd', function () { + var nyc = new NYC(configUtil.buildYargs(__dirname).parse()) + nyc.cwd.should.eql(path.join(__dirname, '..')) + }) + + it('uses --cwd for cwd if it is set (highest priority and does not look upwards for package.json) ', function () { + var nyc = new NYC(configUtil.buildYargs(__dirname).parse(['--cwd', __dirname])) + nyc.cwd.should.eql(__dirname) + }) + }) + + describe('config', function () { + it("loads 'exclude' patterns from package.json#nyc", function () { + var nyc = new NYC(configUtil.buildYargs(path.resolve(__dirname, './fixtures')).parse()) + nyc.exclude.exclude.length.should.eql(8) + }) + + it("loads 'extension' patterns from package.json#nyc", function () { + var nyc = new NYC(configUtil.buildYargs(path.resolve(__dirname, './fixtures/conf-multiple-extensions')).parse()) + nyc.extensions.length.should.eql(3) + }) + + it("ignores 'include' option if it's falsy or []", function () { + var nyc1 = new NYC(configUtil.buildYargs(path.resolve(__dirname, './fixtures/conf-empty')).parse()) + + nyc1.exclude.include.should.equal(false) + + var nyc2 = new NYC({ + include: [] + }) + + nyc2.exclude.include.should.equal(false) + }) + + it("ignores 'exclude' option if it's falsy", function () { + var nyc1 = new NYC(configUtil.buildYargs(path.resolve(__dirname, './fixtures/conf-empty')).parse()) + nyc1.exclude.exclude.length.should.eql(15) + }) + + it("allows for empty 'exclude'", function () { + var nyc2 = new NYC({ exclude: [] }) + + // an empty exclude still has **/node_modules/**, node_modules/** and added. + nyc2.exclude.exclude.length.should.eql(2) + }) + }) + + describe('shouldInstrumentFile', function () { + it('should exclude appropriately with defaults', function () { + var nyc = new NYC(configUtil.buildYargs('/cwd').parse([ + '--exclude=test/**', + '--exclude=test{,-*}.js', + '--exclude=**/*.test.js', + '--exclude=**/__tests__/**' + ])) + + // nyc always excludes "node_modules/**" + nyc.exclude.shouldInstrument('/cwd/foo', 'foo').should.equal(true) + nyc.exclude.shouldInstrument('/cwd/node_modules/bar', 'node_modules/bar').should.equal(false) + nyc.exclude.shouldInstrument('/cwd/foo/node_modules/bar', 'foo/node_modules/bar').should.equal(false) + nyc.exclude.shouldInstrument('/cwd/test.js', 'test.js').should.equal(false) + nyc.exclude.shouldInstrument('/cwd/testfoo.js', 'testfoo.js').should.equal(true) + nyc.exclude.shouldInstrument('/cwd/test-foo.js', 'test-foo.js').should.equal(false) + nyc.exclude.shouldInstrument('/cwd/lib/test.js', 'lib/test.js').should.equal(true) + nyc.exclude.shouldInstrument('/cwd/foo/bar/test.js', './test.js').should.equal(false) + nyc.exclude.shouldInstrument('/cwd/foo/bar/test.js', '.\\test.js').should.equal(false) + nyc.exclude.shouldInstrument('/cwd/foo/bar/foo.test.js', './foo.test.js').should.equal(false) + nyc.exclude.shouldInstrument('/cwd/foo/bar/__tests__/foo.js', './__tests__/foo.js').should.equal(false) + }) + + it('should exclude appropriately with config.exclude', function () { + var nyc = new NYC(configUtil.buildYargs(fixtures).parse()) + + // fixtures/package.json configures excludes: "blarg", "blerg" + nyc.exclude.shouldInstrument('blarg', 'blarg').should.equal(false) + nyc.exclude.shouldInstrument('blarg/foo.js', 'blarg/foo.js').should.equal(false) + nyc.exclude.shouldInstrument('blerg', 'blerg').should.equal(false) + nyc.exclude.shouldInstrument('./blerg', './blerg').should.equal(false) + nyc.exclude.shouldInstrument('./blerg', '.\\blerg').should.equal(false) + }) + + it('should exclude outside of the current working directory', function () { + var nyc = new NYC(configUtil.buildYargs('/cwd/foo/').parse()) + nyc.exclude.shouldInstrument('/cwd/bar', '../bar').should.equal(false) + }) + + it('should not exclude if the current working directory is inside node_modules', function () { + var nyc = new NYC(configUtil.buildYargs('/cwd/node_modules/foo/').parse()) + nyc.exclude.shouldInstrument('/cwd/node_modules/foo/bar', './bar').should.equal(true) + nyc.exclude.shouldInstrument('/cwd/node_modules/foo/bar', '.\\bar').should.equal(true) + }) + + it('allows files to be explicitly included, rather than excluded', function () { + var nyc = new NYC(configUtil.buildYargs('/cwd/').parse(['--include=foo.js'])) + + nyc.exclude.shouldInstrument('/cwd/foo.js', 'foo.js').should.equal(true) + nyc.exclude.shouldInstrument('/cwd/index.js', 'index.js').should.equal(false) + }) + + it('exclude overrides include', function () { + var nyc = new NYC(configUtil.buildYargs('/cwd/').parse([ + '--include=foo.js', + '--include=test.js', + '--exclude=**/node_modules/**', + '--exclude=test/**', + '--exclude=test{,-*}.js' + ])) + + nyc.exclude.shouldInstrument('/cwd/foo.js', 'foo.js').should.equal(true) + nyc.exclude.shouldInstrument('/cwd/test.js', 'test.js').should.equal(false) + }) + }) + + describe('wrap', function () { + it('wraps modules with coverage counters when they are required', function () { + var nyc = new NYC(configUtil.buildYargs().parse()) + nyc.reset() + nyc.wrap() + + var check = require('./fixtures/check-instrumented') + check().should.equal(true) + }) + + describe('custom require hooks are installed', function () { + it('wraps modules with coverage counters when the custom require hook compiles them', function () { + let required = false + const hook = function (module, filename) { + if (filename.indexOf('check-instrumented.js') !== -1) { + required = true + } + module._compile(fs.readFileSync(filename, 'utf8'), filename) + } + + var nyc = new NYC(configUtil.buildYargs().parse()) + nyc.reset() + nyc.wrap() + + // install the custom require hook + require.extensions['.js'] = hook // eslint-disable-line + + const check = require('./fixtures/check-instrumented') + check().should.equal(true) + + // and the hook should have been called + required.should.equal(true) + }) + }) + + describe('produce source map', function () { + it('handles stack traces', function () { + var nyc = new NYC(configUtil.buildYargs().parse('--produce-source-map')) + nyc.reset() + nyc.wrap() + + var check = require('./fixtures/stack-trace') + check().should.match(/stack-trace.js:4:/) + }) + + it('does not handle stack traces when disabled', function () { + var nyc = new NYC(configUtil.buildYargs().parse()) + nyc.reset() + nyc.wrap() + + var check = require('./fixtures/stack-trace') + check().should.match(/stack-trace.js:1:/) + }) + }) + + describe('compile handlers for custom extensions are assigned', function () { + it('assigns a function to custom extensions', function () { + var nyc = new NYC(configUtil.buildYargs( + path.resolve(__dirname, './fixtures/conf-multiple-extensions') + ).parse()) + nyc.reset() + nyc.wrap() + + require.extensions['.es6'].should.be.a('function') // eslint-disable-line + require.extensions['.foo.bar'].should.be.a('function') // eslint-disable-line + + // default should still exist + require.extensions['.js'].should.be.a('function') // eslint-disable-line + }) + + it('calls the `_handleJs` function for custom file extensions', function () { + const required = { + es6: false, + custom: false + } + var nyc = new NYC(configUtil.buildYargs( + path.resolve(__dirname, './fixtures/conf-multiple-extensions') + ).parse()) + + nyc['_handleJs'] = (code, options) => { + if (options.filename.indexOf('check-instrumented.es6') !== -1) { + required.es6 = true + } + if (options.filename.indexOf('check-instrumented.foo.bar') !== -1) { + required.custom = true + } + return code + } + + nyc.reset() + nyc.wrap() + + require('./fixtures/conf-multiple-extensions/check-instrumented.es6') + require('./fixtures/conf-multiple-extensions/check-instrumented.foo.bar') + required.custom.should.equal(true) + required.es6.should.equal(true) + }) + }) + + function testSignal (signal, done) { + var nyc = (new NYC(configUtil.buildYargs(fixtures).parse())) + + var proc = spawn(process.execPath, [bin, './' + signal + '.js'], { + cwd: fixtures, + env: {}, + stdio: 'ignore' + }) + + proc.on('close', function () { + var reports = _.filter(nyc.loadReports(), function (report) { + return report[path.join(fixtures, signal + '.js')] + }) + reports.length.should.equal(1) + return done() + }) + } + + it('writes coverage report when process is killed with SIGTERM', function (done) { + if (isWindows) return done() + testSignal('sigterm', done) + }) + + it('writes coverage report when process is killed with SIGINT', function (done) { + if (isWindows) return done() + testSignal('sigint', done) + }) + + it('does not output coverage for files that have not been included, by default', function (done) { + var nyc = (new NYC(configUtil.buildYargs(process.cwd()).parse())) + nyc.wrap() + nyc.reset() + + var reports = _.filter(nyc.loadReports(), function (report) { + return report['./test/fixtures/not-loaded.js'] + }) + reports.length.should.equal(0) + return done() + }) + }) + + describe('report', function () { + it('allows coverage report to be output in an alternative directory', function (done) { + var nyc = new NYC(configUtil.buildYargs().parse( + ['--report-dir=./alternative-report', '--reporter=lcov'] + )) + nyc.reset() + + var proc = spawn(process.execPath, ['./test/fixtures/child-1.js'], { + cwd: process.cwd(), + env: process.env, + stdio: 'inherit' + }) + + proc.on('close', function () { + nyc.report() + fs.existsSync('./alternative-report/lcov.info').should.equal(true) + rimraf.sync('./alternative-report') + return done() + }) + }) + }) + + describe('addAllFiles', function () { + it('outputs an empty coverage report for all files that are not excluded', function (done) { + var nyc = new NYC(configUtil.buildYargs(fixtures).parse()) + nyc.reset() + nyc.addAllFiles() + + var notLoadedPath = path.join(fixtures, './not-loaded.js') + var reports = _.filter(nyc.loadReports(), function (report) { + return ap(report)[notLoadedPath] + }) + var report = reports[0][notLoadedPath] + + reports.length.should.equal(1) + report.s['0'].should.equal(0) + report.s['1'].should.equal(0) + return done() + }) + + it('outputs an empty coverage report for multiple configured extensions', function (done) { + var cwd = path.resolve(fixtures, './conf-multiple-extensions') + var nyc = new NYC(configUtil.buildYargs(cwd).parse()) + nyc.reset() + nyc.addAllFiles() + + var notLoadedPath1 = path.join(cwd, './not-loaded.es6') + var notLoadedPath2 = path.join(cwd, './not-loaded.js') + var reports = _.filter(nyc.loadReports(), function (report) { + var apr = ap(report) + return apr[notLoadedPath1] || apr[notLoadedPath2] + }) + + reports.length.should.equal(1) + + var report1 = reports[0][notLoadedPath1] + report1.s['0'].should.equal(0) + report1.s['1'].should.equal(0) + + var report2 = reports[0][notLoadedPath2] + report2.s['0'].should.equal(0) + report2.s['1'].should.equal(0) + + return done() + }) + + it('tracks coverage appropriately once the file is required', function (done) { + var nyc = (new NYC(configUtil.buildYargs(fixtures).parse())) + nyc.reset() + nyc.wrap() + + require('./fixtures/not-loaded') + + nyc.writeCoverageFile() + + var notLoadedPath = path.join(fixtures, './not-loaded.js') + var reports = _.filter(nyc.loadReports(), function (report) { + return report[notLoadedPath] + }) + var report = reports[0][notLoadedPath] + + reports.length.should.equal(1) + report.s['0'].should.equal(1) + report.s['1'].should.equal(1) + + return done() + }) + + it('transpiles .js files added via addAllFiles', function (done) { + fs.writeFileSync( + './test/fixtures/needs-transpile.js', + '--> pork chop sandwiches <--\nvar a = 99', + 'utf-8' + ) + + var nyc = (new NYC(configUtil.buildYargs(fixtures).parse(['--require', './test/fixtures/transpile-hook']))) + nyc.reset() + nyc.addAllFiles() + + var needsTranspilePath = path.join(fixtures, './needs-transpile.js') + var reports = _.filter(nyc.loadReports(), function (report) { + return ap(report)[needsTranspilePath] + }) + var report = reports[0][needsTranspilePath] + + reports.length.should.equal(1) + report.s['0'].should.equal(0) + + fs.unlinkSync(needsTranspilePath) + return done() + }) + + it('does not attempt to transpile files when they are excluded', function (done) { + var notNeedTranspilePath = path.join(fixtures, './do-not-need-transpile.do-not-transpile') + fs.writeFileSync( + notNeedTranspilePath, + '--> pork chop sandwiches <--\nvar a = 99', + 'utf-8' + ) + + var nyc = (new NYC(configUtil.buildYargs(fixtures).parse([ + '--require=./test/fixtures/transpile-hook', + '--extension=.do-not-transpile', + '--include=needs-transpile.do-not-transpile' + ]))) + + nyc.reset() + nyc.addAllFiles() + fs.unlinkSync(notNeedTranspilePath) + return done() + }) + }) + + it('transpiles non-.js files added via addAllFiles', function (done) { + fs.writeFileSync( + './test/fixtures/needs-transpile.whatever', + '--> pork chop sandwiches <--\nvar a = 99', + 'utf-8' + ) + + var nyc = (new NYC(configUtil.buildYargs(fixtures).parse([ + '--require=./test/fixtures/transpile-hook', + '--extension=.whatever' + ]))) + + nyc.reset() + nyc.addAllFiles() + + var needsTranspilePath = path.join(fixtures, './needs-transpile.whatever') + var reports = _.filter(nyc.loadReports(), function (report) { + return ap(report)[needsTranspilePath] + }) + var report = reports[0][needsTranspilePath] + + reports.length.should.equal(1) + report.s['0'].should.equal(0) + + fs.unlinkSync(needsTranspilePath) + return done() + }) + + describe('cache', function () { + it('handles collisions', function (done) { + var nyc = new NYC(configUtil.buildYargs(fixtures).parse()) + nyc.clearCache() + + var args = [bin, process.execPath, './cache-collision-runner.js'] + + var proc = spawn(process.execPath, args, { + cwd: fixtures, + env: {} + }) + + proc.on('close', function (code) { + code.should.equal(0) + done() + }) + }) + + it('handles identical files', function (done) { + var nyc = new NYC(configUtil.buildYargs(fixtures).parse()) + nyc.clearCache() + + var args = [bin, process.execPath, './identical-file-runner.js'] + + var proc = spawn(process.execPath, args, { + cwd: fixtures, + env: {} + }) + + proc.on('close', function (code) { + code.should.equal(0) + done() + }) + }) + }) + + describe('_disableCachingTransform', function () { + it('is disabled if cache is "false"', function () { + const nyc = new NYC({ cache: false }) + nyc._disableCachingTransform().should.equal(true) + }) + + it('is enabled if cache is "true" and isChildProcess is "true"', function () { + const nyc = new NYC({ + cache: true, + isChildProcess: true + }) + nyc._disableCachingTransform().should.equal(false) + }) + + it('is disabled if cache is "true" and isChildProcess is "false"', function () { + const nyc = new NYC({ + cache: true, + isChildProcess: true + }) + nyc._disableCachingTransform().should.equal(false) + }) + }) + + describe('cacheDirectory', function () { + it('should resolve default cache folder to absolute path', function () { + const nyc = new NYC({ + cache: true + }) + path.isAbsolute(nyc.cacheDirectory).should.equal(true) + }) + + it('should resolve custom cache folder to absolute path', function () { + const nyc = new NYC({ + cacheDir: '.nyc_cache', + cache: true + }) + path.isAbsolute(nyc.cacheDirectory).should.equal(true) + }) + }) +}) diff --git a/test/nyc-bin.js b/test/nyc-integration.js similarity index 99% rename from test/nyc-bin.js rename to test/nyc-integration.js index 1aae65b97..aa6b9a39c 100644 --- a/test/nyc-bin.js +++ b/test/nyc-integration.js @@ -15,6 +15,7 @@ const spawn = require('child_process').spawn const si = require('strip-indent') require('chai').should() +require('tap').mochaGlobals() // beforeEach rimraf.sync(path.resolve(fakebin, 'node')) diff --git a/test/nyc.js b/test/nyc.js deleted file mode 100644 index 11e1c0c50..000000000 --- a/test/nyc.js +++ /dev/null @@ -1,48 +0,0 @@ -/* global describe, it */ - -const NYC = require('../') -const path = require('path') - -require('chai').should() - -describe('NYC', function () { - describe('_disableCachingTransform', function () { - it('is disabled if cache is "false"', function () { - const nyc = new NYC({ cache: false }) - nyc._disableCachingTransform().should.equal(true) - }) - - it('is enabled if cache is "true" and isChildProcess is "true"', function () { - const nyc = new NYC({ - cache: true, - isChildProcess: true - }) - nyc._disableCachingTransform().should.equal(false) - }) - - it('is disabled if cache is "true" and isChildProcess is "false"', function () { - const nyc = new NYC({ - cache: true, - isChildProcess: true - }) - nyc._disableCachingTransform().should.equal(false) - }) - }) - - describe('cacheDirectory', function () { - it('should resolve default cache folder to absolute path', function () { - const nyc = new NYC({ - cache: true - }) - path.isAbsolute(nyc.cacheDirectory).should.equal(true) - }) - - it('should resolve custom cache folder to absolute path', function () { - const nyc = new NYC({ - cacheDir: '.nyc_cache', - cache: true - }) - path.isAbsolute(nyc.cacheDirectory).should.equal(true) - }) - }) -}) diff --git a/test/process-args.js b/test/process-args.js index bab9806e8..4509bc5e3 100644 --- a/test/process-args.js +++ b/test/process-args.js @@ -1,6 +1,7 @@ /* global describe, it */ require('chai').should() +require('tap').mochaGlobals() const processArgs = require('../lib/process-args')