ortoni-report 2.0.4 → 2.0.6

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.
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.startReportServer = void 0;
7
+ const express_1 = __importDefault(require("express"));
8
+ /**
9
+ * Starts an Express server to serve the HTML report and keeps it running.
10
+ * @param {string} reportPath - Path to the folder where the report is stored.
11
+ * @param {string} reportFilename - Name of the HTML report file to serve.
12
+ * @param {number} port - Port number to serve the report on (default is 8080).
13
+ */
14
+ function startReportServer(reportPath, reportFilename, port = 8080) {
15
+ const app = (0, express_1.default)();
16
+ // Serve static files from the report directory
17
+ app.use(express_1.default.static(reportPath));
18
+ // Start the server and keep it running
19
+ const server = app.listen(port, () => {
20
+ const reportUrl = `http://localhost:${port}/${reportFilename}`;
21
+ console.log(`Report is available at ${reportUrl}`);
22
+ });
23
+ // Ensure that the process doesn't exit prematurely
24
+ process.on('SIGINT', () => {
25
+ console.log('Shutting down the server...');
26
+ server.close(() => {
27
+ console.log('Server closed');
28
+ process.exit(0);
29
+ });
30
+ });
31
+ return server;
32
+ }
33
+ exports.startReportServer = startReportServer;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ensureHtmlExtension = exports.safeStringify = exports.formatDate = exports.normalizeFilePath = exports.msToTime = void 0;
6
+ exports.escapeHtml = exports.ensureHtmlExtension = exports.safeStringify = exports.formatDate = exports.normalizeFilePath = exports.msToTime = void 0;
7
7
  const path_1 = __importDefault(require("path"));
