blob: 2c79efc5c2d0239b62c51471f5768a12e5776f03 [file] [log] [blame]
var fs = require('fs');
var tapOut = require('tap-out');
var through = require('through2');
var duplexer = require('duplexer');
var format = require('chalk');
var prettyMs = require('pretty-ms');
var _ = require('lodash');
var repeat = require('repeat-string');
var symbols = require('figures');
var lTrimList = require('./lib/utils/l-trim-list');
module.exports = function (spec) {
spec = spec || {};
var OUTPUT_PADDING = spec.padding || ' ';
var output = through();
var parser = tapOut();
var stream = duplexer(parser, output);
var startTime = new Date().getTime();
output.push('\n');
parser.on('test', function (test) {
output.push('\n' + pad(format.underline(test.name)) + '\n\n');
});
// Passing assertions
parser.on('pass', function (assertion) {
if (/# SKIP/.test(assertion.name)) {
var name = assertion.name.replace(' # SKIP', '')
name = format.cyan('- ' + name);
output.push(pad(' ' + name + '\n'));
}
else {
var glyph = format.green(symbols.tick);
var name = format.dim(assertion.name);
output.push(pad(' ' + glyph + ' ' + name + '\n'));
}
});
// Failing assertions
parser.on('fail', function (assertion) {
var glyph = symbols.cross;
var title = glyph + ' ' + assertion.name;
var raw = format.cyan(prettifyRawError(assertion.error.raw));
var divider = _.fill(
new Array((title).length + 1),
'-'
).join('');
output.push('\n' + pad(' ' + format.red(title) + '\n'));
output.push(pad(' ' + format.red(divider) + '\n'));
output.push(raw);
stream.failed = true;
});
parser.on('comment', function (comment) {
output.push(pad(' ' + format.yellow(comment.raw)) + '\n');
});
// All done
parser.on('output', function (results) {
output.push('\n\n');
// Most likely a failure upstream
if (results.plans.length < 1) {
process.exit(1);
}
if (results.fail.length > 0) {
output.push(formatErrors(results));
output.push('\n');
}
output.push(formatTotals(results));
output.push('\n\n\n');
// Exit if no tests run. This is a result of 1 of 2 things:
// 1. No tests and asserts were written
// 2. There was some error before the TAP got to the parser
if (results.tests.length === 0 &&
results.asserts.length === 0) {
process.exit(1);
}
});
// Utils
function prettifyRawError (rawError) {
return rawError.split('\n').map(function (line) {
return pad(line);
}).join('\n') + '\n\n';
}
function formatErrors (results) {
var failCount = results.fail.length;
var past = (failCount === 1) ? 'was' : 'were';
var plural = (failCount === 1) ? 'failure' : 'failures';
var out = '\n' + pad(format.red.bold('Failed Tests:') + ' There ' + past + ' ' + format.red.bold(failCount) + ' ' + plural + '\n');
out += formatFailedAssertions(results);
return out;
}
function formatTotals (results) {
if (results.tests.length === 0 &&
results.asserts.length === 0) {
return pad(format.red(symbols.cross + ' No tests found'));
}
return _.filter([
pad('total: ' + results.asserts.length),
pad(format.green('passing: ' + results.pass.length)),
results.fail.length > 0 ? pad(format.red('failing: ' + results.fail.length)) : undefined,
pad('duration: ' + prettyMs(new Date().getTime() - startTime))
], _.identity).join('\n');
}
function formatFailedAssertions (results) {
var out = '';
var groupedAssertions = _.groupBy(results.fail, function (assertion) {
return assertion.test;
});
_.each(groupedAssertions, function (assertions, testNumber) {
// Wrie failed assertion's test name
var test = _.find(results.tests, {number: parseInt(testNumber)});
out += '\n' + pad(' ' + test.name + '\n\n');
// Write failed assertion
_.each(assertions, function (assertion) {
out += pad(' ' + format.red(symbols.cross) + ' ' + format.red(assertion.name)) + '\n';
});
out += '\n';
});
return out;
}
function pad (str) {
return OUTPUT_PADDING + str;
}
return stream;
};