167 lines
3.5 KiB
JavaScript
167 lines
3.5 KiB
JavaScript
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var Base = require('./base');
|
|
var utils = require('../utils');
|
|
var inherits = utils.inherits;
|
|
var fs = require('fs');
|
|
var escape = utils.escape;
|
|
var mkdirp = require('mkdirp');
|
|
var path = require('path');
|
|
|
|
/**
|
|
* Save timer references to avoid Sinon interfering (see GH-237).
|
|
*/
|
|
|
|
/* eslint-disable no-unused-vars, no-native-reassign */
|
|
var Date = global.Date;
|
|
var setTimeout = global.setTimeout;
|
|
var setInterval = global.setInterval;
|
|
var clearTimeout = global.clearTimeout;
|
|
var clearInterval = global.clearInterval;
|
|
/* eslint-enable no-unused-vars, no-native-reassign */
|
|
|
|
/**
|
|
* Expose `XUnit`.
|
|
*/
|
|
|
|
exports = module.exports = XUnit;
|
|
|
|
/**
|
|
* Initialize a new `XUnit` reporter.
|
|
*
|
|
* @api public
|
|
* @param {Runner} runner
|
|
*/
|
|
function XUnit(runner, options) {
|
|
Base.call(this, runner);
|
|
|
|
var stats = this.stats;
|
|
var tests = [];
|
|
var self = this;
|
|
|
|
if (options.reporterOptions && options.reporterOptions.output) {
|
|
if (!fs.createWriteStream) {
|
|
throw new Error('file output not supported in browser');
|
|
}
|
|
mkdirp.sync(path.dirname(options.reporterOptions.output));
|
|
self.fileStream = fs.createWriteStream(options.reporterOptions.output);
|
|
}
|
|
|
|
runner.on('pending', function(test) {
|
|
tests.push(test);
|
|
});
|
|
|
|
runner.on('pass', function(test) {
|
|
tests.push(test);
|
|
});
|
|
|
|
runner.on('fail', function(test) {
|
|
tests.push(test);
|
|
});
|
|
|
|
runner.on('end', function() {
|
|
self.write(tag('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) || 0
|
|
}, false));
|
|
|
|
tests.forEach(function(t) {
|
|
self.test(t);
|
|
});
|
|
|
|
self.write('</testsuite>');
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Inherit from `Base.prototype`.
|
|
*/
|
|
inherits(XUnit, Base);
|
|
|
|
/**
|
|
* Override done to close the stream (if it's a file).
|
|
*
|
|
* @param failures
|
|
* @param {Function} fn
|
|
*/
|
|
XUnit.prototype.done = function(failures, fn) {
|
|
if (this.fileStream) {
|
|
this.fileStream.end(function() {
|
|
fn(failures);
|
|
});
|
|
} else {
|
|
fn(failures);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Write out the given line.
|
|
*
|
|
* @param {string} line
|
|
*/
|
|
XUnit.prototype.write = function(line) {
|
|
if (this.fileStream) {
|
|
this.fileStream.write(line + '\n');
|
|
} else if (typeof process === 'object' && process.stdout) {
|
|
process.stdout.write(line + '\n');
|
|
} else {
|
|
console.log(line);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Output tag for the given `test.`
|
|
*
|
|
* @param {Test} test
|
|
*/
|
|
XUnit.prototype.test = function(test) {
|
|
var attrs = {
|
|
classname: test.parent.fullTitle(),
|
|
name: test.title,
|
|
time: (test.duration / 1000) || 0
|
|
};
|
|
|
|
if (test.state === 'failed') {
|
|
var err = test.err;
|
|
this.write(tag('testcase', attrs, false, tag('failure', {}, false, escape(err.message) + '\n' + escape(err.stack))));
|
|
} else if (test.isPending()) {
|
|
this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
|
|
} else {
|
|
this.write(tag('testcase', attrs, true));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* HTML tag helper.
|
|
*
|
|
* @param name
|
|
* @param attrs
|
|
* @param close
|
|
* @param content
|
|
* @return {string}
|
|
*/
|
|
function tag(name, attrs, close, content) {
|
|
var end = close ? '/>' : '>';
|
|
var pairs = [];
|
|
var tag;
|
|
|
|
for (var key in attrs) {
|
|
if (Object.prototype.hasOwnProperty.call(attrs, key)) {
|
|
pairs.push(key + '="' + escape(attrs[key]) + '"');
|
|
}
|
|
}
|
|
|
|
tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
|
|
if (content) {
|
|
tag += content + '</' + name + end;
|
|
}
|
|
return tag;
|
|
}
|