Skip to content

Commit

Permalink
Merge pull request #36 from enb-bem/feature/coverage2
Browse files Browse the repository at this point in the history
Support reporting coverage for tests
  • Loading branch information
Andrew Abramov committed Nov 27, 2014
2 parents 4f49c91 + 97508b4 commit 1051072
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 19 deletions.
81 changes: 81 additions & 0 deletions lib/coverage-object.js
@@ -0,0 +1,81 @@
var _ = require('lodash'),
inherit = require('inherit');

module.exports = inherit({
__constructor: function () {
this._indices = {
};
this.coverage = {
};
},

addStatement: function (start, end, counter) {
var sourceName = start.source,
sourceObj = this._getFileObject(sourceName),
idx = this._indices[sourceName].s++;
sourceObj.statementMap[idx] = {
start: getLocation(start),
end: getLocation(end)
};
sourceObj.s[idx] = counter;
},

addFunction: function (name, start, end, counter) {
var sourceName = start.source,
sourceObj = this._getFileObject(sourceName),
idx = this._indices[sourceName].f++;
sourceObj.fnMap[idx] = {
name: name,
line: start.line,
loc: {
start: getLocation(start),
end: getLocation(end)
}
};

sourceObj.f[idx] = counter;
},

addBranch: function (type, locations, counter) {
var sourceName = locations[0].start.source,
sourceObj = this._getFileObject(sourceName),
idx = this._indices[sourceName].b++;
sourceObj.branchMap[idx] = {
line: locations[0].start.line,
type: type,
locations: locations.map(function (loc) {
return {
start: getLocation(loc.start),
end: getLocation(loc.end)
};
})
};
sourceObj.b[idx] = counter;
},

_getFileObject: function (sourceName) {
var fileObject = this.coverage[sourceName];
if (!fileObject) {
fileObject = this.coverage[sourceName] = {
path: sourceName,
s: {},
b: {},
f: {},
statementMap: {},
fnMap: {},
branchMap: {}
};

this._indices[sourceName] = {
s: 1,
b: 1,
f: 1
};
}
return fileObject;
}
});

function getLocation(soureMapLocation) {
return _.pick(soureMapLocation, 'line', 'column');
}
26 changes: 22 additions & 4 deletions lib/node-configurator.js
@@ -1,6 +1,8 @@
var path = require('path'),
fs = require('fs'),

_ = require('lodash'),

levels = require('enb-bem-techs/techs/levels'),
files = require('enb-bem-techs/techs/files'),

Expand All @@ -10,7 +12,9 @@ var path = require('path'),
mergeBemdecl = require('enb-bem-techs/techs/merge-bemdecl'),

references = require('./techs/references'),
spec = require('./techs/tmpl-spec');
istanbul = require('./techs/istanbul'),
spec = require('./techs/tmpl-spec'),
instrumentedTarget = require('./util').instrumentedTarget;

