ortoni-report 2.0.2 → 2.0.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.
@@ -1,166 +1,26 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en" data-theme="{{preferredTheme}}">
3
+
3
4
  <head>
4
5
  <meta charset="UTF-8">
5
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <meta name="description" content="Playwright HTML report by LetCode Koushik - V2.0.2">
7
+ <meta name="description" content="Playwright HTML report by LetCode Koushik - V2.0.3">
7
8
  <title>{{title}}</title>
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/main.css">
10
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css" integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
11
- <link rel="preconnect" href="https://fonts.googleapis.com">
12
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
13
- <link href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,opsz,wght@0,6..12,200..1000;1,6..12,200..1000&family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap" rel="stylesheet">
9
+ <link rel="icon" href="https://raw.githubusercontent.com/ortoniKC/ortoni-report/refs/heads/main/favicon.png" type="image/x-icon">
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css"
11
+ integrity="sha512-Kc323vGBEqzTmouAECnVceyQqyqdsSiqLQISBL29aUW4U/M7pSPA/gEUZQqv1cwx4OnYxTxve5UMg5GT6L4JJg=="
12
+ crossorigin="anonymous" referrerpolicy="no-referrer" />
14
13
  </head>
15
14
  <style>
16
- #summary, #testDetails, button#back-to-summary {
17
- transition: opacity 0.2s ease-in-out;
18
- }
19
-
20
- .filter.active {
21
- box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
22
- font-weight: bold;
23
- }
24
-
25
- ::-webkit-scrollbar {
26
- width: 8px;
27
- height: 8px;
28
- }
29
-
30
- ::-webkit-scrollbar-track {
31
- background: #f1f1f1;
32
- }
33
-
34
- ::-webkit-scrollbar-thumb {
35
- background: var(--bulma-primary);
36
- border-radius: 4px;
37
- }
38
-
39
- ::-webkit-scrollbar-thumb:hover {
40
- background: #555;
41
- }
42
-
43
- div#testDetails {
44
- position: sticky;
45
- top: 0;
46
- z-index: 1;
47
- }
48
-
49
- .sidebar {
50
- overflow-y: auto;
51
- max-height: calc(100vh - 100px);
52
- }
53
-
54
- aside li {
55
- cursor: pointer;
56
- }
57
- details summary {
58
- cursor: pointer;
59
- display: flex;
60
- justify-content: space-between;
61
- align-items: center;
62
- }
63
- details summary::after {
64
- content: '';
65
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
66
- transform:rotate(-90deg);
67
- background-size: 1em;
68
- background-repeat: no-repeat;
69
- background-position:right-center;
70
- width: 1em;
71
- height: 1em;
72
- transition: transform 0.2s ease-in-out;
73
- }
74
- details[open] > summary::after {
75
- transform: rotate(0deg);
76
- }
77
- .logoimage{
78
- max-width: 100px;
79
- }
80
- .listselected {
81
- border-image: linear-gradient(45deg, var(--bulma-primary),#FF6B6B) 1;
82
- font-weight: bold;
83
- }
84
- .has-background-flaky {
85
- background-color: #ffb70f !important;
86
- }
87
- .has-background-retry {
88
- background-color: #69748c !important;
89
- }
90
-
91
- </style>
15
+ {{{inlineCss}}}
16
+ </style>
92
17
  <body>
93
- <section class="section">
94
- <header class="container">
95
- <div class="columns is-vcentered">
96
- <div class="column is-two-fifths">
97
- <div class="columns is-desktop is-vcentered is-multiline">
98
- {{#if logo}}
99
- <div class="column-1">
100
- <figure class="image logoimage">
101
- <img src="{{logo}}" />
102
- </figure>
103
- </div>
104
- {{/if}}
105
- {{#if projectName}}<div class="column"><span class="title">{{projectName}}</span></div>{{/if}}
106
- </div>
107
- </div>
108
- <div class="column">
109
- <div class="control">
110
- <input class="input" name="search" type="search" placeholder="Search by test title" />
111
- </div>
112
- <div class="control has-text-info">
113
- <span id="selected-filters"></span>
114
- </div>
115
- </div>
116
- <div class="column is-1">
117
- <div class="control">
118
- <button id="toggle-theme" data-theme-status="{{preferredTheme}}" class="button">Theme</button>
119
- </div>
120
- </div>
121
- </div>
122
- </header>
123
- </section>
18
+ {{> navbar}}
124
19
  <section class="section">
125
20
  <main class="container">
126
21
  <div class="columns">
127
22
  <aside class="column is-two-fifths">
128
- <div class="columns is-mobile is-vcentered">
129
- <div class="column">
130
- <h2 class="title is-4">Tests</h2>
131
- </div>
132
- <div class="column">
133
- <div id="project-filter" class="dropdown is-right is-hoverable is-pulled-right">
134
- <div class="dropdown-trigger">
135
- <button class="button" aria-haspopup="true" aria-controls="select-filter">
136
- <span>Filters</span>
137
- <span class="icon is-small">
138
- <i class="fas fa-angle-down" aria-hidden="true"></i>
139
- </span>
140
- </button>
141
- </div>
142
- <div class="dropdown-menu" id="select-filter" role="menu">
143
- <div class="dropdown-content">
144
- {{#each projects}}
145
- <div class="dropdown-item">
146
- <label class="checkbox">
147
- <input type="checkbox" data-filter-type="project" value="{{this}}">{{this}}
148
- </label>
149
- </div>
150
- {{/each}}
151
- <hr class="dropdown-divider" />
152
- {{#each allTags}}
153
- <div class="dropdown-item">
154
- <label class="checkbox">
155
- <input type="checkbox" data-filter-type="test-tags" value="{{this}}">{{this}}
156
- </label>
157
- </div>
158
- {{/each}}
159
- </div>
160
- </div>
161
- </div>
162
- </div>
163
- </div>
23
+ {{> testPanel}}
164
24
  <div class="content sidebar">
165
25
  {{#if showProject}}
166
26
  {{#each groupedResults}}
@@ -168,7 +28,7 @@
168
28
  <summary class="is-size-5 has-icon-right">
169
29
  <div class="icon-text">
170
30
  <span class="icon has-text-info">
171
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/file.png" alt="file name">
31
+ <svg class="image is-16x16" height="512" viewBox="0 0 32 32" width="512" xmlns="http://www.w3.org/2000/svg"><g data-name="Layer 2"><path d="M11.612 30.945a3.986 3.986 0 01-2.829-1.17l-5.209-5.208a4.006 4.006 0 010-5.657l3.955-3.954a1 1 0 011.414 1.414l-3.955 3.954a2.003 2.003 0 000 2.829l5.21 5.208a2.004 2.004 0 002.828 0l7.235-7.234a2.003 2.003 0 000-2.828l-4.63-4.63a1 1 0 011.414-1.414l4.63 4.63a4.006 4.006 0 010 5.657l-7.235 7.233a3.988 3.988 0 01-2.828 1.17z" fill="#232323"/><path d="M15.662 20.038a.997.997 0 01-.707-.293l-4.63-4.63a4.006 4.006 0 010-5.656l7.235-7.234a4.004 4.004 0 015.657 0l5.209 5.208a4.006 4.006 0 010 5.657l-3.955 3.954a1 1 0 01-1.414-1.414l3.955-3.954a2.003 2.003 0 000-2.829l-5.21-5.208a2.002 2.002 0 00-2.828 0l-7.235 7.234a2.003 2.003 0 000 2.828l4.63 4.63a1 1 0 01-.707 1.707z" fill="#7fbde7"/></g></svg>
172
32
  </span>
173
33
  <span>{{@key}}</span>
174
34
  </div>
@@ -179,7 +39,7 @@
179
39
  <summary class="is-size-5 is-capitalized">
180
40
  <div class="icon-text">
181
41
  <span class="icon has-text-info">
182
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
42
+ <svg class="image is-16x16"xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M181.344 42.656v394.671C181.344 478.562 214.781 512 256 512c41.25 0 74.672-33.438 74.672-74.673V42.656H181.344z" fill="#e6e9ed"/><path d="M352 32c0 17.672-14.312 32-32 32H192c-17.656 0-32-14.328-32-32s14.344-32 32-32h128c17.688 0 32 14.328 32 32z" fill="#ccd1d9"/><path d="M181.344 192v245.327C181.344 478.562 214.781 512 256 512c41.25 0 74.672-33.438 74.672-74.673V192H181.344z" fill="#a0d468"/><g fill="#434a54"><path d="M245.344 266.655c0-5.89-4.781-10.655-10.672-10.655h-53.328v21.328h53.328c5.89 0 10.672-4.781 10.672-10.673zM234.672 319.999h-53.328v21.328h53.328c5.891 0 10.672-4.781 10.672-10.671s-4.782-10.657-10.672-10.657zM245.344 202.656c0-5.891-4.781-10.656-10.672-10.656h-53.328v21.328h53.328c5.89 0 10.672-4.781 10.672-10.672z"/></g></svg>
183
43
  </span>
184
44
  <span>{{@key}}</span>
185
45
  </div>
@@ -194,40 +54,7 @@
194
54
  data-test-duration="{{duration}}" data-test-tags="{{joinWithSpace testTags}}"
195
55
  data-project-name="{{projectName}}" data-test-id="{{index}}"
196
56
  data-test-status="{{status}} {{retry}}">
197
- <div class="icon-text">
198
- {{#if isRetry}}
199
- <span class="icon has-text-info">
200
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/retry.png"
201
- alt="Retry"></span>
202
- {{/if}}
203
- {{#if (eq status "passed")}}
204
- <span class="icon has-text-info">
205
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/pass.png"
206
- alt="Pass"></span>
207
- {{/if}}
208
- {{#if (eq status "failed")}}
209
- <span class="icon has-text-info">
210
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/fail.png"
211
- alt="Fail"></span>
212
- {{else}}
213
- {{#if (eq status "skipped")}}
214
- <span class="icon has-text-info">
215
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/skip.png"
216
- alt="Skip"></span>
217
- {{/if}}
218
- {{/if}}
219
- {{#if (eq status "timedOut")}}
220
- <span class="icon has-text-info">
221
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/timeout.png"
222
- alt="timedOut"></span>
223
- {{/if}}
224
- {{#if (eq status "flaky")}}
225
- <span class="icon has-text-info">
226
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/flaky.png"
227
- alt="flaky"></span>
228
- {{/if}}
229
- <span>{{title}}</span>
230
- </div>
57
+ {{> testStatus}}
231
58
  </li>
232
59
  {{/each}}
233
60
  </ul>
@@ -245,7 +72,7 @@
245
72
  <summary class="is-size-5 has-icon-right">
246
73
  <div class="icon-text">
247
74
  <span class="icon has-text-info">
248
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/file.png" alt="file name">
75
+ <svg class="image is-16x16" height="512" viewBox="0 0 32 32" width="512" xmlns="http://www.w3.org/2000/svg"><g data-name="Layer 2"><path d="M11.612 30.945a3.986 3.986 0 01-2.829-1.17l-5.209-5.208a4.006 4.006 0 010-5.657l3.955-3.954a1 1 0 011.414 1.414l-3.955 3.954a2.003 2.003 0 000 2.829l5.21 5.208a2.004 2.004 0 002.828 0l7.235-7.234a2.003 2.003 0 000-2.828l-4.63-4.63a1 1 0 011.414-1.414l4.63 4.63a4.006 4.006 0 010 5.657l-7.235 7.233a3.988 3.988 0 01-2.828 1.17z" fill="#232323"/><path d="M15.662 20.038a.997.997 0 01-.707-.293l-4.63-4.63a4.006 4.006 0 010-5.656l7.235-7.234a4.004 4.004 0 015.657 0l5.209 5.208a4.006 4.006 0 010 5.657l-3.955 3.954a1 1 0 01-1.414-1.414l3.955-3.954a2.003 2.003 0 000-2.829l-5.21-5.208a2.002 2.002 0 00-2.828 0l-7.235 7.234a2.003 2.003 0 000 2.828l4.63 4.63a1 1 0 01-.707 1.707z" fill="#7fbde7"/></g></svg>
249
76
  </span>
250
77
  <span>{{@key}}</span>
251
78
  </div>
@@ -256,7 +83,7 @@
256
83
  <summary class="is-size-5 is-capitalized">
257
84
  <div class="icon-text">
258
85
  <span class="icon has-text-info">
259
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
86
+ <svg class="image is-16x16"xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M181.344 42.656v394.671C181.344 478.562 214.781 512 256 512c41.25 0 74.672-33.438 74.672-74.673V42.656H181.344z" fill="#e6e9ed"/><path d="M352 32c0 17.672-14.312 32-32 32H192c-17.656 0-32-14.328-32-32s14.344-32 32-32h128c17.688 0 32 14.328 32 32z" fill="#ccd1d9"/><path d="M181.344 192v245.327C181.344 478.562 214.781 512 256 512c41.25 0 74.672-33.438 74.672-74.673V192H181.344z" fill="#a0d468"/><g fill="#434a54"><path d="M245.344 266.655c0-5.89-4.781-10.655-10.672-10.655h-53.328v21.328h53.328c5.89 0 10.672-4.781 10.672-10.673zM234.672 319.999h-53.328v21.328h53.328c5.891 0 10.672-4.781 10.672-10.671s-4.782-10.657-10.672-10.657zM245.344 202.656c0-5.891-4.781-10.656-10.672-10.656h-53.328v21.328h53.328c5.89 0 10.672-4.781 10.672-10.672z"/></g></svg>
260
87
  </span>
261
88
  <span>{{@key}}</span>
262
89
  </div>
@@ -267,40 +94,7 @@
267
94
  data-test-duration="{{duration}}" data-test-tags="{{joinWithSpace testTags}}"
268
95
  data-project-name="{{projectName}}" data-test-id="{{index}}"
269
96
  data-test-status="{{status}} {{retry}}">
270
- <div class="icon-text">
271
- {{#if isRetry}}
272
- <span class="icon has-text-info">
273
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/retry.png"
274
- alt="Retry"></span>
275
- {{/if}}
276
- {{#if (eq status "passed")}}
277
- <span class="icon has-text-info">
278
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/pass.png"
279
- alt="Pass"></span>
280
- {{/if}}
281
- {{#if (eq status "failed")}}
282
- <span class="icon has-text-info">
283
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/fail.png"
284
- alt="Fail"></span>
285
- {{else}}
286
- {{#if (eq status "skipped")}}
287
- <span class="icon has-text-info">
288
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/skip.png"
289
- alt="Skip"></span>
290
- {{/if}}
291
- {{/if}}
292
- {{#if (eq status "timedOut")}}
293
- <span class="icon has-text-info">
294
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/timeout.png"
295
- alt="timedOut"></span>
296
- {{/if}}
297
- {{#if (eq status "flaky")}}
298
- <span class="icon has-text-info">
299
- <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/flaky.png"
300
- alt="flaky"></span>
301
- {{/if}}
302
- <span>{{title}}</span>
303
- </div>
97
+ {{> testStatus}}
304
98
  </li>
305
99
  {{/each}}
306
100
  </ul>
@@ -316,126 +110,31 @@
316
110
  {{!-- Overall summary --}}
317
111
  <div id="summary">
318
112
  <div class="columns is-multiline">
319
- <div class="column is-one-third">
320
- <div class="card is-clickable has-background-primary filter" data-status="all">
321
- <header class="card-header has-text-centered">
322
- <p class="card-header-title has-text-white">All Tests</p>
323
- <button class="card-header-icon" aria-label="more options">
324
- <span class="icon">
325
- <img aria-hidden="true" class="image is-16x16" src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
326
- </span>
327
- </button>
328
- </header>
329
- <div class="card-content">
330
- <div class="content">
331
- <p class="has-text-centered has-text-white">{{totalCount}}</p>
332
- </div>
333
- </div>
334
- </div>
335
- </div>
336
- <div class="column is-one-third">
337
- <div class="card is-clickable has-background-success filter" data-status="passed">
338
- <header class="card-header has-text-centered">
339
- <p class="card-header-title has-text-white">Passed</p>
340
- <button class="card-header-icon" aria-label="more options">
341
- <span class="icon">
342
- <img aria-hidden="true" class="image is-16x16" src="node_modules/ortoni-report/dist/icon/pass.png" alt="test name">
343
- </span>
344
- </button>
345
- </header>
346
- <div class="card-content">
347
- <div class="content">
348
- <p class="has-text-centered has-text-white">{{passCount}}</p>
349
- </div>
350
- </div>
351
- </div>
352
- </div>
353
- <div class="column is-one-third">
354
- <div class="card is-clickable has-background-danger filter" data-status="failed">
355
- <header class="card-header">
356
- <p class="card-header-title has-text-white has-text-centered">Failed</p>
357
- <button class="card-header-icon" aria-label="more options">
358
- <span class="icon">
359
- <img aria-hidden="true" class="image is-16x16" src="node_modules/ortoni-report/dist/icon/fail.png" alt="test name">
360
- </span>
361
- </button>
362
- </header>
363
- <div class="card-content">
364
- <div class="content">
365
- <p class="has-text-centered has-text-white">{{failCount}}</p>
366
- </div>
367
- </div>
368
- </div>
369
- </div>
113
+ {{> summaryCard bg="primary" status="all" statusHeader="All Tests" statusCount=totalCount}}
114
+ {{> summaryCard bg="success" status="passed" statusHeader="Passed" statusCount=passCount}}
115
+ {{> summaryCard bg="danger" status="failed" statusHeader="Failed" statusCount=failCount}}
370
116
  </div>
371
117
  <div class="columns is-multiline">
372
- <div class="column is-one-third">
373
- <div class="card is-clickable has-background-info filter" data-status="skipped">
374
- <header class="card-header has-text-centered">
375
- <p class="card-header-title has-text-white">Skipped</p>
376
- </header>
377
- <div class="card-content">
378
- <div class="content">
379
- <p class="has-text-centered has-text-white">{{skipCount}}</p>
380
- </div>
381
- </div>
382
- </div>
383
- </div>
384
- <div class="column is-one-third">
385
- <div class="card is-clickable has-background-warning filter" data-status="flaky">
386
- <header class="card-header has-text-centered">
387
- <p class="card-header-title has-text-white">Flaky</p>
388
- </header>
389
- <div class="card-content">
390
- <div class="content">
391
- <p class="has-text-centered has-text-white">{{flakyCount}}</p>
392
- </div>
393
- </div>
394
- </div>
395
- </div>
396
- <div class="column is-one-third">
397
- <div class="card is-clickable has-background-retry filter" data-status="retry">
398
- <header class="card-header has-text-centered">
399
- <p class="card-header-title has-text-white">Retry</p>
400
- </header>
401
- <div class="card-content">
402
- <div class="content">
403
- <p class="has-text-centered has-text-white">{{retryCount}}</p>
404
- </div>
405
- </div>
406
- </div>
407
- </div>
118
+ {{> summaryCard bg="info" status="skipped" statusHeader="Skipped" statusCount=skipCount}}
119
+ {{> summaryCard bg="warning" status="flaky" statusHeader="Flaky" statusCount=flakyCount}}
120
+ {{> summaryCard bg="retry" status="retry" statusHeader="Retry" statusCount=retryCount}}
408
121
  </div>
409
- {{!-- Suite details with chart --}}
410
122
  <div class="box">
411
- <header class="has-text-centered title is-4">Suite</header>
412
- <div class="columns">
413
- <div class="column is-half">
414
- {{#if authorName}}<h4>Author: {{authorName}}</h4>{{/if}}
415
- {{#if testType}}<h4>Test Type: {{testType}}</h4>{{/if}}
416
- {{#if totalDuration}}<h4>Duration: {{totalDuration}}</h4>{{/if}}
417
- <h4>Success Rate: {{successRate}} %</h4>
418
- <h4>Last Run: {{lastRunDate}}</h4>
419
- </div>
420
- <div class="column is-half">
421
- <div class="chart-container">
422
- <canvas id="testChart"></canvas>
423
- </div>
424
- </div>
425
- </div>
123
+ {{> userInfo}}
426
124
  </div>
427
125
  </div>
428
126
  {{!-- Test details --}}
429
127
  <div id="testDetails" style="display: none;">
430
128
  <!-- Back button should be outside the dynamic content -->
431
- <button class="button content" id="back-to-summary"onclick="showSummary()">Back to Summary</button>
129
+ <button class="button content" id="back-to-summary" onclick="showSummary()">Back to
130
+ Summary</button>
432
131
  <!-- Test Details will be displayed here -->
433
132
  </div>
434
133
  </section>
435
134
  </div>
436
135
  </main>
437
136
  </section>
438
- <script src="node_modules/ortoni-report/dist/utils/chart.js"></script>
137
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
439
138
  <script>
440
139
  function escapeHtml(unsafe) {
441
140
  return unsafe.replace(/[&<"']/g, function (match) {
@@ -457,30 +156,35 @@
457
156
 
458
157
  const themeButton = document.getElementById("toggle-theme");
459
158
  const preferredTheme = themeButton.getAttribute("data-theme-status");
159
+ const themeIcon = document.getElementById("theme-icon");
460
160
  const htmlElement = document.documentElement;
461
-
161
+
462
162
  if (preferredTheme === 'dark') {
463
163
  htmlElement.setAttribute('data-theme', 'dark');
464
- themeButton.classList.add('is-dark');
465
- themeButton.textContent = 'Dark';
164
+ themeIcon.classList='';
165
+ themeIcon.classList.add("fa","fa-moon");
466
166
  } else if (preferredTheme === 'light') {
467
167
  htmlElement.setAttribute('data-theme', 'light');
468
- themeButton.classList.add('is-light');
469
- themeButton.textContent = 'Light';
168
+ themeIcon.classList='';
169
+ themeIcon.classList.add("fa","fa-sun");
170
+ } else {
171
+ if(window.matchMedia('(prefers-color-scheme: light)').matches){
172
+ themeIcon.classList.add("fa","fa-sun");
173
+ }
174
+ else{
175
+ themeIcon.classList.add("fa","fa-moon");
176
+ }
470
177
  }
471
-
472
178
  themeButton.addEventListener('click', () => {
473
179
  const currentTheme = htmlElement.getAttribute('data-theme');
474
180
  const newTheme = currentTheme === 'light' ? 'dark' : 'light';
475
181
  htmlElement.setAttribute('data-theme', newTheme);
476
182
  if (newTheme === 'dark') {
477
- themeButton.classList.remove('is-light');
478
- themeButton.classList.add('is-dark');
479
- themeButton.textContent = 'Dark';
183
+ themeIcon.classList='';
184
+ themeIcon.classList.add("fa","fa-moon");
480
185
  } else {
481
- themeButton.classList.remove('is-dark');
482
- themeButton.classList.add('is-light');
483
- themeButton.textContent = 'Light';
186
+ themeIcon.classList='';
187
+ themeIcon.classList.add("fa","fa-sun");
484
188
  }
485
189
  });
486
190
 
@@ -490,7 +194,33 @@
490
194
  backButton.style.display = 'none';
491
195
  }
492
196
  window.showSummary = showSummary;
197
+
493
198
  function displayTestDetails(test) {
199
+ let currentScreenshotIndex = 0;
200
+ function changeScreenshot(direction) {
201
+ const screenshots = test.screenshots;
202
+ currentScreenshotIndex = (currentScreenshotIndex + direction + screenshots.length) % screenshots.length;
203
+ updateScreenshot();
204
+ }
205
+
206
+ function gotoScreenshot(index) {
207
+ currentScreenshotIndex = index;
208
+ updateScreenshot();
209
+ }
210
+
211
+ function updateScreenshot() {
212
+ const screenshots = test.screenshots;
213
+ document.getElementById('screenshot-main-img').src = screenshots[currentScreenshotIndex];
214
+ document.getElementById('screenshot-modal-img').src = screenshots[currentScreenshotIndex];
215
+
216
+ document.querySelectorAll('.pagination-link').forEach((link, index) => {
217
+ if (index === currentScreenshotIndex) {
218
+ link.classList.add('is-current');
219
+ } else {
220
+ link.classList.remove('is-current');
221
+ }
222
+ });
223
+ }
494
224
  const summary = document.getElementById('summary');
495
225
  const testDetails = document.getElementById('testDetails');
496
226
  const backButton = document.querySelector('button#back-to-summary');
@@ -515,7 +245,10 @@
515
245
  }
516
246
 
517
247
  testDetails.innerHTML = `
518
- <button class="button content" id="back-to-summary" style="display: block" onclick="showSummary()">Back to Summary</button>
248
+ <button class="button content" id="back-to-summary" style="display: block"
249
+ onclick="showSummary()">
250
+ <span class="icon"><i class="fa-solid fa-chevron-left" style="color: #63E6BE;"></i></span>
251
+ <span>Back to Summary</span></button>
519
252
  <div class="content has-text-centered">
520
253
  <p class="title">${test.title}</p>
521
254
  <p class="subtitle" id="filepath">${test.location}</p>
@@ -555,23 +288,43 @@
555
288
  <button onclick="closeVideo()" class="modal-close is-large" aria-label="close"></button>
556
289
  </div>
557
290
  <button class="button mt-4" onclick="openVideo()">Attachment: Video</button>
558
- `:''}
291
+ `: ''}
292
+ ${test.tracePath ? `
293
+ <div class="trace-viewer mt-4">
294
+ <a href="${test.tracePath}" target="_blank" class="button is-small is-link">
295
+ View Trace
296
+ </a>
297
+ </div>
298
+ ` : ''}
559
299
  </div>
560
300
  <div class="column content">
561
- ${test.screenshotPath ? `
301
+ ${test.screenshots && test.screenshots.length > 0 ? `
562
302
  <div id="testImage" class="modal">
563
303
  <div class="modal-background"></div>
564
304
  <div class="modal-content">
565
305
  <p class="image">
566
- <img src="${test.base64Image ? test.screenshotPath : `file://${test.screenshotPath}`}" alt="Screenshot">
306
+ <img id="screenshot-modal-img" src="${test.screenshots[0]}" alt="Screenshot">
567
307
  </p>
568
308
  </div>
569
309
  <button onclick="closeModal()" class="modal-close is-large" aria-label="close"></button>
570
310
  </div>
311
+
571
312
  <figure class="image box">
572
- <img onclick="openModal()" src="${test.base64Image ? test.screenshotPath : `file://${test.screenshotPath}`}" alt="Screenshot">
573
- </figure>` : ''}
574
- </div>
313
+ <img id="screenshot-main-img" onclick="openModal()" src="${test.screenshots[0]}" alt="Screenshot">
314
+ </figure>
315
+ <nav class="pagination is-small is-centered ${test.screenshots.length > 1 ? '' : 'is-hidden'}" role="navigation" aria-label="pagination">
316
+ <a class="pagination-previous" >Previous</a>
317
+ <a class="pagination-next" >Next</a>
318
+ <ul class="pagination-list">
319
+ ${test.screenshots.map((_, index) => `
320
+ <li>
321
+ <a class="pagination-link ${index === 0 ? 'is-current' : ''}" aria-label="Goto screenshot ${index + 1}" >${index + 1}</a>
322
+ </li>`).join('')}
323
+ </ul>
324
+ </nav>
325
+ ` : ''}
326
+ </div>
327
+
575
328
  </div>
576
329
  <div class="content">
577
330
  ${test.steps.length > 0 ? `
@@ -596,16 +349,21 @@
596
349
  </div>` : ''}
597
350
  </div>
598
351
  `;
599
-
352
+ document.querySelector('.pagination-previous').addEventListener('click', () => changeScreenshot(-1));
353
+ document.querySelector('.pagination-next').addEventListener('click', () => changeScreenshot(1));
354
+ document.querySelectorAll('.pagination-link').forEach((link, index) => {
355
+ link.addEventListener('click', () => gotoScreenshot(index));
356
+ });
600
357
  const stepDetailsDiv = document.getElementById('stepDetails');
601
- if(stepDetailsDiv){
358
+ if (stepDetailsDiv) {
602
359
  const stepsList = attachSteps(test);
603
360
  const detail = document.getElementById("stepopen");
604
- if(test.errors.length > 0){
361
+ if (test.errors.length > 0) {
605
362
  detail.setAttribute("open", "");
606
363
  }
607
364
  stepDetailsDiv.appendChild(stepsList);
608
365
  }
366
+
609
367
  }
610
368
 
611
369
  function attachSteps(test) {
@@ -632,11 +390,11 @@
632
390
  let modal = document.querySelector("#testImage");
633
391
  modal.classList.add("is-active");
634
392
  }
635
- function openVideo(){
393
+ function openVideo() {
636
394
  let modal = document.querySelector("#testVideo");
637
395
  modal.classList.add("is-active");
638
396
  }
639
- function closeVideo(){
397
+ function closeVideo() {
640
398
  let modal = document.querySelector("#testVideo");
641
399
  modal.classList.remove("is-active");
642
400
  }
@@ -644,15 +402,17 @@
644
402
  let modal = document.querySelector("#testImage");
645
403
  modal.classList.remove("is-active");
646
404
  }
405
+
647
406
  window.openModal = openModal;
648
407
  window.openVideo = openVideo;
649
408
  window.closeVideo = closeVideo;
650
409
  window.closeModal = closeModal;
651
-
410
+
652
411
  document.addEventListener('keydown', (event) => {
653
- if (event.key === "Escape") {
654
- closeModal();
655
- }});
412
+ if (event.key === "Escape") {
413
+ closeModal();
414
+ }
415
+ });
656
416
 
657
417
  function attachEventListeners() {
658
418
  const checkboxes = document.querySelectorAll('#select-filter input[type="checkbox"]');
@@ -709,7 +469,7 @@
709
469
  const matchesTags = selectedTags.length === 0 || selectedTags.every(tag => testTags.includes(tag));
710
470
  const matchesStatus = (selectedStatus === 'all' && testStatus !== 'skipped') ||
711
471
  (selectedStatus !== 'all' && (
712
- testStatus.includes(selectedStatus) ||
472
+ testStatus === selectedStatus ||
713
473
  (selectedStatus === 'failed' && (testStatus === 'failed' || testStatus === 'timedOut')) ||
714
474
  (selectedStatus === 'retry' && testStatus.includes('retry')) ||
715
475
  (selectedStatus === 'flaky' && testStatus.includes('flaky'))
@@ -726,7 +486,7 @@
726
486
  });
727
487
  updateSelectedFiltersDisplay(selectedProjects, selectedTags, selectedStatus);
728
488
  }
729
-
489
+
730
490
  function updateSelectedFiltersDisplay(projects, tags, status) {
731
491
  const filtersDisplay = document.getElementById('selected-filters');
732
492
  filtersDisplay.innerHTML = '';
@@ -747,7 +507,7 @@
747
507
  }
748
508
  const searchInput = document.querySelector('input[name="search"]');
749
509
  const detailsElements = document.querySelectorAll('details');
750
- function filterTests(search){
510
+ function filterTests(search) {
751
511
  const searchTerm = search.toLowerCase();
752
512
  const testItems = document.querySelectorAll('[data-test-id]');
753
513
 
@@ -783,41 +543,41 @@
783
543
  }
784
544
  function debounce(func, wait) {
785
545
  let timeout;
786
- return function(...args) {
546
+ return function (...args) {
787
547
  clearTimeout(timeout);
788
548
  timeout = setTimeout(() => func.apply(this, args), wait);
789
549
  };
790
550
  }
791
-
551
+
792
552
  const debouncedSearch = debounce((event) => {
793
553
  filterTests(event.target.value);
794
554
  }, 300);
795
555
 
796
556
  searchInput.addEventListener('input', debouncedSearch);
797
557
 
798
- const ctx = document.getElementById('testChart').getContext('2d');
558
+ const ctx = document.getElementById('testChart');
799
559
  new Chart(ctx, {
800
560
  type: 'doughnut',
801
561
  data: {
802
- labels: ['Passed', 'Failed', 'Skipped','Flaky'],
562
+ labels: ['Passed', 'Failed', 'Skipped', 'Flaky'],
803
563
  datasets: [{
804
- data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}, {{flakyCount}}],
564
+ data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}, {{ flakyCount }}],
805
565
  backgroundColor: ['#28a745', '#ff6685', '#66d1ff', '#ffb70f']
806
- }]
807
- },
566
+ }]
567
+ },
808
568
  options: {
809
- responsive: true,
810
- maintainAspectRatio: false,
811
- plugins: {
812
- legend: {
813
- display: false
814
- }
569
+ responsive: true,
570
+ maintainAspectRatio: false,
571
+ plugins: {
572
+ legend: {
573
+ display: false
815
574
  }
816
575
  }
576
+ }
817
577
  });
818
-
819
578
  attachEventListeners();
820
- });
579
+ });
821
580
  </script>
822
581
  </body>
582
+
823
583
  </html>