8
8
  function msToTime(duration) {
9
9
  const milliseconds = Math.floor(duration % 1000);
@@ -67,3 +67,19 @@ function ensureHtmlExtension(filename) {
67
67
  return `${filename}.html`;
68
68
  }
69
69
  exports.ensureHtmlExtension = ensureHtmlExtension;
70
+ function escapeHtml(unsafe) {
71
+ if (typeof unsafe !== 'string') {
72
+ return String(unsafe);
73
+ }
74
+ return unsafe.replace(/[&<"']/g, function (match) {
75
+ const escapeMap = {
76
+ '&': '&amp;',
77
+ '<': '&lt;',
78
+ '>': '&gt;',
79
+ '"': '&quot;',
80
+ "'": '&#039;'
81
+ };
82
+ return escapeMap[match] || match;
83
+ });
84
+ }
85
+ exports.escapeHtml = escapeHtml;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const http_1 = __importDefault(require("http"));
30
+ const ws_1 = __importStar(require("ws"));
31
+ const utils_1 = require("./utils");
32
+ class WebSocketHelper {
33
+ constructor(port) {
34
+ this.port = port;
35
+ this.wss = null;
36
+ }
37
+ setupWebSocket() {
38
+ const server = http_1.default.createServer();
39
+ this.wss = new ws_1.WebSocketServer({ server });
40
+ this.wss.on('connection', (ws) => {
41
+ ws.send((0, utils_1.safeStringify)({ type: 'initial', data: 'Connected to WS service' }));
42
+ });
43
+ server.listen(this.port, () => {
44
+ console.log(`WebSocket server is running on http://localhost:${this.port}`);
45
+ });
46
+ }
47
+ setupCleanup() {
48
+ const gracefulShutdown = () => {
49
+ console.log('Shutting down WebSocket server...');
50
+ this.closeWebSocket();
51
+ process.exit();
52
+ };
53
+ process.on('exit', gracefulShutdown);
54
+ process.on('SIGINT', () => {
55
+ console.log('Received SIGINT. Closing WebSocket server...');
56
+ gracefulShutdown();
57
+ });
58
+ }
59
+ broadcastUpdate(tests) {
60
+ if (this.wss) {
61
+ this.wss.clients.forEach((client) => {
62
+ if (client.readyState === ws_1.default.OPEN) {
63
+ client.send((0, utils_1.safeStringify)({ type: 'update', data: tests }));
64
+ }
65
+ });
66
+ }
67
+ }
68
+ closeWebSocket() {
69
+ if (this.wss) {
70
+ this.wss.clients.forEach((client) => {
71
+ if (client.readyState === ws_1.default.OPEN) {
72
+ client.close(1000, 'Test run completed');
73
+ }
74
+ });
75
+ this.wss.close(() => {
76
+ console.log('WebSocket server closed');
77
+ });
78
+ }
79
+ }
80
+ testComplete() {
81
+ if (this.wss) {
82
+ this.wss.clients.forEach((client) => {
83
+ if (client.readyState === ws_1.default.OPEN) {
84
+ client.send((0, utils_1.safeStringify)({ type: 'complete', data: 'Test run completed' }));
85
+ }
86
+ });
87
+ }
88
+ setTimeout(() => {
89
+ this.closeWebSocket();
90
+ }, 1000);
91
+ }
92
+ }
93
+ exports.default = WebSocketHelper;
@@ -1,10 +1,9 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en" data-theme="{{preferredTheme}}">
3
-
4
3
  <head>
5
4
  <meta charset="UTF-8">
6
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <meta name="description" content="Playwright HTML report by LetCode Koushik - V2.0.4">
6
+ <meta name="description" content="Playwright HTML report by LetCode Koushik - V2.0.6">
8
7
  <title>{{title}}</title>
9
8
  <link rel="icon" href="https://raw.githubusercontent.com/ortoniKC/ortoni-report/refs/heads/main/favicon.png"
10
9
  type="image/x-icon">
@@ -15,10 +14,9 @@
15
14
  <style>
16
15
  {{{inlineCss}}}
17
16
  </style>
18
-
19
17
  <body>
20
18
  {{> navbar }}
21
- <section class="section">
19
+ <section class="section mt-6">
22
20
  <main class="container">
23
21
  <div class="columns">
24
22
  <aside class="column is-two-fifths">
@@ -26,7 +24,6 @@
26
24
  {{> project}}
27
25
  </aside>
28
26
  <section class="column is-three-fifths">
29
- {{!-- Overall summary --}}
30
27
  <div id="summary">
31
28
  <div class="columns is-multiline has-text-centered">
32
29
  {{> summaryCard bg="primary" status="all" statusHeader="All Tests" statusCount=totalCount}}
@@ -40,13 +37,7 @@
40
37
  {{> userInfo}}
41
38
  </div>
42
39
  </div>
43
- {{!-- Test details --}}
44
- <div id="testDetails" style="display: none;">
45
- <!-- Back button should be outside the dynamic content -->
46
- <button class="button content" id="back-to-summary" onclick="showSummary()">Back to
47
- Summary</button>
48
- <!-- Test Details will be displayed here -->
49
- </div>
40
+ <div id="testDetails" style="display: none;"></div>
50
41
  </section>
51
42
  </div>
52
43
  </main>
@@ -95,11 +86,18 @@
95
86
  function showSummary() {
96
87
  summary.style.display = 'block';
97
88
  testDetails.style.display = 'none';
98
- backButton.style.display = 'none';
99
89
  }
100
90
  window.showSummary = showSummary;
101
91
 
102
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);
103
101
  let currentScreenshotIndex = 0;
104
102
  function changeScreenshot(direction) {
105
103
  const screenshots = test.screenshots;
@@ -125,85 +123,83 @@
125
123
  }
126
124
  });
127
125
  }
128
- const summary = document.getElementById('summary');
129
- const testDetails = document.getElementById('testDetails');
130
- const backButton = document.querySelector('button#back-to-summary');
131
- summary.style.display = 'none';
132
- testDetails.style.opacity = '0';
133
- testDetails.style.display = 'block';
134
- setTimeout(() => {
135
- testDetails.style.opacity = '1';
136
- backButton.style.opacity = '1';
137
- }, 50);
138
-
139
126
  let statusClass = '';
127
+ let statusIcon = '';
140
128
  let statusText = test.status.toUpperCase();
129
+
141
130
  if (test.status.startsWith('passed')) {
142
- statusClass = 'tag is-success';
131
+ statusClass = 'success';
132
+ statusIcon = 'check-circle';
143
133
  } else if (test.status === 'flaky') {
144
- statusClass = 'tag is-warning';
134
+ statusClass = 'warning';
135
+ statusIcon = 'exclamation-triangle';
145
136
  } else if (test.status === 'failed') {
146
- statusClass = 'tag is-danger';
137
+ statusClass = 'danger';
138
+ statusIcon = 'times-circle';
147
139
  } else {
148
- statusClass = 'tag is-info';
140
+ statusClass = 'info';
141
+ statusIcon = 'question-circle';
149
142
  }
