@testomatio/reporter 2.0.0-beta-esm → 2.0.0-beta.1-xml
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/adapter/codecept.d.ts +2 -0
- package/lib/adapter/codecept.js +31 -26
- package/lib/adapter/cucumber/current.d.ts +14 -0
- package/lib/adapter/cucumber/legacy.d.ts +0 -0
- package/lib/adapter/cucumber.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.js +10 -10
- package/lib/adapter/jasmine.d.ts +11 -0
- package/lib/adapter/jest.d.ts +13 -0
- package/lib/adapter/mocha.d.ts +2 -0
- package/lib/adapter/mocha.js +4 -4
- package/lib/adapter/nightwatch.d.ts +4 -0
- package/lib/adapter/nightwatch.js +80 -0
- package/lib/adapter/playwright.d.ts +14 -0
- package/lib/adapter/playwright.js +58 -33
- package/lib/adapter/vitest.d.ts +35 -0
- package/lib/adapter/vitest.js +6 -6
- package/lib/adapter/webdriver.d.ts +24 -0
- package/lib/adapter/webdriver.js +51 -14
- package/lib/bin/cli.d.ts +2 -0
- package/lib/bin/cli.js +250 -0
- package/lib/bin/reportXml.d.ts +2 -0
- package/lib/bin/reportXml.js +15 -11
- package/lib/bin/startTest.d.ts +2 -0
- package/lib/bin/startTest.js +12 -7
- package/lib/bin/uploadArtifacts.d.ts +2 -0
- package/lib/bin/uploadArtifacts.js +82 -0
- package/lib/client.d.ts +76 -0
- package/lib/client.js +128 -53
- package/lib/config.d.ts +1 -0
- package/lib/config.js +2 -2
- package/lib/constants.d.ts +25 -0
- package/lib/constants.js +5 -1
- package/lib/data-storage.d.ts +34 -0
- package/lib/data-storage.js +19 -9
- package/lib/junit-adapter/adapter.d.ts +9 -0
- package/lib/junit-adapter/csharp.d.ts +5 -0
- package/lib/junit-adapter/csharp.js +11 -1
- package/lib/junit-adapter/index.d.ts +3 -0
- package/lib/junit-adapter/java.d.ts +5 -0
- package/lib/junit-adapter/javascript.d.ts +4 -0
- package/lib/junit-adapter/python.d.ts +5 -0
- package/lib/junit-adapter/ruby.d.ts +4 -0
- package/lib/output.d.ts +11 -0
- package/lib/package.json +3 -1
- package/lib/pipe/bitbucket.d.ts +23 -0
- package/lib/pipe/bitbucket.js +19 -9
- package/lib/pipe/csv.d.ts +47 -0
- package/lib/pipe/csv.js +2 -2
- package/lib/pipe/debug.d.ts +29 -0
- package/lib/pipe/debug.js +108 -0
- package/lib/pipe/github.d.ts +30 -0
- package/lib/pipe/github.js +37 -5
- package/lib/pipe/gitlab.d.ts +23 -0
- package/lib/pipe/gitlab.js +2 -3
- package/lib/pipe/html.d.ts +35 -0
- package/lib/pipe/html.js +9 -4
- package/lib/pipe/index.d.ts +1 -0
- package/lib/pipe/index.js +20 -10
- package/lib/pipe/testomatio.d.ts +70 -0
- package/lib/pipe/testomatio.js +54 -39
- package/lib/reporter-functions.d.ts +34 -0
- package/lib/reporter-functions.js +17 -7
- package/lib/reporter.d.ts +232 -0
- package/lib/reporter.js +19 -33
- package/lib/services/artifacts.d.ts +33 -0
- package/lib/services/index.d.ts +9 -0
- package/lib/services/key-values.d.ts +27 -0
- package/lib/services/key-values.js +1 -1
- package/lib/services/logger.d.ts +64 -0
- package/lib/services/logger.js +1 -2
- package/lib/template/testomatio.hbs +651 -1366
- package/lib/uploader.d.ts +60 -0
- package/lib/uploader.js +312 -0
- package/lib/utils/pipe_utils.d.ts +41 -0
- package/lib/utils/pipe_utils.js +3 -5
- package/lib/utils/utils.d.ts +47 -0
- package/lib/utils/utils.js +99 -12
- package/lib/xmlReader.d.ts +92 -0
- package/lib/xmlReader.js +64 -25
- package/package.json +19 -13
- package/src/adapter/codecept.js +30 -26
- package/src/adapter/cypress-plugin/index.js +5 -5
- package/src/adapter/mocha.cjs +1 -1
- package/src/adapter/mocha.js +4 -4
- package/src/adapter/nightwatch.js +88 -0
- package/src/adapter/playwright.js +59 -31
- package/src/adapter/vitest.js +6 -6
- package/src/adapter/webdriver.js +42 -12
- package/src/bin/cli.js +303 -0
- package/src/bin/reportXml.js +19 -9
- package/src/bin/startTest.js +9 -4
- package/src/bin/uploadArtifacts.js +91 -0
- package/src/client.js +137 -57
- package/src/config.js +2 -2
- package/src/constants.js +5 -1
- package/src/data-storage.js +2 -2
- package/src/junit-adapter/csharp.js +13 -1
- package/src/pipe/bitbucket.js +2 -2
- package/src/pipe/csv.js +3 -3
- package/src/pipe/debug.js +104 -0
- package/src/pipe/github.js +3 -5
- package/src/pipe/gitlab.js +6 -7
- package/src/pipe/html.js +14 -7
- package/src/pipe/index.js +5 -7
- package/src/pipe/testomatio.js +75 -76
- package/src/reporter-functions.js +18 -7
- package/src/reporter.cjs_decprecated +21 -0
- package/src/reporter.js +20 -11
- package/src/services/key-values.js +1 -1
- package/src/services/logger.js +5 -4
- package/src/template/testomatio.hbs +651 -1366
- package/src/uploader.js +371 -0
- package/src/utils/pipe_utils.js +4 -12
- package/src/utils/utils.js +64 -15
- package/src/xmlReader.js +76 -26
- package/lib/adapter/jasmine/jasmine.js +0 -63
- package/lib/adapter/mocha/mocha.js +0 -125
- package/lib/fileUploader.js +0 -245
- package/lib/utils/chalk.js +0 -10
- package/src/fileUploader.js +0 -307
- package/src/reporter.cjs +0 -22
- package/src/utils/chalk.js +0 -13
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
export default XmlReader;
|
|
2
|
+
declare class XmlReader {
|
|
3
|
+
constructor(opts?: {});
|
|
4
|
+
requestParams: {
|
|
5
|
+
apiKey: any;
|
|
6
|
+
url: any;
|
|
7
|
+
title: string;
|
|
8
|
+
env: string;
|
|
9
|
+
group_title: string;
|
|
10
|
+
detach: string;
|
|
11
|
+
isBatchEnabled: boolean;
|
|
12
|
+
};
|
|
13
|
+
runId: any;
|
|
14
|
+
adapter: import("./junit-adapter/adapter.js").default;
|
|
15
|
+
opts: {};
|
|
16
|
+
store: {};
|
|
17
|
+
pipesPromise: Promise<any[]>;
|
|
18
|
+
parser: XMLParser;
|
|
19
|
+
tests: any[];
|
|
20
|
+
stats: {};
|
|
21
|
+
uploader: S3Uploader;
|
|
22
|
+
version: any;
|
|
23
|
+
connectAdapter(): import("./junit-adapter/adapter.js").default;
|
|
24
|
+
parse(fileName: any): {
|
|
25
|
+
status: string;
|
|
26
|
+
create_tests: boolean;
|
|
27
|
+
tests_count: number;
|
|
28
|
+
passed_count: number;
|
|
29
|
+
skipped_count: number;
|
|
30
|
+
failed_count: number;
|
|
31
|
+
tests: any;
|
|
32
|
+
} | {
|
|
33
|
+
status: any;
|
|
34
|
+
create_tests: boolean;
|
|
35
|
+
tests_count: number;
|
|
36
|
+
passed_count: number;
|
|
37
|
+
failed_count: number;
|
|
38
|
+
skipped_count: number;
|
|
39
|
+
tests: any[];
|
|
40
|
+
};
|
|
41
|
+
processJUnit(jsonSuite: any): {
|
|
42
|
+
create_tests: boolean;
|
|
43
|
+
duration: number;
|
|
44
|
+
failed_count: number;
|
|
45
|
+
name: any;
|
|
46
|
+
passed_count: number;
|
|
47
|
+
skipped_count: number;
|
|
48
|
+
status: string;
|
|
49
|
+
tests: any[];
|
|
50
|
+
tests_count: number;
|
|
51
|
+
};
|
|
52
|
+
processNUnit(jsonSuite: any): {
|
|
53
|
+
status: any;
|
|
54
|
+
create_tests: boolean;
|
|
55
|
+
tests_count: number;
|
|
56
|
+
passed_count: number;
|
|
57
|
+
failed_count: number;
|
|
58
|
+
skipped_count: number;
|
|
59
|
+
tests: any[];
|
|
60
|
+
};
|
|
61
|
+
processTRX(jsonSuite: any): {
|
|
62
|
+
status: string;
|
|
63
|
+
create_tests: boolean;
|
|
64
|
+
tests_count: number;
|
|
65
|
+
passed_count: number;
|
|
66
|
+
skipped_count: number;
|
|
67
|
+
failed_count: number;
|
|
68
|
+
tests: any;
|
|
69
|
+
};
|
|
70
|
+
processXUnit(assemblies: any): {
|
|
71
|
+
status: string;
|
|
72
|
+
create_tests: boolean;
|
|
73
|
+
name: string;
|
|
74
|
+
tests_count: number;
|
|
75
|
+
passed_count: number;
|
|
76
|
+
failed_count: number;
|
|
77
|
+
skipped_count: number;
|
|
78
|
+
tests: any[];
|
|
79
|
+
};
|
|
80
|
+
calculateStats(): {};
|
|
81
|
+
fetchSourceCode(): void;
|
|
82
|
+
formatTests(): void;
|
|
83
|
+
formatErrors(): void;
|
|
84
|
+
formatStack(t: any): any;
|
|
85
|
+
uploadArtifacts(): Promise<void>;
|
|
86
|
+
createRun(): Promise<any[]>;
|
|
87
|
+
pipes: any;
|
|
88
|
+
uploadData(): Promise<any[]>;
|
|
89
|
+
_finishRun(): Promise<any[]>;
|
|
90
|
+
}
|
|
91
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
92
|
+
import { S3Uploader } from './uploader.js';
|
package/lib/xmlReader.js
CHANGED
|
@@ -15,12 +15,12 @@ const utils_js_1 = require("./utils/utils.js");
|
|
|
15
15
|
const index_js_1 = require("./pipe/index.js");
|
|
16
16
|
const index_js_2 = __importDefault(require("./junit-adapter/index.js"));
|
|
17
17
|
const config_js_1 = require("./config.js");
|
|
18
|
-
const
|
|
18
|
+
const uploader_js_1 = require("./uploader.js");
|
|
19
19
|
// @ts-ignore this line will be removed in compiled code, because __dirname is defined in commonjs
|
|
20
20
|
const debug = (0, debug_1.default)('@testomatio/reporter:xml');
|
|
21
21
|
const ridRunId = (0, crypto_1.randomUUID)();
|
|
22
22
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
23
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
23
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_SUITE, TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } = process.env;
|
|
24
24
|
const options = {
|
|
25
25
|
ignoreDeclaration: true,
|
|
26
26
|
ignoreAttributes: false,
|
|
@@ -28,6 +28,7 @@ const options = {
|
|
|
28
28
|
attributeNamePrefix: '',
|
|
29
29
|
parseTagValue: true,
|
|
30
30
|
};
|
|
31
|
+
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
31
32
|
const reduceOptions = {};
|
|
32
33
|
class XmlReader {
|
|
33
34
|
constructor(opts = {}) {
|
|
@@ -37,6 +38,7 @@ class XmlReader {
|
|
|
37
38
|
title: TESTOMATIO_TITLE,
|
|
38
39
|
env: TESTOMATIO_ENV,
|
|
39
40
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
41
|
+
detach: TESTOMATIO_MARK_DETACHED,
|
|
40
42
|
// batch uploading is implemented for xml already
|
|
41
43
|
isBatchEnabled: false,
|
|
42
44
|
};
|
|
@@ -51,7 +53,7 @@ class XmlReader {
|
|
|
51
53
|
this.tests = [];
|
|
52
54
|
this.stats = {};
|
|
53
55
|
this.stats.language = opts.lang?.toLowerCase();
|
|
54
|
-
this.
|
|
56
|
+
this.uploader = new uploader_js_1.S3Uploader();
|
|
55
57
|
// @ts-ignore
|
|
56
58
|
const packageJsonPath = path_1.default.resolve(__dirname, '..', 'package.json');
|
|
57
59
|
this.version = JSON.parse(fs_1.default.readFileSync(packageJsonPath).toString()).version;
|
|
@@ -74,7 +76,7 @@ class XmlReader {
|
|
|
74
76
|
/(<system-out><!\[CDATA\[)([\s\S]*?)(\]\]><\/system-out>)/g,
|
|
75
77
|
];
|
|
76
78
|
for (const regex of cutRegexes) {
|
|
77
|
-
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0,
|
|
79
|
+
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
|
|
78
80
|
}
|
|
79
81
|
const jsonResult = this.parser.parse(xmlData);
|
|
80
82
|
let jsonSuite;
|
|
@@ -105,16 +107,24 @@ class XmlReader {
|
|
|
105
107
|
const resultTests = processTestSuite(testsuite);
|
|
106
108
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
107
109
|
const status = failures > 0 || errors > 0 || hasFailures ? 'failed' : 'passed';
|
|
110
|
+
const time = testsuite.time || 0;
|
|
111
|
+
// debug('time', jsonSuite, time)
|
|
112
|
+
if (time) {
|
|
113
|
+
if (!this.stats.duration)
|
|
114
|
+
this.stats.duration = 0;
|
|
115
|
+
this.stats.duration += parseFloat(time);
|
|
116
|
+
}
|
|
108
117
|
this.tests = this.tests.concat(resultTests);
|
|
109
118
|
return {
|
|
110
|
-
status,
|
|
111
119
|
create_tests: true,
|
|
120
|
+
duration: parseFloat(time),
|
|
121
|
+
failed_count: parseInt(failures, 10),
|
|
112
122
|
name,
|
|
113
|
-
tests_count: parseInt(tests, 10),
|
|
114
123
|
passed_count: parseInt(tests, 10) - parseInt(failures, 10),
|
|
115
|
-
failed_count: parseInt(failures, 10),
|
|
116
124
|
skipped_count: 0,
|
|
125
|
+
status,
|
|
117
126
|
tests: resultTests,
|
|
127
|
+
tests_count: parseInt(tests, 10),
|
|
118
128
|
};
|
|
119
129
|
}
|
|
120
130
|
processNUnit(jsonSuite) {
|
|
@@ -193,7 +203,7 @@ class XmlReader {
|
|
|
193
203
|
this.tests = results.filter(t => !!t.title);
|
|
194
204
|
return {
|
|
195
205
|
status,
|
|
196
|
-
create_tests:
|
|
206
|
+
create_tests: !process.env.IGNORE_NEW_TESTS,
|
|
197
207
|
tests_count: parseInt(counters.total, 10),
|
|
198
208
|
passed_count: parseInt(counters.passed, 10),
|
|
199
209
|
skipped_count: parseInt(counters.notExecuted, 10),
|
|
@@ -266,6 +276,7 @@ class XmlReader {
|
|
|
266
276
|
calculateStats() {
|
|
267
277
|
this.stats = {
|
|
268
278
|
...this.stats,
|
|
279
|
+
detach: this.requestParams.detach,
|
|
269
280
|
status: 'passed',
|
|
270
281
|
create_tests: true,
|
|
271
282
|
tests_count: 0,
|
|
@@ -303,6 +314,8 @@ class XmlReader {
|
|
|
303
314
|
this.stats.language = 'js';
|
|
304
315
|
if (file.endsWith('.ts'))
|
|
305
316
|
this.stats.language = 'ts';
|
|
317
|
+
if (file.endsWith('.cs'))
|
|
318
|
+
this.stats.language = 'csharp';
|
|
306
319
|
}
|
|
307
320
|
if (!fs_1.default.existsSync(file)) {
|
|
308
321
|
debug('Failed to open file with the source code', file);
|
|
@@ -349,13 +362,13 @@ class XmlReader {
|
|
|
349
362
|
async uploadArtifacts() {
|
|
350
363
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
351
364
|
let files = [];
|
|
352
|
-
if (test.files?.length)
|
|
353
|
-
|
|
354
|
-
files =
|
|
365
|
+
if (!test.files?.length)
|
|
366
|
+
continue;
|
|
367
|
+
files = test.files.map(f => path_1.default.isAbsolute(f) ? f : path_1.default.join(process.cwd(), f));
|
|
355
368
|
if (!files.length)
|
|
356
369
|
continue;
|
|
357
370
|
const runId = this.runId || this.store.runId || Date.now().toString();
|
|
358
|
-
test.artifacts = await Promise.all(files.map(f =>
|
|
371
|
+
test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId, path_1.default.basename(f)])));
|
|
359
372
|
console.log(constants_js_1.APP_PREFIX, `🗄️ Uploaded ${picocolors_1.default.bold(`${files.length} artifacts`)} for test ${test.title}`);
|
|
360
373
|
}
|
|
361
374
|
}
|
|
@@ -369,7 +382,9 @@ class XmlReader {
|
|
|
369
382
|
};
|
|
370
383
|
debug('Run', runParams);
|
|
371
384
|
this.pipes = this.pipes || (await this.pipesPromise);
|
|
372
|
-
|
|
385
|
+
const run = await Promise.all(this.pipes.map(p => p.createRun(runParams)));
|
|
386
|
+
this.uploader.checkEnabled();
|
|
387
|
+
return run;
|
|
373
388
|
}
|
|
374
389
|
async uploadData() {
|
|
375
390
|
await this.uploadArtifacts();
|
|
@@ -378,16 +393,14 @@ class XmlReader {
|
|
|
378
393
|
this.fetchSourceCode();
|
|
379
394
|
this.formatErrors();
|
|
380
395
|
this.formatTests();
|
|
381
|
-
debug('Uploading data', {
|
|
382
|
-
...this.stats,
|
|
383
|
-
tests: this.tests,
|
|
384
|
-
});
|
|
385
396
|
const dataString = {
|
|
386
397
|
...this.stats,
|
|
387
398
|
api_key: this.requestParams.apiKey,
|
|
388
399
|
status: 'finished',
|
|
400
|
+
duration: this.stats.duration,
|
|
389
401
|
tests: this.tests,
|
|
390
402
|
};
|
|
403
|
+
debug('Uploading data', dataString);
|
|
391
404
|
this.pipes = this.pipes || (await this.pipesPromise);
|
|
392
405
|
return Promise.all(this.pipes.map(p => p.finishRun(dataString)));
|
|
393
406
|
}
|
|
@@ -405,14 +418,17 @@ function reduceTestCases(prev, item) {
|
|
|
405
418
|
testCases = [testCases];
|
|
406
419
|
}
|
|
407
420
|
// suite inside test case
|
|
408
|
-
|
|
409
|
-
|
|
421
|
+
const testCase = item['test-suite']?.['test-case'];
|
|
422
|
+
if (testCase) {
|
|
423
|
+
const nestedCases = Array.isArray(testCase) ? testCase : [testCase];
|
|
424
|
+
testCases.push(...nestedCases);
|
|
425
|
+
}
|
|
410
426
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
411
427
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
412
428
|
testCases
|
|
413
429
|
.filter(t => !!t)
|
|
414
430
|
.forEach(testCaseItem => {
|
|
415
|
-
const file = testCaseItem.file || item.filepath || '';
|
|
431
|
+
const file = testCaseItem.file || item.filepath || item.fullname || '';
|
|
416
432
|
let stack = '';
|
|
417
433
|
let message = '';
|
|
418
434
|
if (testCaseItem.error)
|
|
@@ -444,17 +460,35 @@ function reduceTestCases(prev, item) {
|
|
|
444
460
|
example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
|
|
445
461
|
title = title.replace(/\(.*?\)/, '').trim();
|
|
446
462
|
}
|
|
447
|
-
// eslint-disable-next-line
|
|
448
463
|
stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
449
|
-
|
|
464
|
+
let testId = (0, utils_js_1.fetchIdFromOutput)(stack);
|
|
465
|
+
if (tags?.length && !testId) {
|
|
466
|
+
testId = tags.filter(t => t.startsWith('T')).map(t => `@${t}`).find(t => t.match(utils_js_1.TEST_ID_REGEX))?.slice(2);
|
|
467
|
+
}
|
|
450
468
|
let status = constants_js_1.STATUS.PASSED.toString();
|
|
451
469
|
if ('failure' in testCaseItem || 'error' in testCaseItem)
|
|
452
470
|
status = constants_js_1.STATUS.FAILED;
|
|
453
471
|
if ('skipped' in testCaseItem)
|
|
454
472
|
status = constants_js_1.STATUS.SKIPPED;
|
|
473
|
+
if (testCaseItem.result && Object.values(constants_js_1.STATUS).includes(testCaseItem.result.toLowerCase())) {
|
|
474
|
+
status = testCaseItem.result.toLowerCase();
|
|
475
|
+
}
|
|
455
476
|
let rid = null;
|
|
456
477
|
if (testCaseItem.id)
|
|
457
478
|
rid = `${ridRunId}-${testCaseItem.id}`;
|
|
479
|
+
// Extract attachments
|
|
480
|
+
let files = [];
|
|
481
|
+
if (testCaseItem.attachments) {
|
|
482
|
+
const attachments = Array.isArray(testCaseItem.attachments.attachment)
|
|
483
|
+
? testCaseItem.attachments.attachment
|
|
484
|
+
: [testCaseItem.attachments.attachment];
|
|
485
|
+
files = attachments
|
|
486
|
+
.filter(a => a && a.filePath)
|
|
487
|
+
.map(a => a.filePath);
|
|
488
|
+
}
|
|
489
|
+
// Extract files from stack trace using existing utility
|
|
490
|
+
const stackFiles = (0, utils_js_1.fetchFilesFromStackTrace)(stack);
|
|
491
|
+
files = [...new Set([...files, ...stackFiles])]; // Remove duplicates
|
|
458
492
|
prev.push({
|
|
459
493
|
rid,
|
|
460
494
|
file,
|
|
@@ -469,7 +503,9 @@ function reduceTestCases(prev, item) {
|
|
|
469
503
|
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
470
504
|
status,
|
|
471
505
|
title,
|
|
506
|
+
root_suite_id: TESTOMATIO_SUITE,
|
|
472
507
|
suite_title: suiteTitle,
|
|
508
|
+
files,
|
|
473
509
|
});
|
|
474
510
|
});
|
|
475
511
|
return prev;
|
|
@@ -493,11 +529,14 @@ function fetchProperties(item) {
|
|
|
493
529
|
let title = '';
|
|
494
530
|
if (!item.properties)
|
|
495
531
|
return {};
|
|
496
|
-
|
|
532
|
+
// Handle both single property and array of properties
|
|
533
|
+
const properties = Array.isArray(item.properties.property)
|
|
534
|
+
? item.properties.property
|
|
535
|
+
: [item.properties.property].filter(Boolean);
|
|
536
|
+
const prop = properties.find(p => p.name === 'Description');
|
|
497
537
|
if (prop)
|
|
498
538
|
title = prop.value;
|
|
499
|
-
|
|
500
|
-
.flat()
|
|
539
|
+
properties
|
|
501
540
|
.filter(p => p.name === 'Category')
|
|
502
541
|
.forEach(p => tags.push(p.value));
|
|
503
542
|
return { title, tags };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "2.0.0-beta-
|
|
3
|
+
"version": "2.0.0-beta.1-xml",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18"
|
|
@@ -14,19 +14,19 @@
|
|
|
14
14
|
"@aws-sdk/client-s3": "^3.279.0",
|
|
15
15
|
"@aws-sdk/lib-storage": "^3.279.0",
|
|
16
16
|
"@cucumber/cucumber": "^10.9.0",
|
|
17
|
-
"@octokit/rest": "^
|
|
17
|
+
"@octokit/rest": "^21.1.1",
|
|
18
18
|
"aws-sdk": "^2.1072.0",
|
|
19
19
|
"axios": "^1.6.2",
|
|
20
20
|
"axios-retry": "^3.9.1",
|
|
21
21
|
"callsite-record": "^4.1.4",
|
|
22
|
-
"
|
|
23
|
-
"commander": "^4.1.1",
|
|
22
|
+
"commander": "^12",
|
|
24
23
|
"cross-spawn": "^7.0.3",
|
|
25
24
|
"csv-writer": "^1.6.0",
|
|
26
25
|
"debug": "^4.3.4",
|
|
27
26
|
"dotenv": "^16.0.1",
|
|
28
27
|
"fast-xml-parser": "^4.4.1",
|
|
29
28
|
"file-url": "3.0.0",
|
|
29
|
+
"filesize": "^10.1.6",
|
|
30
30
|
"glob": "^10.3",
|
|
31
31
|
"handlebars": "^4.7.8",
|
|
32
32
|
"has-flag": "^5.0.1",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"lodash.merge": "^4.6.2",
|
|
38
38
|
"minimatch": "^9.0.3",
|
|
39
39
|
"picocolors": "^1.0.1",
|
|
40
|
+
"pretty-ms": "^7.0.1",
|
|
40
41
|
"promise-retry": "^2.0.1",
|
|
41
42
|
"strip-ansi": "^7.1.0",
|
|
42
43
|
"uuid": "^9.0.0"
|
|
@@ -48,7 +49,6 @@
|
|
|
48
49
|
"testcafe"
|
|
49
50
|
],
|
|
50
51
|
"scripts": {
|
|
51
|
-
"@cucumber/cucumber": "^10.9.0",
|
|
52
52
|
"clear-exportdir": "rm -rf export/",
|
|
53
53
|
"pretty": "npx prettier --check .",
|
|
54
54
|
"pretty:fix": "prettier --write .",
|
|
@@ -67,10 +67,12 @@
|
|
|
67
67
|
"test:adapter:playwright:example": "npx playwright test --config='./tests/adapter/examples/playwright/playwright.config.ts'",
|
|
68
68
|
"test:adapter:vitest:example": "npx vitest --config='./tests/adapter/examples/vitest/vitest.config.ts'",
|
|
69
69
|
"test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/logger-2.test.js && npx mocha tests-storage/reporter-functions.test.js",
|
|
70
|
-
"build
|
|
70
|
+
"build": "rm -rf ./cjs && tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh",
|
|
71
|
+
"build:bun": "rm -rf ./cjs && bun build ./src/reporter.js ./src/bin/reportXml.js ./src/bin/startTest.js ./src/bin/uploadArtifacts.js --outdir ./cjs --target node",
|
|
72
|
+
"build:watch:bun": "rm -rf ./cjs && bun build ./src/bin/reportXml.js ./src/bin/startTest.js ./src/bin/uploadArtifacts.js --outdir ./cjs --target node --watch --onSuccess \"build/scripts/post-build.js\""
|
|
71
73
|
},
|
|
72
74
|
"devDependencies": {
|
|
73
|
-
"@playwright/test": "^1.
|
|
75
|
+
"@playwright/test": "^1.49.1",
|
|
74
76
|
"@redocly/cli": "^1.0.0-beta.125",
|
|
75
77
|
"@types/cross-spawn": "^6.0.6",
|
|
76
78
|
"@types/cucumber": "^7.0.0",
|
|
@@ -79,8 +81,7 @@
|
|
|
79
81
|
"chai": "^4.3.6",
|
|
80
82
|
"codeceptjs": "^3.6.5",
|
|
81
83
|
"cucumber": "^6.0.7",
|
|
82
|
-
"eslint": "^
|
|
83
|
-
"eslint-config-airbnb-base": "^15.0.0",
|
|
84
|
+
"eslint": "^9.24.0",
|
|
84
85
|
"eslint-config-prettier": "^8.3.0",
|
|
85
86
|
"eslint-plugin-import": "^2.25.4",
|
|
86
87
|
"jasmine": "^5.2.0",
|
|
@@ -95,14 +96,16 @@
|
|
|
95
96
|
"vitest": "^1.6.0"
|
|
96
97
|
},
|
|
97
98
|
"bin": {
|
|
99
|
+
"@testomatio/reporter": "./lib/bin/cli.js",
|
|
98
100
|
"report-xml": "./lib/bin/reportXml.js",
|
|
99
|
-
"start-test-run": "./lib/bin/startTest.js"
|
|
101
|
+
"start-test-run": "./lib/bin/startTest.js",
|
|
102
|
+
"upload-artifacts": "./lib/bin/uploadArtifacts.js"
|
|
100
103
|
},
|
|
101
104
|
"exports": {
|
|
102
105
|
".": {
|
|
103
106
|
"import": "./src/reporter.js",
|
|
104
107
|
"require": "./lib/reporter.js",
|
|
105
|
-
"types": "./
|
|
108
|
+
"types": "./types/types.d.ts"
|
|
106
109
|
},
|
|
107
110
|
"./lib/adapter/codecept/codecept.js": "./lib/adapter/codecept.js",
|
|
108
111
|
"./lib/adapter/codecept": "./lib/adapter/codecept.js",
|
|
@@ -111,6 +114,7 @@
|
|
|
111
114
|
"./cucumber": "./lib/adapter/cucumber/current.js",
|
|
112
115
|
"./lib/adapter/cypress-plugin": "./lib/adapter/cypress-plugin/index.js",
|
|
113
116
|
"./cypress-plugin": "./lib/adapter/cypress-plugin/index.js",
|
|
117
|
+
"./cypress": "./lib/adapter/cypress-plugin/index.js",
|
|
114
118
|
"./lib/adapter/jasmine.js": "./lib/adapter/jasmine.js",
|
|
115
119
|
"./jasmine": "./lib/adapter/jasmine.js",
|
|
116
120
|
"./lib/adapter/jest.js": "./lib/adapter/jest.js",
|
|
@@ -118,10 +122,12 @@
|
|
|
118
122
|
"./lib/adapter/mocha/mocha.js": "./lib/adapter/mocha.js",
|
|
119
123
|
"./mocha": "./lib/adapter/mocha.js",
|
|
120
124
|
"./lib/adapter/playwright.js": "./lib/adapter/playwright.js",
|
|
125
|
+
"./nightwatch": "./lib/adapter/nightwatch.js",
|
|
121
126
|
"./playwright": "./lib/adapter/playwright.js",
|
|
122
|
-
"./vitest": "./
|
|
127
|
+
"./vitest": "./lib/adapter/vitest.js",
|
|
123
128
|
"./lib/adapter/webdriver.js": "./lib/adapter/webdriver.js",
|
|
124
129
|
"./lib/adapter/webdriver": "./lib/adapter/webdriver.js",
|
|
125
|
-
"./webdriver": "./lib/adapter/webdriver.js"
|
|
130
|
+
"./webdriver": "./lib/adapter/webdriver.js",
|
|
131
|
+
"./wdio": "./lib/adapter/webdriver.js"
|
|
126
132
|
}
|
|
127
133
|
}
|
package/src/adapter/codecept.js
CHANGED
|
@@ -4,8 +4,6 @@ import TestomatClient from '../client.js';
|
|
|
4
4
|
import { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
|
|
5
5
|
import { getTestomatIdFromTestTitle, fileSystem } from '../utils/utils.js';
|
|
6
6
|
import { services } from '../services/index.js';
|
|
7
|
-
import { upload } from '../fileUploader.js';
|
|
8
|
-
// eslint-disable-next-line
|
|
9
7
|
import codeceptjs from 'codeceptjs';
|
|
10
8
|
|
|
11
9
|
const debug = createDebugMessages('@testomatio/reporter:adapter:codeceptjs');
|
|
@@ -46,8 +44,9 @@ function CodeceptReporter(config) {
|
|
|
46
44
|
const { apiKey } = config;
|
|
47
45
|
|
|
48
46
|
const getDuration = test => {
|
|
49
|
-
if (
|
|
50
|
-
|
|
47
|
+
if (!test.uid) return 0;
|
|
48
|
+
if (testTimeMap[test.uid]) {
|
|
49
|
+
return Date.now() - testTimeMap[test.uid];
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
return 0;
|
|
@@ -125,7 +124,8 @@ function CodeceptReporter(config) {
|
|
|
125
124
|
services.setContext(test.fullTitle());
|
|
126
125
|
|
|
127
126
|
testTimeMap[test.id] = Date.now();
|
|
128
|
-
|
|
127
|
+
if (!test.uid) return;
|
|
128
|
+
testTimeMap[test.uid] = Date.now();
|
|
129
129
|
});
|
|
130
130
|
|
|
131
131
|
event.dispatcher.on(event.all.result, async () => {
|
|
@@ -133,10 +133,8 @@ function CodeceptReporter(config) {
|
|
|
133
133
|
// all tests were reported and we can upload videos
|
|
134
134
|
await Promise.all(reportTestPromises);
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
await uploadAttachments(client, traces, '📁 Uploading', 'trace');
|
|
139
|
-
}
|
|
136
|
+
await uploadAttachments(client, videos, '🎞️ Uploading', 'video');
|
|
137
|
+
await uploadAttachments(client, traces, '📁 Uploading', 'trace');
|
|
140
138
|
|
|
141
139
|
const status = failedTests.length === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
142
140
|
// @ts-ignore
|
|
@@ -144,9 +142,9 @@ function CodeceptReporter(config) {
|
|
|
144
142
|
});
|
|
145
143
|
|
|
146
144
|
event.dispatcher.on(event.test.passed, test => {
|
|
147
|
-
const {
|
|
148
|
-
if (
|
|
149
|
-
failedTests = failedTests.filter(failed =>
|
|
145
|
+
const { uid, tags, title } = test;
|
|
146
|
+
if (uid && failedTests.includes(uid)) {
|
|
147
|
+
failedTests = failedTests.filter(failed => uid !== failed);
|
|
150
148
|
}
|
|
151
149
|
const testObj = getTestAndMessage(title);
|
|
152
150
|
|
|
@@ -157,7 +155,7 @@ function CodeceptReporter(config) {
|
|
|
157
155
|
|
|
158
156
|
client.addTestRun(STATUS.PASSED, {
|
|
159
157
|
...stripExampleFromTitle(title),
|
|
160
|
-
rid:
|
|
158
|
+
rid: uid,
|
|
161
159
|
suite_title: test.parent && test.parent.title,
|
|
162
160
|
message: testObj.message,
|
|
163
161
|
time: getDuration(test),
|
|
@@ -180,12 +178,12 @@ function CodeceptReporter(config) {
|
|
|
180
178
|
if (!suite) return;
|
|
181
179
|
if (!suite.tests) return;
|
|
182
180
|
for (const test of suite.tests) {
|
|
183
|
-
const {
|
|
184
|
-
failedTests.push(
|
|
181
|
+
const { uid, tags, title } = test;
|
|
182
|
+
failedTests.push(uid || title);
|
|
185
183
|
const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
186
184
|
|
|
187
185
|
client.addTestRun(STATUS.FAILED, {
|
|
188
|
-
rid:
|
|
186
|
+
rid: uid,
|
|
189
187
|
...stripExampleFromTitle(title),
|
|
190
188
|
suite_title: suite.title,
|
|
191
189
|
test_id: testId,
|
|
@@ -199,8 +197,8 @@ function CodeceptReporter(config) {
|
|
|
199
197
|
event.dispatcher.on(event.test.after, test => {
|
|
200
198
|
if (test.state && test.state !== STATUS.FAILED) return;
|
|
201
199
|
if (test.err) error = test.err;
|
|
202
|
-
const {
|
|
203
|
-
failedTests.push(
|
|
200
|
+
const { uid, tags, title, artifacts } = test;
|
|
201
|
+
failedTests.push(uid || title);
|
|
204
202
|
const testObj = getTestAndMessage(title);
|
|
205
203
|
|
|
206
204
|
const files = [];
|
|
@@ -214,7 +212,7 @@ function CodeceptReporter(config) {
|
|
|
214
212
|
|
|
215
213
|
client.addTestRun(STATUS.FAILED, {
|
|
216
214
|
...stripExampleFromTitle(title),
|
|
217
|
-
rid:
|
|
215
|
+
rid: uid,
|
|
218
216
|
test_id: getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`),
|
|
219
217
|
suite_title: test.parent && test.parent.title,
|
|
220
218
|
error,
|
|
@@ -230,20 +228,20 @@ function CodeceptReporter(config) {
|
|
|
230
228
|
debug('artifacts', artifacts);
|
|
231
229
|
|
|
232
230
|
for (const aid in artifacts) {
|
|
233
|
-
if (aid.startsWith('video')) videos.push({ rid:
|
|
234
|
-
if (aid.startsWith('trace')) traces.push({ rid:
|
|
231
|
+
if (aid.startsWith('video')) videos.push({ rid: uid, title, path: artifacts[aid], type: 'video/webm' });
|
|
232
|
+
if (aid.startsWith('trace')) traces.push({ rid: uid, title, path: artifacts[aid], type: 'application/zip' });
|
|
235
233
|
}
|
|
236
234
|
|
|
237
235
|
// output.stop();
|
|
238
236
|
});
|
|
239
237
|
|
|
240
238
|
event.dispatcher.on(event.test.skipped, test => {
|
|
241
|
-
const {
|
|
242
|
-
if (failedTests.includes(
|
|
239
|
+
const { uid, tags, title } = test;
|
|
240
|
+
if (failedTests.includes(uid || title)) return;
|
|
243
241
|
|
|
244
242
|
const testObj = getTestAndMessage(title);
|
|
245
243
|
client.addTestRun(STATUS.SKIPPED, {
|
|
246
|
-
rid:
|
|
244
|
+
rid: uid,
|
|
247
245
|
...stripExampleFromTitle(title),
|
|
248
246
|
test_id: getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`),
|
|
249
247
|
suite_title: test.parent && test.parent.title,
|
|
@@ -272,7 +270,6 @@ function CodeceptReporter(config) {
|
|
|
272
270
|
for (let i = 0; i < Math.max(currentMetaStep.length, metaSteps.length); i++) {
|
|
273
271
|
if (currentMetaStep[i] !== metaSteps[i]) {
|
|
274
272
|
stepShift = 2 * i;
|
|
275
|
-
// eslint-disable-next-line no-continue
|
|
276
273
|
if (!metaSteps[i]) continue;
|
|
277
274
|
if (metaSteps[i].isBDD()) {
|
|
278
275
|
// output.push(repeat(stepShift) + pc.bold(metaSteps[i].toString()) + metaSteps[i].comment);
|
|
@@ -312,11 +309,17 @@ function CodeceptReporter(config) {
|
|
|
312
309
|
async function uploadAttachments(client, attachments, messagePrefix, attachmentType) {
|
|
313
310
|
if (!attachments?.length) return;
|
|
314
311
|
|
|
315
|
-
|
|
312
|
+
if (client.uploader.isEnabled) {
|
|
313
|
+
console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType} ...`);
|
|
314
|
+
}
|
|
316
315
|
|
|
317
316
|
const promises = attachments.map(async attachment => {
|
|
318
317
|
const { rid, title, path, type } = attachment;
|
|
319
318
|
const file = { path, type, title };
|
|
319
|
+
|
|
320
|
+
// we are storing file if upload is disabled
|
|
321
|
+
if (!client.uploader.isEnabled) return client.uploader.storeUploadedFile(path, client.runId, rid, false);
|
|
322
|
+
|
|
320
323
|
return client.addTestRun(undefined, {
|
|
321
324
|
...stripExampleFromTitle(title),
|
|
322
325
|
rid,
|
|
@@ -367,3 +370,4 @@ function getTestLogs(test) {
|
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
export { CodeceptReporter };
|
|
373
|
+
export default CodeceptReporter;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { STATUS } from '../../constants.js';
|
|
2
2
|
import { getTestomatIdFromTestTitle, parseSuite } from '../../utils/utils.js';
|
|
3
3
|
import TestomatClient from '../../client.js';
|
|
4
|
-
import {config} from '../../config.js';
|
|
4
|
+
import { config } from '../../config.js';
|
|
5
5
|
|
|
6
6
|
const testomatioReporter = on => {
|
|
7
7
|
if (!config.TESTOMATIO) {
|
|
@@ -44,22 +44,22 @@ const testomatioReporter = on => {
|
|
|
44
44
|
|
|
45
45
|
if (!error && test.displayError) {
|
|
46
46
|
error = { message: test.displayError };
|
|
47
|
-
// eslint-disable-next-line
|
|
48
47
|
error.inspect = function () {
|
|
49
48
|
return this.message;
|
|
50
49
|
};
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
const formattedError = error
|
|
52
|
+
const formattedError = error
|
|
53
|
+
? {
|
|
54
54
|
message: error.message,
|
|
55
55
|
name: error.name,
|
|
56
56
|
inspect:
|
|
57
57
|
error.inspect ||
|
|
58
|
-
// eslint-disable-next-line
|
|
59
58
|
function () {
|
|
60
59
|
return this.message;
|
|
61
60
|
},
|
|
62
|
-
}
|
|
61
|
+
}
|
|
62
|
+
: undefined;
|
|
63
63
|
|
|
64
64
|
const screenshots = Array.isArray(results.screenshots)
|
|
65
65
|
? results.screenshots
|
package/src/adapter/mocha.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const MochaReporter = require('../../../lib/adapter/mocha
|
|
1
|
+
const MochaReporter = require('../../../lib/adapter/mocha.js');
|
|
2
2
|
module.exports = MochaReporter;
|
package/src/adapter/mocha.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
1
|
import Mocha from 'mocha';
|
|
3
2
|
import TestomatClient from '../client.js';
|
|
4
3
|
import { STATUS, TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
|
|
@@ -33,6 +32,7 @@ function MochaReporter(runner, opts) {
|
|
|
33
32
|
runner.on(EVENT_RUN_BEGIN, () => {
|
|
34
33
|
client.createRun();
|
|
35
34
|
|
|
35
|
+
// clear dir with artifacts/logs
|
|
36
36
|
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
37
37
|
});
|
|
38
38
|
|
|
@@ -66,7 +66,7 @@ function MochaReporter(runner, opts) {
|
|
|
66
66
|
test_id: testId,
|
|
67
67
|
suite_title: getSuiteTitle(test),
|
|
68
68
|
title: getTestName(test),
|
|
69
|
-
code: test.body.toString(),
|
|
69
|
+
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
70
70
|
file: getFile(test),
|
|
71
71
|
time: test.duration,
|
|
72
72
|
logs,
|
|
@@ -82,7 +82,7 @@ function MochaReporter(runner, opts) {
|
|
|
82
82
|
client.addTestRun(STATUS.SKIPPED, {
|
|
83
83
|
title: getTestName(test),
|
|
84
84
|
suite_title: getSuiteTitle(test),
|
|
85
|
-
code: test.body.toString(),
|
|
85
|
+
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
86
86
|
file: getFile(test),
|
|
87
87
|
test_id: testId,
|
|
88
88
|
time: test.duration,
|
|
@@ -102,7 +102,7 @@ function MochaReporter(runner, opts) {
|
|
|
102
102
|
file: getFile(test),
|
|
103
103
|
test_id: testId,
|
|
104
104
|
title: getTestName(test),
|
|
105
|
-
code: test.body.toString(),
|
|
105
|
+
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
106
106
|
time: test.duration,
|
|
107
107
|
logs,
|
|
108
108
|
});
|