@govtechsg/oobee 0.10.72 → 0.10.74

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.
package/DETAILS.md CHANGED
@@ -130,6 +130,7 @@ Note: Level AAA are disabled by default. Please specify `enable-wcag-aaa` in ru
130
130
  | td-headers-attr | Ensure that each cell in a table that uses the headers attribute refers only to other cells in that table | Must Fix | WCAG 1.3.1 |
131
131
  | th-has-data-cells | Ensure that `<th>` elements and elements with role=columnheader/rowheader have data cells they describe | Must Fix | WCAG 1.3.1 |
132
132
  | video-caption | Ensures `<video>` elements have captions | Must Fix | WCAG 1.2.2 |
133
+ | summary-name | Ensure summary elements have discernible text | Must Fix | WCAG 4.1.2
133
134
  | oobee-confusing-alt-text | The image alt text set as 'img', 'image', 'picture', 'photo', or 'graphic' is confusing or not useful | Must Fix | WCAG 1.1.1
134
135
  | oobee-accessible-label | Clickable elements (i.e. elements with mouse-click interaction) must have accessible labels. | Must Fix | WCAG 2.1.1, WCAG 4.1.2 |
135
136
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@govtechsg/oobee",
3
3
  "main": "dist/npmIndex.js",
4
- "version": "0.10.72",
4
+ "version": "0.10.74",
5
5
  "type": "module",
6
6
  "author": "Government Technology Agency <info@tech.gov.sg>",
7
7
  "dependencies": {
@@ -10,7 +10,7 @@
10
10
  "@napi-rs/canvas": "^0.1.53",
11
11
  "@sentry/node": "^9.13.0",
12
12
  "@types/aws-sdk": "^0.0.42",
13
- "axe-core": "^4.10.3",
13
+ "axe-core": "^4.11.1",
14
14
  "axios": "^1.8.2",
15
15
  "base64-stream": "^1.0.0",
16
16
  "cheerio": "^1.0.0-rc.12",
@@ -75,7 +75,8 @@
75
75
  "tough-cookie": "^5.0.0-rc.2",
76
76
  "micromatch": "github:micromatch/micromatch.git#4.0.8",
77
77
  "brace-expansion": "^1.1.12",
78
- "tmp": "0.2.4"
78
+ "tmp": "0.2.4",
79
+ "tar": "^7.5.6"
79
80
  },
80
81
  "optionalDependencies": {
81
82
  "@napi-rs/canvas-darwin-arm64": "^0.1.53",
package/src/combine.ts CHANGED
@@ -328,4 +328,4 @@ const combineRun = async (details: Data, deviceToScan: string) => {
328
328
  }
329
329
  };
330
330
 