150
-
151
143
  testDetails.innerHTML = `
152
- <button class="button is-info is-light mb-4" id="back-to-summary" style="display: block"
153
- onclick="showSummary()">
154
- <span class="icon"><i class="fa-solid fa-chevron-left" style="color: #63E6BE;"></i></span>
155
- <span>Back to Summary</span></button>
156
- <div class="content has-text-centered">
157
- <p class="title">${test.title}</p>
158
- <p class="subtitle" id="filepath">${test.location}</p>
159
- </div>
160
- <div class="columns">
161
- <div class="column content">
162
- <h4 class="title is-4">Status</h4>
163
- <p class="${statusClass}">${statusText}</p>
164
- ${test.duration.length > 0 ? `
165
- <h4 class="title is-4">Duration</h4>
166
- <p class="${statusClass}">${test.duration}</p>` : ""}
167
- ${test.projectName.length > 0 ? `
168
- <div class="control">
169
- <div class="tags has-addons">
170
- <span class="tag is-dark">Project</span>
171
- <span class="tag is-info is-capitalized">${test.projectName}</span>
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>
172
165
  </div>
173
- </div>`: ""}
174
- ${test.testTags.length > 0 ? `
175
- <div class="control mt-4">
176
- <div class="tags has-addons">
177
- <span class="tag is-dark">Test Tags</span>
178
- <span class="tag is-info">${test.testTags.join(" ")}</span>
179
166
  </div>
180
- </div>`: ""}
181
- ${test.videoPath ? `
182
- <div id="testVideo" class="modal">
183
- <div class="modal-background"></div>
184
- <div class="modal-content">
185
- <figure>
186
- <video controls>
187
- <source src="file://${test.videoPath}" type="video/webm">
188
- Your browser does not support the video tag.
189
- </video>
190
- </figure>
191
- </div>
192
- <button onclick="closeVideo()" class="modal-close is-large" aria-label="close"></button>
193
167
  </div>
194
- <button class="button mt-4" onclick="openVideo()">Attachment: Video</button>
195
- `: ''}
196
- ${test.tracePath ? `
197
- <div class="trace-viewer mt-4">
198
- <a href="${test.tracePath}" target="_blank" class="button is-small is-link">
199
- View Trace
200
- </a>
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>
177
+ </div>
178
+ </div>
201
179
  </div>
202
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>
203
192
  </div>
204
- <div class="column content">
205
- ${test.screenshots && test.screenshots.length > 0 ? `
206
- <div id="testImage" class="modal">
193
+ </div>
194
+
195
+ <div class="content-wrapper">
196
+ ${test.status != "skipped" ?
197
+ `<div class="card mb-5">
198
+ <div class="card-content">
199
+ <div class="columns is-multiline">
200
+ ${test.screenshots && test.screenshots.length > 0 ? `
201
+ <div class="column is-half">
202
+ <div id="testImage" class="modal">
207
203
  <div class="modal-background"></div>
208
204
  <div class="modal-content">
209
205
  <p class="image">
@@ -213,10 +209,10 @@
213
209
  <button onclick="closeModal()" class="modal-close is-large" aria-label="close"></button>
214
210
  </div>
215
211
 
216
- <figure class="image box">
212
+ <figure class="image">
217
213
  <img id="screenshot-main-img" onclick="openModal()" src="${test.screenshots[0]}" alt="Screenshot">
218
214
  </figure>
219
- <nav class="pagination is-small is-centered ${test.screenshots.length > 1 ? '' : 'is-hidden'}" role="navigation" aria-label="pagination">
215
+ <nav class="mt-4 pagination is-small is-centered ${test.screenshots.length > 1 ? '' : 'is-hidden'}" role="navigation" aria-label="pagination">
220
216
  <a class="pagination-previous" >Previous</a>
221
217
  <a class="pagination-next" >Next</a>
222
218
  <ul class="pagination-list">
@@ -226,55 +222,118 @@
226
222
  </li>`).join('')}
227
223
  </ul>
