123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- 'use strict';
- /**
- * @module XUnit
- */
- /**
- * Module dependencies.
- */
- var Base = require('./base');
- var utils = require('../utils');
- var fs = require('fs');
- var mkdirp = require('mkdirp');
- var path = require('path');
- var errors = require('../errors');
- var createUnsupportedError = errors.createUnsupportedError;
- var constants = require('../runner').constants;
- var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
- var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
- var EVENT_RUN_END = constants.EVENT_RUN_END;
- var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
- var STATE_FAILED = require('../runnable').constants.STATE_FAILED;
- var inherits = utils.inherits;
- var escape = utils.escape;
- /**
- * Save timer references to avoid Sinon interfering (see GH-237).
- */
- var Date = global.Date;
- /**
- * Expose `XUnit`.
- */
- exports = module.exports = XUnit;
- /**
- * Initialize a new `XUnit` reporter.
- *
- * @public
- * @class
- * @memberof Mocha.reporters
- * @extends Mocha.reporters.Base
- * @param {Runner} runner
- */
- function XUnit(runner, options) {
- Base.call(this, runner);
- var stats = this.stats;
- var tests = [];
- var self = this;
- // the name of the test suite, as it will appear in the resulting XML file
- var suiteName;
- // the default name of the test suite if none is provided
- var DEFAULT_SUITE_NAME = 'Mocha Tests';
- if (options && options.reporterOptions) {
- if (options.reporterOptions.output) {
- if (!fs.createWriteStream) {
- throw createUnsupportedError('file output not supported in browser');
- }
- mkdirp.sync(path.dirname(options.reporterOptions.output));
- self.fileStream = fs.createWriteStream(options.reporterOptions.output);
- }
- // get the suite name from the reporter options (if provided)
- suiteName = options.reporterOptions.suiteName;
- }
- // fall back to the default suite name
- suiteName = suiteName || DEFAULT_SUITE_NAME;
- runner.on(EVENT_TEST_PENDING, function(test) {
- tests.push(test);
- });
- runner.on(EVENT_TEST_PASS, function(test) {
- tests.push(test);
- });
- runner.on(EVENT_TEST_FAIL, function(test) {
- tests.push(test);
- });
- runner.once(EVENT_RUN_END, function() {
- self.write(
- tag(
- 'testsuite',
- {
- name: suiteName,
- tests: stats.tests,
- failures: 0,
- 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) {
- Base.useColors = false;
- var attrs = {
- classname: test.parent.fullTitle(),
- name: test.title,
- time: test.duration / 1000 || 0
- };
- if (test.state === STATE_FAILED) {
- var err = test.err;
- var diff =
- Base.hideDiff || !err.actual || !err.expected
- ? ''
- : '\n' + Base.generateDiff(err.actual, err.expected);
- this.write(
- tag(
- 'testcase',
- attrs,
- false,
- tag(
- 'failure',
- {},
- false,
- escape(err.message) + escape(diff) + '\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;
- }
- XUnit.description = 'XUnit-compatible XML output';
|