@govtechsg/oobee 0.10.20 → 0.10.28
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/.github/workflows/docker-test.yml +1 -1
- package/DETAILS.md +40 -25
- package/Dockerfile +41 -47
- package/LICENSE-3RD-PARTY-REPORT.txt +448 -0
- package/LICENSE-3RD-PARTY.txt +19913 -0
- package/README.md +26 -0
- package/__mocks__/mock-report.html +1503 -1360
- package/package.json +9 -5
- package/scripts/decodeUnzipParse.js +29 -0
- package/scripts/install_oobee_dependencies.command +2 -2
- package/scripts/install_oobee_dependencies.ps1 +3 -3
- package/src/cli.ts +9 -7
- package/src/combine.ts +13 -5
- package/src/constants/cliFunctions.ts +38 -1
- package/src/constants/common.ts +31 -5
- package/src/constants/constants.ts +28 -26
- package/src/constants/questions.ts +4 -1
- package/src/crawlers/commonCrawlerFunc.ts +114 -152
- package/src/crawlers/crawlDomain.ts +25 -32
- package/src/crawlers/crawlIntelligentSitemap.ts +7 -1
- package/src/crawlers/crawlLocalFile.ts +1 -1
- package/src/crawlers/crawlSitemap.ts +1 -1
- package/src/crawlers/custom/flagUnlabelledClickableElements.ts +546 -472
- package/src/crawlers/customAxeFunctions.ts +1 -1
- package/src/index.ts +2 -2
- package/src/mergeAxeResults.ts +590 -214
- package/src/screenshotFunc/pdfScreenshotFunc.ts +3 -3
- package/src/static/ejs/partials/components/scanAbout.ejs +65 -0
- package/src/static/ejs/partials/components/wcagCompliance.ejs +10 -29
- package/src/static/ejs/partials/footer.ejs +10 -13
- package/src/static/ejs/partials/scripts/categorySummary.ejs +2 -2
- package/src/static/ejs/partials/scripts/decodeUnzipParse.ejs +3 -0
- package/src/static/ejs/partials/scripts/reportSearch.ejs +1 -0
- package/src/static/ejs/partials/scripts/ruleOffcanvas.ejs +54 -52
- package/src/static/ejs/partials/scripts/scanAboutScript.ejs +38 -0
- package/src/static/ejs/partials/styles/styles.ejs +26 -1
- package/src/static/ejs/partials/summaryMain.ejs +15 -42
- package/src/static/ejs/report.ejs +22 -12
- package/src/utils.ts +10 -2
- package/src/xPathToCss.ts +186 -0
- package/a11y-scan-results.zip +0 -0
- package/src/types/xpath-to-css.d.ts +0 -3
package/package.json
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
{
|
2
2
|
"name": "@govtechsg/oobee",
|
3
3
|
"main": "dist/npmIndex.js",
|
4
|
-
"version": "0.10.
|
4
|
+
"version": "0.10.28",
|
5
5
|
"type": "module",
|
6
|
+
"author": "Government Technology Agency <info@tech.gov.sg>",
|
6
7
|
"dependencies": {
|
7
8
|
"@json2csv/node": "^7.0.3",
|
8
9
|
"@napi-rs/canvas": "^0.1.53",
|
9
10
|
"axe-core": "^4.10.2",
|
10
11
|
"axios": "^1.7.4",
|
12
|
+
"base64-stream": "^1.0.0",
|
11
13
|
"cheerio": "^1.0.0-rc.12",
|
12
14
|
"crawlee": "^3.11.1",
|
13
15
|
"ejs": "^3.1.9",
|
@@ -20,8 +22,8 @@
|
|
20
22
|
"lodash": "^4.17.21",
|
21
23
|
"mime-types": "^2.1.35",
|
22
24
|
"minimatch": "^9.0.3",
|
23
|
-
"pdfjs-dist": "github:veraPDF/pdfjs-dist#
|
24
|
-
"playwright": "1.
|
25
|
+
"pdfjs-dist": "github:veraPDF/pdfjs-dist#v4.4.168-taggedPdf-0.1.20",
|
26
|
+
"playwright": "1.49.1",
|
25
27
|
"prettier": "^3.1.0",
|
26
28
|
"print-message": "^3.0.1",
|
27
29
|
"safe-regex": "^2.1.1",
|
@@ -39,6 +41,7 @@
|
|
39
41
|
"devDependencies": {
|
40
42
|
"@eslint/eslintrc": "^3.0.2",
|
41
43
|
"@eslint/js": "^9.6.0",
|
44
|
+
"@types/base64-stream": "^1.0.5",
|
42
45
|
"@types/eslint__js": "^8.42.3",
|
43
46
|
"@types/fs-extra": "^11.0.4",
|
44
47
|
"@types/inquirer": "^9.0.7",
|
@@ -47,6 +50,7 @@
|
|
47
50
|
"@types/validator": "^13.11.10",
|
48
51
|
"@types/which": "^3.0.4",
|
49
52
|
"@types/xml2js": "^0.4.14",
|
53
|
+
"browserify-zlib": "^0.2.0",
|
50
54
|
"eslint": "^8.57.0",
|
51
55
|
"eslint-config-airbnb-base": "^15.0.0",
|
52
56
|
"eslint-config-prettier": "^8.6.0",
|
@@ -54,6 +58,7 @@
|
|
54
58
|
"eslint-plugin-prettier": "^5.0.0",
|
55
59
|
"globals": "^15.2.0",
|
56
60
|
"jest": "^29.7.0",
|
61
|
+
"readable-stream": "^4.7.0",
|
57
62
|
"typescript-eslint": "^8.3.0"
|
58
63
|
},
|
59
64
|
"overrides": {
|
@@ -82,7 +87,6 @@
|
|
82
87
|
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
83
88
|
"lint:fix": "eslint . --fix --report-unused-disable-directives --max-warnings 0"
|
84
89
|
},
|
85
|
-
"author": "",
|
86
90
|
"license": "MIT",
|
87
91
|
"description": "Oobee is a customisable, automated accessibility testing tool that allows software development teams to assess whether their products are user-friendly to persons with disabilities (PWDs).",
|
88
92
|
"repository": {
|
@@ -93,4 +97,4 @@
|
|
93
97
|
"url": "https://github.com/GovTechSG/oobee/issues"
|
94
98
|
},
|
95
99
|
"homepage": "https://github.com/GovTechSG/oobee#readme"
|
96
|
-
}
|
100
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/* Run the below command to get /src/static/ejs/partials/scripts/decodeUnzipParse.ejs */
|
2
|
+
/* ( echo '<script>'; npx browserify decodeUnzipParse.js --standalone decodeUnzipParse | npx uglify-js; echo '</script>' ) > decodeUnzipParse.ejs */
|
3
|
+
const { Readable } = require('readable-stream');
|
4
|
+
const { Base64Decode } = require('base64-stream');
|
5
|
+
const { createGunzip } = require('browserify-zlib');
|
6
|
+
const { parser } = require('stream-json');
|
7
|
+
const Asm = require('stream-json/Assembler');
|
8
|
+
|
9
|
+
module.exports = function parseGzipBase64Json(base64String) {
|
10
|
+
return new Promise((resolve, reject) => {
|
11
|
+
const pipeline = Readable.from([base64String])
|
12
|
+
.pipe(new Base64Decode())
|
13
|
+
.pipe(createGunzip())
|
14
|
+
.pipe(parser());
|
15
|
+
|
16
|
+
// Assembler to assemble the streamed JSON tokens
|
17
|
+
const assembler = Asm.connectTo(pipeline);
|
18
|
+
|
19
|
+
// On 'done' event, the entire JSON object is ready
|
20
|
+
assembler.on('done', asm => {
|
21
|
+
resolve(asm.current);
|
22
|
+
});
|
23
|
+
|
24
|
+
// If anything goes wrong at any stage, reject the promise
|
25
|
+
pipeline.on('error', err => {
|
26
|
+
reject(err);
|
27
|
+
});
|
28
|
+
});
|
29
|
+
};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
|
-
NODE_VERSION="
|
3
|
+
NODE_VERSION="22.13.1"
|
4
4
|
|
5
5
|
# Get current shell command
|
6
6
|
SHELL_COMMAND=$(ps -o comm= -p $$)
|
@@ -98,4 +98,4 @@ if [ "$(uname -m)" = "arm64" ] && /usr/bin/pgrep oahd >/dev/null 2>&1; then
|
|
98
98
|
fi
|
99
99
|
|
100
100
|
echo "Build TypeScript"
|
101
|
-
npm run build || true
|
101
|
+
npm run build || true
|
@@ -9,11 +9,11 @@ $ErrorActionPreference = 'Stop'
|
|
9
9
|
# Install NodeJS binaries
|
10
10
|
if (-Not (Test-Path nodejs-win\node.exe)) {
|
11
11
|
Write-Output "Downloading Node"
|
12
|
-
Invoke-WebRequest -o ./nodejs-win.zip "https://nodejs.org/dist/
|
12
|
+
Invoke-WebRequest -o ./nodejs-win.zip "https://nodejs.org/dist/v22.13.1/node-v22.13.1-win-x64.zip"
|
13
13
|
|
14
14
|
Write-Output "Unzip Node"
|
15
15
|
Expand-Archive .\nodejs-win.zip -DestinationPath .
|
16
|
-
Rename-Item node-
|
16
|
+
Rename-Item node-v22.13.1-win-x64 -NewName nodejs-win
|
17
17
|
Remove-Item -Force .\nodejs-win.zip
|
18
18
|
}
|
19
19
|
|
@@ -107,4 +107,4 @@ if (Test-Path oobee) {
|
|
107
107
|
} else {
|
108
108
|
Write-Output "Could not find oobee"
|
109
109
|
}
|
110
|
-
}
|
110
|
+
}
|
package/src/cli.ts
CHANGED
@@ -184,11 +184,10 @@ Usage: npm run cli -- -c <crawler> -d <device> -w <viewport> -u <url> OPTIONS`,
|
|
184
184
|
return option;
|
185
185
|
})
|
186
186
|
.check(argvs => {
|
187
|
-
if (
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
throw new Error('-p or --maxpages is only available in website and sitemap scans.');
|
187
|
+
if (argvs.scanner === ScannerTypes.CUSTOM && argvs.maxpages) {
|
188
|
+
throw new Error(
|
189
|
+
'-p or --maxpages is only available in website, sitemap and local file scans.',
|
190
|
+
);
|
192
191
|
}
|
193
192
|
return true;
|
194
193
|
})
|
@@ -209,6 +208,9 @@ const scanInit = async (argvs: Answers): Promise<string> => {
|
|
209
208
|
|
210
209
|
const updatedArgvs = { ...argvs };
|
211
210
|
|
211
|
+
// Cannot use data.browser and data.isHeadless as the connectivity check comes first before prepareData
|
212
|
+
setHeadlessMode(updatedArgvs.browserToRun, updatedArgvs.headless);
|
213
|
+
|
212
214
|
// let chromeDataDir = null;
|
213
215
|
// let edgeDataDir = null;
|
214
216
|
// Empty string for profile directory will use incognito mode in playwright
|
@@ -338,8 +340,6 @@ const scanInit = async (argvs: Answers): Promise<string> => {
|
|
338
340
|
}
|
339
341
|
}
|
340
342
|
|
341
|
-
setHeadlessMode(data.browser, data.isHeadless);
|
342
|
-
|
343
343
|
const screenToScan = getScreenToScan(
|
344
344
|
updatedArgvs.deviceChosen,
|
345
345
|
updatedArgvs.customDevice,
|
@@ -394,7 +394,9 @@ const optionsAnswer: Answers = {
|
|
394
394
|
blacklistedPatternsFilename: options.blacklistedPatternsFilename,
|
395
395
|
playwrightDeviceDetailsObject: options.playwrightDeviceDetailsObject,
|
396
396
|
ruleset: options.ruleset,
|
397
|
+
generateJsonFiles: options.generateJsonFiles,
|
397
398
|
};
|
399
|
+
|
398
400
|
await scanInit(optionsAnswer);
|
399
401
|
process.exit(0);
|
400
402
|
|
package/src/combine.ts
CHANGED
@@ -48,18 +48,19 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
48
48
|
maxRequestsPerCrawl,
|
49
49
|
browser,
|
50
50
|
userDataDirectory,
|
51
|
-
strategy,
|
52
|
-
specifiedMaxConcurrency,
|
51
|
+
strategy, // Allow subdomains: if checked, = 'same-domain'
|
52
|
+
specifiedMaxConcurrency, // Slow scan mode: if checked, = '1'
|
53
53
|
fileTypes,
|
54
54
|
blacklistedPatternsFilename,
|
55
|
-
includeScreenshots,
|
56
|
-
followRobots,
|
55
|
+
includeScreenshots, // Include screenshots: if checked, = 'true'
|
56
|
+
followRobots, // Adhere to robots.txt: if checked, = 'true'
|
57
57
|
metadata,
|
58
58
|
customFlowLabel = 'Custom Flow',
|
59
59
|
extraHTTPHeaders,
|
60
60
|
safeMode,
|
61
61
|
zip,
|
62
|
-
ruleset,
|
62
|
+
ruleset, // Enable custom checks, Enable WCAG AAA: if checked, = 'enable-wcag-aaa')
|
63
|
+
generateJsonFiles,
|
63
64
|
} = envDetails;
|
64
65
|
|
65
66
|
process.env.CRAWLEE_LOG_LEVEL = 'ERROR';
|
@@ -90,6 +91,12 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
90
91
|
crawlType: type,
|
91
92
|
requestUrl: finalUrl,
|
92
93
|
urlsCrawled: new UrlsCrawled(),
|
94
|
+
isIncludeScreenshots: envDetails.includeScreenshots,
|
95
|
+
isAllowSubdomains: envDetails.strategy,
|
96
|
+
isEnableCustomChecks: envDetails.ruleset,
|
97
|
+
isEnableWcagAaa: envDetails.ruleset,
|
98
|
+
isSlowScanMode: envDetails.specifiedMaxConcurrency,
|
99
|
+
isAdhereRobots: envDetails.followRobots,
|
93
100
|
};
|
94
101
|
|
95
102
|
const viewportSettings: ViewportSettingsClass = new ViewportSettingsClass(
|
@@ -214,6 +221,7 @@ const combineRun = async (details: Data, deviceToScan: string) => {
|
|
214
221
|
undefined,
|
215
222
|
scanDetails,
|
216
223
|
zip,
|
224
|
+
generateJsonFiles,
|
217
225
|
);
|
218
226
|
const [name, email] = nameEmail.split(':');
|
219
227
|
|
@@ -270,7 +270,9 @@ export const cliOptions: { [key: string]: Options } = {
|
|
270
270
|
coerce: option => {
|
271
271
|
const validChoices = Object.values(RuleFlags);
|
272
272
|
const userChoices: string[] = option.split(',');
|
273
|
-
const invalidUserChoices = userChoices.filter(
|
273
|
+
const invalidUserChoices = userChoices.filter(
|
274
|
+
choice => !validChoices.includes(choice as RuleFlags),
|
275
|
+
);
|
274
276
|
if (invalidUserChoices.length > 0) {
|
275
277
|
printMessage(
|
276
278
|
[
|
@@ -294,6 +296,41 @@ export const cliOptions: { [key: string]: Options } = {
|
|
294
296
|
return userChoices;
|
295
297
|
},
|
296
298
|
},
|
299
|
+
g: {
|
300
|
+
alias: 'generateJsonFiles',
|
301
|
+
describe: `Generate two gzipped and base64-encoded JSON files containing the results of the accessibility scan:\n
|
302
|
+
1. scanData.json.gz.b64: Provides an overview of the scan, including:
|
303
|
+
- WCAG compliance score
|
304
|
+
- Violated WCAG clauses
|
305
|
+
- Metadata (e.g., scan start and end times)
|
306
|
+
- Pages scanned and skipped
|
307
|
+
2. scanItems.json.gz.b64: Contains detailed information about detected accessibility issues, including:
|
308
|
+
- Severity levels
|
309
|
+
- Issue descriptions
|
310
|
+
- Related WCAG guidelines
|
311
|
+
- URL of the pages violated the WCAG clauses
|
312
|
+
Useful for in-depth analysis or integration with external reporting tools.\n
|
313
|
+
To obtain the JSON files, you need to base64-decode the file followed by gunzip. For example:\n
|
314
|
+
(macOS) base64 -D -i scanData.json.gz.b64 | gunzip > scanData.json\n
|
315
|
+
(linux) base64 -d scanData.json.gz.b64 | gunzip > scanData.json\n
|
316
|
+
`,
|
317
|
+
type: 'string',
|
318
|
+
requiresArg: true,
|
319
|
+
default: 'no',
|
320
|
+
demandOption: false,
|
321
|
+
coerce: value => {
|
322
|
+
const validYes = ['yes', 'y'];
|
323
|
+
const validNo = ['no', 'n'];
|
324
|
+
|
325
|
+
if (validYes.includes(value.toLowerCase())) {
|
326
|
+
return true;
|
327
|
+
}
|
328
|
+
if (validNo.includes(value.toLowerCase())) {
|
329
|
+
return false;
|
330
|
+
}
|
331
|
+
throw new Error(`Invalid value "${value}" for --generate. Use "yes", "y", "no", or "n".`);
|
332
|
+
},
|
333
|
+
},
|
297
334
|
};
|
298
335
|
|
299
336
|
export const configureReportSetting = (isEnabled: boolean): void => {
|
package/src/constants/common.ts
CHANGED
@@ -402,8 +402,16 @@ const checkUrlConnectivityWithBrowser = async (
|
|
402
402
|
let browserContext;
|
403
403
|
|
404
404
|
try {
|
405
|
+
// Temporary browserContextLaunchOptions to force headless mode during connectivity check
|
406
|
+
// If a user selects cli options h=no, the connectivity check should still proceed in headless
|
407
|
+
const launchOptions = getPlaywrightLaunchOptions(browserToRun);
|
408
|
+
const browserContextLaunchOptions = {
|
409
|
+
...launchOptions,
|
410
|
+
args: [...launchOptions.args, '--headless=new'],
|
411
|
+
};
|
412
|
+
|
405
413
|
browserContext = await constants.launcher.launchPersistentContext(clonedDataDir, {
|
406
|
-
...
|
414
|
+
...browserContextLaunchOptions,
|
407
415
|
...(viewport && { viewport }),
|
408
416
|
...(userAgent && { userAgent }),
|
409
417
|
...(extraHTTPHeaders && { extraHTTPHeaders }),
|
@@ -437,6 +445,7 @@ const checkUrlConnectivityWithBrowser = async (
|
|
437
445
|
silentLogger.info('Unable to detect networkidle');
|
438
446
|
}
|
439
447
|
|
448
|
+
// This response state doesn't seem to work with the new headless=new flag
|
440
449
|
if (response.status() === 401) {
|
441
450
|
res.status = constants.urlCheckStatuses.unauthorised.code;
|
442
451
|
} else {
|
@@ -459,8 +468,14 @@ const checkUrlConnectivityWithBrowser = async (
|
|
459
468
|
res.content = responseFromUrl.content;
|
460
469
|
}
|
461
470
|
} catch (error) {
|
462
|
-
|
463
|
-
|
471
|
+
|
472
|
+
// But this does work with the headless=new flag
|
473
|
+
if (error.message.includes('net::ERR_INVALID_AUTH_CREDENTIALS')) {
|
474
|
+
res.status = constants.urlCheckStatuses.unauthorised.code;
|
475
|
+
} else {
|
476
|
+
// enters here if input is not a URL or not using http/https protocols
|
477
|
+
res.status = constants.urlCheckStatuses.systemError.code;
|
478
|
+
}
|
464
479
|
} finally {
|
465
480
|
await browserContext.close();
|
466
481
|
}
|
@@ -579,6 +594,7 @@ export const prepareData = async (argv: Answers): Promise<Data> => {
|
|
579
594
|
safeMode,
|
580
595
|
zip,
|
581
596
|
ruleset,
|
597
|
+
generateJsonFiles,
|
582
598
|
} = argv;
|
583
599
|
|
584
600
|
// construct filename for scan results
|
@@ -625,6 +641,7 @@ export const prepareData = async (argv: Answers): Promise<Data> => {
|
|
625
641
|
safeMode,
|
626
642
|
zip,
|
627
643
|
ruleset,
|
644
|
+
generateJsonFiles,
|
628
645
|
};
|
629
646
|
};
|
630
647
|
|
@@ -1768,15 +1785,24 @@ export const getPlaywrightLaunchOptions = (browser?: string): LaunchOptions => {
|
|
1768
1785
|
if (browser) {
|
1769
1786
|
channel = browser;
|
1770
1787
|
}
|
1788
|
+
|
1789
|
+
// Set new headless mode as Chrome 132 does not support headless=old
|
1790
|
+
if (process.env.CRAWLEE_HEADLESS === '1') constants.launchOptionsArgs.push('--headless=new');
|
1791
|
+
|
1771
1792
|
const options: LaunchOptions = {
|
1772
1793
|
// Drop the --use-mock-keychain flag to allow MacOS devices
|
1773
1794
|
// to use the cloned cookies.
|
1774
|
-
ignoreDefaultArgs: ['--use-mock-keychain'],
|
1795
|
+
ignoreDefaultArgs: ['--use-mock-keychain', '--headless'],
|
1796
|
+
// necessary from Chrome 132 to use our own headless=new flag
|
1775
1797
|
args: constants.launchOptionsArgs,
|
1798
|
+
headless: false,
|
1776
1799
|
...(channel && { channel }), // Having no channel is equivalent to "chromium"
|
1777
1800
|
};
|
1801
|
+
|
1802
|
+
// Necessary as Chrome 132 does not support headless=old
|
1803
|
+
options.headless = false;
|
1804
|
+
|
1778
1805
|
if (proxy) {
|
1779
|
-
options.headless = false;
|
1780
1806
|
options.slowMo = 1000; // To ensure server-side rendered proxy page is loaded
|
1781
1807
|
} else if (browser === BrowserTypes.EDGE && os.platform() === 'win32') {
|
1782
1808
|
// edge should be in non-headless mode
|
@@ -304,33 +304,35 @@ export const sitemapPaths = [
|
|
304
304
|
'/sitemap_index.xml.xz',
|
305
305
|
];
|
306
306
|
|
307
|
+
// Remember to update getWcagPassPercentage() in src/utils/utils.ts if you change this
|
307
308
|
const wcagLinks = {
|
308
|
-
'WCAG 1.1.1': 'https://www.w3.org/TR/
|
309
|
-
'WCAG 1.2.2': 'https://www.w3.org/TR/
|
310
|
-
'WCAG 1.3.1': 'https://www.w3.org/TR/
|
311
|
-
// 'WCAG 1.3.4': 'https://www.w3.org/TR/
|
312
|
-
'WCAG 1.3.5': 'https://www.w3.org/TR/
|
313
|
-
'WCAG 1.4.1': 'https://www.w3.org/TR/
|
314
|
-
'WCAG 1.4.2': 'https://www.w3.org/TR/
|
315
|
-
'WCAG 1.4.3': 'https://www.w3.org/TR/
|
316
|
-
'WCAG 1.4.4': 'https://www.w3.org/TR/
|
317
|
-
'WCAG 1.4.6': 'https://www.w3.org/TR/
|
318
|
-
// 'WCAG 1.4.10': 'https://www.w3.org/TR/
|
319
|
-
'WCAG 1.4.12': 'https://www.w3.org/TR/
|
320
|
-
'WCAG 2.1.1': 'https://www.w3.org/TR/
|
321
|
-
'WCAG 2.2.1': 'https://www.w3.org/TR/
|
322
|
-
'WCAG 2.2.2': 'https://www.w3.org/TR/
|
323
|
-
'WCAG 2.2.4': 'https://www.w3.org/TR/
|
324
|
-
'WCAG 2.4.1': 'https://www.w3.org/TR/
|
325
|
-
'WCAG 2.4.2': 'https://www.w3.org/TR/
|
326
|
-
'WCAG 2.4.
|
327
|
-
'WCAG 2.4.
|
328
|
-
'WCAG 2.4.9': 'https://www.w3.org/TR/WCAG21/#link-purpose-link-only',
|
309
|
+
'WCAG 1.1.1': 'https://www.w3.org/TR/WCAG22/#non-text-content',
|
310
|
+
'WCAG 1.2.2': 'https://www.w3.org/TR/WCAG22/#captions-prerecorded',
|
311
|
+
'WCAG 1.3.1': 'https://www.w3.org/TR/WCAG22/#info-and-relationships',
|
312
|
+
// 'WCAG 1.3.4': 'https://www.w3.org/TR/WCAG22/#orientation', - TODO: review for veraPDF
|
313
|
+
'WCAG 1.3.5': 'https://www.w3.org/TR/WCAG22/#use-of-color',
|
314
|
+
'WCAG 1.4.1': 'https://www.w3.org/TR/WCAG22/#use-of-color',
|
315
|
+
'WCAG 1.4.2': 'https://www.w3.org/TR/WCAG22/#audio-control',
|
316
|
+
'WCAG 1.4.3': 'https://www.w3.org/TR/WCAG22/#contrast-minimum',
|
317
|
+
'WCAG 1.4.4': 'https://www.w3.org/TR/WCAG22/#resize-text',
|
318
|
+
'WCAG 1.4.6': 'https://www.w3.org/TR/WCAG22/#contrast-enhanced', // AAA
|
319
|
+
// 'WCAG 1.4.10': 'https://www.w3.org/TR/WCAG22/#reflow', - TODO: review for veraPDF
|
320
|
+
'WCAG 1.4.12': 'https://www.w3.org/TR/WCAG22/#text-spacing',
|
321
|
+
'WCAG 2.1.1': 'https://www.w3.org/TR/WCAG22/#pause-stop-hide',
|
322
|
+
'WCAG 2.2.1': 'https://www.w3.org/TR/WCAG22/#timing-adjustable',
|
323
|
+
'WCAG 2.2.2': 'https://www.w3.org/TR/WCAG22/#pause-stop-hide',
|
324
|
+
'WCAG 2.2.4': 'https://www.w3.org/TR/WCAG22/#interruptions', // AAA
|
325
|
+
'WCAG 2.4.1': 'https://www.w3.org/TR/WCAG22/#bypass-blocks',
|
326
|
+
'WCAG 2.4.2': 'https://www.w3.org/TR/WCAG22/#page-titled',
|
327
|
+
'WCAG 2.4.4': 'https://www.w3.org/TR/WCAG22/#link-purpose-in-context',
|
328
|
+
'WCAG 2.4.9': 'https://www.w3.org/TR/WCAG22/#link-purpose-link-only', // AAA
|
329
329
|
'WCAG 2.5.8': 'https://www.w3.org/TR/WCAG22/#target-size-minimum',
|
330
|
-
'WCAG 3.1.1': 'https://www.w3.org/TR/
|
331
|
-
'WCAG 3.1.2': 'https://www.w3.org/TR/
|
332
|
-
'WCAG 3.
|
333
|
-
'WCAG
|
330
|
+
'WCAG 3.1.1': 'https://www.w3.org/TR/WCAG22/#language-of-page',
|
331
|
+
'WCAG 3.1.2': 'https://www.w3.org/TR/WCAG22/#labels-or-instructions',
|
332
|
+
'WCAG 3.1.5': 'https://www.w3.org/TR/WCAG22/#reading-level', // AAA
|
333
|
+
'WCAG 3.2.5': 'https://www.w3.org/TR/WCAG22/#change-on-request', // AAA
|
334
|
+
'WCAG 3.3.2': 'https://www.w3.org/TR/WCAG22/#labels-or-instructions',
|
335
|
+
'WCAG 4.1.2': 'https://www.w3.org/TR/WCAG22/#name-role-value',
|
334
336
|
};
|
335
337
|
|
336
338
|
const urlCheckStatuses = {
|
@@ -423,7 +425,7 @@ export default {
|
|
423
425
|
};
|
424
426
|
|
425
427
|
export const rootPath = dirname;
|
426
|
-
export const wcagWebPage = 'https://www.w3.org/TR/
|
428
|
+
export const wcagWebPage = 'https://www.w3.org/TR/WCAG22/';
|
427
429
|
const latestAxeVersion = '4.9';
|
428
430
|
export const axeVersion = latestAxeVersion;
|
429
431
|
export const axeWebPage = `https://dequeuniversity.com/rules/axe/${latestAxeVersion}/`;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Question } from 'inquirer';
|
2
2
|
import { Answers } from '../index.js';
|
3
|
-
import { getUserDataTxt } from '../utils.js';
|
3
|
+
import { getUserDataTxt, setHeadlessMode } from '../utils.js';
|
4
4
|
import {
|
5
5
|
checkUrl,
|
6
6
|
deleteClonedProfiles,
|
@@ -79,6 +79,9 @@ const startScanQuestions = [
|
|
79
79
|
|
80
80
|
const statuses = constants.urlCheckStatuses;
|
81
81
|
const { browserToRun, clonedBrowserDataDir } = getBrowserToRun(BrowserTypes.CHROME);
|
82
|
+
|
83
|
+
setHeadlessMode(browserToRun, answers.headless);
|
84
|
+
|
82
85
|
const playwrightDeviceDetailsObject = getPlaywrightDeviceDetailsObject(
|
83
86
|
answers.deviceChosen,
|
84
87
|
answers.customDevice,
|