@testomatio/reporter 1.0.8 → 1.0.9
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/lib/junit-adapter/java.js +27 -9
- package/lib/util.js +46 -0
- package/lib/xmlReader.js +84 -16
- package/package.json +1 -1
|
@@ -10,24 +10,42 @@ class JavaAdapter extends Adapter {
|
|
|
10
10
|
|
|
11
11
|
formatTest(t) {
|
|
12
12
|
const fileParts = t.suite_title.split('.')
|
|
13
|
-
const example = t.title.match(/\[(.*)\]/)?.[1];
|
|
14
|
-
if (example) t.example = { "#": example }
|
|
15
13
|
|
|
16
14
|
t.file = namespaceToFileName(t.suite_title);
|
|
17
15
|
t.title = t.title.split('(')[0];
|
|
16
|
+
|
|
17
|
+
// detect params
|
|
18
|
+
const paramMatches = t.title.match(/\[(.*?)\]/g);
|
|
19
|
+
|
|
20
|
+
if (paramMatches) {
|
|
21
|
+
const params = paramMatches.map((_match, index) => `param${index + 1}`);
|
|
22
|
+
if (params.length === 1) params[0] = 'param';
|
|
23
|
+
let paramIndex = 0;
|
|
24
|
+
|
|
25
|
+
t.title = t.title.replace(/: \[(.*?)\]/g, () => {
|
|
26
|
+
if (params.length < 2) return `\${param}`
|
|
27
|
+
const paramName = params[paramIndex] || `param${paramIndex + 1}`;
|
|
28
|
+
paramIndex++;
|
|
29
|
+
return `\${${paramName}}`;
|
|
30
|
+
});
|
|
31
|
+
const example = {};
|
|
32
|
+
paramMatches.forEach((match, index) => { example[params[index]] = match.replace(/[[\]]/g, '') });
|
|
33
|
+
t.example = example;
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
t.suite_title = fileParts[fileParts.length - 1].replace(/\$/g, ' | ')
|
|
19
37
|
return t;
|
|
20
38
|
}
|
|
21
39
|
|
|
22
|
-
formatStack(t) {
|
|
23
|
-
|
|
40
|
+
// formatStack(t) {
|
|
41
|
+
// const stack = super.formatStack(t);
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
// const file = t.suite_title.split('.');
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
45
|
+
// const fileLine = `at .*${file[file.length - 1]}\.java:(\\d+)` // eslint-disable-line no-useless-escape
|
|
46
|
+
// const regexp = new RegExp(fileLine,"g")
|
|
47
|
+
// return stack.replace(regexp, `${this.opts.javaTests}${path.sep}${namespaceToFileName(t.suite_title)}:$1:`);
|
|
48
|
+
// }
|
|
31
49
|
}
|
|
32
50
|
|
|
33
51
|
function namespaceToFileName(fileName) {
|
package/lib/util.js
CHANGED
|
@@ -192,6 +192,51 @@ const fileSystem = {
|
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
194
|
|
|
195
|
+
const humanize = (text) => {
|
|
196
|
+
text = decamelize(text);
|
|
197
|
+
return text.replace(/_./g, match => ` ${ match.charAt(1).toUpperCase()}`)
|
|
198
|
+
.trim()
|
|
199
|
+
.replace(/^(.)|\s(.)/g, ($1) => $1.toUpperCase()).trim()
|
|
200
|
+
.replace(/\sA\s/g, ' a ') // replace a|the
|
|
201
|
+
.replace(/\sThe\s/g, ' the ') // replace a|the
|
|
202
|
+
.replace(/^Test\s/, '')
|
|
203
|
+
.replace(/^Should\s/, '')
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* From https://github.com/sindresorhus/decamelize/blob/main/index.js
|
|
208
|
+
* @param {*} text
|
|
209
|
+
* @returns
|
|
210
|
+
*/
|
|
211
|
+
const decamelize = (text) => {
|
|
212
|
+
const separator = '_';
|
|
213
|
+
const replacement = `$1${separator}$2`;
|
|
214
|
+
|
|
215
|
+
// Split lowercase sequences followed by uppercase character.
|
|
216
|
+
// `dataForUSACounties` → `data_For_USACounties`
|
|
217
|
+
// `myURLstring → `my_URLstring`
|
|
218
|
+
let decamelized = text.replace(
|
|
219
|
+
/([\p{Lowercase_Letter}\d])(\p{Uppercase_Letter})/gu,
|
|
220
|
+
replacement,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// Lowercase all single uppercase characters. As we
|
|
224
|
+
// want to preserve uppercase sequences, we cannot
|
|
225
|
+
// simply lowercase the separated string at the end.
|
|
226
|
+
// `data_For_USACounties` → `data_for_USACounties`
|
|
227
|
+
decamelized = decamelized.replace(
|
|
228
|
+
/((?<![\p{Uppercase_Letter}\d])[\p{Uppercase_Letter}\d](?![\p{Uppercase_Letter}\d]))/gu,
|
|
229
|
+
$0 => $0.toLowerCase(),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// Remaining uppercase sequences will be separated from lowercase sequences.
|
|
233
|
+
// `data_For_USACounties` → `data_for_USA_counties`
|
|
234
|
+
return decamelized.replace(
|
|
235
|
+
/(\p{Uppercase_Letter}+)(\p{Uppercase_Letter}\p{Lowercase_Letter}+)/gu,
|
|
236
|
+
(_, $1, $2) => $1 + separator + $2.toLowerCase(),
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
|
+
|
|
195
240
|
module.exports = {
|
|
196
241
|
isSameTest,
|
|
197
242
|
fetchSourceCode,
|
|
@@ -204,4 +249,5 @@ module.exports = {
|
|
|
204
249
|
ansiRegExp,
|
|
205
250
|
parseTest,
|
|
206
251
|
parseSuite,
|
|
252
|
+
humanize,
|
|
207
253
|
};
|
package/lib/xmlReader.js
CHANGED
|
@@ -8,7 +8,7 @@ const { fetchFilesFromStackTrace, fetchSourceCode, fetchSourceCodeFromStackTrace
|
|
|
8
8
|
const upload = require('./fileUploader');
|
|
9
9
|
const pipesFactory = require('./pipe');
|
|
10
10
|
const adapterFactory = require('./junit-adapter');
|
|
11
|
-
|
|
11
|
+
const { humanize } = require('./util')
|
|
12
12
|
|
|
13
13
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || "https://app.testomat.io";
|
|
14
14
|
const TESTOMATIO = process.env.TESTOMATIO; // key?
|
|
@@ -35,7 +35,7 @@ class XmlReader {
|
|
|
35
35
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
36
36
|
};
|
|
37
37
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
38
|
-
this.adapter = adapterFactory(
|
|
38
|
+
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts)
|
|
39
39
|
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
40
40
|
|
|
41
41
|
this.opts = opts || {};
|
|
@@ -53,8 +53,12 @@ class XmlReader {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
connectAdapter() {
|
|
56
|
-
if (this.opts.javaTests)
|
|
57
|
-
|
|
56
|
+
if (this.opts.javaTests) {
|
|
57
|
+
this.adapter = adapterFactory('java', this.opts);
|
|
58
|
+
return this.adapter;
|
|
59
|
+
}
|
|
60
|
+
this.adapter = adapterFactory(this.stats.language, this.opts);
|
|
61
|
+
return this.adapter;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
parse(fileName) {
|
|
@@ -70,6 +74,8 @@ class XmlReader {
|
|
|
70
74
|
return this.processTRX(jsonResult);
|
|
71
75
|
} else if (jsonResult['test-run']) {
|
|
72
76
|
return this.processNUnit(jsonResult['test-run']);
|
|
77
|
+
} else if (jsonResult.assemblies) {
|
|
78
|
+
return this.processXUnit(jsonResult.assemblies);
|
|
73
79
|
} else {
|
|
74
80
|
console.log(jsonResult)
|
|
75
81
|
throw new Error("Format can't be parsed")
|
|
@@ -190,6 +196,79 @@ class XmlReader {
|
|
|
190
196
|
};
|
|
191
197
|
}
|
|
192
198
|
|
|
199
|
+
processXUnit(assemblies) {
|
|
200
|
+
const tests = [];
|
|
201
|
+
|
|
202
|
+
assemblies = Array.isArray(assemblies.assembly) ? assemblies.assembly : [assemblies.assembly];
|
|
203
|
+
|
|
204
|
+
assemblies.forEach(assembly => {
|
|
205
|
+
const { collection } = assembly;
|
|
206
|
+
|
|
207
|
+
const suites = Array.isArray(collection) ? collection : [collection];
|
|
208
|
+
|
|
209
|
+
suites.forEach(suite => {
|
|
210
|
+
const { test } = suite;
|
|
211
|
+
if (!test) return;
|
|
212
|
+
const cases = Array.isArray(test) ? test : [test];
|
|
213
|
+
cases.forEach(testCase => {
|
|
214
|
+
const { type, time, result } = testCase;
|
|
215
|
+
|
|
216
|
+
let message = '';
|
|
217
|
+
let stack = '';
|
|
218
|
+
|
|
219
|
+
if (testCase.failure) {
|
|
220
|
+
message = testCase.failure.message;
|
|
221
|
+
stack = testCase.failure['stack-trace']
|
|
222
|
+
}
|
|
223
|
+
if (testCase.reason) {
|
|
224
|
+
message = testCase.reason.message;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
let status = STATUS.PASSED;
|
|
228
|
+
if (result === 'Pass') status = STATUS.PASSED;
|
|
229
|
+
if (result === 'Fail') status = STATUS.FAILED;
|
|
230
|
+
if (result === 'Skip') status = STATUS.SKIPPED;
|
|
231
|
+
|
|
232
|
+
const pathParts = type.split('.');
|
|
233
|
+
const suite_title = pathParts[pathParts.length - 1];
|
|
234
|
+
const file = pathParts.slice(0, -1).join('/');
|
|
235
|
+
const title = testCase.method || testCase.name.split('.').pop();
|
|
236
|
+
const run_time = parseFloat(time) * 1000;
|
|
237
|
+
|
|
238
|
+
tests.push({
|
|
239
|
+
create: true,
|
|
240
|
+
stack,
|
|
241
|
+
message,
|
|
242
|
+
file,
|
|
243
|
+
status,
|
|
244
|
+
title,
|
|
245
|
+
suite_title,
|
|
246
|
+
run_time,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const hasFailures = tests.filter(t => t.status === STATUS.FAILED).length > 0;
|
|
254
|
+
const status = hasFailures ? STATUS.FAILED : STATUS.PASSED;
|
|
255
|
+
|
|
256
|
+
this.tests = tests;
|
|
257
|
+
|
|
258
|
+
debug(tests);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
status,
|
|
262
|
+
create_tests: true,
|
|
263
|
+
name: 'xUnit',
|
|
264
|
+
tests_count: tests.length,
|
|
265
|
+
passed_count: tests.filter(t => t.status === STATUS.PASSED).length,
|
|
266
|
+
failed_count: tests.filter(t => t.status === STATUS.FAILED).length,
|
|
267
|
+
skipped_count: tests.filter(t => t.status === STATUS.SKIPPED).length,
|
|
268
|
+
tests,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
193
272
|
calculateStats() {
|
|
194
273
|
this.stats = {
|
|
195
274
|
...this.stats,
|
|
@@ -241,18 +320,7 @@ class XmlReader {
|
|
|
241
320
|
|
|
242
321
|
this.adapter.formatTest(t)
|
|
243
322
|
|
|
244
|
-
t.title = t.title
|
|
245
|
-
// insert a space before all caps
|
|
246
|
-
.replace(/([A-Z])/g, ' $1')
|
|
247
|
-
// _ chars to spaces
|
|
248
|
-
.replace(/_/g, ' ')
|
|
249
|
-
// uppercase the first character
|
|
250
|
-
.replace(/^(.)|\s(.)/g, $1 => $1.toLowerCase())
|
|
251
|
-
|
|
252
|
-
// remove standard prefixes
|
|
253
|
-
.replace(/^test\s/, '')
|
|
254
|
-
.replace(/^should\s/, '')
|
|
255
|
-
.trim();
|
|
323
|
+
t.title = humanize(t.title);
|
|
256
324
|
});
|
|
257
325
|
}
|
|
258
326
|
|