compound-ex4
Version:
Compound-ex4 - MVC framework for NodeJS (ExpressJs 4 version), fork compoundjs(https://github.com/1602/compound)
382 lines (313 loc) • 7.75 kB
JavaScript
/**
* Module dependencies.
*/
var tty = require('tty')
, diff = require('diff')
, ms = require('../ms');
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = global.Date
, setTimeout = global.setTimeout
, setInterval = global.setInterval
, clearTimeout = global.clearTimeout
, clearInterval = global.clearInterval;
/**
* Check if both stdio streams are associated with a tty.
*/
var isatty = tty.isatty(1) && tty.isatty(2);
/**
* Expose `Base`.
*/
exports = module.exports = Base;
/**
* Enable coloring by default.
*/
exports.useColors = isatty;
/**
* Default color map.
*/
exports.colors = {
'pass': 90
, 'fail': 31
, 'bright pass': 92
, 'bright fail': 91
, 'bright yellow': 93
, 'pending': 36
, 'suite': 0
, 'error title': 0
, 'error message': 31
, 'error stack': 90
, 'checkmark': 32
, 'fast': 90
, 'medium': 33
, 'slow': 31
, 'green': 32
, 'light': 90
, 'diff gutter': 90
, 'diff added': 42
, 'diff removed': 41
};
/**
* Default symbol map.
*/
exports.symbols = {
ok: '✓',
err: '✖',
dot: '․'
};
// With node.js on Windows: use symbols available in terminal default fonts
if ('win32' == process.platform) {
exports.symbols.ok = '\u221A';
exports.symbols.err = '\u00D7';
exports.symbols.dot = '.';
}
/**
* Color `str` with the given `type`,
* allowing colors to be disabled,
* as well as user-defined color
* schemes.
*
* @param {String} type
* @param {String} str
* @return {String}
* @api private
*/
var color = exports.color = function(type, str) {
if (!exports.useColors) return str;
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
};
/**
* Expose term window size, with some
* defaults for when stderr is not a tty.
*/
exports.window = {
width: isatty
? process.stdout.getWindowSize
? process.stdout.getWindowSize(1)[0]
: tty.getWindowSize()[1]
: 75
};
/**
* Expose some basic cursor interactions
* that are common among reporters.
*/
exports.cursor = {
hide: function(){
process.stdout.write('\u001b[?25l');
},
show: function(){
process.stdout.write('\u001b[?25h');
},
deleteLine: function(){
process.stdout.write('\u001b[2K');
},
beginningOfLine: function(){
process.stdout.write('\u001b[0G');
},
CR: function(){
exports.cursor.deleteLine();
exports.cursor.beginningOfLine();
}
};
/**
* Outut the given `failures` as a list.
*
* @param {Array} failures
* @api public
*/
exports.list = function(failures){
console.error();
failures.forEach(function(test, i){
// format
var fmt = color('error title', ' %s) %s:\n')
+ color('error message', ' %s')
+ color('error stack', '\n%s\n');
// msg
var err = test.err
, message = err.message || ''
, stack = err.stack || message
, index = stack.indexOf(message) + message.length
, msg = stack.slice(0, index)
, actual = err.actual
, expected = err.expected
, escape = true;
// explicitly show diff
if (err.showDiff) {
escape = false;
err.actual = actual = JSON.stringify(actual, null, 2);
err.expected = expected = JSON.stringify(expected, null, 2);
}
// actual / expected diff
if ('string' == typeof actual && 'string' == typeof expected) {
var len = Math.max(actual.length, expected.length);
if (len < 20) msg = errorDiff(err, 'Chars', escape);
else msg = errorDiff(err, 'Words', escape);
// linenos
var lines = msg.split('\n');
if (lines.length > 4) {
var width = String(lines.length).length;
msg = lines.map(function(str, i){
return pad(++i, width) + ' |' + ' ' + str;
}).join('\n');
}
// legend
msg = '\n'
+ color('diff removed', 'actual')
+ ' '
+ color('diff added', 'expected')
+ '\n\n'
+ msg
+ '\n';
// indent
msg = msg.replace(/^/gm, ' ');
fmt = color('error title', ' %s) %s:\n%s')
+ color('error stack', '\n%s\n');
}
// indent stack trace without msg
stack = stack.slice(index ? index + 1 : index)
.replace(/^/gm, ' ');
console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
});
};
/**
* Initialize a new `Base` reporter.
*
* All other reporters generally
* inherit from this reporter, providing
* stats such as test duration, number
* of tests passed / failed etc.
*
* @param {Runner} runner
* @api public
*/
function Base(runner) {
var self = this
, stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 }
, failures = this.failures = [];
if (!runner) return;
this.runner = runner;
runner.stats = stats;
runner.on('start', function(){
stats.start = new Date;
});
runner.on('suite', function(suite){
stats.suites = stats.suites || 0;
suite.root || stats.suites++;
});
runner.on('test end', function(test){
stats.tests = stats.tests || 0;
stats.tests++;
});
runner.on('pass', function(test){
stats.passes = stats.passes || 0;
var medium = test.slow() / 2;
test.speed = test.duration > test.slow()
? 'slow'
: test.duration > medium
? 'medium'
: 'fast';
stats.passes++;
});
runner.on('fail', function(test, err){
stats.failures = stats.failures || 0;
stats.failures++;
test.err = err;
failures.push(test);
});
runner.on('end', function(){
stats.end = new Date;
stats.duration = new Date - stats.start;
});
runner.on('pending', function(){
stats.pending++;
});
}
/**
* Output common epilogue used by many of
* the bundled reporters.
*
* @api public
*/
Base.prototype.epilogue = function(){
var stats = this.stats
, fmt
, tests;
console.log();
function pluralize(n) {
return 1 == n ? 'test' : 'tests';
}
// failure
if (stats.failures) {
fmt = color('bright fail', ' ' + exports.symbols.err)
+ color('fail', ' %d of %d %s failed')
+ color('light', ':')
console.error(fmt,
stats.failures,
this.runner.total,
pluralize(this.runner.total));
Base.list(this.failures);
console.error();
return;
}
// pass
fmt = color('bright pass', ' ')
+ color('green', ' %d %s complete')
+ color('light', ' (%s)');
console.log(fmt,
stats.tests || 0,
pluralize(stats.tests),
ms(stats.duration));
// pending
if (stats.pending) {
fmt = color('pending', ' ')
+ color('pending', ' %d %s pending');
console.log(fmt, stats.pending, pluralize(stats.pending));
}
console.log();
};
/**
* Pad the given `str` to `len`.
*
* @param {String} str
* @param {String} len
* @return {String}
* @api private
*/
function pad(str, len) {
str = String(str);
return Array(len - str.length + 1).join(' ') + str;
}
/**
* Return a character diff for `err`.
*
* @param {Error} err
* @return {String}
* @api private
*/
function errorDiff(err, type, escape) {
return diff['diff' + type](err.actual, err.expected).map(function(str){
if (escape) {
str.value = str.value
.replace(/\t/g, '<tab>')
.replace(/\r/g, '<CR>')
.replace(/\n/g, '<LF>\n');
}
if (str.added) return colorLines('diff added', str.value);
if (str.removed) return colorLines('diff removed', str.value);
return str.value;
}).join('');
}
/**
* Color lines for `str`, using the color `name`.
*
* @param {String} name
* @param {String} str
* @return {String}
* @api private
*/
function colorLines(name, str) {
return str.split('\n').map(function(str){
return color(name, str);
}).join('\n');
}