aboutsummaryrefslogtreecommitdiff
path: root/node_modules/istanbul/lib/report
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/istanbul/lib/report')
-rw-r--r--node_modules/istanbul/lib/report/clover.js227
-rw-r--r--node_modules/istanbul/lib/report/cobertura.js221
-rw-r--r--node_modules/istanbul/lib/report/common/defaults.js51
-rw-r--r--node_modules/istanbul/lib/report/html.js572
-rw-r--r--node_modules/istanbul/lib/report/index.js104
-rw-r--r--node_modules/istanbul/lib/report/json-summary.js75
-rw-r--r--node_modules/istanbul/lib/report/json.js69
-rw-r--r--node_modules/istanbul/lib/report/lcov.js65
-rw-r--r--node_modules/istanbul/lib/report/lcovonly.js103
-rw-r--r--node_modules/istanbul/lib/report/none.js41
-rw-r--r--node_modules/istanbul/lib/report/teamcity.js92
-rw-r--r--node_modules/istanbul/lib/report/templates/foot.txt20
-rw-r--r--node_modules/istanbul/lib/report/templates/head.txt60
-rw-r--r--node_modules/istanbul/lib/report/text-lcov.js50
-rw-r--r--node_modules/istanbul/lib/report/text-summary.js93
-rw-r--r--node_modules/istanbul/lib/report/text.js234
16 files changed, 2077 insertions, 0 deletions
diff --git a/node_modules/istanbul/lib/report/clover.js b/node_modules/istanbul/lib/report/clover.js
new file mode 100644
index 000000000..fe73aefcd
--- /dev/null
+++ b/node_modules/istanbul/lib/report/clover.js
@@ -0,0 +1,227 @@
+var path = require('path'),
+ util = require('util'),
+ Report = require('./index'),
+ FileWriter = require('../util/file-writer'),
+ TreeSummarizer = require('../util/tree-summarizer'),
+ utils = require('../object-utils');
+
+/**
+ * a `Report` implementation that produces a clover-style XML file.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('clover');
+ *
+ * @class CloverReport
+ * @module report
+ * @extends Report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the clover.xml will be written
+ * @param {String} [opts.file] the file name, defaulted to config attribute or 'clover.xml'
+ */
+function CloverReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ this.projectRoot = process.cwd();
+ this.dir = opts.dir || this.projectRoot;
+ this.file = opts.file || this.getDefaultConfig().file;
+ this.opts = opts;
+}
+
+CloverReport.TYPE = 'clover';
+util.inherits(CloverReport, Report);
+
+function asJavaPackage(node) {
+ return node.displayShortName().
+ replace(/\//g, '.').
+ replace(/\\/g, '.').
+ replace(/\.$/, '');
+}
+
+function asClassName(node) {
+ /*jslint regexp: true */
+ return node.fullPath().replace(/.*[\\\/]/, '');
+}
+
+function quote(thing) {
+ return '"' + thing + '"';
+}
+
+function attr(n, v) {
+ return ' ' + n + '=' + quote(v) + ' ';
+}
+
+function branchCoverageByLine(fileCoverage) {
+ var branchMap = fileCoverage.branchMap,
+ branches = fileCoverage.b,
+ ret = {};
+ Object.keys(branchMap).forEach(function (k) {
+ var line = branchMap[k].line,
+ branchData = branches[k];
+ ret[line] = ret[line] || [];
+ ret[line].push.apply(ret[line], branchData);
+ });
+ Object.keys(ret).forEach(function (k) {
+ var dataArray = ret[k],
+ covered = dataArray.filter(function (item) { return item > 0; }),
+ coverage = covered.length / dataArray.length * 100;
+ ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage };
+ });
+ return ret;
+}
+
+function addClassStats(node, fileCoverage, writer) {
+ fileCoverage = utils.incrementIgnoredTotals(fileCoverage);
+
+ var metrics = node.metrics,
+ branchByLine = branchCoverageByLine(fileCoverage),
+ fnMap,
+ lines;
+
+ writer.println('\t\t\t<file' +
+ attr('name', asClassName(node)) +
+ attr('path', node.fullPath()) +
+ '>');
+
+ writer.println('\t\t\t\t<metrics' +
+ attr('statements', metrics.lines.total) +
+ attr('coveredstatements', metrics.lines.covered) +
+ attr('conditionals', metrics.branches.total) +
+ attr('coveredconditionals', metrics.branches.covered) +
+ attr('methods', metrics.functions.total) +
+ attr('coveredmethods', metrics.functions.covered) +
+ '/>');
+
+ fnMap = fileCoverage.fnMap;
+ lines = fileCoverage.l;
+ Object.keys(lines).forEach(function (k) {
+ var str = '\t\t\t\t<line' +
+ attr('num', k) +
+ attr('count', lines[k]),
+ branchDetail = branchByLine[k];
+
+ if (!branchDetail) {
+ str += ' type="stmt" ';
+ } else {
+ str += ' type="cond" ' +
+ attr('truecount', branchDetail.covered) +
+ attr('falsecount', (branchDetail.total - branchDetail.covered));
+ }
+ writer.println(str + '/>');
+ });
+
+ writer.println('\t\t\t</file>');
+}
+
+function walk(node, collector, writer, level, projectRoot) {
+ var metrics,
+ totalFiles = 0,
+ totalPackages = 0,
+ totalLines = 0,
+ tempLines = 0;
+ if (level === 0) {
+ metrics = node.metrics;
+ writer.println('<?xml version="1.0" encoding="UTF-8"?>');
+ writer.println('<coverage' +
+ attr('generated', Date.now()) +
+ 'clover="3.2.0">');
+
+ writer.println('\t<project' +
+ attr('timestamp', Date.now()) +
+ attr('name', 'All Files') +
+ '>');
+
+ node.children.filter(function (child) { return child.kind === 'dir'; }).
+ forEach(function (child) {
+ totalPackages += 1;
+ child.children.filter(function (child) { return child.kind !== 'dir'; }).
+ forEach(function (child) {
+ Object.keys(collector.fileCoverageFor(child.fullPath()).l).forEach(function (k){
+ tempLines = k;
+ });
+ totalLines += Number(tempLines);
+ totalFiles += 1;
+ });
+ });
+
+ writer.println('\t\t<metrics' +
+ attr('statements', metrics.lines.total) +
+ attr('coveredstatements', metrics.lines.covered) +
+ attr('conditionals', metrics.branches.total) +
+ attr('coveredconditionals', metrics.branches.covered) +
+ attr('methods', metrics.functions.total) +
+ attr('coveredmethods', metrics.functions.covered) +
+ attr('elements', metrics.lines.total + metrics.branches.total + metrics.functions.total) +
+ attr('coveredelements', metrics.lines.covered + metrics.branches.covered + metrics.functions.covered) +
+ attr('complexity', 0) +
+ attr('packages', totalPackages) +
+ attr('files', totalFiles) +
+ attr('classes', totalFiles) +
+ attr('loc', totalLines) +
+ attr('ncloc', totalLines) +
+ '/>');
+ }
+ if (node.packageMetrics) {
+ metrics = node.packageMetrics;
+ writer.println('\t\t<package' +
+ attr('name', asJavaPackage(node)) +
+ '>');
+
+ writer.println('\t\t\t<metrics' +
+ attr('statements', metrics.lines.total) +
+ attr('coveredstatements', metrics.lines.covered) +
+ attr('conditionals', metrics.branches.total) +
+ attr('coveredconditionals', metrics.branches.covered) +
+ attr('methods', metrics.functions.total) +
+ attr('coveredmethods', metrics.functions.covered) +
+ '/>');
+
+ node.children.filter(function (child) { return child.kind !== 'dir'; }).
+ forEach(function (child) {
+ addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer);
+ });
+ writer.println('\t\t</package>');
+ }
+ node.children.filter(function (child) { return child.kind === 'dir'; }).
+ forEach(function (child) {
+ walk(child, collector, writer, level + 1, projectRoot);
+ });
+
+ if (level === 0) {
+ writer.println('\t</project>');
+ writer.println('</coverage>');
+ }
+}
+
+Report.mix(CloverReport, {
+ synopsis: function () {
+ return 'XML coverage report that can be consumed by the clover tool';
+ },
+ getDefaultConfig: function () {
+ return { file: 'clover.xml' };
+ },
+ writeReport: function (collector, sync) {
+ var summarizer = new TreeSummarizer(),
+ outputFile = path.join(this.dir, this.file),
+ writer = this.opts.writer || new FileWriter(sync),
+ projectRoot = this.projectRoot,
+ that = this,
+ tree,
+ root;
+
+ collector.files().forEach(function (key) {
+ summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
+ });
+ tree = summarizer.getTreeSummary();
+ root = tree.root;
+ writer.on('done', function () { that.emit('done'); });
+ writer.writeFile(outputFile, function (contentWriter) {
+ walk(root, collector, contentWriter, 0, projectRoot);
+ writer.done();
+ });
+ }
+});
+
+module.exports = CloverReport;
diff --git a/node_modules/istanbul/lib/report/cobertura.js b/node_modules/istanbul/lib/report/cobertura.js
new file mode 100644
index 000000000..d63456c95
--- /dev/null
+++ b/node_modules/istanbul/lib/report/cobertura.js
@@ -0,0 +1,221 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ util = require('util'),
+ Report = require('./index'),
+ FileWriter = require('../util/file-writer'),
+ TreeSummarizer = require('../util/tree-summarizer'),
+ utils = require('../object-utils');
+
+/**
+ * a `Report` implementation that produces a cobertura-style XML file that conforms to the
+ * http://cobertura.sourceforge.net/xml/coverage-04.dtd DTD.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('cobertura');
+ *
+ * @class CoberturaReport
+ * @module report
+ * @extends Report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the cobertura-coverage.xml will be written
+ */
+function CoberturaReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ this.projectRoot = process.cwd();
+ this.dir = opts.dir || this.projectRoot;
+ this.file = opts.file || this.getDefaultConfig().file;
+ this.opts = opts;
+}
+
+CoberturaReport.TYPE = 'cobertura';
+util.inherits(CoberturaReport, Report);
+
+function asJavaPackage(node) {
+ return node.displayShortName().
+ replace(/\//g, '.').
+ replace(/\\/g, '.').
+ replace(/\.$/, '');
+}
+
+function asClassName(node) {
+ /*jslint regexp: true */
+ return node.fullPath().replace(/.*[\\\/]/, '');
+}
+
+function quote(thing) {
+ return '"' + thing + '"';
+}
+
+function attr(n, v) {
+ return ' ' + n + '=' + quote(v) + ' ';
+}
+
+function branchCoverageByLine(fileCoverage) {
+ var branchMap = fileCoverage.branchMap,
+ branches = fileCoverage.b,
+ ret = {};
+ Object.keys(branchMap).forEach(function (k) {
+ var line = branchMap[k].line,
+ branchData = branches[k];
+ ret[line] = ret[line] || [];
+ ret[line].push.apply(ret[line], branchData);
+ });
+ Object.keys(ret).forEach(function (k) {
+ var dataArray = ret[k],
+ covered = dataArray.filter(function (item) { return item > 0; }),
+ coverage = covered.length / dataArray.length * 100;
+ ret[k] = { covered: covered.length, total: dataArray.length, coverage: coverage };
+ });
+ return ret;
+}
+
+function addClassStats(node, fileCoverage, writer, projectRoot) {
+ fileCoverage = utils.incrementIgnoredTotals(fileCoverage);
+
+ var metrics = node.metrics,
+ branchByLine = branchCoverageByLine(fileCoverage),
+ fnMap,
+ lines;
+
+ writer.println('\t\t<class' +
+ attr('name', asClassName(node)) +
+ attr('filename', path.relative(projectRoot, node.fullPath())) +
+ attr('line-rate', metrics.lines.pct / 100.0) +
+ attr('branch-rate', metrics.branches.pct / 100.0) +
+ '>');
+
+ writer.println('\t\t<methods>');
+ fnMap = fileCoverage.fnMap;
+ Object.keys(fnMap).forEach(function (k) {
+ var name = fnMap[k].name,
+ hits = fileCoverage.f[k];
+
+ writer.println(
+ '\t\t\t<method' +
+ attr('name', name) +
+ attr('hits', hits) +
+ attr('signature', '()V') + //fake out a no-args void return
+ '>'
+ );
+
+ //Add the function definition line and hits so that jenkins cobertura plugin records method hits
+ writer.println(
+ '\t\t\t\t<lines>' +
+ '<line' +
+ attr('number', fnMap[k].line) +
+ attr('hits', fileCoverage.f[k]) +
+ '/>' +
+ '</lines>'
+ );
+
+ writer.println('\t\t\t</method>');
+
+ });
+ writer.println('\t\t</methods>');
+
+ writer.println('\t\t<lines>');
+ lines = fileCoverage.l;
+ Object.keys(lines).forEach(function (k) {
+ var str = '\t\t\t<line' +
+ attr('number', k) +
+ attr('hits', lines[k]),
+ branchDetail = branchByLine[k];
+
+ if (!branchDetail) {
+ str += attr('branch', false);
+ } else {
+ str += attr('branch', true) +
+ attr('condition-coverage', branchDetail.coverage +
+ '% (' + branchDetail.covered + '/' + branchDetail.total + ')');
+ }
+ writer.println(str + '/>');
+ });
+ writer.println('\t\t</lines>');
+
+ writer.println('\t\t</class>');
+}
+
+function walk(node, collector, writer, level, projectRoot) {
+ var metrics;
+ if (level === 0) {
+ metrics = node.metrics;
+ writer.println('<?xml version="1.0" ?>');
+ writer.println('<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">');
+ writer.println('<coverage' +
+ attr('lines-valid', metrics.lines.total) +
+ attr('lines-covered', metrics.lines.covered) +
+ attr('line-rate', metrics.lines.pct / 100.0) +
+ attr('branches-valid', metrics.branches.total) +
+ attr('branches-covered', metrics.branches.covered) +
+ attr('branch-rate', metrics.branches.pct / 100.0) +
+ attr('timestamp', Date.now()) +
+ 'complexity="0" version="0.1">');
+ writer.println('<sources>');
+ writer.println('\t<source>' + projectRoot + '</source>');
+ writer.println('</sources>');
+ writer.println('<packages>');
+ }
+ if (node.packageMetrics) {
+ metrics = node.packageMetrics;
+ writer.println('\t<package' +
+ attr('name', asJavaPackage(node)) +
+ attr('line-rate', metrics.lines.pct / 100.0) +
+ attr('branch-rate', metrics.branches.pct / 100.0) +
+ '>');
+ writer.println('\t<classes>');
+ node.children.filter(function (child) { return child.kind !== 'dir'; }).
+ forEach(function (child) {
+ addClassStats(child, collector.fileCoverageFor(child.fullPath()), writer, projectRoot);
+ });
+ writer.println('\t</classes>');
+ writer.println('\t</package>');
+ }
+ node.children.filter(function (child) { return child.kind === 'dir'; }).
+ forEach(function (child) {
+ walk(child, collector, writer, level + 1, projectRoot);
+ });
+
+ if (level === 0) {
+ writer.println('</packages>');
+ writer.println('</coverage>');
+ }
+}
+
+Report.mix(CoberturaReport, {
+ synopsis: function () {
+ return 'XML coverage report that can be consumed by the cobertura tool';
+ },
+ getDefaultConfig: function () {
+ return { file: 'cobertura-coverage.xml' };
+ },
+ writeReport: function (collector, sync) {
+ var summarizer = new TreeSummarizer(),
+ outputFile = path.join(this.dir, this.file),
+ writer = this.opts.writer || new FileWriter(sync),
+ projectRoot = this.projectRoot,
+ that = this,
+ tree,
+ root;
+
+ collector.files().forEach(function (key) {
+ summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
+ });
+ tree = summarizer.getTreeSummary();
+ root = tree.root;
+ writer.on('done', function () { that.emit('done'); });
+ writer.writeFile(outputFile, function (contentWriter) {
+ walk(root, collector, contentWriter, 0, projectRoot);
+ writer.done();
+ });
+ }
+});
+
+module.exports = CoberturaReport;
diff --git a/node_modules/istanbul/lib/report/common/defaults.js b/node_modules/istanbul/lib/report/common/defaults.js
new file mode 100644
index 000000000..a9851c32f
--- /dev/null
+++ b/node_modules/istanbul/lib/report/common/defaults.js
@@ -0,0 +1,51 @@
+/*
+ Copyright (c) 2013, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var Report = require('../index');
+var supportsColor = require('supports-color');
+
+module.exports = {
+ watermarks: function () {
+ return {
+ statements: [ 50, 80 ],
+ lines: [ 50, 80 ],
+ functions: [ 50, 80],
+ branches: [ 50, 80 ]
+ };
+ },
+
+ classFor: function (type, metrics, watermarks) {
+ var mark = watermarks[type],
+ value = metrics[type].pct;
+ return value >= mark[1] ? 'high' : value >= mark[0] ? 'medium' : 'low';
+ },
+
+ colorize: function (str, clazz) {
+ /* istanbul ignore if: untestable in batch mode */
+ var colors = {
+ low: '31;1',
+ medium: '33;1',
+ high: '32;1'
+ };
+
+ if (supportsColor && colors[clazz]) {
+ return '\u001b[' + colors[clazz] + 'm' + str + '\u001b[0m';
+ }
+ return str;
+ },
+
+ defaultReportConfig: function () {
+ var cfg = {};
+ Report.getReportList().forEach(function (type) {
+ var rpt = Report.create(type),
+ c = rpt.getDefaultConfig();
+ if (c) {
+ cfg[type] = c;
+ }
+ });
+ return cfg;
+ }
+};
+
diff --git a/node_modules/istanbul/lib/report/html.js b/node_modules/istanbul/lib/report/html.js
new file mode 100644
index 000000000..1dab26d56
--- /dev/null
+++ b/node_modules/istanbul/lib/report/html.js
@@ -0,0 +1,572 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+/*jshint maxlen: 300 */
+var handlebars = require('handlebars').create(),
+ defaults = require('./common/defaults'),
+ path = require('path'),
+ fs = require('fs'),
+ util = require('util'),
+ FileWriter = require('../util/file-writer'),
+ Report = require('./index'),
+ Store = require('../store'),
+ InsertionText = require('../util/insertion-text'),
+ TreeSummarizer = require('../util/tree-summarizer'),
+ utils = require('../object-utils'),
+ templateFor = function (name) { return handlebars.compile(fs.readFileSync(path.resolve(__dirname, 'templates', name + '.txt'), 'utf8')); },
+ headerTemplate = templateFor('head'),
+ footerTemplate = templateFor('foot'),
+ detailTemplate = handlebars.compile([
+ '<tr>',
+ '<td class="line-count quiet">{{#show_lines}}{{maxLines}}{{/show_lines}}</td>',
+ '<td class="line-coverage quiet">{{#show_line_execution_counts fileCoverage}}{{maxLines}}{{/show_line_execution_counts}}</td>',
+ '<td class="text"><pre class="prettyprint lang-js">{{#show_code structured}}{{/show_code}}</pre></td>',
+ '</tr>\n'
+ ].join('')),
+ summaryTableHeader = [
+ '<div class="pad1">',
+ '<table class="coverage-summary">',
+ '<thead>',
+ '<tr>',
+ ' <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>',
+ ' <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>',
+ ' <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>',
+ ' <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>',
+ ' <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>',
+ ' <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>',
+ ' <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>',
+ ' <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>',
+ ' <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>',
+ ' <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>',
+ '</tr>',
+ '</thead>',
+ '<tbody>'
+ ].join('\n'),
+ summaryLineTemplate = handlebars.compile([
+ '<tr>',
+ '<td class="file {{reportClasses.statements}}" data-value="{{file}}"><a href="{{output}}">{{file}}</a></td>',
+ '<td data-value="{{metrics.statements.pct}}" class="pic {{reportClasses.statements}}"><div class="chart">{{#show_picture}}{{metrics.statements.pct}}{{/show_picture}}</div></td>',
+ '<td data-value="{{metrics.statements.pct}}" class="pct {{reportClasses.statements}}">{{metrics.statements.pct}}%</td>',
+ '<td data-value="{{metrics.statements.total}}" class="abs {{reportClasses.statements}}">{{metrics.statements.covered}}/{{metrics.statements.total}}</td>',
+ '<td data-value="{{metrics.branches.pct}}" class="pct {{reportClasses.branches}}">{{metrics.branches.pct}}%</td>',
+ '<td data-value="{{metrics.branches.total}}" class="abs {{reportClasses.branches}}">{{metrics.branches.covered}}/{{metrics.branches.total}}</td>',
+ '<td data-value="{{metrics.functions.pct}}" class="pct {{reportClasses.functions}}">{{metrics.functions.pct}}%</td>',
+ '<td data-value="{{metrics.functions.total}}" class="abs {{reportClasses.functions}}">{{metrics.functions.covered}}/{{metrics.functions.total}}</td>',
+ '<td data-value="{{metrics.lines.pct}}" class="pct {{reportClasses.lines}}">{{metrics.lines.pct}}%</td>',
+ '<td data-value="{{metrics.lines.total}}" class="abs {{reportClasses.lines}}">{{metrics.lines.covered}}/{{metrics.lines.total}}</td>',
+ '</tr>\n'
+ ].join('\n\t')),
+ summaryTableFooter = [
+ '</tbody>',
+ '</table>',
+ '</div>'
+ ].join('\n'),
+ lt = '\u0001',
+ gt = '\u0002',
+ RE_LT = /</g,
+ RE_GT = />/g,
+ RE_AMP = /&/g,
+ RE_lt = /\u0001/g,
+ RE_gt = /\u0002/g;
+
+handlebars.registerHelper('show_picture', function (opts) {
+ var num = Number(opts.fn(this)),
+ rest,
+ cls = '';
+ if (isFinite(num)) {
+ if (num === 100) {
+ cls = ' cover-full';
+ }
+ num = Math.floor(num);
+ rest = 100 - num;
+ return '<div class="cover-fill' + cls + '" style="width: ' + num + '%;"></div>' +
+ '<div class="cover-empty" style="width:' + rest + '%;"></div>';
+ } else {
+ return '';
+ }
+});
+
+handlebars.registerHelper('if_has_ignores', function (metrics, opts) {
+ return (metrics.statements.skipped +
+ metrics.functions.skipped +
+ metrics.branches.skipped) === 0 ? '' : opts.fn(this);
+});
+
+handlebars.registerHelper('show_ignores', function (metrics) {
+ var statements = metrics.statements.skipped,
+ functions = metrics.functions.skipped,
+ branches = metrics.branches.skipped,
+ result;
+
+ if (statements === 0 && functions === 0 && branches === 0) {
+ return '<span class="ignore-none">none</span>';
+ }
+
+ result = [];
+ if (statements >0) { result.push(statements === 1 ? '1 statement': statements + ' statements'); }
+ if (functions >0) { result.push(functions === 1 ? '1 function' : functions + ' functions'); }
+ if (branches >0) { result.push(branches === 1 ? '1 branch' : branches + ' branches'); }
+
+ return result.join(', ');
+});
+
+handlebars.registerHelper('show_lines', function (opts) {
+ var maxLines = Number(opts.fn(this)),
+ i,
+ array = [];
+
+ for (i = 0; i < maxLines; i += 1) {
+ array[i] = i + 1;
+ }
+ return array.join('\n');
+});
+
+handlebars.registerHelper('show_line_execution_counts', function (context, opts) {
+ var lines = context.l,
+ maxLines = Number(opts.fn(this)),
+ i,
+ lineNumber,
+ array = [],
+ covered,
+ value = '';
+
+ for (i = 0; i < maxLines; i += 1) {
+ lineNumber = i + 1;
+ value = '&nbsp;';
+ covered = 'neutral';
+ if (lines.hasOwnProperty(lineNumber)) {
+ if (lines[lineNumber] > 0) {
+ covered = 'yes';
+ value = lines[lineNumber] + '×';
+ } else {
+ covered = 'no';
+ }
+ }
+ array.push('<span class="cline-any cline-' + covered + '">' + value + '</span>');
+ }
+ return array.join('\n');
+});
+
+function customEscape(text) {
+ text = text.toString();
+ return text.replace(RE_AMP, '&amp;')
+ .replace(RE_LT, '&lt;')
+ .replace(RE_GT, '&gt;')
+ .replace(RE_lt, '<')
+ .replace(RE_gt, '>');
+}
+
+handlebars.registerHelper('show_code', function (context /*, opts */) {
+ var array = [];
+
+ context.forEach(function (item) {
+ array.push(customEscape(item.text) || '&nbsp;');
+ });
+ return array.join('\n');
+});
+
+function title(str) {
+ return ' title="' + str + '" ';
+}
+
+function annotateLines(fileCoverage, structuredText) {
+ var lineStats = fileCoverage.l;
+ if (!lineStats) { return; }
+ Object.keys(lineStats).forEach(function (lineNumber) {
+ var count = lineStats[lineNumber];
+ if (structuredText[lineNumber]) {
+ structuredText[lineNumber].covered = count > 0 ? 'yes' : 'no';
+ }
+ });
+ structuredText.forEach(function (item) {
+ if (item.covered === null) {
+ item.covered = 'neutral';
+ }
+ });
+}
+
+function annotateStatements(fileCoverage, structuredText) {
+ var statementStats = fileCoverage.s,
+ statementMeta = fileCoverage.statementMap;
+ Object.keys(statementStats).forEach(function (stName) {
+ var count = statementStats[stName],
+ meta = statementMeta[stName],
+ type = count > 0 ? 'yes' : 'no',
+ startCol = meta.start.column,
+ endCol = meta.end.column + 1,
+ startLine = meta.start.line,
+ endLine = meta.end.line,
+ openSpan = lt + 'span class="' + (meta.skip ? 'cstat-skip' : 'cstat-no') + '"' + title('statement not covered') + gt,
+ closeSpan = lt + '/span' + gt,
+ text;
+
+ if (type === 'no') {
+ if (endLine !== startLine) {
+ endLine = startLine;
+ endCol = structuredText[startLine].text.originalLength();
+ }
+ text = structuredText[startLine].text;
+ text.wrap(startCol,
+ openSpan,
+ startLine === endLine ? endCol : text.originalLength(),
+ closeSpan);
+ }
+ });
+}
+
+function annotateFunctions(fileCoverage, structuredText) {
+
+ var fnStats = fileCoverage.f,
+ fnMeta = fileCoverage.fnMap;
+ if (!fnStats) { return; }
+ Object.keys(fnStats).forEach(function (fName) {
+ var count = fnStats[fName],
+ meta = fnMeta[fName],
+ type = count > 0 ? 'yes' : 'no',
+ startCol = meta.loc.start.column,
+ endCol = meta.loc.end.column + 1,
+ startLine = meta.loc.start.line,
+ endLine = meta.loc.end.line,
+ openSpan = lt + 'span class="' + (meta.skip ? 'fstat-skip' : 'fstat-no') + '"' + title('function not covered') + gt,
+ closeSpan = lt + '/span' + gt,
+ text;
+
+ if (type === 'no') {
+ if (endLine !== startLine) {
+ endLine = startLine;
+ endCol = structuredText[startLine].text.originalLength();
+ }
+ text = structuredText[startLine].text;
+ text.wrap(startCol,
+ openSpan,
+ startLine === endLine ? endCol : text.originalLength(),
+ closeSpan);
+ }
+ });
+}
+
+function annotateBranches(fileCoverage, structuredText) {
+ var branchStats = fileCoverage.b,
+ branchMeta = fileCoverage.branchMap;
+ if (!branchStats) { return; }
+
+ Object.keys(branchStats).forEach(function (branchName) {
+ var branchArray = branchStats[branchName],
+ sumCount = branchArray.reduce(function (p, n) { return p + n; }, 0),
+ metaArray = branchMeta[branchName].locations,
+ i,
+ count,
+ meta,
+ type,
+ startCol,
+ endCol,
+ startLine,
+ endLine,
+ openSpan,
+ closeSpan,
+ text;
+
+ if (sumCount > 0) { //only highlight if partial branches are missing
+ for (i = 0; i < branchArray.length; i += 1) {
+ count = branchArray[i];
+ meta = metaArray[i];
+ type = count > 0 ? 'yes' : 'no';
+ startCol = meta.start.column;
+ endCol = meta.end.column + 1;
+ startLine = meta.start.line;
+ endLine = meta.end.line;
+ openSpan = lt + 'span class="branch-' + i + ' ' + (meta.skip ? 'cbranch-skip' : 'cbranch-no') + '"' + title('branch not covered') + gt;
+ closeSpan = lt + '/span' + gt;
+
+ if (count === 0) { //skip branches taken
+ if (endLine !== startLine) {
+ endLine = startLine;
+ endCol = structuredText[startLine].text.originalLength();
+ }
+ text = structuredText[startLine].text;
+ if (branchMeta[branchName].type === 'if') { // and 'if' is a special case since the else branch might not be visible, being non-existent
+ text.insertAt(startCol, lt + 'span class="' + (meta.skip ? 'skip-if-branch' : 'missing-if-branch') + '"' +
+ title((i === 0 ? 'if' : 'else') + ' path not taken') + gt +
+ (i === 0 ? 'I' : 'E') + lt + '/span' + gt, true, false);
+ } else {
+ text.wrap(startCol,
+ openSpan,
+ startLine === endLine ? endCol : text.originalLength(),
+ closeSpan);
+ }
+ }
+ }
+ }
+ });
+}
+
+function getReportClass(stats, watermark) {
+ var coveragePct = stats.pct,
+ identity = 1;
+ if (coveragePct * identity === coveragePct) {
+ return coveragePct >= watermark[1] ? 'high' : coveragePct >= watermark[0] ? 'medium' : 'low';
+ } else {
+ return '';
+ }
+}
+
+function cleanPath(name) {
+ var SEP = path.sep || '/';
+ return (SEP !== '/') ? name.split(SEP).join('/') : name;
+}
+
+function isEmptySourceStore(sourceStore) {
+ if (!sourceStore) {
+ return true;
+ }
+
+ var cache = sourceStore.sourceCache;
+ return cache && !Object.keys(cache).length;
+}
+
+/**
+ * a `Report` implementation that produces HTML coverage reports.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('html');
+ *
+ *
+ * @class HtmlReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to generate reports. Defaults to `./html-report`
+ */
+function HtmlReport(opts) {
+ Report.call(this);
+ this.opts = opts || {};
+ this.opts.dir = this.opts.dir || path.resolve(process.cwd(), 'html-report');
+ this.opts.sourceStore = isEmptySourceStore(this.opts.sourceStore) ?
+ Store.create('fslookup') : this.opts.sourceStore;
+ this.opts.linkMapper = this.opts.linkMapper || this.standardLinkMapper();
+ this.opts.writer = this.opts.writer || null;
+ this.opts.templateData = { datetime: Date() };
+ this.opts.watermarks = this.opts.watermarks || defaults.watermarks();
+}
+
+HtmlReport.TYPE = 'html';
+util.inherits(HtmlReport, Report);
+
+Report.mix(HtmlReport, {
+
+ synopsis: function () {
+ return 'Navigable HTML coverage report for every file and directory';
+ },
+
+ getPathHtml: function (node, linkMapper) {
+ var parent = node.parent,
+ nodePath = [],
+ linkPath = [],
+ i;
+
+ while (parent) {
+ nodePath.push(parent);
+ parent = parent.parent;
+ }
+
+ for (i = 0; i < nodePath.length; i += 1) {
+ linkPath.push('<a href="' + linkMapper.ancestor(node, i + 1) + '">' +
+ (cleanPath(nodePath[i].relativeName) || 'all files') + '</a>');
+ }
+ linkPath.reverse();
+ return linkPath.length > 0 ? linkPath.join(' / ') + ' ' +
+ cleanPath(node.displayShortName()) : '/';
+ },
+
+ fillTemplate: function (node, templateData) {
+ var opts = this.opts,
+ linkMapper = opts.linkMapper;
+
+ templateData.entity = node.name || 'All files';
+ templateData.metrics = node.metrics;
+ templateData.reportClass = getReportClass(node.metrics.statements, opts.watermarks.statements);
+ templateData.pathHtml = this.getPathHtml(node, linkMapper);
+ templateData.base = {
+ css: linkMapper.asset(node, 'base.css')
+ };
+ templateData.sorter = {
+ js: linkMapper.asset(node, 'sorter.js'),
+ image: linkMapper.asset(node, 'sort-arrow-sprite.png')
+ };
+ templateData.prettify = {
+ js: linkMapper.asset(node, 'prettify.js'),
+ css: linkMapper.asset(node, 'prettify.css')
+ };
+ },
+ writeDetailPage: function (writer, node, fileCoverage) {
+ var opts = this.opts,
+ sourceStore = opts.sourceStore,
+ templateData = opts.templateData,
+ sourceText = fileCoverage.code && Array.isArray(fileCoverage.code) ?
+ fileCoverage.code.join('\n') + '\n' : sourceStore.get(fileCoverage.path),
+ code = sourceText.split(/(?:\r?\n)|\r/),
+ count = 0,
+ structured = code.map(function (str) { count += 1; return { line: count, covered: null, text: new InsertionText(str, true) }; }),
+ context;
+
+ structured.unshift({ line: 0, covered: null, text: new InsertionText("") });
+
+ this.fillTemplate(node, templateData);
+ writer.write(headerTemplate(templateData));
+ writer.write('<pre><table class="coverage">\n');
+
+ annotateLines(fileCoverage, structured);
+ //note: order is important, since statements typically result in spanning the whole line and doing branches late
+ //causes mismatched tags
+ annotateBranches(fileCoverage, structured);
+ annotateFunctions(fileCoverage, structured);
+ annotateStatements(fileCoverage, structured);
+
+ structured.shift();
+ context = {
+ structured: structured,
+ maxLines: structured.length,
+ fileCoverage: fileCoverage
+ };
+ writer.write(detailTemplate(context));
+ writer.write('</table></pre>\n');
+ writer.write(footerTemplate(templateData));
+ },
+
+ writeIndexPage: function (writer, node) {
+ var linkMapper = this.opts.linkMapper,
+ templateData = this.opts.templateData,
+ children = Array.prototype.slice.apply(node.children),
+ watermarks = this.opts.watermarks;
+
+ children.sort(function (a, b) {
+ return a.name < b.name ? -1 : 1;
+ });
+
+ this.fillTemplate(node, templateData);
+ writer.write(headerTemplate(templateData));
+ writer.write(summaryTableHeader);
+ children.forEach(function (child) {
+ var metrics = child.metrics,
+ reportClasses = {
+ statements: getReportClass(metrics.statements, watermarks.statements),
+ lines: getReportClass(metrics.lines, watermarks.lines),
+ functions: getReportClass(metrics.functions, watermarks.functions),
+ branches: getReportClass(metrics.branches, watermarks.branches)
+ },
+ data = {
+ metrics: metrics,
+ reportClasses: reportClasses,
+ file: cleanPath(child.displayShortName()),
+ output: linkMapper.fromParent(child)
+ };
+ writer.write(summaryLineTemplate(data) + '\n');
+ });
+ writer.write(summaryTableFooter);
+ writer.write(footerTemplate(templateData));
+ },
+
+ writeFiles: function (writer, node, dir, collector) {
+ var that = this,
+ indexFile = path.resolve(dir, 'index.html'),
+ childFile;
+ if (this.opts.verbose) { console.error('Writing ' + indexFile); }
+ writer.writeFile(indexFile, function (contentWriter) {
+ that.writeIndexPage(contentWriter, node);
+ });
+ node.children.forEach(function (child) {
+ if (child.kind === 'dir') {
+ that.writeFiles(writer, child, path.resolve(dir, child.relativeName), collector);
+ } else {
+ childFile = path.resolve(dir, child.relativeName + '.html');
+ if (that.opts.verbose) { console.error('Writing ' + childFile); }
+ writer.writeFile(childFile, function (contentWriter) {
+ that.writeDetailPage(contentWriter, child, collector.fileCoverageFor(child.fullPath()));
+ });
+ }
+ });
+ },
+
+ standardLinkMapper: function () {
+ return {
+ fromParent: function (node) {
+ var relativeName = cleanPath(node.relativeName);
+
+ return node.kind === 'dir' ? relativeName + 'index.html' : relativeName + '.html';
+ },
+ ancestorHref: function (node, num) {
+ var href = '',
+ notDot = function(part) {
+ return part !== '.';
+ },
+ separated,
+ levels,
+ i,
+ j;
+
+ for (i = 0; i < num; i += 1) {
+ separated = cleanPath(node.relativeName).split('/').filter(notDot);
+ levels = separated.length - 1;
+ for (j = 0; j < levels; j += 1) {
+ href += '../';
+ }
+ node = node.parent;
+ }
+ return href;
+ },
+ ancestor: function (node, num) {
+ return this.ancestorHref(node, num) + 'index.html';
+ },
+ asset: function (node, name) {
+ var i = 0,
+ parent = node.parent;
+ while (parent) { i += 1; parent = parent.parent; }
+ return this.ancestorHref(node, i) + name;
+ }
+ };
+ },
+
+ writeReport: function (collector, sync) {
+ var opts = this.opts,
+ dir = opts.dir,
+ summarizer = new TreeSummarizer(),
+ writer = opts.writer || new FileWriter(sync),
+ that = this,
+ tree,
+ copyAssets = function (subdir) {
+ var srcDir = path.resolve(__dirname, '..', 'assets', subdir);
+ fs.readdirSync(srcDir).forEach(function (f) {
+ var resolvedSource = path.resolve(srcDir, f),
+ resolvedDestination = path.resolve(dir, f),
+ stat = fs.statSync(resolvedSource);
+
+ if (stat.isFile()) {
+ if (opts.verbose) {
+ console.log('Write asset: ' + resolvedDestination);
+ }
+ writer.copyFile(resolvedSource, resolvedDestination);
+ }
+ });
+ };
+
+ collector.files().forEach(function (key) {
+ summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(collector.fileCoverageFor(key)));
+ });
+ tree = summarizer.getTreeSummary();
+ [ '.', 'vendor'].forEach(function (subdir) {
+ copyAssets(subdir);
+ });
+ writer.on('done', function () { that.emit('done'); });
+ //console.log(JSON.stringify(tree.root, undefined, 4));
+ this.writeFiles(writer, tree.root, dir, collector);
+ writer.done();
+ }
+});
+
+module.exports = HtmlReport;
+
diff --git a/node_modules/istanbul/lib/report/index.js b/node_modules/istanbul/lib/report/index.js
new file mode 100644
index 000000000..13e7effbe
--- /dev/null
+++ b/node_modules/istanbul/lib/report/index.js
@@ -0,0 +1,104 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var util = require('util'),
+ EventEmitter = require('events').EventEmitter,
+ Factory = require('../util/factory'),
+ factory = new Factory('report', __dirname, false);
+/**
+ * An abstraction for producing coverage reports.
+ * This class is both the base class as well as a factory for `Report` implementations.
+ * All reports are event emitters and are expected to emit a `done` event when
+ * the report writing is complete.
+ *
+ * See also the `Reporter` class for easily producing multiple coverage reports
+ * with a single call.
+ *
+ * Usage
+ * -----
+ *
+ * var Report = require('istanbul').Report,
+ * report = Report.create('html'),
+ * collector = new require('istanbul').Collector;
+ *
+ * collector.add(coverageObject);
+ * report.on('done', function () { console.log('done'); });
+ * report.writeReport(collector);
+ *
+ * @class Report
+ * @module report
+ * @main report
+ * @constructor
+ * @protected
+ * @param {Object} options Optional. The options supported by a specific store implementation.
+ */
+function Report(/* options */) {
+ EventEmitter.call(this);
+}
+
+util.inherits(Report, EventEmitter);
+
+//add register, create, mix, loadAll, getReportList as class methods
+factory.bindClassMethods(Report);
+
+/**
+ * registers a new report implementation.
+ * @method register
+ * @static
+ * @param {Function} constructor the constructor function for the report. This function must have a
+ * `TYPE` property of type String, that will be used in `Report.create()`
+ */
+/**
+ * returns a report implementation of the specified type.
+ * @method create
+ * @static
+ * @param {String} type the type of report to create
+ * @param {Object} opts Optional. Options specific to the report implementation
+ * @return {Report} a new store of the specified type
+ */
+/**
+ * returns the list of available reports as an array of strings
+ * @method getReportList
+ * @static
+ * @return an array of supported report formats
+ */
+
+var proto = {
+ /**
+ * returns a one-line summary of the report
+ * @method synopsis
+ * @return {String} a description of what the report is about
+ */
+ synopsis: function () {
+ throw new Error('synopsis must be overridden');
+ },
+ /**
+ * returns a config object that has override-able keys settable via config
+ * @method getDefaultConfig
+ * @return {Object|null} an object representing keys that can be overridden via
+ * the istanbul configuration where the values are the defaults used when
+ * not specified. A null return implies no config attributes
+ */
+ getDefaultConfig: function () {
+ return null;
+ },
+ /**
+ * writes the report for a set of coverage objects added to a collector.
+ * @method writeReport
+ * @param {Collector} collector the collector for getting the set of files and coverage
+ * @param {Boolean} sync true if reports must be written synchronously, false if they can be written using asynchronous means (e.g. stream.write)
+ */
+ writeReport: function (/* collector, sync */) {
+ throw new Error('writeReport: must be overridden');
+ }
+};
+
+Object.keys(proto).forEach(function (k) {
+ Report.prototype[k] = proto[k];
+});
+
+module.exports = Report;
+
+
diff --git a/node_modules/istanbul/lib/report/json-summary.js b/node_modules/istanbul/lib/report/json-summary.js
new file mode 100644
index 000000000..6ab7caae9
--- /dev/null
+++ b/node_modules/istanbul/lib/report/json-summary.js
@@ -0,0 +1,75 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ objectUtils = require('../object-utils'),
+ Writer = require('../util/file-writer'),
+ util = require('util'),
+ Report = require('./index');
+/**
+ * a `Report` implementation that produces a coverage JSON object with summary info only.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('json-summary');
+ *
+ *
+ * @class JsonSummaryReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to write the `coverage-summary.json` file. Defaults to `process.cwd()`
+ */
+function JsonSummaryReport(opts) {
+ this.opts = opts || {};
+ this.opts.dir = this.opts.dir || process.cwd();
+ this.opts.file = this.opts.file || this.getDefaultConfig().file;
+ this.opts.writer = this.opts.writer || null;
+}
+JsonSummaryReport.TYPE = 'json-summary';
+util.inherits(JsonSummaryReport, Report);
+
+Report.mix(JsonSummaryReport, {
+ synopsis: function () {
+ return 'prints a summary coverage object as JSON to a file';
+ },
+ getDefaultConfig: function () {
+ return {
+ file: 'coverage-summary.json'
+ };
+ },
+ writeReport: function (collector, sync) {
+ var outputFile = path.resolve(this.opts.dir, this.opts.file),
+ writer = this.opts.writer || new Writer(sync),
+ that = this;
+
+ var summaries = [],
+ finalSummary;
+ collector.files().forEach(function (file) {
+ summaries.push(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(file)));
+ });
+ finalSummary = objectUtils.mergeSummaryObjects.apply(null, summaries);
+
+ writer.on('done', function () { that.emit('done'); });
+ writer.writeFile(outputFile, function (contentWriter) {
+ contentWriter.println("{");
+ contentWriter.write('"total":');
+ contentWriter.write(JSON.stringify(finalSummary));
+
+ collector.files().forEach(function (key) {
+ contentWriter.println(",");
+ contentWriter.write(JSON.stringify(key));
+ contentWriter.write(":");
+ contentWriter.write(JSON.stringify(objectUtils.summarizeFileCoverage(collector.fileCoverageFor(key))));
+ });
+ contentWriter.println("}");
+ });
+ writer.done();
+ }
+});
+
+module.exports = JsonSummaryReport;
diff --git a/node_modules/istanbul/lib/report/json.js b/node_modules/istanbul/lib/report/json.js
new file mode 100644
index 000000000..2def51ac0
--- /dev/null
+++ b/node_modules/istanbul/lib/report/json.js
@@ -0,0 +1,69 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ Writer = require('../util/file-writer'),
+ util = require('util'),
+ Report = require('./index');
+/**
+ * a `Report` implementation that produces a coverage JSON object.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('json');
+ *
+ *
+ * @class JsonReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to write the `coverage-final.json` file. Defaults to `process.cwd()`
+ */
+function JsonReport(opts) {
+ this.opts = opts || {};
+ this.opts.dir = this.opts.dir || process.cwd();
+ this.opts.file = this.opts.file || this.getDefaultConfig().file;
+ this.opts.writer = this.opts.writer || null;
+}
+JsonReport.TYPE = 'json';
+util.inherits(JsonReport, Report);
+
+Report.mix(JsonReport, {
+ synopsis: function () {
+ return 'prints the coverage object as JSON to a file';
+ },
+ getDefaultConfig: function () {
+ return {
+ file: 'coverage-final.json'
+ };
+ },
+ writeReport: function (collector, sync) {
+ var outputFile = path.resolve(this.opts.dir, this.opts.file),
+ writer = this.opts.writer || new Writer(sync),
+ that = this;
+
+ writer.on('done', function () { that.emit('done'); });
+ writer.writeFile(outputFile, function (contentWriter) {
+ var first = true;
+ contentWriter.println("{");
+ collector.files().forEach(function (key) {
+ if (first) {
+ first = false;
+ } else {
+ contentWriter.println(",");
+ }
+ contentWriter.write(JSON.stringify(key));
+ contentWriter.write(":");
+ contentWriter.write(JSON.stringify(collector.fileCoverageFor(key)));
+ });
+ contentWriter.println("}");
+ });
+ writer.done();
+ }
+});
+
+module.exports = JsonReport;
diff --git a/node_modules/istanbul/lib/report/lcov.js b/node_modules/istanbul/lib/report/lcov.js
new file mode 100644
index 000000000..87e01eaab
--- /dev/null
+++ b/node_modules/istanbul/lib/report/lcov.js
@@ -0,0 +1,65 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ util = require('util'),
+ mkdirp = require('mkdirp'),
+ Report = require('./index'),
+ LcovOnlyReport = require('./lcovonly'),
+ HtmlReport = require('./html');
+
+/**
+ * a `Report` implementation that produces an LCOV coverage file and an associated HTML report from coverage objects.
+ * The name and behavior of this report is designed to ease migration for projects that currently use `yuitest_coverage`
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('lcov');
+ *
+ *
+ * @class LcovReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the `lcov.info` file.
+ * HTML files are written in a subdirectory called `lcov-report`. Defaults to `process.cwd()`
+ */
+function LcovReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ var baseDir = path.resolve(opts.dir || process.cwd()),
+ htmlDir = path.resolve(baseDir, 'lcov-report');
+
+ mkdirp.sync(baseDir);
+ this.lcov = new LcovOnlyReport({ dir: baseDir, watermarks: opts.watermarks });
+ this.html = new HtmlReport({ dir: htmlDir, watermarks: opts.watermarks, sourceStore: opts.sourceStore});
+}
+
+LcovReport.TYPE = 'lcov';
+util.inherits(LcovReport, Report);
+
+Report.mix(LcovReport, {
+ synopsis: function () {
+ return 'combined lcovonly and html report that generates an lcov.info file as well as HTML';
+ },
+ writeReport: function (collector, sync) {
+ var handler = this.handleDone.bind(this);
+ this.inProgress = 2;
+ this.lcov.on('done', handler);
+ this.html.on('done', handler);
+ this.lcov.writeReport(collector, sync);
+ this.html.writeReport(collector, sync);
+ },
+ handleDone: function () {
+ this.inProgress -= 1;
+ if (this.inProgress === 0) {
+ this.emit('done');
+ }
+ }
+});
+
+module.exports = LcovReport;
diff --git a/node_modules/istanbul/lib/report/lcovonly.js b/node_modules/istanbul/lib/report/lcovonly.js
new file mode 100644
index 000000000..2c1be46d8
--- /dev/null
+++ b/node_modules/istanbul/lib/report/lcovonly.js
@@ -0,0 +1,103 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ Writer = require('../util/file-writer'),
+ util = require('util'),
+ Report = require('./index'),
+ utils = require('../object-utils');
+/**
+ * a `Report` implementation that produces an LCOV coverage file from coverage objects.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('lcovonly');
+ *
+ *
+ * @class LcovOnlyReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the `lcov.info` file. Defaults to `process.cwd()`
+ */
+function LcovOnlyReport(opts) {
+ this.opts = opts || {};
+ this.opts.dir = this.opts.dir || process.cwd();
+ this.opts.file = this.opts.file || this.getDefaultConfig().file;
+ this.opts.writer = this.opts.writer || null;
+}
+LcovOnlyReport.TYPE = 'lcovonly';
+util.inherits(LcovOnlyReport, Report);
+
+Report.mix(LcovOnlyReport, {
+ synopsis: function () {
+ return 'lcov coverage report that can be consumed by the lcov tool';
+ },
+ getDefaultConfig: function () {
+ return { file: 'lcov.info' };
+ },
+ writeFileCoverage: function (writer, fc) {
+ var functions = fc.f,
+ functionMap = fc.fnMap,
+ lines = fc.l,
+ branches = fc.b,
+ branchMap = fc.branchMap,
+ summary = utils.summarizeFileCoverage(fc);
+
+ writer.println('TN:'); //no test name
+ writer.println('SF:' + fc.path);
+
+ Object.keys(functions).forEach(function (key) {
+ var meta = functionMap[key];
+ writer.println('FN:' + [ meta.line, meta.name ].join(','));
+ });
+ writer.println('FNF:' + summary.functions.total);
+ writer.println('FNH:' + summary.functions.covered);
+
+ Object.keys(functions).forEach(function (key) {
+ var stats = functions[key],
+ meta = functionMap[key];
+ writer.println('FNDA:' + [ stats, meta.name ].join(','));
+ });
+
+ Object.keys(lines).forEach(function (key) {
+ var stat = lines[key];
+ writer.println('DA:' + [ key, stat ].join(','));
+ });
+ writer.println('LF:' + summary.lines.total);
+ writer.println('LH:' + summary.lines.covered);
+
+ Object.keys(branches).forEach(function (key) {
+ var branchArray = branches[key],
+ meta = branchMap[key],
+ line = meta.line,
+ i = 0;
+ branchArray.forEach(function (b) {
+ writer.println('BRDA:' + [line, key, i, b].join(','));
+ i += 1;
+ });
+ });
+ writer.println('BRF:' + summary.branches.total);
+ writer.println('BRH:' + summary.branches.covered);
+ writer.println('end_of_record');
+ },
+
+ writeReport: function (collector, sync) {
+ var outputFile = path.resolve(this.opts.dir, this.opts.file),
+ writer = this.opts.writer || new Writer(sync),
+ that = this;
+ writer.on('done', function () { that.emit('done'); });
+ writer.writeFile(outputFile, function (contentWriter) {
+ collector.files().forEach(function (key) {
+ that.writeFileCoverage(contentWriter, collector.fileCoverageFor(key));
+ });
+ });
+ writer.done();
+ }
+});
+
+module.exports = LcovOnlyReport;
diff --git a/node_modules/istanbul/lib/report/none.js b/node_modules/istanbul/lib/report/none.js
new file mode 100644
index 000000000..0fd5cfca6
--- /dev/null
+++ b/node_modules/istanbul/lib/report/none.js
@@ -0,0 +1,41 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var util = require('util'),
+ Report = require('./index');
+
+/**
+ * a `Report` implementation that does nothing. Use to specify that no reporting
+ * is needed.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('none');
+ *
+ *
+ * @class NoneReport
+ * @extends Report
+ * @module report
+ * @constructor
+ */
+function NoneReport() {
+ Report.call(this);
+}
+
+NoneReport.TYPE = 'none';
+util.inherits(NoneReport, Report);
+
+Report.mix(NoneReport, {
+ synopsis: function () {
+ return 'Does nothing. Useful to override default behavior and suppress reporting entirely';
+ },
+ writeReport: function (/* collector, sync */) {
+ //noop
+ this.emit('done');
+ }
+});
+
+module.exports = NoneReport;
diff --git a/node_modules/istanbul/lib/report/teamcity.js b/node_modules/istanbul/lib/report/teamcity.js
new file mode 100644
index 000000000..f6b90fc95
--- /dev/null
+++ b/node_modules/istanbul/lib/report/teamcity.js
@@ -0,0 +1,92 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ util = require('util'),
+ mkdirp = require('mkdirp'),
+ fs = require('fs'),
+ utils = require('../object-utils'),
+ Report = require('./index');
+
+/**
+ * a `Report` implementation that produces system messages interpretable by TeamCity.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('teamcity');
+ *
+ * @class TeamcityReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file
+ * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console
+ */
+function TeamcityReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ this.dir = opts.dir || process.cwd();
+ this.file = opts.file;
+ this.blockName = opts.blockName || this.getDefaultConfig().blockName;
+}
+
+TeamcityReport.TYPE = 'teamcity';
+util.inherits(TeamcityReport, Report);
+
+function lineForKey(value, teamcityVar) {
+ return '##teamcity[buildStatisticValue key=\'' + teamcityVar + '\' value=\'' + value + '\']';
+}
+
+Report.mix(TeamcityReport, {
+ synopsis: function () {
+ return 'report with system messages that can be interpreted with TeamCity';
+ },
+ getDefaultConfig: function () {
+ return { file: null , blockName: 'Code Coverage Summary'};
+ },
+ writeReport: function (collector /*, sync */) {
+ var summaries = [],
+ finalSummary,
+ lines = [],
+ text;
+
+ collector.files().forEach(function (file) {
+ summaries.push(utils.summarizeFileCoverage(collector.fileCoverageFor(file)));
+ });
+
+ finalSummary = utils.mergeSummaryObjects.apply(null, summaries);
+
+ lines.push('');
+ lines.push('##teamcity[blockOpened name=\''+ this.blockName +'\']');
+
+ //Statements Covered
+ lines.push(lineForKey(finalSummary.statements.pct, 'CodeCoverageB'));
+
+ //Methods Covered
+ lines.push(lineForKey(finalSummary.functions.covered, 'CodeCoverageAbsMCovered'));
+ lines.push(lineForKey(finalSummary.functions.total, 'CodeCoverageAbsMTotal'));
+ lines.push(lineForKey(finalSummary.functions.pct, 'CodeCoverageM'));
+
+ //Lines Covered
+ lines.push(lineForKey(finalSummary.lines.covered, 'CodeCoverageAbsLCovered'));
+ lines.push(lineForKey(finalSummary.lines.total, 'CodeCoverageAbsLTotal'));
+ lines.push(lineForKey(finalSummary.lines.pct, 'CodeCoverageL'));
+
+ lines.push('##teamcity[blockClosed name=\''+ this.blockName +'\']');
+
+ text = lines.join('\n');
+ if (this.file) {
+ mkdirp.sync(this.dir);
+ fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8');
+ } else {
+ console.log(text);
+ }
+ this.emit('done');
+ }
+});
+
+module.exports = TeamcityReport;
diff --git a/node_modules/istanbul/lib/report/templates/foot.txt b/node_modules/istanbul/lib/report/templates/foot.txt
new file mode 100644
index 000000000..e853251dd
--- /dev/null
+++ b/node_modules/istanbul/lib/report/templates/foot.txt
@@ -0,0 +1,20 @@
+<div class='push'></div><!-- for sticky footer -->
+</div><!-- /wrapper -->
+<div class='footer quiet pad2 space-top1 center small'>
+ Code coverage
+ generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at {{datetime}}
+</div>
+</div>
+{{#if prettify}}
+<script src="{{prettify.js}}"></script>
+<script>
+window.onload = function () {
+ if (typeof prettyPrint === 'function') {
+ prettyPrint();
+ }
+};
+</script>
+{{/if}}
+<script src="{{sorter.js}}"></script>
+</body>
+</html>
diff --git a/node_modules/istanbul/lib/report/templates/head.txt b/node_modules/istanbul/lib/report/templates/head.txt
new file mode 100644
index 000000000..f98094e5f
--- /dev/null
+++ b/node_modules/istanbul/lib/report/templates/head.txt
@@ -0,0 +1,60 @@
+<!doctype html>
+<html lang="en">
+<head>
+ <title>Code coverage report for {{entity}}</title>
+ <meta charset="utf-8" />
+{{#if prettify}}
+ <link rel="stylesheet" href="{{prettify.css}}" />
+{{/if}}
+ <link rel="stylesheet" href="{{base.css}}" />
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <style type='text/css'>
+ .coverage-summary .sorter {
+ background-image: url({{sorter.image}});
+ }
+ </style>
+</head>
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1>
+ {{{pathHtml}}}
+ </h1>
+ <div class='clearfix'>
+ {{#with metrics.statements}}
+ <div class='fl pad1y space-right2'>
+ <span class="strong">{{pct}}% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>{{covered}}/{{total}}</span>
+ </div>
+ {{/with}}
+ {{#with metrics.branches}}
+ <div class='fl pad1y space-right2'>
+ <span class="strong">{{pct}}% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>{{covered}}/{{total}}</span>
+ </div>
+ {{/with}}
+ {{#with metrics.functions}}
+ <div class='fl pad1y space-right2'>
+ <span class="strong">{{pct}}% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>{{covered}}/{{total}}</span>
+ </div>
+ {{/with}}
+ {{#with metrics.lines}}
+ <div class='fl pad1y space-right2'>
+ <span class="strong">{{pct}}% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>{{covered}}/{{total}}</span>
+ </div>
+ {{/with}}
+ {{#if_has_ignores metrics}}
+ <div class='fl pad1y'>
+ <span class="strong">{{#show_ignores metrics}}{{/show_ignores}}</span>
+ <span class="quiet">Ignored</span> &nbsp;&nbsp;&nbsp;&nbsp;
+ </div>
+ {{/if_has_ignores}}
+ </div>
+ </div>
+ <div class='status-line {{reportClass}}'></div>
diff --git a/node_modules/istanbul/lib/report/text-lcov.js b/node_modules/istanbul/lib/report/text-lcov.js
new file mode 100644
index 000000000..15e1a48ca
--- /dev/null
+++ b/node_modules/istanbul/lib/report/text-lcov.js
@@ -0,0 +1,50 @@
+var LcovOnly = require('./lcovonly'),
+ util = require('util');
+
+/**
+ * a `Report` implementation that produces an LCOV coverage and prints it
+ * to standard out.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('text-lcov');
+ *
+ * @class TextLcov
+ * @module report
+ * @extends LcovOnly
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.log] the method used to log to console.
+ */
+function TextLcov(opts) {
+ var that = this;
+
+ LcovOnly.call(this);
+
+ this.opts = opts || {};
+ this.opts.log = this.opts.log || console.log;
+ this.opts.writer = {
+ println: function (ln) {
+ that.opts.log(ln);
+ }
+ };
+}
+
+TextLcov.TYPE = 'text-lcov';
+util.inherits(TextLcov, LcovOnly);
+
+LcovOnly.super_.mix(TextLcov, {
+ writeReport: function (collector) {
+ var that = this,
+ writer = this.opts.writer;
+
+ collector.files().forEach(function (key) {
+ that.writeFileCoverage(writer, collector.fileCoverageFor(key));
+ });
+
+ this.emit('done');
+ }
+});
+
+module.exports = TextLcov;
diff --git a/node_modules/istanbul/lib/report/text-summary.js b/node_modules/istanbul/lib/report/text-summary.js
new file mode 100644
index 000000000..9537cbe20
--- /dev/null
+++ b/node_modules/istanbul/lib/report/text-summary.js
@@ -0,0 +1,93 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ util = require('util'),
+ mkdirp = require('mkdirp'),
+ defaults = require('./common/defaults'),
+ fs = require('fs'),
+ utils = require('../object-utils'),
+ Report = require('./index');
+
+/**
+ * a `Report` implementation that produces text output for overall coverage in summary format.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('text-summary');
+ *
+ * @class TextSummaryReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file
+ * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console
+ */
+function TextSummaryReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ this.dir = opts.dir || process.cwd();
+ this.file = opts.file;
+ this.watermarks = opts.watermarks || defaults.watermarks();
+}
+
+TextSummaryReport.TYPE = 'text-summary';
+util.inherits(TextSummaryReport, Report);
+
+function lineForKey(summary, key, watermarks) {
+ var metrics = summary[key],
+ skipped,
+ result,
+ clazz = defaults.classFor(key, summary, watermarks);
+ key = key.substring(0, 1).toUpperCase() + key.substring(1);
+ if (key.length < 12) { key += ' '.substring(0, 12 - key.length); }
+ result = [ key , ':', metrics.pct + '%', '(', metrics.covered + '/' + metrics.total, ')'].join(' ');
+ skipped = metrics.skipped;
+ if (skipped > 0) {
+ result += ', ' + skipped + ' ignored';
+ }
+ return defaults.colorize(result, clazz);
+}
+
+Report.mix(TextSummaryReport, {
+ synopsis: function () {
+ return 'text report that prints a coverage summary across all files, typically to console';
+ },
+ getDefaultConfig: function () {
+ return { file: null };
+ },
+ writeReport: function (collector /*, sync */) {
+ var summaries = [],
+ finalSummary,
+ lines = [],
+ watermarks = this.watermarks,
+ text;
+ collector.files().forEach(function (file) {
+ summaries.push(utils.summarizeFileCoverage(collector.fileCoverageFor(file)));
+ });
+ finalSummary = utils.mergeSummaryObjects.apply(null, summaries);
+ lines.push('');
+ lines.push('=============================== Coverage summary ===============================');
+ lines.push.apply(lines, [
+ lineForKey(finalSummary, 'statements', watermarks),
+ lineForKey(finalSummary, 'branches', watermarks),
+ lineForKey(finalSummary, 'functions', watermarks),
+ lineForKey(finalSummary, 'lines', watermarks)
+ ]);
+ lines.push('================================================================================');
+ text = lines.join('\n');
+ if (this.file) {
+ mkdirp.sync(this.dir);
+ fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8');
+ } else {
+ console.log(text);
+ }
+ this.emit('done');
+ }
+});
+
+module.exports = TextSummaryReport;
diff --git a/node_modules/istanbul/lib/report/text.js b/node_modules/istanbul/lib/report/text.js
new file mode 100644
index 000000000..8ab2b7d13
--- /dev/null
+++ b/node_modules/istanbul/lib/report/text.js
@@ -0,0 +1,234 @@
+/*
+ Copyright (c) 2012, Yahoo! Inc. All rights reserved.
+ Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+
+var path = require('path'),
+ mkdirp = require('mkdirp'),
+ util = require('util'),
+ fs = require('fs'),
+ defaults = require('./common/defaults'),
+ Report = require('./index'),
+ TreeSummarizer = require('../util/tree-summarizer'),
+ utils = require('../object-utils'),
+ PCT_COLS = 9,
+ MISSING_COL = 15,
+ TAB_SIZE = 1,
+ DELIM = ' |',
+ COL_DELIM = '-|';
+
+/**
+ * a `Report` implementation that produces text output in a detailed table.
+ *
+ * Usage
+ * -----
+ *
+ * var report = require('istanbul').Report.create('text');
+ *
+ * @class TextReport
+ * @extends Report
+ * @module report
+ * @constructor
+ * @param {Object} opts optional
+ * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file
+ * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console
+ * @param {Number} [opts.maxCols] the max column width of the report. By default, the width of the report is adjusted based on the length of the paths
+ * to be reported.
+ */
+function TextReport(opts) {
+ Report.call(this);
+ opts = opts || {};
+ this.dir = opts.dir || process.cwd();
+ this.file = opts.file;
+ this.summary = opts.summary;
+ this.maxCols = opts.maxCols || 0;
+ this.watermarks = opts.watermarks || defaults.watermarks();
+}
+
+TextReport.TYPE = 'text';
+util.inherits(TextReport, Report);
+
+function padding(num, ch) {
+ var str = '',
+ i;
+ ch = ch || ' ';
+ for (i = 0; i < num; i += 1) {
+ str += ch;
+ }
+ return str;
+}
+
+function fill(str, width, right, tabs, clazz) {
+ tabs = tabs || 0;
+ str = String(str);
+
+ var leadingSpaces = tabs * TAB_SIZE,
+ remaining = width - leadingSpaces,
+ leader = padding(leadingSpaces),
+ fmtStr = '',
+ fillStr,
+ strlen = str.length;
+
+ if (remaining > 0) {
+ if (remaining >= strlen) {
+ fillStr = padding(remaining - strlen);
+ fmtStr = right ? fillStr + str : str + fillStr;
+ } else {
+ fmtStr = str.substring(strlen - remaining);
+ fmtStr = '... ' + fmtStr.substring(4);
+ }
+ }
+
+ fmtStr = defaults.colorize(fmtStr, clazz);
+ return leader + fmtStr;
+}
+
+function formatName(name, maxCols, level, clazz) {
+ return fill(name, maxCols, false, level, clazz);
+}
+
+function formatPct(pct, clazz, width) {
+ return fill(pct, width || PCT_COLS, true, 0, clazz);
+}
+
+function nodeName(node) {
+ return node.displayShortName() || 'All files';
+}
+
+function tableHeader(maxNameCols) {
+ var elements = [];
+ elements.push(formatName('File', maxNameCols, 0));
+ elements.push(formatPct('% Stmts'));
+ elements.push(formatPct('% Branch'));
+ elements.push(formatPct('% Funcs'));
+ elements.push(formatPct('% Lines'));
+ elements.push(formatPct('Uncovered Lines', undefined, MISSING_COL));
+ return elements.join(' |') + ' |';
+}
+
+function collectMissingLines(kind, linesCovered) {
+ var missingLines = [];
+
+ if (kind !== 'file') {
+ return [];
+ }
+
+ Object.keys(linesCovered).forEach(function (key) {
+ if (!linesCovered[key]) {
+ missingLines.push(key);
+ }
+ });
+
+ return missingLines;
+}
+
+function tableRow(node, maxNameCols, level, watermarks) {
+ var name = nodeName(node),
+ statements = node.metrics.statements.pct,
+ branches = node.metrics.branches.pct,
+ functions = node.metrics.functions.pct,
+ lines = node.metrics.lines.pct,
+ missingLines = collectMissingLines(node.kind, node.metrics.linesCovered),
+ elements = [];
+
+ elements.push(formatName(name, maxNameCols, level, defaults.classFor('statements', node.metrics, watermarks)));
+ elements.push(formatPct(statements, defaults.classFor('statements', node.metrics, watermarks)));
+ elements.push(formatPct(branches, defaults.classFor('branches', node.metrics, watermarks)));
+ elements.push(formatPct(functions, defaults.classFor('functions', node.metrics, watermarks)));
+ elements.push(formatPct(lines, defaults.classFor('lines', node.metrics, watermarks)));
+ elements.push(formatPct(missingLines.join(','), 'low', MISSING_COL));
+
+ return elements.join(DELIM) + DELIM;
+}
+
+function findNameWidth(node, level, last) {
+ last = last || 0;
+ level = level || 0;
+ var idealWidth = TAB_SIZE * level + nodeName(node).length;
+ if (idealWidth > last) {
+ last = idealWidth;
+ }
+ node.children.forEach(function (child) {
+ last = findNameWidth(child, level + 1, last);
+ });
+ return last;
+}
+
+function makeLine(nameWidth) {
+ var name = padding(nameWidth, '-'),
+ pct = padding(PCT_COLS, '-'),
+ elements = [];
+
+ elements.push(name);
+ elements.push(pct);
+ elements.push(pct);
+ elements.push(pct);
+ elements.push(pct);
+ elements.push(padding(MISSING_COL, '-'));
+ return elements.join(COL_DELIM) + COL_DELIM;
+}
+
+function walk(node, nameWidth, array, level, watermarks) {
+ var line;
+ if (level === 0) {
+ line = makeLine(nameWidth);
+ array.push(line);
+ array.push(tableHeader(nameWidth));
+ array.push(line);
+ } else {
+ array.push(tableRow(node, nameWidth, level, watermarks));
+ }
+ node.children.forEach(function (child) {
+ walk(child, nameWidth, array, level + 1, watermarks);
+ });
+ if (level === 0) {
+ array.push(line);
+ array.push(tableRow(node, nameWidth, level, watermarks));
+ array.push(line);
+ }
+}
+
+Report.mix(TextReport, {
+ synopsis: function () {
+ return 'text report that prints a coverage line for every file, typically to console';
+ },
+ getDefaultConfig: function () {
+ return { file: null, maxCols: 0 };
+ },
+ writeReport: function (collector /*, sync */) {
+ var summarizer = new TreeSummarizer(),
+ tree,
+ root,
+ nameWidth,
+ statsWidth = 4 * (PCT_COLS + 2) + MISSING_COL,
+ maxRemaining,
+ strings = [],
+ text;
+
+ collector.files().forEach(function (key) {
+ summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(
+ collector.fileCoverageFor(key)
+ ));
+ });
+ tree = summarizer.getTreeSummary();
+ root = tree.root;
+ nameWidth = findNameWidth(root);
+ if (this.maxCols > 0) {
+ maxRemaining = this.maxCols - statsWidth - 2;
+ if (nameWidth > maxRemaining) {
+ nameWidth = maxRemaining;
+ }
+ }
+ walk(root, nameWidth, strings, 0, this.watermarks);
+ text = strings.join('\n') + '\n';
+ if (this.file) {
+ mkdirp.sync(this.dir);
+ fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8');
+ } else {
+ console.log(text);
+ }
+ this.emit('done');
+ }
+});
+
+module.exports = TextReport;