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.
@@ -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\ortoni-report\src\icon\32.png" type="image/x-icon">
9
- <link rel="stylesheet" href="node_modules\ortoni-report\src\css\pico.css">
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>{{@key}}</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>{{@key}}</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
- <li data-suite-name="{{suite}}" data-project-name="{{projectName}}"
102
- data-test-id="{{index}}">{{title}}</li>
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 summar --}}
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
- const testDetails = document.getElementById('testDetails');
196
- const summary = document.getElementById('summary');
197
- const backButton = document.querySelector('.back-button');
198
- let highlightedItem = null;
199
-
200
- function showSummary() {
201
- summary.style.display = 'block';
202
- testDetails.style.display = 'none';
203
- backButton.style.display = 'none';
204
- if (highlightedItem) {
205
- highlightedItem.classList.remove('highlight');
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
- window.showSummary = showSummary;
289
+ window.showSummary = showSummary;
210
290
 
211
- function displayTestDetails(test) {
212
- summary.style.display = 'none';
213
- testDetails.style.display = 'block';
214
- backButton.style.display = 'block';
215
- testDetails.innerHTML = `
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
- <h4>Screenshot</h4>
229
- <img src="${test.screenshotPath}" alt="Screenshot">
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
- function attachEventListeners() {
251
- const testItems = document.querySelectorAll('[data-test-id]');
252
- testItems.forEach(item => {
253
- item.addEventListener('click', () => {
254
- const testId = item.getAttribute('data-test-id');
255
- const test = testData[testId];
256
- displayTestDetails(test);
257
- if (highlightedItem) {
258
- highlightedItem.classList.remove('highlight');
259
- }
260
- item.classList.add('highlight');
261
- highlightedItem = item;
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
- attachEventListeners(); // Attach event listeners initially
267
-
268
- const searchInput = document.querySelector('input[name="search"]');
269
- const detailsElements = document.querySelectorAll('details');
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
- searchInput.addEventListener('input', () => {
272
- const searchTerm = searchInput.value.toLowerCase();
273
- const testItems = document.querySelectorAll('[data-test-id]');
372
+ function applyFilter(status) {
373
+ const testItems = document.querySelectorAll('[data-test-id]');
374
+ const detailsElements = document.querySelectorAll('details');
274
375
 
275
- if (searchTerm) {
276
- detailsElements.forEach(detail => {
277
- detail.open = false; // Collapse all details initially
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
- testItems.forEach(item => {
281
- const testTitle = item.textContent.toLowerCase();
282
- if (testTitle.includes(searchTerm)) {
283
- item.style.display = 'block'; // Show matching test item
284
-
285
- let parent = item.parentElement;
286
- while (parent && parent.tagName !== 'ASIDE') {
287
- if (parent.tagName === 'DETAILS') {
288
- parent.open = true; // Expand parent details elements
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
- parent = parent.parentElement;
419
+ } else {
420
+ item.style.display = 'none';
291
421
  }
292
- } else {
293
- item.style.display = 'none'; // Hide non-matching test item
294
- }
295
- });
296
- } else {
297
- testItems.forEach(item => {
298
- item.style.display = 'block'; // Show all test items
299
- });
300
- detailsElements.forEach(detail => {
301
- detail.open = false; // Collapse all details elements
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
- const ctx = document.getElementById('testChart').getContext('2d');
308
- new Chart(ctx, {
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
- backgroundColor: ['#28a745', '#dc3545', '#d5d4a1']
440
+ backgroundColor: ['#28a745', '#dc3545', '#d5d4a1']
315
441
  }]
316
442
  },
317
443
  options: {
318
444
  responsive: true,
319
445
  maintainAspectRatio: false,
320
- plugins: {
321
- legend: {
322
- position: 'bottom'
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>