331
- export default combineRun;
331
+ export default combineRun;
@@ -1257,14 +1257,14 @@ const cloneChromeProfileCookieFiles = (options: GlobOptionsWithFileTypesFalse, d
1257
1257
  if (os.platform() === 'win32') {
1258
1258
  profileCookiesDir = globSync('**/Network/Cookies', {
1259
1259
  ...options,
1260
- ignore: ['oobee/**'],
1260
+ ignore: ['oobee*/**'],
1261
1261
  });
1262
1262
  profileNamesRegex = /User Data\\(.*?)\\Network/;
1263
1263
  } else if (os.platform() === 'darwin') {
1264
1264
  // maxDepth 2 to avoid copying cookies from the oobee directory if it exists
1265
1265
  profileCookiesDir = globSync('**/Cookies', {
1266
1266
  ...options,
1267
- ignore: 'oobee/**',
1267
+ ignore: 'oobee*/**',
1268
1268
  });
1269
1269
  profileNamesRegex = /Chrome\/(.*?)\/Cookies/;
1270
1270
  }
@@ -610,6 +610,7 @@ export const a11yRuleShortDescriptionMap = {
610
610
  'table-duplicate-name': 'Table caption and summary must not be identical',
611
611
  'meta-viewport': 'Pages must allow zoom and text scaling',
612
612
  'aria-allowed-role': 'Elements must use appropriate roles matching their actual behavior',
613
+ 'summary-name': 'Summary must have discernible text'
613
614
  };
614
615
 
615
616
  export const a11yRuleLongDescriptionMap = {
@@ -801,6 +802,7 @@ export const a11yRuleLongDescriptionMap = {
801
802
  'meta-viewport':
802
803
  'Pages must allow users to zoom in and scale text using their browser or pinch-to-zoom on mobile devices. Disabling zoom locks people with low vision out of being able to enlarge content to read them comfortably. The viewport meta tag should allow scaling and not restrict maximum zoom.',
803
804
  'aria-allowed-role': `Buttons, links, and interactive elements should behave the way they're marked. e.g., if something looks and acts like a button (performs an action), it should be labeled as a button. If it goes to a different page, it should be labeled as a link. When the label doesn't match the actual behavior, screen reader users get confused about what will happen when they click. When possible, use real buttons (<button>) and real links (<a>) instead of creating fake buttons or links from plain text and code.`,
805
+ 'summary-name': 'Ensure summary elements have discernible text that clearly indicates the topic or purpose of the information that will be revealed when using the summary control.'
804
806
  };
805
807
 
806
808
  export const disabilityBadgesMap = {
@@ -447,4 +447,4 @@ const crawlSitemap = async ({
447
447
  return { urlsCrawled, durationExceeded };
448
448
  };
449
449
 
450
- export default crawlSitemap;
450
+ export default crawlSitemap;
@@ -210,7 +210,7 @@ const writeCsv = async (allIssues, storagePath) => {
210
210
  scanCompletedAt: allIssues.endTime ? allIssues.endTime.toISOString() : '',
211
211
  severity: severity || '',
212
212
  issueId: issueId || '',
213
- issueDescription: issueDescription || '',
213
+ issueDescription: a11yRuleShortDescriptionMap[issueId] || issueDescription || '',
214
214
  wcagConformance: wcagConformance || '',
215
215
  url: url || '',
216
216
  pageTitle: affectedPage.pageTitle || 'No page title',
@@ -390,8 +390,37 @@ const writeHTML = async (
390
390
 
391
391
  outputStream.write(prefixData);
392
392
 
393
- outputStream.write(`let proxyUrl = "${process.env.PROXY_API_BASE_URL}"\n`);
393
+ // For Proxied AI environments only
394
+ outputStream.write(`let proxyUrl = "${process.env.PROXY_API_BASE_URL || ""}"\n`);
394
395
 
396
+ // Initialize GenAI feature flag
397
+ outputStream.write(`
398
+ // Fetch GenAI feature flag from backend
399
+ window.oobeeGenAiFeatureEnabled = false;
400
+ if (proxyUrl !== "" && proxyUrl !== undefined && proxyUrl !== null) {
401
+ (async () => {
402
+ try {
403
+ const featuresUrl = proxyUrl + '/api/ai/features';
404
+ const response = await fetch(featuresUrl, {
405
+ method: 'GET',
406
+ headers: { 'Accept': 'application/json' }
407
+ });
408
+ if (response.ok) {
409
+ const features = await response.json();
410
+ window.oobeeGenAiFeatureEnabled = features.genai_ui_enabled || false;
411
+ console.log('GenAI UI feature flag:', window.oobeeGenAiFeatureEnabled);
412
+ } else {
413
+ console.warn('Failed to fetch GenAI feature flag:', response.status);
414
+ }
415
+ } catch (error) {
416
+ console.warn('Error fetching GenAI feature flag:', error);
417
+ }
418
+ })();
419
+ } else {
420
+ console.warn('Skipping fetch GenAI feature as it is local report');
421
+ }
422
+ \n`)
423
+
395
424
  // outputStream.write("scanData = decompressJsonObject('");
396
425
  outputStream.write(
397
426
  "let scanDataPromise = (async () => { console.log('Loading scanData...'); scanData = await decodeUnzipParse('",
@@ -446,6 +475,13 @@ const writeSummaryHTML = async (
446
475
  fs.writeFileSync(`${storagePath}/${htmlFilename}.html`, html);
447
476
  };
448
477
 
478
+ const writeSitemap = async (pagesScanned: PageInfo[], storagePath: string) => {
479
+ const sitemapPath = path.join(storagePath, 'sitemap.txt');
480
+ const content = pagesScanned.map(p => p.url).join('\n');
481
+ await fs.writeFile(sitemapPath, content, { encoding: 'utf-8' });
482
+ consoleLogger.info(`Sitemap written to ${sitemapPath}`);
483
+ };
484
+
449
485
  const cleanUpJsonFiles = async (filesToDelete: string[]) => {
450
486
  consoleLogger.info('Cleaning up JSON files...');
451
487
  filesToDelete.forEach(file => {
@@ -1951,6 +1987,7 @@ const generateArtifacts = async (
1951
1987
  }
1952
1988
 
1953
1989
  await writeCsv(allIssues, storagePath);
1990
+ await writeSitemap(pagesScanned, storagePath);
1954
1991
  const {
1955
1992
  scanDataJsonFilePath,
1956
1993
  scanDataBase64FilePath,
@@ -2125,4 +2162,4 @@ export {
2125
2162
  formatAboutStartTime,
2126
2163
  };
2127
2164
 
2128
- export default generateArtifacts;
2165
+ export default generateArtifacts;
@@ -181,4 +181,4 @@ export const getS3UploadPrefix = (): string | null => {
181
181
  }
182
182
 
183
183
  return `users/${userId}/scans/${scanId}`;
184
- };
184
+ };
@@ -1,5 +1,5 @@
1
1
  <script>
2
- const a11yPlaygroundLink = 'https://a11y.tech.gov.sg/';
2
+ const a11yPlaygroundLink = 'https://a11y.tech.gov.sg/?utm_source=oobee';
3
3
 
4
4
  const whyItMatters = {
5
5
  accesskeys:
@@ -945,5 +945,13 @@
945
945
  review: `Test with keyboard: Try clicking buttons (Space or Enter), try clicking links (Enter). Use a screen reader to hear if it's announced as a button or link. Confirm what it announces matches what it actually does.`,
946
946
  learn: 'Review and learn more about this issue on A11y Playground',
947
947
  },
948
+ 'summary-name': {
949
+ check:
950
+ 'Find summary elements with and check if text is inside, or has a label (aria-label or title). Check: Do they convey meaningful information about the details?',
951
+ fix: '(Developer) If summary are missing text or labels, add one to provide context or explanation to the details.',
952
+ review:
953
+ 'Use a screen reader to navigate to the summary and ensure it is announced with clear information.',
954
+ learn: 'Review and learn more about this issue on A11y Playground',
955
+ },
948
956
  };
949
957
  </script>
@@ -147,6 +147,7 @@
147
147
 
148
148
  function createItemCard(item, isPurpleAiRule, oobeeAiQueryLabel, aiConfig) {
149
149
  const isGenAiSupportedRule = ['color-contrast', 'oobee-accessible-label', 'image-alt', 'listitem', 'link-in-text-block', 'target-size'].includes(aiConfig.ruleInCategory.rule);
150
+ const showGenAiUI = window.oobeeGenAiFeatureEnabled && isGenAiSupportedRule;
150
151
 
151
152
  return createElementFromString(`
152
153
  <div>
@@ -166,7 +167,7 @@
166
167
  : ''
167
168
  }
168
169
  ${isPurpleAiRule ? createAiSuggestionSection(item, oobeeAiQueryLabel, aiConfig) : ''}
169
- ${isGenAiSupportedRule ? createGenAiSuggestFixSection(item, aiConfig) : ''}
170
+ ${showGenAiUI ? createGenAiSuggestFixSection(item, aiConfig) : ''}
170
171
  </div>
171
172
  </div>
172
173
  `);