@testomatio/reporter 1.1.0-beta.mocha-create.9 → 1.1.0-rc.2
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 +11 -17
- package/lib/adapter/codecept.js +81 -6
- package/lib/adapter/cucumber/current.js +15 -8
- package/lib/adapter/cucumber/legacy.js +2 -1
- package/lib/adapter/cypress-plugin/index.js +46 -26
- package/lib/adapter/jest.js +46 -8
- package/lib/adapter/mocha.js +58 -5
- package/lib/adapter/playwright.js +21 -9
- package/lib/bin/startTest.js +2 -1
- package/lib/client.js +55 -46
- package/lib/config.js +5 -0
- package/lib/data-storage.js +203 -0
- package/lib/fileUploader.js +7 -4
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/testomatio.js +2 -1
- package/lib/reporter-functions.js +12 -9
- package/lib/reporter.js +8 -12
- package/lib/services/artifacts.js +57 -0
- package/lib/services/index.js +13 -0
- package/lib/{storages/key-value-storage.js → services/key-values.js} +19 -19
- package/lib/{storages → services}/logger.js +34 -21
- package/lib/utils/utils.js +13 -4
- package/lib/xmlReader.js +106 -104
- package/package.json +2 -2
- package/Changelog.md +0 -367
- package/lib/storages/artifact-storage.js +0 -70
- package/lib/storages/data-storage.js +0 -307
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
const debug = require('debug')('@testomatio/reporter:key-value
|
|
2
|
-
const
|
|
1
|
+
const debug = require('debug')('@testomatio/reporter:services-key-value');
|
|
2
|
+
const { dataStorage } = require('../data-storage');
|
|
3
3
|
|
|
4
4
|
class KeyValueStorage {
|
|
5
|
-
|
|
6
|
-
this.dataStorage = new DataStorage('keyvalue');
|
|
5
|
+
static #instance;
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @returns {KeyValueStorage}
|
|
10
|
+
*/
|
|
11
|
+
static getInstance() {
|
|
12
|
+
if (!this.#instance) {
|
|
13
|
+
this.#instance = new KeyValueStorage();
|
|
11
14
|
}
|
|
15
|
+
return this.#instance;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
/**
|
|
15
19
|
* Stores key-value pair and passes it to reporter
|
|
16
20
|
* @param {{key: string}} keyValue - key-value pair(s) as object
|
|
17
|
-
* @param {*} context
|
|
21
|
+
* @param {*} context - full test title
|
|
18
22
|
*/
|
|
19
23
|
put(keyValue, context = null) {
|
|
20
24
|
if (!keyValue) return;
|
|
21
|
-
|
|
25
|
+
dataStorage.putData('keyvalue', keyValue, context);
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
#isKeyValueObject(smth) {
|
|
@@ -28,13 +32,11 @@ class KeyValueStorage {
|
|
|
28
32
|
/**
|
|
29
33
|
* Returns key-values pairs for the test as object
|
|
30
34
|
* @param {*} context testId or test context from test runner
|
|
31
|
-
* @returns {
|
|
35
|
+
* @returns {{[key: string]: string} | {}} key-values pairs as object, e.g. {priority: 'high', browser: 'chrome'}
|
|
32
36
|
*/
|
|
33
|
-
get(context) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const keyValuesList = this.dataStorage.getData(context);
|
|
37
|
-
if (!keyValuesList || !keyValuesList?.length) return null;
|
|
37
|
+
get(context = null) {
|
|
38
|
+
const keyValuesList = dataStorage.getData('keyvalue', context);
|
|
39
|
+
if (!keyValuesList || !keyValuesList?.length) return {};
|
|
38
40
|
|
|
39
41
|
const keyValues = {};
|
|
40
42
|
for (const keyValue of keyValuesList) {
|
|
@@ -49,10 +51,8 @@ class KeyValueStorage {
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
return
|
|
54
|
+
return keyValues;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
module.exports = new KeyValueStorage();
|
|
58
|
+
module.exports.keyValueStorage = KeyValueStorage.getInstance();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
|
-
const debug = require('debug')('@testomatio/reporter:logger');
|
|
3
|
-
const
|
|
2
|
+
const debug = require('debug')('@testomatio/reporter:services-logger');
|
|
3
|
+
const { dataStorage } = require('../data-storage');
|
|
4
4
|
|
|
5
5
|
const LOG_METHODS = ['assert', 'debug', 'error', 'info', 'log', 'trace', 'warn'];
|
|
6
6
|
const LEVELS = {
|
|
@@ -26,18 +26,26 @@ class Logger {
|
|
|
26
26
|
// set default logger to be used in log, warn, error, etc methods
|
|
27
27
|
#originalUserLogger = { ...console };
|
|
28
28
|
|
|
29
|
-
#
|
|
29
|
+
#isConsoleIntercepted = false;
|
|
30
|
+
|
|
31
|
+
static #instance;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
*
|
|
35
|
+
* @returns {Logger}
|
|
36
|
+
*/
|
|
37
|
+
static getInstance() {
|
|
38
|
+
if (!this.#instance) {
|
|
39
|
+
this.#instance = new Logger();
|
|
40
|
+
}
|
|
41
|
+
return this.#instance;
|
|
42
|
+
}
|
|
30
43
|
|
|
31
44
|
logLevel = process?.env?.LOG_LEVEL?.toUpperCase() || 'ALL';
|
|
32
45
|
|
|
33
46
|
constructor() {
|
|
34
47
|
// intercept console by default
|
|
35
48
|
this.intercept(console);
|
|
36
|
-
|
|
37
|
-
// singleton
|
|
38
|
-
if (!Logger.instance) {
|
|
39
|
-
Logger.instance = this;
|
|
40
|
-
}
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
/**
|
|
@@ -55,17 +63,18 @@ class Logger {
|
|
|
55
63
|
}
|
|
56
64
|
}
|
|
57
65
|
logs = chalk.blue(`> ${logs}`);
|
|
58
|
-
|
|
66
|
+
dataStorage.putData('log', logs);
|
|
59
67
|
}
|
|
60
68
|
|
|
61
69
|
/**
|
|
62
70
|
*
|
|
63
|
-
* @param {
|
|
64
|
-
* @returns
|
|
71
|
+
* @param {string} context testId or test context from test runner
|
|
72
|
+
* @returns {string[]}
|
|
65
73
|
*/
|
|
66
74
|
getLogs(context) {
|
|
67
|
-
const logs =
|
|
68
|
-
|
|
75
|
+
const logs = dataStorage.getData('log', context);
|
|
76
|
+
if (!logs) return [];
|
|
77
|
+
return logs;
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
#stringifyLogs(...args) {
|
|
@@ -124,7 +133,7 @@ class Logger {
|
|
|
124
133
|
logs = this.#stringifyLogs(strings, ...args);
|
|
125
134
|
}
|
|
126
135
|
this.#originalUserLogger.log(logs);
|
|
127
|
-
|
|
136
|
+
dataStorage.putData('log', logs);
|
|
128
137
|
}
|
|
129
138
|
|
|
130
139
|
/**
|
|
@@ -140,8 +149,13 @@ class Logger {
|
|
|
140
149
|
if (severity < LEVELS[this.logLevel]?.severity) return;
|
|
141
150
|
|
|
142
151
|
const logs = this.#stringifyLogs(...argsArray);
|
|
152
|
+
|
|
143
153
|
const colorizedLogs = chalk[LEVELS[level].color](logs);
|
|
144
|
-
|
|
154
|
+
// do not attach logs from testomatio reporter itself
|
|
155
|
+
if (!logs.includes('[TESTOMATIO]')) {
|
|
156
|
+
dataStorage.putData('log', colorizedLogs);
|
|
157
|
+
}
|
|
158
|
+
|
|
145
159
|
try {
|
|
146
160
|
// level.toLowerCase() represents method name (log, warn, error, etc)
|
|
147
161
|
this.#originalUserLogger[level.toLowerCase()](colorizedLogs);
|
|
@@ -187,7 +201,7 @@ class Logger {
|
|
|
187
201
|
/* prevent multiple console interceptions (cause of infinite loop)
|
|
188
202
|
actual only for "console", because its used as default output and is intercepted by default */
|
|
189
203
|
const isUserLoggerConsole = userLogger.toString?.().toLowerCase() === '[object console]';
|
|
190
|
-
if (isUserLoggerConsole &&
|
|
204
|
+
if (isUserLoggerConsole && this.#isConsoleIntercepted) {
|
|
191
205
|
debug(`Try to intercept console, but it is already intercepted`);
|
|
192
206
|
return;
|
|
193
207
|
}
|
|
@@ -239,11 +253,7 @@ class Logger {
|
|
|
239
253
|
}
|
|
240
254
|
}
|
|
241
255
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const logger = new Logger();
|
|
245
|
-
|
|
246
|
-
module.exports = logger;
|
|
256
|
+
module.exports.logger = Logger.getInstance();
|
|
247
257
|
|
|
248
258
|
// TODO: parse passed arguments as {level: 'str', message: 'str'} because some loggers use such syntax;
|
|
249
259
|
// upd: did not face such loggers, but still could be useful
|
|
@@ -288,3 +298,6 @@ Finally, in the test it will look like:
|
|
|
288
298
|
|
|
289
299
|
Parallelization in Cypress is only available if using Cypress Dashboard??
|
|
290
300
|
*/
|
|
301
|
+
|
|
302
|
+
// TODO: add time to logs
|
|
303
|
+
// TODO: add logger name to logs?
|
package/lib/utils/utils.js
CHANGED
|
@@ -141,7 +141,15 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
141
141
|
// remove special chars from title
|
|
142
142
|
if (!lineIndex && opts.title) {
|
|
143
143
|
const title = opts.title.replace(/[([@].*/g, '');
|
|
144
|
-
|
|
144
|
+
|
|
145
|
+
if (opts.lang === 'java') {
|
|
146
|
+
lineIndex = lines.findIndex(l => l.includes(`test${title}`));
|
|
147
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`@DisplayName("${title}`));
|
|
148
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
149
|
+
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
150
|
+
} else {
|
|
151
|
+
lineIndex = lines.findIndex(l => l.includes(title));
|
|
152
|
+
}
|
|
145
153
|
}
|
|
146
154
|
|
|
147
155
|
if (opts.prepend) {
|
|
@@ -294,9 +302,10 @@ const jestHelpers = {
|
|
|
294
302
|
getIdOfCurrentlyRunningTest: () => {
|
|
295
303
|
if (!process.env.JEST_WORKER_ID) return null;
|
|
296
304
|
try {
|
|
305
|
+
// TODO: expect?.getState()?.testPath + ' ' + expect?.getState()?.currentTestName
|
|
297
306
|
// @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
|
|
298
307
|
// eslint-disable-next-line no-undef
|
|
299
|
-
|
|
308
|
+
return expect?.getState()?.currentTestName;
|
|
300
309
|
} catch (e) {
|
|
301
310
|
return null;
|
|
302
311
|
}
|
|
@@ -312,7 +321,6 @@ module.exports = {
|
|
|
312
321
|
fetchFilesFromStackTrace,
|
|
313
322
|
fileSystem,
|
|
314
323
|
getCurrentDateTime,
|
|
315
|
-
jestHelpers,
|
|
316
324
|
specificTestInfo,
|
|
317
325
|
isValidUrl,
|
|
318
326
|
ansiRegExp,
|
|
@@ -320,5 +328,6 @@ module.exports = {
|
|
|
320
328
|
parseSuite,
|
|
321
329
|
humanize,
|
|
322
330
|
removeColorCodes,
|
|
323
|
-
foundedTestLog
|
|
331
|
+
foundedTestLog,
|
|
332
|
+
jestHelpers,
|
|
324
333
|
};
|
package/lib/xmlReader.js
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:xml');
|
|
2
|
-
const path = require(
|
|
2
|
+
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const fs = require(
|
|
5
|
-
const { XMLParser } = require(
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { XMLParser } = require('fast-xml-parser');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('./constants');
|
|
7
|
-
const {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
const {
|
|
8
|
+
fetchFilesFromStackTrace,
|
|
9
|
+
fetchIdFromOutput,
|
|
10
|
+
fetchSourceCode,
|
|
11
|
+
fetchSourceCodeFromStackTrace,
|
|
12
|
+
fetchIdFromCode,
|
|
13
|
+
humanize,
|
|
14
|
+
} = require('./utils/utils');
|
|
14
15
|
const upload = require('./fileUploader');
|
|
15
16
|
const pipesFactory = require('./pipe');
|
|
16
17
|
const adapterFactory = require('./junit-adapter');
|
|
18
|
+
const { TESTOMATIO } = require('./config');
|
|
17
19
|
|
|
18
|
-
const TESTOMATIO_URL = process.env.TESTOMATIO_URL ||
|
|
19
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN
|
|
20
|
+
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
21
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
20
22
|
|
|
21
23
|
const options = {
|
|
22
24
|
ignoreDeclaration: true,
|
|
23
25
|
ignoreAttributes: false,
|
|
24
26
|
alwaysCreateTextNode: false,
|
|
25
|
-
attributeNamePrefix:
|
|
27
|
+
attributeNamePrefix: '',
|
|
26
28
|
parseTagValue: true,
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
const reduceOptions = {};
|
|
30
32
|
|
|
31
33
|
class XmlReader {
|
|
32
|
-
|
|
33
34
|
constructor(opts = {}) {
|
|
34
35
|
this.requestParams = {
|
|
35
36
|
apiKey: opts.apiKey || TESTOMATIO,
|
|
@@ -39,18 +40,18 @@ class XmlReader {
|
|
|
39
40
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
40
41
|
};
|
|
41
42
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
42
|
-
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts)
|
|
43
|
+
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
|
|
43
44
|
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
44
45
|
|
|
45
46
|
this.opts = opts || {};
|
|
46
|
-
this.store = {}
|
|
47
|
+
this.store = {};
|
|
47
48
|
this.pipes = pipesFactory(opts, this.store);
|
|
48
49
|
|
|
49
50
|
this.parser = new XMLParser(options);
|
|
50
|
-
this.tests = []
|
|
51
|
-
this.stats = {}
|
|
51
|
+
this.tests = [];
|
|
52
|
+
this.stats = {};
|
|
52
53
|
this.stats.language = opts.lang?.toLowerCase();
|
|
53
|
-
this.filesToUpload = {}
|
|
54
|
+
this.filesToUpload = {};
|
|
54
55
|
|
|
55
56
|
this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
|
|
56
57
|
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
@@ -81,8 +82,8 @@ class XmlReader {
|
|
|
81
82
|
} else if (jsonResult.assemblies) {
|
|
82
83
|
return this.processXUnit(jsonResult.assemblies);
|
|
83
84
|
} else {
|
|
84
|
-
console.log(jsonResult)
|
|
85
|
-
throw new Error("Format can't be parsed")
|
|
85
|
+
console.log(jsonResult);
|
|
86
|
+
throw new Error("Format can't be parsed");
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
return this.processJUnit(jsonSuite);
|
|
@@ -95,7 +96,7 @@ class XmlReader {
|
|
|
95
96
|
const resultTests = processTestSuite(testsuite);
|
|
96
97
|
|
|
97
98
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
98
|
-
const status =
|
|
99
|
+
const status = failures > 0 || errors > 0 || hasFailures ? 'failed' : 'passed';
|
|
99
100
|
|
|
100
101
|
this.tests = this.tests.concat(resultTests);
|
|
101
102
|
|
|
@@ -134,21 +135,22 @@ class XmlReader {
|
|
|
134
135
|
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
135
136
|
if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
|
|
136
137
|
|
|
137
|
-
const tests =
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
138
|
+
const tests =
|
|
139
|
+
defs.map(td => {
|
|
140
|
+
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
141
|
+
let example = td.name.match(/\((.*?)\)/);
|
|
142
|
+
if (example) example = { ...example[1].split(',') };
|
|
143
|
+
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
144
|
+
const suite_title = suite.pop();
|
|
145
|
+
return {
|
|
146
|
+
title,
|
|
147
|
+
example,
|
|
148
|
+
file: suite.join('/'),
|
|
149
|
+
description: td.Description,
|
|
150
|
+
suite_title,
|
|
151
|
+
id: td.Execution.id,
|
|
152
|
+
};
|
|
153
|
+
}) || [];
|
|
152
154
|
|
|
153
155
|
let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
|
|
154
156
|
if (!Array.isArray(result)) result = [result].filter(d => !!d);
|
|
@@ -159,10 +161,9 @@ class XmlReader {
|
|
|
159
161
|
run_time: parseFloat(td.duration) * 1000,
|
|
160
162
|
status: td.outcome,
|
|
161
163
|
stack: td.Output.StdOut,
|
|
162
|
-
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
|
|
164
|
+
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
|
|
163
165
|
}));
|
|
164
166
|
|
|
165
|
-
|
|
166
167
|
results.forEach(r => {
|
|
167
168
|
const test = tests.find(t => t.id === r.id) || {};
|
|
168
169
|
r.suite_title = test.suite_title;
|
|
@@ -222,7 +223,7 @@ class XmlReader {
|
|
|
222
223
|
|
|
223
224
|
if (testCase.failure) {
|
|
224
225
|
message = testCase.failure.message;
|
|
225
|
-
stack = testCase.failure['stack-trace']
|
|
226
|
+
stack = testCase.failure['stack-trace'];
|
|
226
227
|
}
|
|
227
228
|
if (testCase.reason) {
|
|
228
229
|
message = testCase.reason.message;
|
|
@@ -249,7 +250,6 @@ class XmlReader {
|
|
|
249
250
|
suite_title,
|
|
250
251
|
run_time,
|
|
251
252
|
});
|
|
252
|
-
|
|
253
253
|
});
|
|
254
254
|
});
|
|
255
255
|
});
|
|
@@ -282,12 +282,12 @@ class XmlReader {
|
|
|
282
282
|
passed_count: 0,
|
|
283
283
|
failed_count: 0,
|
|
284
284
|
skipped_count: 0,
|
|
285
|
-
}
|
|
285
|
+
};
|
|
286
286
|
this.tests.forEach(t => {
|
|
287
287
|
this.stats.tests_count++;
|
|
288
288
|
if (t.status === 'passed') this.stats.passed_count++;
|
|
289
289
|
if (t.status === 'failed') this.stats.failed_count++;
|
|
290
|
-
})
|
|
290
|
+
});
|
|
291
291
|
if (this.stats.failed_count) this.stats.status = 'failed';
|
|
292
292
|
|
|
293
293
|
return this.stats;
|
|
@@ -296,7 +296,7 @@ class XmlReader {
|
|
|
296
296
|
fetchSourceCode() {
|
|
297
297
|
this.tests.forEach(t => {
|
|
298
298
|
try {
|
|
299
|
-
const file = this.adapter.getFilePath(t)
|
|
299
|
+
const file = this.adapter.getFilePath(t);
|
|
300
300
|
if (!file) return;
|
|
301
301
|
|
|
302
302
|
if (!this.stats.language) {
|
|
@@ -309,16 +309,16 @@ class XmlReader {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
if (!fs.existsSync(file)) {
|
|
312
|
-
debug('Failed to open file with the source code', file)
|
|
312
|
+
debug('Failed to open file with the source code', file);
|
|
313
313
|
return;
|
|
314
314
|
}
|
|
315
315
|
const contents = fs.readFileSync(file).toString();
|
|
316
|
-
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
|
|
316
|
+
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language });
|
|
317
317
|
if (t.code) debug('Fetched code for test %s', t.title);
|
|
318
|
-
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language })
|
|
318
|
+
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language });
|
|
319
319
|
if (t.test_id) debug('Fetched test id %s for test %s', t.test_id, t.title);
|
|
320
320
|
} catch (err) {
|
|
321
|
-
debug(err)
|
|
321
|
+
debug(err);
|
|
322
322
|
}
|
|
323
323
|
});
|
|
324
324
|
}
|
|
@@ -326,21 +326,22 @@ class XmlReader {
|
|
|
326
326
|
formatTests() {
|
|
327
327
|
this.tests.forEach(t => {
|
|
328
328
|
if (t.file) {
|
|
329
|
-
t.file = t.file.replace(process.cwd() + path.sep, '')
|
|
329
|
+
t.file = t.file.replace(process.cwd() + path.sep, '');
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
this.adapter.formatTest(t)
|
|
332
|
+
this.adapter.formatTest(t);
|
|
333
333
|
|
|
334
334
|
t.title = humanize(t.title);
|
|
335
335
|
});
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
formatErrors() {
|
|
339
|
-
this.tests
|
|
340
|
-
t
|
|
341
|
-
t
|
|
342
|
-
|
|
343
|
-
|
|
339
|
+
this.tests
|
|
340
|
+
.filter(t => !!t.stack)
|
|
341
|
+
.forEach(t => {
|
|
342
|
+
t.stack = this.formatStack(t);
|
|
343
|
+
t.message = this.adapter.formatMessage(t);
|
|
344
|
+
});
|
|
344
345
|
}
|
|
345
346
|
|
|
346
347
|
formatStack(t) {
|
|
@@ -358,7 +359,7 @@ class XmlReader {
|
|
|
358
359
|
async uploadArtifacts() {
|
|
359
360
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
360
361
|
let files = [];
|
|
361
|
-
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f))
|
|
362
|
+
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f));
|
|
362
363
|
files = [...files, ...fetchFilesFromStackTrace(test.stack)];
|
|
363
364
|
|
|
364
365
|
if (!files.length) continue;
|
|
@@ -377,7 +378,7 @@ class XmlReader {
|
|
|
377
378
|
group_title: this.requestParams.group_title,
|
|
378
379
|
};
|
|
379
380
|
|
|
380
|
-
debug(
|
|
381
|
+
debug('Run', runParams);
|
|
381
382
|
|
|
382
383
|
return Promise.all(this.pipes.map(p => p.createRun(runParams)));
|
|
383
384
|
}
|
|
@@ -390,12 +391,10 @@ class XmlReader {
|
|
|
390
391
|
this.formatErrors();
|
|
391
392
|
this.formatTests();
|
|
392
393
|
|
|
393
|
-
debug(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
tests: this.tests,
|
|
398
|
-
})
|
|
394
|
+
debug('Uploading data', {
|
|
395
|
+
...this.stats,
|
|
396
|
+
tests: this.tests,
|
|
397
|
+
});
|
|
399
398
|
|
|
400
399
|
const dataString = {
|
|
401
400
|
...this.stats,
|
|
@@ -410,59 +409,62 @@ class XmlReader {
|
|
|
410
409
|
|
|
411
410
|
module.exports = XmlReader;
|
|
412
411
|
|
|
413
|
-
|
|
414
412
|
function reduceTestCases(prev, item) {
|
|
415
413
|
let testCases = item.testcase;
|
|
416
414
|
if (!testCases) testCases = item['test-case'];
|
|
417
415
|
if (!Array.isArray(testCases)) {
|
|
418
|
-
testCases = [testCases]
|
|
416
|
+
testCases = [testCases];
|
|
419
417
|
}
|
|
420
418
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
421
419
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
422
|
-
testCases
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
420
|
+
testCases
|
|
421
|
+
.filter(t => !!t)
|
|
422
|
+
.forEach(testCaseItem => {
|
|
423
|
+
const file = testCaseItem.file || item.filepath || '';
|
|
424
|
+
|
|
425
|
+
let stack = '';
|
|
426
|
+
let message = '';
|
|
427
|
+
if (testCaseItem.error) stack = testCaseItem.error;
|
|
428
|
+
if (testCaseItem.failure) stack = testCaseItem.failure;
|
|
429
|
+
if (testCaseItem?.failure?.['stack-trace']) stack = testCaseItem.failure['stack-trace'];
|
|
430
|
+
if (testCaseItem?.failure?.message) message = testCaseItem.failure.message;
|
|
431
|
+
if (testCaseItem?.error?.message) message = testCaseItem.error.message;
|
|
432
|
+
|
|
433
|
+
if (testCaseItem.failure && testCaseItem.failure['#text']) stack = testCaseItem.failure['#text'];
|
|
434
|
+
if (testCaseItem.error && testCaseItem.error['#text']) stack = testCaseItem.error['#text'];
|
|
435
|
+
if (!message) message = stack.trim().split('\n')[0];
|
|
436
|
+
|
|
437
|
+
// eslint-disable-next-line
|
|
438
|
+
stack = `${
|
|
439
|
+
testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''
|
|
440
|
+
}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
441
|
+
const testId = fetchIdFromOutput(stack);
|
|
442
|
+
|
|
443
|
+
let status = STATUS.PASSED.toString();
|
|
444
|
+
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
445
|
+
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
446
|
+
|
|
447
|
+
prev.push({
|
|
448
|
+
create: true,
|
|
449
|
+
file,
|
|
450
|
+
stack,
|
|
451
|
+
test_id: testId,
|
|
452
|
+
message,
|
|
453
|
+
line: testCaseItem.lineno,
|
|
454
|
+
// seconds are used in junit reports, but ms are used by testomatio
|
|
455
|
+
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
456
|
+
status,
|
|
457
|
+
title: testCaseItem.name,
|
|
458
|
+
suite_title: reduceOptions.preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname,
|
|
459
|
+
});
|
|
460
|
+
});
|
|
459
461
|
return prev;
|
|
460
462
|
}
|
|
461
463
|
|
|
462
464
|
function processTestSuite(testsuite) {
|
|
463
465
|
if (!testsuite) return [];
|
|
464
|
-
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite)
|
|
465
|
-
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite'])
|
|
466
|
+
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite);
|
|
467
|
+
if (testsuite['test-suite']) return processTestSuite(testsuite['test-suite']);
|
|
466
468
|
|
|
467
469
|
let suites = testsuite;
|
|
468
470
|
if (!Array.isArray(testsuite)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "1.1.0-
|
|
3
|
+
"version": "1.1.0-rc.2",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
6
|
"typings": "typings/index.d.ts",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
|
|
53
53
|
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
|
|
54
54
|
"test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js",
|
|
55
|
-
"test:storage": "npx mocha
|
|
55
|
+
"test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && npx mocha tests-storage/logger.test.js && npx mocha tests-storage/reporter-functions.test.js"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@cucumber/cucumber": "^9.3.0",
|