Created
December 4, 2013 11:47
-
-
Save keis/7786301 to your computer and use it in GitHub Desktop.
extend mocha dot reporter with log4js capture
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var log4js = require('log4js'), | |
Base = require('mocha/lib/reporters/base'), | |
Dot = require('mocha/lib/reporters/dot'), | |
diff = require('diff'), | |
ms = require('mocha/lib/ms'), | |
color = Base.color; | |
function LogReport(runner) { | |
var captured = [], | |
layout = log4js.layouts.colouredLayout; | |
Dot.call(this, runner); | |
runner.on('start', function () { | |
log4js.clearAppenders(); | |
log4js.addAppender(function (logEvent) { | |
captured.push(layout(logEvent)); | |
}); | |
}); | |
runner.on('test', function () { | |
captured = []; | |
}); | |
runner.on('fail', function (test) { | |
if (captured.length) { | |
test.capturedLog = captured; | |
} | |
}); | |
} | |
/** | |
* Check that a / b have the same type. | |
* | |
* @param {Object} a | |
* @param {Object} b | |
* @return {Boolean} | |
* @api private | |
*/ | |
function sameType(a, b) { | |
a = Object.prototype.toString.call(a); | |
b = Object.prototype.toString.call(b); | |
return a == b; | |
} | |
/** | |
* Returns a string with all invisible characters in plain text | |
* | |
* @param {String} line | |
* @return {String} | |
* @api private | |
*/ | |
function escapeInvisibles(line) { | |
return line.replace(/\t/g, '<tab>') | |
.replace(/\r/g, '<CR>') | |
.replace(/\n/g, '<LF>\n'); | |
} | |
/** | |
* 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'); | |
} | |
/** | |
* Returns an inline diff between 2 strings with coloured ANSI output | |
* | |
* @param {Error} Error with actual/expected | |
* @return {String} Diff | |
* @api private | |
*/ | |
function inlineDiff(err, escape) { | |
var msg = errorDiff(err, 'WordsWithSpace', 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, ' '); | |
return msg; | |
} | |
/** | |
* Returns a unified diff between 2 strings | |
* | |
* @param {Error} Error with actual/expected | |
* @return {String} Diff | |
* @api private | |
*/ | |
function unifiedDiff(err, escape) { | |
var indent = ' '; | |
function cleanUp(line) { | |
if (escape) { | |
line = escapeInvisibles(line); | |
} | |
if (line[0] === '+') return indent + colorLines('diff added', line); | |
if (line[0] === '-') return indent + colorLines('diff removed', line); | |
if (line.match(/\@\@/)) return null; | |
if (line.match(/\\ No newline/)) return null; | |
else return indent + line; | |
} | |
function notBlank(line) { | |
return line != null; | |
} | |
msg = diff.createPatch('string', err.actual, err.expected); | |
var lines = msg.split('\n').splice(4); | |
return '\n ' | |
+ colorLines('diff added', '+ expected') + ' ' | |
+ colorLines('diff removed', '- actual') | |
+ '\n\n' | |
+ lines.map(cleanUp).filter(notBlank).join('\n'); | |
} | |
/** | |
* Outut the given `failures` as a list. | |
* | |
* @param {Array} failures | |
* @api public | |
*/ | |
function list(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; | |
// uncaught | |
if (err.uncaught) { | |
msg = 'Uncaught ' + msg; | |
} | |
// explicitly show diff | |
if (err.showDiff && sameType(actual, expected)) { | |
escape = false; | |
err.actual = actual = stringify(actual); | |
err.expected = expected = stringify(expected); | |
} | |
// actual / expected diff | |
if ('string' == typeof actual && 'string' == typeof expected) { | |
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n'); | |
var match = message.match(/^([^:]+): expected/); | |
msg = match ? '\n ' + color('error message', match[1]) : ''; | |
if (exports.inlineDiffs) { | |
msg += inlineDiff(err, escape); | |
} else { | |
msg += unifiedDiff(err, escape); | |
} | |
} | |
if (test.capturedLog) { | |
msg += '\n Captured log messages:\n'; | |
test.capturedLog.forEach(function (log) { | |
msg += ' ' + log; | |
}); | |
} | |
// indent stack trace without msg | |
stack = stack.slice(index ? index + 1 : index) | |
.replace(/^/gm, ' '); | |
console.error(fmt, (i + 1), test.fullTitle(), msg, stack); | |
}); | |
} | |
LogReport.prototype = Object.create(Dot.prototype); | |
LogReport.prototype.epilogue = function () { | |
var stats = this.stats; | |
var tests; | |
var fmt; | |
console.log(); | |
// passes | |
fmt = color('bright pass', ' ') | |
+ color('green', ' %d passing') | |
+ color('light', ' (%s)'); | |
console.log(fmt, | |
stats.passes || 0, | |
ms(stats.duration)); | |
// pending | |
if (stats.pending) { | |
fmt = color('pending', ' ') | |
+ color('pending', ' %d pending'); | |
console.log(fmt, stats.pending); | |
} | |
// failures | |
if (stats.failures) { | |
fmt = color('fail', ' %d failing'); | |
console.error(fmt, | |
stats.failures); | |
list(this.failures); | |
console.error(); | |
} | |
}; | |
module.exports = LogReport; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment