@testomatio/reporter 1.2.0-beta-3 → 1.2.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 +27 -18
- package/lib/adapter/codecept.js +83 -11
- package/lib/adapter/cucumber/current.js +17 -10
- package/lib/adapter/cucumber/legacy.js +4 -3
- package/lib/adapter/cypress-plugin/index.js +51 -24
- package/lib/adapter/jest.js +47 -23
- package/lib/adapter/mocha.js +99 -45
- package/lib/adapter/playwright.js +94 -32
- package/lib/bin/startTest.js +8 -7
- package/lib/client.js +119 -62
- package/lib/config.js +34 -0
- package/lib/constants.js +9 -7
- package/lib/data-storage.js +203 -0
- package/lib/fileUploader.js +125 -46
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/gitlab.js +1 -1
- package/lib/pipe/html.js +269 -232
- package/lib/pipe/testomatio.js +73 -50
- package/lib/reporter-functions.js +46 -0
- package/lib/reporter.js +11 -10
- package/lib/services/artifacts.js +57 -0
- package/lib/services/index.js +13 -0
- package/lib/services/key-values.js +58 -0
- package/lib/{logger.js → services/logger.js} +58 -66
- package/lib/template/testomatio.hbs +94 -62
- package/lib/utils/utils.js +28 -2
- package/lib/xmlReader.js +106 -105
- package/package.json +6 -3
- package/lib/_ArtifactStorageOld.js +0 -142
- package/lib/artifactStorage.js +0 -25
- package/lib/dataStorage.js +0 -232
- package/lib/template/real-data-example.js +0 -47
- /package/lib/template/{template.hbs → template-draft.hbs} +0 -0
|
@@ -7,7 +7,11 @@
|
|
|
7
7
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
8
8
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
|
9
9
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css" integrity="sha512-SzlrxWUlpfuzQ+pcUCosxcglQRNAq/DZjVsC0lE40xsADsfeQoEypE+enwcOiGjk/bSuGGKHEyjSoQ1zVisanQ==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
|
10
|
-
|
|
10
|
+
{{#if runId}}
|
|
11
|
+
<title>Report #{{runId}} - Testomat.io</title>
|
|
12
|
+
{{else}}
|
|
13
|
+
<title>Report Testomat.io</title>
|
|
14
|
+
{{/if}}
|
|
11
15
|
<style>
|
|
12
16
|
body {
|
|
13
17
|
padding: 0;
|
|
@@ -32,7 +36,7 @@
|
|
|
32
36
|
}
|
|
33
37
|
p, span{
|
|
34
38
|
font-weight: 400;
|
|
35
|
-
font-size:
|
|
39
|
+
font-size: 14px;
|
|
36
40
|
color: grey;
|
|
37
41
|
margin: 0;
|
|
38
42
|
}
|
|
@@ -245,6 +249,10 @@
|
|
|
245
249
|
font-size: 14px;
|
|
246
250
|
white-space: pre-line;
|
|
247
251
|
}
|
|
252
|
+
.test__empty__list {
|
|
253
|
+
margin-bottom: 10px;
|
|
254
|
+
color: grey;
|
|
255
|
+
}
|
|
248
256
|
/* Passed TAB*/
|
|
249
257
|
#passedTest:not(:checked) + .btn-outline-dark {
|
|
250
258
|
background-color: #39BD2F;
|
|
@@ -279,19 +287,27 @@
|
|
|
279
287
|
<div class="col-12">
|
|
280
288
|
<div class="header">
|
|
281
289
|
<div class="header__block">
|
|
282
|
-
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
290
|
+
{{#if runId}}
|
|
291
|
+
<div class="header__case">
|
|
292
|
+
<p>Run</p>
|
|
293
|
+
<span><strong>#</strong>{{runId}}</span>
|
|
294
|
+
</div>
|
|
295
|
+
{{/if}}
|
|
286
296
|
<p class="header__type">
|
|
287
297
|
<i class="fa-solid fa-face-smile"></i>
|
|
288
298
|
automated job
|
|
289
299
|
</p>
|
|
290
300
|
</div>
|
|
291
301
|
<div class="header__block">
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
302
|
+
{{#if runUrl}}
|
|
303
|
+
<a href="{{runUrl}}" target="_blank" class="btn btn-primary">
|
|
304
|
+
Full Report
|
|
305
|
+
</a>
|
|
306
|
+
{{else}}
|
|
307
|
+
<button class="btn btn-primary" disabled>
|
|
308
|
+
Full Report
|
|
309
|
+
</button>
|
|
310
|
+
{{/if}}
|
|
295
311
|
</div>
|
|
296
312
|
</div>
|
|
297
313
|
</div>
|
|
@@ -389,72 +405,78 @@
|
|
|
389
405
|
|
|
390
406
|
<div class="row level_5">
|
|
391
407
|
<div class="col-12">
|
|
408
|
+
{{#if tests.length}}
|
|
409
|
+
<div class="testWrapp">
|
|
410
|
+
{{#each tests}}
|
|
392
411
|
|
|
393
|
-
|
|
394
|
-
{{#each tests}}
|
|
412
|
+
<div class="testitem d-none" name="testitem" type="dummy" category="false">
|
|
395
413
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
<
|
|
414
|
+
<div class="testitem__top">
|
|
415
|
+
<div class="testitem__icon">
|
|
416
|
+
<i class="fa-solid fa-chevron-right testitem__ico testitem__ico_right"></i>
|
|
417
|
+
<i class="fa-solid fa-chevron-down d-none testitem__ico testitem__ico_down"></i>
|
|
418
|
+
</div>
|
|
419
|
+
<p class="testitem__name">Test</p>
|
|
402
420
|
</div>
|
|
403
|
-
<p class="testitem__name">Test</p>
|
|
404
|
-
</div>
|
|
405
421
|
|
|
406
|
-
|
|
422
|
+
<div class="testitem__body d-none">
|
|
407
423
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
424
|
+
<div class="testitem__menu">
|
|
425
|
+
<span type="steps" class="testitem__mitem testitem__mitem_active">Steps <i class="fa-solid fa-arrow-right"></i></span>
|
|
426
|
+
<span type="status" class="testitem__mitem">Status <i class="fa-solid fa-arrow-right"></i></span>
|
|
427
|
+
<span type="message" class="testitem__mitem">Message <i class="fa-solid fa-arrow-right"></i></span>
|
|
428
|
+
<span type="files" class="testitem__mitem">Files <i class="fa-solid fa-arrow-right"></i></span>
|
|
429
|
+
</div>
|
|
414
430
|
|
|
415
|
-
|
|
431
|
+
<div class="testitem__content">
|
|
416
432
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
433
|
+
<!-- 1 -->
|
|
434
|
+
<div class="testitem__case" type="steps">
|
|
435
|
+
<p class="testitem__title">Steps</p>
|
|
436
|
+
<div class="testitem__block">
|
|
437
|
+
<span>...</span>
|
|
438
|
+
</div>
|
|
422
439
|
</div>
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
440
|
+
<!-- 1 -->
|
|
441
|
+
|
|
442
|
+
<!-- 2 -->
|
|
443
|
+
<div class="testitem__case d-none" type="status">
|
|
444
|
+
<p class="testitem__title">Status</p>
|
|
445
|
+
<div class="testitem__block">
|
|
446
|
+
<span>...</span>
|
|
447
|
+
</div>
|
|
431
448
|
</div>
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
449
|
+
<!-- 2 -->
|
|
450
|
+
|
|
451
|
+
<!-- 3 -->
|
|
452
|
+
<div class="testitem__case d-none" type="message">
|
|
453
|
+
<p class="testitem__title">Message</p>
|
|
454
|
+
<div class="testitem__block">
|
|
455
|
+
<span>...</span>
|
|
456
|
+
</div>
|
|
440
457
|
</div>
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
458
|
+
<!-- 3 -->
|
|
459
|
+
|
|
460
|
+
<!-- 4 -->
|
|
461
|
+
<div class="testitem__case d-none" type="files">
|
|
462
|
+
<p class="testitem__title">Files</p>
|
|
463
|
+
<div class="testitem__block">
|
|
464
|
+
<span>...</span>
|
|
465
|
+
</div>
|
|
449
466
|
</div>
|
|
467
|
+
<!-- 4 -->
|
|
450
468
|
</div>
|
|
451
|
-
<!-- 4 -->
|
|
452
469
|
</div>
|
|
453
470
|
</div>
|
|
471
|
+
{{/each}}
|
|
472
|
+
</div>
|
|
473
|
+
{{else}}
|
|
474
|
+
<div class="row">
|
|
475
|
+
<div class="col-12 test__empty__list">
|
|
476
|
+
<p>No tests found to display 😔</p>
|
|
454
477
|
</div>
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
478
|
+
</div>
|
|
479
|
+
{{/if}}
|
|
458
480
|
</div>
|
|
459
481
|
</div>
|
|
460
482
|
|
|
@@ -469,23 +491,33 @@
|
|
|
469
491
|
|
|
470
492
|
// Draw the chart and set the chart values
|
|
471
493
|
function drawChart() {
|
|
494
|
+
let data = {};
|
|
472
495
|
const passedTests = {{getTestsByStatus tests "PASSED"}};
|
|
473
496
|
const failedTests = {{getTestsByStatus tests "FAILED"}};
|
|
474
497
|
const skippedTests = {{getTestsByStatus tests "SKIPPED"}};
|
|
475
498
|
|
|
476
|
-
|
|
499
|
+
if (passedTests === 0 && failedTests === 0 && skippedTests === 0) {
|
|
500
|
+
data = google.visualization.arrayToDataTable([
|
|
501
|
+
['Task', 'Tests'],
|
|
502
|
+
['No Tests', 1]
|
|
503
|
+
])
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
data = google.visualization.arrayToDataTable([
|
|
477
507
|
['Task', 'Tests'],
|
|
508
|
+
['No Tests', 0],
|
|
478
509
|
['Passed', passedTests],
|
|
479
510
|
['Failed', failedTests],
|
|
480
511
|
['Skipped', skippedTests],
|
|
481
512
|
]);
|
|
513
|
+
}
|
|
482
514
|
|
|
483
515
|
// Optional: add a title and set the width and height of the chart
|
|
484
516
|
const options = {
|
|
485
517
|
'title':'',
|
|
486
518
|
'width':550,
|
|
487
519
|
'height':300,
|
|
488
|
-
'colors': ['#39BD2F', '#F2230C', '#F2C00C']
|
|
520
|
+
'colors': ['#939992', '#39BD2F', '#F2230C', '#F2C00C']
|
|
489
521
|
};
|
|
490
522
|
|
|
491
523
|
// Display the chart inside the <div> element with id="piechart"
|
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) {
|
|
@@ -290,6 +298,23 @@ function removeColorCodes(input) {
|
|
|
290
298
|
return input.replace(/\x1b\[[0-9;]*m/g, '');
|
|
291
299
|
}
|
|
292
300
|
|
|
301
|
+
const testRunnerHelper = {
|
|
302
|
+
// for Jest
|
|
303
|
+
getNameOfCurrentlyRunningTest: () => {
|
|
304
|
+
if (global.testomatioTestTitle) return global.testomatioTestTitle;
|
|
305
|
+
|
|
306
|
+
if (!process.env.JEST_WORKER_ID) return null;
|
|
307
|
+
try {
|
|
308
|
+
// TODO: expect?.getState()?.testPath + ' ' + expect?.getState()?.currentTestName
|
|
309
|
+
// @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
|
|
310
|
+
// eslint-disable-next-line no-undef
|
|
311
|
+
return expect?.getState()?.currentTestName;
|
|
312
|
+
} catch (e) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
293
318
|
module.exports = {
|
|
294
319
|
isSameTest,
|
|
295
320
|
fetchSourceCode,
|
|
@@ -306,5 +331,6 @@ module.exports = {
|
|
|
306
331
|
parseSuite,
|
|
307
332
|
humanize,
|
|
308
333
|
removeColorCodes,
|
|
309
|
-
foundedTestLog
|
|
334
|
+
foundedTestLog,
|
|
335
|
+
testRunnerHelper,
|
|
310
336
|
};
|
package/lib/xmlReader.js
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
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 config = require('./config');
|
|
17
19
|
|
|
18
|
-
const TESTOMATIO_URL = process.env.TESTOMATIO_URL ||
|
|
19
|
-
const TESTOMATIO = process.env.TESTOMATIO; // key?
|
|
20
|
+
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
20
21
|
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
21
22
|
|
|
22
23
|
const options = {
|
|
23
24
|
ignoreDeclaration: true,
|
|
24
25
|
ignoreAttributes: false,
|
|
25
26
|
alwaysCreateTextNode: false,
|
|
26
|
-
attributeNamePrefix:
|
|
27
|
+
attributeNamePrefix: '',
|
|
27
28
|
parseTagValue: true,
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
const reduceOptions = {};
|
|
31
32
|
|
|
32
33
|
class XmlReader {
|
|
33
|
-
|
|
34
34
|
constructor(opts = {}) {
|
|
35
35
|
this.requestParams = {
|
|
36
|
-
apiKey: opts.apiKey || TESTOMATIO,
|
|
36
|
+
apiKey: opts.apiKey || config.TESTOMATIO,
|
|
37
37
|
url: opts.url || TESTOMATIO_URL,
|
|
38
38
|
title: TESTOMATIO_TITLE,
|
|
39
39
|
env: TESTOMATIO_ENV,
|
|
40
40
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
41
41
|
};
|
|
42
42
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
43
|
-
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts)
|
|
43
|
+
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
|
|
44
44
|
if (!this.adapter) throw new Error('XML adapter for this format not found');
|
|
45
45
|
|
|
46
46
|
this.opts = opts || {};
|
|
47
|
-
this.store = {}
|
|
47
|
+
this.store = {};
|
|
48
48
|
this.pipes = pipesFactory(opts, this.store);
|
|
49
49
|
|
|
50
50
|
this.parser = new XMLParser(options);
|
|
51
|
-
this.tests = []
|
|
52
|
-
this.stats = {}
|
|
51
|
+
this.tests = [];
|
|
52
|
+
this.stats = {};
|
|
53
53
|
this.stats.language = opts.lang?.toLowerCase();
|
|
54
|
-
this.filesToUpload = {}
|
|
54
|
+
this.filesToUpload = {};
|
|
55
55
|
|
|
56
56
|
this.version = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString()).version;
|
|
57
57
|
console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
|
|
@@ -82,8 +82,8 @@ class XmlReader {
|
|
|
82
82
|
} else if (jsonResult.assemblies) {
|
|
83
83
|
return this.processXUnit(jsonResult.assemblies);
|
|
84
84
|
} else {
|
|
85
|
-
console.log(jsonResult)
|
|
86
|
-
throw new Error("Format can't be parsed")
|
|
85
|
+
console.log(jsonResult);
|
|
86
|
+
throw new Error("Format can't be parsed");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
return this.processJUnit(jsonSuite);
|
|
@@ -96,7 +96,7 @@ class XmlReader {
|
|
|
96
96
|
const resultTests = processTestSuite(testsuite);
|
|
97
97
|
|
|
98
98
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
99
|
-
const status =
|
|
99
|
+
const status = failures > 0 || errors > 0 || hasFailures ? 'failed' : 'passed';
|
|
100
100
|
|
|
101
101
|
this.tests = this.tests.concat(resultTests);
|
|
102
102
|
|
|
@@ -135,21 +135,22 @@ class XmlReader {
|
|
|
135
135
|
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
136
136
|
if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);
|
|
137
137
|
|
|
138
|
-
const tests =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
+
}) || [];
|
|
153
154
|
|
|
154
155
|
let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
|
|
155
156
|
if (!Array.isArray(result)) result = [result].filter(d => !!d);
|
|
@@ -160,10 +161,9 @@ class XmlReader {
|
|
|
160
161
|
run_time: parseFloat(td.duration) * 1000,
|
|
161
162
|
status: td.outcome,
|
|
162
163
|
stack: td.Output.StdOut,
|
|
163
|
-
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
|
|
164
|
+
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
|
|
164
165
|
}));
|
|
165
166
|
|
|
166
|
-
|
|
167
167
|
results.forEach(r => {
|
|
168
168
|
const test = tests.find(t => t.id === r.id) || {};
|
|
169
169
|
r.suite_title = test.suite_title;
|
|
@@ -223,7 +223,7 @@ class XmlReader {
|
|
|
223
223
|
|
|
224
224
|
if (testCase.failure) {
|
|
225
225
|
message = testCase.failure.message;
|
|
226
|
-
stack = testCase.failure['stack-trace']
|
|
226
|
+
stack = testCase.failure['stack-trace'];
|
|
227
227
|
}
|
|
228
228
|
if (testCase.reason) {
|
|
229
229
|
message = testCase.reason.message;
|
|
@@ -250,7 +250,6 @@ class XmlReader {
|
|
|
250
250
|
suite_title,
|
|
251
251
|
run_time,
|
|
252
252
|
});
|
|
253
|
-
|
|
254
253
|
});
|
|
255
254
|
});
|
|
256
255
|
});
|
|
@@ -283,12 +282,12 @@ class XmlReader {
|
|
|
283
282
|
passed_count: 0,
|
|
284
283
|
failed_count: 0,
|
|
285
284
|
skipped_count: 0,
|
|
286
|
-
}
|
|
285
|
+
};
|
|
287
286
|
this.tests.forEach(t => {
|
|
288
287
|
this.stats.tests_count++;
|
|
289
288
|
if (t.status === 'passed') this.stats.passed_count++;
|
|
290
289
|
if (t.status === 'failed') this.stats.failed_count++;
|
|
291
|
-
})
|
|
290
|
+
});
|
|
292
291
|
if (this.stats.failed_count) this.stats.status = 'failed';
|
|
293
292
|
|
|
294
293
|
return this.stats;
|
|
@@ -297,7 +296,7 @@ class XmlReader {
|
|
|
297
296
|
fetchSourceCode() {
|
|
298
297
|
this.tests.forEach(t => {
|
|
299
298
|
try {
|
|
300
|
-
const file = this.adapter.getFilePath(t)
|
|
299
|
+
const file = this.adapter.getFilePath(t);
|
|
301
300
|
if (!file) return;
|
|
302
301
|
|
|
303
302
|
if (!this.stats.language) {
|
|
@@ -310,16 +309,16 @@ class XmlReader {
|
|
|
310
309
|
}
|
|
311
310
|
|
|
312
311
|
if (!fs.existsSync(file)) {
|
|
313
|
-
debug('Failed to open file with the source code', file)
|
|
312
|
+
debug('Failed to open file with the source code', file);
|
|
314
313
|
return;
|
|
315
314
|
}
|
|
316
315
|
const contents = fs.readFileSync(file).toString();
|
|
317
|
-
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language })
|
|
316
|
+
t.code = fetchSourceCode(contents, { ...t, lang: this.stats.language });
|
|
318
317
|
if (t.code) debug('Fetched code for test %s', t.title);
|
|
319
|
-
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language })
|
|
318
|
+
t.test_id = fetchIdFromCode(t.code, { lang: this.stats.language });
|
|
320
319
|
if (t.test_id) debug('Fetched test id %s for test %s', t.test_id, t.title);
|
|
321
320
|
} catch (err) {
|
|
322
|
-
debug(err)
|
|
321
|
+
debug(err);
|
|
323
322
|
}
|
|
324
323
|
});
|
|
325
324
|
}
|
|
@@ -327,21 +326,22 @@ class XmlReader {
|
|
|
327
326
|
formatTests() {
|
|
328
327
|
this.tests.forEach(t => {
|
|
329
328
|
if (t.file) {
|
|
330
|
-
t.file = t.file.replace(process.cwd() + path.sep, '')
|
|
329
|
+
t.file = t.file.replace(process.cwd() + path.sep, '');
|
|
331
330
|
}
|
|
332
331
|
|
|
333
|
-
this.adapter.formatTest(t)
|
|
332
|
+
this.adapter.formatTest(t);
|
|
334
333
|
|
|
335
334
|
t.title = humanize(t.title);
|
|
336
335
|
});
|
|
337
336
|
}
|
|
338
337
|
|
|
339
338
|
formatErrors() {
|
|
340
|
-
this.tests
|
|
341
|
-
t
|
|
342
|
-
t
|
|
343
|
-
|
|
344
|
-
|
|
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
|
+
});
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
formatStack(t) {
|
|
@@ -359,7 +359,7 @@ class XmlReader {
|
|
|
359
359
|
async uploadArtifacts() {
|
|
360
360
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
361
361
|
let files = [];
|
|
362
|
-
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));
|
|
363
363
|
files = [...files, ...fetchFilesFromStackTrace(test.stack)];
|
|
364
364
|
|
|
365
365
|
if (!files.length) continue;
|
|
@@ -378,7 +378,7 @@ class XmlReader {
|
|
|
378
378
|
group_title: this.requestParams.group_title,
|
|
379
379
|
};
|
|
380
380
|
|
|
381
|
-
debug(
|
|
381
|
+
debug('Run', runParams);
|
|
382
382
|
|
|
383
383
|
return Promise.all(this.pipes.map(p => p.createRun(runParams)));
|
|
384
384
|
}
|
|
@@ -391,12 +391,10 @@ class XmlReader {
|
|
|
391
391
|
this.formatErrors();
|
|
392
392
|
this.formatTests();
|
|
393
393
|
|
|
394
|
-
debug(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
tests: this.tests,
|
|
399
|
-
})
|
|
394
|
+
debug('Uploading data', {
|
|
395
|
+
...this.stats,
|
|
396
|
+
tests: this.tests,
|
|
397
|
+
});
|
|
400
398
|
|
|
401
399
|
const dataString = {
|
|
402
400
|
...this.stats,
|
|
@@ -411,59 +409,62 @@ class XmlReader {
|
|
|
411
409
|
|
|
412
410
|
module.exports = XmlReader;
|
|
413
411
|
|
|
414
|
-
|
|
415
412
|
function reduceTestCases(prev, item) {
|
|
416
413
|
let testCases = item.testcase;
|
|
417
414
|
if (!testCases) testCases = item['test-case'];
|
|
418
415
|
if (!Array.isArray(testCases)) {
|
|
419
|
-
testCases = [testCases]
|
|
416
|
+
testCases = [testCases];
|
|
420
417
|
}
|
|
421
418
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
422
419
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
423
|
-
testCases
|
|
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
|
-
|
|
459
|
-
|
|
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
|
+
});
|
|
460
461
|
return prev;
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
function processTestSuite(testsuite) {
|
|
464
465
|
if (!testsuite) return [];
|
|
465
|
-
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite)
|
|
466
|
-
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']);
|
|
467
468
|
|
|
468
469
|
let suites = testsuite;
|
|
469
470
|
if (!Array.isArray(testsuite)) {
|