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