@govtechsg/oobee 0.10.39 → 0.10.43

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/src/npmIndex.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import printMessage from 'print-message';
4
- import axe, { ImpactValue } from 'axe-core';
4
+ import axe, { AxeResults, ImpactValue } from 'axe-core';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { EnqueueStrategy } from 'crawlee';
7
7
  import constants, { BrowserTypes, RuleFlags, ScannerTypes } from './constants/constants.js';
@@ -16,7 +16,7 @@ import { createCrawleeSubFolders, filterAxeResults } from './crawlers/commonCraw
16
16
  import { createAndUpdateResultsFolders, createDetailsAndLogs } from './utils.js';
17
17
  import generateArtifacts from './mergeAxeResults.js';
18
18
  import { takeScreenshotForHTMLElements } from './screenshotFunc/htmlScreenshotFunc.js';
19
- import { silentLogger } from './logs.js';
19
+ import { consoleLogger, silentLogger } from './logs.js';
20
20
  import { alertMessageOptions } from './constants/cliFunctions.js';
21
21
  import { evaluateAltText } from './crawlers/custom/evaluateAltText.js';
22
22
  import { escapeCssSelector } from './crawlers/custom/escapeCssSelector.js';
@@ -24,7 +24,7 @@ import { framesCheck } from './crawlers/custom/framesCheck.js';
24
24
  import { findElementByCssSelector } from './crawlers/custom/findElementByCssSelector.js';
25
25
  import { getAxeConfiguration } from './crawlers/custom/getAxeConfiguration.js';
26
26
  import { flagUnlabelledClickableElements } from './crawlers/custom/flagUnlabelledClickableElements.js';
27
- import { xPathToCss } from './crawlers/custom/xPathToCss.js';
27
+ import xPathToCss from './crawlers/custom/xPathToCss.js';
28
28
  import { extractText } from './crawlers/custom/extractText.js';
29
29
  import { gradeReadability } from './crawlers/custom/gradeReadability.js';
30
30
 
@@ -65,7 +65,7 @@ export const init = async ({
65
65
  specifiedMaxConcurrency?: number;
66
66
  followRobots?: boolean;
67
67
  }) => {
68
- console.log('Starting Oobee');
68
+ consoleLogger.info('Starting Oobee');
69
69
 
70
70
  const [date, time] = new Date().toLocaleString('sv').replaceAll(/-|:/g, '').split(' ');
71
71
  const domain = new URL(entryUrl).hostname;
@@ -126,7 +126,7 @@ export const init = async ({
126
126
  const cssSelector = xPathToCss(xpath);
127
127
  return cssSelector;
128
128
  } catch (e) {
129
- console.error('Error converting XPath to CSS: ', xpath, e);
129
+ consoleLogger.error(`Error converting XPath to CSS: ${xpath} - ${e}`);
130
130
  return '';
131
131
  }
132
132
  })
