Skip to content

Commit

Permalink
feat: add --exclude-after-remap option for users who pre-instrument t…
Browse files Browse the repository at this point in the history
…heir codebase (#697)
  • Loading branch information
bcoe committed Oct 23, 2017
1 parent a413f6a commit cdfdff3
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 23 deletions.
41 changes: 33 additions & 8 deletions README.md
Expand Up @@ -53,14 +53,14 @@ and a `text-lcov` coverage report.
nyc --reporter=lcov --reporter=text-lcov npm test
```

### Accurate stack traces using source maps
### Accurate stack traces using source-maps

When `produce-source-map` is set to true, then the instrumented source files will
include inline source maps for the instrumenter transform. When combined with
[source-map-support](https://github.com/evanw/node-source-map-support),
stack traces for instrumented code will reflect their original lines.

### Support for custom require hooks (babel, webpack, etc.)
### Support for custom require hooks (babel, typescript, etc.)

nyc supports custom require hooks like
[`babel-register`](http://babeljs.io/docs/usage/require/). nyc can
Expand All @@ -69,9 +69,20 @@ flag](#require-additional-modules).

Source maps are used to map coverage information back to the appropriate lines
of the pre-transpiled code. You'll have to configure your custom require hook
to inline the source map in the transpiled code. For Babel that means setting
to inline the source-map in the transpiled code. For Babel that means setting
the `sourceMaps` option to `inline`.

### Source-Map support for pre-instrumented codebases

If you opt to pre-instrument your source-code (rather than using a just-in-time
transpiler like [`babel-register`](http://babeljs.io/docs/usage/require/))
nyc supports both inline source-maps and `.map` files.

_Important: If you are using nyc with a project that pre-instruments its code,
run nyc with the configuration option `--exclude-after-remap` set to `false`.
Otherwise nyc's reports will exclude any files that source-maps remap to folders
covered under exclude rules._

## Use with `babel-plugin-istanbul` for Babel Support

We recommend using [`babel-plugin-istanbul`](https://github.com/istanbuljs/babel-plugin-istanbul) if your
Expand Down Expand Up @@ -118,20 +129,20 @@ That's all there is to it, better ES2015+ syntax highlighting awaits:

<img width="500" src="screen2.png">

## Support for alternate file extensions (.jsx, .es6)
## Support for alternate file extensions (.jsx, .mjs)

Supporting file extensions can be configured through either the configuration arguments or with the `nyc` config section in `package.json`.

```shell
nyc --extension .jsx --extension .es6 npm test
nyc --extension .jsx --extension .mjs npm test
```

```json
{
"nyc": {
"extension": [
".jsx",
".es6"
".mjs"
]
}
}
Expand Down Expand Up @@ -320,9 +331,18 @@ You can specify custom high and low watermarks in nyc's configuration:
}
```

## Other advanced features
## Parsing Hints (Ignoring Lines)

Take a look at http://istanbul.js.org/docs/advanced/ and please feel free to [contribute documentation](https://github.com/istanbuljs/istanbuljs.github.io/tree/development/content).
There may be some sections of your codebase that you wish to purposefully
exclude from coverage tracking, to do so you can use the following parsing
hints:

* `/* istanbul ignore if */`: ignore the next if statement.
* `/* istanbul ignore else */`: ignore the else portion of an if statement.
* `/* istanbul ignore next */`: ignore the next _thing_ in the source-code (
functions, if statements, classes, you name it).
* `/* istanbul ignore file */`: ignore an entire source-file (this should be
placed at the top of the file).

## Integrating with coveralls

Expand Down Expand Up @@ -396,8 +416,13 @@ Here's how to get `nyc` integrated with codecov and travis-ci.org:
That's all there is to it!

## Integrating with TAP formatters

Many testing frameworks (Mocha, Tape, Tap, etc.) can produce [TAP](https://en.wikipedia.org/wiki/Test_Anything_Protocol) output. [tap-nyc](https://github.com/MegaArman/tap-nyc) is a TAP formatter designed to look nice with nyc.

## More tutorials

You can find more tutorials at http://istanbul.js.org/docs/tutorials

## Other advanced features

Take a look at http://istanbul.js.org/docs/advanced/ and please feel free to [contribute documentation](https://github.com/istanbuljs/istanbuljs.github.io/tree/development/content).
12 changes: 9 additions & 3 deletions index.js
Expand Up @@ -420,9 +420,15 @@ NYC.prototype._getCoverageMapFromAllCoverageFiles = function () {
this.loadReports().forEach(function (report) {
map.merge(report)
})
map.filter(function (filename) {
return _this.exclude.shouldInstrument(filename)
})
// depending on whether source-code is pre-instrumented
// or instrumented using a JIT plugin like babel-require
// you may opt to exclude files after applying
// source-map remapping logic.
if (this.config.excludeAfterRemap) {
map.filter(function (filename) {
return _this.exclude.shouldInstrument(filename)
})
}
map.data = this.sourceMaps.remapCoverage(map.data)
return map
}
Expand Down
6 changes: 6 additions & 0 deletions lib/config-util.js
Expand Up @@ -75,6 +75,12 @@ Config.buildYargs = function (cwd) {
describe: 'a list of specific files and directories that should be excluded from coverage, glob patterns are supported, node_modules is always excluded',
global: false
})
.option('exclude-after-remap', {
default: true,
type: 'boolean',
description: 'should exclude logic be performed after the source-map remaps filenames?',
global: false
})
.option('include', {
alias: 'n',
default: [],
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -96,7 +96,7 @@
"resolve-from": "^2.0.0",
"rimraf": "^2.5.4",
"signal-exit": "^3.0.1",
"spawn-wrap": "^1.4.0",
"spawn-wrap": "1.3.8",
"test-exclude": "^4.1.1",
"yargs": "^10.0.3",
"yargs-parser": "^8.0.0"
Expand All @@ -119,6 +119,7 @@
"split-lines": "^1.0.0",
"standard": "^9.0.2",
"standard-version": "^4.0.0",
"strip-indent": "^2.0.0",
"tap": "^10.0.0",
"which": "^1.2.11",
"zero-fill": "^2.2.3"
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/source-maps/instrumented/s1.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/fixtures/source-maps/instrumented/s1.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions test/fixtures/source-maps/instrumented/s2.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions test/fixtures/source-maps/original/s1.js
@@ -0,0 +1,9 @@
var apple = 99
var banana = 200
function add (item1, item2) {
return item1 + item2
}
function multiply (item1, item2) {
return item1 * item2
}
add(apple, banana)
6 changes: 6 additions & 0 deletions test/fixtures/source-maps/original/s2.js
@@ -0,0 +1,6 @@
var strawberry = 99
var pineapple = 200
function add (item1, item2) {
return item1 + item2
}
add(strawberry, pineapple)
1 change: 1 addition & 0 deletions test/fixtures/source-maps/package.json
@@ -0,0 +1 @@
{}
140 changes: 129 additions & 11 deletions test/nyc-bin.js
@@ -1,16 +1,18 @@
/* global describe, it */

var _ = require('lodash')
var path = require('path')
var bin = path.resolve(__dirname, '../bin/nyc')
var fixturesCLI = path.resolve(__dirname, './fixtures/cli')
var fakebin = path.resolve(fixturesCLI, 'fakebin')
var fixturesHooks = path.resolve(__dirname, './fixtures/hooks')
var fs = require('fs')
var glob = require('glob')
var isWindows = require('is-windows')()
var rimraf = require('rimraf')
var spawn = require('child_process').spawn
const _ = require('lodash')
const path = require('path')
const bin = path.resolve(__dirname, '../bin/nyc')
const fixturesCLI = path.resolve(__dirname, './fixtures/cli')
const fixturesHooks = path.resolve(__dirname, './fixtures/hooks')
const fixturesSourceMaps = path.resolve(__dirname, './fixtures/source-maps')
const fakebin = path.resolve(fixturesCLI, 'fakebin')
const fs = require('fs')
const glob = require('glob')
const isWindows = require('is-windows')()
const rimraf = require('rimraf')
const spawn = require('child_process').spawn
const si = require('strip-indent')

require('chai').should()

Expand Down Expand Up @@ -762,4 +764,120 @@ describe('the nyc cli', function () {
done()
})
})

// the following tests exercise nyc's behavior around source-maps
// that have been included with pre-instrumented files. Perhaps, as an
// example, unit tests are being run against minified JavaScript.
// --exclude-after-remap will likely need to be set to false when
// using nyc with this type of configuration.
describe('source-maps', () => {
describe('--all', () => {
it('includes files with both .map files and inline source-maps', (done) => {
const args = [
bin,
'--all',
'--cache', 'false',
'--exclude-after-remap', 'false',
'--exclude', 'original',
process.execPath, './instrumented/s1.min.js'
]

const proc = spawn(process.execPath, args, {
cwd: fixturesSourceMaps,
env: env
})

var stdout = ''
proc.stdout.on('data', function (chunk) {
stdout += chunk
})

proc.on('close', function (code) {
code.should.equal(0)
stdoutShouldEqual(stdout, `
----------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
All files | 44.44 | 100 | 33.33 | 44.44 | |
s1.js | 80 | 100 | 50 | 80 | 7 |
s2.js | 0 | 100 | 0 | 0 | 1,2,4,6 |
----------|----------|----------|----------|----------|----------------|`
)
done()
})
})
})

describe('.map file', () => {
it('appropriately instruments file with corresponding .map file', (done) => {
const args = [
bin,
'--cache', 'false',
'--exclude-after-remap', 'false',
'--exclude', 'original',
process.execPath, './instrumented/s1.min.js'
]

const proc = spawn(process.execPath, args, {
cwd: fixturesSourceMaps,
env: env
})

var stdout = ''
proc.stdout.on('data', function (chunk) {
stdout += chunk
})

proc.on('close', function (code) {
code.should.equal(0)
stdoutShouldEqual(stdout, `
----------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
All files | 80 | 100 | 50 | 80 | |
s1.js | 80 | 100 | 50 | 80 | 7 |
----------|----------|----------|----------|----------|----------------|`)
done()
})
})
})

describe('inline', () => {
it('appropriately instruments a file with an inline source-map', (done) => {
const args = [
bin,
'--cache', 'false',
'--exclude-after-remap', 'false',
'--exclude', 'original',
process.execPath, './instrumented/s2.min.js'
]

const proc = spawn(process.execPath, args, {
cwd: fixturesSourceMaps,
env: env
})

var stdout = ''
proc.stdout.on('data', function (chunk) {
stdout += chunk
})

proc.on('close', function (code) {
code.should.equal(0)
stdoutShouldEqual(stdout, `
----------|----------|----------|----------|----------|----------------|
File | % Stmts | % Branch | % Funcs | % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
All files | 100 | 100 | 100 | 100 | |
s2.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|----------------|`)
done()
})
})
})
})
})

function stdoutShouldEqual (stdout, expected) {
`\n${stdout}`.should.equal(`${si(expected)}\n`)
}

0 comments on commit cdfdff3

Please sign in to comment.