diff --git a/docs/instrument.md b/docs/instrument.md index 06126b992..f0a6d3bf8 100644 --- a/docs/instrument.md +++ b/docs/instrument.md @@ -18,6 +18,9 @@ nyc instrument [output] The `[output]` directory is optional and can be located anywhere, if not set the instrumented code will be sent to `stdout`. For example, `nyc instrument . ./output` will produce instrumented versions of any source files it finds in `.` and store them in `./output`. +The `--baseline` option will save a baseline coverage file to `/baseline/coverage.json`. +Include this file when reporting to highlight files with no test coverage. + The `--delete` option will remove the existing output directory before instrumenting. The `--in-place` option will allow you to run the instrument command. diff --git a/index.js b/index.js index 6d74d0831..2d7b31ef2 100755 --- a/index.js +++ b/index.js @@ -48,6 +48,7 @@ class NYC { this.subprocessBin = config.subprocessBin || path.resolve(__dirname, './bin/nyc.js') this._tempDirectory = config.tempDirectory || config.tempDir || './.nyc_output' + this._baselineDirectoryName = 'baseline' this._instrumenterLib = require(config.instrumenter || './lib/instrumenters/istanbul') this._reportDir = config.reportDir || 'coverage' this._sourceMap = typeof config.sourceMap === 'boolean' ? config.sourceMap : true @@ -170,7 +171,7 @@ class NYC { return source } - async addAllFiles () { + async addAllFiles (coverageFilename) { this._loadAdditionalModules() this.fakeRequire = true @@ -190,7 +191,7 @@ class NYC { }) this.fakeRequire = false - this.writeCoverageFile() + this.writeCoverageFile(coverageFilename) } async instrumentAllFiles (input, output) { @@ -335,6 +336,10 @@ class NYC { await mkdirp(this.cacheDirectory) } + if (this.config.baseline) { + await mkdirp(this.baselineDirectory()) + } + await mkdirp(this.processInfo.directory) } @@ -374,26 +379,25 @@ class NYC { return this } - writeCoverageFile () { - var coverage = coverageFinder() + writeCoverageFile (filename = this.processInfo.uuid) { + const coverage = coverageFinder() // Remove any files that should be excluded but snuck into the coverage - Object.keys(coverage).forEach(function (absFile) { + Object.keys(coverage).forEach(absFile => { if (!this.exclude.shouldInstrument(absFile)) { delete coverage[absFile] } - }, this) + }) if (this.cache) { - Object.keys(coverage).forEach(function (absFile) { + Object.keys(coverage).forEach(absFile => { if (this.hashCache[absFile] && coverage[absFile]) { coverage[absFile].contentHash = this.hashCache[absFile] } - }, this) + }) } - var id = this.processInfo.uuid - var coverageFilename = path.resolve(this.tempDirectory(), id + '.json') + const coverageFilename = path.resolve(this.tempDirectory(), `${filename}.json`) fs.writeFileSync( coverageFilename, @@ -516,6 +520,10 @@ class NYC { return path.resolve(this.cwd, this._tempDirectory) } + baselineDirectory () { + return path.resolve(this.cwd, this._tempDirectory, this._baselineDirectoryName) + } + reportDirectory () { return path.resolve(this.cwd, this._reportDir) } diff --git a/lib/commands/instrument.js b/lib/commands/instrument.js index cabb43775..944b0c658 100644 --- a/lib/commands/instrument.js +++ b/lib/commands/instrument.js @@ -59,5 +59,14 @@ exports.handler = cliWrapper(async argv => { }) } + if (argv.baseline) { + if (argv.clean) { + await nyc.reset() + } else { + await nyc.createTempDirectory() + } + await nyc.addAllFiles('baseline/coverage') + } + await nyc.instrumentAllFiles(argv.input, argv.output) }) diff --git a/test/instrument.js b/test/instrument.js index 284c2f1cb..2d02d509b 100644 --- a/test/instrument.js +++ b/test/instrument.js @@ -353,3 +353,52 @@ t.test('aborts if trying to clean outside working directory', async t => { t.strictEqual(status, 1) t.match(stderr, /attempt to delete/) }) + +t.test('can produce baseline coverage if `--baseline` is set', async t => { + const { status } = await runNYC({ + args: [ + 'instrument', + '--baseline', + './input-dir', + './output-dir' + ], + cwd: subdir + }) + + t.strictEqual(status, 0) + const target = path.resolve(subdir, 'output-dir', 'index.js') + t.match(await fs.readFile(target, 'utf8'), /console.log\('Hello, World!'\)/) + const baseline = path.resolve(fixturesCLI, '.nyc_output', 'baseline', 'coverage.json') + t.match(await fs.readFile(baseline, 'utf8'), /statementMap/) +}) + +t.test('can skip cleaning if `--no-clean` is set during baseline', async t => { + const args = [ + 'instrument', + '--baseline', + './input-dir', + './output-dir' + ] + const cwd = subdir + + const { status: cleanStatus } = await runNYC({ args, cwd }) + + t.strictEqual(cleanStatus, 0) + const target = path.resolve(subdir, 'output-dir', 'index.js') + t.match(await fs.readFile(target, 'utf8'), /console.log\('Hello, World!'\)/) + const baseline = path.resolve(fixturesCLI, '.nyc_output', 'baseline', 'coverage.json') + t.match(await fs.readFile(baseline, 'utf8'), /statementMap/) + + args.splice(2, 0, '--no-clean') + const { status: noCleanStatus } = await runNYC({ args, cwd }) + + t.strictEqual(noCleanStatus, 0) + + const baselineDir = path.resolve(fixturesCLI, '.nyc_output', 'baseline') + const processinfoDir = path.resolve(fixturesCLI, '.nyc_output', 'processinfo') + + const baselineFiles = await fs.readdir(baselineDir) + const processinfoFiles = await fs.readdir(processinfoDir) + t.equal(baselineFiles.length, 1) + t.equal(processinfoFiles.length, 2) +})