ortoni-report 2.0.6 → 2.0.7
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 +16 -0
- package/dist/ortoni-report.d.ts +16 -4
- package/dist/ortoni-report.js +427 -240
- package/dist/ortoni-report.mjs +427 -240
- package/dist/style/main.css +92 -94
- package/dist/views/head.hbs +11 -0
- package/dist/views/main.hbs +482 -454
- package/dist/views/navbar.hbs +1 -1
- package/dist/views/project.hbs +1 -3
- package/dist/views/testIcons.hbs +1 -151
- package/dist/views/testStatus.hbs +1 -1
- package/package.json +4 -1
- package/readme.md +23 -20
- package/dist/types/reporterConfig.js +0 -2
- package/dist/types/testResults.js +0 -2
- package/dist/utils/expressServer.js +0 -33
- package/dist/utils/utils.js +0 -85
- package/dist/utils/webSocketHelper.js +0 -93
package/dist/views/main.hbs
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en" data-theme="{{preferredTheme}}">
|
|
3
|
-
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<meta name="description" content="Playwright HTML report by LetCode Koushik - V2.0.6">
|
|
7
|
-
<title>{{title}}</title>
|
|
8
|
-
<link rel="icon" href="https://raw.githubusercontent.com/ortoniKC/ortoni-report/refs/heads/main/favicon.png"
|
|
9
|
-
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" />
|
|
13
|
-
</head>
|
|
3
|
+
{{> head}}
|
|
14
4
|
<style>
|
|
15
5
|
{{{inlineCss}}}
|
|
16
6
|
</style>
|
|
@@ -44,506 +34,544 @@
|
|
|
44
34
|
</section>
|
|
45
35
|
<script>
|
|
46
36
|
document.addEventListener('DOMContentLoaded', () => {
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
37
|
+
const testData = {{{ json results }}};
|
|
38
|
+
const testHistory = {{{ json testHistories }}};
|
|
39
|
+
let testHistoriesMap = {};
|
|
40
|
+
let testHistoryTitle = '';
|
|
51
41
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
const elements = {
|
|
43
|
+
testDetails: document.getElementById('testDetails'),
|
|
44
|
+
summary: document.getElementById('summary'),
|
|
45
|
+
themeButton: document.getElementById("toggle-theme"),
|
|
46
|
+
themeIcon: document.getElementById("theme-icon"),
|
|
47
|
+
htmlElement: document.documentElement,
|
|
48
|
+
searchInput: document.querySelector('input[name="search"]'),
|
|
49
|
+
detailsElements: document.querySelectorAll('details'),
|
|
50
|
+
filtersDisplay: document.getElementById('selected-filters')
|
|
51
|
+
};
|
|
56
52
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
themeIcon.classList.add("fa", "fa-moon");
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
themeButton.addEventListener('click', () => {
|
|
74
|
-
const currentTheme = htmlElement.getAttribute('data-theme');
|
|
75
|
-
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
|
76
|
-
htmlElement.setAttribute('data-theme', newTheme);
|
|
77
|
-
if (newTheme === 'dark') {
|
|
78
|
-
themeIcon.classList = '';
|
|
79
|
-
themeIcon.classList.add("fa", "fa-moon");
|
|
80
|
-
} else {
|
|
81
|
-
themeIcon.classList = '';
|
|
82
|
-
themeIcon.classList.add("fa", "fa-sun");
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
function showSummary() {
|
|
87
|
-
summary.style.display = 'block';
|
|
88
|
-
testDetails.style.display = 'none';
|
|
89
|
-
}
|
|
90
|
-
window.showSummary = showSummary;
|
|
91
|
-
|
|
92
|
-
function displayTestDetails(test) {
|
|
93
|
-
const summary = document.getElementById('summary');
|
|
94
|
-
const testDetails = document.getElementById('testDetails');
|
|
95
|
-
summary.style.display = 'none';
|
|
96
|
-
testDetails.style.opacity = '0';
|
|
97
|
-
testDetails.style.display = 'block';
|
|
98
|
-
setTimeout(() => {
|
|
99
|
-
testDetails.style.opacity = '1';
|
|
100
|
-
}, 50);
|
|
101
|
-
let currentScreenshotIndex = 0;
|
|
102
|
-
function changeScreenshot(direction) {
|
|
103
|
-
const screenshots = test.screenshots;
|
|
104
|
-
currentScreenshotIndex = (currentScreenshotIndex + direction + screenshots.length) % screenshots.length;
|
|
105
|
-
updateScreenshot();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function gotoScreenshot(index) {
|
|
109
|
-
currentScreenshotIndex = index;
|
|
110
|
-
updateScreenshot();
|
|
53
|
+
const themeManager = {
|
|
54
|
+
init() {
|
|
55
|
+
const preferredTheme = elements.themeButton.getAttribute("data-theme-status");
|
|
56
|
+
this.setTheme(preferredTheme);
|
|
57
|
+
elements.themeButton.addEventListener('click', () => this.toggleTheme());
|
|
58
|
+
},
|
|
59
|
+
setTheme(theme) {
|
|
60
|
+
elements.htmlElement.setAttribute('data-theme', theme);
|
|
61
|
+
elements.themeIcon.className = `fa fa-${theme === 'dark' ? 'moon' : 'sun'}`;
|
|
62
|
+
},
|
|
63
|
+
toggleTheme() {
|
|
64
|
+
const currentTheme = elements.htmlElement.getAttribute('data-theme');
|
|
65
|
+
this.setTheme(currentTheme === 'light' ? 'dark' : 'light');
|
|
111
66
|
}
|
|
67
|
+
};
|
|
112
68
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
69
|
+
const testDetailsManager = {
|
|
70
|
+
show(test) {
|
|
71
|
+
elements.summary.style.display = 'none';
|
|
72
|
+
elements.testDetails.style.opacity = '0';
|
|
73
|
+
elements.testDetails.style.display = 'block';
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
elements.testDetails.style.opacity = '1';
|
|
76
|
+
}, 50);
|
|
77
|
+
this.render(test);
|
|
78
|
+
},
|
|
79
|
+
hide() {
|
|
80
|
+
elements.summary.style.display = 'block';
|
|
81
|
+
elements.testDetails.style.display = 'none';
|
|
82
|
+
},
|
|
83
|
+
render(test) {
|
|
84
|
+
let currentScreenshotIndex = 0;
|
|
85
|
+
const statusClass = this.getStatusClass(test.status);
|
|
86
|
+
const statusIcon = this.getStatusIcon(test.status);
|
|
87
|
+
const projectIcon = this.getProjectIcon(test.projectName);
|
|
117
88
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (test.status.startsWith('passed')) {
|
|
131
|
-
statusClass = 'success';
|
|
132
|
-
statusIcon = 'check-circle';
|
|
133
|
-
} else if (test.status === 'flaky') {
|
|
134
|
-
statusClass = 'warning';
|
|
135
|
-
statusIcon = 'exclamation-triangle';
|
|
136
|
-
} else if (test.status === 'failed') {
|
|
137
|
-
statusClass = 'danger';
|
|
138
|
-
statusIcon = 'times-circle';
|
|
139
|
-
} else {
|
|
140
|
-
statusClass = 'info';
|
|
141
|
-
statusIcon = 'question-circle';
|
|
142
|
-
}
|
|
143
|
-
testDetails.innerHTML = `
|
|
144
|
-
<div class="sticky-header">
|
|
145
|
-
<div class="card mb-3">
|
|
146
|
-
<button class="button is-primary mb-3" id="back-to-summary" onclick="showSummary()">
|
|
147
|
-
<span class="icon"><i class="fa fa-chevron-left" style="color: #63E6BE;"></i></span>
|
|
148
|
-
<span>Back to Summary</span>
|
|
149
|
-
</button>
|
|
150
|
-
<div class="card-content">
|
|
151
|
-
<div class="content has-text-centered">
|
|
152
|
-
<h1 class="title is-2">${test.title}</h1>
|
|
153
|
-
<p class="subtitle is-5" id="filepath">${test.location}</p>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
<footer class="card-footer">
|
|
157
|
-
<div class="card-footer-item">
|
|
158
|
-
<div class="columns is-mobile">
|
|
159
|
-
<div class="column is-half">
|
|
160
|
-
<div class="is-flex is-align-items-center">
|
|
161
|
-
<span class="icon status-icon has-text-${statusClass}">
|
|
162
|
-
<i class="fa fa-${statusIcon}"></i>
|
|
163
|
-
</span>
|
|
164
|
-
<span class="has-text-weight-bold is-uppercase has-text-${statusClass}">${test.status}</span>
|
|
165
|
-
</div>
|
|
166
|
-
</div>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
${test.duration ? `
|
|
170
|
-
<div class="card-footer-item">
|
|
171
|
-
<div class="column is-half">
|
|
172
|
-
<div class="is-flex is-align-items-center">
|
|
173
|
-
<span class="icon status-icon has-text-info">
|
|
174
|
-
<i class="fa fa-clock"></i>
|
|
175
|
-
</span>
|
|
176
|
-
<span class="has-text-info has-text-weight-semibold">${test.duration}</span>
|
|
89
|
+
elements.testDetails.innerHTML = `
|
|
90
|
+
<div class="sticky-header">
|
|
91
|
+
<div class="card mb-3">
|
|
92
|
+
<button class="button is-primary mb-3" id="back-to-summary" onclick="showSummary()">
|
|
93
|
+
<span class="icon"><i class="fa fa-chevron-left"></i></span>
|
|
94
|
+
<span>Back to Summary</span>
|
|
95
|
+
</button>
|
|
96
|
+
<div class="card-content">
|
|
97
|
+
<div class="content has-text-centered">
|
|
98
|
+
<h1 class="title is-2">${test.title}</h1>
|
|
99
|
+
<p class="subtitle is-5" id="filepath">${test.location}</p>
|
|
100
|
+
</div>
|
|
177
101
|
</div>
|
|
102
|
+
<footer class="card-footer">
|
|
103
|
+
<div class="card-footer-item">
|
|
104
|
+
<div class="columns is-mobile">
|
|
105
|
+
<div class="column is-half">
|
|
106
|
+
<div class="is-flex is-align-items-center">
|
|
107
|
+
<span class="icon status-icon has-text-${statusClass}">
|
|
108
|
+
<i class="fa fa-${statusIcon}"></i>
|
|
109
|
+
</span>
|
|
110
|
+
<span class="has-text-weight-bold is-uppercase has-text-${statusClass}">${test.status}</span>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
${test.duration ? `
|
|
116
|
+
<div class="card-footer-item">
|
|
117
|
+
<div class="column is-half">
|
|
118
|
+
<div class="is-flex is-align-items-center">
|
|
119
|
+
<span class="icon status-icon has-text-info">
|
|
120
|
+
<i class="fa fa-clock"></i>
|
|
121
|
+
</span>
|
|
122
|
+
<span class="has-text-info has-text-weight-semibold">${test.duration}</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
` : ''}
|
|
127
|
+
${test.projectName ? `
|
|
128
|
+
<div class="card-footer-item">
|
|
129
|
+
<div class="is-flex is-align-items-center">
|
|
130
|
+
<span class="icon status-icon has-text-link">
|
|
131
|
+
${projectIcon}
|
|
132
|
+
</span>
|
|
133
|
+
<span> ${test.projectName}</span>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
` : ''}
|
|
137
|
+
</footer>
|
|
178
138
|
</div>
|
|
179
|
-
</div>
|
|
180
|
-
` : ''}
|
|
181
|
-
${test.projectName ? `
|
|
182
|
-
<div class="card-footer-item">
|
|
183
|
-
<div class="is-flex is-align-items-center">
|
|
184
|
-
<span class="icon status-icon has-text-info">
|
|
185
|
-
<i class="fa fa-window-maximize" style="color: #B197FC;"></i>
|
|
186
|
-
</span>
|
|
187
|
-
<span class="" style="color: #B197FC;"> ${test.projectName}</span>
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
` : ''}
|
|
191
|
-
</footer>
|
|
192
139
|
</div>
|
|
193
|
-
|
|
140
|
+
<div class="content-wrapper">
|
|
141
|
+
${this.renderTestContent(test)}
|
|
142
|
+
</div>
|
|
143
|
+
`;
|
|
194
144
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
145
|
+
this.attachScreenshotListeners(test);
|
|
146
|
+
this.attachSteps(test);
|
|
147
|
+
},
|
|
148
|
+
getStatusClass(status) {
|
|
149
|
+
if (status.startsWith('passed')) return 'success';
|
|
150
|
+
if (status === 'flaky') return 'warning';
|
|
151
|
+
if (status === 'failed') return 'danger';
|
|
152
|
+
return 'info';
|
|
153
|
+
},
|
|
154
|
+
getStatusIcon(status) {
|
|
155
|
+
if (status.startsWith('passed')) return 'check-circle';
|
|
156
|
+
if (status === 'flaky') return 'exclamation-triangle';
|
|
157
|
+
if (status === 'failed') return 'times-circle';
|
|
158
|
+
return 'question-circle';
|
|
159
|
+
},
|
|
160
|
+
getProjectIcon(project) {
|
|
161
|
+
if (project === 'webkit') return `<i class="fa-brands fa-safari"></i>`;
|
|
162
|
+
if (project === 'firefox') return `<i class="fa-brands fa-firefox"></i>`;
|
|
163
|
+
return `<i class="fa-brands fa-chrome"></i>`;
|
|
164
|
+
},
|
|
165
|
+
renderTestContent(test) {
|
|
166
|
+
let content = '';
|
|
167
|
+
if (test.status !== "skipped") {
|
|
168
|
+
content += this.renderScreenshotsAndVideo(test);
|
|
169
|
+
}
|
|
170
|
+
content += this.renderAdditionalInfo(test);
|
|
171
|
+
content += this.renderSteps(test);
|
|
172
|
+
content += this.renderErrors(test);
|
|
173
|
+
content += this.renderLogs(test);
|
|
174
|
+
return content;
|
|
175
|
+
},
|
|
176
|
+
renderScreenshotsAndVideo(test) {
|
|
177
|
+
let content = '<div class="card mb-5"><div class="card-content"><div class="columns is-multiline">';
|
|
178
|
+
if (test.screenshots && test.screenshots.length > 0) {
|
|
179
|
+
content += `
|
|
201
180
|
<div class="column is-half">
|
|
202
181
|
<div id="testImage" class="modal">
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
182
|
+
<div class="modal-background"></div>
|
|
183
|
+
<div class="modal-content">
|
|
184
|
+
<p class="image">
|
|
185
|
+
<img id="screenshot-modal-img" src="${test.screenshots[0]}" alt="Screenshot">
|
|
186
|
+
</p>
|
|
187
|
+
</div>
|
|
188
|
+
<button onclick="closeModal()" class="modal-close is-large" aria-label="close"></button>
|
|
208
189
|
</div>
|
|
209
|
-
<
|
|
190
|
+
<figure class="image">
|
|
191
|
+
<img id="screenshot-main-img" onclick="openModal()" src="${test.screenshots[0]}" alt="Screenshot">
|
|
192
|
+
</figure>
|
|
193
|
+
<nav class="mt-4 pagination is-small is-centered ${test.screenshots.length > 1 ? '' : 'is-hidden'}" role="navigation" aria-label="pagination">
|
|
194
|
+
<a class="pagination-previous">Previous</a>
|
|
195
|
+
<a class="pagination-next">Next</a>
|
|
196
|
+
<ul class="pagination-list">
|
|
197
|
+
${test.screenshots.map((_, index) => `
|
|
198
|
+
<li>
|
|
199
|
+
<a class="pagination-link ${index === 0 ? 'is-current' : ''}" aria-label="Goto screenshot ${index + 1}">${index + 1}</a>
|
|
200
|
+
</li>`).join('')}
|
|
201
|
+
</ul>
|
|
202
|
+
</nav>
|
|
210
203
|
</div>
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
<nav class="mt-4 pagination is-small is-centered ${test.screenshots.length > 1 ? '' : 'is-hidden'}" role="navigation" aria-label="pagination">
|
|
216
|
-
<a class="pagination-previous" >Previous</a>
|
|
217
|
-
<a class="pagination-next" >Next</a>
|
|
218
|
-
<ul class="pagination-list">
|
|
219
|
-
${test.screenshots.map((_, index) => `
|
|
220
|
-
<li>
|
|
221
|
-
<a class="pagination-link ${index === 0 ? 'is-current' : ''}" aria-label="Goto screenshot ${index + 1}" >${index + 1}</a>
|
|
222
|
-
</li>`).join('')}
|
|
223
|
-
</ul>
|
|
224
|
-
</nav>
|
|
225
|
-
</div>
|
|
226
|
-
` : ''}
|
|
227
|
-
${test.videoPath ? `
|
|
204
|
+
`;
|
|
205
|
+
}
|
|
206
|
+
if (test.videoPath) {
|
|
207
|
+
content += `
|
|
228
208
|
<div class="column is-half">
|
|
229
|
-
<div class="video-preview"
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
209
|
+
<div class="video-preview">
|
|
210
|
+
<video controls width="100%" height="auto" preload="metadata">
|
|
211
|
+
<source src="${test.videoPath}" type="video/webm">
|
|
212
|
+
Your browser does not support the video tag.
|
|
213
|
+
</video>
|
|
234
214
|
</div>
|
|
235
215
|
</div>
|
|
236
|
-
|
|
216
|
+
`;
|
|
217
|
+
}
|
|
218
|
+
content += '</div>';
|
|
219
|
+
content += `
|
|
220
|
+
<div class="columns">
|
|
221
|
+
<div class="column">
|
|
222
|
+
<button
|
|
223
|
+
onclick="openHistory()"
|
|
224
|
+
class="button is-primary is-fullwidth mt-3">
|
|
225
|
+
<span class="icon"><i class="fa-solid fa-timeline"></i></span>
|
|
226
|
+
<span class="has-text-white pl-2">Open history</span>
|
|
227
|
+
</button>
|
|
228
|
+
<div id="historyModal" class="modal">
|
|
229
|
+
<div class="modal-background"></div>
|
|
230
|
+
</div>
|
|
237
231
|
</div>
|
|
238
232
|
${test.tracePath ? `
|
|
239
|
-
<div class="
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
</button>
|
|
248
|
-
</div>
|
|
233
|
+
<div class="column">
|
|
234
|
+
<button
|
|
235
|
+
data-trace="${test.tracePath}"
|
|
236
|
+
onclick="openTraceViewer(this)"
|
|
237
|
+
class="button is-primary is-fullwidth mt-3">
|
|
238
|
+
<span class="icon"><i class="fa-solid fa-tv"></i></span>
|
|
239
|
+
<span class="has-text-white pl-2">View Trace</span>
|
|
240
|
+
</button>
|
|
249
241
|
</div>
|
|
250
242
|
` : ''}
|
|
251
243
|
</div>
|
|
252
|
-
|
|
253
|
-
|
|
244
|
+
`;
|
|
245
|
+
|
|
246
|
+
content += '</div></div>';
|
|
247
|
+
return content;
|
|
248
|
+
},
|
|
249
|
+
renderAdditionalInfo(test) {
|
|
250
|
+
if (!(test.annotations.length || test.testTags.length > 0)) return '';
|
|
251
|
+
return `
|
|
254
252
|
<div class="card mb-5">
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
253
|
+
<header class="card-header">
|
|
254
|
+
<p class="card-header-title">Additional Information</p>
|
|
255
|
+
</header>
|
|
256
|
+
<div class="card-content">
|
|
257
|
+
<div class="content">
|
|
258
|
+
${test.testTags.length > 0 ? `
|
|
259
|
+
<div class="control mb-4">
|
|
260
|
+
<div class="tags is-rounded">
|
|
261
|
+
${test.testTags.map(tag => `<span class="tag is-primary is-medium">${tag}</span>`).join('')}
|
|
262
|
+
</div>
|
|
263
|
+
</div>` : ""}
|
|
264
|
+
${test.annotations
|
|
265
|
+
.filter(annotation => annotation !== null && annotation !== undefined)
|
|
266
|
+
.map(annotation => `
|
|
267
|
+
<div class="mb-4">
|
|
268
|
+
${annotation?.type ? `<strong class="has-text-link">Type: </strong><span>${annotation.type}</span>` : ''}
|
|
269
|
+
<br>
|
|
270
|
+
${annotation?.description ? `<strong class="has-text-link">Description: </strong><span>${annotation.description}</span>` : ''}
|
|
271
|
+
</div>
|
|
272
|
+
`).join('')}
|
|
264
273
|
</div>
|
|
265
|
-
</div>` : ""}
|
|
266
|
-
${test.annotations
|
|
267
|
-
.filter(annotation => annotation !== null && annotation !== undefined)
|
|
268
|
-
.map(annotation => `
|
|
269
|
-
<div class="mb-4">
|
|
270
|
-
${annotation?.type ? `<strong class="has-text-link">Type: </strong><span>${annotation.type}</span>` : ''}
|
|
271
|
-
<br>
|
|
272
|
-
${annotation?.description ? `<strong class="has-text-link">Description: </strong><span>${annotation.description}</span>` : ''}
|
|
273
|
-
</div>
|
|
274
|
-
`).join('')}
|
|
275
274
|
</div>
|
|
276
275
|
</div>
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
276
|
+
`;
|
|
277
|
+
},
|
|
278
|
+
renderSteps(test) {
|
|
279
|
+
if (test.steps.length === 0) return '';
|
|
280
|
+
return `
|
|
280
281
|
<div class="card">
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
282
|
+
<header class="card-header">
|
|
283
|
+
<p class="card-header-title">Steps</p>
|
|
284
|
+
</header>
|
|
285
|
+
<div class="card-content">
|
|
286
|
+
<div class="content">
|
|
287
|
+
<span id="stepDetails" class="content"></span>
|
|
288
|
+
</div>
|
|
287
289
|
</div>
|
|
288
290
|
</div>
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
291
|
+
`;
|
|
292
|
+
},
|
|
293
|
+
renderErrors(test) {
|
|
294
|
+
if (!test.errors.length) return '';
|
|
295
|
+
return `
|
|
292
296
|
<div class="card mt-5">
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
<header class="card-header">
|
|
298
|
+
<p class="card-header-title">Errors</p>
|
|
299
|
+
</header>
|
|
300
|
+
<div class="card-content">
|
|
301
|
+
<div class="content">
|
|
302
|
+
<pre><code class="data-lang=js">${test.errors.join('\n')}</code></pre>
|
|
303
|
+
</div>
|
|
299
304
|
</div>
|
|
300
305
|
</div>
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
306
|
+
`;
|
|
307
|
+
},
|
|
308
|
+
renderLogs(test) {
|
|
309
|
+
if (!test.logs) return '';
|
|
310
|
+
return `
|
|
304
311
|
<div class="card mt-5">
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
</div>
|
|
313
|
-
</div>
|
|
314
|
-
` : ''}
|
|
315
|
-
${test.videoPath ? `
|
|
316
|
-
<div id="testVideo" class="modal">
|
|
317
|
-
<div class="modal-background"></div>
|
|
318
|
-
<div class="modal-content">
|
|
319
|
-
<div class="box">
|
|
320
|
-
<video controls style="width: 100%;">
|
|
321
|
-
<source src="${test.videoPath}" type="video/webm">
|
|
322
|
-
Your browser does not support the video tag.
|
|
323
|
-
</video>
|
|
324
|
-
</div>
|
|
312
|
+
<header class="card-header">
|
|
313
|
+
<p class="card-header-title">Logs</p>
|
|
314
|
+
</header>
|
|
315
|
+
<div class="card-content">
|
|
316
|
+
<div class="content">
|
|
317
|
+
<pre>${test.logs}</pre>
|
|
318
|
+
</div>
|
|
325
319
|
</div>
|
|
326
|
-
<button class="modal-close is-large" aria-label="close" onclick="closeVideo()"></button>
|
|
327
320
|
</div>
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
321
|
+
`;
|
|
322
|
+
},
|
|
323
|
+
attachScreenshotListeners(test) {
|
|
324
|
+
if (test.screenshots && test.screenshots.length > 0) {
|
|
325
|
+
let currentScreenshotIndex = 0;
|
|
326
|
+
const changeScreenshot = (direction) => {
|
|
327
|
+
currentScreenshotIndex = (currentScreenshotIndex + direction + test.screenshots.length) % test.screenshots.length;
|
|
328
|
+
this.updateScreenshot(test.screenshots, currentScreenshotIndex);
|
|
329
|
+
};
|
|
330
|
+
const gotoScreenshot = (index) => {
|
|
331
|
+
currentScreenshotIndex = index;
|
|
332
|
+
this.updateScreenshot(test.screenshots, currentScreenshotIndex);
|
|
333
|
+
};
|
|
334
|
+
document.querySelector('.pagination-previous').addEventListener('click', () => changeScreenshot(-1));
|
|
335
|
+
document.querySelector('.pagination-next').addEventListener('click', () => changeScreenshot(1));
|
|
336
|
+
document.querySelectorAll('.pagination-link').forEach((link, index) => {
|
|
337
|
+
link.addEventListener('click', () => gotoScreenshot(index));
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
updateScreenshot(screenshots, index) {
|
|
342
|
+
document.getElementById('screenshot-main-img').src = screenshots[index];
|
|
343
|
+
document.getElementById('screenshot-modal-img').src = screenshots[index];
|
|
344
|
+
document.querySelectorAll('.pagination-link').forEach((link, i) => {
|
|
345
|
+
link.classList.toggle('is-current', i === index);
|
|
335
346
|
});
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
pre.appendChild(code);
|
|
358
|
-
li.appendChild(pre);
|
|
347
|
+
},
|
|
348
|
+
attachSteps(test) {
|
|
349
|
+
const stepDetailsDiv = document.getElementById('stepDetails');
|
|
350
|
+
if (stepDetailsDiv) {
|
|
351
|
+
const stepsList = document.createElement("ul");
|
|
352
|
+
stepsList.setAttribute("id", "steps");
|
|
353
|
+
test.steps.forEach(step => {
|
|
354
|
+
const li = document.createElement('li');
|
|
355
|
+
li.innerHTML = `<strong class="${step.snippet ? 'has-text-danger' : ''}">${step.title}</strong>`;
|
|
356
|
+
if (step.snippet) {
|
|
357
|
+
const pre = document.createElement('pre');
|
|
358
|
+
const code = document.createElement('code');
|
|
359
|
+
const locationText = step.location ? `\n\nat: ${step.location}` : '';
|
|
360
|
+
code.innerHTML = `${step.snippet}${locationText}`;
|
|
361
|
+
code.setAttribute('data-lang', 'js');
|
|
362
|
+
pre.appendChild(code);
|
|
363
|
+
li.appendChild(pre);
|
|
364
|
+
}
|
|
365
|
+
stepsList.appendChild(li);
|
|
366
|
+
});
|
|
367
|
+
stepDetailsDiv.appendChild(stepsList);
|
|
359
368
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
function closeModal() {
|
|
377
|
-
let modal = document.querySelector("#testImage");
|
|
378
|
-
modal.classList.remove("is-active");
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
window.openModal = openModal;
|
|
382
|
-
window.openVideo = openVideo;
|
|
383
|
-
window.closeVideo = closeVideo;
|
|
384
|
-
window.closeModal = closeModal;
|
|
385
|
-
window.openTraceViewer = openTraceViewer;
|
|
386
|
-
|
|
387
|
-
document.addEventListener('keydown', (event) => {
|
|
388
|
-
if (event.key === "Escape") {
|
|
389
|
-
closeModal();
|
|
369
|
+
},
|
|
370
|
+
attachEventListeners() {
|
|
371
|
+
const testItems = document.querySelectorAll('[data-test-id]');
|
|
372
|
+
testItems.forEach(item => {
|
|
373
|
+
item.addEventListener('click', () => {
|
|
374
|
+
testItems.forEach(i => i.classList.remove('listselected'));
|
|
375
|
+
item.classList.add('listselected');
|
|
376
|
+
const testId = item.getAttribute('data-test-id');
|
|
377
|
+
const testHistoryId = item.getAttribute('data-test-history-id');
|
|
378
|
+
const test = testData[testId];
|
|
379
|
+
const historyEntry = testHistory.find(entry => entry.testId === testHistoryId);
|
|
380
|
+
testHistoriesMap = historyEntry ? historyEntry.history : null;
|
|
381
|
+
testHistoryTitle = historyEntry.testId ? historyEntry.testId.split(":")[2] : '';
|
|
382
|
+
this.show(test);
|
|
383
|
+
});
|
|
384
|
+
});
|
|
390
385
|
}
|
|
391
|
-
}
|
|
392
|
-
function openTraceViewer(button) {
|
|
393
|
-
const tracePath = button.getAttribute("data-trace");
|
|
394
|
-
try {
|
|
395
|
-
if (tracePath) {
|
|
396
|
-
const baseUrl = window.location.origin;
|
|
397
|
-
const traceViewerUrl = `${baseUrl}/trace/index.html?trace=${baseUrl}/${tracePath}`;
|
|
398
|
-
window.open(traceViewerUrl, "_blank");
|
|
399
|
-
}
|
|
400
|
-
} catch (error) { }
|
|
401
|
-
}
|
|
386
|
+
};
|
|
402
387
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
testItems.forEach(i => i.classList.remove('listselected'));
|
|
412
|
-
item.classList.add('listselected');
|
|
413
|
-
const testId = item.getAttribute('data-test-id');
|
|
414
|
-
const test = testData[testId];
|
|
415
|
-
displayTestDetails(test);
|
|
388
|
+
const filterManager = {
|
|
389
|
+
init() {
|
|
390
|
+
this.attachEventListeners();
|
|
391
|
+
},
|
|
392
|
+
attachEventListeners() {
|
|
393
|
+
const checkboxes = document.querySelectorAll('#select-filter input[type="checkbox"]');
|
|
394
|
+
checkboxes.forEach(checkbox => {
|
|
395
|
+
checkbox.addEventListener('change', () => this.applyFilters());
|
|
416
396
|
});
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
f.classList.remove('active');
|
|
425
|
-
}
|
|
397
|
+
|
|
398
|
+
const filters = document.querySelectorAll('.filter');
|
|
399
|
+
filters.forEach(filter => {
|
|
400
|
+
filter.addEventListener('click', () => {
|
|
401
|
+
filters.forEach(f => f.classList.remove('active'));
|
|
402
|
+
filter.classList.add('active');
|
|
403
|
+
this.applyFilters();
|
|
426
404
|
});
|
|
427
|
-
filter.classList.add('active');
|
|
428
|
-
applyFilters();
|
|
429
405
|
});
|
|
430
|
-
}
|
|
431
|
-
|
|
406
|
+
},
|
|
407
|
+
applyFilters() {
|
|
408
|
+
const selectedProjects = this.getSelectedValues('project');
|
|
409
|
+
const selectedTags = this.getSelectedValues('test-tags');
|
|
410
|
+
const selectedStatus = document.querySelector('.filter.active')?.getAttribute('data-status') || 'all';
|
|
432
411
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
});
|
|
446
|
-
const detailsElements = document.querySelectorAll('.sidebar details');
|
|
447
|
-
detailsElements.forEach(details => {
|
|
448
|
-
let shouldShowDetails = false;
|
|
449
|
-
const items = details.querySelectorAll('div[data-test-id]');
|
|
450
|
-
items.forEach(item => {
|
|
451
|
-
const testTags = item.getAttribute('data-test-tags').trim().split(' ').filter(Boolean);
|
|
452
|
-
const projectName = item.getAttribute('data-project-name').trim();
|
|
453
|
-
const testStatus = item.getAttribute('data-test-status').trim();
|
|
454
|
-
const matchesProject = selectedProjects.length === 0 || selectedProjects.includes(projectName);
|
|
455
|
-
const matchesTags = selectedTags.length === 0 || selectedTags.every(tag => testTags.includes(tag));
|
|
456
|
-
const matchesStatus = (selectedStatus === 'all' && testStatus !== 'skipped') ||
|
|
457
|
-
(selectedStatus !== 'all' && (
|
|
458
|
-
testStatus === selectedStatus ||
|
|
459
|
-
(selectedStatus === 'failed' && (testStatus === 'failed' || testStatus === 'timedOut')) ||
|
|
460
|
-
(selectedStatus === 'retry' && testStatus.includes('retry')) ||
|
|
461
|
-
(selectedStatus === 'flaky' && testStatus.includes('flaky'))
|
|
462
|
-
));
|
|
463
|
-
if (matchesProject && matchesTags && matchesStatus) {
|
|
464
|
-
item.classList.remove('is-hidden');
|
|
465
|
-
shouldShowDetails = true;
|
|
466
|
-
} else {
|
|
467
|
-
item.classList.add('is-hidden');
|
|
468
|
-
}
|
|
412
|
+
elements.detailsElements.forEach(details => {
|
|
413
|
+
const items = details.querySelectorAll('div[data-test-id]');
|
|
414
|
+
let shouldShowDetails = false;
|
|
415
|
+
|
|
416
|
+
items.forEach(item => {
|
|
417
|
+
const isVisible = this.shouldShowItem(item, selectedProjects, selectedTags, selectedStatus);
|
|
418
|
+
item.classList.toggle('is-hidden', !isVisible);
|
|
419
|
+
shouldShowDetails = shouldShowDetails || isVisible;
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
details.open = shouldShowDetails;
|
|
423
|
+
details.classList.toggle('is-hidden', !shouldShowDetails);
|
|
469
424
|
});
|
|
470
|
-
details.open = shouldShowDetails;
|
|
471
|
-
details.classList.toggle('is-hidden', !shouldShowDetails);
|
|
472
|
-
});
|
|
473
|
-
updateSelectedFiltersDisplay(selectedProjects, selectedTags, selectedStatus);
|
|
474
|
-
}
|
|
475
425
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
426
|
+
this.updateSelectedFiltersDisplay(selectedProjects, selectedTags, selectedStatus);
|
|
427
|
+
},
|
|
428
|
+
getSelectedValues(type) {
|
|
429
|
+
return Array.from(document.querySelectorAll(`#select-filter input[type="checkbox"][data-filter-type="${type}"]:checked`))
|
|
430
|
+
.map(checkbox => checkbox.value.trim());
|
|
431
|
+
},
|
|
432
|
+
shouldShowItem(item, projects, tags, status) {
|
|
433
|
+
const testTags = item.getAttribute('data-test-tags').trim().split(' ').filter(Boolean);
|
|
434
|
+
const projectName = item.getAttribute('data-project-name').trim();
|
|
435
|
+
const testStatus = item.getAttribute('data-test-status').trim();
|
|
479
436
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
if (tags.length > 0) {
|
|
484
|
-
filtersDisplay.innerHTML += `<span> Tags: ${tags.join(', ')}</span>`;
|
|
485
|
-
}
|
|
486
|
-
if (status !== 'all') {
|
|
487
|
-
filtersDisplay.innerHTML += `<span> Status: ${status}</span>`;
|
|
488
|
-
}
|
|
437
|
+
const matchesProject = projects.length === 0 || projects.includes(projectName);
|
|
438
|
+
const matchesTags = tags.length === 0 || tags.every(tag => testTags.includes(tag));
|
|
439
|
+
const matchesStatus = this.matchesStatus(testStatus, status);
|
|
489
440
|
|
|
490
|
-
|
|
491
|
-
|
|
441
|
+
return matchesProject && matchesTags && matchesStatus;
|
|
442
|
+
},
|
|
443
|
+
matchesStatus(testStatus, selectedStatus) {
|
|
444
|
+
if (selectedStatus === 'all') return testStatus !== 'skipped';
|
|
445
|
+
if (selectedStatus === 'failed') return testStatus === 'failed' || testStatus === 'timedOut';
|
|
446
|
+
if (selectedStatus === 'retry') return testStatus.includes('retry');
|
|
447
|
+
if (selectedStatus === 'flaky') return testStatus.includes('flaky');
|
|
448
|
+
return testStatus === selectedStatus;
|
|
449
|
+
},
|
|
450
|
+
updateSelectedFiltersDisplay(projects, tags, status) {
|
|
451
|
+
let displayText = [];
|
|
452
|
+
if (projects.length > 0) displayText.push(`Projects: ${projects.join(', ')}`);
|
|
453
|
+
if (tags.length > 0) displayText.push(`Tags: ${tags.join(', ')}`);
|
|
454
|
+
if (status !== 'all') displayText.push(`Status: ${status}`);
|
|
455
|
+
elements.filtersDisplay.innerHTML = displayText.length > 0 ? displayText.join(' | ') : 'All Tests';
|
|
492
456
|
}
|
|
493
|
-
}
|
|
494
|
-
const searchInput = document.querySelector('input[name="search"]');
|
|
495
|
-
const detailsElements = document.querySelectorAll('details');
|
|
496
|
-
function filterTests(search) {
|
|
497
|
-
const searchTerm = search.toLowerCase();
|
|
498
|
-
const testItems = document.querySelectorAll('[data-test-id]');
|
|
457
|
+
};
|
|
499
458
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
459
|
+
const searchManager = {
|
|
460
|
+
init() {
|
|
461
|
+
elements.searchInput.addEventListener('input', this.debounce(this.filterTests, 300));
|
|
462
|
+
},
|
|
463
|
+
filterTests(event) {
|
|
464
|
+
const searchTerm = event.target.value.toLowerCase();
|
|
465
|
+
const testItems = document.querySelectorAll('[data-test-id]');
|
|
504
466
|
|
|
505
|
-
|
|
506
|
-
const testTitle = item.textContent.toLowerCase();
|
|
507
|
-
if (testTitle.includes(searchTerm)) {
|
|
508
|
-
item.style.display = 'flex'; // Show matching test item
|
|
467
|
+
elements.detailsElements.forEach(detail => detail.open = !!searchTerm);
|
|
509
468
|
|
|
510
|
-
let parent = item.parentElement;
|
|
511
|
-
while (parent && parent.tagName !== 'ASIDE') {
|
|
512
|
-
if (parent.tagName === 'DETAILS') {
|
|
513
|
-
parent.open = true;
|
|
514
|
-
}
|
|
515
|
-
parent = parent.parentElement;
|
|
516
|
-
}
|
|
517
|
-
} else {
|
|
518
|
-
item.style.display = 'none';
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
} else {
|
|
522
469
|
testItems.forEach(item => {
|
|
523
|
-
item.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
detail.open = false;
|
|
470
|
+
const isVisible = item.textContent.toLowerCase().includes(searchTerm);
|
|
471
|
+
item.style.display = isVisible ? 'flex' : 'none';
|
|
472
|
+
if (isVisible && searchTerm) searchManager.openParentDetails(item);
|
|
527
473
|
});
|
|
474
|
+
},
|
|
475
|
+
openParentDetails(item) {
|
|
476
|
+
let parent = item.parentElement;
|
|
477
|
+
while (parent && parent.tagName !== 'ASIDE') {
|
|
478
|
+
if (parent.tagName === 'DETAILS') parent.open = true;
|
|
479
|
+
parent = parent.parentElement;
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
debounce(func, wait) {
|
|
483
|
+
let timeout;
|
|
484
|
+
return function(...args) {
|
|
485
|
+
clearTimeout(timeout);
|
|
486
|
+
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
487
|
+
};
|
|
528
488
|
}
|
|
529
|
-
}
|
|
530
|
-
function debounce(func, wait) {
|
|
531
|
-
let timeout;
|
|
532
|
-
return function (...args) {
|
|
533
|
-
clearTimeout(timeout);
|
|
534
|
-
timeout = setTimeout(() => func.apply(this, args), wait);
|
|
535
|
-
};
|
|
536
|
-
}
|
|
489
|
+
};
|
|
537
490
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
491
|
+
// Initialize all managers
|
|
492
|
+
themeManager.init();
|
|
493
|
+
testDetailsManager.attachEventListeners();
|
|
494
|
+
filterManager.init();
|
|
495
|
+
searchManager.init();
|
|
541
496
|
|
|
542
|
-
|
|
497
|
+
// Expose necessary functions to the global scope
|
|
498
|
+
window.showSummary = testDetailsManager.hide;
|
|
499
|
+
window.openModal = () => document.querySelector("#testImage").classList.add("is-active");
|
|
500
|
+
window.closeModal = () => document.querySelector("#testImage").classList.remove("is-active");
|
|
501
|
+
window.closeErrorModal = (modalId) => document.getElementById(modalId).classList.remove("is-active");
|
|
502
|
+
window.openTraceViewer = (button) => {
|
|
503
|
+
const tracePath = button.getAttribute("data-trace");
|
|
504
|
+
if (tracePath) {
|
|
505
|
+
const normalizedTracePath = tracePath.replace(/\\/g, '/');
|
|
506
|
+
const baseUrl = getAdjustedBaseUrl();
|
|
507
|
+
window.open(`${baseUrl}/trace/index.html?trace=${baseUrl}/${normalizedTracePath}`, "_blank");
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
window.getAdjustedBaseUrl = () => {
|
|
511
|
+
const origin = window.location.origin;
|
|
512
|
+
const pathname = window.location.pathname;
|
|
513
|
+
if (pathname.endsWith('.html')) {
|
|
514
|
+
const directoryPath = pathname.substring(0, pathname.lastIndexOf('/') + 1);
|
|
515
|
+
return `${origin}${directoryPath}`;
|
|
516
|
+
}
|
|
517
|
+
return origin;
|
|
518
|
+
}
|
|
519
|
+
window.openHistory = () => {
|
|
520
|
+
const historyElement = document.getElementById("historyModal");
|
|
521
|
+
historyElement.classList.add('is-active');
|
|
522
|
+
|
|
523
|
+
let historyContent = '';
|
|
524
|
+
if (testHistoriesMap && testHistoriesMap.length > 0) {
|
|
525
|
+
historyContent = testHistoriesMap.map((h, index) => `
|
|
526
|
+
<tr>
|
|
527
|
+
<td>${h.run_date}</td>
|
|
528
|
+
<td>${h.status}</td>
|
|
529
|
+
<td>${h.duration}</td>
|
|
530
|
+
${h.error_message ? `<td><div class="modal" id="${index}">
|
|
531
|
+
<div class="modal-background"></div>
|
|
532
|
+
<div class="modal-content">
|
|
533
|
+
<pre><code>${h.error_message}</code></pre>
|
|
534
|
+
</div>
|
|
535
|
+
<button class="button is-primary" onclick="closeErrorModal(${index})">Close</button>
|
|
536
|
+
</div><a class="button is-link" onclick="showHistoryErrorMessage(${index})">Show error</a></td>` : '<td>No Error</td>'}
|
|
537
|
+
</tr>
|
|
538
|
+
`).join('');
|
|
539
|
+
} else {
|
|
540
|
+
historyContent = '<p class="title">No history available</p>';
|
|
541
|
+
}
|
|
543
542
|
|
|
544
|
-
|
|
543
|
+
historyElement.innerHTML = `
|
|
544
|
+
<div class="modal-background"></div>
|
|
545
|
+
<div class="modal-card">
|
|
546
|
+
<header class="modal-card-head">
|
|
547
|
+
<p class="modal-card-title">${testHistoryTitle}</p>
|
|
548
|
+
<button class="button is-primary" onclick="closeHistoryModal()">Close</button>
|
|
549
|
+
</header>
|
|
550
|
+
<section class="modal-card-body">
|
|
551
|
+
<table class="table is-hoverable is-fullwidth">
|
|
552
|
+
<thead>
|
|
553
|
+
<tr>
|
|
554
|
+
<th title="Run Date">Run Date</th>
|
|
555
|
+
<th title="Status">Status</th>
|
|
556
|
+
<th title="Duration">Duration</th>
|
|
557
|
+
<th title="Reason">Reason</th>
|
|
558
|
+
</tr>
|
|
559
|
+
</thead>
|
|
560
|
+
<tbody>
|
|
561
|
+
${historyContent}
|
|
562
|
+
</tbody>
|
|
563
|
+
</table>
|
|
564
|
+
</section>
|
|
565
|
+
</div>
|
|
566
|
+
`;
|
|
567
|
+
};
|
|
568
|
+
window.closeHistoryModal = () => {
|
|
569
|
+
document.getElementById("historyModal").classList.remove('is-active');
|
|
570
|
+
};
|
|
571
|
+
window.showHistoryErrorMessage = (modalId) => {
|
|
572
|
+
document.getElementById(modalId)?.classList.add('is-active');
|
|
573
|
+
};
|
|
545
574
|
});
|
|
546
575
|
</script>
|
|
547
576
|
</body>
|
|
548
|
-
|
|
549
577
|
</html>
|