forked from gazal-k/wct-xunit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplugin.js
228 lines (207 loc) · 7.22 KB
/
plugin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
var Mocha = require('mocha');
var _ = require('lodash');
/**
* Plugin for Web-Component-Tester.
*
* Exports the test results to XUnit XML.
* Uses the Mocha XUnit reporter internally.
*
* WebComponentTester users Mocha reporters but then,
* for some reason, translates the Mocha runner events into
* custom wct events.
*
* This plugin tries to reverse engineer the structure
* and information in those events and pass those
* to a mocha reporter.
*
* It would be possible to modify the WCT to propagate
* all Mocha events, however this implementation is better
* because it does not require WCT modifications.
*
* Mocha test runners will run in a browser, this plugin
* runs on the server (node).
*
* @see web-component-tester/browser/clisocket.js
* @see mocha/lib/reporters/xunit.js
* @see https://github.com/mochajs/mocha/blob/master/lib/runner.js#L36
*
* @param wct
* @param pluginOptions
* @param plugin
*/
module.exports = function(wct, pluginOptions, plugin) {
// ...
console.log('starting wct xunit plugin');
// stores a reporter for each browser instance and test file
var reporters = [];
/**
* stores the last reporter
*
* the test runner iterates serially over all test cases.
* when the browser or the file name change, we create a new
* reporter.
* when the last reporter changes, the end date of the reporter
* is set to correctly calculate the test duration.
*/
var lastReporter;
/**
* Check if we're in a new browser or file.
* If yes, then instantiate a new reporter and set the
* end data for the old reporter.
*/
wct.on('test-start', function(browser, test, stats) {
// initialize the reporter
var newReporter = getReporter(reporters, browser, test);
if (lastReporter && newReporter !== lastReporter) {
// close old reporter
lastReporter.stats.end = new Date();
lastReporter.stats.duration = new Date() - lastReporter.stats.start;
}
});
/**
* Convert the test object to one that the XUnit-Reporter can understand.
* Add the converted object to the reporter and set some test statistics.
*/
wct.on('test-end', function(browser, test, stats) {
var reporter = getReporter(reporters, browser, test);
reporter.stats.tests++;
if (test.state === 'passing') {
reporter.stats.passes++;
} else if (test.state === 'failing') {
reporter.stats.failures++;
}
// adapt call to match reporters expected test structure
reporter.tests.push({
parent: {
// must be a function, matches the className in the xunit xml
fullTitle: function() {
// test[0] == fileName, test[1] == tagName
return browser.browserName + '.' + test.test[1]; // tag-name
}
},
// the last string is the name of the test method
title: test.test[test.test.length - 1],
state: getState(test.state),
// map to a boolean value
isPending: function() {return test.state === 'pending'},
duration: test.duration,
err: test.err
});
});
/**
* All tests in a single browser are finished.
* Write the content of each reporter into a XUnit-compatible XML file.
*
* matcher runner.on('end')
*/
wct.on('browser-end', function(browser, error, stats) {
for (var fileName in reporters[browser.id]) {
if (reporters[browser.id].hasOwnProperty(fileName)) {
var reporter = reporters[browser.id][fileName];
if (!reporter.stats.end) {
// make sure the last reporter is also properly closed
reporter.stats.end = new Date();
reporter.stats.duration = new Date() - reporter.stats.start;
}
var reporterStats = reporter.stats;
reporter.write(tag('testsuite', {
/**
* !!! IMPORTANT
* Jenkins determines new test results depending on the name and timestamp.
* The timestamps can be the same for multiple files if tests run very fast.
* In that case, at least the name attribute must be different, otherwise
* a file may be silently ignored by Jenkins.
*/
name: browser.browserName + '.' + fileName,
tests: reporterStats.tests,
failures: reporterStats.failures,
errors: reporterStats.failures,
skipped: reporterStats.tests - reporterStats.failures - reporterStats.passes,
timestamp: (new Date()).toUTCString(),
time: (reporterStats.duration / 1000) || 0
}, false));
reporter.tests.forEach(function(t) {
reporter.test(t);
});
reporter.write('</testsuite>');
}
}
});
};
function getState(state) {
"use strict";
if (state === 'passing') {
return 'passed';
} else if (state === 'failing') {
return 'failed';
}
else return null;
}
function getReporter(reporters, browser, test) {
var browserReporters = reporters[browser.id];
if (!browserReporters) {
browserReporters = {};
reporters[browser.id] = browserReporters;
}
var fileName = test.test[0];
var fileReporter = browserReporters[fileName];
if (!fileReporter) {
fileReporter = new Mocha.reporters.XUnit({
/**
* Fakes a Mocha test runner.
* The XUnit reporter uses the function to register
* event listeners on the test runner.
*
* Since we only use the XML formatting functionality of
* the XUnit reporter, an empty function to prevent
* runtime errors is sufficient.
*/
on: function() {
}
}, {
reporterOptions: {
output: 'build/test-results/' + fileName + '-' + browser.browserName + '-' + browser.version + '.xml'
}
});
fileReporter.tests = [];
fileReporter.stats.start = new Date();
fileReporter.stats.tests = 0;
fileReporter.stats.passes = 0;
fileReporter.stats.failures = 0;
browserReporters[fileName] = fileReporter;
}
return fileReporter;
}
/**
* TODO copied from XUnit Reporter
*/
/**
* 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;
}
/**
* Return cdata escaped CDATA `str`.
*/
function cdata(str) {
return '<![CDATA[' + escape(str) + ']]>';
}