2 Copyright (c) 2012, Yahoo! Inc. All rights reserved.
3 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
6 var path = require('path'),
7 mkdirp = require('mkdirp'),
8 util = require('util'),
10 defaults = require('./common/defaults'),
11 Report = require('./index'),
12 TreeSummarizer = require('../util/tree-summarizer'),
13 utils = require('../object-utils'),
21 * a `Report` implementation that produces text output in a detailed table.
26 * var report = require('istanbul').Report.create('text');
32 * @param {Object} opts optional
33 * @param {String} [opts.dir] the directory in which to the text coverage report will be written, when writing to a file
34 * @param {String} [opts.file] the filename for the report. When omitted, the report is written to console
35 * @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
38 function TextReport(opts) {
41 this.dir = opts.dir || process.cwd();
42 this.file = opts.file;
43 this.summary = opts.summary;
44 this.maxCols = opts.maxCols || 0;
45 this.watermarks = opts.watermarks || defaults.watermarks();
48 TextReport.TYPE = 'text';
49 util.inherits(TextReport, Report);
51 function padding(num, ch) {
55 for (i = 0; i < num; i += 1) {
61 function fill(str, width, right, tabs, clazz) {
65 var leadingSpaces = tabs * TAB_SIZE,
66 remaining = width - leadingSpaces,
67 leader = padding(leadingSpaces),
73 if (remaining >= strlen) {
74 fillStr = padding(remaining - strlen);
75 fmtStr = right ? fillStr + str : str + fillStr;
77 fmtStr = str.substring(strlen - remaining);
78 fmtStr = '... ' + fmtStr.substring(4);
82 fmtStr = defaults.colorize(fmtStr, clazz);
83 return leader + fmtStr;
86 function formatName(name, maxCols, level, clazz) {
87 return fill(name, maxCols, false, level, clazz);
90 function formatPct(pct, clazz, width) {
91 return fill(pct, width || PCT_COLS, true, 0, clazz);
94 function nodeName(node) {
95 return node.displayShortName() || 'All files';
98 function tableHeader(maxNameCols) {
100 elements.push(formatName('File', maxNameCols, 0));
101 elements.push(formatPct('% Stmts'));
102 elements.push(formatPct('% Branch'));
103 elements.push(formatPct('% Funcs'));
104 elements.push(formatPct('% Lines'));
105 elements.push(formatPct('Uncovered Lines', undefined, MISSING_COL));
106 return elements.join(' |') + ' |';
109 function collectMissingLines(kind, linesCovered) {
110 var missingLines = [];
112 if (kind !== 'file') {
116 Object.keys(linesCovered).forEach(function (key) {
117 if (!linesCovered[key]) {
118 missingLines.push(key);
125 function tableRow(node, maxNameCols, level, watermarks) {
126 var name = nodeName(node),
127 statements = node.metrics.statements.pct,
128 branches = node.metrics.branches.pct,
129 functions = node.metrics.functions.pct,
130 lines = node.metrics.lines.pct,
131 missingLines = collectMissingLines(node.kind, node.metrics.linesCovered),
134 elements.push(formatName(name, maxNameCols, level, defaults.classFor('statements', node.metrics, watermarks)));
135 elements.push(formatPct(statements, defaults.classFor('statements', node.metrics, watermarks)));
136 elements.push(formatPct(branches, defaults.classFor('branches', node.metrics, watermarks)));
137 elements.push(formatPct(functions, defaults.classFor('functions', node.metrics, watermarks)));
138 elements.push(formatPct(lines, defaults.classFor('lines', node.metrics, watermarks)));
139 elements.push(formatPct(missingLines.join(','), 'low', MISSING_COL));
141 return elements.join(DELIM) + DELIM;
144 function findNameWidth(node, level, last) {
147 var idealWidth = TAB_SIZE * level + nodeName(node).length;
148 if (idealWidth > last) {
151 node.children.forEach(function (child) {
152 last = findNameWidth(child, level + 1, last);
157 function makeLine(nameWidth) {
158 var name = padding(nameWidth, '-'),
159 pct = padding(PCT_COLS, '-'),
167 elements.push(padding(MISSING_COL, '-'));
168 return elements.join(COL_DELIM) + COL_DELIM;
171 function walk(node, nameWidth, array, level, watermarks) {
174 line = makeLine(nameWidth);
176 array.push(tableHeader(nameWidth));
179 array.push(tableRow(node, nameWidth, level, watermarks));
181 node.children.forEach(function (child) {
182 walk(child, nameWidth, array, level + 1, watermarks);
186 array.push(tableRow(node, nameWidth, level, watermarks));
191 Report.mix(TextReport, {
192 synopsis: function () {
193 return 'text report that prints a coverage line for every file, typically to console';
195 getDefaultConfig: function () {
196 return { file: null, maxCols: 0 };
198 writeReport: function (collector /*, sync */) {
199 var summarizer = new TreeSummarizer(),
203 statsWidth = 4 * (PCT_COLS + 2) + MISSING_COL,
208 collector.files().forEach(function (key) {
209 summarizer.addFileCoverageSummary(key, utils.summarizeFileCoverage(
210 collector.fileCoverageFor(key)
213 tree = summarizer.getTreeSummary();
215 nameWidth = findNameWidth(root);
216 if (this.maxCols > 0) {
217 maxRemaining = this.maxCols - statsWidth - 2;
218 if (nameWidth > maxRemaining) {
219 nameWidth = maxRemaining;
222 walk(root, nameWidth, strings, 0, this.watermarks);
223 text = strings.join('\n') + '\n';
225 mkdirp.sync(this.dir);
226 fs.writeFileSync(path.join(this.dir, this.file), text, 'utf8');
234 module.exports = TextReport;