228
224
  </nav>
229
- ` : ''}
225
+ </div>
226
+ ` : ''}
227
+ ${test.videoPath ? `
228
+ <div class="column is-half">
229
+ <div class="video-preview" onclick="openVideo()">
230
+ <video controls width="100%" height="auto" preload="metadata">
231
+ <source src="${test.videoPath}" type="video/webm">
232
+ Your browser does not support the video tag.
233
+ </video>
234
+ </div>
235
+ </div>
236
+ ` : ''}
237
+ </div>
238
+ ${test.tracePath ? `
239
+ <div class="columns is-centered">
240
+ <div class="column is-3">
241
+ <button
242
+ data-trace="${test.tracePath}"
243
+ onclick="openTraceViewer(this)"
244
+ class="button is-link is-fullwidth mt-3">
245
+ <span class="icon"><i class="fa-solid fa-tv" style="color: #FFD43B;"></i></span>
246
+ <span class="has-text-white pl-2">View Trace</span>
247
+ </button>
248
+ </div>
249
+ </div>
250
+ ` : ''}
230
251
  </div>
231
- </div>
232
- ${test.annotations && test.annotations.length ? `
233
- <article class="message">
234
- <div class="message-body">
235
- ${test.annotations
252
+ </div>` : ''}
253
+ ${test.annotations.length || test.testTags.length > 0 ? `
254
+ <div class="card mb-5">
255
+ <header class="card-header">
256
+ <p class="card-header-title">Additional Information</p>
257
+ </header>
258
+ <div class="card-content">
259
+ <div class="content">
260
+ ${test.testTags.length > 0 ? `
261
+ <div class="control mb-4">
262
+ <div class="tags is-rounded">
263
+ ${test.testTags.map(tag => `<span class="tag is-primary is-medium">${tag}</span>`).join('')}
264
+ </div>
265
+ </div>` : ""}
266
+ ${test.annotations
236
267
  .filter(annotation => annotation !== null && annotation !== undefined)
237
268
  .map(annotation => `
238
- <div>
239
- ${annotation?.type ? `<strong class="has-text-link">Type: </strong><span>${annotation?.type}</span>` : ''}
240
- <br>
241
- ${annotation?.description ? `<strong class="has-text-link">Description: </strong><span>${annotation?.description}</span>` : ''}
242
- </div>
269
+ <div class="mb-4">
270
+ ${annotation?.type ? `<strong class="has-text-link">Type: </strong><span>${annotation.type}</span>` : ''}
243
271
  <br>
272
+ ${annotation?.description ? `<strong class="has-text-link">Description: </strong><span>${annotation.description}</span>` : ''}
273
+ </div>
244
274
  `).join('')}
245
275
  </div>
246
- </article>
247
- ` : ``}
248
- <div class="content">
276
+ </div>
277
+ </div>
278
+ ` : ''}
249
279
  ${test.steps.length > 0 ? `
250
- <details id="stepopen" open>
251
- <summary><h4 class="title is-4">Steps</h4></summary>
252
- <span id="stepDetails" class="content"></span>
253
- </details>
254
- `: ``}
255
- </div class="content">
256
- <div>
280
+ <div class="card">
281
+ <header class="card-header">
282
+ <p class="card-header-title">Steps</p>
283
+ </header>
284
+ <div class="card-content">
285
+ <div class="content">
286
+ <span id="stepDetails" class="content"></span>
287
+ </div>
288
+ </div>
289
+ </div>
290
+ ` : ''}
257
291
  ${test.errors.length ? `
258
- <h4 class="title is-4">Errors</h4>
259
- <div class="content">
292
+ <div class="card mt-5">
293
+ <header class="card-header">
294
+ <p class="card-header-title">Errors</p>
295
+ </header>
296
+ <div class="card-content">
297
+ <div class="content">
260
298
  <pre><code class="data-lang=js">${test.errors.join('\n')}</code></pre>
261
- </div>` : ''}
262
- </div>
263
- <div>
299
+ </div>
300
+ </div>
301
+ </div>
302
+ ` : ''}
264
303
  ${test.logs ? `
265
- <h4 class="title is-4">Logs</h4>
266
- <div class="box">
304
+ <div class="card mt-5">
305
+ <header class="card-header">
306
+ <p class="card-header-title">Logs</p>
307
+ </header>
308
+ <div class="card-content">
309
+ <div class="content">
267
310
  <pre>${test.logs}</pre>
