@govtechsg/oobee 0.10.76 → 0.10.78-alpha1

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 (137) hide show
  1. package/.github/workflows/publish.yml +8 -1
  2. package/INTEGRATION.md +50 -3
  3. package/dist/cli.js +252 -0
  4. package/dist/combine.js +221 -0
  5. package/dist/constants/cliFunctions.js +306 -0
  6. package/dist/constants/common.js +1669 -0
  7. package/dist/constants/constants.js +913 -0
  8. package/dist/constants/errorMeta.json +319 -0
  9. package/dist/constants/itemTypeDescription.js +7 -0
  10. package/dist/constants/oobeeAi.js +121 -0
  11. package/dist/constants/questions.js +151 -0
  12. package/dist/constants/sampleData.js +176 -0
  13. package/dist/crawlers/commonCrawlerFunc.js +428 -0
  14. package/dist/crawlers/crawlDomain.js +613 -0
  15. package/dist/crawlers/crawlIntelligentSitemap.js +135 -0
  16. package/dist/crawlers/crawlLocalFile.js +151 -0
  17. package/dist/crawlers/crawlSitemap.js +303 -0
  18. package/dist/crawlers/custom/escapeCssSelector.js +10 -0
  19. package/dist/crawlers/custom/evaluateAltText.js +11 -0
  20. package/dist/crawlers/custom/extractAndGradeText.js +44 -0
  21. package/dist/crawlers/custom/extractText.js +27 -0
  22. package/dist/crawlers/custom/findElementByCssSelector.js +36 -0
  23. package/dist/crawlers/custom/flagUnlabelledClickableElements.js +963 -0
  24. package/dist/crawlers/custom/framesCheck.js +37 -0
  25. package/dist/crawlers/custom/getAxeConfiguration.js +111 -0
  26. package/dist/crawlers/custom/gradeReadability.js +23 -0
  27. package/dist/crawlers/custom/utils.js +1024 -0
  28. package/dist/crawlers/custom/xPathToCss.js +147 -0
  29. package/dist/crawlers/guards/urlGuard.js +71 -0
  30. package/dist/crawlers/pdfScanFunc.js +276 -0
  31. package/dist/crawlers/runCustom.js +89 -0
  32. package/dist/exclusions.txt +7 -0
  33. package/dist/generateHtmlReport.js +144 -0
  34. package/dist/index.js +62 -0
  35. package/dist/logs.js +84 -0
  36. package/dist/mergeAxeResults.js +1588 -0
  37. package/dist/npmIndex.js +640 -0
  38. package/dist/proxyService.js +360 -0
  39. package/dist/runGenerateJustHtmlReport.js +16 -0
  40. package/dist/screenshotFunc/htmlScreenshotFunc.js +355 -0
  41. package/dist/screenshotFunc/pdfScreenshotFunc.js +645 -0
  42. package/dist/services/s3Uploader.js +127 -0
  43. package/dist/static/ejs/partials/components/allIssues/AllIssues.ejs +9 -0
  44. package/dist/static/ejs/partials/components/allIssues/CategoryBadges.ejs +82 -0
  45. package/dist/static/ejs/partials/components/allIssues/FilterBar.ejs +33 -0
  46. package/dist/static/ejs/partials/components/allIssues/IssuesTable.ejs +41 -0
  47. package/dist/static/ejs/partials/components/header/SiteInfo.ejs +119 -0
  48. package/dist/static/ejs/partials/components/header/aboutScanModal/AboutScanModal.ejs +15 -0
  49. package/dist/static/ejs/partials/components/header/aboutScanModal/ScanConfiguration.ejs +44 -0
  50. package/dist/static/ejs/partials/components/header/aboutScanModal/ScanDetails.ejs +142 -0
  51. package/dist/static/ejs/partials/components/prioritiseIssues/IssueDetailCard.ejs +36 -0
  52. package/dist/static/ejs/partials/components/prioritiseIssues/PrioritiseIssues.ejs +47 -0
  53. package/dist/static/ejs/partials/components/ruleModal/ruleOffcanvas.ejs +196 -0
  54. package/dist/static/ejs/partials/components/scannedPagesSegmentedTabs.ejs +48 -0
  55. package/dist/static/ejs/partials/components/screenshotLightbox.ejs +13 -0
  56. package/dist/static/ejs/partials/components/shared/InfoAlert.ejs +3 -0
  57. package/dist/static/ejs/partials/components/summaryScanAbout.ejs +141 -0
  58. package/dist/static/ejs/partials/components/summaryScanResults.ejs +16 -0
  59. package/dist/static/ejs/partials/components/summaryTable.ejs +20 -0
  60. package/dist/static/ejs/partials/components/summaryWcagCompliance.ejs +94 -0
  61. package/dist/static/ejs/partials/components/topTen.ejs +6 -0
  62. package/dist/static/ejs/partials/components/wcagCompliance/FailedCriteria.ejs +47 -0
  63. package/dist/static/ejs/partials/components/wcagCompliance/WcagCompliance.ejs +16 -0
  64. package/dist/static/ejs/partials/components/wcagCompliance/WcagGaugeBar.ejs +16 -0
  65. package/dist/static/ejs/partials/components/wcagCoverageDetails.ejs +18 -0
  66. package/dist/static/ejs/partials/footer.ejs +24 -0
  67. package/dist/static/ejs/partials/header.ejs +14 -0
  68. package/dist/static/ejs/partials/main.ejs +29 -0
  69. package/dist/static/ejs/partials/scripts/allIssues/AllIssues.ejs +376 -0
  70. package/dist/static/ejs/partials/scripts/bootstrap.ejs +8 -0
  71. package/dist/static/ejs/partials/scripts/categorySummary.ejs +141 -0
  72. package/dist/static/ejs/partials/scripts/decodeUnzipParse.ejs +3 -0
  73. package/dist/static/ejs/partials/scripts/header/SiteInfo.ejs +44 -0
  74. package/dist/static/ejs/partials/scripts/header/aboutScanModal/AboutScanModal.ejs +51 -0
  75. package/dist/static/ejs/partials/scripts/header/aboutScanModal/ScanConfiguration.ejs +127 -0
  76. package/dist/static/ejs/partials/scripts/header/aboutScanModal/ScanDetails.ejs +60 -0
  77. package/dist/static/ejs/partials/scripts/highlightjs.ejs +335 -0
  78. package/dist/static/ejs/partials/scripts/popper.ejs +7 -0
  79. package/dist/static/ejs/partials/scripts/prioritiseIssues/IssueDetailCard.ejs +137 -0
  80. package/dist/static/ejs/partials/scripts/prioritiseIssues/PrioritiseIssues.ejs +214 -0
  81. package/dist/static/ejs/partials/scripts/prioritiseIssues/wcagSvgMap.ejs +861 -0
  82. package/dist/static/ejs/partials/scripts/ruleModal/constants.ejs +957 -0
  83. package/dist/static/ejs/partials/scripts/ruleModal/itemCardRenderer.ejs +353 -0
  84. package/dist/static/ejs/partials/scripts/ruleModal/pageAccordionBuilder.ejs +468 -0
  85. package/dist/static/ejs/partials/scripts/ruleModal/ruleOffcanvas.ejs +306 -0
  86. package/dist/static/ejs/partials/scripts/ruleModal/utilities.ejs +483 -0
  87. package/dist/static/ejs/partials/scripts/scannedPagesSegmentedTabs.ejs +35 -0
  88. package/dist/static/ejs/partials/scripts/screenshotLightbox.ejs +75 -0
  89. package/dist/static/ejs/partials/scripts/summaryScanResults.ejs +14 -0
  90. package/dist/static/ejs/partials/scripts/summaryTable.ejs +78 -0
  91. package/dist/static/ejs/partials/scripts/topTen.ejs +61 -0
  92. package/dist/static/ejs/partials/scripts/utils.ejs +453 -0
  93. package/dist/static/ejs/partials/scripts/wcagCompliance/FailedCriteria.ejs +103 -0
  94. package/dist/static/ejs/partials/scripts/wcagCompliance/WcagGaugeBar.ejs +47 -0
  95. package/dist/static/ejs/partials/scripts/wcagCompliance.ejs +15 -0
  96. package/dist/static/ejs/partials/scripts/wcagCoverageDetails.ejs +75 -0
  97. package/dist/static/ejs/partials/styles/allIssues/AllIssues.ejs +384 -0
  98. package/dist/static/ejs/partials/styles/bootstrap.ejs +12391 -0
  99. package/dist/static/ejs/partials/styles/header/SiteInfo.ejs +121 -0
  100. package/dist/static/ejs/partials/styles/header/aboutScanModal/AboutScanModal.ejs +82 -0
  101. package/dist/static/ejs/partials/styles/header/aboutScanModal/ScanConfiguration.ejs +50 -0
  102. package/dist/static/ejs/partials/styles/header/aboutScanModal/ScanDetails.ejs +149 -0
  103. package/dist/static/ejs/partials/styles/header.ejs +7 -0
  104. package/dist/static/ejs/partials/styles/highlightjs.ejs +54 -0
  105. package/dist/static/ejs/partials/styles/prioritiseIssues/IssueDetailCard.ejs +141 -0
  106. package/dist/static/ejs/partials/styles/prioritiseIssues/PrioritiseIssues.ejs +204 -0
  107. package/dist/static/ejs/partials/styles/ruleModal/ruleOffcanvas.ejs +456 -0
  108. package/dist/static/ejs/partials/styles/scannedPagesSegmentedTabs.ejs +46 -0
  109. package/dist/static/ejs/partials/styles/shared/InfoAlert.ejs +12 -0
  110. package/dist/static/ejs/partials/styles/styles.ejs +1607 -0
  111. package/dist/static/ejs/partials/styles/summaryBootstrap.ejs +12458 -0
  112. package/dist/static/ejs/partials/styles/topTenCard.ejs +44 -0
  113. package/dist/static/ejs/partials/styles/wcagCompliance/FailedCriteria.ejs +59 -0
  114. package/dist/static/ejs/partials/styles/wcagCompliance/WcagGaugeBar.ejs +62 -0
  115. package/dist/static/ejs/partials/styles/wcagCompliance.ejs +36 -0
  116. package/dist/static/ejs/partials/styles/wcagCoverageDetails.ejs +33 -0
  117. package/dist/static/ejs/partials/summaryHeader.ejs +70 -0
  118. package/dist/static/ejs/partials/summaryMain.ejs +49 -0
  119. package/dist/static/ejs/report.ejs +226 -0
  120. package/dist/static/ejs/summary.ejs +47 -0
  121. package/dist/types/types.js +1 -0
  122. package/dist/utils.js +1070 -0
  123. package/examples/oobee-cypress-integration-js/cypress/support/e2e.js +36 -6
  124. package/examples/oobee-cypress-integration-js/cypress.config.js +45 -1
  125. package/examples/oobee-cypress-integration-ts/cypress.config.ts +47 -1
  126. package/examples/oobee-cypress-integration-ts/src/cypress/support/e2e.ts +36 -6
  127. package/examples/oobee-playwright-integration-js/oobee-playwright-demo.js +2 -1
  128. package/examples/oobee-playwright-integration-ts/src/oobee-playwright-demo.ts +2 -1
  129. package/examples/oobee-scan-html-demo.js +51 -0
  130. package/examples/oobee-scan-page-demo.js +40 -0
  131. package/package.json +9 -3
  132. package/src/constants/common.ts +2 -2
  133. package/src/constants/constants.ts +3 -1
  134. package/src/crawlers/crawlDomain.ts +1 -0
  135. package/src/crawlers/runCustom.ts +0 -1
  136. package/src/mergeAxeResults.ts +43 -22
  137. package/src/npmIndex.ts +500 -131
