Skip to content

Commit

Permalink
feat(preprocessor): free instrumenter
Browse files Browse the repository at this point in the history
Allow any Istanbul instrumenter to be used in the karma-coverage.
Will help #120

Closes #101 #49

BREAKING CHANGE: Karma-coverage does not ship with additional instrumenter. You need to explicitly install the instrumenter you need.

Removed **Ibrik** instrumenter that need to be installed explicitly.

Quick list of known community instrumenters :
- [Ibrik](https://github.com/Constellation/ibrik) (CoffeeScript files instrumenter).
- [Ismailia](https://github.com/Spote/ismailia) (ES6 files instrumenter using Traceur).
- [Isparta](https://github.com/douglasduteil/isparta) (ES6 files instrumenter using 6to5).
  • Loading branch information
douglasduteil committed Feb 3, 2015
1 parent 0c99f75 commit 626e7b0
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 24 deletions.
33 changes: 27 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,26 +206,47 @@ coverageReporter: {
```

#### instrumenter
Karma-coverage infers the instrumenter regarding of the file extension.
The `.coffee` files are by default covered using
[Ibrik](https://github.com/Constellation/ibrik) (an
[Istanbul](https://github.com/gotwarlost/istanbul) analog for
CoffeeScript files). It is possible to override this behavior and point out an
Karma-coverage can infers the instrumenter regarding of the file extension.
It is possible to override this behavior and point out an
instrumenter for the files matching a specific pattern.
To do so, you need to declare an object under with the keys represents the
pattern to match, and the instrumenter to apply. The matching will be done
using [minimatch](https://github.com/isaacs/minimatch).
If two patterns match, the last one will take the precedence.

For example you can use [Ibrik](https://github.com/Constellation/ibrik) (an
[Istanbul](https://github.com/gotwarlost/istanbul) analog for
CoffeeScript files) with:

```javascript
coverageReporter: {
instrumenters: { ibrik : require('ibrik') }
instrumenter: {
'**/*.coffee': 'istanbul' // Force the use of the Istanbul instrumenter to cover CoffeeScript files
'**/*.coffee': 'ibrik'
},
// ...
}
```

You can pass options additional options to specific instrumenter with:

```javascript
var to5Options = { experimental: true };

// [...]

coverageReporter: {
instrumenters: { isparta : require('isparta') },
instrumenter: {
'**/*.js': 'isparta'
},
instrumenterOptions: {
isparta: { to5 : to5Options }
}
}
```


----

For more information on Karma see the [homepage].
Expand Down
26 changes: 14 additions & 12 deletions lib/preprocessor.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
var istanbul = require('istanbul'),
ibrik = require('ibrik'),
minimatch = require('minimatch'),
globalSourceCache = require('./sourceCache');
globalSourceCache = require('./sourceCache'),
extend = require('util')._extend;

var createCoveragePreprocessor = function(logger, basePath, reporters, coverageReporter) {
var log = logger.create('preprocessor.coverage');
var instrumenterOverrides = (coverageReporter && coverageReporter.instrumenter) || {};
var instrumenters = {istanbul: istanbul, ibrik: ibrik};
var instrumenters = extend({istanbul: istanbul}, (coverageReporter && coverageReporter.instrumenters));
var sourceCache = globalSourceCache.getByBasePath(basePath);
var instrumentersOptions = Object.keys(instrumenters).reduce(function getInstumenterOptions(memo, instrumenterName){
memo[instrumenterName] = (coverageReporter && coverageReporter.instrumenterOptions && coverageReporter.instrumenterOptions[instrumenterName]) || {};
return memo;
}, {});

// if coverage reporter is not used, do not preprocess the files
if (reporters.indexOf('coverage') === -1) {
Expand All @@ -20,8 +24,8 @@ var createCoveragePreprocessor = function(logger, basePath, reporters, coverageR
function checkInstrumenters() {
var literal;
for (var pattern in instrumenterOverrides) {
literal = String(instrumenterOverrides[pattern]).toLowerCase();
if (literal !== 'istanbul' && literal !== 'ibrik') {
literal = String(instrumenterOverrides[pattern]);
if (Object.keys(instrumenters).indexOf(literal) < 0) {
log.error('Unknown instrumenter: %s', literal);
return false;
}
Expand All @@ -38,25 +42,23 @@ var createCoveragePreprocessor = function(logger, basePath, reporters, coverageR
log.debug('Processing "%s".', file.originalPath);

var jsPath = file.originalPath.replace(basePath + '/', './');
var instrumenterLiteral = jsPath.match(/\.coffee$/) ? 'ibrik' : 'istanbul';
// default instrumenters
var instrumenterLiteral = 'istanbul';

for (var pattern in instrumenterOverrides) {
if (minimatch(file.originalPath, pattern, {dot: true})) {
instrumenterLiteral = String(instrumenterOverrides[pattern]).toLowerCase();
instrumenterLiteral = String(instrumenterOverrides[pattern]);
}
}

var instrumenter = new instrumenters[instrumenterLiteral].Instrumenter();
var instrumenter = new instrumenters[instrumenterLiteral].Instrumenter(instrumentersOptions[instrumenterLiteral] || {});

instrumenter.instrument(content, jsPath, function(err, instrumentedCode) {

if (err) {
log.error('%s\n at %s', err.message, file.originalPath);
}

if (instrumenterLiteral === 'ibrik') {
file.path = file.path.replace(/\.coffee$/, '.js');
}

// remember the actual immediate instrumented JS for given original path
sourceCache[jsPath] = content;

Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"author": "SATO taichi <ryushi@gmail.com>",
"dependencies": {
"istanbul": "~0.3.0",
"ibrik": "~2.0.0",
"dateformat": "~1.0.6",
"minimatch": "~0.3.0"
},
Expand Down
40 changes: 35 additions & 5 deletions test/preprocessor.spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,50 @@ describe 'preprocessor', ->
expect(sandbox.__coverage__).to.have.ownProperty './file.js'
done()

it 'should preprocess the coffee code', (done) ->
process = createPreprocessor mockLogger, '/base/path', ['coverage', 'progress'], {}
file = new File '/base/path/file.coffee'
it 'should preprocess the fake code', (done) ->
fakeInstanbulLikeInstrumenter = ->
fakeInstanbulLikeInstrumenter::instrument = (_a, _b, callback) ->
callback()
return
process = createPreprocessor mockLogger, '/base/path', ['coverage', 'progress'],
instrumenters:
fakeInstanbulLike :
Instrumenter : fakeInstanbulLikeInstrumenter
instrumenter:
'**/*.fake': 'fakeInstanbulLike'
file = new File '/base/path/file.fake'

process ORIGINAL_COFFEE_CODE, file, (preprocessedCode) ->
sandbox =
a: true
something: ->

vm.runInNewContext preprocessedCode, sandbox
expect(file.path).to.equal '/base/path/file.js'
expect(sandbox.__coverage__).to.have.ownProperty './file.coffee'
expect(file.path).to.equal '/base/path/file.fake'
done()

it 'should preprocess the fake code with the config options', (done) ->
fakeInstanbulLikeInstrumenter = (options) ->
expect(options.experimental).to.be.ok
return
fakeInstanbulLikeInstrumenter::instrument = (_a, _b, callback) ->
callback()
return

process = createPreprocessor mockLogger, '/base/path', ['coverage', 'progress'],
instrumenters:
fakeInstanbulLike:
Instrumenter: fakeInstanbulLikeInstrumenter
instrumenterOptions:
fakeInstanbulLike:
experimental: yes
instrumenter:
'**/*.fake': 'fakeInstanbulLike'

file = new File '/base/path/file.fake'

process ORIGINAL_COFFEE_CODE, file, done

it 'should not preprocess the coffee code', (done) ->
process = createPreprocessor mockLogger, '/base/path', ['coverage', 'progress'],
instrumenter:
Expand Down

0 comments on commit 626e7b0

Please sign in to comment.