ortoni-report 2.0.1 → 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.
- package/changelog.md +54 -0
- package/dist/cli/cli.js +1 -1
- package/dist/ortoni-report.d.ts +20 -3
- package/dist/ortoni-report.js +86 -45
- package/dist/ortoni-report.mjs +86 -45
- package/dist/style/main.css +86 -0
- package/dist/utils/utils.js +0 -2
- package/dist/views/main.hbs +583 -0
- package/dist/views/navbar.hbs +46 -0
- package/dist/views/summaryCard.hbs +14 -0
- package/dist/views/testPanel.hbs +37 -0
- package/dist/views/testStatus.hbs +33 -0
- package/dist/views/userInfo.hbs +46 -0
- package/package.json +3 -8
- package/readme.md +42 -57
- package/dist/css/main.css +0 -22445
- package/dist/icon/32.png +0 -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/report-template.hbs +0 -674
- package/dist/utils/chart.js +0 -11128
package/dist/report-template.hbs
DELETED
|
@@ -1,674 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" data-theme="{{preferredTheme}}">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<meta name="description" content="Playwright HTML report by LetCode Koushik - V1.1.7">
|
|
8
|
-
<title>Ortoni Playwright Test Report</title>
|
|
9
|
-
<link rel="icon" href="node_modules/ortoni-report/dist/icon/32.png" type="image/x-icon">
|
|
10
|
-
<link rel="stylesheet" href="node_modules/ortoni-report/dist/css/main.css">
|
|
11
|
-
</head>
|
|
12
|
-
<style>
|
|
13
|
-
#summary, #testDetails, button#back-to-summary {
|
|
14
|
-
transition: opacity 0.2s ease-in-out;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.filter.active {
|
|
18
|
-
background-color: var(--bulma-background-active);
|
|
19
|
-
}
|
|
20
|
-
::-webkit-scrollbar {
|
|
21
|
-
width: 8px;
|
|
22
|
-
height: 8px;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
::-webkit-scrollbar-track {
|
|
26
|
-
background: #f1f1f1;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
::-webkit-scrollbar-thumb {
|
|
30
|
-
background: #888;
|
|
31
|
-
border-radius: 4px;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
::-webkit-scrollbar-thumb:hover {
|
|
35
|
-
background: #555;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
div#testDetails {
|
|
39
|
-
position: sticky;
|
|
40
|
-
top: 0;
|
|
41
|
-
z-index: 1;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
.sidebar {
|
|
45
|
-
overflow-y: auto;
|
|
46
|
-
max-height: calc(100vh - 100px);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
aside li {
|
|
50
|
-
cursor: pointer;
|
|
51
|
-
}
|
|
52
|
-
details summary {
|
|
53
|
-
cursor: pointer;
|
|
54
|
-
display: flex;
|
|
55
|
-
justify-content: space-between;
|
|
56
|
-
align-items: center;
|
|
57
|
-
}
|
|
58
|
-
details summary::after {
|
|
59
|
-
content: '';
|
|
60
|
-
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");
|
|
61
|
-
transform:rotate(-90deg);
|
|
62
|
-
background-size: 1em;
|
|
63
|
-
background-repeat: no-repeat;
|
|
64
|
-
background-position:right-center;
|
|
65
|
-
width: 1em;
|
|
66
|
-
height: 1em;
|
|
67
|
-
transition: transform 0.2s ease-in-out;
|
|
68
|
-
}
|
|
69
|
-
details[open] > summary::after {
|
|
70
|
-
transform: rotate(0deg);
|
|
71
|
-
}
|
|
72
|
-
.logoimage{
|
|
73
|
-
max-width: 100px;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
</style>
|
|
77
|
-
|
|
78
|
-
<body>
|
|
79
|
-
<section class="section">
|
|
80
|
-
<header class="container">
|
|
81
|
-
<div class="columns is-vcentered">
|
|
82
|
-
<div class="column is-two-fifths">
|
|
83
|
-
<div class="columns is-desktop is-vcentered is-multiline">
|
|
84
|
-
{{#if logo}}
|
|
85
|
-
<div class="column-1">
|
|
86
|
-
<figure class="image logoimage">
|
|
87
|
-
<img src="{{logo}}" />
|
|
88
|
-
</figure>
|
|
89
|
-
</div>
|
|
90
|
-
{{/if}}
|
|
91
|
-
{{#if projectName}}<div class="column"><span class="title">{{projectName}}</span></div>{{/if}}
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
<div class="column">
|
|
95
|
-
<div class="control">
|
|
96
|
-
<input class="input" name="search" type="search" placeholder="Search by test title" />
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
<div class="column is-1">
|
|
100
|
-
<div class="control">
|
|
101
|
-
<button id="toggle-theme" data-theme-status="{{preferredTheme}}" class="button">Theme</button>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
</header>
|
|
106
|
-
</section>
|
|
107
|
-
<section class="section">
|
|
108
|
-
<main class="container">
|
|
109
|
-
<div class="columns">
|
|
110
|
-
<aside class="column is-two-fifths">
|
|
111
|
-
<div class="columns is-mobile">
|
|
112
|
-
<div class="column">
|
|
113
|
-
<h2 class="title is-4">Tests</h2>
|
|
114
|
-
</div>
|
|
115
|
-
<div class="column">
|
|
116
|
-
<div class="select is-pulled-right">
|
|
117
|
-
<select id="project-filter">
|
|
118
|
-
<option value="all">All Projects</option>
|
|
119
|
-
{{#each projects}}
|
|
120
|
-
<option value="{{this}}">{{this}}</option>
|
|
121
|
-
{{/each}}
|
|
122
|
-
</select>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
</div>
|
|
126
|
-
<div class="content sidebar">
|
|
127
|
-
{{#each groupedResults}}
|
|
128
|
-
<details class="box">
|
|
129
|
-
<summary class="is-size-5 has-icon-right">
|
|
130
|
-
<div class="icon-text">
|
|
131
|
-
<span class="icon has-text-info">
|
|
132
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/file.png" alt="file name">
|
|
133
|
-
</span>
|
|
134
|
-
<span>{{@key}}</span>
|
|
135
|
-
</div>
|
|
136
|
-
</summary>
|
|
137
|
-
<ul>
|
|
138
|
-
{{#each this}}
|
|
139
|
-
<details class="mt-1">
|
|
140
|
-
<summary class="is-size-5 is-capitalized">
|
|
141
|
-
<div class="icon-text">
|
|
142
|
-
<span class="icon has-text-info">
|
|
143
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
|
|
144
|
-
</span>
|
|
145
|
-
<span>{{@key}}</span>
|
|
146
|
-
</div>
|
|
147
|
-
</summary>
|
|
148
|
-
<ul>
|
|
149
|
-
{{#each this}}
|
|
150
|
-
<details class="mb-1">
|
|
151
|
-
<summary class="is-capitalized is-size-6">{{@key}}</summary>
|
|
152
|
-
<ul>
|
|
153
|
-
{{#each this}}
|
|
154
|
-
<li class="media" data-suite-name="{{suite}}"
|
|
155
|
-
data-test-duration="{{duration}}"
|
|
156
|
-
data-project-name="{{projectName}}" data-test-id="{{index}}"
|
|
157
|
-
data-test-status="{{status}} {{retry}}">
|
|
158
|
-
<div class="icon-text">
|
|
159
|
-
{{#if isRetry}}
|
|
160
|
-
<span class="icon has-text-info">
|
|
161
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/retry.png"
|
|
162
|
-
alt="Retry"></span>
|
|
163
|
-
{{/if}}
|
|
164
|
-
{{#if (eq status "passed")}}
|
|
165
|
-
<span class="icon has-text-info">
|
|
166
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/pass.png"
|
|
167
|
-
alt="Pass"></span>
|
|
168
|
-
{{/if}}
|
|
169
|
-
{{#if (eq status "failed")}}
|
|
170
|
-
<span class="icon has-text-info">
|
|
171
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/fail.png"
|
|
172
|
-
alt="Fail"></span>
|
|
173
|
-
{{else}}
|
|
174
|
-
{{#if (eq status "skipped")}}
|
|
175
|
-
<span class="icon has-text-info">
|
|
176
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/skip.png"
|
|
177
|
-
alt="Skip"></span>
|
|
178
|
-
{{/if}}
|
|
179
|
-
{{/if}}
|
|
180
|
-
{{#if (eq status "timedOut")}}
|
|
181
|
-
<span class="icon has-text-info">
|
|
182
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/timeout.png"
|
|
183
|
-
alt="timedOut"></span>
|
|
184
|
-
{{/if}}
|
|
185
|
-
{{#if (eq status "flaky")}}
|
|
186
|
-
<span class="icon has-text-info">
|
|
187
|
-
<img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/flaky.png"
|
|
188
|
-
alt="flaky"></span>
|
|
189
|
-
{{/if}}
|
|
190
|
-
<span>{{title}}</span>
|
|
191
|
-
</div>
|
|
192
|
-
</li>
|
|
193
|
-
{{/each}}
|
|
194
|
-
</ul>
|
|
195
|
-
</details>
|
|
196
|
-
{{/each}}
|
|
197
|
-
</ul>
|
|
198
|
-
</details>
|
|
199
|
-
{{/each}}
|
|
200
|
-
</ul>
|
|
201
|
-
</details>
|
|
202
|
-
{{/each}}
|
|
203
|
-
</div>
|
|
204
|
-
</aside>
|
|
205
|
-
<section class="column is-three-fifths">
|
|
206
|
-
{{!-- Overall summary --}}
|
|
207
|
-
<div id="summary">
|
|
208
|
-
<div class="columns is-multiline">
|
|
209
|
-
<div class="column is-one-third">
|
|
210
|
-
<div class="card is-clickable filter" data-status="all">
|
|
211
|
-
<header class="card-header has-text-centered">
|
|
212
|
-
<p class="card-header-title">All Tests</p>
|
|
213
|
-
</header>
|
|
214
|
-
<div class="card-content">
|
|
215
|
-
<div class="content">
|
|
216
|
-
<p class="has-text-centered has-text-primary">{{totalCount}}</p>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
<div class="column is-one-third">
|
|
222
|
-
<div class="card is-clickable filter" data-status="passed">
|
|
223
|
-
<header class="card-header has-text-centered">
|
|
224
|
-
<p class="card-header-title">Passed</p>
|
|
225
|
-
</header>
|
|
226
|
-
<div class="card-content">
|
|
227
|
-
<div class="content">
|
|
228
|
-
<p class="has-text-centered has-text-success">{{passCount}}</p>
|
|
229
|
-
</div>
|
|
230
|
-
</div>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
<div class="column is-one-third">
|
|
234
|
-
<div class="card is-clickable filter" data-status="failed">
|
|
235
|
-
<header class="card-header">
|
|
236
|
-
<p class="card-header-title has-text-centered">Failed</p>
|
|
237
|
-
</header>
|
|
238
|
-
<div class="card-content">
|
|
239
|
-
<div class="content">
|
|
240
|
-
<p class="has-text-centered has-text-danger">{{failCount}}</p>
|
|
241
|
-
</div>
|
|
242
|
-
</div>
|
|
243
|
-
</div>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
<div class="columns is-multiline">
|
|
247
|
-
<div class="column is-one-third">
|
|
248
|
-
<div class="card is-clickable filter" data-status="skipped">
|
|
249
|
-
<header class="card-header has-text-centered">
|
|
250
|
-
<p class="card-header-title">Skipped</p>
|
|
251
|
-
</header>
|
|
252
|
-
<div class="card-content">
|
|
253
|
-
<div class="content">
|
|
254
|
-
<p class="has-text-centered has-text-info">{{skipCount}}</p>
|
|
255
|
-
</div>
|
|
256
|
-
</div>
|
|
257
|
-
</div>
|
|
258
|
-
</div>
|
|
259
|
-
<div class="column is-one-third">
|
|
260
|
-
<div class="card is-clickable filter" data-status="flaky">
|
|
261
|
-
<header class="card-header has-text-centered">
|
|
262
|
-
<p class="card-header-title">Flaky</p>
|
|
263
|
-
</header>
|
|
264
|
-
<div class="card-content">
|
|
265
|
-
<div class="content">
|
|
266
|
-
<p class="has-text-centered has-text-warning">{{flakyCount}}</p>
|
|
267
|
-
</div>
|
|
268
|
-
</div>
|
|
269
|
-
</div>
|
|
270
|
-
</div>
|
|
271
|
-
<div class="column is-one-third">
|
|
272
|
-
<div class="card is-clickable filter" data-status="retry">
|
|
273
|
-
<header class="card-header has-text-centered">
|
|
274
|
-
<p class="card-header-title">Retry</p>
|
|
275
|
-
</header>
|
|
276
|
-
<div class="card-content">
|
|
277
|
-
<div class="content">
|
|
278
|
-
<p class="has-text-centered has-text-info">{{retryCount}}</p>
|
|
279
|
-
</div>
|
|
280
|
-
</div>
|
|
281
|
-
</div>
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
{{!-- Suite details with chart --}}
|
|
285
|
-
<div class="box">
|
|
286
|
-
<header class="has-text-centered title is-4">Suite</header>
|
|
287
|
-
<div class="columns">
|
|
288
|
-
<div class="column is-half">
|
|
289
|
-
{{#if authorName}}<h4>Author: {{authorName}}</h4>{{/if}}
|
|
290
|
-
{{#if testType}}<h4>Test Type: {{testType}}</h4>{{/if}}
|
|
291
|
-
{{#if totalDuration}}<h4>Duration: {{totalDuration}}</h4>{{/if}}
|
|
292
|
-
<h4>Success Rate: {{successRate}} %</h4>
|
|
293
|
-
<h4>Last Run: {{lastRunDate}}</h4>
|
|
294
|
-
</div>
|
|
295
|
-
<div class="column is-half">
|
|
296
|
-
<div class="chart-container">
|
|
297
|
-
<canvas id="testChart"></canvas>
|
|
298
|
-
</div>
|
|
299
|
-
</div>
|
|
300
|
-
</div>
|
|
301
|
-
</div>
|
|
302
|
-
</div>
|
|
303
|
-
{{!-- Test details --}}
|
|
304
|
-
<div id="testDetails" style="display: none;">
|
|
305
|
-
<!-- Back button should be outside the dynamic content -->
|
|
306
|
-
<button class="button content" id="back-to-summary"onclick="showSummary()">Back to Summary</button>
|
|
307
|
-
<div class="tags" id="attachTags"></div>
|
|
308
|
-
<!-- Test Details will be displayed here -->
|
|
309
|
-
</div>
|
|
310
|
-
</section>
|
|
311
|
-
</div>
|
|
312
|
-
</main>
|
|
313
|
-
</section>
|
|
314
|
-
<script src="node_modules/ortoni-report/dist/utils/chart.js"></script>
|
|
315
|
-
<script>
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
function escapeHtml(unsafe) {
|
|
319
|
-
return unsafe.replace(/[&<"']/g, function (match) {
|
|
320
|
-
const escapeMap = {
|
|
321
|
-
'&': '&',
|
|
322
|
-
'<': '<',
|
|
323
|
-
'>': '>',
|
|
324
|
-
'"': '"',
|
|
325
|
-
"'": '''
|
|
326
|
-
};
|
|
327
|
-
return escapeMap[match];
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
332
|
-
const testData = {{{ json results }}};
|
|
333
|
-
const testDetails = document.getElementById('testDetails');
|
|
334
|
-
const summary = document.getElementById('summary');
|
|
335
|
-
const backButton = document.querySelector('button#back-to-summary');
|
|
336
|
-
|
|
337
|
-
const themeButton = document.getElementById("toggle-theme");
|
|
338
|
-
const preferredTheme = themeButton.getAttribute("data-theme-status");
|
|
339
|
-
const htmlElement = document.documentElement;
|
|
340
|
-
|
|
341
|
-
if (preferredTheme === 'dark') {
|
|
342
|
-
htmlElement.setAttribute('data-theme', 'dark');
|
|
343
|
-
themeButton.classList.add('is-dark');
|
|
344
|
-
themeButton.textContent = 'Dark';
|
|
345
|
-
} else if (preferredTheme === 'light') {
|
|
346
|
-
htmlElement.setAttribute('data-theme', 'light');
|
|
347
|
-
themeButton.classList.add('is-light');
|
|
348
|
-
themeButton.textContent = 'Light';
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
themeButton.addEventListener('click', () => {
|
|
352
|
-
const currentTheme = htmlElement.getAttribute('data-theme');
|
|
353
|
-
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
|
354
|
-
htmlElement.setAttribute('data-theme', newTheme);
|
|
355
|
-
if (newTheme === 'dark') {
|
|
356
|
-
themeButton.classList.remove('is-light');
|
|
357
|
-
themeButton.classList.add('is-dark');
|
|
358
|
-
themeButton.textContent = 'Dark';
|
|
359
|
-
} else {
|
|
360
|
-
themeButton.classList.remove('is-dark');
|
|
361
|
-
themeButton.classList.add('is-light');
|
|
362
|
-
themeButton.textContent = 'Light';
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
function showSummary() {
|
|
367
|
-
summary.style.display = 'block';
|
|
368
|
-
testDetails.style.display = 'none';
|
|
369
|
-
backButton.style.display = 'none';
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
window.showSummary = showSummary;
|
|
373
|
-
|
|
374
|
-
function attachTags(testTags){
|
|
375
|
-
const tags = document.querySelector("#attachTags");
|
|
376
|
-
testTags.forEach(tag => {
|
|
377
|
-
const tagElement = document.createElement('span');
|
|
378
|
-
tagElement.className = 'tag is-info';
|
|
379
|
-
tagElement.textContent = tag;
|
|
380
|
-
tags.appendChild(tagElement);
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function displayTestDetails(test) {
|
|
385
|
-
const summary = document.getElementById('summary');
|
|
386
|
-
const testDetails = document.getElementById('testDetails');
|
|
387
|
-
const backButton = document.querySelector('button#back-to-summary');
|
|
388
|
-
summary.style.display = 'none';
|
|
389
|
-
testDetails.style.opacity = '0';
|
|
390
|
-
testDetails.style.display = 'block';
|
|
391
|
-
setTimeout(() => {
|
|
392
|
-
testDetails.style.opacity = '1';
|
|
393
|
-
backButton.style.opacity = '1';
|
|
394
|
-
}, 50);
|
|
395
|
-
|
|
396
|
-
let statusClass = '';
|
|
397
|
-
let statusText = test.status.toUpperCase();
|
|
398
|
-
if (test.status.startsWith('passed')) {
|
|
399
|
-
statusClass = 'tag is-success';
|
|
400
|
-
} else if (test.status === 'flaky') {
|
|
401
|
-
statusClass = 'tag is-warning';
|
|
402
|
-
} else if (test.status === 'failed') {
|
|
403
|
-
statusClass = 'tag is-danger';
|
|
404
|
-
} else {
|
|
405
|
-
statusClass = 'tag is-info';
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
testDetails.innerHTML = `
|
|
409
|
-
<button class="button content" id="back-to-summary" style="display: block" onclick="showSummary()">Back to Summary</button>
|
|
410
|
-
<div class="content has-text-centered">
|
|
411
|
-
<p class="title">${test.title}</p>
|
|
412
|
-
<p class="subtitle" id="filepath">${test.location}</p>
|
|
413
|
-
</div>
|
|
414
|
-
<div class="columns">
|
|
415
|
-
<div class="column content">
|
|
416
|
-
<h4 class="title is-4">Status</h4>
|
|
417
|
-
<p class="${statusClass}">${statusText}</p>
|
|
418
|
-
${test.duration.length > 0 ? `
|
|
419
|
-
<h4 class="title is-4">Duration</h4>
|
|
420
|
-
<p class="${statusClass}">${test.duration}</p>` : ""}
|
|
421
|
-
<div class="tags" id="attachTags"></div>
|
|
422
|
-
${test.videoPath ? `
|
|
423
|
-
<div id="testVideo" class="modal">
|
|
424
|
-
<div class="modal-background"></div>
|
|
425
|
-
<div class="modal-content">
|
|
426
|
-
<figure>
|
|
427
|
-
<video controls>
|
|
428
|
-
<source src="file://${test.videoPath}" type="video/webm">
|
|
429
|
-
Your browser does not support the video tag.
|
|
430
|
-
</video>
|
|
431
|
-
</figure>
|
|
432
|
-
</div>
|
|
433
|
-
<button onclick="closeVideo()" class="modal-close is-large" aria-label="close"></button>
|
|
434
|
-
</div>
|
|
435
|
-
<button class="button" onclick="openVideo()">Attachment: Video</button>
|
|
436
|
-
`:''}
|
|
437
|
-
</div>
|
|
438
|
-
<div class="column content">
|
|
439
|
-
${test.screenshotPath ? `
|
|
440
|
-
<div id="testImage" class="modal">
|
|
441
|
-
<div class="modal-background"></div>
|
|
442
|
-
<div class="modal-content">
|
|
443
|
-
<p class="image">
|
|
444
|
-
<img src="${test.base64Image ? test.screenshotPath : `file://${test.screenshotPath}`}" alt="Screenshot">
|
|
445
|
-
</p>
|
|
446
|
-
</div>
|
|
447
|
-
<button onclick="closeModal()" class="modal-close is-large" aria-label="close"></button>
|
|
448
|
-
</div>
|
|
449
|
-
<figure class="image box">
|
|
450
|
-
<img onclick="openModal()" src="${test.base64Image ? test.screenshotPath : `file://${test.screenshotPath}`}" alt="Screenshot">
|
|
451
|
-
</figure>` : ''}
|
|
452
|
-
</div>
|
|
453
|
-
</div>
|
|
454
|
-
<div class="content">
|
|
455
|
-
${test.steps.length > 0 ? `
|
|
456
|
-
<details id="stepopen">
|
|
457
|
-
<summary><h4 class="title is-4">Steps</h4></summary>
|
|
458
|
-
<span id="stepDetails" class="content"></span>
|
|
459
|
-
</details>
|
|
460
|
-
`: ``}
|
|
461
|
-
</div class="content">
|
|
462
|
-
<div>
|
|
463
|
-
${test.errors.length ? `
|
|
464
|
-
<h4 class="title is-4">Errors</h4>
|
|
465
|
-
<div class="content">
|
|
466
|
-
<pre><code class="data-lang=js">${escapeHtml(test.errors.join('\n'))}</code></pre>
|
|
467
|
-
</div>` : ''}
|
|
468
|
-
</div>
|
|
469
|
-
<div>
|
|
470
|
-
${test.logs ? `
|
|
471
|
-
<h4 class="title is-4">Logs</h4>
|
|
472
|
-
<div class="box">
|
|
473
|
-
<pre>${escapeHtml(test.logs)}</pre>
|
|
474
|
-
</div>` : ''}
|
|
475
|
-
</div>
|
|
476
|
-
`;
|
|
477
|
-
|
|
478
|
-
const stepDetailsDiv = document.getElementById('stepDetails');
|
|
479
|
-
if(stepDetailsDiv){
|
|
480
|
-
const stepsList = attachSteps(test);
|
|
481
|
-
const detail = document.getElementById("stepopen");
|
|
482
|
-
if(test.errors.length > 0){
|
|
483
|
-
detail.setAttribute("open", "");
|
|
484
|
-
}
|
|
485
|
-
stepDetailsDiv.appendChild(stepsList);
|
|
486
|
-
}
|
|
487
|
-
attachTags(test.testTags);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function attachSteps(test) {
|
|
491
|
-
const stepsList = document.createElement("ul");
|
|
492
|
-
stepsList.setAttribute("id", "steps");
|
|
493
|
-
stepsList.innerHTML = '';
|
|
494
|
-
test.steps.forEach(step => {
|
|
495
|
-
const li = document.createElement('li');
|
|
496
|
-
li.innerHTML = `<strong class="${step.snippet ? 'has-text-danger' : ''}">${step.title}</strong>`;
|
|
497
|
-
if (step.snippet) {
|
|
498
|
-
const pre = document.createElement('pre');
|
|
499
|
-
const code = document.createElement('code');
|
|
500
|
-
const locationText = step.location ? `\n\nat: ${step.location}` : '';
|
|
501
|
-
code.textContent = `${step.snippet}${locationText}`;
|
|
502
|
-
code.setAttribute('data-lang', 'js');
|
|
503
|
-
pre.appendChild(code);
|
|
504
|
-
li.appendChild(pre);
|
|
505
|
-
}
|
|
506
|
-
stepsList.appendChild(li);
|
|
507
|
-
});
|
|
508
|
-
return stepsList;
|
|
509
|
-
}
|
|
510
|
-
function openModal() {
|
|
511
|
-
let modal = document.querySelector("#testImage");
|
|
512
|
-
modal.classList.add("is-active");
|
|
513
|
-
}
|
|
514
|
-
function openVideo(){
|
|
515
|
-
let modal = document.querySelector("#testVideo");
|
|
516
|
-
modal.classList.add("is-active");
|
|
517
|
-
}
|
|
518
|
-
function closeVideo(){
|
|
519
|
-
let modal = document.querySelector("#testVideo");
|
|
520
|
-
modal.classList.remove("is-active");
|
|
521
|
-
}
|
|
522
|
-
function closeModal() {
|
|
523
|
-
let modal = document.querySelector("#testImage");
|
|
524
|
-
modal.classList.remove("is-active");
|
|
525
|
-
}
|
|
526
|
-
window.openModal = openModal;
|
|
527
|
-
window.openVideo = openVideo;
|
|
528
|
-
window.closeVideo = closeVideo;
|
|
529
|
-
window.closeModal = closeModal;
|
|
530
|
-
|
|
531
|
-
document.addEventListener('keydown', (event) => {
|
|
532
|
-
if (event.key === "Escape") {
|
|
533
|
-
closeModal();
|
|
534
|
-
}});
|
|
535
|
-
|
|
536
|
-
function attachEventListeners() {
|
|
537
|
-
const testItems = document.querySelectorAll('[data-test-id]');
|
|
538
|
-
testItems.forEach(item => {
|
|
539
|
-
item.addEventListener('click', () => {
|
|
540
|
-
const testId = item.getAttribute('data-test-id');
|
|
541
|
-
const test = testData[testId];
|
|
542
|
-
displayTestDetails(test);
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
const filters = document.querySelectorAll('.filter');
|
|
547
|
-
filters.forEach(filter => {
|
|
548
|
-
filter.addEventListener('click', () => {
|
|
549
|
-
const status = filter.getAttribute('data-status');
|
|
550
|
-
filters.forEach(f => {
|
|
551
|
-
if (f.getAttribute('data-status')) {
|
|
552
|
-
f.classList.remove('active');
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
filter.classList.add('active');
|
|
556
|
-
applyFilters();
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
const projectFilter = document.getElementById('project-filter');
|
|
561
|
-
projectFilter.addEventListener('change', () => {
|
|
562
|
-
applyFilters();
|
|
563
|
-
});
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
function applyFilters() {
|
|
567
|
-
const selectedProject = document.getElementById('project-filter').value;
|
|
568
|
-
const activeFilter = document.querySelector('.filter.active');
|
|
569
|
-
const selectedStatus = activeFilter ? activeFilter.getAttribute('data-status') : 'all';
|
|
570
|
-
|
|
571
|
-
const testItems = document.querySelectorAll('li[data-test-id]');
|
|
572
|
-
const detailsElements = document.querySelectorAll('details');
|
|
573
|
-
|
|
574
|
-
detailsElements.forEach(details => {
|
|
575
|
-
let shouldShowDetails = false;
|
|
576
|
-
const items = details.querySelectorAll('li[data-test-id]');
|
|
577
|
-
items.forEach(item => {
|
|
578
|
-
const projectName = item.getAttribute('data-project-name').trim();
|
|
579
|
-
const testStatus = item.getAttribute('data-test-status').trim();
|
|
580
|
-
const matchesProject = (selectedProject === 'all' || projectName === selectedProject);
|
|
581
|
-
const matchesStatus = (selectedStatus === 'all' || testStatus.includes(selectedStatus) ||
|
|
582
|
-
(selectedStatus === 'failed' && (testStatus === 'failed' || testStatus === 'timedOut')) ||
|
|
583
|
-
(selectedStatus === 'retry' && testStatus.includes('retry')) ||
|
|
584
|
-
(selectedStatus === 'flaky' && testStatus.includes('flaky')));
|
|
585
|
-
|
|
586
|
-
if (matchesProject && matchesStatus) {
|
|
587
|
-
item.classList.remove('is-hidden');
|
|
588
|
-
shouldShowDetails = true;
|
|
589
|
-
} else {
|
|
590
|
-
item.classList.add('is-hidden');
|
|
591
|
-
}
|
|
592
|
-
});
|
|
593
|
-
details.open = shouldShowDetails;
|
|
594
|
-
details.classList.toggle('is-hidden', !shouldShowDetails);
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const searchInput = document.querySelector('input[name="search"]');
|
|
599
|
-
const detailsElements = document.querySelectorAll('details');
|
|
600
|
-
function filterTests(search){
|
|
601
|
-
const searchTerm = search.toLowerCase();
|
|
602
|
-
const testItems = document.querySelectorAll('[data-test-id]');
|
|
603
|
-
|
|
604
|
-
if (searchTerm) {
|
|
605
|
-
detailsElements.forEach(detail => {
|
|
606
|
-
detail.open = false; // Collapse all details initially
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
testItems.forEach(item => {
|
|
610
|
-
const testTitle = item.textContent.toLowerCase();
|
|
611
|
-
if (testTitle.includes(searchTerm)) {
|
|
612
|
-
item.style.display = 'block'; // Show matching test item
|
|
613
|
-
|
|
614
|
-
let parent = item.parentElement;
|
|
615
|
-
while (parent && parent.tagName !== 'ASIDE') {
|
|
616
|
-
if (parent.tagName === 'DETAILS') {
|
|
617
|
-
parent.open = true;
|
|
618
|
-
}
|
|
619
|
-
parent = parent.parentElement;
|
|
620
|
-
}
|
|
621
|
-
} else {
|
|
622
|
-
item.style.display = 'none';
|
|
623
|
-
}
|
|
624
|
-
});
|
|
625
|
-
} else {
|
|
626
|
-
testItems.forEach(item => {
|
|
627
|
-
item.style.display = 'block';
|
|
628
|
-
});
|
|
629
|
-
detailsElements.forEach(detail => {
|
|
630
|
-
detail.open = false;
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
function debounce(func, wait) {
|
|
635
|
-
let timeout;
|
|
636
|
-
return function(...args) {
|
|
637
|
-
clearTimeout(timeout);
|
|
638
|
-
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
const debouncedSearch = debounce((event) => {
|
|
643
|
-
filterTests(event.target.value);
|
|
644
|
-
}, 300);
|
|
645
|
-
|
|
646
|
-
searchInput.addEventListener('input', debouncedSearch);
|
|
647
|
-
|
|
648
|
-
const ctx = document.getElementById('testChart').getContext('2d');
|
|
649
|
-
new Chart(ctx, {
|
|
650
|
-
type: 'doughnut',
|
|
651
|
-
data: {
|
|
652
|
-
labels: ['Passed', 'Failed', 'Skipped','Flaky'],
|
|
653
|
-
datasets: [{
|
|
654
|
-
data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}, {{flakyCount}}],
|
|
655
|
-
backgroundColor: ['#28a745', '#dc3545', '#d5d4a1', '#FFB704']
|
|
656
|
-
}]
|
|
657
|
-
},
|
|
658
|
-
options: {
|
|
659
|
-
responsive: true,
|
|
660
|
-
maintainAspectRatio: false,
|
|
661
|
-
plugins: {
|
|
662
|
-
legend: {
|
|
663
|
-
position: 'right'
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
});
|
|
668
|
-
|
|
669
|
-
attachEventListeners();
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
</script>
|
|
673
|
-
</body>
|
|
674
|
-
</html>
|