UNPKG

3.65 kBJavaScriptView Raw
1'use strict';
2
3/**
4 * Module dependencies.
5 */
6
7var Base = require('./base');
8var utils = require('../utils');
9var inherits = utils.inherits;
10var fs = require('fs');
11var escape = utils.escape;
12var mkdirp = require('mkdirp');
13var path = require('path');
14
15/**
16 * Save timer references to avoid Sinon interfering (see GH-237).
17 */
18
19/* eslint-disable no-unused-vars, no-native-reassign */
20var Date = global.Date;
21var setTimeout = global.setTimeout;
22var setInterval = global.setInterval;
23var clearTimeout = global.clearTimeout;
24var clearInterval = global.clearInterval;
25/* eslint-enable no-unused-vars, no-native-reassign */
26
27/**
28 * Expose `XUnit`.
29 */
30
31exports = module.exports = XUnit;
32
33/**
34 * Initialize a new `XUnit` reporter.
35 *
36 * @api public
37 * @param {Runner} runner
38 */
39function XUnit (runner, options) {
40 Base.call(this, runner);
41
42 var stats = this.stats;
43 var tests = [];
44 var self = this;
45
46 if (options && options.reporterOptions && options.reporterOptions.output) {
47 if (!fs.createWriteStream) {
48 throw new Error('file output not supported in browser');
49 }
50 mkdirp.sync(path.dirname(options.reporterOptions.output));
51 self.fileStream = fs.createWriteStream(options.reporterOptions.output);
52 }
53
54 runner.on('pending', function (test) {
55 tests.push(test);
56 });
57
58 runner.on('pass', function (test) {
59 tests.push(test);
60 });
61
62 runner.on('fail', function (test) {
63 tests.push(test);
64 });
65
66 runner.on('end', function () {
67 self.write(tag('testsuite', {
68 name: 'Mocha Tests',
69 tests: stats.tests,
70 failures: stats.failures,
71 errors: stats.failures,
72 skipped: stats.tests - stats.failures - stats.passes,
73 timestamp: (new Date()).toUTCString(),
74 time: (stats.duration / 1000) || 0
75 }, false));
76
77 tests.forEach(function (t) {
78 self.test(t);
79 });
80
81 self.write('</testsuite>');
82 });
83}
84
85/**
86 * Inherit from `Base.prototype`.
87 */
88inherits(XUnit, Base);
89
90/**
91 * Override done to close the stream (if it's a file).
92 *
93 * @param failures
94 * @param {Function} fn
95 */
96XUnit.prototype.done = function (failures, fn) {
97 if (this.fileStream) {
98 this.fileStream.end(function () {
99 fn(failures);
100 });
101 } else {
102 fn(failures);
103 }
104};
105
106/**
107 * Write out the given line.
108 *
109 * @param {string} line
110 */
111XUnit.prototype.write = function (line) {
112 if (this.fileStream) {
113 this.fileStream.write(line + '\n');
114 } else if (typeof process === 'object' && process.stdout) {
115 process.stdout.write(line + '\n');
116 } else {
117 console.log(line);
118 }
119};
120
121/**
122 * Output tag for the given `test.`
123 *
124 * @param {Test} test
125 */
126XUnit.prototype.test = function (test) {
127 var attrs = {
128 classname: test.parent.fullTitle(),
129 name: test.title,
130 time: (test.duration / 1000) || 0
131 };
132
133 if (test.state === 'failed') {
134 var err = test.err;
135 this.write(tag('testcase', attrs, false, tag('failure', {}, false, escape(err.message) + '\n' + escape(err.stack))));
136 } else if (test.isPending()) {
137 this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
138 } else {
139 this.write(tag('testcase', attrs, true));
140 }
141};
142
143/**
144 * HTML tag helper.
145 *
146 * @param name
147 * @param attrs
148 * @param close
149 * @param content
150 * @return {string}
151 */
152function tag (name, attrs, close, content) {
153 var end = close ? '/>' : '>';
154 var pairs = [];
155 var tag;
156
157 for (var key in attrs) {
158 if (Object.prototype.hasOwnProperty.call(attrs, key)) {
159 pairs.push(key + '="' + escape(attrs[key]) + '"');
160 }
161 }
162
163 tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
164 if (content) {
165 tag += content + '</' + name + end;
166 }
167 return tag;
168}