@testomatio/reporter 1.4.6-proxy-support → 1.4.7
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 +12 -13
- package/lib/adapter/playwright.js +50 -24
- package/lib/bin/reportXml.js +4 -9
- package/lib/client.js +3 -3
- package/lib/pipe/gitlab.js +4 -4
- package/lib/pipe/testomatio.js +1 -1
- package/lib/xmlReader.js +17 -7
- package/package.json +1 -1
package/lib/adapter/codecept.js
CHANGED
|
@@ -207,18 +207,18 @@ function CodeceptReporter(config) {
|
|
|
207
207
|
services.setContext(null);
|
|
208
208
|
|
|
209
209
|
client.addTestRun(STATUS.FAILED, {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
210
|
+
...stripExampleFromTitle(title),
|
|
211
|
+
rid: id,
|
|
212
|
+
test_id: getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`),
|
|
213
|
+
suite_title: test.parent && test.parent.title,
|
|
214
|
+
error,
|
|
215
|
+
message: testObj.message,
|
|
216
|
+
time: getDuration(test),
|
|
217
|
+
files,
|
|
218
|
+
steps: global.testomatioDataStore?.steps?.join('\n') || null,
|
|
219
|
+
logs,
|
|
220
|
+
manuallyAttachedArtifacts,
|
|
221
|
+
meta: keyValues,
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
debug('artifacts', artifacts);
|
|
@@ -319,7 +319,6 @@ async function uploadAttachments(client, attachments, messagePrefix, attachmentT
|
|
|
319
319
|
});
|
|
320
320
|
|
|
321
321
|
await Promise.all(promises);
|
|
322
|
-
|
|
323
322
|
}
|
|
324
323
|
|
|
325
324
|
function getTestAndMessage(title) {
|
|
@@ -39,14 +39,15 @@ class PlaywrightReporter {
|
|
|
39
39
|
if (!this.client) return;
|
|
40
40
|
|
|
41
41
|
const { title } = test;
|
|
42
|
-
|
|
43
42
|
const { error, duration } = result;
|
|
44
|
-
|
|
45
43
|
const suite_title = test.parent ? test.parent?.title : path.basename(test?.location?.file);
|
|
46
44
|
|
|
47
45
|
const steps = [];
|
|
48
46
|
for (const step of result.steps) {
|
|
49
|
-
appendStep(step
|
|
47
|
+
const appendedStep = appendStep(step);
|
|
48
|
+
if (appendedStep) {
|
|
49
|
+
steps.push(appendedStep);
|
|
50
|
+
}
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
const fullTestTitle = getTestContextName(test);
|
|
@@ -56,22 +57,21 @@ class PlaywrightReporter {
|
|
|
56
57
|
}
|
|
57
58
|
const manuallyAttachedArtifacts = services.artifacts.get(fullTestTitle);
|
|
58
59
|
const keyValues = services.keyValues.get(fullTestTitle);
|
|
59
|
-
|
|
60
60
|
const rid = test.id || test.testId || uuidv4();
|
|
61
61
|
|
|
62
62
|
const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
63
|
+
rid,
|
|
64
|
+
error,
|
|
65
|
+
test_id: getTestomatIdFromTestTitle(`${title} ${test.tags?.join(' ')}`),
|
|
66
|
+
suite_title,
|
|
67
|
+
title,
|
|
68
|
+
steps: steps.length ? steps : undefined,
|
|
69
|
+
time: duration,
|
|
70
|
+
logs,
|
|
71
|
+
manuallyAttachedArtifacts,
|
|
72
|
+
meta: keyValues,
|
|
73
|
+
file: test.location?.file,
|
|
74
|
+
});
|
|
75
75
|
|
|
76
76
|
this.uploads.push({
|
|
77
77
|
rid,
|
|
@@ -146,18 +146,44 @@ function checkStatus(status) {
|
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
function appendStep(step,
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
function appendStep(step, shift = 0) {
|
|
150
|
+
let newCategory = step.category;
|
|
151
|
+
switch (newCategory) {
|
|
152
|
+
case 'test.step':
|
|
153
|
+
newCategory = 'user';
|
|
154
|
+
break;
|
|
155
|
+
case 'hook':
|
|
156
|
+
newCategory = 'hook';
|
|
157
|
+
break;
|
|
158
|
+
case 'attach':
|
|
159
|
+
return null; // Skip steps with category 'attach'
|
|
160
|
+
default:
|
|
161
|
+
newCategory = 'framework';
|
|
156
162
|
}
|
|
157
163
|
|
|
164
|
+
const formattedSteps = [];
|
|
158
165
|
for (const child of step.steps || []) {
|
|
159
|
-
appendStep(child,
|
|
166
|
+
const appendedChild = appendStep(child, shift + 2);
|
|
167
|
+
if (appendedChild) {
|
|
168
|
+
formattedSteps.push(appendedChild);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const resultStep = {
|
|
173
|
+
category: newCategory,
|
|
174
|
+
title: step.title,
|
|
175
|
+
duration: step.duration,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
if (formattedSteps.length) {
|
|
179
|
+
resultStep.steps = formattedSteps;
|
|
160
180
|
}
|
|
181
|
+
|
|
182
|
+
if (step.error !== undefined) {
|
|
183
|
+
resultStep.error = step.error;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return resultStep;
|
|
161
187
|
}
|
|
162
188
|
|
|
163
189
|
function tmpFile(prefix = 'tmp.') {
|
package/lib/bin/reportXml.js
CHANGED
|
@@ -44,15 +44,10 @@ program
|
|
|
44
44
|
|
|
45
45
|
let timeoutTimer;
|
|
46
46
|
if (opts.timelimit) {
|
|
47
|
-
timeoutTimer = setTimeout(
|
|
48
|
-
(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
);
|
|
52
|
-
process.exit(0);
|
|
53
|
-
},
|
|
54
|
-
parseInt(opts.timelimit, 10) * 1000,
|
|
55
|
-
);
|
|
47
|
+
timeoutTimer = setTimeout(() => {
|
|
48
|
+
console.log(`⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`);
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}, parseInt(opts.timelimit, 10) * 1000);
|
|
56
51
|
}
|
|
57
52
|
|
|
58
53
|
try {
|
package/lib/client.js
CHANGED
|
@@ -227,7 +227,7 @@ class Client {
|
|
|
227
227
|
/**
|
|
228
228
|
*
|
|
229
229
|
* Updates the status of the current test run and finishes the run.
|
|
230
|
-
* @param {'passed' | 'failed' | 'finished'} status - The status of the current test run.
|
|
230
|
+
* @param {'passed' | 'failed' | 'skipped' | 'finished'} status - The status of the current test run.
|
|
231
231
|
* Must be one of "passed", "failed", or "finished"
|
|
232
232
|
* @param {boolean} [isParallel] - Whether the current test run was executed in parallel with other tests.
|
|
233
233
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
@@ -276,7 +276,7 @@ class Client {
|
|
|
276
276
|
*/
|
|
277
277
|
formatLogs({ error, steps, logs }) {
|
|
278
278
|
error = error?.trim();
|
|
279
|
-
steps = steps?.trim();
|
|
279
|
+
steps = typeof steps === 'string' ? steps?.trim() : steps;
|
|
280
280
|
logs = logs?.trim();
|
|
281
281
|
|
|
282
282
|
let testLogs = '';
|
|
@@ -299,7 +299,7 @@ class Client {
|
|
|
299
299
|
stack += `${message}\n`;
|
|
300
300
|
|
|
301
301
|
if (error.diff) {
|
|
302
|
-
// diff for vitest
|
|
302
|
+
// diff for vitest
|
|
303
303
|
stack += error.diff;
|
|
304
304
|
stack += '\n\n';
|
|
305
305
|
} else if (error.actual && error.expected && error.actual !== error.expected) {
|
package/lib/pipe/gitlab.js
CHANGED
|
@@ -79,13 +79,13 @@ class GitLabPipe {
|
|
|
79
79
|
let summary = `${this.hiddenCommentData}
|
|
80
80
|
|
|
81
81
|
| [](https://testomat.io) | ${statusEmoji(
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
runParams.status,
|
|
83
|
+
)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
84
84
|
| --- | --- |
|
|
85
85
|
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
86
86
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
'passed',
|
|
88
|
+
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
89
89
|
| Duration | 🕐 **${humanizeDuration(
|
|
90
90
|
parseInt(
|
|
91
91
|
this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -350,7 +350,7 @@ class TestomatioPipe {
|
|
|
350
350
|
if (!this.runId) return;
|
|
351
351
|
|
|
352
352
|
// add test ID + run ID
|
|
353
|
-
data.rid = `${this.runId}-${data.rid}`;
|
|
353
|
+
if (data.rid) data.rid = `${this.runId}-${data.rid}`;
|
|
354
354
|
data.api_key = this.apiKey;
|
|
355
355
|
data.create = this.createNewTests;
|
|
356
356
|
|
package/lib/xmlReader.js
CHANGED
|
@@ -2,6 +2,7 @@ const debug = require('debug')('@testomatio/reporter:xml');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
+
const { randomUUID } = require('crypto');
|
|
5
6
|
const { XMLParser } = require('fast-xml-parser');
|
|
6
7
|
const { APP_PREFIX, STATUS } = require('./constants');
|
|
7
8
|
const {
|
|
@@ -17,6 +18,8 @@ const pipesFactory = require('./pipe');
|
|
|
17
18
|
const adapterFactory = require('./junit-adapter');
|
|
18
19
|
const config = require('./config');
|
|
19
20
|
|
|
21
|
+
const ridRunId = randomUUID();
|
|
22
|
+
|
|
20
23
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
21
24
|
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } = process.env;
|
|
22
25
|
|
|
@@ -457,17 +460,20 @@ function reduceTestCases(prev, item) {
|
|
|
457
460
|
if (testCaseItem.error && testCaseItem.error['#text']) stack = testCaseItem.error['#text'];
|
|
458
461
|
if (!message) message = stack.trim().split('\n')[0];
|
|
459
462
|
|
|
463
|
+
const isParametrized = item.type === 'ParameterizedMethod';
|
|
464
|
+
const preferClassname = reduceOptions.preferClassname || isParametrized;
|
|
465
|
+
|
|
460
466
|
// SpecFlow config
|
|
461
|
-
let { title, tags } = fetchProperties(
|
|
467
|
+
let { title, tags } = fetchProperties(isParametrized ? item : testCaseItem);
|
|
462
468
|
let example = null;
|
|
463
|
-
const suiteTitle =
|
|
469
|
+
const suiteTitle = preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname;
|
|
464
470
|
|
|
465
471
|
title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
|
|
466
472
|
tags ||= [];
|
|
467
473
|
|
|
468
|
-
const exampleMatches =
|
|
474
|
+
const exampleMatches = testCaseItem.name?.match(/\S\((.*?)\)/);
|
|
469
475
|
if (exampleMatches) {
|
|
470
|
-
example = { ...exampleMatches[1].split(',').map(v => v.replace(
|
|
476
|
+
example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
|
|
471
477
|
title = title.replace(/\(.*?\)/, '').trim();
|
|
472
478
|
}
|
|
473
479
|
|
|
@@ -481,12 +487,16 @@ function reduceTestCases(prev, item) {
|
|
|
481
487
|
if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
|
|
482
488
|
if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
|
|
483
489
|
|
|
490
|
+
let rid = null;
|
|
491
|
+
if (testCaseItem.id) rid = `${ridRunId}-${testCaseItem.id}`;
|
|
492
|
+
|
|
484
493
|
prev.push({
|
|
485
|
-
|
|
494
|
+
rid,
|
|
486
495
|
file,
|
|
487
496
|
stack,
|
|
488
497
|
example,
|
|
489
498
|
tags,
|
|
499
|
+
create: true,
|
|
490
500
|
test_id: testId,
|
|
491
501
|
message,
|
|
492
502
|
line: testCaseItem.lineno,
|
|
@@ -510,9 +520,9 @@ function processTestSuite(testsuite) {
|
|
|
510
520
|
suites = [testsuite];
|
|
511
521
|
}
|
|
512
522
|
|
|
513
|
-
const
|
|
523
|
+
const subSuites = suites.filter(s => s['test-suite'] && !testsuite['test-case']);
|
|
514
524
|
|
|
515
|
-
return
|
|
525
|
+
return [...subSuites.map(s => processTestSuite(s['test-suite'])), ...suites.reduce(reduceTestCases, [])].flat();
|
|
516
526
|
}
|
|
517
527
|
|
|
518
528
|
function fetchProperties(item) {
|