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