exports.configure = function (config, options) {
var pattern = path.join(options.destPath, '*'),
Expand All @@ -20,6 +24,7 @@ exports.configure = function (config, options) {
var nodePath = nodeConfig.getNodePath(),
sublevel = path.join(nodePath, 'blocks'),
engines = options.engines,
coverageEngines = options.coverage.engines,
engineTargets = [];

if (fs.existsSync(sublevel)) {
Expand Down Expand Up @@ -47,13 +52,26 @@ exports.configure = function (config, options) {

engines.forEach(function (engine) {
nodeConfig.addTech([engine.tech, engine.options]);
nodeConfig.addTarget(engine.target);
engineTargets.push(engine.target);
if (_.contains(coverageEngines, engine.name)) {
var instrumented = instrumentedTarget(engine.target);
nodeConfig.addTech([
istanbul,
{ source: engine.target, target: instrumented }
]);
engineTargets.push(instrumented);
} else {
engineTargets.push(engine.target);
}
});

nodeConfig.addTechs([
[references, { dirsTarget: '?.base.dirs' }],
[spec, { engines: engines, engineTargets: engineTargets, saveHtml: options.saveHtml }]
[spec, {
engines: engines,
engineTargets: engineTargets,
saveHtml: options.saveHtml,
coverageEngines: coverageEngines
}]
]);

nodeConfig.addTarget('?.tmpl-spec.js');
Expand Down
26 changes: 23 additions & 3 deletions lib/plugin.js
Expand Up @@ -22,7 +22,14 @@ module.exports = function (helper) {

_init: function (options) {
var root = helper.getRootPath(),
engines = options.engines;
engines = options.engines,
coverage = options.coverage || {};

if (coverage === true) {
coverage = {
engines: Object.keys(engines)
};
}

return {
_options: options,
Expand Down Expand Up @@ -58,6 +65,18 @@ module.exports = function (helper) {
};
}),
sourceLevels: options.sourceLevels || options.levels,
coverage: _.defaults(coverage, {
engines: [],
reportDirectory: 'coverage',
exclude: [
'**/node_modules/**',
'**/libs/**'
],
reporters: process.env.BEM_TMPL_SPECS_COV_REPORTERS ?
process.env.BEM_TMPL_SPECS_COV_REPORTERS.split(',')
: ['lcov']
}),

referenceDirSuffixes: options.referenceDirSuffixes || ['tmpl-specs'],
saveHtml: (typeof process.env.BEM_TMPL_SPECS_SAVE_HTML === 'undefined' ?
options.saveHtml :
Expand Down Expand Up @@ -106,7 +125,8 @@ module.exports = function (helper) {
destPath: options.destPath,
sourceLevels: options.sourceLevels,
engines: options.engines,
saveHtml: options.saveHtml
saveHtml: options.saveHtml,
coverage: options.coverage
});
});
},
Expand Down Expand Up @@ -142,7 +162,7 @@ module.exports = function (helper) {
return path.join(options.root, node, basename + '.tmpl-spec.js');
});

return filesToRun.length && runner.run(filesToRun)
return filesToRun.length && runner.run(filesToRun, options)
.fail(function (err) {
if (err.stack) {
console.error(err.stack);
Expand Down
53 changes: 50 additions & 3 deletions lib/runner.js
@@ -1,7 +1,12 @@
var vow = require('vow'),
Mocha = require('mocha');
Mocha = require('mocha'),
istanbul = require('istanbul'),
minimatch = require('minimatch'),
_ = require('lodash'),

exports.run = function (files) {
unmapCoverageObject = require('./unmap-coverage');

exports.run = function (files, opts) {
var defer = vow.defer(),
mocha = new Mocha({
ui: 'bdd',
Expand All @@ -10,7 +15,15 @@ exports.run = function (files) {

mocha.files = files;
mocha.run(function (failures) {
failures ? defer.reject(failures) : defer.resolve();
function resolvePromise() {
failures ? defer.reject(failures) : defer.resolve();
}

if (opts.coverage.engines.length > 0) {
processCoverage(opts.coverage).done(resolvePromise);
} else {
resolvePromise();
}

process.on('exit', function () {
process.exit(failures);
Expand Down Expand Up @@ -49,3 +62,37 @@ function getReporters() {

return reporters;
}

function processCoverage(coverageOpts) {
var coverage = global.__coverage__;

return unmapCoverageObject(coverage)
.then(function (unmapedCoverage) {
return filterAndSaveCoverage(unmapedCoverage, coverageOpts);
});
}

function filterAndSaveCoverage(coverage, opts) {
coverage = filterCoverage(coverage, opts.exclude);
return saveCoverageReport(coverage, opts);
}

function filterCoverage(coverage, exclude) {
return _.omit(coverage, function (value, fileName) {
return exclude.some(function (pattern) {
return minimatch(fileName, pattern, { matchBase: true });
});
});
}

function saveCoverageReport(coverage, opts) {
var defer = vow.defer(),
reporter = new istanbul.Reporter(null, opts.reportDirectory);
reporter.addAll(opts.reporters);
var collector = new istanbul.Collector();
collector.add(coverage);
reporter.write(collector, false, function () {
defer.resolve();
});
return defer.promise();
}
15 changes: 15 additions & 0 deletions lib/techs/istanbul.js
@@ -0,0 +1,15 @@
var Instrumenter = require('istanbul').Instrumenter,
fs = require('vow-fs');

module.exports = require('enb/lib/build-flow').create()
.name('istanbul')
.target('target', '?.instrumented.js')
.useSourceFilename('source', '?.js')
.builder(function (source) {
return fs.read(source, 'utf8')
.then(function (content) {
var instrumenter = new Instrumenter();
return instrumenter.instrumentSync(content, source);
});
})
.createTech();
12 changes: 9 additions & 3 deletions lib/techs/tmpl-spec.js
@@ -1,15 +1,18 @@
var path = require('path'),
vfs = require('enb/lib/fs/async-fs'),
readAsset = vfs.read(path.join(__dirname, '..', 'assets', 'tmpl-spec.jst')),
template = require('lodash').template,
_ = require('lodash'),
template = _.template,
htmlDifferFilename = require.resolve('html-differ'),
jsBeautifyFilename = require.resolve('js-beautify');
jsBeautifyFilename = require.resolve('js-beautify'),
instrumentedTarget = require('../util').instrumentedTarget;

module.exports = require('enb/lib/build-flow').create()
.name('tmpl-spec')
.target('target', '?.tmpl-spec.js')
.defineRequiredOption('engines')
.defineOption('saveHtml', false)
.defineOption('coverageEngines', [])
.useSourceFilename('references', '?.references.js')
.useSourceListFilenames('engineTargets', [])
.needRebuild(function (cache) {
Expand All @@ -25,6 +28,7 @@ module.exports = require('enb/lib/build-flow').create()
references = require(referencesFilename),
engines = this._engines,
saveHtml = this._saveHtml,
coverageEngines = this._coverageEngines,
its = [];

Object.keys(references).forEach(function (name) {
Expand All @@ -39,9 +43,11 @@ module.exports = require('enb/lib/build-flow').create()
describe: path.basename(nodePath) + ' (' + path.dirname(nodePath) + ')',
its: its,
engines: engines.map(function (engine) {
var target = _.contains(coverageEngines, engine.name) ?
instrumentedTarget(engine.target) : engine.target;
return {
name: engine.name,
target: node.unmaskTargetName(engine.target),
target: node.unmaskTargetName(target),
exportName: engine.exportName
};
}),
Expand Down

0 comments on commit 1051072

Please sign in to comment.