Skip to content

Commit

Permalink
feat: allows an array of ignored method names to be provided (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
katerberg authored and bcoe committed Feb 22, 2018
1 parent c798930 commit 67918e2
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 6 deletions.
8 changes: 5 additions & 3 deletions packages/istanbul-api/lib/config.js
Expand Up @@ -28,7 +28,8 @@ function defaultConfig() {
'include-all-sources': false,
'include-pid': false,
'es-modules': false,
'auto-wrap': false
'auto-wrap': false,
'ignore-class-methods': []
},
reporting: {
print: 'summary',
Expand Down Expand Up @@ -191,7 +192,7 @@ addMethods(InstrumentOptions,
'extensions', 'defaultExcludes', 'completeCopy',
'variable', 'compact', 'preserveComments',
'saveBaseline', 'baselineFile', 'esModules',
'includeAllSources', 'includePid', 'autoWrap');
'includeAllSources', 'includePid', 'autoWrap', 'ignoreClassMethods');

/**
* returns the root directory used by istanbul which is typically the root of the
Expand Down Expand Up @@ -225,7 +226,8 @@ InstrumentOptions.prototype.getInstrumenterOpts = function () {
compact: this.compact(),
preserveComments: this.preserveComments(),
esModules: this.esModules(),
autoWrap: this.autoWrap()
autoWrap: this.autoWrap(),
ignoreClassMethods: this.ignoreClassMethods()
};
};

Expand Down
2 changes: 2 additions & 0 deletions packages/istanbul-lib-instrument/api.md
Expand Up @@ -36,6 +36,7 @@ instead.
- `opts.esModules` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** set to true to instrument ES6 modules. (optional, default `false`)
- `opts.autoWrap` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** set to true to allow `return` statements outside of functions. (optional, default `false`)
- `opts.produceSourceMap` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** set to true to produce a source map for the instrumented code. (optional, default `false`)
- `opts.ignoreClassMethods` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)** set to array of class method names to ignore for coverage. (optional, default `[]`)
- `opts.sourceMapUrlCallback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** a callback function that is called when a source map URL
is found in the original code. This function is called with the source file name and the source map URL. (optional, default `null`)
- `opts.debug` **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)** turn debugging on (optional, default `false`)
Expand Down Expand Up @@ -104,5 +105,6 @@ The exit function returns an object that currently has the following keys:
- `sourceFilePath` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** the path to source file (optional, default `'unknown.js'`)
- `opts` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** additional options (optional, default `{coverageVariable:'__coverage__',inputSourceMap:undefined}`)
- `opts.coverageVariable` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** the global coverage variable name. (optional, default `__coverage__`)
- `opts.ignoreClassMethods` **[Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)** names of methods to ignore by default on classes. (optional, default `[]`)
- `opts.inputSourceMap` **[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** the input source map, that maps the uninstrumented code back to the
original code. (optional, default `undefined`)
3 changes: 3 additions & 0 deletions packages/istanbul-lib-instrument/src/instrumenter.js
Expand Up @@ -16,6 +16,7 @@ function defaultOpts() {
esModules: false,
autoWrap: false,
produceSourceMap: false,
ignoreClassMethods: [],
sourceMapUrlCallback: null,
debug: false
};
Expand All @@ -32,6 +33,7 @@ function defaultOpts() {
* @param {boolean} [opts.esModules=false] set to true to instrument ES6 modules.
* @param {boolean} [opts.autoWrap=false] set to true to allow `return` statements outside of functions.
* @param {boolean} [opts.produceSourceMap=false] set to true to produce a source map for the instrumented code.
* @param {Array} [opts.ignoreClassMethods=[]] set to array of class method names to ignore for coverage.
* @param {Function} [opts.sourceMapUrlCallback=null] a callback function that is called when a source map URL
* is found in the original code. This function is called with the source file name and the source map URL.
* @param {boolean} [opts.debug=false] - turn debugging on
Expand Down Expand Up @@ -91,6 +93,7 @@ class Instrumenter {
});
const ee = programVisitor(t, filename, {
coverageVariable: opts.coverageVariable,
ignoreClassMethods: opts.ignoreClassMethods,
inputSourceMap: inputSourceMap
});
let output = {};
Expand Down
25 changes: 22 additions & 3 deletions packages/istanbul-lib-instrument/src/visitor.js
Expand Up @@ -20,7 +20,7 @@ function genVar(filename) {
// VisitState holds the state of the visitor, provides helper functions
// and is the `this` for the individual coverage visitors.
class VisitState {
constructor(types, sourceFilePath, inputSourceMap) {
constructor(types, sourceFilePath, inputSourceMap, ignoreClassMethods) {
this.varName = genVar(sourceFilePath);
this.attrs = {};
this.nextIgnore = null;
Expand All @@ -29,6 +29,7 @@ class VisitState {
if (typeof (inputSourceMap) !== "undefined") {
this.cov.inputSourceMap(inputSourceMap);
}
this.ignoreClassMethods = ignoreClassMethods;
this.types = types;
this.sourceMappingURL = null;
}
Expand Down Expand Up @@ -72,6 +73,7 @@ class VisitState {
extractURL(node.leadingComments);
extractURL(node.trailingComments);
}


// for these expressions the statement counter needs to be hoisted, so
// function name inference can be preserved
Expand Down Expand Up @@ -99,6 +101,16 @@ class VisitState {
if (this.getAttr(path.node, 'skip-all') !== null) {
this.nextIgnore = n;
}

// else check for ignored class methods
if (path.isFunctionExpression() && this.ignoreClassMethods.some(name => path.node.id && name === path.node.id.name)) {

This comment has been minimized.

Copy link
@chrisjlee

chrisjlee Mar 8, 2018

@katerberg Would it be acceptable to include a check for ignoreClassMethods if it is undefined? otherwise, you would get a some of undefined error if it's not an array ? If so i can add this to a MR.

Currently, i'm having an issue with this and it's breaking a lot of our unit tests for jsx classes that are functions. #150

Thoughts @bcoe ?

This comment has been minimized.

Copy link
@katerberg

katerberg Mar 9, 2018

Author Contributor

Thanks for adding this!

this.nextIgnore = n;
return;
}
if (path.isClassMethod() && this.ignoreClassMethods.some(name => name === path.node.key.name)) {
this.nextIgnore = n;
return;
}
}

// all the generic stuff on exit of a node,
Expand Down Expand Up @@ -491,6 +503,12 @@ function alreadyInstrumented(path, visitState) {
function shouldIgnoreFile(programNode) {
return programNode.parent && programNode.parent.comments.some(c => COMMENT_FILE_RE.test(c.value));
}

const defaultProgramVisitorOpts = {
coverageVariable: '__coverage__',
ignoreClassMethods: [],
inputSourceMap: undefined
};
/**
* programVisitor is a `babel` adaptor for instrumentation.
* It returns an object with two methods `enter` and `exit`.
Expand All @@ -508,12 +526,13 @@ function shouldIgnoreFile(programNode) {
* @param {string} sourceFilePath - the path to source file
* @param {Object} opts - additional options
* @param {string} [opts.coverageVariable=__coverage__] the global coverage variable name.
* @param {Array} [opts.ignoreClassMethods=[]] names of methods to ignore by default on classes.
* @param {object} [opts.inputSourceMap=undefined] the input source map, that maps the uninstrumented code back to the
* original code.
*/
function programVisitor(types, sourceFilePath = 'unknown.js', opts = {coverageVariable: '__coverage__', inputSourceMap: undefined }) {
function programVisitor(types, sourceFilePath = 'unknown.js', opts = defaultProgramVisitorOpts) {
const T = types;
const visitState = new VisitState(types, sourceFilePath, opts.inputSourceMap);
const visitState = new VisitState(types, sourceFilePath, opts.inputSourceMap, opts.ignoreClassMethods);
return {
enter(path) {
if (shouldIgnoreFile(path.find(p => p.isProgram()))) {
Expand Down
43 changes: 43 additions & 0 deletions packages/istanbul-lib-instrument/test/specs/statement-hints.yaml
Expand Up @@ -149,3 +149,46 @@ tests:
lines: {'1': 1, '2': 1, '4': 0}
branches: {'0': [1, 0] }
statements: {'0': 1, '1': 1, '2': 0}
---
name: ignore class methods
guard: isClassAvailable
code: |
class TestClass {
dummy(i) {return i;}
nonIgnored(i) {return i;}
}
var testClass = new TestClass();
testClass.nonIgnored();
output = testClass.dummy(args[0]);
instrumentOpts:
ignoreClassMethods: ['dummy']
tests:
- name: ignores only specified es6 methods
args: [10]
out: 10
lines: {'3': 1, '5': 1, '6': 1, '7': 1}
functions: {'0': 1}
branches: {}
statements: {'0': 1, '1': 1, '2': 1, '3': 1}
---
name: ignore class methods
code: |
function TestClass() {}
TestClass.prototype.testMethod = function testMethod(i) {
return i;
};
TestClass.prototype.goodMethod = function goodMethod(i) {return i;};
var testClass = new TestClass();
testClass.goodMethod();
output = testClass.testMethod(args[0]);
instrumentOpts:
ignoreClassMethods: ['testMethod']
tests:
- name: ignores only specified es5 methods
args: [10]
out: 10
lines: {'2': 1, '5': 1, '6': 1, '7': 1, '8': 1}
functions: {'0': 1, '1': 1}
branches: {}
statements: {'0': 1, '1': 1, '2': 1, '3': 1, '4': 1, '5': 1}

0 comments on commit 67918e2

Please sign in to comment.