240 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			240 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 Copyright 2012-2015, Yahoo Inc.
							 | 
						||
| 
								 | 
							
								 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 TAB_SIZE = 1;
							 | 
						||
| 
								 | 
							
								const DELIM = ' |';
							 | 
						||
| 
								 | 
							
								const COL_DELIM = '-|';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function padding(num, ch) {
							 | 
						||
| 
								 | 
							
								    let str = '';
							 | 
						||
| 
								 | 
							
								    let i;
							 | 
						||
| 
								 | 
							
								    ch = ch || ' ';
							 | 
						||
| 
								 | 
							
								    for (i = 0; i < num; i += 1) {
							 | 
						||
| 
								 | 
							
								        str += ch;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return str;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function fill(str, width, right, tabs) {
							 | 
						||
| 
								 | 
							
								    tabs = tabs || 0;
							 | 
						||
| 
								 | 
							
								    str = String(str);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const leadingSpaces = tabs * TAB_SIZE;
							 | 
						||
| 
								 | 
							
								    const remaining = width - leadingSpaces;
							 | 
						||
| 
								 | 
							
								    const leader = padding(leadingSpaces);
							 | 
						||
| 
								 | 
							
								    let fmtStr = '';
							 | 
						||
| 
								 | 
							
								    let fillStr;
							 | 
						||
| 
								 | 
							
								    const 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);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return leader + fmtStr;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function formatName(name, maxCols, level) {
							 | 
						||
| 
								 | 
							
								    return fill(name, maxCols, false, level);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function formatPct(pct, width) {
							 | 
						||
| 
								 | 
							
								    return fill(pct, width || PCT_COLS, true, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function nodeName(node) {
							 | 
						||
| 
								 | 
							
								    return node.getRelativeName() || 'All files';
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function depthFor(node) {
							 | 
						||
| 
								 | 
							
								    let ret = 0;
							 | 
						||
| 
								 | 
							
								    node = node.getParent();
							 | 
						||
| 
								 | 
							
								    while (node) {
							 | 
						||
| 
								 | 
							
								        ret += 1;
							 | 
						||
| 
								 | 
							
								        node = node.getParent();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return ret;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function findNameWidth(node, context) {
							 | 
						||
| 
								 | 
							
								    let last = 0;
							 | 
						||
| 
								 | 
							
								    const compareWidth = function(node) {
							 | 
						||
| 
								 | 
							
								        const depth = depthFor(node);
							 | 
						||
| 
								 | 
							
								        const idealWidth = TAB_SIZE * depth + nodeName(node).length;
							 | 
						||
| 
								 | 
							
								        if (idealWidth > last) {
							 | 
						||
| 
								 | 
							
								            last = idealWidth;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    const visitor = {
							 | 
						||
| 
								 | 
							
								        onSummary(node) {
							 | 
						||
| 
								 | 
							
								            compareWidth(node);
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								        onDetail(node) {
							 | 
						||
| 
								 | 
							
								            compareWidth(node);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    node.visit(context.getVisitor(visitor));
							 | 
						||
| 
								 | 
							
								    return last;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function makeLine(nameWidth) {
							 | 
						||
| 
								 | 
							
								    const name = padding(nameWidth, '-');
							 | 
						||
| 
								 | 
							
								    const pct = padding(PCT_COLS, '-');
							 | 
						||
| 
								 | 
							
								    const 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 tableHeader(maxNameCols) {
							 | 
						||
| 
								 | 
							
								    const 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 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)
							 | 
						||
| 
								 | 
							
								        .map(key => key);
							 | 
						||
| 
								 | 
							
								    return colorizer(formatPct(missingLines.join(','), MISSING_COL), 'medium');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isFull(metrics) {
							 | 
						||
| 
								 | 
							
								    return (
							 | 
						||
| 
								 | 
							
								        metrics.statements.pct === 100 &&
							 | 
						||
| 
								 | 
							
								        metrics.branches.pct === 100 &&
							 | 
						||
| 
								 | 
							
								        metrics.functions.pct === 100 &&
							 | 
						||
| 
								 | 
							
								        metrics.lines.pct === 100
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function tableRow(
							 | 
						||
| 
								 | 
							
								    node,
							 | 
						||
| 
								 | 
							
								    context,
							 | 
						||
| 
								 | 
							
								    colorizer,
							 | 
						||
| 
								 | 
							
								    maxNameCols,
							 | 
						||
| 
								 | 
							
								    level,
							 | 
						||
| 
								 | 
							
								    skipEmpty,
							 | 
						||
| 
								 | 
							
								    skipFull
							 | 
						||
| 
								 | 
							
								) {
							 | 
						||
| 
								 | 
							
								    const name = nodeName(node);
							 | 
						||
| 
								 | 
							
								    const metrics = node.getCoverageSummary();
							 | 
						||
| 
								 | 
							
								    const isEmpty = metrics.isEmpty();
							 | 
						||
| 
								 | 
							
								    if (skipEmpty && isEmpty) {
							 | 
						||
| 
								 | 
							
								        return '';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (skipFull && isFull(metrics)) {
							 | 
						||
| 
								 | 
							
								        return '';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const mm = {
							 | 
						||
| 
								 | 
							
								        statements: isEmpty ? 0 : metrics.statements.pct,
							 | 
						||
| 
								 | 
							
								        branches: isEmpty ? 0 : metrics.branches.pct,
							 | 
						||
| 
								 | 
							
								        functions: isEmpty ? 0 : metrics.functions.pct,
							 | 
						||
| 
								 | 
							
								        lines: isEmpty ? 0 : metrics.lines.pct
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    const colorize = isEmpty
							 | 
						||
| 
								 | 
							
								        ? function(str) {
							 | 
						||
| 
								 | 
							
								              return str;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        : function(str, key) {
							 | 
						||
| 
								 | 
							
								              return colorizer(str, context.classForPercent(key, mm[key]));
							 | 
						||
| 
								 | 
							
								          };
							 | 
						||
| 
								 | 
							
								    const elements = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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.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;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function TextReport(opts) {
							 | 
						||
| 
								 | 
							
								    opts = opts || {};
							 | 
						||
| 
								 | 
							
								    this.file = opts.file || null;
							 | 
						||
| 
								 | 
							
								    this.maxCols = opts.maxCols || 0;
							 | 
						||
| 
								 | 
							
								    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);
							 | 
						||
| 
								 | 
							
								    if (this.maxCols > 0) {
							 | 
						||
| 
								 | 
							
								        const maxRemaining = this.maxCols - statsWidth - 2;
							 | 
						||
| 
								 | 
							
								        if (this.nameWidth > maxRemaining) {
							 | 
						||
| 
								 | 
							
								            this.nameWidth = maxRemaining;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const line = makeLine(this.nameWidth);
							 | 
						||
| 
								 | 
							
								    this.cw.println(line);
							 | 
						||
| 
								 | 
							
								    this.cw.println(tableHeader(this.nameWidth));
							 | 
						||
| 
								 | 
							
								    this.cw.println(line);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TextReport.prototype.onSummary = function(node, context) {
							 | 
						||
| 
								 | 
							
								    const nodeDepth = depthFor(node);
							 | 
						||
| 
								 | 
							
								    const row = tableRow(
							 | 
						||
| 
								 | 
							
								        node,
							 | 
						||
| 
								 | 
							
								        context,
							 | 
						||
| 
								 | 
							
								        this.cw.colorize.bind(this.cw),
							 | 
						||
| 
								 | 
							
								        this.nameWidth,
							 | 
						||
| 
								 | 
							
								        nodeDepth,
							 | 
						||
| 
								 | 
							
								        this.skipEmpty,
							 | 
						||
| 
								 | 
							
								        this.skipFull
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								    if (row) {
							 | 
						||
| 
								 | 
							
								        this.cw.println(row);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TextReport.prototype.onDetail = function(node, context) {
							 | 
						||
| 
								 | 
							
								    return this.onSummary(node, context);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TextReport.prototype.onEnd = function() {
							 | 
						||
| 
								 | 
							
								    this.cw.println(makeLine(this.nameWidth));
							 | 
						||
| 
								 | 
							
								    this.cw.close();
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = TextReport;
							 |