ortoni-report 1.1.5 → 1.1.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.
@@ -4,8 +4,8 @@
4
4
  <head>
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <meta name="description" content="Plawright HTML report by LetCode Koushik - V1.1.5">
8
- <title>Playwright Test Report</title>
7
+ <meta name="description" content="Playwright HTML report by LetCode Koushik - V1.1.7">
8
+ <title>Ortoni Playwright Test Report</title>
9
9
  <link rel="icon" href="node_modules/ortoni-report/dist/icon/32.png" type="image/x-icon">
10
10
  <link rel="stylesheet" href="node_modules/ortoni-report/dist/css/main.css">
11
11
  </head>
@@ -13,33 +13,28 @@
13
13
  #summary, #testDetails, button#back-to-summary {
14
14
  transition: opacity 0.2s ease-in-out;
15
15
  }
16
- .filter.active {
16
+
17
+ .filter.active {
17
18
  background-color: var(--bulma-background-active);
18
19
  }
19
-
20
20
  ::-webkit-scrollbar {
21
- width: 0px;
22
- background-color: transparent;
21
+ width: 8px;
22
+ height: 8px;
23
23
  }
24
24
 
25
- ::-webkit-scrollbar-thumb {
26
- background-color: var(--pico-secondary-background);
27
- ;
28
- border-radius: 0px;
25
+ ::-webkit-scrollbar-track {
26
+ background: #f1f1f1;
29
27
  }
30
28
 
31
- ::-webkit-scrollbar-thumb:hover {
32
- background-color: var(--pico-secondary-background);
33
- ;
29
+ ::-webkit-scrollbar-thumb {
30
+ background: #888;
31
+ border-radius: 4px;
34
32
  }
35
33
 
36
- ::-webkit-scrollbar-track {
37
- background-color: #f1f1f1;
34
+ ::-webkit-scrollbar-thumb:hover {
35
+ background: #555;
38
36
  }
39
37
 
40
- ::-webkit-scrollbar-corner {
41
- background-color: #fff;
42
- }
43
38
  div#testDetails {
44
39
  position: sticky;
45
40
  top: 0;
@@ -74,17 +69,27 @@
74
69
  details[open] > summary::after {
75
70
  transform: rotate(0deg);
76
71
  }
72
+ .logoimage{
73
+ max-width: 100px;
74
+ }
77
75
 
78
76
  </style>
79
77
 
80
78
  <body>
81
- <!-- Header -->
82
79
  <section class="section">
83
80
  <header class="container">
84
81
  <div class="columns is-vcentered">
85
- <div class="column is-one-third">
86
- {{!-- Custom Project Name --}}
87
- {{#if projectName}}<h1 class="title">{{projectName}}</h1>{{/if}}
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>
88
93
  </div>
89
94
  <div class="column">
90
95
  <div class="control">
@@ -99,13 +104,11 @@
99
104
  </div>
100
105
  </header>
101
106
  </section>
102
-
103
- <!-- Main Content -->
104
107
  <section class="section">
105
108
  <main class="container">
106
109
  <div class="columns">
107
- <aside class="column is-one-third sidebar">
108
- <div class="columns">
110
+ <aside class="column is-two-fifths">
111
+ <div class="columns is-mobile">
109
112
  <div class="column">
110
113
  <h2 class="title is-4">Tests</h2>
111
114
  </div>
@@ -120,7 +123,7 @@
120
123
  </div>
121
124
  </div>
122
125
  </div>
123
- <div class="content">
126
+ <div class="content sidebar">
124
127
  {{#each groupedResults}}
125
128
  <details class="box">
126
129
  <summary class="is-size-5 has-icon-right">
@@ -133,8 +136,8 @@
133
136
  </summary>
134
137
  <ul>
135
138
  {{#each this}}
136
- <details>
137
- <summary class="is-size-5">
139
+ <details class="mt-1">
140
+ <summary class="is-size-5 is-capitalized">
138
141
  <div class="icon-text">
139
142
  <span class="icon has-text-info">
140
143
  <img class="image is-16x16" src="node_modules/ortoni-report/dist/icon/test.png" alt="test name">
@@ -144,11 +147,12 @@
144
147
  </summary>
145
148
  <ul>
146
149
  {{#each this}}
147
- <details>
148
- <summary>{{@key}}</summary>
150
+ <details class="mb-1">
151
+ <summary class="is-capitalized is-size-6">{{@key}}</summary>
149
152
  <ul>
150
153
  {{#each this}}
151
154
  <li class="media" data-suite-name="{{suite}}"
155
+ data-test-duration="{{duration}}"
152
156
  data-project-name="{{projectName}}" data-test-id="{{index}}"
153
157
  data-test-status="{{status}} {{retry}}">
154
158
  <div class="icon-text">
@@ -201,8 +205,7 @@
201
205
  {{/each}}
202
206
  </div>
203
207
  </aside>
204
-
205
- <section class="column">
208
+ <section class="column is-three-fifths">
206
209
  {{!-- Overall summary --}}
207
210
  <div id="summary">
208
211
  <div class="columns is-multiline">
@@ -304,6 +307,7 @@
304
307
  <div id="testDetails" style="display: none;">
305
308
  <!-- Back button should be outside the dynamic content -->
306
309
  <button class="button content" id="back-to-summary"onclick="showSummary()">Back to Summary</button>
310
+ <div class="tags" id="attachTags"></div>
307
311
  <!-- Test Details will be displayed here -->
308
312
  </div>
309
313
  </section>
@@ -325,53 +329,63 @@
325
329
  });
326
330
  }
327
331
 
328
- document.addEventListener('DOMContentLoaded', () => {
329
- const testData = {{{ json results }}};
330
- const testDetails = document.getElementById('testDetails');
331
- const summary = document.getElementById('summary');
332
- const backButton = document.querySelector('button#back-to-summary');
333
-
334
- const themeButton = document.getElementById("toggle-theme");
335
- const preferredTheme = themeButton.getAttribute("data-theme-status");
336
- const htmlElement = document.documentElement;
337
-
338
- if (preferredTheme === 'dark') {
339
- htmlElement.setAttribute('data-theme', 'dark');
340
- themeButton.classList.add('is-dark');
341
- themeButton.textContent = 'Dark';
342
- } else if (preferredTheme === 'light') {
343
- htmlElement.setAttribute('data-theme', 'light');
344
- themeButton.classList.add('is-light');
345
- themeButton.textContent = 'Light';
346
- }
332
+ document.addEventListener('DOMContentLoaded', () => {
333
+ const testData = {{{ json results }}};
334
+ const testDetails = document.getElementById('testDetails');
335
+ const summary = document.getElementById('summary');
336
+ const backButton = document.querySelector('button#back-to-summary');
347
337
 
348
- themeButton.addEventListener('click', () => {
349
- const currentTheme = htmlElement.getAttribute('data-theme');
350
- const newTheme = currentTheme === 'light' ? 'dark' : 'light';
351
- htmlElement.setAttribute('data-theme', newTheme);
352
- if (newTheme === 'dark') {
353
- themeButton.classList.remove('is-light');
338
+ const themeButton = document.getElementById("toggle-theme");
339
+ const preferredTheme = themeButton.getAttribute("data-theme-status");
340
+ const htmlElement = document.documentElement;
341
+
342
+ if (preferredTheme === 'dark') {
343
+ htmlElement.setAttribute('data-theme', 'dark');
354
344
  themeButton.classList.add('is-dark');
355
345
  themeButton.textContent = 'Dark';
356
- } else {
357
- themeButton.classList.remove('is-dark');
346
+ } else if (preferredTheme === 'light') {
347
+ htmlElement.setAttribute('data-theme', 'light');
358
348
  themeButton.classList.add('is-light');
359
349
  themeButton.textContent = 'Light';
360
350
  }
361
- });
362
351
 
363
- function showSummary() {
364
- summary.style.display = 'block';
365
- testDetails.style.display = 'none';
366
- backButton.style.display = 'none';
367
- }
352
+ themeButton.addEventListener('click', () => {
353
+ const currentTheme = htmlElement.getAttribute('data-theme');
354
+ const newTheme = currentTheme === 'light' ? 'dark' : 'light';
355
+ htmlElement.setAttribute('data-theme', newTheme);
356
+ if (newTheme === 'dark') {
357
+ themeButton.classList.remove('is-light');
358
+ themeButton.classList.add('is-dark');
359
+ themeButton.textContent = 'Dark';
360
+ } else {
361
+ themeButton.classList.remove('is-dark');
362
+ themeButton.classList.add('is-light');
363
+ themeButton.textContent = 'Light';
364
+ }
365
+ });
368
366
 
369
- window.showSummary = showSummary;
367
+ function showSummary() {
368
+ summary.style.display = 'block';
369
+ testDetails.style.display = 'none';
370
+ backButton.style.display = 'none';
371
+ }
370
372
 
371
- function displayTestDetails(test) {
372
- const summary = document.getElementById('summary');
373
- const testDetails = document.getElementById('testDetails');
374
- const backButton = document.querySelector('button#back-to-summary');
373
+ window.showSummary = showSummary;
374
+
375
+ function attachTags(testTags){
376
+ const tags = document.querySelector("#attachTags");
377
+ testTags.forEach(tag => {
378
+ const tagElement = document.createElement('span');
379
+ tagElement.className = 'tag is-info';
380
+ tagElement.textContent = tag;
381
+ tags.appendChild(tagElement);
382
+ });
383
+ }
384
+
385
+ function displayTestDetails(test) {
386
+ const summary = document.getElementById('summary');
387
+ const testDetails = document.getElementById('testDetails');
388
+ const backButton = document.querySelector('button#back-to-summary');
375
389
  summary.style.display = 'none';
376
390
  testDetails.style.opacity = '0';
377
391
  testDetails.style.display = 'block';
@@ -379,218 +393,260 @@
379
393
  testDetails.style.opacity = '1';
380
394
  backButton.style.opacity = '1';
381
395
  }, 50);
382
-
383
-
384
- let statusClass = '';
385
- let statusText = test.status.toUpperCase();
386
- if (test.status.startsWith('passed')) {
387
- statusClass = 'tag is-success';
388
- } else if (test.status === 'flaky') {
389
- statusClass = 'tag is-warning';
390
- } else if (test.status === 'failed') {
391
- statusClass = 'tag is-danger';
392
- } else {
393
- statusClass = 'tag is-info';
394
- }
395
396
 
396
- testDetails.innerHTML = `
397
- <button class="button content" id="back-to-summary" style="display: block" onclick="showSummary()">Back to Summary</button>
398
- <h3 class="title is-3 has-text-centered">${test.title}</h3>
399
- <div class="columns">
400
- <div class="column content">
401
- <h4 class="title is-4">Status</h4>
402
- <p class="${statusClass}">${statusText}</p>
403
- ${test.duration.length > 0 ? `
404
- <h4 class="title is-4">Duration</h4>
405
- <p class="${statusClass}">${test.duration}</p>` : ""}
397
+ let statusClass = '';
398
+ let statusText = test.status.toUpperCase();
399
+ if (test.status.startsWith('passed')) {
400
+ statusClass = 'tag is-success';
401
+ } else if (test.status === 'flaky') {
402
+ statusClass = 'tag is-warning';
403
+ } else if (test.status === 'failed') {
404
+ statusClass = 'tag is-danger';
405
+ } else {
406
+ statusClass = 'tag is-info';
407
+ }
408
+
409
+ testDetails.innerHTML = `
410
+ <button class="button content" id="back-to-summary" style="display: block" onclick="showSummary()">Back to Summary</button>
411
+ <div class="content has-text-centered">
412
+ <p class="title">${test.title}</p>
413
+ <p class="subtitle" id="filepath">${test.location}</p>
406
414
  </div>
407
- <div class="column content">
408
- ${test.screenshotPath ? `
409
- <div id="modal-js-example" class="modal">
410
- <div class="modal-background"></div>
415
+ <div class="columns">
416
+ <div class="column content">
417
+ <h4 class="title is-4">Status</h4>
418
+ <p class="${statusClass}">${statusText}</p>
419
+ ${test.duration.length > 0 ? `
420
+ <h4 class="title is-4">Duration</h4>
421
+ <p class="${statusClass}">${test.duration}</p>` : ""}
422
+ <div class="tags" id="attachTags"></div>
423
+ ${test.videoPath ? `
424
+ <div id="testVideo" class="modal">
425
+ <div class="modal-background"></div>
426
+ <div class="modal-content">
427
+ <figure>
428
+ <video controls>
429
+ <source src="file://${test.videoPath}" type="video/webm">
430
+ Your browser does not support the video tag.
431
+ </video>
432
+ </figure>
433
+ </div>
434
+ <button onclick="closeVideo()" class="modal-close is-large" aria-label="close"></button>
435
+ </div>
436
+ <button class="button" onclick="openVideo()">Attachment: Video</button>
437
+ `:''}
438
+ </div>
439
+ <div class="column content">
440
+ ${test.screenshotPath ? `
441
+ <div id="testImage" class="modal">
442
+ <div class="modal-background"></div>
411
443
  <div class="modal-content">
412
- <p class="image is-16by9">
413
- <img src="data:image/png;base64, ${test.screenshotPath}" alt="Screenshot">
444
+ <p class="image">
445
+ <img src="${test.base64Image ? test.screenshotPath : `file://${test.screenshotPath}`}" alt="Screenshot">
414
446
  </p>
415
447
  </div>
416
- <button onclick="closeModal()" class="modal-close is-large" aria-label="close"></button>
448
+ <button onclick="closeModal()" class="modal-close is-large" aria-label="close"></button>
449
+ </div>
450
+ <figure class="image box">
451
+ <img onclick="openModal()" src="${test.base64Image ? test.screenshotPath : `file://${test.screenshotPath}`}" alt="Screenshot">
452
+ </figure>` : ''}
417
453
  </div>
418
- <figure class="image is-16by9">
419
- <img onclick="openModal()" src="data:image/png;base64, ${test.screenshotPath}" alt="Screenshot">
420
- </figure>` : ''}
421
454
  </div>
422
- </div>
423
- <div class="content">
424
- ${test.steps.length > 0 ? `
425
- <details id="stepopen">
426
- <summary><h4 class="title is-4">Steps</h4></summary>
427
- <span id="stepDetails" class="content"></span>
428
- </details>
429
- `: ``}
430
- </div class="content">
431
- <div>
432
- ${test.errors.length ? `
433
- <h4 class="title is-4">Errors</h4>
434
455
  <div class="content">
435
- <pre>${escapeHtml(test.errors.join('\n'))}</pre>
436
- </div>` : ''}
437
- </div>
438
- <div>
439
- ${test.logs ? `
440
- <h4 class="title is-4">Logs</h4>
441
- <div class="box">
442
- <pre>${escapeHtml(test.logs)}</pre>
443
- </div>` : ''}
444
- </div>
445
- `;
446
-
447
- const stepDetailsDiv = document.getElementById('stepDetails');
448
- if(stepDetailsDiv){
449
- const stepsList = attachSteps(test);
450
- const detail = document.getElementById("stepopen");
451
- detail.setAttribute("open", "");
452
- stepDetailsDiv.appendChild(stepsList);
456
+ ${test.steps.length > 0 ? `
457
+ <details id="stepopen">
458
+ <summary><h4 class="title is-4">Steps</h4></summary>
459
+ <span id="stepDetails" class="content"></span>
460
+ </details>
461
+ `: ``}
462
+ </div class="content">
463
+ <div>
464
+ ${test.errors.length ? `
465
+ <h4 class="title is-4">Errors</h4>
466
+ <div class="content">
467
+ <pre><code class="data-lang=js">${escapeHtml(test.errors.join('\n'))}</code></pre>
468
+ </div>` : ''}
469
+ </div>
470
+ <div>
471
+ ${test.logs ? `
472
+ <h4 class="title is-4">Logs</h4>
473
+ <div class="box">
474
+ <pre>${escapeHtml(test.logs)}</pre>
475
+ </div>` : ''}
476
+ </div>
477
+ `;
478
+
479
+ const stepDetailsDiv = document.getElementById('stepDetails');
480
+ if(stepDetailsDiv){
481
+ const stepsList = attachSteps(test);
482
+ const detail = document.getElementById("stepopen");
483
+ if(test.errors.length > 0){
484
+ detail.setAttribute("open", "");
485
+ }
486
+ stepDetailsDiv.appendChild(stepsList);
487
+ }
488
+ attachTags(test.testTags);
453
489
  }
454
- }
455
490
 
456
- function attachSteps(test) {
457
- const stepsList = document.createElement("ul");
458
- stepsList.setAttribute("id", "steps");
459
- stepsList.innerHTML = '';
460
- test.steps.forEach(step => {
461
- const li = document.createElement('li');
462
- li.innerHTML = `<strong class="${step.error ? 'has-text-danger' : ''}">${step.title}</strong>`;
463
- stepsList.appendChild(li);
464
- });
465
- return stepsList;
466
- }
467
-
468
- function attachEventListeners() {
469
- const testItems = document.querySelectorAll('[data-test-id]');
470
- testItems.forEach(item => {
471
- item.addEventListener('click', () => {
472
- const testId = item.getAttribute('data-test-id');
473
- const test = testData[testId];
474
- displayTestDetails(test);
491
+ function attachSteps(test) {
492
+ const stepsList = document.createElement("ul");
493
+ stepsList.setAttribute("id", "steps");
494
+ stepsList.innerHTML = '';
495
+ test.steps.forEach(step => {
496
+ const li = document.createElement('li');
497
+ li.innerHTML = `<strong class="${step.snippet ? 'has-text-danger' : ''}">${step.title}</strong>`;
498
+ if (step.snippet) {
499
+ const pre = document.createElement('pre');
500
+ const code = document.createElement('code');
501
+ const locationText = step.location ? `\n\nat: ${step.location}` : '';
502
+ code.textContent = `${step.snippet}${locationText}`;
503
+ code.setAttribute('data-lang', 'js');
504
+ pre.appendChild(code);
505
+ li.appendChild(pre);
506
+ }
507
+ stepsList.appendChild(li);
475
508
  });
476
- });
509
+ return stepsList;
510
+ }
477
511
 
478
- // Event listeners for the filter articles
479
- const filters = document.querySelectorAll('.filter');
480
- filters.forEach(filter => {
481
- filter.addEventListener('click', () => {
482
- const status = filter.getAttribute('data-status');
483
- filters.forEach(f => {
484
- if (f.getAttribute('data-status')) {
485
- f.classList.remove('active');
486
- }
512
+
513
+ function attachEventListeners() {
514
+ const testItems = document.querySelectorAll('[data-test-id]');
515
+ testItems.forEach(item => {
516
+ item.addEventListener('click', () => {
517
+ const testId = item.getAttribute('data-test-id');
518
+ const test = testData[testId];
519
+ displayTestDetails(test);
487
520
  });
488
- filter.classList.add('active');
489
- applyFilters();
490
521
  });
491
- });
492
522
 
493
- const projectFilter = document.getElementById('project-filter');
494
- projectFilter.addEventListener('change', () => {
495
- applyFilters();
496
- });
497
- }
498
-
499
- function applyFilters() {
500
- const selectedProject = document.getElementById('project-filter').value;
501
- const activeFilter = document.querySelector('.filter.active');
502
- const selectedStatus = activeFilter ? activeFilter.getAttribute('data-status') : 'all';
523
+ const filters = document.querySelectorAll('.filter');
524
+ filters.forEach(filter => {
525
+ filter.addEventListener('click', () => {
526
+ const status = filter.getAttribute('data-status');
527
+ filters.forEach(f => {
528
+ if (f.getAttribute('data-status')) {
529
+ f.classList.remove('active');
530
+ }
531
+ });
532
+ filter.classList.add('active');
533
+ applyFilters();
534
+ });
535
+ });
503
536
 
504
- const testItems = document.querySelectorAll('li[data-test-id]');
505
- const detailsElements = document.querySelectorAll('details');
537
+ const projectFilter = document.getElementById('project-filter');
538
+ projectFilter.addEventListener('change', () => {
539
+ applyFilters();
540
+ });
541
+ }
506
542
 
507
- detailsElements.forEach(details => {
508
- let shouldShowDetails = false;
509
- const items = details.querySelectorAll('li[data-test-id]');
510
- items.forEach(item => {
511
- const projectName = item.getAttribute('data-project-name').trim();
512
- const testStatus = item.getAttribute('data-test-status').trim();
513
- const matchesProject = (selectedProject === 'all' || projectName === selectedProject);
514
- const matchesStatus = (selectedStatus === 'all' || testStatus.includes(selectedStatus) ||
515
- (selectedStatus === 'failed' && (testStatus === 'failed' || testStatus === 'timedOut')) ||
516
- (selectedStatus === 'retry' && testStatus.includes('retry')) ||
517
- (selectedStatus === 'flaky' && testStatus.includes('flaky')));
518
-
519
- if (matchesProject && matchesStatus) {
520
- item.classList.remove('is-hidden');
521
- shouldShowDetails = true;
522
- } else {
523
- item.classList.add('is-hidden');
524
- }
543
+ function applyFilters() {
544
+ const selectedProject = document.getElementById('project-filter').value;
545
+ const activeFilter = document.querySelector('.filter.active');
546
+ const selectedStatus = activeFilter ? activeFilter.getAttribute('data-status') : 'all';
547
+
548
+ const testItems = document.querySelectorAll('li[data-test-id]');
549
+ const detailsElements = document.querySelectorAll('details');
550
+
551
+ detailsElements.forEach(details => {
552
+ let shouldShowDetails = false;
553
+ const items = details.querySelectorAll('li[data-test-id]');
554
+ items.forEach(item => {
555
+ const projectName = item.getAttribute('data-project-name').trim();
556
+ const testStatus = item.getAttribute('data-test-status').trim();
557
+ const matchesProject = (selectedProject === 'all' || projectName === selectedProject);
558
+ const matchesStatus = (selectedStatus === 'all' || testStatus.includes(selectedStatus) ||
559
+ (selectedStatus === 'failed' && (testStatus === 'failed' || testStatus === 'timedOut')) ||
560
+ (selectedStatus === 'retry' && testStatus.includes('retry')) ||
561
+ (selectedStatus === 'flaky' && testStatus.includes('flaky')));
562
+
563
+ if (matchesProject && matchesStatus) {
564
+ item.classList.remove('is-hidden');
565
+ shouldShowDetails = true;
566
+ } else {
567
+ item.classList.add('is-hidden');
568
+ }
569
+ });
570
+ details.open = shouldShowDetails;
571
+ details.classList.toggle('is-hidden', !shouldShowDetails);
525
572
  });
526
- details.open = shouldShowDetails;
527
- details.classList.toggle('is-hidden', !shouldShowDetails);
528
- });
529
- }
573
+ }
530
574
 
531
- const searchInput = document.querySelector('input[name="search"]');
532
- const detailsElements = document.querySelectorAll('details');
533
- searchInput.addEventListener('input', () => {
534
- const searchTerm = searchInput.value.toLowerCase();
535
- const testItems = document.querySelectorAll('[data-test-id]');
575
+ const searchInput = document.querySelector('input[name="search"]');
576
+ const detailsElements = document.querySelectorAll('details');
577
+ function filterTests(search){
578
+ const searchTerm = search.toLowerCase();
579
+ const testItems = document.querySelectorAll('[data-test-id]');
536
580
 
537
- if (searchTerm) {
538
- detailsElements.forEach(detail => {
539
- detail.open = false; // Collapse all details initially
540
- });
581
+ if (searchTerm) {
582
+ detailsElements.forEach(detail => {
583
+ detail.open = false; // Collapse all details initially
584
+ });
541
585
 
542
- testItems.forEach(item => {
543
- const testTitle = item.textContent.toLowerCase();
544
- if (testTitle.includes(searchTerm)) {
545
- item.style.display = 'block'; // Show matching test item
546
-
547
- let parent = item.parentElement;
548
- while (parent && parent.tagName !== 'ASIDE') {
549
- if (parent.tagName === 'DETAILS') {
550
- parent.open = true;
586
+ testItems.forEach(item => {
587
+ const testTitle = item.textContent.toLowerCase();
588
+ if (testTitle.includes(searchTerm)) {
589
+ item.style.display = 'block'; // Show matching test item
590
+
591
+ let parent = item.parentElement;
592
+ while (parent && parent.tagName !== 'ASIDE') {
593
+ if (parent.tagName === 'DETAILS') {
594
+ parent.open = true;
595
+ }
596
+ parent = parent.parentElement;
551
597
  }
552
- parent = parent.parentElement;
598
+ } else {
599
+ item.style.display = 'none';
553
600
  }
554
- } else {
555
- item.style.display = 'none';
556
- }
557
- });
558
- } else {
559
- testItems.forEach(item => {
560
- item.style.display = 'block';
561
- });
562
- detailsElements.forEach(detail => {
563
- detail.open = false;
564
- });
601
+ });
602
+ } else {
603
+ testItems.forEach(item => {
604
+ item.style.display = 'block';
605
+ });
606
+ detailsElements.forEach(detail => {
607
+ detail.open = false;
608
+ });
609
+ }
565
610
  }
566
- });
567
-
568
- const ctx = document.getElementById('testChart').getContext('2d');
569
- new Chart(ctx, {
570
- type: 'doughnut',
571
- data: {
572
- labels: ['Passed', 'Failed', 'Skipped','Flaky'],
573
- datasets: [{
574
- data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}, {{flakyCount}}],
575
- backgroundColor: ['#28a745', '#dc3545', '#d5d4a1', '#FFB704']
576
- }]
577
- },
578
- options: {
579
- responsive: true,
580
- maintainAspectRatio: false,
581
- plugins: {
582
- legend: {
583
- position: 'bottom'
611
+ function debounce(func, wait) {
612
+ let timeout;
613
+ return function(...args) {
614
+ clearTimeout(timeout);
615
+ timeout = setTimeout(() => func.apply(this, args), wait);
616
+ };
617
+ }
618
+
619
+ const debouncedSearch = debounce((event) => {
620
+ filterTests(event.target.value);
621
+ }, 300);
622
+
623
+ searchInput.addEventListener('input', debouncedSearch);
624
+
625
+ const ctx = document.getElementById('testChart').getContext('2d');
626
+ new Chart(ctx, {
627
+ type: 'doughnut',
628
+ data: {
629
+ labels: ['Passed', 'Failed', 'Skipped','Flaky'],
630
+ datasets: [{
631
+ data: [{{ passCount }}, {{ failCount }}, {{ skipCount }}, {{flakyCount}}],
632
+ backgroundColor: ['#28a745', '#dc3545', '#d5d4a1', '#FFB704']
633
+ }]
634
+ },
635
+ options: {
636
+ responsive: true,
637
+ maintainAspectRatio: false,
638
+ plugins: {
639
+ legend: {
640
+ position: 'right'
641
+ }
584
642
  }
585
643
  }
586
- }
587
- });
644
+ });
588
645
 
589
- attachEventListeners();
590
- });
646
+ attachEventListeners();
647
+ });
591
648
 
592
- </script>
649
+ </script>
593
650
  <script src="node_modules/ortoni-report/dist/utils/modal.js"></script>
594
651
  </body>
595
-
596
652
  </html>