snap-ally 0.2.5-beta → 0.3.0

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.
Files changed (75) hide show
  1. package/dist/A11yReportAssets.d.ts +5 -12
  2. package/dist/A11yReportAssets.js +16 -82
  3. package/dist/A11yScanner.d.ts +2 -21
  4. package/dist/A11yScanner.js +16 -22
  5. package/dist/A11yTimeUtils.js +11 -23
  6. package/dist/A11yVisualReporter.d.ts +50 -0
  7. package/dist/A11yVisualReporter.js +188 -0
  8. package/dist/AccessibilityReporterOptions.d.ts +24 -0
  9. package/dist/AccessibilityReporterOptions.js +5 -0
  10. package/dist/ResolvedColors.d.ts +15 -0
  11. package/dist/ResolvedColors.js +20 -0
  12. package/dist/SnapAllyReporter.d.ts +12 -72
  13. package/dist/SnapAllyReporter.js +218 -329
  14. package/dist/core/A11yHtmlRenderer.d.ts +17 -0
  15. package/dist/core/A11yHtmlRenderer.js +118 -0
  16. package/dist/core/A11yReportAssets.d.ts +30 -0
  17. package/dist/core/A11yReportAssets.js +127 -0
  18. package/dist/core/A11yScanner.d.ts +8 -0
  19. package/dist/core/A11yScanner.js +178 -0
  20. package/dist/core/A11yVisualReporter.d.ts +50 -0
  21. package/dist/core/A11yVisualReporter.js +188 -0
  22. package/dist/core/HtmlRenderer.d.ts +14 -0
  23. package/dist/core/HtmlRenderer.js +106 -0
  24. package/dist/core/ReportAssets.d.ts +29 -0
  25. package/dist/core/ReportAssets.js +126 -0
  26. package/dist/core/Scanner.d.ts +7 -0
  27. package/dist/core/Scanner.js +162 -0
  28. package/dist/core/VisualReporter.d.ts +54 -0
  29. package/dist/core/VisualReporter.js +192 -0
  30. package/dist/index.d.ts +6 -6
  31. package/dist/index.js +13 -12
  32. package/dist/models/A11yDataSource.d.ts +15 -0
  33. package/dist/models/A11yDataSource.js +2 -0
  34. package/dist/models/A11yError.d.ts +34 -0
  35. package/dist/models/A11yError.js +11 -0
  36. package/dist/models/A11yScannerOptions.d.ts +24 -0
  37. package/dist/models/A11yScannerOptions.js +2 -0
  38. package/dist/models/AccessibilityReporterOptions.d.ts +24 -0
  39. package/dist/models/AccessibilityReporterOptions.js +5 -0
  40. package/dist/models/DataSource.d.ts +15 -0
  41. package/dist/models/DataSource.js +2 -0
  42. package/dist/models/ImagePath.d.ts +5 -0
  43. package/dist/models/ImagePath.js +3 -0
  44. package/dist/models/ReportData.d.ts +24 -0
  45. package/dist/models/ReportData.js +2 -0
  46. package/dist/models/ReporterOptions.d.ts +24 -0
  47. package/dist/models/ReporterOptions.js +5 -0
  48. package/dist/models/ResolvedColors.d.ts +16 -0
  49. package/dist/models/ResolvedColors.js +24 -0
  50. package/dist/models/ScannerOptions.d.ts +30 -0
  51. package/dist/models/ScannerOptions.js +2 -0
  52. package/dist/models/Severity.d.ts +7 -0
  53. package/dist/models/Severity.js +11 -0
  54. package/dist/models/Target.d.ts +10 -0
  55. package/dist/models/Target.js +3 -0
  56. package/dist/models/TestResults.d.ts +41 -0
  57. package/dist/models/TestResults.js +2 -0
  58. package/dist/models/TestStatusIcon.d.ts +8 -0
  59. package/dist/models/TestStatusIcon.js +12 -0
  60. package/dist/models/TestSummary.d.ts +34 -0
  61. package/dist/models/TestSummary.js +2 -0
  62. package/dist/models/Violation.d.ts +13 -0
  63. package/dist/models/Violation.js +2 -0
  64. package/dist/models/index.d.ts +12 -113
  65. package/dist/models/index.js +26 -16
  66. package/dist/templates/accessibility-report.html +75 -101
  67. package/dist/templates/execution-summary.html +37 -103
  68. package/dist/templates/global-report-styles.css +400 -9
  69. package/dist/templates/report-app.js +171 -73
  70. package/dist/templates/test-execution-report.html +84 -113
  71. package/dist/utils/A11yTimeUtils.d.ts +13 -0
  72. package/dist/utils/A11yTimeUtils.js +40 -0
  73. package/dist/utils/TimeUtils.d.ts +13 -0
  74. package/dist/utils/TimeUtils.js +39 -0
  75. package/package.json +2 -2