@@ -0,0 +1,127 @@
1
+ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import mime from 'mime-types';
5
+ import { consoleLogger } from '../logs.js';
6
+ const REGION = process.env.AWS_REGION || 'ap-southeast-1';
7
+ const s3Client = new S3Client({ region: REGION });
8
+ export const uploadFileToS3 = async (localFilePath, s3Key, metadata) => {
9
+ const fileStream = fs.readFileSync(localFilePath);
10
+ const contentType = mime.lookup(localFilePath) || 'application/octet-stream';
11
+ const uploadParams = {
12
+ Bucket: process.env.S3_BUCKET_NAME,
13
+ Key: s3Key,
14
+ Body: fileStream,
15
+ ContentType: contentType,
16
+ ...(metadata && { Metadata: metadata }),
17
+ };
18
+ const command = new PutObjectCommand(uploadParams);
19
+ await s3Client.send(command);
20
+ const uploadedUrl = process.env.CLOUDFRONT_BASE_URL
21
+ ? `${process.env.CLOUDFRONT_BASE_URL}/${s3Key}`
22
+ : `https://${process.env.S3_BUCKET_NAME}.s3.${REGION}.amazonaws.com/${s3Key}`;
23
+ return uploadedUrl;
24
+ };
25
+ function getAllFiles(dir, rootDir) {
26
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
27
+ return entries.reduce((files, entry) => {
28
+ const fullPath = path.join(dir, entry.name);
29
+ if (entry.isDirectory()) {
30
+ return files.concat(getAllFiles(fullPath, rootDir));
31
+ }
32
+ const relativePath = path.relative(rootDir, fullPath);
33
+ return files.concat(relativePath);
34
+ }, []);
35
+ }
36
+ export const uploadFolderToS3 = async (localFolderPath, s3Prefix, scanMetadata) => {
37
+ const uploadedFiles = [];
38
+ const files = getAllFiles(localFolderPath, localFolderPath);
39
+ const allowedFileExtRegex = /\.(html|csv|pdf|zip)$/;
40
+ const metadata = {
41
+ scanid: scanMetadata.scanId,
42
+ userid: scanMetadata.userId,
43
+ useremail: scanMetadata.email,
44
+ };
45
+ // Add optional metadata fields if present
46
+ if (scanMetadata.messageId) {
47
+ metadata.messageid = scanMetadata.messageId;
48
+ }
49
+ if (scanMetadata.amplitudeUserId) {
50
+ metadata.amplitudeuserid = scanMetadata.amplitudeUserId;
51
+ }
52
+ if (scanMetadata.deviceId) {
53
+ metadata.deviceid = scanMetadata.deviceId;
54
+ }
55
+ if (scanMetadata.orgId) {
56
+ metadata.orgid = scanMetadata.orgId;
57
+ }
58
+ if (scanMetadata.userRole) {
59
+ metadata.userrole = scanMetadata.userRole;
60
+ }
61
+ if (scanMetadata.siteName) {
62
+ metadata.sitename = scanMetadata.siteName;
63
+ }
64
+ if (scanMetadata.durationExceeded !== undefined) {
65
+ metadata.durationexceeded = scanMetadata.durationExceeded;
66
+ }
67
+ consoleLogger.info(`Uploading ${files.length} files to S3...`);
68
+ const uploadPromises = files.map(async (relativePath) => {
69
+ const fullPath = path.join(localFolderPath, relativePath);
70
+ const s3Path = `${s3Prefix}/${relativePath.replace(/\\/g, '/')}`;
71
+ try {
72
+ const uploadedUrl = await uploadFileToS3(fullPath, s3Path, metadata);
73
+ consoleLogger.info(`Uploaded: ${relativePath}`);
74
+ if (allowedFileExtRegex.test(relativePath)) {
75
+ return {
76
+ filename: relativePath,
77
+ s3Path,
78
+ uploadedUrl,
79
+ };
80
+ }
81
+ return null;
82
+ }
83
+ catch (err) {
84
+ const e = err;
85
+ consoleLogger.error(`Failed to upload ${relativePath}: ${e.message}`);
86
+ throw new Error(`Failed to upload files to S3: ${e.message}`);
87
+ }
88
+ });
89
+ const results = await Promise.all(uploadPromises);
90
+ uploadedFiles.push(...results.filter((file) => file !== null));
91
+ consoleLogger.info(`Successfully uploaded ${uploadedFiles.length} files to S3 at ${s3Prefix}`);
92
+ return uploadedFiles;
93
+ };
94
+ export const isS3UploadEnabled = () => {
95
+ return !!(process.env.S3_BUCKET_NAME &&
96
+ process.env.OOBEE_SCAN_ID &&
97
+ process.env.OOBEE_USER_ID &&
98
+ process.env.OOBEE_USER_EMAIL);
99
+ };
100
+ export const getS3MetadataFromEnv = (siteName, durationExceeded) => {
101
+ const scanId = process.env.OOBEE_SCAN_ID;
102
+ const userId = process.env.OOBEE_USER_ID;
103
+ const email = process.env.OOBEE_USER_EMAIL;
104
+ if (!scanId || !userId || !email) {
105
+ return null;
106
+ }
107
+ return {
108
+ scanId,
109
+ userId,
110
+ email,
111
+ messageId: process.env.OOBEE_MESSAGE_ID,
112
+ amplitudeUserId: process.env.OOBEE_AMPLITUDE_USER_ID,
113
+ deviceId: process.env.OOBEE_DEVICE_ID,
114
+ orgId: process.env.OOBEE_ORG_ID,
115
+ userRole: process.env.OOBEE_USER_ROLE,
116
+ siteName,
117
+ durationExceeded: durationExceeded ? 'true' : undefined,
118
+ };
119
+ };
120
+ export const getS3UploadPrefix = () => {
121
+ const scanId = process.env.OOBEE_SCAN_ID;
122
+ const userId = process.env.OOBEE_USER_ID;
123
+ if (!scanId || !userId) {
124
+ return null;
125
+ }
126
+ return `users/${userId}/scans/${scanId}`;
127
+ };
@@ -0,0 +1,9 @@
1
+ <div id="allIssuesSection" class="card rounded mt-4">
2
+ <div class="card-body">
3
+ <div class="d-flex flex-row gap-5 mb-3">
4
+ <h2>All Issues</h2>
5
+ <%- include('./CategoryBadges') %>
6
+ </div>
7
+ <%- include('./FilterBar') %> <%- include('./IssuesTable') %>
8
+ </div>
9
+ </div>
@@ -0,0 +1,82 @@
1
+ <div class="category-badges-container">
2
+ <div class="category-item">
3
+ <div class="category-item-inner">
4
+ <span class="category-badge must-fix">Must Fix</span>
5
+
6
+ <span class="category-tooltip-wrapper">
7
+ <button
8
+ type="button"
9
+ class="category-tooltip-icon"
10
+ aria-describedby="mustFixTooltip"
11
+ >
12
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"
13
+ viewBox="0 0 14 14" fill="none" aria-hidden="true" focusable="false">
14
+ <path d="M6.3 11.2H7.7V9.8H6.3V11.2ZM7 0C3.136 0 0 3.136 0 7C0 10.864 3.136 14 7 14C10.864 14 14 10.864 14 7C14 3.136 10.864 0 7 0ZM7 12.6C3.913 12.6 1.4 10.087 1.4 7C1.4 3.913 3.913 1.4 7 1.4C10.087 1.4 12.6 3.913 12.6 7C12.6 10.087 10.087 12.6 7 12.6ZM7 2.8C5.453 2.8 4.2 4.053 4.2 5.6H5.6C5.6 4.83 6.23 4.2 7 4.2C7.77 4.2 8.4 4.83 8.4 5.6C8.4 7 6.3 6.825 6.3 9.1H7.7C7.7 7.525 9.8 7.35 9.8 5.6C9.8 4.053 8.547 2.8 7 2.8Z"
15
+ fill="#38268B"/>
16
+ </svg>
17
+ </button>
18
+
19
+ <span class="category-tooltip-bubble" id="mustFixTooltip" role="tooltip">
20
+ Must Fix highlights issues based on the minimum accessibility standards
21
+ (Level A and AA). Fix them to achieve 20/20 WCAG Score.
22
+ </span>
23
+ </span>
24
+
25
+ <span><%= items.mustFix.rules.length %> issues</span>
26
+ </div>
27
+ </div>
28
+
29
+ <div class="category-item">
30
+ <div class="category-item-inner">
31
+ <span class="category-badge good-to-fix">Good to Fix</span>
32
+
33
+ <span class="category-tooltip-wrapper">
34
+ <button
35
+ type="button"
36
+ class="category-tooltip-icon"
37
+ aria-describedby="goodToFixTooltip"
38
+ >
39
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"
40
+ viewBox="0 0 14 14" fill="none" aria-hidden="true" focusable="false">
41
+ <path d="M6.3 11.2H7.7V9.8H6.3V11.2ZM7 0C3.136 0 0 3.136 0 7C0 10.864 3.136 14 7 14C10.864 14 14 10.864 14 7C14 3.136 10.864 0 7 0ZM7 12.6C3.913 12.6 1.4 10.087 1.4 7C1.4 3.913 3.913 1.4 7 1.4C10.087 1.4 12.6 3.913 12.6 7C12.6 10.087 10.087 12.6 7 12.6ZM7 2.8C5.453 2.8 4.2 4.053 4.2 5.6H5.6C5.6 4.83 6.23 4.2 7 4.2C7.77 4.2 8.4 4.83 8.4 5.6C8.4 7 6.3 6.825 6.3 9.1H7.7C7.7 7.525 9.8 7.35 9.8 5.6C9.8 4.053 8.547 2.8 7 2.8Z"
42
+ fill="#38268B"/>
43
+ </svg>
44
+ </button>
45
+
46
+ <span class="category-tooltip-bubble" id="goodToFixTooltip" role="tooltip">
47
+ Good to Fix highlights issues based on WCAG Level AAA, and all industry
48
+ best practices.
49
+ </span>
50
+ </span>
51
+
52
+ <span><%= items.goodToFix.rules.length %> issues</span>
53
+ </div>
54
+ </div>
55
+
56
+ <div class="category-item">
57
+ <div class="category-item-inner">
58
+ <span class="category-badge needs-review">Manual Test</span>
59
+
60
+ <span class="category-tooltip-wrapper">
61
+ <button
62
+ type="button"
63
+ class="category-tooltip-icon"
64
+ aria-describedby="manualTestTooltip"
65
+ >
66
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"
67
+ viewBox="0 0 14 14" fill="none" aria-hidden="true" focusable="false">
68
+ <path d="M6.3 11.2H7.7V9.8H6.3V11.2ZM7 0C3.136 0 0 3.136 0 7C0 10.864 3.136 14 7 14C10.864 14 14 10.864 14 7C14 3.136 10.864 0 7 0ZM7 12.6C3.913 12.6 1.4 10.087 1.4 7C1.4 3.913 3.913 1.4 7 1.4C10.087 1.4 12.6 3.913 12.6 7C12.6 10.087 10.087 12.6 7 12.6ZM7 2.8C5.453 2.8 4.2 4.053 4.2 5.6H5.6C5.6 4.83 6.23 4.2 7 4.2C7.77 4.2 8.4 4.83 8.4 5.6C8.4 7 6.3 6.825 6.3 9.1H7.7C7.7 7.525 9.8 7.35 9.8 5.6C9.8 4.053 8.547 2.8 7 2.8Z"
69
+ fill="#38268B"/>
70
+ </svg>
71
+ </button>
72
+
73
+ <span class="category-tooltip-bubble" id="manualTestTooltip" role="tooltip">
74
+ Manual Test highlights issues that require human testing using screen
75
+ readers and keyboards.
76
+ </span>
77
+ </span>
78
+
79
+ <span><%= items.needsReview.rules.length %> issues</span>
80
+ </div>
81
+ </div>
82
+ </div>
@@ -0,0 +1,33 @@
1
+ <div class="filter-bar">
2
+ <div class="filter-dropdowns">
3
+ <select id="severityFilter" class="filter-dropdown" aria-label="Filter by severity">
4
+ <option value="all">All Severity Groups</option>
5
+ <option value="mustFix">Must Fix</option>
6
+ <option value="goodToFix">Good to Fix</option>
7
+ <option value="needsReview">Manual Review</option>
8
+ </select>
9
+
10
+ <select id="criteriaFilter" class="filter-dropdown" aria-label="Filter by WCAG criteria">
11
+ <option value="all">All Failed Criteria</option>
12
+ <!-- Dynamically populated -->
13
+ </select>
14
+
15
+ <select id="disabilityFilter" class="filter-dropdown" aria-label="Filter by disability type">
16
+ <option value="all">All Disabilities</option>
17
+ <option value="Visual">Visual Disability</option>
18
+ <option value="Hearing">Hearing Disability</option>
19
+ <option value="Motor">Motor Disability</option>
20
+ <option value="Learning">Learning Disability</option>
21
+ </select>
22
+ </div>
23
+
24
+ <div class="search-container">
25
+ <input
26
+ type="text"
27
+ id="issuesSearchInput"
28
+ class="search-input"
29
+ placeholder="Search"
30
+ aria-label="Search issues"
31
+ />
32
+ </div>
33
+ </div>
@@ -0,0 +1,41 @@
1
+ <div class="issues-table-container" tabindex="0">
2
+ <table class="issues-table" id="issuesTable">
3
+ <thead>
4
+ <tr>
5
+ <th class="sortable" role="button" tabindex="0" aria-sort="none" style="width: 15%;">
6
+ <span>Severity</span>
7
+ <svg class="sort-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
8
+ <path d="M7 9L12 4L17 9H7Z" fill="currentColor" opacity="1" />
9
+ <path d="M7 15L12 20L17 15H7Z" fill="currentColor" opacity="0.3" />
10
+ </svg>
11
+ </th>
12
+ <th class="sortable" role="button" tabindex="0" aria-sort="none">
13
+ <span>Issue Name</span>
14
+ <svg class="sort-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
15
+ <path d="M7 9L12 4L17 9H7Z" fill="currentColor" opacity="0.3" />
16
+ <path d="M7 15L12 20L17 15H7Z" fill="currentColor" opacity="1" />
17
+ </svg>
18
+ </th>
19
+ <th class="sortable" role="button" tabindex="0" aria-sort="descending" style="width: 15%;">
20
+ <span>Occurrence</span>
21
+ <svg class="sort-icon" width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
22
+ <path d="M7 9L12 4L17 9H7Z" fill="currentColor" opacity="0.3" />
23
+ <path d="M7 15L12 20L17 15H7Z" fill="currentColor" opacity="1" />
24
+ </svg>
25
+ </th>
26
+ <th style="width: 30%;" aria-hidden="true"></th>
27
+ </tr>
28
+ </thead>
29
+ <tbody id="issuesTableBody">
30
+ <!-- Dynamically populated by script -->
31
+ </tbody>
32
+ </table>
33
+
34
+ <div id="emptyState" class="empty-state" style="display: none">
35
+ <p>No issues found matching your criteria.</p>
36
+ </div>
37
+
38
+ <div id="totalIssuesCount" class="total-issues-count">
39
+ <!-- Dynamically updated -->
40
+ </div>
41
+ </div>
@@ -0,0 +1,119 @@
1
+ <div id="site-header">
2
+ <div class="d-flex flex-column">
3
+ <div id="site-container">
4
+ <!-- Site Name -->
5
+ <div class="d-flex align-items-center gap-2">
6
+ <svg
7
+ width="24"
8
+ height="24"
9
+ viewBox="0 0 24 24"
10
+ fill="none"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ >
13
+ <path
14
+ d="M18.2222 4H5.77778C4.79111 4 4 4.8 4 5.77778V18.2222C4 19.2 4.79111 20 5.77778 20H18.2222C19.2 20 20 19.2 20 18.2222V5.77778C20 4.8 19.2089 4 18.2222 4ZM18.2222 18.2222H5.77778V7.55556H18.2222V18.2222ZM16.4444 12H7.55556V10.2222H16.4444V12ZM12.8889 15.5556H7.55556V13.7778H12.8889V15.5556Z"
15
+ fill="#686868"
16
+ />
17
+ </svg>
18
+ <div class="flex-grow-1">
19
+ <p class="label" id="siteName">N/A</p>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- Site URL -->
24
+ <div class="d-flex align-items-center gap-2">
25
+ <svg
26
+ width="24"
27
+ height="24"
28
+ viewBox="0 0 24 24"
29
+ fill="none"
30
+ xmlns="http://www.w3.org/2000/svg"
31
+ >
32
+ <g clip-path="url(#clip0_343_424)">
33
+ <path
34
+ d="M5.52 12C5.52 10.632 6.632 9.52 8 9.52H11.2V8H8C5.792 8 4 9.792 4 12C4 14.208 5.792 16 8 16H11.2V14.48H8C6.632 14.48 5.52 13.368 5.52 12ZM8.8 12.8H15.2V11.2H8.8V12.8ZM16 8H12.8V9.52H16C17.368 9.52 18.48 10.632 18.48 12C18.48 13.368 17.368 14.48 16 14.48H12.8V16H16C18.208 16 20 14.208 20 12C20 9.792 18.208 8 16 8Z"
35
+ fill="#686868"
36
+ />
37
+ </g>
38
+ <defs>
39
+ <clipPath id="clip0_343_424">
40
+ <rect width="24" height="24" fill="white" />
41
+ </clipPath>
42
+ </defs>
43
+ </svg>
44
+ <a
45
+ id="siteUrl"
46
+ href="#"
47
+ target="_blank"
48
+ rel="noopener noreferrer"
49
+ class="link d-flex align-items-center gap-1"
50
+ aria-label="Visit scanned page (opens in a new tab)"
51
+ ><span class="url-text link">N/A
52
+ <svg
53
+ width="24"
54
+ height="24"
55
+ viewBox="0 0 24 24"
56
+ fill="none"
57
+ xmlns="http://www.w3.org/2000/svg"
58
+ >
59
+ <g clip-path="url(#clip0_182_3910)">
60
+ <path
61
+ d="M16.6667 16.6667H7.33333V7.33333H12V6H7.33333C6.59333 6 6 6.6 6 7.33333V16.6667C6 17.4 6.59333 18 7.33333 18H16.6667C17.4 18 18 17.4 18 16.6667V12H16.6667V16.6667ZM13.3333 6V7.33333H15.7267L9.17333 13.8867L10.1133 14.8267L16.6667 8.27333V10.6667H18V6H13.3333Z"
62
+ fill="#5735DF"
63
+ />
64
+ </g>
65
+ <defs>
66
+ <clipPath id="clip0_182_3910">
67
+ <rect width="24" height="24" fill="white" />
68
+ </clipPath>
69
+ </defs>
70
+ </svg>
71
+ </span>
72
+ </a>
73
+ </div>
74
+
75
+ <!-- Scanned On -->
76
+ <div class="d-flex align-items-center gap-2">
77
+ <svg
78
+ width="24"
79
+ height="24"
80
+ viewBox="0 0 24 24"
81
+ fill="none"
82
+ xmlns="http://www.w3.org/2000/svg"
83
+ >
84
+ <g clip-path="url(#clip0_343_419)">
85
+ <path
86
+ d="M11.992 4C7.576 4 4 7.584 4 12C4 16.416 7.576 20 11.992 20C16.416 20 20 16.416 20 12C20 7.584 16.416 4 11.992 4ZM12 18.4C8.464 18.4 5.6 15.536 5.6 12C5.6 8.464 8.464 5.6 12 5.6C15.536 5.6 18.4 8.464 18.4 12C18.4 15.536 15.536 18.4 12 18.4Z"
87
+ fill="#686868"
88
+ />
89
+ <path d="M12.25 8H11V12.5902L15.375 15L16 14.059L12.25 12.0164V8Z" fill="#4E4E4E" />
90
+ </g>
91
+ <defs>
92
+ <clipPath id="clip0_343_419">
93
+ <rect width="24" height="24" fill="white" />
94
+ </clipPath>
95
+ </defs>
96
+ </svg>
97
+ <p id="siteScannedOn" class="label">N/A</p>
98
+ </div>
99
+ </div>
100
+
101
+ <!-- More Details Link -->
102
+ <div class="d-flex justify-content-between">
103
+ <button
104
+ type="button"
105
+ class="wcag-link p-0 border-0 bg-transparent link"
106
+ data-bs-toggle="modal"
107
+ data-bs-target="#aboutScanModal"
108
+ >
109
+ <span class="link">More details about this scan</span>
110
+ <svg width="8" height="10" viewBox="0 0 6 10" aria-hidden="true" focusable="false">
111
+ <path
112
+ d="M0.726562 7.06L3.7799 4L0.726562 0.94L1.66656 0L5.66656 4L1.66656 8L0.726562 7.06Z"
113
+ fill="#5735DF"
114
+ />
115
+ </svg>
116
+ </button>
117
+ </div>
118
+ </div>
119
+ </div>
@@ -0,0 +1,15 @@
1
+ <div id="aboutScanModal" class="modal fade" tabindex="-1" aria-labelledby="aboutScanModalLabel" aria-hidden="true">
2
+ <div class="modal-dialog modal-dialog-centered">
3
+ <div class="modal-content">
4
+ <div class="modal-header">
5
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
6
+ </div>
7
+ <div class="modal-body">
8
+ <div class="about-scan-modal-grid">
9
+ <%- include("ScanDetails") %>
10
+ <%- include("ScanConfiguration") %>
11
+ </div>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </div>
@@ -0,0 +1,44 @@
1
+ <section class="about-scan-details-right">
2
+ <section id="view-crawl" data-viewpane>
3
+ <h2 id="pagesScannedModalLabel" class="modal-title fw-bold">
4
+ NA
5
+ </h2>
6
+ <%- include('../../scannedPagesSegmentedTabs') %>
7
+ <div class="seg-panels">
8
+ <div id="pages-scanned" role="tabpanel">
9
+ <ul id="pagesScannedList" class="unbulleted-list">
10
+ <!-- dynamically populated -->
11
+ </ul>
12
+ </div>
13
+ <div id="pages-not-scanned" role="tabpanel" hidden>
14
+ <ul id="pagesNotScannedList" class="unbulleted-list">
15
+ <!-- dynamically populated -->
16
+ </ul>
17
+ </div>
18
+ <div id="pages-unsupported" role="tabpanel" hidden>
19
+ <ul id="unsupportedDocsList" class="unbulleted-list">
20
+ <!-- dynamically populated -->
21
+ </ul>
22
+ </div>
23
+ </div>
24
+ </section>
25
+
26
+ <section id="view-wcag" data-viewpane hidden>
27
+ <h2 class="modal-title fw-bold">
28
+ WCAG Automated Testing Coverage
29
+ </h2>
30
+ <p>
31
+ Only 20 WCAG 2.2 Success Criteria (A &amp; AA) can be checked reasonably through automated testing.
32
+ </p>
33
+ <p>
34
+ <a aria-label="Manual Testing" href="https://go.gov.sg/a11y-manual-testing" target="_blank" rel="noopener">
35
+ Manual Testing
36
+ <svg class="link-external-icon" width="16" height="12" viewBox="0 0 8 8" aria-hidden="true" focusable="false">
37
+ <path d="M7.11111 7.11111H0.888889V0.888889H4V0H0.888889C0.395556 0 0 0.4 0 0.888889V7.11111C0 7.6 0.395556 8 0.888889 8H7.11111C7.6 8 8 7.6 8 7.11111V4H7.11111V7.11111ZM4.88889 0V0.888889H6.48444L2.11556 5.25778L2.74222 5.88444L7.11111 1.51556V3.11111H8V0H4.88889Z" fill="#5735DF"/>
38
+ </svg>
39
+ </a> is still recommended as they involve subjective judgements and human interpretation, which cannot be fully automated.
40
+ </p>
41
+
42
+ <%- include('../../wcagCoverageDetails') %>
43
+ </section>
44
+ </section>
@@ -0,0 +1,142 @@
1
+ <aside id="scan-about" class="about-scan-details-left">
2
+ <h1>About this scan</h1>
3
+ <ul>
4
+ <li>
5
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
6
+ <path d="M14.2222 0H1.77778C0.791111 0 0 0.8 0 1.77778V14.2222C0 15.2 0.791111 16 1.77778 16H14.2222C15.2 16 16 15.2 16 14.2222V1.77778C16 0.8 15.2089 0 14.2222 0ZM14.2222 14.2222H1.77778V3.55556H14.2222V14.2222ZM12.4444 8H3.55556V6.22222H12.4444V8ZM8.88889 11.5556H3.55556V9.77778H8.88889V11.5556Z" fill="#686868"/>
7
+ </svg>
8
+ <span id="websiteTitle" class="website-title">
9
+ N/A
10
+ </span>
11
+ </li>
12
+
13
+ <li>
14
+ <svg width="16" height="16" viewBox="0 0 16 8" aria-hidden="true" focusable="false">
15
+ <path d="M1.52 4C1.52 2.632 2.632 1.52 4 1.52H7.2V0H4C1.792 0 0 1.792 0 4C0 6.208 1.792 8 4 8H7.2V6.48H4C2.632 6.48 1.52 5.368 1.52 4ZM4.8 4.8H11.2V3.2H4.8V4.8ZM12 0H8.8V1.52H12C13.368 1.52 14.48 2.632 14.48 4C14.48 5.368 13.368 6.48 12 6.48H8.8V8H12C14.208 8 16 6.208 16 4C16 1.792 14.208 0 12 0Z" fill="#686868"/>
16
+ </svg>
17
+ <a id="urlScanned" href="#" target="_blank" rel="noopener noreferrer">
18
+ <span id="urlScannedText" class="url-scanned-text">
19
+ N/A
20
+ </span>
21
+ <svg class="link-external-icon" width="16" height="12" viewBox="0 0 8 8" aria-hidden="true" focusable="false">
22
+ <path d="M7.11111 7.11111H0.888889V0.888889H4V0H0.888889C0.395556 0 0 0.4 0 0.888889V7.11111C0 7.6 0.395556 8 0.888889 8H7.11111C7.6 8 8 7.6 8 7.11111V4H7.11111V7.11111ZM4.88889 0V0.888889H6.48444L2.11556 5.25778L2.74222 5.88444L7.11111 1.51556V3.11111H8V0H4.88889Z" fill="#5735DF"/>
23
+ </svg>
24
+ </a>
25
+ </li>
26
+ <li>
27
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
28
+ <path d="M7.992 0C3.576 0 0 3.584 0 8C0 12.416 3.576 16 7.992 16C12.416 16 16 12.416 16 8C16 3.584 12.416 0 7.992 0ZM8 14.4C4.464 14.4 1.6 11.536 1.6 8C1.6 4.464 4.464 1.6 8 1.6C11.536 1.6 14.4 4.464 14.4 8C14.4 11.536 11.536 14.4 8 14.4Z" fill="#686868"/>
29
+ <path d="M8.25 4H7V8.59016L11.375 11L12 10.059L8.25 8.01639V4Z" fill="#4E4E4E"/>
30
+ </svg>
31
+ <span id="aboutStartTime">
32
+ N/A
33
+ </span>
34
+ </li>
35
+
36
+ <hr class="about-scan-divider" />
37
+
38
+ <% if (viewport !== null) { %>
39
+ <li>
40
+ <% if (viewport.startsWith('Desktop')) { %>
41
+ <svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
42
+ <path d="M16.3636 0H1.63636C0.736364 0 0 0.72 0 1.6V11.2C0 12.08 0.736364 12.8 1.63636 12.8H7.36364V14.4H5.72727V16H12.2727V14.4H10.6364V12.8H16.3636C17.2636 12.8 18 12.08 18 11.2V1.6C18 0.72 17.2636 0 16.3636 0ZM16.3636 11.2H1.63636V1.6H16.3636V11.2Z" fill="#686868"/>
43
+ </svg>
44
+ <% } else if (viewport.startsWith('CustomWidth')) { %>
45
+ <svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
46
+ <path d="M14.5455 9.33333H16V10.8889H14.5455V9.33333ZM14.5455 6.22222H16V7.77778H14.5455V6.22222ZM16 12.4444H14.5455V14C15.2727 14 16 13.2222 16 12.4444ZM8.72727 0H10.1818V1.55556H8.72727V0ZM14.5455 3.11111H16V4.66667H14.5455V3.11111ZM14.5455 0V1.55556H16C16 0.777778 15.2727 0 14.5455 0ZM0 3.11111H1.45455V4.66667H0V3.11111ZM11.6364 0H13.0909V1.55556H11.6364V0ZM11.6364 12.4444H13.0909V14H11.6364V12.4444ZM1.45455 0C0.727273 0 0 0.777778 0 1.55556H1.45455V0ZM5.81818 0H7.27273V1.55556H5.81818V0ZM2.90909 0H4.36364V1.55556H2.90909V0ZM0 6.22222V12.4444C0 13.3 0.654545 14 1.45455 14H10.1818V6.22222H0ZM1.45455 12.4444L3.27273 9.94778L4.57455 11.62L6.39273 9.11556L8.72727 12.4444H1.45455Z" fill="#686868"/>
47
+ </svg>
48
+ <% } else { %>
49
+ <svg width="16" height="16" viewBox="0 0 16 11" fill="none" xmlns="http://www.w3.org/2000/svg">
50
+ <path d="M2.66667 1.375H14.6667V0H2.66667C1.93333 0 1.33333 0.61875 1.33333 1.375V8.9375H0V11H9.33333V8.9375H2.66667V1.375ZM15.3333 2.75H11.3333C10.9667 2.75 10.6667 3.05938 10.6667 3.4375V10.3125C10.6667 10.6906 10.9667 11 11.3333 11H15.3333C15.7 11 16 10.6906 16 10.3125V3.4375C16 3.05938 15.7 2.75 15.3333 2.75ZM14.6667 8.9375H12V4.125H14.6667V8.9375Z" fill="#686868"/>
51
+ </svg>
52
+ <% } %>
53
+ <span id="viewport-text">
54
+ N/A
55
+ </span>
56
+ </li>
57
+ <% } %>
58
+
59
+ <li>
60
+ <svg width="16" height="16" viewBox="0 0 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
61
+ <path d="M13 14.072V4.8L8.125 0H1.625C0.73125 0 0.00812519 0.72 0.00812519 1.6L0 14.4C0 15.28 0.723125 16 1.61687 16H11.375C11.7406 16 12.0656 15.88 12.3419 15.68L8.7425 12.136C8.0925 12.552 7.32875 12.8 6.5 12.8C4.2575 12.8 2.4375 11.008 2.4375 8.8C2.4375 6.592 4.2575 4.8 6.5 4.8C8.7425 4.8 10.5625 6.592 10.5625 8.8C10.5625 9.616 10.3106 10.368 9.88813 11L13 14.072ZM4.0625 8.8C4.0625 10.128 5.15125 11.2 6.5 11.2C7.84875 11.2 8.9375 10.128 8.9375 8.8C8.9375 7.472 7.84875 6.4 6.5 6.4C5.15125 6.4 4.0625 7.472 4.0625 8.8Z" fill="#686868"/>
62
+ </svg>
63
+ <button
64
+ type="button"
65
+ class="js-view-btn about-scan-toggle"
66
+ aria-controls="view-crawl"
67
+ aria-selected="true"
68
+ >
69
+ <div class="d-flex">
70
+ <div class="about-scan-link-row">
71
+ <span id="pagesScannedModalToggleTxt" class="about-us-type-of-scan-text">N/A</span>
72
+ <svg class="about-scan-link-chev" width="10" height="12" viewBox="0 0 6 10" aria-hidden="true" focusable="false">
73
+ <path d="M0.726562 7.06L3.7799 4L0.726562 0.94L1.66656 0L5.66656 4L1.66656 8L0.726562 7.06Z" fill="#5735DF"/>
74
+ </svg>
75
+ </div>
76
+ </div>
77
+ </button>
78
+ </li>
79
+
80
+ <li class="advanced-group">
81
+ <svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
82
+ <path d="M13.2346 8.78333C13.2668 8.53333 13.2828 8.275 13.2828 8C13.2828 7.73333 13.2668 7.46667 13.2266 7.21667L14.857 5.9C15.0016 5.78333 15.0418 5.55833 14.9534 5.39167L13.4113 2.625C13.315 2.44167 13.1142 2.38333 12.9375 2.44167L11.0179 3.24167C10.6163 2.925 10.1906 2.65833 9.71675 2.45833L9.42761 0.341667C9.39548 0.141667 9.23485 0 9.04209 0H5.95791C5.76515 0 5.61255 0.141667 5.58042 0.341667L5.29128 2.45833C4.81741 2.65833 4.3837 2.93333 3.99015 3.24167L2.07057 2.44167C1.89387 2.375 1.69308 2.44167 1.5967 2.625L0.0626475 5.39167C-0.0337329 5.56667 -0.00160615 5.78333 0.159028 5.9L1.78946 7.21667C1.7493 7.46667 1.71718 7.74167 1.71718 8C1.71718 8.25833 1.73324 8.53333 1.7734 8.78333L0.142964 10.1C-0.00160614 10.2167 -0.0417645 10.4417 0.0465841 10.6083L1.58867 13.375C1.68505 13.5583 1.88584 13.6167 2.06254 13.5583L3.98212 12.7583C4.3837 13.075 4.80938 13.3417 5.28325 13.5417L5.57239 15.6583C5.61255 15.8583 5.76515 16 5.95791 16H9.04209C9.23485 16 9.39548 15.8583 9.41958 15.6583L9.70872 13.5417C10.1826 13.3417 10.6163 13.075 11.0099 12.7583L12.9294 13.5583C13.1061 13.625 13.3069 13.5583 13.4033 13.375L14.9454 10.6083C15.0418 10.425 15.0016 10.2167 14.849 10.1L13.2346 8.78333ZM7.5 11C5.90972 11 4.60859 9.65 4.60859 8C4.60859 6.35 5.90972 5 7.5 5C9.09028 5 10.3914 6.35 10.3914 8C10.3914 9.65 9.09028 11 7.5 11Z" fill="#686868"/>
83
+ </svg>
84
+
85
+ <div>
86
+ Advanced scan options enabled
87
+ </div>
88
+ </li>
89
+ <ul class="advanced-sublist">
90
+ <% if (advancedScanOptionsSummaryItems.showIncludeScreenshots) { %>
91
+ <li class="advanced-sublist-li">Include screenshots</li>
92
+ <% } %>
93
+ <% if (advancedScanOptionsSummaryItems.showAllowSubdomains) { %>
94
+ <li class="advanced-sublist-li">Allow subdomains for scans</li>
95
+ <% } %>
96
+ <% if (advancedScanOptionsSummaryItems.showEnableCustomChecks) { %>
97
+ <li class="advanced-sublist-li">Enable custom checks</li>
98
+ <% } %>
99
+ <% if (advancedScanOptionsSummaryItems.showEnableWcagAaa) { %>
100
+ <li class="advanced-sublist-li">Enable WCAG AAA checks</li>
101
+ <% } %>
102
+ <% if (advancedScanOptionsSummaryItems.showSlowScanMode) { %>
103
+ <li class="advanced-sublist-li">Slow scan mode</li>
104
+ <% } %>
105
+ <% if (advancedScanOptionsSummaryItems.showAdhereRobots) { %>
106
+ <li class="advanced-sublist-li">Adhere to robots.txt</li>
107
+ <% } %>
108
+ </ul>
109
+
110
+ <li>
111
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
112
+ <path d="M14.2222 1.77778V14.2222H1.77778V1.77778H14.2222ZM15.2 0H0.8C0.355556 0 0 0.355556 0 0.8V15.2C0 15.5556 0.355556 16 0.8 16H15.2C15.5556 16 16 15.5556 16 15.2V0.8C16 0.355556 15.5556 0 15.2 0ZM7.11111 3.55556H12.4444V5.33333H7.11111V3.55556ZM7.11111 7.11111H12.4444V8.88889H7.11111V7.11111ZM7.11111 10.6667H12.4444V12.4444H7.11111V10.6667ZM3.55556 3.55556H5.33333V5.33333H3.55556V3.55556ZM3.55556 7.11111H5.33333V8.88889H3.55556V7.11111ZM3.55556 10.6667H5.33333V12.4444H3.55556V10.6667Z" fill="#323232"/>
113
+ </svg>
114
+ <button
115
+ type="button"
116
+ class="js-view-btn about-scan-toggle"
117
+ aria-controls="view-wcag"
118
+ aria-selected="false"
119
+ >
120
+ <span class="wcag-criteria-label">
121
+ WCAG Automated Testing
122
+ </span>
123
+ <div class="about-scan-link-row">
124
+ <span class="about-scan-link-text">
125
+ 20 (A &amp; AA) and 6 (AAA) Criteria
126
+ </span>
127
+ <svg class="about-scan-link-chev" width="10" height="12" viewBox="0 0 6 10" aria-hidden="true" focusable="false">
128
+ <path d="M0.726562 7.06L3.7799 4L0.726562 0.94L1.66656 0L5.66656 4L1.66656 8L0.726562 7.06Z" fill="#5735DF"/>
129
+ </svg>
130
+ </div>
131
+ </button>
132
+ </li>
133
+
134
+ <hr class="about-scan-divider" />
135
+
136
+ <li>
137
+ <span id="oobeeAppVersion" class="oobee-version-text">N/A</span>
138
+ </li>
139
+
140
+ <li id = "cypressScanAboutMetadata"></li>
141
+ </ul>
142
+ </aside>
@@ -0,0 +1,36 @@
1
+ <div id="a11yIssueDetailCard" class="issue-detail-card" style="display: none">
2
+ <div class="issue-detail-content">
3
+ <!-- Image placeholder -->
4
+ <div class="issue-detail-image-container">
5
+ <img id="issueDetailImage" src="" alt="Issue illustration" class="issue-detail-image" />
6
+ </div>
7
+
8
+ <!-- Issue title -->
9
+ <h1 id="issueDetailTitle" class="issue-detail-title"></h1>
10
+
11
+ <!-- Conformance badges -->
12
+ <div class="issue-detail-conformance">
13
+ <span class="mustfix-badge-label">Must Fix</span>
14
+ <div id="issueDetailConformance" class="issue-detail-conformance-badges"></div>
15
+ </div>
16
+
17
+ <!-- Long description -->
18
+ <p id="issueDetailLongDescription"></p>
19
+
20
+ <!-- Disability impact -->
21
+ <div id="issueDetailDisabilitySection" class="issue-detail-disability" style="display: none">
22
+ <div id="issueDetailDisabilityMessage" class="info-alert" role="alert"></div>
23
+ </div>
24
+
25
+ <!-- View issue details button -->
26
+ <button id="viewIssueDetailsBtn" class="btn-view-issue-details">
27
+ View issue details
28
+ <svg width="8" height="10" viewBox="0 0 6 10" aria-hidden="true" focusable="false">
29
+ <path
30
+ d="M0.726562 7.06L3.7799 4L0.726562 0.94L1.66656 0L5.66656 4L1.66656 8L0.726562 7.06Z"
31
+ fill="#5735DF"
32
+ />
33
+ </svg>
34
+ </button>
35
+ </div>
36
+ </div>