Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add --exclude-after-remap option for users who pre-instrument their codebase #697

Merged
merged 2 commits into from
Oct 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section looks good to me 👍


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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've noticed some weirdness around pinning versions in npm 5; using =1.3.8 seems to avoid it

Copy link

@rumpl rumpl Oct 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason spawn-wrap was downgraded? There is a fix in version 1.4.0 that can be useful for people that want to run nyc in a container.

The issue in question: istanbuljs/spawn-wrap#49

"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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
140 changes: 129 additions & 11 deletions test/nyc-bin.js
Original file line number Diff line number Diff line change
@@ -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`)
}