Skip to content

Commit

Permalink
feat: adding support for including all sources in coverage data
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Winchester authored and Matt Winchester committed Mar 23, 2015
1 parent b328e2e commit 1809175
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 4 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,20 @@ coverageReporter: {
}
```

#### includeAllSources
**Type:** Boolean

You can opt to include all sources files, as indicated by the coverage preprocessor, in your code coverage data, even if there are no tests covering them. (Default `false`)

```javascript
coverageReporter: {
type : 'text',
dir : 'coverage/',
file : 'coverage.txt',
includeAllSources: true
}
```

#### multiple reporters
You can use multiple reporters, by providing array of options.

Expand Down
19 changes: 19 additions & 0 deletions lib/coverageMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var coverageMap = {};

function addCoverage(coverageObj){
coverageMap[coverageObj.path] = coverageObj;
}

function getCoverageMap(){
return coverageMap;
}

function resetCoverage(){
coverageMap = {};
}

module.exports = {
add: addCoverage,
get: getCoverageMap,
reset: resetCoverage
};
15 changes: 14 additions & 1 deletion lib/preprocessor.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
var istanbul = require('istanbul'),
minimatch = require('minimatch'),
globalSourceCache = require('./sourceCache'),
extend = require('util')._extend;
extend = require('util')._extend,
coverageMap = require('./coverageMap');

var createCoveragePreprocessor = function(logger, basePath, reporters, coverageReporter) {
var log = logger.create('preprocessor.coverage');
var instrumenterOverrides = (coverageReporter && coverageReporter.instrumenter) || {};
var instrumenters = extend({istanbul: istanbul}, (coverageReporter && coverageReporter.instrumenters));
var sourceCache = globalSourceCache.getByBasePath(basePath);
var includeAllSources = coverageReporter && coverageReporter.includeAllSources === true;
var instrumentersOptions = Object.keys(instrumenters).reduce(function getInstumenterOptions(memo, instrumenterName){
memo[instrumenterName] = (coverageReporter && coverageReporter.instrumenterOptions && coverageReporter.instrumenterOptions[instrumenterName]) || {};
return memo;
Expand Down Expand Up @@ -62,6 +64,17 @@ var createCoveragePreprocessor = function(logger, basePath, reporters, coverageR
// remember the actual immediate instrumented JS for given original path
sourceCache[jsPath] = content;

if (includeAllSources) {
var coverageObjRegex = /\{.*"path".*"fnMap".*"statementMap".*"branchMap".*\}/g;
var coverageObjMatch = coverageObjRegex.exec(instrumentedCode);

if (coverageObjMatch !== null) {
var coverageObj = JSON.parse(coverageObjMatch[0]);

coverageMap.add(coverageObj);
}
}

done(instrumentedCode);
});
};
Expand Down
9 changes: 6 additions & 3 deletions lib/reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var util = require('util');
var istanbul = require('istanbul');
var dateformat = require('dateformat');
var globalSourceCache = require('./sourceCache');
var coverageMap = require('./coverageMap');


var Store = istanbul.Store;
Expand Down Expand Up @@ -39,6 +40,7 @@ var CoverageReporter = function(rootConfig, helper, logger) {
var basePath = rootConfig.basePath;
var reporters = config.reporters;
var sourceCache = globalSourceCache.getByBasePath(basePath);
var includeAllSources = config.includeAllSources === true;

if (!helper.isDefined(reporters)) {
reporters = [config];
Expand Down Expand Up @@ -91,14 +93,15 @@ var CoverageReporter = function(rootConfig, helper, logger) {

// TODO(vojta): remove once we don't care about Karma 0.10
if (browsers) {
browsers.forEach(function(browser) {
collectors[browser.id] = new istanbul.Collector();
});
browsers.forEach(this.onBrowserStart.bind(this));
}
};

this.onBrowserStart = function(browser) {
collectors[browser.id] = new istanbul.Collector();
if(includeAllSources){
collectors[browser.id].add(coverageMap.get());
}
};

this.onBrowserComplete = function(browser, result) {
Expand Down
28 changes: 28 additions & 0 deletions test/coverageMap.spec.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
coverageMap = require '../lib/coverageMap'
coverageObj = { path: './path.js', otherThings: 'that are in instrumented code' }

describe 'coverageMap', ->
it 'should add coverageMap and get them', ->
coverageMap.add(coverageObj)

expect(coverageMap.get()['./path.js']).to.equal coverageObj

it 'should be able to be reset', ->
coverageMap.reset()

expect(coverageMap.get()['./path.js']).to.not.exist

coverageMap.add(coverageObj)

expect(coverageMap.get()['./path.js']).to.equal coverageObj

coverageMap.reset()

expect(coverageMap.get()['./path.js']).to.not.exist

it 'should be able to have multiple coverageMap', ->
coverageMap.reset()
coverageMap.add(coverageObj)
coverageMap.add({ path: './anotherFile.js', moarKeys: [1, 2, 3] })

expect(Object.keys(coverageMap.get()).length).to.equal 2
32 changes: 32 additions & 0 deletions test/preprocessor.spec.coffee
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
vm = require 'vm'
util = require 'util'

coverageMap = require '../lib/coverageMap'

describe 'preprocessor', ->
createPreprocessor = require '../lib/preprocessor'

Expand Down Expand Up @@ -124,3 +126,33 @@ describe 'preprocessor', ->
'**/*.coffee': 'madeup'
expect(work).to.throw()
done()

it 'should add coverageMap when including all sources', (done) ->
process = createPreprocessor mockLogger, '/base/path', ['coverage'], { includeAllSources: true }
file = new File '/base/path/file.js'

coverageMap.reset()

process ORIGINAL_CODE, file, (preprocessedCode) ->
expect(coverageMap.get()['./file.js']).to.exist
done()

it 'should not add coverageMap when not including all sources', (done) ->
process = createPreprocessor mockLogger, '/base/path', ['coverage'], { includeAllSources: false }
file = new File '/base/path/file.js'

coverageMap.reset()

process ORIGINAL_CODE, file, (preprocessedCode) ->
expect(coverageMap.get()['./file.js']).to.not.exist
done()

it 'should not add coverageMap in the default state', (done) ->
process = createPreprocessor mockLogger, '/base/path', ['coverage'], {}
file = new File '/base/path/file.js'

coverageMap.reset()

process ORIGINAL_CODE, file, (preprocessedCode) ->
expect(coverageMap.get()['./file.js']).to.not.exist
done()
47 changes: 47 additions & 0 deletions test/reporter.spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ describe 'reporter', ->
merge: (v...) -> helper.merge v...
mkdirIfNotExists: mockMkdir
normalizeWinPath: (path) -> helper.normalizeWinPath path
mockCoverageMap =
add: sinon.spy()
get: sinon.spy()

mocks =
fs: mockFs
Expand All @@ -46,6 +49,7 @@ describe 'reporter', ->
Collector: mockCollector
Report: create: mockReportCreate
dateformat: require 'dateformat'
'./coverageMap': mockCoverageMap

beforeEach ->
m = loadFile __dirname + '/../lib/reporter.js', mocks
Expand Down Expand Up @@ -246,3 +250,46 @@ describe 'reporter', ->
rootConfig.coverageReporter.reporters = [{ type: 'text-summary', file: 'file' }]
run()
expect(mockMkdir).to.have.been.calledTwice

it 'should support including all sources', ->
customConfig = _.merge {}, rootConfig,
coverageReporter:
dir: 'defaultdir'
includeAllSources: true

mockCoverageMap.get.reset()
mockAdd.reset()

reporter = new m.CoverageReporter customConfig, mockHelper, mockLogger
reporter.onRunStart()
browsers.forEach (b) -> reporter.onBrowserStart b

expect(mockCoverageMap.get).to.have.been.called
expect(mockAdd).to.have.been.calledWith mockCoverageMap.get.returnValues[0]

it 'should not retrieve the coverageMap if we aren\'t including all sources', ->
customConfig = _.merge {}, rootConfig,
coverageReporter:
dir: 'defaultdir'
includeAllSources: false

mockCoverageMap.get.reset()

reporter = new m.CoverageReporter customConfig, mockHelper, mockLogger
reporter.onRunStart()
browsers.forEach (b) -> reporter.onBrowserStart b

expect(mockCoverageMap.get).not.to.have.been.called

it 'should default to not including all sources', ->
customConfig = _.merge {}, rootConfig,
coverageReporter:
dir: 'defaultdir'

mockCoverageMap.get.reset()

reporter = new m.CoverageReporter customConfig, mockHelper, mockLogger
reporter.onRunStart()
browsers.forEach (b) -> reporter.onBrowserStart b

expect(mockCoverageMap.get).not.to.have.been.called

0 comments on commit 1809175

Please sign in to comment.