/** * Module dependencies. */ var Base = require('./base'); /** * Expose `JSONCov`. */ exports = module.exports = JSONCov; /** * Initialize a new `JsCoverage` reporter. * * @api public * @param {Runner} runner * @param {boolean} output */ function JSONCov(runner, output) { Base.call(this, runner); output = arguments.length === 1 || output; var self = this; var tests = []; var failures = []; var passes = []; runner.on('test end', function(test) { tests.push(test); }); runner.on('pass', function(test) { passes.push(test); }); runner.on('fail', function(test) { failures.push(test); }); runner.on('end', function() { var cov = global._$jscoverage || {}; var result = self.cov = map(cov); result.stats = self.stats; result.tests = tests.map(clean); result.failures = failures.map(clean); result.passes = passes.map(clean); if (!output) { return; } process.stdout.write(JSON.stringify(result, null, 2)); }); } /** * Map jscoverage data to a JSON structure * suitable for reporting. * * @api private * @param {Object} cov * @return {Object} */ function map(cov) { var ret = { instrumentation: 'node-jscoverage', sloc: 0, hits: 0, misses: 0, coverage: 0, files: [] }; for (var filename in cov) { if (Object.prototype.hasOwnProperty.call(cov, filename)) { var data = coverage(filename, cov[filename]); ret.files.push(data); ret.hits += data.hits; ret.misses += data.misses; ret.sloc += data.sloc; } } ret.files.sort(function(a, b) { return a.filename.localeCompare(b.filename); }); if (ret.sloc > 0) { ret.coverage = (ret.hits / ret.sloc) * 100; } return ret; } /** * Map jscoverage data for a single source file * to a JSON structure suitable for reporting. * * @api private * @param {string} filename name of the source file * @param {Object} data jscoverage coverage data * @return {Object} */ function coverage(filename, data) { var ret = { filename: filename, coverage: 0, hits: 0, misses: 0, sloc: 0, source: {} }; data.source.forEach(function(line, num) { num++; if (data[num] === 0) { ret.misses++; ret.sloc++; } else if (data[num] !== undefined) { ret.hits++; ret.sloc++; } ret.source[num] = { source: line, coverage: data[num] === undefined ? '' : data[num] }; }); ret.coverage = ret.hits / ret.sloc * 100; return ret; } /** * Return a plain-object representation of `test` * free of cyclic properties etc. * * @api private * @param {Object} test * @return {Object} */ function clean(test) { return { duration: test.duration, currentRetry: test.currentRetry(), fullTitle: test.fullTitle(), title: test.title }; }