@@ -6,6 +6,7 @@
6
6
  document.addEventListener('DOMContentLoaded', () => {
7
7
  // Pull injected data
8
8
  const data = window.snapAllyData;
9
+ console.log('Snap Ally Debug: injectedData=', data);
9
10
  if (!data) {
10
11
  console.error('Snap Ally: No report data found in window.snapAllyData');
11
12
  return;
@@ -52,11 +53,26 @@ function applyCustomColors(data) {
52
53
  function renderTestExecutionReport(data) {
53
54
  const root = document.getElementById('test-execution-root');
54
55
  if (!root) return;
55
- root.style.display = 'block';
56
+ root.classList.remove('hidden');
56
57
 
57
58
  // Set document title
58
59
  document.title = `Snap Ally - Test Execution: ${data.title}`;
59
60
 
61
+ if (data.a11yReportPath) {
62
+ console.log('[SnapAlly Diagnostic] Found report path:', data.a11yReportPath);
63
+ const a11yLink = document.getElementById('view-a11y-report-link');
64
+ if (a11yLink) {
65
+ const filename = data.a11yReportPath.split(/[/\\]/).pop();
66
+ a11yLink.href = filename;
67
+ a11yLink.classList.remove('hidden');
68
+ console.log('[SnapAlly Diagnostic] Link shown. Href:', a11yLink.href);
69
+ } else {
70
+ console.warn('[SnapAlly Diagnostic] Link element "view-a11y-report-link" NOT in DOM');
71
+ }
72
+ } else {
73
+ console.log('[SnapAlly Diagnostic] No a11yReportPath found in data');
74
+ }
75
+
60
76
  // Header
61
77
  document.getElementById('report-title').textContent = data.title;
62
78
 
@@ -65,8 +81,11 @@ function renderTestExecutionReport(data) {
65
81
  document.getElementById('report-status-icon').textContent = data.statusIcon;
66
82
  document.getElementById('report-status-text').textContent = data.status;
67
83
 
68
- if (data.a11yReportPath && data.a11yErrorCount === 0) {
69
- document.getElementById('report-a11y-verified').style.display = 'flex';
84
+ // Show verified badge only if no errors
85
+ // Show verified badge only if no errors and no path-reported violations
86
+ const hasA11yErrors = (data.a11yErrorCount > 0) || (data.a11yErrors && data.a11yErrors.length > 0);
87
+ if (data.a11yReportPath && !hasA11yErrors) {
88
+ document.getElementById('report-a11y-verified').classList.remove('hidden');
70
89
  }
71
90
 
72
91
  document.getElementById('report-duration').textContent = data.duration;
@@ -81,22 +100,18 @@ function renderTestExecutionReport(data) {
81
100
  tagsContainer.appendChild(clone);
82
101
  });
83
102
 
84
- if (data.a11yReportPath) {
85
- const a11yLink = document.getElementById('view-a11y-report-link');
86
- a11yLink.href = `./${data.a11yReportPath}`;
87
- a11yLink.style.display = 'flex';
88
- }
89
103
 
90
104
  // Description
91
105
  if (data.description) {
92
- document.getElementById('card-description').style.display = 'block';
106
+ const card = document.getElementById('card-description');
107
+ card.classList.remove('hidden');
93
108
  document.getElementById('report-description').textContent = data.description;
94
109
  }
95
110
 
96
111
  // Helper method for array elements
97
112
  const renderList = (array, listId, cardId) => {
98
113
  if (array && array.length > 0) {
99
- document.getElementById(cardId).style.display = 'block';
114
+ document.getElementById(cardId).classList.remove('hidden');
100
115
  const list = document.getElementById(listId);
101
116
  const tpl = document.getElementById('string-item-template');
102
117
  array.forEach((item) => {
@@ -117,15 +132,45 @@ function renderTestExecutionReport(data) {
117
132
  );
118
133
  renderList(filteredErrs, 'list-exceptions', 'card-exceptions');
119
134
 
120
- if (data.videoPath) {
121
- document.getElementById('card-video').style.display = 'block';
122
- const source = document.getElementById('report-video-source');
123
- source.src = data.videoPath;
124
- source.parentElement.load();
135
+ // Normalize to an array of videos
136
+ const videos = Array.isArray(data.videoPath)
137
+ ? data.videoPath
138
+ : data.videoPath
139
+ ? [data.videoPath]
140
+ : [];
141
+
142
+ if (videos.length > 0) {
143
+ document.getElementById('card-video').classList.remove('hidden');
144
+ const container = document.getElementById('report-video-container');
145
+ container.innerHTML = '';
146
+
147
+ videos.forEach((vPath, idx) => {
148
+ const wrapper = document.createElement('div');
149
+ wrapper.style.marginBottom = '12px';
150
+
151
+ const label = document.createElement('div');
152
+ label.style.fontSize = '0.85rem';
153
+ label.style.color = 'var(--text-muted)';
154
+ label.style.marginBottom = '4px';
155
+ label.textContent = videos.length > 1 ? `Recording ${idx + 1}` : 'Recording';
156
+ wrapper.appendChild(label);
157
+
158
+ const videoEl = document.createElement('video');
159
+ videoEl.controls = true;
160
+ videoEl.style.width = '100%';
161
+
162
+ const source = document.createElement('source');
163
+ source.type = 'video/webm';
164
+ source.src = vPath;
165
+ videoEl.appendChild(source);
166
+
167
+ wrapper.appendChild(videoEl);
168
+ container.appendChild(wrapper);
169
+ });
125
170
  }
126
171
 
127
172
  if (data.screenshotPaths && data.screenshotPaths.length > 0) {
128
- document.getElementById('card-screenshots').style.display = 'block';
173
+ document.getElementById('card-screenshots').classList.remove('hidden');
129
174
  const grid = document.getElementById('grid-screenshots');
130
175
  const tpl = document.getElementById('screenshot-template');
131
176
  data.screenshotPaths.forEach((p) => {
@@ -136,7 +181,7 @@ function renderTestExecutionReport(data) {
136
181
  }
137
182
 
138
183
  if (data.attachments && data.attachments.length > 0) {
139
- document.getElementById('card-attachments').style.display = 'block';
184
+ document.getElementById('card-attachments').classList.remove('hidden');
140
185
  const list = document.getElementById('list-attachments');
141
186
  const tpl = document.getElementById('attachment-template');
142
187
  data.attachments.forEach((att) => {
@@ -150,9 +195,9 @@ function renderTestExecutionReport(data) {
150
195
 
151
196
  // Accessibility Success / Error Cards inside general test report
152
197
  if (data.a11yReportPath && data.a11yErrorCount === 0) {
153
- document.getElementById('card-a11y-success').style.display = 'flex';
198
+ document.getElementById('card-a11y-success').classList.remove('hidden');
154
199
  } else if (data.a11yErrors && data.a11yErrors.length > 0) {
155
- document.getElementById('card-a11y-errors').style.display = 'block';
200
+ document.getElementById('card-a11y-errors').classList.remove('hidden');
156
201
  const list = document.getElementById('list-a11y-errors');
157
202
  const tplError = document.getElementById('a11y-error-template');
158
203
  const tplInstance = document.getElementById('a11y-instance-template');
@@ -168,7 +213,7 @@ function renderTestExecutionReport(data) {
168
213
  if (error.description) {
169
214
  const desc = clone.querySelector('.desc-text');
170
215
  desc.textContent = error.description;
171
- desc.style.display = 'block';
216
+ desc.classList.remove('hidden');
172
217
  }
173
218
  clone.querySelector('.occ-count').textContent = error.total;
174
219
 
@@ -197,7 +242,7 @@ function renderTestExecutionReport(data) {
197
242
  function renderExecutionSummary(data) {
198
243
  const root = document.getElementById('report-summary-root');
199
244
  if (!root) return;
200
- root.style.display = 'block';
245
+ root.classList.remove('hidden');
201
246
 
202
247
  // Hero Section
203
248
  document.getElementById('summary-status-badge').classList.add(`status-${data.status}`);
@@ -219,15 +264,13 @@ function renderExecutionSummary(data) {
219
264
 
220
265
  const browsers = Object.keys(data.browserSummaries);
221
266
 
222
- const setElDisplay = (id, disp) => {
223
- const el = document.getElementById(id);
224
- if (el) el.style.display = disp;
225
- };
226
267
 
227
268
  if (data.totalA11yErrorCount > 0) {
228
- setElDisplay('summary-global-a11y-box', 'flex');
269
+ const a11yBox = document.getElementById('summary-global-a11y-box');
270
+ if (a11yBox) a11yBox.classList.remove('hidden');
229
271
  setElText('summary-a11y-total', data.totalA11yErrorCount);
230
- setElDisplay('global-errors-container', 'block');
272
+ const errContainer = document.getElementById('global-errors-container');
273
+ if (errContainer) errContainer.classList.remove('hidden');
231
274
 
232
275
  // Populate WCAG Error Cards
233
276
  const wcagGrid = document.getElementById('global-metrics-grid');
@@ -246,7 +289,7 @@ function renderExecutionSummary(data) {
246
289
  if (info.description) {
247
290
  const desc = clone.querySelector('.metric-desc');
248
291
  desc.textContent = info.description;
249
- desc.style.display = 'block';
292
+ desc.classList.remove('hidden');
250
293
  }
251
294
 
252
295
  const count = clone.querySelector('.metric-count');
@@ -256,13 +299,13 @@ function renderExecutionSummary(data) {
256
299
  if (info.helpUrl) {
257
300
  const btn = clone.querySelector('.btn-guide');
258
301
  btn.href = info.helpUrl;
259
- btn.style.display = 'inline-flex';
302
+ btn.classList.remove('hidden');
260
303
  }
261
304
  wcagGrid.appendChild(clone);
262
305
  });
263
306
  } else {
264
307
  const successCard = document.getElementById('global-success-card');
265
- successCard.style.display = 'block'; // Container block, layout inside is flex
308
+ if (successCard) successCard.classList.remove('hidden');
266
309
  const icon = document.getElementById('global-success-icon');
267
310
  const title = document.getElementById('global-success-title');
268
311
  const desc = document.getElementById('global-success-desc');
@@ -306,7 +349,8 @@ function renderExecutionSummary(data) {
306
349
  tabDiv.id = `tab-${browser}`;
307
350
 
308
351
  if (bStats.totalA11yErrorCount > 0) {
309
- contentClone.querySelector('.browser-errors-container').style.display = 'block';
352
+ const bErrContainer = contentClone.querySelector('.browser-errors-container');
353
+ if (bErrContainer) bErrContainer.classList.remove('hidden');
310
354
  contentClone.querySelector('.browser-chart-title').textContent = capBrowser;
311
355
  contentClone.querySelector('.browser-chart-container').id =
312
356
  `chart-${browser}-violations`;
@@ -328,7 +372,7 @@ function renderExecutionSummary(data) {
328
372
  if (info.description) {
329
373
  const desc = mClone.querySelector('.metric-desc');
330
374
  desc.textContent = info.description;
331
- desc.style.display = 'block';
375
+ desc.classList.remove('hidden');
332
376
  }
333
377
 
334
378
  const count = mClone.querySelector('.metric-count');
@@ -338,7 +382,7 @@ function renderExecutionSummary(data) {
338
382
  });
339
383
  } else {
340
384
  const sucContainer = contentClone.querySelector('.browser-success-container');
341
- sucContainer.style.display = 'block'; // Container block, layout inside is flex
385
+ if (sucContainer) sucContainer.classList.remove('hidden');
342
386
  const bTitle = sucContainer.querySelector('.browser-success-title');
343
387
  if (bTitle) bTitle.textContent = `${capBrowser} Compliant`;
344
388
  }
@@ -400,13 +444,16 @@ function renderExecutionSummary(data) {
400
444
 
401
445
  // Render Charts if ApexCharts is loaded
402
446
  if (typeof ApexCharts !== 'undefined') {
403
- if (data.totalA11yErrorCount > 0) {
447
+ const globalChartEl = document.getElementById('chart-global-violations');
448
+ if (globalChartEl && data.totalA11yErrorCount > 0) {
404
449
  renderBarChart('chart-global-violations', data.wcagErrors, data.colors);
405
450
  }
406
451
  browsers.forEach((browser) => {
407
- if (data.browserSummaries[browser].totalA11yErrorCount > 0) {
452
+ const chartId = `chart-${browser}-violations`;
453
+ const browserChartEl = document.getElementById(chartId);
454
+ if (browserChartEl && data.browserSummaries[browser].totalA11yErrorCount > 0) {
408
455
  renderBarChart(
409
- `chart-${browser}-violations`,
456
+ chartId,
410
457
  data.browserSummaries[browser].wcagErrors,
411
458
  data.colors
412
459
  );
@@ -421,7 +468,7 @@ function renderExecutionSummary(data) {
421
468
  function renderAccessibilityReport(injectedData) {
422
469
  const root = document.getElementById('accessibility-report-root');
423
470
  if (!root) return;
424
- root.style.display = 'block';
471
+ root.classList.remove('hidden');
425
472
 
426
473
  document.title = 'Snap Ally - Accessibility Audit';
427
474
 
@@ -441,7 +488,14 @@ function renderAccessibilityReport(injectedData) {
441
488
  0
442
489
  );
443
490
  }
444
- const videoPath = data.video || data.videoPath || '';
491
+ // Support either a single video or an array of videos
492
+ const videoArray = Array.isArray(data.video || data.videoPath)
493
+ ? data.video || data.videoPath
494
+ : data.video || data.videoPath
495
+ ? [data.video || data.videoPath]
496
+ : [];
497
+ const videoPath = videoArray[0] || '';
498
+ const browser = data.browser || injectedData.browser || (window.snapAllyData && window.snapAllyData.browser) || 'Unknown';
445
499
 
446
500
  // Hero Section
447
501
  const pageUrlEl = document.getElementById('a11y-page-url');
@@ -450,14 +504,19 @@ function renderAccessibilityReport(injectedData) {
450
504
  const timestampEl = document.getElementById('a11y-timestamp');
451
505
  if (timestampEl) timestampEl.textContent = timestamp;
452
506
 
507
+ const browserContainer = document.getElementById('a11y-browser-container');
508
+ const browserText = document.getElementById('a11y-browser-text');
509
+ if (browserContainer) browserContainer.classList.remove('hidden');
510
+ if (browserText) browserText.textContent = browser;
511
+
453
512
  if (failedCount === 0) {
454
513
  const passedPill = document.getElementById('a11y-pill-passed');
455
- if (passedPill) passedPill.style.display = 'flex';
514
+ if (passedPill) passedPill.classList.remove('hidden');
456
515
  const successCard = document.getElementById('a11y-success-card');
457
- if (successCard) successCard.style.display = 'flex';
516
+ if (successCard) successCard.classList.remove('hidden');
458
517
  } else {
459
518
  const failedPill = document.getElementById('a11y-pill-failed');
460
- if (failedPill) failedPill.style.display = 'flex';
519
+ if (failedPill) failedPill.classList.remove('hidden');
461
520
  const failedCountEl = document.getElementById('a11y-failed-count');
462
521
  if (failedCountEl) failedCountEl.textContent = failedCount;
463
522
  }
@@ -469,11 +528,20 @@ function renderAccessibilityReport(injectedData) {
469
528
  }
470
529
 
471
530
  // Render video section if exists
472
- if (videoPath) {
473
- document.getElementById('a11y-video-card').style.display = 'block';
474
- const source = document.getElementById('a11y-video-source');
475
- source.src = videoPath;
476
- source.parentElement.load();
531
+ if (videoArray.length > 0) {
532
+ const videoCard = document.getElementById('a11y-video-card');
533
+ if (videoCard) videoCard.classList.remove('hidden');
534
+
535
+ const videoList = document.getElementById('a11y-videos-list');
536
+ if (videoList) {
537
+ videoList.innerHTML = ''; // Clear fallback
538
+ videoArray.forEach((v) => {
539
+ const video = document.createElement('video');
540
+ video.controls = true;
541
+ video.innerHTML = `<source src="${v}" type="video/webm">Your browser does not support the video tag.`;
542
+ videoList.appendChild(video);
543
+ });
544
+ }
477
545
  }
478
546
 
479
547
  // Iterate over violations
@@ -525,7 +593,7 @@ function renderAccessibilityReport(injectedData) {
525
593
  bClone.querySelector('.bug-snippet-text').textContent = snippetText;
526
594
 
527
595
  // Parse steps for both the card display and the bug dialog
528
- let stepsArray = node.steps || [];
596
+ let stepsArray = (node.steps && node.steps.length > 0) ? node.steps : (data.steps || []);
529
597
  if (typeof stepsArray === 'string') {
530
598
  try {
531
599
  stepsArray = JSON.parse(stepsArray);
@@ -539,9 +607,10 @@ function renderAccessibilityReport(injectedData) {
539
607
  const safeSnippet = escapeHtml(snippetText);
540
608
  const wcag = escapeHtml(v.wcagRule || (v.tags ? v.tags.join(', ') : ''));
541
609
  const safeStepsJson = encodeURIComponent(JSON.stringify(stepsArray));
610
+ const safeHelpUrl = escapeHtml(v.helpUrl || '');
542
611
  btn.setAttribute(
543
612
  'onclick',
544
- `event.preventDefault(); event.stopPropagation(); window.generateAdoPayload('${escapeHtml(v.id || 'Unknown ID')}', '${escapeHtml(v.help || 'No Help Provided')}', '${escapeHtml(node.failureSummary || '')}', '${escapeHtml(node.html || '')}', '${impact || 'unknown'}', '${escapeHtml(node.screenshotBase64 || node.screenshot || node.screenshotPath || '')}', '${escapeHtml(videoPath || '')}', '${safeSnippet}', '${wcag}', '${safeStepsJson}')`
613
+ `event.preventDefault(); event.stopPropagation(); window.generateAdoPayload('${escapeHtml(v.id || 'Unknown ID')}', '${escapeHtml(v.help || 'No Help Provided')}', '${escapeHtml(node.failureSummary || '')}', '${escapeHtml(node.html || '')}', '${impact || 'unknown'}', '${escapeHtml(node.screenshotBase64 || node.screenshot || node.screenshotPath || '')}', '${escapeHtml(videoPath || '')}', '${safeSnippet}', '${wcag}', '${safeStepsJson}', '${safeHelpUrl}', '${escapeHtml(browser)}')`
545
614
  );
546
615
 
547
616
  const failSec = bClone.querySelector('.bug-failure-summary');
@@ -558,7 +627,7 @@ function renderAccessibilityReport(injectedData) {
558
627
 
559
628
  if (node.screenshot || node.screenshotPath) {
560
629
  const visSec = bClone.querySelector('.visual-evidence-section');
561
- visSec.style.display = 'block';
630
+ visSec.classList.remove('hidden');
562
631
  bClone.querySelector('.bug-screenshot').src =
563
632
  node.screenshot || node.screenshotPath;
564
633
  }
@@ -569,6 +638,10 @@ function renderAccessibilityReport(injectedData) {
569
638
  container.appendChild(vClone);
570
639
  });
571
640
  }
641
+
642
+ // Hide loader after rendering everything
643
+ const loader = document.getElementById('loader-overlay');
644
+ if (loader) loader.classList.add('hidden');
572
645
  }
573
646
 
574
647
  // ============================================================================
@@ -630,17 +703,15 @@ function escapeHtml(unsafe) {
630
703
 
631
704
  // Chart Generation
632
705
  function renderBarChart(elementId, wcagData, colors) {
633
- const el = document.querySelector('#' + elementId);
634
- if (!el) {
635
- throw new Error('CRITICAL_NULL_ELEMENT_ID: ' + elementId);
636
- }
706
+ const el = document.getElementById(elementId);
707
+ if (!el) return;
637
708
 
638
709
  const wcagEntries = Object.entries(wcagData).sort((a, b) => b[1].count - a[1].count);
639
710
  if (wcagEntries.length === 0) return;
640
711
 
641
- const chartColors = wcagEntries.map((e) => colors[e[1].severity] || '#ef4444');
712
+ const chartColors = wcagEntries.map((e) => (colors && colors[e[1].severity]) || '#ef4444');
642
713
 
643
- new ApexCharts(document.querySelector('#' + elementId), {
714
+ new ApexCharts(el, {
644
715
  chart: { type: 'bar', height: 350, toolbar: { show: false } },
645
716
  series: [{ name: 'Violations', data: wcagEntries.map((e) => e[1].count) }],
646
717
  xaxis: {
@@ -752,7 +823,9 @@ window.generateAdoPayload = function (
752
823
  videoPath,
753
824
  snippet,
754
825
  wcag,
755
- stepsJson
826
+ stepsJson,
827
+ helpUrl,
828
+ browser
756
829
  ) {
757
830
  const pat = sessionStorage.getItem('userToken');
758
831
  if (!pat && window.bootstrap) {
@@ -770,6 +843,8 @@ window.generateAdoPayload = function (
770
843
  const data = rawData.data || rawData;
771
844
  const currentUrl = document.getElementById('bugUrlPreview');
772
845
  if (currentUrl) currentUrl.textContent = data.pageUrl || data.pageKey || 'Resource';
846
+ const browserPreview = document.getElementById('bugBrowserPreview');
847
+ if (browserPreview) browserPreview.textContent = browser || 'Unknown';
773
848
 
774
849
  // Decode the reproduction steps passed from the violation card
775
850
  let reproSteps = [];
@@ -792,12 +867,27 @@ window.generateAdoPayload = function (
792
867
  }
793
868
 
794
869
  const failureHtml = `
795
- <div style="font-family: monospace; background: #fffcf0; padding: 12px; border: 1px solid #e2e8f0;">
796
- ${failureSummary ? failureSummary.replace(/\\n/g, '<br>') : 'Issue discovered via static analysis scans.'}
870
+ <div class="repro-section">
871
+ <div class="repro-section-label">Failure Message</div>
872
+ <div class="repro-mono-box">
873
+ ${failureSummary ? failureSummary.replace(/\\n/g, '<br>') : 'Issue discovered via static analysis scans.'}
874
+ </div>
797
875
  </div>
798
- <br>
799
- <div style="font-family: monospace; background: #fffcf0; padding: 12px; border: 1px solid #e2e8f0; overflow-x: auto;">
800
- ${htmlSnippet ? htmlSnippet.replace(/</g, '&lt;').replace(/>/g, '&gt;') : 'No DOM snippet available.'}
876
+ <div class="repro-section">
877
+ <div class="repro-section-label">How to Fix</div>
878
+ <div class="repro-hint-box">
879
+ <span class="material-symbols-outlined repro-hint-icon" aria-hidden="true">lightbulb</span>
880
+ <div>
881
+ <strong>${help || 'No recommendation available.'}</strong>
882
+ ${helpUrl ? `<br><a href="${helpUrl}" target="_blank" rel="noopener" class="repro-deque-link"><span class="material-symbols-outlined" style="font-size:13px;vertical-align:middle">open_in_new</span> View Axe Rule Documentation (deque.com)</a>` : ''}
883
+ </div>
884
+ </div>
885
+ </div>
886
+ <div class="repro-section">
887
+ <div class="repro-section-label">DOM Element</div>
888
+ <div class="repro-mono-box repro-code">
889
+ ${htmlSnippet ? htmlSnippet.replace(/</g, '&lt;').replace(/>/g, '&gt;') : 'No DOM snippet available.'}
890
+ </div>
801
891
  </div>
802
892
  `;
803
893
 
@@ -805,11 +895,18 @@ window.generateAdoPayload = function (
805
895
  const stepsHtml = reproStepsHtml + failureHtml;
806
896
 
807
897
  document.getElementById('bugReproPreview').innerHTML = `
808
- <div style="margin-bottom: 4px;"><b>Rule:</b> ${axeId} (${wcag})</div>
809
- <div style="margin-bottom: 8px;"><b>Recommendation:</b> ${help}</div>
810
- ${reproStepsHtml ? reproStepsHtml : ''}
811
- <div style="border-top: 1px solid #e2e8f0; margin: 8px 0; padding-top: 8px;"><b>Failure Summary:</b></div>
812
- ${failureHtml}
898
+ <div class="repro-preview-root">
899
+ <div class="repro-meta-pills">
900
+ <span class="repro-pill repro-pill-id">${axeId}</span>
901
+ <span class="repro-pill repro-pill-wcag">${wcag}</span>
902
+ <span class="repro-pill repro-pill-sev repro-pill-sev-${severity}">${severity}</span>
903
+ </div>
904
+ ${reproStepsHtml ? `<div class="repro-section">
905
+ <div class="repro-section-label">Reproduction Steps</div>
906
+ ${reproStepsHtml}
907
+ </div>` : ''}
908
+ ${failureHtml}
909
+ </div>
813
910
  `;
814
911
 
815
912
  const screenshotPreview = document.getElementById('bugScreenshotPreview');
@@ -826,21 +923,21 @@ window.generateAdoPayload = function (
826
923
  } else {
827
924
  screenshotPreview.src = `data:image/png;base64,${screenshotBase64}`;
828
925
  }
829
- if (screenshotThumbContainer) screenshotThumbContainer.style.display = 'block';
926
+ if (screenshotThumbContainer) screenshotThumbContainer.classList.remove('hidden');
830
927
  } else {
831
- if (screenshotThumbContainer) screenshotThumbContainer.style.display = 'none';
928
+ if (screenshotThumbContainer) screenshotThumbContainer.classList.add('hidden');
832
929
  }
833
930
 
834
931
  const videoThumbContainer = document.getElementById('videoThumbContainer');
835
932
  const videoPreview = document.getElementById('bugVideoPreview');
836
933
  if (videoPath) {
837
934
  if (videoPreview) videoPreview.src = videoPath;
838
- if (videoThumbContainer) videoThumbContainer.style.display = 'block';
935
+ if (videoThumbContainer) videoThumbContainer.classList.remove('hidden');
839
936
  } else {
840
- if (videoThumbContainer) videoThumbContainer.style.display = 'none';
937
+ if (videoThumbContainer) videoThumbContainer.classList.add('hidden');
841
938
  }
842
939
 
843
- window.currentBugData = { axeId, wcag, help, stepsHtml, screenshotBase64, videoPath };
940
+ window.currentBugData = { axeId, wcag, help, stepsHtml, screenshotBase64, videoPath, browser };
844
941
 
845
942
  if (window.bootstrap) {
846
943
  const modal = bootstrap.Modal.getOrCreateInstance(
@@ -876,11 +973,11 @@ async function submitFinalBug() {
876
973
  const org = data.adoOrganization;
877
974
  const proj = data.adoProject;
878
975
  const pat = sessionStorage.getItem('userToken');
879
- const { axeId, wcag, help, stepsHtml, screenshotBase64, videoPath } = window.currentBugData;
976
+ const { axeId, wcag, help, stepsHtml, screenshotBase64, videoPath, browser } = window.currentBugData;
880
977
 
881
978
  const title = document.getElementById('bugTitleInput').value;
882
979
  const severity = document.getElementById('bugSeverityInput').value;
883
- const area = document.getElementById('bugAreaInput').value;
980
+ const area = document.getElementById('bugAreaInput').value.replace(/\\+/g, '\\');
884
981
 
885
982
  let screenshotUrl = null;
886
983
  if (screenshotBase64) {
@@ -914,6 +1011,7 @@ async function submitFinalBug() {
914
1011
 
915
1012
  const combinedReproHtml = `
916
1013
  <div style="margin-bottom: 12px;"><b>Rule:</b> ${axeId} (${wcag})</div>
1014
+ <div style="margin-bottom: 12px;"><b>Browser:</b> ${browser}</div>
917
1015
  <div style="margin-bottom: 12px;"><b>Recommendation:</b> ${help}</div>
918
1016
  <hr>
919
1017
  <div style="margin-bottom: 8px;"><b>Failure Trace & Details:</b></div>