@testomatio/reporter 2.7.4-beta.allure-1 → 2.7.5
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 +9 -4
- package/lib/adapter/vitest.d.ts +22 -3
- package/lib/adapter/vitest.js +250 -26
- package/lib/bin/cli.js +0 -28
- package/lib/client.d.ts +2 -1
- package/lib/client.js +3 -2
- package/lib/junit-adapter/index.js +0 -4
- package/lib/pipe/bitbucket.js +16 -1
- package/lib/pipe/html.js +24 -5
- package/lib/template/testomatio.hbs +131 -20
- package/lib/utils/utils.js +0 -9
- package/lib/xmlReader.js +7 -5
- package/package.json +1 -1
- package/src/adapter/vitest.js +253 -26
- package/src/bin/cli.js +0 -35
- package/src/client.js +3 -2
- package/src/junit-adapter/index.js +0 -4
- package/src/pipe/bitbucket.js +23 -1
- package/src/pipe/html.js +24 -5
- package/src/template/testomatio.hbs +131 -20
- package/src/utils/utils.js +0 -5
- package/src/xmlReader.js +9 -6
- package/lib/allureReader.d.ts +0 -65
- package/lib/allureReader.js +0 -461
- package/lib/junit-adapter/kotlin.d.ts +0 -5
- package/lib/junit-adapter/kotlin.js +0 -46
- package/src/allureReader.js +0 -540
- package/src/junit-adapter/kotlin.js +0 -48
package/src/bin/cli.js
CHANGED
|
@@ -6,7 +6,6 @@ import { glob } from 'glob';
|
|
|
6
6
|
import createDebugMessages from 'debug';
|
|
7
7
|
import TestomatClient from '../client.js';
|
|
8
8
|
import XmlReader from '../xmlReader.js';
|
|
9
|
-
import AllureReader from '../allureReader.js';
|
|
10
9
|
import { APP_PREFIX, STATUS } from '../constants.js';
|
|
11
10
|
import { cleanLatestRunId, getPackageVersion, applyFilter } from '../utils/utils.js';
|
|
12
11
|
import { config } from '../config.js';
|
|
@@ -265,40 +264,6 @@ program
|
|
|
265
264
|
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
266
265
|
});
|
|
267
266
|
|
|
268
|
-
program
|
|
269
|
-
.command('allure')
|
|
270
|
-
.description('Parse Allure result files and upload to Testomat.io')
|
|
271
|
-
.argument('<pattern>', 'Allure result directory pattern')
|
|
272
|
-
.option('-d, --dir <dir>', 'Project directory')
|
|
273
|
-
.option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
|
|
274
|
-
.option('--with-package', 'Keep full package path in file names (default: strip package prefix)')
|
|
275
|
-
.action(async (pattern, opts) => {
|
|
276
|
-
const runReader = new AllureReader({ withPackage: opts.withPackage });
|
|
277
|
-
|
|
278
|
-
let timeoutTimer;
|
|
279
|
-
if (opts.timelimit) {
|
|
280
|
-
timeoutTimer = setTimeout(
|
|
281
|
-
() => {
|
|
282
|
-
console.log(
|
|
283
|
-
`⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`,
|
|
284
|
-
);
|
|
285
|
-
process.exit(0);
|
|
286
|
-
},
|
|
287
|
-
parseInt(opts.timelimit, 10) * 1000,
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
try {
|
|
292
|
-
await runReader.parse(pattern);
|
|
293
|
-
await runReader.createRun();
|
|
294
|
-
await runReader.uploadData();
|
|
295
|
-
} catch (err) {
|
|
296
|
-
console.log(APP_PREFIX, 'Error uploading Allure results:', err);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
267
|
program
|
|
303
268
|
.command('upload-artifacts')
|
|
304
269
|
.description('Upload artifacts to Testomat.io')
|
package/src/client.js
CHANGED
|
@@ -360,10 +360,11 @@ class Client {
|
|
|
360
360
|
*
|
|
361
361
|
* Updates the status of the current test run and finishes the run.
|
|
362
362
|
* @param {'passed' | 'failed' | 'skipped' | 'finished'} status - The status of the current test run.
|
|
363
|
+
* @param {Partial<import('../types/types.js').RunData>} [params] - Additional run params (e.g. duration).
|
|
363
364
|
* Must be one of "passed", "failed", or "finished"
|
|
364
365
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
365
366
|
*/
|
|
366
|
-
async updateRunStatus(status) {
|
|
367
|
+
async updateRunStatus(status, params = {}) {
|
|
367
368
|
this.pipes ||= await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
368
369
|
this.runId ||= readLatestRunId();
|
|
369
370
|
|
|
@@ -371,7 +372,7 @@ class Client {
|
|
|
371
372
|
// all pipes disabled, skipping
|
|
372
373
|
if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
373
374
|
|
|
374
|
-
const runParams = { status };
|
|
375
|
+
const runParams = { ...params, status };
|
|
375
376
|
|
|
376
377
|
this.queue = this.queue
|
|
377
378
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
@@ -4,7 +4,6 @@ import JavaAdapter from './java.js';
|
|
|
4
4
|
import PythonAdapter from './python.js';
|
|
5
5
|
import RubyAdapter from './ruby.js';
|
|
6
6
|
import CSharpAdapter from './csharp.js';
|
|
7
|
-
import KotlinAdapter from './kotlin.js';
|
|
8
7
|
|
|
9
8
|
function AdapterFactory(lang, opts) {
|
|
10
9
|
if (lang === 'java') {
|
|
@@ -22,9 +21,6 @@ function AdapterFactory(lang, opts) {
|
|
|
22
21
|
if (lang === 'c#' || lang === 'csharp') {
|
|
23
22
|
return new CSharpAdapter(opts);
|
|
24
23
|
}
|
|
25
|
-
if (lang === 'kotlin') {
|
|
26
|
-
return new KotlinAdapter(opts);
|
|
27
|
-
}
|
|
28
24
|
|
|
29
25
|
return new Adapter(opts);
|
|
30
26
|
}
|
package/src/pipe/bitbucket.js
CHANGED
|
@@ -78,6 +78,20 @@ export class BitbucketPipe {
|
|
|
78
78
|
|
|
79
79
|
async finishRun(runParams) {
|
|
80
80
|
if (!this.isEnabled) return;
|
|
81
|
+
if (!this.ENV.BITBUCKET_PR_ID) {
|
|
82
|
+
log.warn(
|
|
83
|
+
pc.yellow('Bitbucket'),
|
|
84
|
+
'Skipping PR comment: BITBUCKET_PR_ID is not set. Run this pipe in a Bitbucket pull-requests pipeline.',
|
|
85
|
+
);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!this.ENV.BITBUCKET_WORKSPACE || !this.ENV.BITBUCKET_REPO_SLUG) {
|
|
89
|
+
log.warn(
|
|
90
|
+
pc.yellow('Bitbucket'),
|
|
91
|
+
'Skipping PR comment: BITBUCKET_WORKSPACE or BITBUCKET_REPO_SLUG is missing.',
|
|
92
|
+
);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
81
95
|
|
|
82
96
|
if (runParams.tests) runParams.tests.forEach(t => this.addTest(t));
|
|
83
97
|
|
|
@@ -196,12 +210,20 @@ export class BitbucketPipe {
|
|
|
196
210
|
|
|
197
211
|
log.info(pc.yellow('Bitbucket'), `Report created: ${pc.magenta(commentURL)}`);
|
|
198
212
|
} catch (err) {
|
|
213
|
+
const isForbiddenError = `${err}`.includes('Forbidden') || `${err}`.includes('403');
|
|
214
|
+
const scopeHint =
|
|
215
|
+
isForbiddenError
|
|
216
|
+
? '\nHint: use a token that can write PR comments '
|
|
217
|
+
+ '(recommended: Repository Access Token with Pull requests: Write '
|
|
218
|
+
+ 'and Repository: Read) and run inside a pull-requests pipeline '
|
|
219
|
+
+ 'where BITBUCKET_PR_ID is available.'
|
|
220
|
+
: '';
|
|
199
221
|
console.error(
|
|
200
222
|
APP_PREFIX,
|
|
201
223
|
pc.yellow('Bitbucket'),
|
|
202
224
|
`Couldn't create Bitbucket report\n${err}.
|
|
203
225
|
Request URL: ${commentsRequestURL}
|
|
204
|
-
Request data: ${body}`,
|
|
226
|
+
Request data: ${body}${scopeHint}`,
|
|
205
227
|
);
|
|
206
228
|
}
|
|
207
229
|
}
|
package/src/pipe/html.js
CHANGED
|
@@ -230,8 +230,10 @@ class HtmlPipe {
|
|
|
230
230
|
];
|
|
231
231
|
|
|
232
232
|
test.artifactsUploaded = allPossibleArtifacts.some(artifact => {
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
let link = artifact?.link || artifact?.path;
|
|
234
|
+
if (!link) return false;
|
|
235
|
+
if (typeof link !== 'string') link = String(link);
|
|
236
|
+
return link.startsWith('http://') || link.startsWith('https://');
|
|
235
237
|
});
|
|
236
238
|
|
|
237
239
|
normalizeRetries(test);
|
|
@@ -1083,6 +1085,18 @@ function normalizeArtifacts(test) {
|
|
|
1083
1085
|
return allArtifacts
|
|
1084
1086
|
.map(artifact => {
|
|
1085
1087
|
if (typeof artifact === 'string') {
|
|
1088
|
+
if (/^https?:\/\//i.test(artifact)) {
|
|
1089
|
+
const base = path.basename(new URL(artifact).pathname) || artifact;
|
|
1090
|
+
|
|
1091
|
+
return {
|
|
1092
|
+
name: base,
|
|
1093
|
+
title: base,
|
|
1094
|
+
path: artifact,
|
|
1095
|
+
fsPath: null,
|
|
1096
|
+
relativePath: artifact,
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1086
1100
|
const abs = path.isAbsolute(artifact) ? artifact : path.resolve(process.cwd(), artifact);
|
|
1087
1101
|
const href = artifact.startsWith('file://') ? artifact : fileUrl(abs, { resolve: true });
|
|
1088
1102
|
const base = path.basename(abs);
|
|
@@ -1099,9 +1113,14 @@ function normalizeArtifacts(test) {
|
|
|
1099
1113
|
if (artifact?.path) {
|
|
1100
1114
|
const raw = String(artifact.path);
|
|
1101
1115
|
const isFileUrl = raw.startsWith('file://');
|
|
1102
|
-
const
|
|
1103
|
-
const
|
|
1104
|
-
const
|
|
1116
|
+
const isHttpUrl = /^https?:\/\//i.test(raw);
|
|
1117
|
+
const abs = isFileUrl || isHttpUrl ? null : path.isAbsolute(raw) ? raw : path.resolve(process.cwd(), raw);
|
|
1118
|
+
const href = isFileUrl || isHttpUrl ? raw : fileUrl(abs, { resolve: true });
|
|
1119
|
+
const base = abs
|
|
1120
|
+
? path.basename(abs)
|
|
1121
|
+
: isHttpUrl
|
|
1122
|
+
? path.basename(new URL(raw).pathname) || artifact.name || artifact.title || 'attachment'
|
|
1123
|
+
: artifact.name || artifact.title || 'attachment';
|
|
1105
1124
|
|
|
1106
1125
|
return {
|
|
1107
1126
|
...artifact,
|
|
@@ -741,6 +741,18 @@
|
|
|
741
741
|
min-width: 0;
|
|
742
742
|
}
|
|
743
743
|
|
|
744
|
+
.step-header {
|
|
745
|
+
display: flex;
|
|
746
|
+
align-items: center;
|
|
747
|
+
gap: 12px;
|
|
748
|
+
width: 100%;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
.step-main {
|
|
752
|
+
flex: 1;
|
|
753
|
+
min-width: 0;
|
|
754
|
+
}
|
|
755
|
+
|
|
744
756
|
.step-text {
|
|
745
757
|
font-size: 14px;
|
|
746
758
|
color: var(--gray-700);
|
|
@@ -755,6 +767,35 @@
|
|
|
755
767
|
margin-top: 4px;
|
|
756
768
|
}
|
|
757
769
|
|
|
770
|
+
.step-toggle {
|
|
771
|
+
width: 24px;
|
|
772
|
+
height: 24px;
|
|
773
|
+
border: none;
|
|
774
|
+
border-radius: 6px;
|
|
775
|
+
background: var(--gray-100);
|
|
776
|
+
color: var(--gray-600);
|
|
777
|
+
display: inline-flex;
|
|
778
|
+
align-items: center;
|
|
779
|
+
justify-content: center;
|
|
780
|
+
cursor: pointer;
|
|
781
|
+
flex-shrink: 0;
|
|
782
|
+
transition: var(--transition);
|
|
783
|
+
margin-top: 2px;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
.step-toggle:hover {
|
|
787
|
+
background: var(--gray-200);
|
|
788
|
+
color: var(--gray-700);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
.step-toggle.collapsed i {
|
|
792
|
+
transform: rotate(-90deg);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.step-toggle i {
|
|
796
|
+
transition: transform 0.2s ease;
|
|
797
|
+
}
|
|
798
|
+
|
|
758
799
|
.step-status {
|
|
759
800
|
width: 8px;
|
|
760
801
|
height: 8px;
|
|
@@ -790,6 +831,10 @@
|
|
|
790
831
|
padding-left: 0;
|
|
791
832
|
}
|
|
792
833
|
|
|
834
|
+
.step-children.collapsed {
|
|
835
|
+
display: none;
|
|
836
|
+
}
|
|
837
|
+
|
|
793
838
|
.step-children::before {
|
|
794
839
|
content: '';
|
|
795
840
|
position: absolute;
|
|
@@ -867,6 +912,30 @@
|
|
|
867
912
|
border-left-color: var(--info-color);
|
|
868
913
|
}
|
|
869
914
|
|
|
915
|
+
.message-block.passed {
|
|
916
|
+
border-left-color: var(--success-color);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.message-block.skipped,
|
|
920
|
+
.message-block.todo {
|
|
921
|
+
border-left-color: var(--warning-color);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.message-block.failed {
|
|
925
|
+
border-left-color: var(--danger-color);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.step-attachments {
|
|
929
|
+
display: grid;
|
|
930
|
+
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
|
931
|
+
gap: 12px;
|
|
932
|
+
margin-top: 12px;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
.step-attachments .attachment-item {
|
|
936
|
+
min-height: 120px;
|
|
937
|
+
}
|
|
938
|
+
|
|
870
939
|
.metadata-grid {
|
|
871
940
|
display: grid;
|
|
872
941
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
@@ -2377,6 +2446,11 @@
|
|
|
2377
2446
|
const isTodo = statusClass === 'todo';
|
|
2378
2447
|
const isSkippedOrTodo = statusClass === 'skipped' || statusClass === 'todo';
|
|
2379
2448
|
const displayTime = isSkippedOrTodo ? '-' : formatDuration(test.run_time || 0);
|
|
2449
|
+
const hasMessage = hasMeaningfulMessage(test.message);
|
|
2450
|
+
const initialTab = getInitialTestTab({ isTodo, hasMessage, hasSteps: test.stepsArray?.length || test.steps });
|
|
2451
|
+
const initialMessageClass = initialTab === 'message' ? ' active' : '';
|
|
2452
|
+
const initialStepsClass = initialTab === 'steps' ? ' active' : '';
|
|
2453
|
+
const initialInfoClass = initialTab === 'info' ? ' active' : '';
|
|
2380
2454
|
|
|
2381
2455
|
testItem.innerHTML = `
|
|
2382
2456
|
<div class='test-header' onclick='toggleTest(this)'>
|
|
@@ -2411,14 +2485,14 @@
|
|
|
2411
2485
|
<div class='test-content'>
|
|
2412
2486
|
<div class='test-tabs'>
|
|
2413
2487
|
${isTodo ? `
|
|
2414
|
-
<button class='test-tab
|
|
2488
|
+
<button class='test-tab${initialInfoClass}' onclick='showTestTab(this, \"info\")'>
|
|
2415
2489
|
<i class='fas fa-info-circle'></i> Info
|
|
2416
2490
|
</button>
|
|
2417
2491
|
` : `
|
|
2418
|
-
<button class='test-tab
|
|
2492
|
+
<button class='test-tab${initialStepsClass}' onclick='showTestTab(this, \"steps\")'>
|
|
2419
2493
|
<i class='fas fa-list-ol'></i> Steps
|
|
2420
2494
|
</button>
|
|
2421
|
-
<button class='test-tab' onclick='showTestTab(this, \"message\")'>
|
|
2495
|
+
<button class='test-tab${initialMessageClass}' onclick='showTestTab(this, \"message\")'>
|
|
2422
2496
|
<i class='fas fa-comment-dots'></i> Message
|
|
2423
2497
|
</button>
|
|
2424
2498
|
${hasStack ? `<button class='test-tab' onclick='showTestTab(this, \"stack\")'>
|
|
@@ -2439,7 +2513,7 @@
|
|
|
2439
2513
|
`}
|
|
2440
2514
|
</div>
|
|
2441
2515
|
${isTodo ? `
|
|
2442
|
-
<div class='test-tab-content
|
|
2516
|
+
<div class='test-tab-content${initialInfoClass}' data-tab='info'>
|
|
2443
2517
|
<div class='todo-info'>
|
|
2444
2518
|
<div class='todo-title'>
|
|
2445
2519
|
<i class='fas fa-list-ul'></i>
|
|
@@ -2456,7 +2530,7 @@
|
|
|
2456
2530
|
</div>
|
|
2457
2531
|
</div>
|
|
2458
2532
|
` : `
|
|
2459
|
-
<div class='test-tab-content
|
|
2533
|
+
<div class='test-tab-content${initialStepsClass}' data-tab='steps'>
|
|
2460
2534
|
<div class='steps-container' id='steps-${index}'>
|
|
2461
2535
|
${(() => {
|
|
2462
2536
|
const hasStepsArray = test.stepsArray && Array.isArray(test.stepsArray) && test.stepsArray.length > 0;
|
|
@@ -2476,7 +2550,7 @@
|
|
|
2476
2550
|
})()}
|
|
2477
2551
|
</div>
|
|
2478
2552
|
</div>
|
|
2479
|
-
<div class='test-tab-content' data-tab='message'>
|
|
2553
|
+
<div class='test-tab-content${initialMessageClass}' data-tab='message'>
|
|
2480
2554
|
<div class='message-block ${statusClass}'>
|
|
2481
2555
|
<strong>${statusClass.toUpperCase()}:</strong><br>
|
|
2482
2556
|
${test.message || 'No message available'}
|
|
@@ -2542,6 +2616,16 @@
|
|
|
2542
2616
|
}
|
|
2543
2617
|
}
|
|
2544
2618
|
|
|
2619
|
+
function hasMeaningfulMessage(message) {
|
|
2620
|
+
return typeof message === 'string' && message.trim().length > 0 && message !== 'No message available';
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
function getInitialTestTab({ isTodo, hasMessage }) {
|
|
2624
|
+
if (isTodo) return 'info';
|
|
2625
|
+
if (hasMessage) return 'message';
|
|
2626
|
+
return 'steps';
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2545
2629
|
function parseStepsToHtml(stepsText) {
|
|
2546
2630
|
if (!stepsText || stepsText === 'No steps recorded') {
|
|
2547
2631
|
return `<div class="step-item">
|
|
@@ -2629,25 +2713,33 @@
|
|
|
2629
2713
|
const status = step.status || 'passed';
|
|
2630
2714
|
const duration = step.duration || 0;
|
|
2631
2715
|
const category = step.category || '';
|
|
2716
|
+
const hasArtifacts = Array.isArray(step.artifacts) && step.artifacts.length > 0;
|
|
2632
2717
|
|
|
2633
2718
|
html += `
|
|
2634
2719
|
<div class="step-item step-level-${depth}" data-step-id="${stepId}">
|
|
2635
|
-
<div class="step-number">${stepNumber}</div>
|
|
2636
2720
|
<div class="step-content">
|
|
2637
|
-
<
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2721
|
+
<div class="step-header">
|
|
2722
|
+
<div class="step-number">${stepNumber}</div>
|
|
2723
|
+
<div class="step-main">
|
|
2724
|
+
<p class="step-text">${step.title || 'Untitled step'}</p>
|
|
2725
|
+
${category && category !== 'user' ? `<div class="step-category">${category}</div>` : ''}
|
|
2726
|
+
${duration > 0 ? `<div class="step-time"><span class="step-duration"><i class="fas fa-clock"></i> ${formatDuration(duration)}</span></div>` : ''}
|
|
2727
|
+
${hasArtifacts ? `<div class="step-attachments">${createAttachmentItems(step.artifacts)}</div>` : ''}
|
|
2728
|
+
</div>
|
|
2729
|
+
${hasChildren ? `
|
|
2730
|
+
<button class="step-toggle" type="button" aria-expanded="true" onclick="toggleStepChildren(this)">
|
|
2731
|
+
<i class="fas fa-chevron-down"></i>
|
|
2732
|
+
</button>
|
|
2733
|
+
` : ''}
|
|
2734
|
+
<div class="step-status ${status}"></div>
|
|
2642
2735
|
</div>
|
|
2643
2736
|
</div>
|
|
2644
|
-
<div class="step-status ${status}"></div>
|
|
2645
2737
|
</div>
|
|
2646
2738
|
`;
|
|
2647
2739
|
|
|
2648
2740
|
if (hasChildren) {
|
|
2649
2741
|
html += `
|
|
2650
|
-
<div class="step-children">
|
|
2742
|
+
<div class="step-children" data-parent-step-id="${stepId}">
|
|
2651
2743
|
${renderStepsTree(step.steps, depth + 1)}
|
|
2652
2744
|
</div>
|
|
2653
2745
|
`;
|
|
@@ -2673,19 +2765,38 @@
|
|
|
2673
2765
|
function createStepHtml(text, number, status = null, category = null) {
|
|
2674
2766
|
const statusClass = status || 'passed';
|
|
2675
2767
|
const categoryText = category && category !== 'user' ? `<div class="step-category">${category}</div>` : '';
|
|
2676
|
-
const durationIcon = status ? `<div class="step-duration"><i class="fas fa-clock"></i> auto</div>` : '';
|
|
2768
|
+
const durationIcon = status ? `<div class="step-time"><span class="step-duration"><i class="fas fa-clock"></i> auto</span></div>` : '';
|
|
2677
2769
|
|
|
2678
2770
|
return `<div class="step-item">
|
|
2679
|
-
<div class="step-number">${number}</div>
|
|
2680
2771
|
<div class="step-content">
|
|
2681
|
-
<
|
|
2682
|
-
|
|
2683
|
-
|
|
2772
|
+
<div class="step-header">
|
|
2773
|
+
<div class="step-number">${number}</div>
|
|
2774
|
+
<div class="step-main">
|
|
2775
|
+
<p class="step-text">${text}</p>
|
|
2776
|
+
${categoryText}
|
|
2777
|
+
${durationIcon}
|
|
2778
|
+
</div>
|
|
2779
|
+
<div class="step-status ${statusClass}"></div>
|
|
2780
|
+
</div>
|
|
2684
2781
|
</div>
|
|
2685
|
-
<div class="step-status ${statusClass}"></div>
|
|
2686
2782
|
</div>`;
|
|
2687
2783
|
}
|
|
2688
2784
|
|
|
2785
|
+
function toggleStepChildren(button) {
|
|
2786
|
+
const stepItem = button.closest('.step-item');
|
|
2787
|
+
if (!stepItem) return;
|
|
2788
|
+
|
|
2789
|
+
const stepId = stepItem.getAttribute('data-step-id');
|
|
2790
|
+
if (!stepId) return;
|
|
2791
|
+
|
|
2792
|
+
const children = stepItem.parentElement.querySelector(`.step-children[data-parent-step-id="${stepId}"]`);
|
|
2793
|
+
if (!children) return;
|
|
2794
|
+
|
|
2795
|
+
const collapsed = children.classList.toggle('collapsed');
|
|
2796
|
+
button.classList.toggle('collapsed', collapsed);
|
|
2797
|
+
button.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2689
2800
|
function createRetryAttempts(retries) {
|
|
2690
2801
|
const attempts = Array.isArray(retries?.attempts) ? retries.attempts : [];
|
|
2691
2802
|
if (!attempts.length) return '';
|
package/src/utils/utils.js
CHANGED
|
@@ -289,11 +289,6 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
289
289
|
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`@DisplayName("${title}`));
|
|
290
290
|
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
291
291
|
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
292
|
-
} else if (opts.lang === 'kotlin') {
|
|
293
|
-
lineIndex = lines.findIndex(l => l.includes(`fun test${title}`));
|
|
294
|
-
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`@DisplayName("${title}`));
|
|
295
|
-
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`fun ${title}`));
|
|
296
|
-
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
297
292
|
} else if (opts.lang === 'csharp') {
|
|
298
293
|
// Find the method declaration line
|
|
299
294
|
let methodLineIndex = lines.findIndex(l => l.includes(`public void ${title}(`));
|
package/src/xmlReader.js
CHANGED
|
@@ -769,17 +769,20 @@ function reduceTestCases(prev, item) {
|
|
|
769
769
|
|
|
770
770
|
function processTestSuite(testsuite) {
|
|
771
771
|
if (!testsuite) return [];
|
|
772
|
-
if (testsuite.testsuite) return processTestSuite(testsuite.testsuite);
|
|
772
|
+
if (testsuite.testsuite && !testsuite.testcase) return processTestSuite(testsuite.testsuite);
|
|
773
773
|
if (testsuite['test-suite'] && !testsuite['test-case']) return processTestSuite(testsuite['test-suite']);
|
|
774
774
|
|
|
775
775
|
let suites = testsuite;
|
|
776
|
-
if (!Array.isArray(testsuite))
|
|
777
|
-
suites = [testsuite];
|
|
778
|
-
}
|
|
776
|
+
if (!Array.isArray(testsuite)) suites = [testsuite];
|
|
779
777
|
|
|
780
|
-
const subSuites = suites.filter(
|
|
778
|
+
const subSuites = suites.filter(
|
|
779
|
+
s => (s['test-suite'] || s.testsuite) && !(s['test-case'] || s.testcase),
|
|
780
|
+
);
|
|
781
781
|
|
|
782
|
-
return [
|
|
782
|
+
return [
|
|
783
|
+
...suites.reduce(reduceTestCases, []),
|
|
784
|
+
...subSuites.map(s => processTestSuite(s['test-suite'] || s.testsuite)),
|
|
785
|
+
].flat();
|
|
783
786
|
}
|
|
784
787
|
|
|
785
788
|
function fetchProperties(item) {
|
package/lib/allureReader.d.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
export default AllureReader;
|
|
2
|
-
declare class AllureReader {
|
|
3
|
-
constructor(opts?: {});
|
|
4
|
-
requestParams: {
|
|
5
|
-
apiKey: any;
|
|
6
|
-
url: any;
|
|
7
|
-
title: string;
|
|
8
|
-
env: string;
|
|
9
|
-
group_title: string;
|
|
10
|
-
isBatchEnabled: boolean;
|
|
11
|
-
};
|
|
12
|
-
runId: any;
|
|
13
|
-
opts: {};
|
|
14
|
-
withPackage: any;
|
|
15
|
-
store: {};
|
|
16
|
-
pipesPromise: Promise<any[]>;
|
|
17
|
-
_tests: any[];
|
|
18
|
-
stats: {};
|
|
19
|
-
suites: {};
|
|
20
|
-
uploader: S3Uploader;
|
|
21
|
-
version: any;
|
|
22
|
-
set tests(value: any[]);
|
|
23
|
-
get tests(): any[];
|
|
24
|
-
createRun(): Promise<any[]>;
|
|
25
|
-
pipes: any;
|
|
26
|
-
parse(resultsPattern: any): {};
|
|
27
|
-
parseContainerFiles(containerFiles: any): void;
|
|
28
|
-
processAllureResult(result: any, resultsDir: any): {
|
|
29
|
-
rid: any;
|
|
30
|
-
title: any;
|
|
31
|
-
status: any;
|
|
32
|
-
suite_title: any;
|
|
33
|
-
file: string;
|
|
34
|
-
run_time: number;
|
|
35
|
-
steps: any;
|
|
36
|
-
message: any;
|
|
37
|
-
stack: any;
|
|
38
|
-
meta: {};
|
|
39
|
-
links: {
|
|
40
|
-
label: string;
|
|
41
|
-
}[];
|
|
42
|
-
artifacts: any[];
|
|
43
|
-
create: boolean;
|
|
44
|
-
overwrite: boolean;
|
|
45
|
-
};
|
|
46
|
-
mapStatus(status: any): any;
|
|
47
|
-
extractSuiteTitle(result: any): any;
|
|
48
|
-
stripNamespace(suiteName: any): any;
|
|
49
|
-
extractFile(result: any): string;
|
|
50
|
-
getFileExtension(result: any): any;
|
|
51
|
-
extractMeta(result: any): {};
|
|
52
|
-
extractLinks(result: any): {
|
|
53
|
-
label: string;
|
|
54
|
-
}[];
|
|
55
|
-
convertSteps(steps: any, depth?: number): any;
|
|
56
|
-
calculateRunTime(item: any): number;
|
|
57
|
-
convertParameters(parameters: any): {};
|
|
58
|
-
combineRetryAttempts(attempts: any): any;
|
|
59
|
-
calculateStats(): {};
|
|
60
|
-
fetchSourceCode(): void;
|
|
61
|
-
getLanguage(): any;
|
|
62
|
-
uploadArtifacts(): Promise<void>;
|
|
63
|
-
uploadData(): Promise<any[]>;
|
|
64
|
-
}
|
|
65
|
-
import { S3Uploader } from './uploader.js';
|