ortoni-report 1.1.1 → 1.1.3
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/changelog.md +35 -0
- package/dist/icon/fail.png +0 -0
- package/dist/icon/file.png +0 -0
- package/dist/icon/flaky.png +0 -0
- package/dist/icon/pass.png +0 -0
- package/dist/icon/retry.png +0 -0
- package/dist/icon/skip.png +0 -0
- package/dist/icon/test.png +0 -0
- package/dist/icon/timeout.png +0 -0
- package/dist/ortoni-report.d.ts +18 -2
- package/dist/ortoni-report.js +71 -27
- package/dist/ortoni-report.mjs +71 -27
- package/dist/report-template.hbs +215 -27
- package/dist/types/reporterConfig.js +2 -0
- package/dist/types/testResults.js +2 -0
- package/dist/utils/chart.js +11128 -0
- package/dist/utils/modal.js +76 -0
- package/dist/utils/utils.js +44 -0
- package/package.json +47 -49
- package/readme.md +15 -6
package/dist/report-template.hbs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<meta name="description" content="Plawright HTML report by LetCode Koushik">
|
|
7
8
|
<title>Playwright Test Report</title>
|
|
8
9
|
<link rel="icon" href="node_modules/ortoni-report/dist/icon/32.png" type="image/x-icon">
|
|
9
10
|
<link rel="stylesheet" href="node_modules/ortoni-report/dist/css/pico.css">
|
|
@@ -29,9 +30,15 @@
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
.highlight {
|
|
32
|
-
|
|
33
|
+
color: var(--pico-primary-background);
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
.filter.active {
|
|
37
|
+
background-color: var(--pico-primary-hover-background);
|
|
38
|
+
color: var(--pico-primary-hover-color);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
35
42
|
.text-success {
|
|
36
43
|
color: #28a745;
|
|
37
44
|
}
|
|
@@ -45,10 +52,19 @@
|
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
.text-flaky {
|
|
48
|
-
color: #
|
|
55
|
+
color: #b5e35e;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
div#testDetails {
|
|
59
|
+
position: sticky;
|
|
60
|
+
top: 0;
|
|
61
|
+
z-index: 1;
|
|
49
62
|
}
|
|
50
63
|
|
|
51
64
|
.sidebar {
|
|
65
|
+
overflow-y: auto;
|
|
66
|
+
max-height: calc(100vh - 100px);
|
|
67
|
+
/* Adjust as needed */
|
|
52
68
|
border-right: 1px solid #ddd;
|
|
53
69
|
padding-right: 10px;
|
|
54
70
|
}
|
|
@@ -64,6 +80,58 @@
|
|
|
64
80
|
display: none;
|
|
65
81
|
margin-bottom: 1rem;
|
|
66
82
|
}
|
|
83
|
+
|
|
84
|
+
.clickable {
|
|
85
|
+
cursor: pointer;
|
|
86
|
+
transition: background-color 0.3s;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.clickable:hover {
|
|
90
|
+
background-color: #607D8B;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
li[data-test-id] {
|
|
94
|
+
overflow: hidden;
|
|
95
|
+
text-overflow: ellipsis;
|
|
96
|
+
white-space: nowrap;
|
|
97
|
+
max-width: 25ch;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
li img {
|
|
101
|
+
max-width: 8%
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
summary img {
|
|
105
|
+
max-width: 5%
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
dialog article {
|
|
109
|
+
max-width: 768px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
::-webkit-scrollbar {
|
|
113
|
+
width: 0px;
|
|
114
|
+
background-color: transparent;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
::-webkit-scrollbar-thumb {
|
|
118
|
+
background-color: var(--pico-secondary-background);
|
|
119
|
+
;
|
|
120
|
+
border-radius: 0px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
::-webkit-scrollbar-thumb:hover {
|
|
124
|
+
background-color: var(--pico-secondary-background);
|
|
125
|
+
;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
::-webkit-scrollbar-track {
|
|
129
|
+
background-color: #f1f1f1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
::-webkit-scrollbar-corner {
|
|
133
|
+
background-color: #fff;
|
|
134
|
+
}
|
|
67
135
|
</style>
|
|
68
136
|
</head>
|
|
69
137
|
|
|
@@ -74,7 +142,6 @@
|
|
|
74
142
|
<div>
|
|
75
143
|
{{#if projectName}}<h1>{{projectName}}</h1>{{/if}}
|
|
76
144
|
</div>
|
|
77
|
-
{{!-- Dummy for now --}}
|
|
78
145
|
<div>
|
|
79
146
|
<input name="search" type="search" placeholder="Search by test title" />
|
|
80
147
|
</div>
|
|
@@ -87,11 +154,17 @@
|
|
|
87
154
|
<div>
|
|
88
155
|
{{#each groupedResults}}
|
|
89
156
|
<details>
|
|
90
|
-
<summary>
|
|
157
|
+
<summary>
|
|
158
|
+
<img src="node_modules/ortoni-report/dist/icon/file.png" alt="file name">
|
|
159
|
+
<span>{{@key}}<span>
|
|
160
|
+
</summary>
|
|
91
161
|
<ul>
|
|
92
162
|
{{#each this}}
|
|
93
163
|
<details>
|
|
94
|
-
<summary>
|
|
164
|
+
<summary>
|
|
165
|
+
<img src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
|
|
166
|
+
<span>{{@key}}<span>
|
|
167
|
+
</summary>
|
|
95
168
|
<ul>
|
|
96
169
|
{{#each this}}
|
|
97
170
|
<details>
|
|
@@ -99,7 +172,31 @@
|
|
|
99
172
|
<ul>
|
|
100
173
|
{{#each this}}
|
|
101
174
|
<li data-suite-name="{{suite}}" data-project-name="{{projectName}}"
|
|
102
|
-
data-test-id="{{index}}"
|
|
175
|
+
data-test-id="{{index}}" data-test-status="{{status}} {{retry}}">
|
|
176
|
+
{{#if isRetry}}
|
|
177
|
+
<img src="node_modules/ortoni-report/dist/icon/retry.png" alt="Retry">
|
|
178
|
+
{{/if}}
|
|
179
|
+
{{#if (eq status "passed")}}
|
|
180
|
+
<img src="node_modules/ortoni-report/dist/icon/pass.png" alt="Pass">
|
|
181
|
+
{{/if}}
|
|
182
|
+
{{#if (eq status "failed")}}
|
|
183
|
+
<img src="node_modules/ortoni-report/dist/icon/fail.png" alt="Fail">
|
|
184
|
+
{{else}}
|
|
185
|
+
{{#if (eq status "skipped")}}
|
|
186
|
+
<img src="node_modules/ortoni-report/dist/icon/skip.png" alt="Skip">
|
|
187
|
+
{{/if}}
|
|
188
|
+
{{/if}}
|
|
189
|
+
{{#if (eq status "timedOut")}}
|
|
190
|
+
<img src="node_modules/ortoni-report/dist/icon/timeout.png" alt="timedOut">
|
|
191
|
+
{{/if}}
|
|
192
|
+
{{#if (eq status "flaky")}}
|
|
193
|
+
<img src="node_modules/ortoni-report/dist/icon/flaky.png" alt="flaky">
|
|
194
|
+
{{/if}}
|
|
195
|
+
<span>{{title}}</span>
|
|
196
|
+
{{#if retryCount}}
|
|
197
|
+
<p>Retry Count: {{retryCount}}</p>
|
|
198
|
+
{{/if}}
|
|
199
|
+
</li>
|
|
103
200
|
{{/each}}
|
|
104
201
|
</ul>
|
|
105
202
|
</details>
|
|
@@ -113,23 +210,23 @@
|
|
|
113
210
|
</div>
|
|
114
211
|
</aside>
|
|
115
212
|
<section>
|
|
116
|
-
{{!-- Overall
|
|
213
|
+
{{!-- Overall summary --}}
|
|
117
214
|
<div id="summary">
|
|
118
215
|
<section class="grid">
|
|
119
216
|
<div>
|
|
120
|
-
<article>
|
|
217
|
+
<article class="clickable filter" data-status="all">
|
|
121
218
|
<header>All Tests</header>
|
|
122
219
|
<p>{{totalCount}}</p>
|
|
123
220
|
</article>
|
|
124
221
|
</div>
|
|
125
222
|
<div>
|
|
126
|
-
<article>
|
|
223
|
+
<article class="clickable filter" data-status="passed">
|
|
127
224
|
<header>Passed</header>
|
|
128
225
|
<p class="text-success">{{passCount}}</p>
|
|
129
226
|
</article>
|
|
130
227
|
</div>
|
|
131
228
|
<div>
|
|
132
|
-
<article>
|
|
229
|
+
<article class="clickable filter" data-status="failed">
|
|
133
230
|
<header>Failed</header>
|
|
134
231
|
<p class="text-failure">{{failCount}}</p>
|
|
135
232
|
</article>
|
|
@@ -137,17 +234,23 @@
|
|
|
137
234
|
</section>
|
|
138
235
|
<section class="grid">
|
|
139
236
|
<div>
|
|
140
|
-
<article>
|
|
237
|
+
<article class="clickable filter" data-status="skipped">
|
|
141
238
|
<header>Skipped</header>
|
|
142
239
|
<p class="text-skip">{{skipCount}}</p>
|
|
143
240
|
</article>
|
|
144
241
|
</div>
|
|
145
242
|
<div>
|
|
146
|
-
<article>
|
|
243
|
+
<article class="clickable filter" data-status="flaky">
|
|
147
244
|
<header>Flaky</header>
|
|
148
245
|
<p class="text-flaky">{{flakyCount}}</p>
|
|
149
246
|
</article>
|
|
150
247
|
</div>
|
|
248
|
+
<div>
|
|
249
|
+
<article class="clickable filter" data-status="retry">
|
|
250
|
+
<header>Retry</header>
|
|
251
|
+
<p class="text-skip">{{retryCount}}</p>
|
|
252
|
+
</article>
|
|
253
|
+
</div>
|
|
151
254
|
</section>
|
|
152
255
|
{{!-- Suite details with chart --}}
|
|
153
256
|
<section>
|
|
@@ -158,6 +261,8 @@
|
|
|
158
261
|
{{#if authorName}}<h4>Author: {{authorName}}</h4>{{/if}}
|
|
159
262
|
{{#if testType}}<h4>Test Type: {{testType}}</h4>{{/if}}
|
|
160
263
|
{{#if totalDuration}}<h4>Duration: {{totalDuration}}</h4>{{/if}}
|
|
264
|
+
<h4>Success Rate: {{successRate}} %</h4>
|
|
265
|
+
<h4>Last Run: {{lastRunDate}}</h4>
|
|
161
266
|
</div>
|
|
162
267
|
<div class="chart-container">
|
|
163
268
|
<canvas id="testChart"></canvas>
|
|
@@ -174,8 +279,7 @@
|
|
|
174
279
|
</div>
|
|
175
280
|
</section>
|
|
176
281
|
</main>
|
|
177
|
-
|
|
178
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.0/chart.min.js"></script>
|
|
282
|
+
<script src="node_modules/ortoni-report/dist/utils/chart.js"></script>
|
|
179
283
|
<script>
|
|
180
284
|
function escapeHtml(unsafe) {
|
|
181
285
|
return unsafe.replace(/[&<"']/g, function (match) {
|
|
@@ -212,24 +316,48 @@
|
|
|
212
316
|
summary.style.display = 'none';
|
|
213
317
|
testDetails.style.display = 'block';
|
|
214
318
|
backButton.style.display = 'block';
|
|
319
|
+
let statusClass = '';
|
|
320
|
+
let statusText = test.status.toUpperCase();
|
|
321
|
+
if (test.status.startsWith('passed')) {
|
|
322
|
+
statusClass = 'text-success';
|
|
323
|
+
} else if (test.status === 'flaky') {
|
|
324
|
+
statusClass = 'text-flaky';
|
|
325
|
+
} else {
|
|
326
|
+
statusClass = 'text-failure';
|
|
327
|
+
}
|
|
215
328
|
testDetails.innerHTML = `
|
|
216
329
|
<button class="back-button" style="display: block" onclick="showSummary()">Back to Summary</button>
|
|
217
330
|
<h3>${test.title}</h3>
|
|
218
331
|
<div class="grid">
|
|
219
332
|
<div>
|
|
220
333
|
<h4>Status</h4>
|
|
221
|
-
<p class="${
|
|
334
|
+
<p class="${statusClass}">${statusText}</p>
|
|
222
335
|
${test.duration != '0s' ? `
|
|
223
336
|
<h4>Duration</h4>
|
|
224
337
|
<p>${test.duration}</p>` : ""}
|
|
225
338
|
</div>
|
|
226
339
|
<div>
|
|
227
340
|
${test.screenshotPath ? `
|
|
228
|
-
<
|
|
229
|
-
<
|
|
230
|
-
|
|
341
|
+
<img src="${test.screenshotPath}" data-target="modal-example" onclick="toggleModal(event)" alt="Screenshot">
|
|
342
|
+
<dialog id="modal-example">
|
|
343
|
+
<article>
|
|
344
|
+
<header>
|
|
345
|
+
<button aria-label="Close" rel="prev" data-target="modal-example" onclick="toggleModal(event)"></button>
|
|
346
|
+
<p>
|
|
347
|
+
<strong>Screenshot</strong>
|
|
348
|
+
</p>
|
|
349
|
+
</header><p>
|
|
350
|
+
<img src="${test.screenshotPath}" alt="Screenshot"></p>
|
|
351
|
+
</article>
|
|
352
|
+
</dialog>` : ''}
|
|
231
353
|
</div>
|
|
232
354
|
</div>
|
|
355
|
+
<div class="grid">
|
|
356
|
+
<details>
|
|
357
|
+
<summary><h4>Steps</h4></summary>
|
|
358
|
+
<span id="stepDetails"></span>
|
|
359
|
+
</details>
|
|
360
|
+
</div>
|
|
233
361
|
<div>
|
|
234
362
|
${test.errors.length ? `
|
|
235
363
|
<h4>Errors</h4>
|
|
@@ -245,6 +373,21 @@
|
|
|
245
373
|
` : ''}
|
|
246
374
|
</div>
|
|
247
375
|
`;
|
|
376
|
+
const stepDetailsDiv = document.getElementById('stepDetails');
|
|
377
|
+
const stepsList = attachSteps(test);
|
|
378
|
+
stepDetailsDiv.appendChild(stepsList);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function attachSteps(test) {
|
|
382
|
+
const stepsList = document.createElement("ul");
|
|
383
|
+
stepsList.setAttribute("id", "steps");
|
|
384
|
+
stepsList.innerHTML = '';
|
|
385
|
+
test.steps.forEach(step => {
|
|
386
|
+
const li = document.createElement('li');
|
|
387
|
+
li.innerHTML = `<strong class=${step.error ? 'text-failure' : 'test-success'}> ${step.title}</strong>`;
|
|
388
|
+
stepsList.appendChild(li);
|
|
389
|
+
});
|
|
390
|
+
return stepsList;
|
|
248
391
|
}
|
|
249
392
|
|
|
250
393
|
function attachEventListeners() {
|
|
@@ -261,9 +404,53 @@
|
|
|
261
404
|
highlightedItem = item;
|
|
262
405
|
});
|
|
263
406
|
});
|
|
407
|
+
|
|
408
|
+
// Event listeners for the filter articles
|
|
409
|
+
const filters = document.querySelectorAll('.filter');
|
|
410
|
+
filters.forEach(filter => {
|
|
411
|
+
filter.addEventListener('click', () => {
|
|
412
|
+
const status = filter.getAttribute('data-status');
|
|
413
|
+
filters.forEach(f => {
|
|
414
|
+
if (f.getAttribute('data-status') !== 'all') {
|
|
415
|
+
f.classList.remove('active');
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
if (status !== 'all') {
|
|
419
|
+
filter.classList.add('active');
|
|
420
|
+
}
|
|
421
|
+
applyFilter(status);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function applyFilter(status) {
|
|
427
|
+
const testItems = document.querySelectorAll('li[data-test-id]');
|
|
428
|
+
const detailsElements = document.querySelectorAll('details');
|
|
429
|
+
|
|
430
|
+
detailsElements.forEach(details => {
|
|
431
|
+
let shouldShowDetails = false;
|
|
432
|
+
const items = details.querySelectorAll('li[data-test-id]');
|
|
433
|
+
items.forEach(item => {
|
|
434
|
+
const testStatus = item.getAttribute('data-test-status');
|
|
435
|
+
if (status === 'all' || testStatus.trim() === status ||
|
|
436
|
+
(status === 'failed' && (testStatus.trim() === 'failed' || testStatus.trim() === 'timedOut'))) {
|
|
437
|
+
item.style.display = 'block'; // Show the item
|
|
438
|
+
shouldShowDetails = true; // Set shouldShowDetails to true
|
|
439
|
+
} else if ((status === 'retry' && testStatus.includes('retry'))) {
|
|
440
|
+
item.style.display = 'block'; // Show the item
|
|
441
|
+
shouldShowDetails = true; // Set shouldShowDetails to true
|
|
442
|
+
}else if ((status === 'flaky' && testStatus.includes('flaky'))) {
|
|
443
|
+
item.style.display = 'block'; // Show the item
|
|
444
|
+
shouldShowDetails = true; // Set shouldShowDetails to true
|
|
445
|
+
} else {
|
|
446
|
+
item.style.display = 'none'; // Hide the item
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
details.open = shouldShowDetails; // Open the details element if it has any matching items
|
|
450
|
+
details.style.display = shouldShowDetails ? 'block' : 'none'; // Show/hide the details element
|
|
451
|
+
});
|
|
264
452
|
}
|
|
265
453
|
|
|
266
|
-
attachEventListeners(); // Attach event listeners initially
|
|
267
454
|
|
|
268
455
|
const searchInput = document.querySelector('input[name="search"]');
|
|
269
456
|
const detailsElements = document.querySelectorAll('details');
|
|
@@ -285,33 +472,32 @@
|
|
|
285
472
|
let parent = item.parentElement;
|
|
286
473
|
while (parent && parent.tagName !== 'ASIDE') {
|
|
287
474
|
if (parent.tagName === 'DETAILS') {
|
|
288
|
-
parent.open = true;
|
|
475
|
+
parent.open = true;
|
|
289
476
|
}
|
|
290
477
|
parent = parent.parentElement;
|
|
291
478
|
}
|
|
292
479
|
} else {
|
|
293
|
-
item.style.display = 'none';
|
|
480
|
+
item.style.display = 'none';
|
|
294
481
|
}
|
|
295
482
|
});
|
|
296
483
|
} else {
|
|
297
484
|
testItems.forEach(item => {
|
|
298
|
-
item.style.display = 'block';
|
|
485
|
+
item.style.display = 'block';
|
|
299
486
|
});
|
|
300
487
|
detailsElements.forEach(detail => {
|
|
301
|
-
detail.open = false;
|
|
488
|
+
detail.open = false;
|
|
302
489
|
});
|
|
303
490
|
}
|
|
304
|
-
attachEventListeners(); // Reattach event listeners after filtering
|
|
305
491
|
});
|
|
306
492
|
|
|
307
493
|
const ctx = document.getElementById('testChart').getContext('2d');
|
|
308
494
|
new Chart(ctx, {
|
|
309
495
|
type: 'doughnut',
|
|
310
496
|
data: {
|
|
311
|
-
labels: ['Passed', 'Failed', 'Skipped'],
|
|
497
|
+
labels: ['Passed', 'Failed', 'Skipped','Flaky'],
|
|
312
498
|
datasets: [{
|
|
313
|
-
data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}],
|
|
314
|
-
backgroundColor: ['#28a745', '#dc3545', '#d5d4a1']
|
|
499
|
+
data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}, {{flakyCount}}],
|
|
500
|
+
backgroundColor: ['#28a745', '#dc3545', '#d5d4a1', '#b5e35e']
|
|
315
501
|
}]
|
|
316
502
|
},
|
|
317
503
|
options: {
|
|
@@ -324,8 +510,10 @@
|
|
|
324
510
|
}
|
|
325
511
|
}
|
|
326
512
|
});
|
|
513
|
+
attachEventListeners();
|
|
327
514
|
});
|
|
328
515
|
</script>
|
|
516
|
+
<script src="node_modules/ortoni-report/dist/utils/modal.js"></script>
|
|
329
517
|
</body>
|
|
330
518
|
|
|
331
519
|
</html>
|