268
- </div>` : ''}
269
- </div>
270
- `;
271
- if(test.screenshots.length > 0){
311
+ </div>
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>
325
+ </div>
326
+ <button class="modal-close is-large" aria-label="close" onclick="closeVideo()"></button>
327
+ </div>
328
+ ` : ''}
329
+ </div>`
330
+ if (test.screenshots.length > 0) {
272
331
  document.querySelector('.pagination-previous').addEventListener('click', () => changeScreenshot(-1));
273
332
  document.querySelector('.pagination-next').addEventListener('click', () => changeScreenshot(1));
274
333
  document.querySelectorAll('.pagination-link').forEach((link, index) => {
275
334
  link.addEventListener('click', () => gotoScreenshot(index));
276
335
  });
277
- }
336
+ }
278
337
  const stepDetailsDiv = document.getElementById('stepDetails');
279
338
  if (stepDetailsDiv) {
280
339
  const stepsList = attachSteps(test);
@@ -282,7 +341,6 @@
282
341
  stepDetailsDiv.appendChild(stepsList);
283
342
  }
284
343
  }
285
-
286
344
  function attachSteps(test) {
287
345
  const stepsList = document.createElement("ul");
288
346
  stepsList.setAttribute("id", "steps");
@@ -324,12 +382,23 @@
324
382
  window.openVideo = openVideo;
325
383
  window.closeVideo = closeVideo;
326
384
  window.closeModal = closeModal;
385
+ window.openTraceViewer = openTraceViewer;
327
386
 
328
387
  document.addEventListener('keydown', (event) => {
329
388
  if (event.key === "Escape") {
330
389
  closeModal();
331
390
  }
332
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
+ }
333
402
 
334
403
  function attachEventListeners() {
335
404
  const checkboxes = document.querySelectorAll('#select-filter input[type="checkbox"]');
@@ -377,7 +446,7 @@
377
446
  const detailsElements = document.querySelectorAll('.sidebar details');
378
447
  detailsElements.forEach(details => {
379
448
  let shouldShowDetails = false;
380
- const items = details.querySelectorAll('li[data-test-id]');
449
+ const items = details.querySelectorAll('div[data-test-id]');
381
450
  items.forEach(item => {
382
451
  const testTags = item.getAttribute('data-test-tags').trim().split(' ').filter(Boolean);
383
452
  const projectName = item.getAttribute('data-project-name').trim();
@@ -436,7 +505,7 @@
436
505
  testItems.forEach(item => {
437
506
  const testTitle = item.textContent.toLowerCase();
438
507
  if (testTitle.includes(searchTerm)) {
439
- item.style.display = 'block'; // Show matching test item
508
+ item.style.display = 'flex'; // Show matching test item
440
509
 
441
510
  let parent = item.parentElement;
442
511
  while (parent && parent.tagName !== 'ASIDE') {
@@ -451,7 +520,7 @@
451
520
  });
452
521
  } else {
453
522
  testItems.forEach(item => {
454
- item.style.display = 'block';
523
+ item.style.display = 'flex';
455
524
  });
456
525
  detailsElements.forEach(detail => {
457
526
  detail.open = false;
@@ -1,4 +1,4 @@
1
- <nav class="navbar is-primary" role="navigation" aria-label="main navigation">
1
+ <nav class="navbar is-primary is-fixed-top" role="navigation" aria-label="main navigation">
2
2
  <div class="navbar-brand">
3
3
  <a class="navbar-item">
4
4
  {{#if logo}}
@@ -16,16 +16,6 @@
16
16
  </div>
17
17
  <div class="navbar-menu">
18
18
  <div class="navbar-end">
19
- <div class="navbar-item">
20
- <span id="selected-filters"></span>
21
- </div>
22
- <div class="navbar-item">
23
- <div class="field">
24
- <p class="control">
25
- <input class="input" name="search" type="search" placeholder="Search by test">
26
- </p>
27
- </div>
28
- </div>
29
19
  <div class="navbar-item">
30
20
  <button id="toggle-theme" class="" data-theme-status="{{preferredTheme}}">
31
21
  <span class="icon">