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.
@@ -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
- background-color: var(--pico-primary-background);
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: #d5d4a1;
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>{{@key}}</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>{{@key}}</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}}">{{title}}</li>
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 summar --}}
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="${test.status === 'passed' ? 'text-success' : 'text-failure'}">${test.status.toUpperCase()}</p>
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
- <h4>Screenshot</h4>
229
- <img src="${test.screenshotPath}" alt="Screenshot">
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; // Expand parent details elements
475
+ parent.open = true;
289
476
  }
290
477
  parent = parent.parentElement;
291
478
  }
292
479
  } else {
293
- item.style.display = 'none'; // Hide non-matching test item
480
+ item.style.display = 'none';
294
481
  }
295
482
  });
296
483
  } else {
297
484
  testItems.forEach(item => {
298
- item.style.display = 'block'; // Show all test items
485
+ item.style.display = 'block';
299
486
  });
300
487
  detailsElements.forEach(detail => {
301
- detail.open = false; // Collapse all details elements
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>
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });