spec-xunit-file
Version:
Mocha reporter which displays Spec output and generates xunit XML file
171 lines (139 loc) • 3.66 kB
JavaScript
/**
* Module dependencies.
*/
var mocha = require("mocha")
, Base = mocha.reporters.Base
, Spec = mocha.reporters.Spec
, utils = mocha.utils
, escape = utils.escape
, config = {
"file": "xunit.xml",
"consoleOutput": {
"suite": false,
"test": false,
"fail": false
}
}
, fs = require("fs")
, consoleOutput = config.consoleOutput || {};
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
/* global Date: true */
var Date = global.Date
/* global setTimeout: true */
, setTimeout = global.setTimeout
/* global setInterval: true */
, setInterval = global.setInterval
/* global clearTimeout: true */
, clearTimeout = global.clearTimeout
/* global clearInterval: true */
, clearInterval = global.clearInterval;
function appendLine(fd, line) {
if (process.env.LOG_XUNIT) {
console.log(line);
}
fs.writeSync(fd, line + "\n", null, 'utf8');
}
/**
* Return cdata escaped CDATA `str`.
*/
function cdata(str) {
return '<![CDATA[' + escape(str) + ']]>';
}
/**
* HTML tag helper.
*/
function writeTag(name, attrs, close, content) {
var end = close ? '/>' : '>'
, pairs = []
, tag;
for (var key in attrs) {
pairs.push(key + '="' + escape(attrs[key]) + '"');
}
tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
if (content){
tag += content + '</' + name + end;
}
return tag;
}
/**
* Output tag for the given `test.`
*/
function writeTest(fd, test) {
var attrs = {
classname: test.parent.fullTitle()
, name: test.title
// , time: test.duration / 1000 //old
,time: test.duration ? test.duration / 1000 : 0 //new
};
if ('failed' === test.state) {
var err = test.err;
appendLine(fd, writeTag('testcase', attrs, false, writeTag('failure', { message: escape(err.message) }, false, cdata(err.stack))));
} else if (test.pending) {
delete attrs.time;
appendLine(fd, writeTag('testcase', attrs, false, writeTag('skipped', {}, true)));
} else {
appendLine(fd, writeTag('testcase', attrs, true) );
}
}
/**
* Initialize a new `SpecXUnitFile` reporter.
*
* @param {Runner} runner
* @api public
*/
function SpecXUnitFile(runner) {
var spec = new Spec(runner);
Base.call(this, runner);
var stats = this.stats
, tests = []
, self = this;
runner.on('suite', function(suite){
if(consoleOutput.suite){
console.log(' ' + suite.title);
}
});
runner.on('test', function(test){
if(consoleOutput.test){
console.log(' ◦ ' + test.title);
}
});
runner.on('pass', function(test){
tests.push(test);
});
runner.on('fail', function(test){
if(consoleOutput.fail){
console.log(' - ' + test.title);
}
tests.push(test);
});
runner.on('pending', function(test) {
tests.push(test);
});
runner.on('end', function(){
var filePath = process.env.XUNIT_FILE || config.file || process.cwd() + "/xunit.xml"
, fd = fs.openSync(filePath, 'w', 0755)
;
appendLine(fd, writeTag('testsuite', {
name: 'Mocha Tests'
, tests: stats.tests
, failures: stats.failures
, errors: stats.failures
, skipped: stats.tests - stats.failures - stats.passes
, timestamp: (new Date()).toUTCString()
, time: stats.duration / 1000
}, false));
tests.forEach(function(test){
writeTest(fd, test);
});
appendLine(fd, '</testsuite>');
fs.closeSync(fd);
});
}
/**
* Inherit from `Base.prototype`.
*/
SpecXUnitFile.prototype = Base.prototype;
// ============ Begin Exports ====================
exports = module.exports = SpecXUnitFile;