@@ -197,7 +197,11 @@ export const init = async ({
197
197
  `;
198
198
  };
199
199
 
200
- const pushScanResults = async (res, metadata, elementsToClick) => {
200
+ const pushScanResults = async (
201
+ res: { pageUrl: string; pageTitle: string; axeScanResults: AxeResults },
202
+ metadata: string,
203
+ elementsToClick: string[],
204
+ ) => {
201
205
  throwErrorIfTerminated();
202
206
  if (includeScreenshots) {
203
207
  // use chrome by default
@@ -211,7 +215,7 @@ export const init = async ({
211
215
  await page.waitForLoadState('networkidle');
212
216
 
213
217
  // click on elements to reveal hidden elements so screenshots can be taken
214
- elementsToClick?.forEach(async elem => {
218
+ elementsToClick?.forEach(async (elem: string) => {
215
219
  try {
216
220
  await page.locator(elem).click();
217
221
  } catch (e) {
@@ -259,7 +263,7 @@ export const init = async ({
259
263
 
260
264
  const terminate = async () => {
261
265
  throwErrorIfTerminated();
262
- console.log('Stopping Oobee');
266
+ consoleLogger.info('Stopping Oobee');
263
267
  isInstanceTerminated = true;
264
268
  scanDetails.endTime = new Date();
265
269
  scanDetails.urlsCrawled = urlsCrawled;
@@ -7,14 +7,14 @@ import { Result } from 'axe-core';
7
7
  import { Page } from 'playwright';
8
8
  import { NodeResultWithScreenshot, ResultWithScreenshot } from '../crawlers/commonCrawlerFunc.js';
9
9
 
10
- const screenshotMap = {}; // Map of screenshot hashkey to its buffer value and screenshot path
10
+ const screenshotMap: Record<string, string> = {}; // Map of screenshot hashkey to its buffer value and screenshot path
11
11
 
12
12
  export const takeScreenshotForHTMLElements = async (
13
13
  violations: Result[],
14
14
  page: Page,
15
15
  randomToken: string,
16
16
  locatorTimeout = 2000,
17
- maxScreenshots = 50,
17
+ maxScreenshots = 100,
18
18
  ): Promise<ResultWithScreenshot[]> => {
19
19
  const newViolations: ResultWithScreenshot[] = [];
20
20
  let screenshotCount = 0;
@@ -75,30 +75,18 @@ export const takeScreenshotForHTMLElements = async (
75
75
  return newViolations;
76
76
  };
77
77
 
78
- const generateBufferHash = (buffer: Buffer) => {
78
+ const generateBufferHash = (buffer: Buffer): string => {
79
79
  const hash = createHash('sha256');
80
80
  hash.update(buffer);
81
81
  return hash.digest('hex');
82
82
  };
83
83
 
84
- const isSameBufferHash = (buffer: Buffer, hash: string) => {
85
- const bufferHash = generateBufferHash(buffer);
86
- return hash === bufferHash;
87
- };
88
-
89
- const getIdenticalScreenshotKey = (buffer: Buffer) => {
90
- for (const hashKey in screenshotMap) {
91
- const isIdentical = isSameBufferHash(buffer, hashKey);
92
- if (isIdentical) return hashKey;
93
- }
94
- return undefined;
95
- };
96
-
97
- const getScreenshotPath = (buffer: Buffer, randomToken: string) => {
98
- let hashKey = getIdenticalScreenshotKey(buffer);
84
+ const getScreenshotPath = (buffer: Buffer, randomToken: string): string => {
85
+ let hashKey = generateBufferHash(buffer);
86
+ const existingPath = screenshotMap[hashKey];
99
87
  // If exists identical entry in screenshot map, get its filepath
100
- if (hashKey) {
101
- return screenshotMap[hashKey];
88
+ if (existingPath) {
89
+ return existingPath;
102
90
  }
103
91
  // Create new entry in screenshot map
104
92
  hashKey = generateBufferHash(buffer);
@@ -1,3 +1,10 @@
1
+ // Monkey patch Path2D to avoid PDF.js crashing
2
+ (globalThis as any).Path2D = class {
3
+ constructor(_path?: string) {}
4
+ rect(_x: number, _y: number, _width: number, _height: number) {}
5
+ addPath(_path: any, _transform?: any) {}
6
+ };
7
+
1
8
  import _ from 'lodash';
2
9
  import { getDocument, PDFPageProxy } from 'pdfjs-dist';
3
10
  import fs from 'fs';
@@ -25,11 +32,36 @@ interface pathObject {
25
32
  annot?: number;
26
33
  }
27
34
 
35
+ // Use safe canvas to avoid Path2D issues
36
+ function createSafeCanvas(width: number, height: number) {
37
+ const canvas = createCanvas(width, height);
38
+ const ctx = canvas.getContext('2d');
39
+
40
+ // Patch clip/stroke/fill/etc. to skip if Path2D is passed
41
+ const wrapIgnorePath2D = (fn: Function) =>
42
+ function (...args: any[]) {
43
+ if (args.length > 0 && args[0] instanceof (globalThis as any).Path2D) {
44
+ // Skip the operation
45
+ return;
46
+ }
47
+ return fn.apply(this, args);
48
+ };
49
+
50
+ ctx.clip = wrapIgnorePath2D(ctx.clip);
51
+ ctx.fill = wrapIgnorePath2D(ctx.fill);
52
+ ctx.stroke = wrapIgnorePath2D(ctx.stroke);
53
+ ctx.isPointInPath = wrapIgnorePath2D(ctx.isPointInPath);
54
+ ctx.isPointInStroke = wrapIgnorePath2D(ctx.isPointInStroke);
55
+
56
+ return canvas;
57
+ }
58
+
59
+ // CanvasFactory for Node.js
28
60
  function NodeCanvasFactory() {}
29
61
  NodeCanvasFactory.prototype = {
30
62
  create: function NodeCanvasFactory_create(width: number, height: number) {
31
63
  assert(width > 0 && height > 0, 'Invalid canvas size');
32
- const canvas = createCanvas(width, height);
64
+ const canvas = createSafeCanvas(width, height);
33
65
  const context = canvas.getContext('2d');
34
66
  return {
35
67
  canvas,
@@ -69,6 +101,7 @@ export async function getPdfScreenshots(
69
101
  const newItems = _.cloneDeep(items);
70
102
  const loadingTask = getDocument({
71
103
  url: pdfFilePath,
104
+ canvasFactory,
72
105
  standardFontDataUrl: path.join(dirname, '../node_modules/pdfjs-dist/standard_fonts/'),
73
106
  disableFontFace: true,
74
107
  verbosity: 0,
@@ -0,0 +1,3 @@
1
+ declare module 'text-readability' {
2
+ function fleschReadingEase(text: string): number;
3
+ }
@@ -21,7 +21,7 @@ export type StructureTree = {
21
21
  pageIndex?: number;
22
22
  };
23
23
 
24
- type DeviceDescriptor = {
24
+ export type DeviceDescriptor = {
25
25
  viewport: ViewportSize;
26
26
  userAgent: string;
27
27
  deviceScaleFactor: number;