@wdio/junit-reporter 9.0.0-alpha.78 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +279 -266
- package/package.json +8 -6
- package/build/types.js +0 -1
- package/build/utils.js +0 -67
- /package/{LICENSE-MIT → LICENSE} +0 -0
package/README.md
CHANGED
|
@@ -120,7 +120,7 @@ All test case failures are mapped as JUnit test case errors. A failed test case
|
|
|
120
120
|
|
|
121
121
|
```xml
|
|
122
122
|
<testcase classname="chrome.a_test_case" name="a_test_suite_a_test_case" time="0.372">
|
|
123
|
-
<
|
|
123
|
+
<failure message="Error: some error"/>
|
|
124
124
|
<system-err>
|
|
125
125
|
<![CDATA[
|
|
126
126
|
Error: some assertion failure
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAc,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACxE,OAAO,YAAY,MAAM,gBAAgB,CAAA;AAGzC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAQtD;;;;;GAKG;AACH,cAAM,aAAc,SAAQ,YAAY;IAQhB,OAAO,EAAE,oBAAoB;IAPjD,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,YAAY,CAAC,CAAQ;IAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAQ;IACjC,OAAO,CAAC,cAAc,CAAC,CAAQ;IAC/B,OAAO,CAAC,cAAc,CAAC,CAAK;IAC5B,OAAO,CAAC,kBAAkB,CAAC,CAAK;gBAEZ,OAAO,EAAE,oBAAoB;IAOjD,WAAW,CAAE,SAAS,EAAE,SAAS;IAIjC,WAAW,CAAE,MAAM,EAAE,WAAW;IAKhC,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,4BAA4B;IA4EpC,OAAO,CAAC,kBAAkB;IA4E1B,OAAO,CAAC,cAAc;IA0CtB,OAAO,CAAC,mBAAmB;IAqB3B,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,aAAa;CAMxB;AAED,eAAe,aAAa,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,282 +1,295 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import WDIOReporter from
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
_activeFeature;
|
|
22
|
-
_activeFeatureName;
|
|
23
|
-
constructor(options) {
|
|
24
|
-
super(options);
|
|
25
|
-
this.options = options;
|
|
26
|
-
this._suiteNameRegEx = this.options.suiteNameFormat instanceof RegExp
|
|
27
|
-
? this.options.suiteNameFormat
|
|
28
|
-
: /[^a-zA-Z0-9@]+/; // Reason for ignoring @ is; reporters like wdio-report-portal will fetch the tags from testcase name given as @foo @bar
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import url from "node:url";
|
|
3
|
+
import WDIOReporter from "@wdio/reporter";
|
|
4
|
+
import junit from "junit-report-builder";
|
|
5
|
+
|
|
6
|
+
// src/utils.ts
|
|
7
|
+
import stringify from "json-stringify-safe";
|
|
8
|
+
var OBJLENGTH = 10;
|
|
9
|
+
var ARRLENGTH = 10;
|
|
10
|
+
var STRINGLIMIT = 1e3;
|
|
11
|
+
var STRINGTRUNCATE = 200;
|
|
12
|
+
var limit = function(rawVal) {
|
|
13
|
+
if (!rawVal) {
|
|
14
|
+
return rawVal;
|
|
15
|
+
}
|
|
16
|
+
let val = JSON.parse(stringify(rawVal));
|
|
17
|
+
const type = Object.prototype.toString.call(val);
|
|
18
|
+
if (type === "[object String]") {
|
|
19
|
+
if (val.length > 100 && isBase64(val)) {
|
|
20
|
+
return `[base64] ${val.length} bytes`;
|
|
29
21
|
}
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
if (val.length > STRINGLIMIT) {
|
|
23
|
+
return val.substr(0, STRINGTRUNCATE) + ` ... (${val.length - STRINGTRUNCATE} more bytes)`;
|
|
32
24
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
return val;
|
|
26
|
+
} else if (type === "[object Array]") {
|
|
27
|
+
const length = val.length;
|
|
28
|
+
if (length > ARRLENGTH) {
|
|
29
|
+
val = val.slice(0, ARRLENGTH);
|
|
30
|
+
val.push(`(${length - ARRLENGTH} more items)`);
|
|
36
31
|
}
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
return val.map(limit);
|
|
33
|
+
} else if (type === "[object Object]") {
|
|
34
|
+
const keys = Object.keys(val);
|
|
35
|
+
const removed = [];
|
|
36
|
+
for (let i = 0, l = keys.length; i < l; i++) {
|
|
37
|
+
if (i < OBJLENGTH) {
|
|
38
|
+
val[keys[i]] = limit(val[keys[i]]);
|
|
39
|
+
} else {
|
|
40
|
+
delete val[keys[i]];
|
|
41
|
+
removed.push(keys[i]);
|
|
42
|
+
}
|
|
39
43
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* Add failed hooks to suite as tests.
|
|
43
|
-
*/
|
|
44
|
-
const failedHooks = suite.hooks.filter(hook => hook.error && hook.title.match(/^"(before|after)( all| each)?" hook/));
|
|
45
|
-
failedHooks.forEach(hook => {
|
|
46
|
-
const { title, _duration, error, state } = hook;
|
|
47
|
-
suite.tests.push({
|
|
48
|
-
_duration,
|
|
49
|
-
title,
|
|
50
|
-
error,
|
|
51
|
-
state: state,
|
|
52
|
-
output: []
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
return suite;
|
|
44
|
+
if (removed.length) {
|
|
45
|
+
val._ = keys.length - OBJLENGTH + " more keys: " + JSON.stringify(removed);
|
|
56
46
|
}
|
|
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
|
-
|
|
47
|
+
return val;
|
|
48
|
+
}
|
|
49
|
+
return val;
|
|
50
|
+
};
|
|
51
|
+
function isBase64(str) {
|
|
52
|
+
if (typeof str !== "string") {
|
|
53
|
+
throw new Error("Expected string but received invalid type.");
|
|
54
|
+
}
|
|
55
|
+
const len = str.length;
|
|
56
|
+
const notBase64 = /[^A-Z0-9+/=]/i;
|
|
57
|
+
if (!len || len % 4 !== 0 || notBase64.test(str)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const firstPaddingChar = str.indexOf("=");
|
|
61
|
+
return firstPaddingChar === -1 || firstPaddingChar === len - 1 || firstPaddingChar === len - 2 && str[len - 1] === "=";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/index.ts
|
|
65
|
+
var ansiRegex = new RegExp([
|
|
66
|
+
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
|
67
|
+
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"
|
|
68
|
+
].join("|"), "g");
|
|
69
|
+
var JunitReporter = class extends WDIOReporter {
|
|
70
|
+
constructor(options) {
|
|
71
|
+
super(options);
|
|
72
|
+
this.options = options;
|
|
73
|
+
this._suiteNameRegEx = this.options.suiteNameFormat instanceof RegExp ? this.options.suiteNameFormat : /[^a-zA-Z0-9@]+/;
|
|
74
|
+
}
|
|
75
|
+
_suiteNameRegEx;
|
|
76
|
+
_packageName;
|
|
77
|
+
_suiteTitleLabel;
|
|
78
|
+
_fileNameLabel;
|
|
79
|
+
_activeFeature;
|
|
80
|
+
_activeFeatureName;
|
|
81
|
+
onTestRetry(testStats) {
|
|
82
|
+
testStats.skip("Retry");
|
|
83
|
+
}
|
|
84
|
+
onRunnerEnd(runner) {
|
|
85
|
+
const xml = this._buildJunitXml(runner);
|
|
86
|
+
this.write(xml);
|
|
87
|
+
}
|
|
88
|
+
_prepareName(name = "Skipped test") {
|
|
89
|
+
return name.split(this._suiteNameRegEx).filter(
|
|
90
|
+
(item) => item && item.length
|
|
91
|
+
).join(" ");
|
|
92
|
+
}
|
|
93
|
+
_addFailedHooks(suite) {
|
|
94
|
+
const failedHooks = suite.hooks.filter((hook) => hook.error && hook.title.match(/^"(before|after)( all| each)?" hook/));
|
|
95
|
+
failedHooks.forEach((hook) => {
|
|
96
|
+
const { title, _duration, error, state } = hook;
|
|
97
|
+
suite.tests.push({
|
|
98
|
+
_duration,
|
|
99
|
+
title,
|
|
100
|
+
error,
|
|
101
|
+
state,
|
|
102
|
+
output: []
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
return suite;
|
|
106
|
+
}
|
|
107
|
+
_addCucumberFeatureToBuilder(builder, runner, specFileName, suite) {
|
|
108
|
+
const featureName = !this.options.suiteNameFormat || this.options.suiteNameFormat instanceof RegExp ? this._prepareName(suite.title) : this.options.suiteNameFormat({ name: this.options.suiteNameFormat.name, suite });
|
|
109
|
+
const filePath = specFileName.replace(process.cwd(), ".");
|
|
110
|
+
if (suite.type === "feature") {
|
|
111
|
+
const feature = builder.testSuite().name(featureName).timestamp(suite.start).time(suite._duration / 1e3).property("specId", 0).property(this._suiteTitleLabel, suite.title).property("capabilities", runner.sanitizedCapabilities).property(this._fileNameLabel, filePath);
|
|
112
|
+
this._activeFeature = feature;
|
|
113
|
+
this._activeFeatureName = featureName;
|
|
114
|
+
} else if (this._activeFeature) {
|
|
115
|
+
let scenario = suite;
|
|
116
|
+
const testName = this._prepareName(suite.title);
|
|
117
|
+
const classNameFormat = this.options.classNameFormat ? this.options.classNameFormat({ packageName: this._packageName, activeFeatureName: this._activeFeatureName }) : `${this._packageName}.${this._activeFeatureName}`;
|
|
118
|
+
const testCase = this._activeFeature.testCase().className(classNameFormat).name(`${testName}`).time(scenario._duration / 1e3);
|
|
119
|
+
if (this.options.addFileAttribute) {
|
|
120
|
+
testCase.file(filePath);
|
|
121
|
+
}
|
|
122
|
+
scenario = this._addFailedHooks(scenario);
|
|
123
|
+
let stepsOutput = "";
|
|
124
|
+
let isFailing = false;
|
|
125
|
+
for (const stepKey of Object.keys(scenario.tests)) {
|
|
126
|
+
if (stepKey === "undefined") {
|
|
127
|
+
continue;
|
|
127
128
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const test = suite.tests[testKey];
|
|
149
|
-
const testName = this._prepareName(test.title);
|
|
150
|
-
const classNameFormat = this.options.classNameFormat
|
|
151
|
-
? this.options.classNameFormat({ packageName: this._packageName, suite })
|
|
152
|
-
: `${this._packageName}.${(suite.fullTitle || suite.title).replace(/\s/g, '_')}`;
|
|
153
|
-
const testCase = testSuite.testCase()
|
|
154
|
-
.className(classNameFormat)
|
|
155
|
-
.name(testName)
|
|
156
|
-
.time(test._duration / 1000);
|
|
157
|
-
if (this.options.addFileAttribute) {
|
|
158
|
-
testCase.file(filePath);
|
|
159
|
-
}
|
|
160
|
-
if (test.state === 'pending' || test.state === 'skipped') {
|
|
161
|
-
testCase.skipped();
|
|
162
|
-
if (test.error) {
|
|
163
|
-
testCase.standardError(`\n${test.error.stack?.replace(ansiRegex, '')}\n`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
else if (test.state === 'failed') {
|
|
167
|
-
if (test.error) {
|
|
168
|
-
if (test.error.message) {
|
|
169
|
-
test.error.message = test.error.message.replace(ansiRegex, '');
|
|
170
|
-
}
|
|
171
|
-
if (this.options.errorOptions) {
|
|
172
|
-
const errorOptions = this.options.errorOptions;
|
|
173
|
-
for (const key of Object.keys(errorOptions)) {
|
|
174
|
-
testCase[key](test.error[errorOptions[key]]);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
// default
|
|
179
|
-
testCase.error(test.error.message);
|
|
180
|
-
}
|
|
181
|
-
testCase.standardError(`\n${test.error.stack?.replace(ansiRegex, '')}\n`);
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
testCase.error();
|
|
185
|
-
}
|
|
186
|
-
testCase.failure();
|
|
187
|
-
}
|
|
188
|
-
const output = this._getStandardOutput(test);
|
|
189
|
-
if (output) {
|
|
190
|
-
testCase.standardOutput(`\n${output}\n`);
|
|
129
|
+
let stepEmoji = "\u2705";
|
|
130
|
+
const step = scenario.tests[stepKey];
|
|
131
|
+
if (step.state === "pending" || step.state === "skipped") {
|
|
132
|
+
if (!isFailing) {
|
|
133
|
+
testCase.skipped();
|
|
134
|
+
}
|
|
135
|
+
stepEmoji = "\u26A0\uFE0F";
|
|
136
|
+
} else if (step.state === "failed") {
|
|
137
|
+
if (step.error) {
|
|
138
|
+
if (this.options.errorOptions) {
|
|
139
|
+
const errorOptions = this.options.errorOptions;
|
|
140
|
+
for (const key of Object.keys(errorOptions)) {
|
|
141
|
+
testCase[key](
|
|
142
|
+
step.error ? step.error[errorOptions[key]] : null
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
testCase.failure(step.error.message);
|
|
191
147
|
}
|
|
148
|
+
testCase.standardError(`
|
|
149
|
+
${step.error.stack}
|
|
150
|
+
`);
|
|
151
|
+
} else {
|
|
152
|
+
testCase.failure();
|
|
153
|
+
}
|
|
154
|
+
isFailing = true;
|
|
155
|
+
stepEmoji = "\u2757";
|
|
192
156
|
}
|
|
193
|
-
|
|
157
|
+
const output = this._getStandardOutput(step);
|
|
158
|
+
stepsOutput += output ? stepEmoji + " " + step.title : stepEmoji + " " + step.title + "\n" + output;
|
|
159
|
+
}
|
|
160
|
+
testCase.standardOutput(`
|
|
161
|
+
${stepsOutput}
|
|
162
|
+
`);
|
|
194
163
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
164
|
+
return builder;
|
|
165
|
+
}
|
|
166
|
+
_addSuiteToBuilder(builder, runner, specFileName, suite) {
|
|
167
|
+
const filePath = specFileName.replace(process.cwd(), ".");
|
|
168
|
+
const suiteName = !this.options.suiteNameFormat || this.options.suiteNameFormat instanceof RegExp ? this._prepareName(suite.title) : this.options.suiteNameFormat({ name: this.options.suiteNameFormat.name, suite });
|
|
169
|
+
const testSuite = builder.testSuite().name(suiteName).timestamp(suite.start).time(suite._duration / 1e3).property("specId", 0).property(this._suiteTitleLabel, suite.title).property("capabilities", runner.sanitizedCapabilities).property(this._fileNameLabel, filePath);
|
|
170
|
+
suite = this._addFailedHooks(suite);
|
|
171
|
+
for (const testKey of Object.keys(suite.tests)) {
|
|
172
|
+
if (testKey === "undefined") {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const test = suite.tests[testKey];
|
|
176
|
+
const testName = this._prepareName(test.title);
|
|
177
|
+
const classNameFormat = this.options.classNameFormat ? this.options.classNameFormat({ packageName: this._packageName, suite }) : `${this._packageName}.${(suite.fullTitle || suite.title).replace(/\s/g, "_")}`;
|
|
178
|
+
const testCase = testSuite.testCase().className(classNameFormat).name(testName).time(test._duration / 1e3);
|
|
179
|
+
if (this.options.addFileAttribute) {
|
|
180
|
+
testCase.file(filePath);
|
|
181
|
+
}
|
|
182
|
+
if (test.state === "pending" || test.state === "skipped") {
|
|
183
|
+
testCase.skipped();
|
|
184
|
+
if (test.error) {
|
|
185
|
+
testCase.standardError(`
|
|
186
|
+
${test.error.stack?.replace(ansiRegex, "")}
|
|
187
|
+
`);
|
|
213
188
|
}
|
|
214
|
-
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
runner.specs.forEach((specFileName) => {
|
|
225
|
-
if (isCucumberFrameworkRunner) {
|
|
226
|
-
this._buildOrderedReport(builder, runner, specFileName, 'feature', isCucumberFrameworkRunner);
|
|
227
|
-
this._buildOrderedReport(builder, runner, specFileName, 'scenario', isCucumberFrameworkRunner);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
this._buildOrderedReport(builder, runner, specFileName, '', isCucumberFrameworkRunner);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
return builder.build();
|
|
234
|
-
}
|
|
235
|
-
_buildOrderedReport(builder, runner, specFileName, type, isCucumberFrameworkRunner) {
|
|
236
|
-
for (const suiteKey of Object.keys(this.suites)) {
|
|
237
|
-
/**
|
|
238
|
-
* ignore root before all
|
|
239
|
-
*/
|
|
240
|
-
/* istanbul ignore if */
|
|
241
|
-
if (suiteKey.match(/^"before all"/)) {
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
const suite = this.suites[suiteKey];
|
|
245
|
-
const sameSpecFileName = this._sameFileName(specFileName, suite.file);
|
|
246
|
-
if (isCucumberFrameworkRunner && suite.type === type && sameSpecFileName) {
|
|
247
|
-
builder = this._addCucumberFeatureToBuilder(builder, runner, specFileName, suite);
|
|
248
|
-
}
|
|
249
|
-
else if (!isCucumberFrameworkRunner && sameSpecFileName) {
|
|
250
|
-
builder = this._addSuiteToBuilder(builder, runner, specFileName, suite);
|
|
189
|
+
} else if (test.state === "failed") {
|
|
190
|
+
if (test.error) {
|
|
191
|
+
if (test.error.message) {
|
|
192
|
+
test.error.message = test.error.message.replace(ansiRegex, "");
|
|
193
|
+
}
|
|
194
|
+
if (this.options.errorOptions) {
|
|
195
|
+
const errorOptions = this.options.errorOptions;
|
|
196
|
+
for (const key of Object.keys(errorOptions)) {
|
|
197
|
+
testCase[key](test.error[errorOptions[key]]);
|
|
251
198
|
}
|
|
199
|
+
} else {
|
|
200
|
+
testCase.failure(test.error.message);
|
|
201
|
+
}
|
|
202
|
+
testCase.standardError(`
|
|
203
|
+
${test.error.stack?.replace(ansiRegex, "")}
|
|
204
|
+
`);
|
|
205
|
+
} else {
|
|
206
|
+
testCase.failure();
|
|
252
207
|
}
|
|
253
|
-
|
|
208
|
+
}
|
|
209
|
+
const output = this._getStandardOutput(test);
|
|
210
|
+
if (output) {
|
|
211
|
+
testCase.standardOutput(`
|
|
212
|
+
${output}
|
|
213
|
+
`);
|
|
214
|
+
}
|
|
254
215
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
216
|
+
return builder;
|
|
217
|
+
}
|
|
218
|
+
_buildJunitXml(runner) {
|
|
219
|
+
const builder = junit.newBuilder();
|
|
220
|
+
if (runner.config.hostname !== void 0 && runner.config.hostname.indexOf("browserstack") > -1) {
|
|
221
|
+
const browserstackSanitizedCapabilities = [
|
|
222
|
+
// @ts-expect-error capability only exists when running on BrowserStack
|
|
223
|
+
runner.capabilities.device,
|
|
224
|
+
// @ts-expect-error capability only exists when running on BrowserStack
|
|
225
|
+
runner.capabilities.os,
|
|
226
|
+
// @ts-expect-error capability only exists when running on BrowserStack
|
|
227
|
+
(runner.capabilities.os_version || "").replace(/\./g, "_")
|
|
228
|
+
].filter(Boolean).map((capability) => capability.toLowerCase()).join(".").replace(/ /g, "") || runner.sanitizedCapabilities;
|
|
229
|
+
this._packageName = this.options.packageName || browserstackSanitizedCapabilities;
|
|
230
|
+
} else {
|
|
231
|
+
this._packageName = this.options.packageName || runner.sanitizedCapabilities;
|
|
271
232
|
}
|
|
272
|
-
|
|
273
|
-
|
|
233
|
+
const isCucumberFrameworkRunner = runner.config.framework === "cucumber";
|
|
234
|
+
if (isCucumberFrameworkRunner) {
|
|
235
|
+
this._packageName = `CucumberJUnitReport-${this._packageName}`;
|
|
236
|
+
this._suiteTitleLabel = "featureName";
|
|
237
|
+
this._fileNameLabel = "featureFile";
|
|
238
|
+
} else {
|
|
239
|
+
this._suiteTitleLabel = "suiteName";
|
|
240
|
+
this._fileNameLabel = "file";
|
|
274
241
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
242
|
+
runner.specs.forEach((specFileName) => {
|
|
243
|
+
if (isCucumberFrameworkRunner) {
|
|
244
|
+
this._buildOrderedReport(builder, runner, specFileName, "feature", isCucumberFrameworkRunner);
|
|
245
|
+
this._buildOrderedReport(builder, runner, specFileName, "scenario", isCucumberFrameworkRunner);
|
|
246
|
+
} else {
|
|
247
|
+
this._buildOrderedReport(builder, runner, specFileName, "", isCucumberFrameworkRunner);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
return builder.build();
|
|
251
|
+
}
|
|
252
|
+
_buildOrderedReport(builder, runner, specFileName, type, isCucumberFrameworkRunner) {
|
|
253
|
+
for (const suiteKey of Object.keys(this.suites)) {
|
|
254
|
+
if (suiteKey.match(/^"before all"/)) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const suite = this.suites[suiteKey];
|
|
258
|
+
const sameSpecFileName = this._sameFileName(specFileName, suite.file);
|
|
259
|
+
if (isCucumberFrameworkRunner && suite.type === type && sameSpecFileName) {
|
|
260
|
+
builder = this._addCucumberFeatureToBuilder(builder, runner, specFileName, suite);
|
|
261
|
+
} else if (!isCucumberFrameworkRunner && sameSpecFileName) {
|
|
262
|
+
builder = this._addSuiteToBuilder(builder, runner, specFileName, suite);
|
|
263
|
+
}
|
|
280
264
|
}
|
|
281
|
-
|
|
282
|
-
|
|
265
|
+
return builder;
|
|
266
|
+
}
|
|
267
|
+
_getStandardOutput(test) {
|
|
268
|
+
const standardOutput = [];
|
|
269
|
+
test.output.forEach((data) => {
|
|
270
|
+
switch (data.type) {
|
|
271
|
+
case "command":
|
|
272
|
+
standardOutput.push(
|
|
273
|
+
data.method ? `COMMAND: ${data.method.toUpperCase()} ${data.endpoint.replace(":sessionId", data.sessionId)} - ${this._format(data.body)}` : `COMMAND: ${data.command} - ${this._format(data.params)}`
|
|
274
|
+
);
|
|
275
|
+
break;
|
|
276
|
+
case "result":
|
|
277
|
+
standardOutput.push(`RESULT: ${this._format(data.body)}`);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
return standardOutput.length ? standardOutput.join("\n") : "";
|
|
282
|
+
}
|
|
283
|
+
_format(val) {
|
|
284
|
+
return JSON.stringify(limit(val));
|
|
285
|
+
}
|
|
286
|
+
_sameFileName(file1, file2) {
|
|
287
|
+
file1 = file1?.startsWith("file://") ? url.fileURLToPath(file1) : file1;
|
|
288
|
+
file2 = file2?.startsWith("file://") ? url.fileURLToPath(file2) : file2;
|
|
289
|
+
return file1 === file2;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
var src_default = JunitReporter;
|
|
293
|
+
export {
|
|
294
|
+
src_default as default
|
|
295
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wdio/junit-reporter",
|
|
3
|
-
"version": "9.0.0
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "A WebdriverIO reporter that creates Jenkins compatible XML based JUnit reports",
|
|
5
5
|
"author": "Christian Bromann <mail@bromann.dev>",
|
|
6
6
|
"homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-junit-reporter",
|
|
@@ -26,13 +26,15 @@
|
|
|
26
26
|
"type": "module",
|
|
27
27
|
"types": "./build/index.d.ts",
|
|
28
28
|
"exports": {
|
|
29
|
-
".":
|
|
30
|
-
|
|
29
|
+
".": {
|
|
30
|
+
"import": "./build/index.js",
|
|
31
|
+
"types": "./build/index.d.ts"
|
|
32
|
+
}
|
|
31
33
|
},
|
|
32
34
|
"typeScriptVersion": "3.8.3",
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"@wdio/reporter": "9.0.0
|
|
35
|
-
"@wdio/types": "9.0.0
|
|
36
|
+
"@wdio/reporter": "9.0.0",
|
|
37
|
+
"@wdio/types": "9.0.0",
|
|
36
38
|
"json-stringify-safe": "^5.0.1",
|
|
37
39
|
"junit-report-builder": "^3.0.0"
|
|
38
40
|
},
|
|
@@ -42,5 +44,5 @@
|
|
|
42
44
|
"publishConfig": {
|
|
43
45
|
"access": "public"
|
|
44
46
|
},
|
|
45
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "957693463371a4cb329395dcdbce8fb0c930ab93"
|
|
46
48
|
}
|
package/build/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/build/utils.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import stringify from 'json-stringify-safe';
|
|
2
|
-
const OBJLENGTH = 10;
|
|
3
|
-
const ARRLENGTH = 10;
|
|
4
|
-
const STRINGLIMIT = 1000;
|
|
5
|
-
const STRINGTRUNCATE = 200;
|
|
6
|
-
export const limit = function (rawVal) {
|
|
7
|
-
if (!rawVal) {
|
|
8
|
-
return rawVal;
|
|
9
|
-
}
|
|
10
|
-
// Ensure we're working with a copy
|
|
11
|
-
let val = JSON.parse(stringify(rawVal));
|
|
12
|
-
const type = Object.prototype.toString.call(val);
|
|
13
|
-
if (type === '[object String]') {
|
|
14
|
-
if (val.length > 100 && isBase64(val)) {
|
|
15
|
-
return `[base64] ${val.length} bytes`;
|
|
16
|
-
}
|
|
17
|
-
if (val.length > STRINGLIMIT) {
|
|
18
|
-
return val.substr(0, STRINGTRUNCATE) + ` ... (${val.length - STRINGTRUNCATE} more bytes)`;
|
|
19
|
-
}
|
|
20
|
-
return val;
|
|
21
|
-
}
|
|
22
|
-
else if (type === '[object Array]') {
|
|
23
|
-
const length = val.length;
|
|
24
|
-
if (length > ARRLENGTH) {
|
|
25
|
-
val = val.slice(0, ARRLENGTH);
|
|
26
|
-
val.push(`(${length - ARRLENGTH} more items)`);
|
|
27
|
-
}
|
|
28
|
-
return val.map(limit);
|
|
29
|
-
}
|
|
30
|
-
else if (type === '[object Object]') {
|
|
31
|
-
const keys = Object.keys(val);
|
|
32
|
-
const removed = [];
|
|
33
|
-
for (let i = 0, l = keys.length; i < l; i++) {
|
|
34
|
-
if (i < OBJLENGTH) {
|
|
35
|
-
val[keys[i]] = limit(val[keys[i]]);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
delete val[keys[i]];
|
|
39
|
-
removed.push(keys[i]);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (removed.length) {
|
|
43
|
-
val._ = (keys.length - OBJLENGTH) + ' more keys: ' + JSON.stringify(removed);
|
|
44
|
-
}
|
|
45
|
-
return val;
|
|
46
|
-
}
|
|
47
|
-
return val;
|
|
48
|
-
};
|
|
49
|
-
/**
|
|
50
|
-
* checks if provided string is Base64
|
|
51
|
-
* @param {string} str string to check
|
|
52
|
-
* @return {boolean} `true` if the provided string is Base64
|
|
53
|
-
*/
|
|
54
|
-
export function isBase64(str) {
|
|
55
|
-
if (typeof str !== 'string') {
|
|
56
|
-
throw new Error('Expected string but received invalid type.');
|
|
57
|
-
}
|
|
58
|
-
const len = str.length;
|
|
59
|
-
const notBase64 = /[^A-Z0-9+/=]/i;
|
|
60
|
-
if (!len || len % 4 !== 0 || notBase64.test(str)) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
const firstPaddingChar = str.indexOf('=');
|
|
64
|
-
return (firstPaddingChar === -1 ||
|
|
65
|
-
firstPaddingChar === len - 1 ||
|
|
66
|
-
(firstPaddingChar === len - 2 && str[len - 1] === '='));
|
|
67
|
-
}
|
/package/{LICENSE-MIT → LICENSE}
RENAMED
|
File without changes
|