Skip to content

Commit

Permalink
feat(text report): Optimize output to show more missing lines (#341)
Browse files Browse the repository at this point in the history
  • Loading branch information
piranna authored and coreyfarrell committed May 26, 2019
1 parent bf3a539 commit c4e8b8e
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 71 deletions.
163 changes: 97 additions & 66 deletions packages/istanbul-reports/lib/text/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
/*
Copyright 2012-2015, Yahoo Inc.
Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
Copyrights licensed under the New BSD License. See the accompanying LICENSE
file for terms.
*/
'use strict';

const PCT_COLS = 9;
const MISSING_COL = 18;
const NAME_COL = 4;
const PCT_COLS = 7;
const MISSING_COL = 17;
const TAB_SIZE = 1;
const DELIM = ' |';
const COL_DELIM = '-|';
const DELIM = ' | ';

function padding(num, ch) {
let str = '';
Expand All @@ -28,17 +29,21 @@ function fill(str, width, right, tabs) {
const remaining = width - leadingSpaces;
const leader = padding(leadingSpaces);
let fmtStr = '';
let fillStr;
const strlen = str.length;

if (remaining > 0) {
const strlen = str.length;
let fillStr;

if (remaining >= strlen) {
fillStr = padding(remaining - strlen);
fmtStr = right ? fillStr + str : str + fillStr;
} else {
fmtStr = str.substring(strlen - remaining);
fmtStr = '... ' + fmtStr.substring(4);
fillStr = '...';
const length = remaining - fillStr.length;

str = str.substring(strlen - length);
right = true;
}
fmtStr = right ? fillStr + str : str + fillStr;
}

return leader + fmtStr;
Expand All @@ -52,6 +57,29 @@ function formatPct(pct, width) {
return fill(pct, width || PCT_COLS, true, 0);
}

function nodeMissing(node) {
if (node.isSummary()) {
return '';
}

const metrics = node.getCoverageSummary();
const isEmpty = metrics.isEmpty();
const lines = isEmpty ? 0 : metrics.lines.pct;

let missingLines;

const fileCoverage = node.getFileCoverage();
if (lines === 100) {
const branches = fileCoverage.getBranchCoverageByLine();
missingLines = Object.keys(branches).filter(
key => branches[key].coverage < 100
);
} else {
missingLines = fileCoverage.getUncoveredLines();
}
return missingLines.join(',');
}

function nodeName(node) {
return node.getRelativeName() || 'All files';
}
Expand All @@ -66,67 +94,49 @@ function depthFor(node) {
return ret;
}

function findNameWidth(node, context) {
function nullDepthFor() {
return 0;
}

function findWidth(node, context, nodeExtractor, depthFor = nullDepthFor) {
let last = 0;
const compareWidth = function(node) {
const depth = depthFor(node);
const idealWidth = TAB_SIZE * depth + nodeName(node).length;
if (idealWidth > last) {
last = idealWidth;
}
};
function compareWidth(node) {
last = Math.max(
last,
TAB_SIZE * depthFor(node) + nodeExtractor(node).length
);
}
const visitor = {
onSummary(node) {
compareWidth(node);
},
onDetail(node) {
compareWidth(node);
}
onSummary: compareWidth,
onDetail: compareWidth
};
node.visit(context.getVisitor(visitor));
return last;
}

function makeLine(nameWidth) {
function makeLine(nameWidth, missingWidth) {
const name = padding(nameWidth, '-');
const pct = padding(PCT_COLS, '-');
const elements = [];

elements.push(name);
elements.push(pct);
elements.push(padding(PCT_COLS + 1, '-'));
elements.push(pct);
elements.push(pct);
elements.push(pct);
elements.push(padding(MISSING_COL, '-'));
return elements.join(COL_DELIM) + COL_DELIM;
elements.push(padding(missingWidth, '-'));
return elements.join(DELIM.replace(/ /g, '-')) + '-';
}

function tableHeader(maxNameCols) {
function tableHeader(maxNameCols, missingWidth) {
const elements = [];
elements.push(formatName('File', maxNameCols, 0));
elements.push(formatPct('% Stmts'));
elements.push(formatPct('% Branch'));
elements.push(formatPct('% Branch', PCT_COLS + 1));
elements.push(formatPct('% Funcs'));
elements.push(formatPct('% Lines'));
elements.push(formatPct('Uncovered Line #s', MISSING_COL));
return elements.join(' |') + ' |';
}

function missingLines(node, colorizer) {
const missingLines = node.isSummary()
? []
: node.getFileCoverage().getUncoveredLines();
return colorizer(formatPct(missingLines.join(','), MISSING_COL), 'low');
}

function missingBranches(node, colorizer) {
const branches = node.isSummary()
? {}
: node.getFileCoverage().getBranchCoverageByLine();
const missingLines = Object.keys(branches).filter(
key => branches[key].coverage < 100
);
return colorizer(formatPct(missingLines.join(','), MISSING_COL), 'medium');
elements.push(formatName('Uncovered Line #s', missingWidth));
return elements.join(DELIM) + ' ';
}

function isFull(metrics) {
Expand All @@ -145,7 +155,8 @@ function tableRow(
maxNameCols,
level,
skipEmpty,
skipFull
skipFull,
missingWidth
) {
const name = nodeName(node);
const metrics = node.getCoverageSummary();
Expand Down Expand Up @@ -174,40 +185,59 @@ function tableRow(

elements.push(colorize(formatName(name, maxNameCols, level), 'statements'));
elements.push(colorize(formatPct(mm.statements), 'statements'));
elements.push(colorize(formatPct(mm.branches), 'branches'));
elements.push(colorize(formatPct(mm.branches, PCT_COLS + 1), 'branches'));
elements.push(colorize(formatPct(mm.functions), 'functions'));
elements.push(colorize(formatPct(mm.lines), 'lines'));
if (mm.lines === 100) {
elements.push(missingBranches(node, colorizer));
} else {
elements.push(missingLines(node, colorizer));
}
return elements.join(DELIM) + DELIM;
elements.push(
colorizer(
formatName(nodeMissing(node), missingWidth),
mm.lines === 100 ? 'medium' : 'low'
)
);

return elements.join(DELIM) + ' ';
}

function TextReport(opts) {
opts = opts || {};

const { maxCols } = opts;

this.file = opts.file || null;
this.maxCols = opts.maxCols || 0;
this.maxCols = maxCols != null ? maxCols : process.stdout.columns || 80;
this.cw = null;
this.skipEmpty = opts.skipEmpty;
this.skipFull = opts.skipFull;
}

TextReport.prototype.onStart = function(root, context) {
const statsWidth = 4 * (PCT_COLS + 2) + MISSING_COL;

this.cw = context.writer.writeFile(this.file);
this.nameWidth = findNameWidth(root, context);
this.nameWidth = Math.max(
NAME_COL,
findWidth(root, context, nodeName, depthFor)
);
this.missingWidth = Math.max(
MISSING_COL,
findWidth(root, context, nodeMissing)
);

if (this.maxCols > 0) {
const maxRemaining = this.maxCols - statsWidth - 2;
const pct_cols = DELIM.length + 4 * (PCT_COLS + DELIM.length) + 2;

const maxRemaining = this.maxCols - (pct_cols + MISSING_COL);
if (this.nameWidth > maxRemaining) {
this.nameWidth = maxRemaining;
this.missingWidth = MISSING_COL;
} else if (this.nameWidth < maxRemaining) {
const maxRemaining = this.maxCols - (this.nameWidth + pct_cols);
if (this.missingWidth > maxRemaining) {
this.missingWidth = maxRemaining;
}
}
}
const line = makeLine(this.nameWidth);
const line = makeLine(this.nameWidth, this.missingWidth);
this.cw.println(line);
this.cw.println(tableHeader(this.nameWidth));
this.cw.println(tableHeader(this.nameWidth, this.missingWidth));
this.cw.println(line);
};

Expand All @@ -220,7 +250,8 @@ TextReport.prototype.onSummary = function(node, context) {
this.nameWidth,
nodeDepth,
this.skipEmpty,
this.skipFull
this.skipFull,
this.missingWidth
);
if (row) {
this.cw.println(row);
Expand All @@ -232,7 +263,7 @@ TextReport.prototype.onDetail = function(node, context) {
};

TextReport.prototype.onEnd = function() {
this.cw.println(makeLine(this.nameWidth));
this.cw.println(makeLine(this.nameWidth, this.missingWidth));
this.cw.close();
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "handles 100% branch and line coverage",
"textReportExpected": "----------|----------|----------|----------|----------|-------------------|\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n----------|----------|----------|----------|----------|-------------------|\n\u001b[32;1mAll files\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[33;1m \u001b[0m |\n\u001b[32;1m index.js\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[33;1m \u001b[0m |\n----------|----------|----------|----------|----------|-------------------|\n",
"textReportExpected": "----------|---------|----------|---------|---------|-------------------\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n----------|---------|----------|---------|---------|-------------------\n\u001b[32;1mAll files\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[33;1m \u001b[0m \n\u001b[32;1m index.js\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[33;1m \u001b[0m \n----------|---------|----------|---------|---------|-------------------\n",
"htmlSpaFiles": ["index.js.html", "index.html"],
"htmlSpaCoverageData": {
"file": "",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "100% line coverage, missing branch coverage",
"textReportExpected": "----------|----------|----------|----------|----------|-------------------|\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n----------|----------|----------|----------|----------|-------------------|\n\u001b[32;1mAll files\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 95.35\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[33;1m \u001b[0m |\n\u001b[32;1m index.js\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 95.35\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[32;1m 100\u001b[0m |\u001b[33;1m 21,29\u001b[0m |\n----------|----------|----------|----------|----------|-------------------|\n",
"textReportExpected": "----------|---------|----------|---------|---------|-------------------\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n----------|---------|----------|---------|---------|-------------------\n\u001b[32;1mAll files\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 95.35\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[33;1m \u001b[0m \n\u001b[32;1m index.js\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 95.35\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[32;1m 100\u001b[0m | \u001b[33;1m21,29 \u001b[0m \n----------|---------|----------|---------|---------|-------------------\n",
"htmlSpaFiles": ["index.js.html", "index.html"],
"htmlSpaCoverageData": {
"file": "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"opts": {
"skipEmpty": true
},
"textReportExpected": "----------|----------|----------|----------|----------|-------------------|\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n----------|----------|----------|----------|----------|-------------------|\n----------|----------|----------|----------|----------|-------------------|\n",
"textReportExpected": "----------|---------|----------|---------|---------|-------------------\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n----------|---------|----------|---------|---------|-------------------\n----------|---------|----------|---------|---------|-------------------\n",
"htmlSpaFiles": ["empty.js.html", "index.html"],
"htmlSpaCoverageData": {
"file": "",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"title": "empty file - show 0%",
"textReportExpected": "----------|----------|----------|----------|----------|-------------------|\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |\n----------|----------|----------|----------|----------|-------------------|\nAll files | 0 | 0 | 0 | 0 |\u001b[31;1m \u001b[0m |\n empty.js | 0 | 0 | 0 | 0 |\u001b[31;1m \u001b[0m |\n----------|----------|----------|----------|----------|-------------------|\n",
"textReportExpected": "----------|---------|----------|---------|---------|-------------------\nFile | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s \n----------|---------|----------|---------|---------|-------------------\nAll files | 0 | 0 | 0 | 0 | \u001b[31;1m \u001b[0m \n empty.js | 0 | 0 | 0 | 0 | \u001b[31;1m \u001b[0m \n----------|---------|----------|---------|---------|-------------------\n",
"htmlSpaFiles": ["empty.js.html", "index.html"],
"htmlSpaCoverageData": {
"file": "",
Expand Down

0 comments on commit c4e8b8e

Please sign in to comment.