@testomatio/reporter 2.0.1-beta-2-ignore-xml → 2.0.1-beta.1
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.js +0 -2
- package/lib/adapter/cypress-plugin/index.js +0 -2
- package/lib/adapter/mocha.js +0 -1
- package/lib/adapter/nightwatch.d.ts +4 -0
- package/lib/adapter/nightwatch.js +80 -0
- package/lib/adapter/webdriver.d.ts +1 -1
- package/lib/adapter/webdriver.js +17 -8
- package/lib/bin/cli.js +126 -8
- package/lib/bin/reportXml.js +4 -2
- package/lib/bin/startTest.js +3 -2
- package/lib/bin/uploadArtifacts.js +5 -4
- package/lib/client.js +18 -9
- package/lib/config.js +2 -2
- package/lib/data-storage.d.ts +1 -1
- package/lib/data-storage.js +17 -7
- package/lib/junit-adapter/csharp.d.ts +1 -0
- package/lib/junit-adapter/csharp.js +11 -1
- package/lib/pipe/bitbucket.d.ts +2 -0
- package/lib/pipe/bitbucket.js +38 -26
- package/lib/pipe/debug.js +17 -3
- package/lib/pipe/github.d.ts +2 -2
- package/lib/pipe/github.js +35 -3
- package/lib/pipe/gitlab.d.ts +2 -0
- package/lib/pipe/gitlab.js +27 -9
- package/lib/pipe/html.d.ts +1 -0
- package/lib/pipe/html.js +1 -3
- package/lib/pipe/index.js +17 -7
- package/lib/pipe/testomatio.d.ts +2 -1
- package/lib/pipe/testomatio.js +79 -73
- package/lib/reporter.d.ts +12 -12
- package/lib/services/artifacts.d.ts +1 -1
- package/lib/services/key-values.d.ts +1 -1
- package/lib/services/logger.d.ts +1 -1
- package/lib/services/logger.js +1 -2
- package/lib/template/testomatio.hbs +443 -68
- package/lib/uploader.js +2 -2
- package/lib/utils/utils.d.ts +2 -0
- package/lib/utils/utils.js +41 -18
- package/lib/xmlReader.js +54 -19
- package/package.json +8 -9
- package/src/adapter/codecept.js +0 -2
- package/src/adapter/cypress-plugin/index.js +0 -2
- package/src/adapter/mocha.js +0 -1
- package/src/adapter/nightwatch.js +88 -0
- package/src/adapter/webdriver.js +1 -2
- package/src/bin/cli.js +131 -2
- package/src/bin/reportXml.js +4 -1
- package/src/bin/startTest.js +2 -1
- package/src/bin/uploadArtifacts.js +2 -1
- package/src/client.js +1 -2
- package/src/config.js +2 -2
- package/src/junit-adapter/csharp.js +13 -1
- package/src/pipe/bitbucket.js +22 -24
- package/src/pipe/debug.js +18 -3
- package/src/pipe/github.js +1 -2
- package/src/pipe/gitlab.js +27 -9
- package/src/pipe/html.js +3 -4
- package/src/pipe/testomatio.js +98 -103
- package/src/services/logger.js +1 -2
- package/src/template/testomatio.hbs +443 -68
- package/src/uploader.js +2 -2
- package/src/utils/utils.js +19 -9
- package/src/xmlReader.js +69 -17
package/src/xmlReader.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
fetchSourceCodeFromStackTrace,
|
|
14
14
|
fetchIdFromCode,
|
|
15
15
|
humanize,
|
|
16
|
+
TEST_ID_REGEX,
|
|
16
17
|
} from './utils/utils.js';
|
|
17
18
|
import { pipesFactory } from './pipe/index.js';
|
|
18
19
|
import adapterFactory from './junit-adapter/index.js';
|
|
@@ -26,8 +27,15 @@ const debug = createDebugMessages('@testomatio/reporter:xml');
|
|
|
26
27
|
const ridRunId = randomUUID();
|
|
27
28
|
|
|
28
29
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
29
|
-
const {
|
|
30
|
-
|
|
30
|
+
const {
|
|
31
|
+
TESTOMATIO_RUNGROUP_TITLE,
|
|
32
|
+
TESTOMATIO_SUITE,
|
|
33
|
+
TESTOMATIO_MAX_STACK_TRACE,
|
|
34
|
+
TESTOMATIO_TITLE,
|
|
35
|
+
TESTOMATIO_ENV,
|
|
36
|
+
TESTOMATIO_RUN,
|
|
37
|
+
TESTOMATIO_MARK_DETACHED,
|
|
38
|
+
} = process.env;
|
|
31
39
|
|
|
32
40
|
const options = {
|
|
33
41
|
ignoreDeclaration: true,
|
|
@@ -37,6 +45,8 @@ const options = {
|
|
|
37
45
|
parseTagValue: true,
|
|
38
46
|
};
|
|
39
47
|
|
|
48
|
+
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
49
|
+
|
|
40
50
|
const reduceOptions = {};
|
|
41
51
|
|
|
42
52
|
class XmlReader {
|
|
@@ -91,7 +101,7 @@ class XmlReader {
|
|
|
91
101
|
];
|
|
92
102
|
|
|
93
103
|
for (const regex of cutRegexes) {
|
|
94
|
-
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0,
|
|
104
|
+
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
const jsonResult = this.parser.parse(xmlData);
|
|
@@ -341,6 +351,7 @@ class XmlReader {
|
|
|
341
351
|
if (file.endsWith('.rb')) this.stats.language = 'ruby';
|
|
342
352
|
if (file.endsWith('.js')) this.stats.language = 'js';
|
|
343
353
|
if (file.endsWith('.ts')) this.stats.language = 'ts';
|
|
354
|
+
if (file.endsWith('.cs')) this.stats.language = 'csharp';
|
|
344
355
|
}
|
|
345
356
|
|
|
346
357
|
if (!fs.existsSync(file)) {
|
|
@@ -394,13 +405,14 @@ class XmlReader {
|
|
|
394
405
|
async uploadArtifacts() {
|
|
395
406
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
396
407
|
let files = [];
|
|
397
|
-
if (test.files?.length)
|
|
398
|
-
|
|
408
|
+
if (!test.files?.length) continue;
|
|
409
|
+
|
|
410
|
+
files = test.files.map(f => (path.isAbsolute(f) ? f : path.join(process.cwd(), f)));
|
|
399
411
|
|
|
400
412
|
if (!files.length) continue;
|
|
401
413
|
|
|
402
414
|
const runId = this.runId || this.store.runId || Date.now().toString();
|
|
403
|
-
test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId])));
|
|
415
|
+
test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId, path.basename(f)])));
|
|
404
416
|
console.log(APP_PREFIX, `🗄️ Uploaded ${pc.bold(`${files.length} artifacts`)} for test ${test.title}`);
|
|
405
417
|
}
|
|
406
418
|
}
|
|
@@ -460,14 +472,18 @@ function reduceTestCases(prev, item) {
|
|
|
460
472
|
}
|
|
461
473
|
|
|
462
474
|
// suite inside test case
|
|
463
|
-
|
|
475
|
+
const testCase = item['test-suite']?.['test-case'];
|
|
476
|
+
if (testCase) {
|
|
477
|
+
const nestedCases = Array.isArray(testCase) ? testCase : [testCase];
|
|
478
|
+
testCases.push(...nestedCases);
|
|
479
|
+
}
|
|
464
480
|
|
|
465
481
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
466
482
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
467
483
|
testCases
|
|
468
484
|
.filter(t => !!t)
|
|
469
485
|
.forEach(testCaseItem => {
|
|
470
|
-
const file = testCaseItem.file || item.filepath || '';
|
|
486
|
+
const file = testCaseItem.file || item.filepath || item.fullname || '';
|
|
471
487
|
|
|
472
488
|
let stack = '';
|
|
473
489
|
let message = '';
|
|
@@ -485,7 +501,7 @@ function reduceTestCases(prev, item) {
|
|
|
485
501
|
const preferClassname = reduceOptions.preferClassname || isParametrized;
|
|
486
502
|
|
|
487
503
|
// SpecFlow config
|
|
488
|
-
let { title, tags } = fetchProperties(isParametrized ? item : testCaseItem);
|
|
504
|
+
let { title, tags, testId } = fetchProperties(isParametrized ? item : testCaseItem);
|
|
489
505
|
let example = null;
|
|
490
506
|
const suiteTitle = preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname;
|
|
491
507
|
|
|
@@ -498,19 +514,44 @@ function reduceTestCases(prev, item) {
|
|
|
498
514
|
title = title.replace(/\(.*?\)/, '').trim();
|
|
499
515
|
}
|
|
500
516
|
|
|
501
|
-
// eslint-disable-next-line
|
|
502
517
|
stack = `${
|
|
503
518
|
testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''
|
|
504
519
|
}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
505
|
-
|
|
520
|
+
|
|
521
|
+
if (!testId) testId = fetchIdFromOutput(stack);
|
|
522
|
+
|
|
523
|
+
if (tags?.length && !testId) {
|
|
524
|
+
testId = tags
|
|
525
|
+
.filter(t => t.startsWith('T'))
|
|
526
|
+
.map(t => `@${t}`)
|
|
527
|
+
.find(t => t.match(TEST_ID_REGEX))
|
|
528
|
+
?.slice(2);
|
|
529
|
+
}
|
|
506
530
|
|
|
507
531
|
let status = STATUS.PASSED.toString();
|
|
508
532
|
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
509
533
|
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
534
|
+
if (testCaseItem.result && Object.values(STATUS).includes(testCaseItem.result.toLowerCase())) {
|
|
535
|
+
status = testCaseItem.result.toLowerCase();
|
|
536
|
+
}
|
|
510
537
|
|
|
511
538
|
let rid = null;
|
|
512
539
|
if (testCaseItem.id) rid = `${ridRunId}-${testCaseItem.id}`;
|
|
513
540
|
|
|
541
|
+
// Extract attachments
|
|
542
|
+
let files = [];
|
|
543
|
+
if (testCaseItem.attachments) {
|
|
544
|
+
const attachments = Array.isArray(testCaseItem.attachments.attachment)
|
|
545
|
+
? testCaseItem.attachments.attachment
|
|
546
|
+
: [testCaseItem.attachments.attachment];
|
|
547
|
+
|
|
548
|
+
files = attachments.filter(a => a && a.filePath).map(a => a.filePath);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Extract files from stack trace using existing utility
|
|
552
|
+
const stackFiles = fetchFilesFromStackTrace(stack);
|
|
553
|
+
files = [...new Set([...files, ...stackFiles])]; // Remove duplicates
|
|
554
|
+
|
|
514
555
|
prev.push({
|
|
515
556
|
rid,
|
|
516
557
|
file,
|
|
@@ -525,7 +566,9 @@ function reduceTestCases(prev, item) {
|
|
|
525
566
|
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
526
567
|
status,
|
|
527
568
|
title,
|
|
569
|
+
root_suite_id: TESTOMATIO_SUITE,
|
|
528
570
|
suite_title: suiteTitle,
|
|
571
|
+
files,
|
|
529
572
|
});
|
|
530
573
|
});
|
|
531
574
|
return prev;
|
|
@@ -552,11 +595,20 @@ function fetchProperties(item) {
|
|
|
552
595
|
|
|
553
596
|
if (!item.properties) return {};
|
|
554
597
|
|
|
555
|
-
|
|
598
|
+
// Handle both single property and array of properties
|
|
599
|
+
const properties = Array.isArray(item.properties.property)
|
|
600
|
+
? item.properties.property
|
|
601
|
+
: [item.properties.property].filter(Boolean);
|
|
602
|
+
|
|
603
|
+
const prop = properties.find(p => p.name === 'Description');
|
|
556
604
|
if (prop) title = prop.value;
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
605
|
+
|
|
606
|
+
let testId = properties.find(p => p.name === 'ID')?.value;
|
|
607
|
+
|
|
608
|
+
if (testId?.startsWith('@')) testId = testId.slice(1);
|
|
609
|
+
if (testId?.startsWith('T')) testId = testId.slice(1);
|
|
610
|
+
|
|
611
|
+
properties.filter(p => p.name === 'Category').forEach(p => tags.push(p.value));
|
|
612
|
+
|
|
613
|
+
return { title, tags, testId };
|
|
562
614
|
}
|