@govtechsg/oobee 0.10.20 → 0.10.21
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/README.md +18 -0
- package/package.json +2 -2
- package/src/cli.ts +6 -5
- package/src/combine.ts +13 -5
- package/src/constants/cliFunctions.ts +24 -1
- package/src/constants/common.ts +2 -0
- package/src/crawlers/crawlDomain.ts +1 -8
- package/src/index.ts +2 -0
- package/src/mergeAxeResults.ts +49 -23
- package/src/static/ejs/partials/components/scanAbout.ejs +65 -0
- package/src/static/ejs/partials/scripts/scanAboutScript.ejs +38 -0
- package/src/static/ejs/partials/styles/styles.ejs +22 -1
- package/src/static/ejs/report.ejs +1 -0
package/README.md
CHANGED
@@ -346,6 +346,24 @@ Options:
|
|
346
346
|
ks
|
347
347
|
[string] [choices: "default", "disable-oobee", "enable-wcag-aaa", "disable-oob
|
348
348
|
ee,enable-wcag-aaa"] [default: "default"]
|
349
|
+
-g, --generateJsonFiles Generate two JSON files containing the
|
350
|
+
results of the accessibility scan:
|
351
|
+
1. `scanData.json`: Provides an overview of
|
352
|
+
the scan, including:
|
353
|
+
- WCAG compliance score
|
354
|
+
- Violated WCAG clauses
|
355
|
+
- Metadata (e.g., scan start and end times)
|
356
|
+
- Pages scanned and skipped
|
357
|
+
2. `scanItems.json`: Contains detailed
|
358
|
+
information about detected accessibility
|
359
|
+
issues, including:
|
360
|
+
- Severity levels
|
361
|
+
- Issue descriptions
|
362
|
+
- Related WCAG guidelines
|
363
|
+
- URL of the pages violated the WCAG clauses
|
364
|
+
Useful for in-depth analysis or integration
|
365
|
+
with external reporting tools.
|
366
|
+
[string] [choices: "yes", "no"] [default: "no"]
|
349
367
|
|
350
368
|
Examples:
|
351
369
|
To scan sitemap of website:', 'npm run cli -- -c [ 1 | sitemap ] -u <url_lin
|
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.
|
4
|
+
"version": "0.10.21",
|
5
5
|
"type": "module",
|
6
6
|
"dependencies": {
|
7
7
|
"@json2csv/node": "^7.0.3",
|
@@ -93,4 +93,4 @@
|
|
93
93
|
"url": "https://github.com/GovTechSG/oobee/issues"
|
94
94
|
},
|
95
95
|
"homepage": "https://github.com/GovTechSG/oobee#readme"
|
96
|
-
}
|
96
|
+
}
|
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
|
})
|
@@ -394,7 +393,9 @@ const optionsAnswer: Answers = {
|
|
394
393
|
blacklistedPatternsFilename: options.blacklistedPatternsFilename,
|
395
394
|
playwrightDeviceDetailsObject: options.playwrightDeviceDetailsObject,
|
396
395
|
ruleset: options.ruleset,
|
396
|
+
generateJsonFiles: options.generateJsonFiles,
|
397
397
|
};
|
398
|
+
|
398
399
|
await scanInit(optionsAnswer);
|
399
400
|
process.exit(0);
|
400
401
|
|
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,27 @@ export const cliOptions: { [key: string]: Options } = {
|
|
294
296
|
return userChoices;
|
295
297
|
},
|
296
298
|
},
|
299
|
+
g: {
|
300
|
+
alias: 'generateJsonFiles',
|
301
|
+
describe:
|
302
|
+
'Generate JSON files in the results folder. Accepts "yes", "no", "y", or "n". Default is "no".',
|
303
|
+
type: 'string',
|
304
|
+
requiresArg: true,
|
305
|
+
default: 'no',
|
306
|
+
demandOption: false,
|
307
|
+
coerce: value => {
|
308
|
+
const validYes = ['yes', 'y'];
|
309
|
+
const validNo = ['no', 'n'];
|
310
|
+
|
311
|
+
if (validYes.includes(value.toLowerCase())) {
|
312
|
+
return true;
|
313
|
+
}
|
314
|
+
if (validNo.includes(value.toLowerCase())) {
|
315
|
+
return false;
|
316
|
+
}
|
317
|
+
throw new Error(`Invalid value "${value}" for --generate. Use "yes", "y", "no", or "n".`);
|
318
|
+
},
|
319
|
+
},
|
297
320
|
};
|
298
321
|
|
299
322
|
export const configureReportSetting = (isEnabled: boolean): void => {
|
package/src/constants/common.ts
CHANGED
@@ -579,6 +579,7 @@ export const prepareData = async (argv: Answers): Promise<Data> => {
|
|
579
579
|
safeMode,
|
580
580
|
zip,
|
581
581
|
ruleset,
|
582
|
+
generateJsonFiles,
|
582
583
|
} = argv;
|
583
584
|
|
584
585
|
// construct filename for scan results
|
@@ -625,6 +626,7 @@ export const prepareData = async (argv: Answers): Promise<Data> => {
|
|
625
626
|
safeMode,
|
626
627
|
zip,
|
627
628
|
ruleset,
|
629
|
+
generateJsonFiles,
|
628
630
|
};
|
629
631
|
};
|
630
632
|
|
@@ -581,18 +581,11 @@ const crawlDomain = async ({
|
|
581
581
|
},
|
582
582
|
]
|
583
583
|
: [
|
584
|
-
async (
|
585
|
-
const { page, request } = crawlingContext;
|
586
|
-
|
584
|
+
async ({ page, request }) => {
|
587
585
|
await page.setExtraHTTPHeaders({
|
588
586
|
...extraHTTPHeaders,
|
589
587
|
});
|
590
588
|
|
591
|
-
Object.assign(gotoOptions, {
|
592
|
-
waitUntil: 'networkidle',
|
593
|
-
timeout: 30000,
|
594
|
-
});
|
595
|
-
|
596
589
|
const processible = await isProcessibleUrl(request.url);
|
597
590
|
if (!processible) {
|
598
591
|
request.skipNavigation = true;
|
package/src/index.ts
CHANGED
@@ -51,6 +51,7 @@ export type Answers = {
|
|
51
51
|
exportDirectory: string;
|
52
52
|
zip: string;
|
53
53
|
ruleset: RuleFlags[];
|
54
|
+
generateJsonFiles: boolean;
|
54
55
|
};
|
55
56
|
|
56
57
|
export type Data = {
|
@@ -80,6 +81,7 @@ export type Data = {
|
|
80
81
|
userDataDirectory?: string;
|
81
82
|
zip?: string;
|
82
83
|
ruleset: RuleFlags[];
|
84
|
+
generateJsonFiles: boolean;
|
83
85
|
};
|
84
86
|
|
85
87
|
const userData = getUserDataTxt();
|
package/src/mergeAxeResults.ts
CHANGED
@@ -24,8 +24,6 @@ import { consoleLogger, silentLogger } from './logs.js';
|
|
24
24
|
import itemTypeDescription from './constants/itemTypeDescription.js';
|
25
25
|
import { oobeeAiHtmlETL, oobeeAiRules } from './constants/oobeeAi.js';
|
26
26
|
|
27
|
-
const cwd = process.cwd();
|
28
|
-
|
29
27
|
export type ItemsInfo = {
|
30
28
|
html: string;
|
31
29
|
message: string;
|
@@ -84,10 +82,12 @@ type AllIssues = {
|
|
84
82
|
cypressScanAboutMetadata: string;
|
85
83
|
wcagLinks: { [key: string]: string };
|
86
84
|
[key: string]: any;
|
85
|
+
advancedScanOptionsSummaryItems: { [key: string]: boolean };
|
87
86
|
};
|
88
87
|
|
89
88
|
const filename = fileURLToPath(import.meta.url);
|
90
89
|
const dirname = path.dirname(filename);
|
90
|
+
const BUFFER_LIMIT = 100 * 1024 * 1024; // 100MB size
|
91
91
|
|
92
92
|
const extractFileNames = async (directory: string): Promise<string[]> => {
|
93
93
|
ensureDirSync(directory);
|
@@ -214,8 +214,8 @@ const compileHtmlWithEJS = async (allIssues, storagePath, htmlFilename = 'report
|
|
214
214
|
const injectScript = `
|
215
215
|
<script>
|
216
216
|
try {
|
217
|
-
const base64DecodeChunkedWithDecoder = (data, chunkSize =
|
218
|
-
const encodedChunks = data.split('
|
217
|
+
const base64DecodeChunkedWithDecoder = (data, chunkSize = ${BUFFER_LIMIT}) => {
|
218
|
+
const encodedChunks = data.split('|');
|
219
219
|
const decoder = new TextDecoder();
|
220
220
|
const jsonParts = [];
|
221
221
|
|
@@ -259,7 +259,7 @@ const splitHtmlAndCreateFiles = async (htmlFilePath, storagePath) => {
|
|
259
259
|
throw new Error('Marker comment not found in the HTML file.');
|
260
260
|
}
|
261
261
|
|
262
|
-
const topContent = htmlContent.slice(0, splitIndex + splitMarker.length)
|
262
|
+
const topContent = `${htmlContent.slice(0, splitIndex + splitMarker.length)}\n\n`;
|
263
263
|
const bottomContent = htmlContent.slice(splitIndex + splitMarker.length);
|
264
264
|
|
265
265
|
const topFilePath = path.join(storagePath, 'report-partial-top.htm.txt');
|
@@ -293,8 +293,6 @@ const writeHTML = async (allIssues, storagePath, htmlFilename = 'report') => {
|
|
293
293
|
|
294
294
|
outputStream.write(prefixData);
|
295
295
|
|
296
|
-
// Create a readable stream for the input file with a highWaterMark set to 10MB
|
297
|
-
const BUFFER_LIMIT = 10 * 1024 * 1024; // 10 MB
|
298
296
|
const inputStream = fs.createReadStream(inputFilePath, {
|
299
297
|
encoding: 'utf-8',
|
300
298
|
highWaterMark: BUFFER_LIMIT,
|
@@ -457,15 +455,14 @@ function writeLargeJsonToFile(obj, filePath) {
|
|
457
455
|
});
|
458
456
|
}
|
459
457
|
|
460
|
-
const base64Encode = async (data, num) => {
|
458
|
+
const base64Encode = async (data, num, storagePath, generateJsonFiles) => {
|
461
459
|
try {
|
462
|
-
const
|
460
|
+
const tempFilePath =
|
463
461
|
num === 1
|
464
|
-
?
|
462
|
+
? path.join(storagePath, 'scanItems.json')
|
465
463
|
: num === 2
|
466
|
-
?
|
467
|
-
: `${uuidv4()}.json
|
468
|
-
const tempFilePath = path.join(process.cwd(), tempFilename);
|
464
|
+
? path.join(storagePath, 'scanData.json')
|
465
|
+
: path.join(storagePath, `${uuidv4()}.json`);
|
469
466
|
|
470
467
|
await writeLargeJsonToFile(data, tempFilePath);
|
471
468
|
|
@@ -473,16 +470,28 @@ const base64Encode = async (data, num) => {
|
|
473
470
|
const outputFilePath = path.join(process.cwd(), outputFilename);
|
474
471
|
|
475
472
|
try {
|
476
|
-
const CHUNK_SIZE = 1024 * 1024; // 1MB chunks
|
477
473
|
const readStream = fs.createReadStream(tempFilePath, {
|
478
474
|
encoding: 'utf8',
|
479
|
-
highWaterMark:
|
475
|
+
highWaterMark: BUFFER_LIMIT,
|
480
476
|
});
|
481
477
|
const writeStream = fs.createWriteStream(outputFilePath, { encoding: 'utf8' });
|
482
478
|
|
479
|
+
let previousChunk = null;
|
480
|
+
|
483
481
|
for await (const chunk of readStream) {
|
484
482
|
const encodedChunk = Buffer.from(chunk).toString('base64');
|
485
|
-
|
483
|
+
|
484
|
+
if (previousChunk !== null) {
|
485
|
+
// Note: Notice the pipe symbol `|`, it is intended to be here as a delimiter
|
486
|
+
// for the scenario where there are chunking happens
|
487
|
+
writeStream.write(`${previousChunk}|`);
|
488
|
+
}
|
489
|
+
|
490
|
+
previousChunk = encodedChunk;
|
491
|
+
}
|
492
|
+
|
493
|
+
if (previousChunk !== null) {
|
494
|
+
writeStream.write(previousChunk);
|
486
495
|
}
|
487
496
|
|
488
497
|
await new Promise((resolve, reject) => {
|
@@ -492,9 +501,11 @@ const base64Encode = async (data, num) => {
|
|
492
501
|
|
493
502
|
return outputFilePath;
|
494
503
|
} finally {
|
495
|
-
|
496
|
-
.
|
497
|
-
|
504
|
+
if (!generateJsonFiles) {
|
505
|
+
await fs.promises
|
506
|
+
.unlink(tempFilePath)
|
507
|
+
.catch(err => console.error('Temp file delete error:', err));
|
508
|
+
}
|
498
509
|
}
|
499
510
|
} catch (error) {
|
500
511
|
console.error('Error encoding data to Base64:', error);
|
@@ -520,10 +531,10 @@ const streamEncodedDataToFile = async (inputFilePath, writeStream, appendComma)
|
|
520
531
|
}
|
521
532
|
};
|
522
533
|
|
523
|
-
const writeBase64 = async (allIssues, storagePath) => {
|
534
|
+
const writeBase64 = async (allIssues, storagePath, generateJsonFiles) => {
|
524
535
|
const { items, ...rest } = allIssues;
|
525
|
-
const encodedScanItemsPath = await base64Encode(items, 1);
|
526
|
-
const encodedScanDataPath = await base64Encode(rest, 2);
|
536
|
+
const encodedScanItemsPath = await base64Encode(items, 1, storagePath, generateJsonFiles);
|
537
|
+
const encodedScanDataPath = await base64Encode(rest, 2, storagePath, generateJsonFiles);
|
527
538
|
|
528
539
|
const filePath = path.join(storagePath, 'scanDetails.csv');
|
529
540
|
const directoryPath = path.dirname(filePath);
|
@@ -766,6 +777,7 @@ const generateArtifacts = async (
|
|
766
777
|
cypressScanAboutMetadata,
|
767
778
|
scanDetails,
|
768
779
|
zip = undefined, // optional
|
780
|
+
generateJsonFiles = false,
|
769
781
|
) => {
|
770
782
|
const intermediateDatasetsPath = `${randomToken}/datasets/${randomToken}`;
|
771
783
|
const phAppVersion = getVersion();
|
@@ -835,6 +847,17 @@ const generateArtifacts = async (
|
|
835
847
|
},
|
836
848
|
cypressScanAboutMetadata,
|
837
849
|
wcagLinks: constants.wcagLinks,
|
850
|
+
// Populate boolean values for id="advancedScanOptionsSummary"
|
851
|
+
advancedScanOptionsSummaryItems: {
|
852
|
+
showIncludeScreenshots: [true].includes(scanDetails.isIncludeScreenshots),
|
853
|
+
showAllowSubdomains: [true].includes(scanDetails.isAllowSubdomains),
|
854
|
+
showEnableCustomChecks: ['default', 'enable-wcag-aaa'].includes(
|
855
|
+
scanDetails.isEnableCustomChecks?.[0],
|
856
|
+
),
|
857
|
+
showEnableWcagAaa: (scanDetails.isEnableWcagAaa || []).includes('enable-wcag-aaa'),
|
858
|
+
showSlowScanMode: [1].includes(scanDetails.isSlowScanMode),
|
859
|
+
showAdhereRobots: [true].includes(scanDetails.isAdhereRobots),
|
860
|
+
},
|
838
861
|
};
|
839
862
|
|
840
863
|
const allFiles = await extractFileNames(intermediateDatasetsPath);
|
@@ -870,6 +893,9 @@ const generateArtifacts = async (
|
|
870
893
|
}
|
871
894
|
|
872
895
|
allIssues.wcagPassPercentage = getWcagPassPercentage(allIssues.wcagViolations);
|
896
|
+
consoleLogger.info(
|
897
|
+
`advancedScanOptionsSummaryItems is ${allIssues.advancedScanOptionsSummaryItems}`,
|
898
|
+
);
|
873
899
|
|
874
900
|
const getAxeImpactCount = (allIssues: AllIssues) => {
|
875
901
|
const impactCount = {
|
@@ -908,7 +934,7 @@ const generateArtifacts = async (
|
|
908
934
|
}
|
909
935
|
|
910
936
|
await writeCsv(allIssues, storagePath);
|
911
|
-
await writeBase64(allIssues, storagePath);
|
937
|
+
await writeBase64(allIssues, storagePath, generateJsonFiles);
|
912
938
|
await writeSummaryHTML(allIssues, storagePath);
|
913
939
|
await writeHTML(allIssues, storagePath);
|
914
940
|
await retryFunction(() => writeSummaryPdf(storagePath, pagesScanned.length), 1);
|
@@ -225,6 +225,71 @@
|
|
225
225
|
</svg>
|
226
226
|
</span>
|
227
227
|
</li>
|
228
|
+
|
229
|
+
<li>
|
230
|
+
<svg aria-label="Advanced options scan summary" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
231
|
+
<rect x="0.87" y="0.87" width="16.26" height="16.26" rx="3.13" stroke="#93928D" stroke-width="1.74"/>
|
232
|
+
<path d="M5 9L7.5 11.5L13 6" stroke="#93928D" stroke-width="1.74" stroke-linecap="square"/>
|
233
|
+
</svg>
|
234
|
+
<div>
|
235
|
+
<div class="d-flex flex-row justify-content-center align-items-center gap-3">
|
236
|
+
<button id="advancedScanOptionsSummaryTitle" onclick="toggleAdvanceScanSummary()">Advanced scan summary
|
237
|
+
</button>
|
238
|
+
</div>
|
239
|
+
|
240
|
+
</div>
|
241
|
+
</li>
|
242
|
+
<ul id="advancedScanOptionsSummary" class="d-none mb-3">
|
243
|
+
<li id="showIncludeScreenshots"class="d-flex flex-row">
|
244
|
+
<svg aria-label="Include screenshots was checked" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
245
|
+
<rect x="0.87" y="0.87" width="16.26" height="16.26" rx="3.13" fill="#93928D" stroke="#93928D" stroke-width="1.74"/>
|
246
|
+
<path d="M5 9L7.5 11.5L13 6" fill="none" stroke="#ffffff" stroke-width="1.74" stroke-linecap="square"/>
|
247
|
+
</svg>
|
248
|
+
<div>Include screenshots
|
249
|
+
</div>
|
250
|
+
</li>
|
251
|
+
<li id="showAllowSubdomains"class="d-flex flex-row">
|
252
|
+
<svg aria-label="Allow subdomains was checked" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
253
|
+
<rect x="0.87" y="0.87" width="16.26" height="16.26" rx="3.13" fill="#93928D" stroke="#93928D" stroke-width="1.74"/>
|
254
|
+
<path d="M5 9L7.5 11.5L13 6" fill="none" stroke="#ffffff" stroke-width="1.74" stroke-linecap="square"/>
|
255
|
+
</svg>
|
256
|
+
<div>Allow subdomains for scans
|
257
|
+
</div>
|
258
|
+
</li>
|
259
|
+
<li id="showEnableCustomChecks"class="d-flex flex-row">
|
260
|
+
<svg aria-label="Enable custom checks was checked" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
261
|
+
<rect x="0.87" y="0.87" width="16.26" height="16.26" rx="3.13" fill="#93928D" stroke="#93928D" stroke-width="1.74"/>
|
262
|
+
<path d="M5 9L7.5 11.5L13 6" fill="none" stroke="#ffffff" stroke-width="1.74" stroke-linecap="square"/>
|
263
|
+
</svg>
|
264
|
+
<div>Enable custom checks
|
265
|
+
</div>
|
266
|
+
</li>
|
267
|
+
<li id="showEnableWcagAaa"class="d-flex flex-row">
|
268
|
+
<svg aria-label="Enable WCAG AAA checks was checked" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
269
|
+
<rect x="0.87" y="0.87" width="16.26" height="16.26" rx="3.13" fill="#93928D" stroke="#93928D" stroke-width="1.74"/>
|
270
|
+
<path d="M5 9L7.5 11.5L13 6" fill="none" stroke="#ffffff" stroke-width="1.74" stroke-linecap="square"/>
|
271
|
+
</svg>
|
272
|
+
<div>Enable WCAG AAA checks
|
273
|
+
</div>
|
274
|
+
</li>
|
275
|
+
<li id="showSlowScanMode"class="d-flex flex-row">
|
276
|
+
<svg aria-label="Slow scan mode was checked" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
277
|
+
<rect x="0.87" y="0.87" width="16.26" height="16.26" rx="3.13" fill="#93928D" stroke="#93928D" stroke-width="1.74"/>
|
278
|
+
<path d="M5 9L7.5 11.5L13 6" fill="none" stroke="#ffffff" stroke-width="1.74" stroke-linecap="square"/>
|
279
|
+
</svg>
|
280
|
+
<div>Slow scan mode
|
281
|
+
</div>
|
282
|
+
</li>
|
283
|
+
|
284
|
+
<li id="showAdhereRobots"class="d-flex flex-row">
|
285
|
+
<svg aria-label="Adhere to robots.txt was checked" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
286
|
+
<rect x="0.87" y="0.87" width="16.26" height="16.26" rx="3.13" fill="#93928D" stroke="#93928D" stroke-width="1.74"/>
|
287
|
+
<path d="M5 9L7.5 11.5L13 6" fill="none" stroke="#ffffff" stroke-width="1.74" stroke-linecap="square"/>
|
288
|
+
</svg>
|
289
|
+
<div>Adhere to robots.txt
|
290
|
+
</div>
|
291
|
+
</li>
|
292
|
+
</ul>
|
228
293
|
<li>
|
229
294
|
<svg
|
230
295
|
aria-label="Scan engine"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<%# functions to handle interaction and ui for advancedScanOptionsSummary in scanAbout.ejs.
|
2
|
+
component %>
|
3
|
+
|
4
|
+
<script>
|
5
|
+
let optionsToCheck = scanData.advancedScanOptionsSummaryItems;
|
6
|
+
|
7
|
+
document.querySelectorAll('#advancedScanOptionsSummary li').forEach(liElement => {
|
8
|
+
liElement.classList.add('d-none');
|
9
|
+
});
|
10
|
+
|
11
|
+
function toggleAdvanceScanSummary() {
|
12
|
+
const chevron = document.getElementById('advancedScanOptionsSummaryTitle');
|
13
|
+
const advancedScanOptionsSummary = document.getElementById('advancedScanOptionsSummary');
|
14
|
+
|
15
|
+
const isHidden = advancedScanOptionsSummary.classList.toggle('d-none');
|
16
|
+
|
17
|
+
chevron.classList.toggle('chevron-rotated', !isHidden);
|
18
|
+
|
19
|
+
if (!isHidden) {
|
20
|
+
showScanOptions(optionsToCheck);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
function showScanOptions(options) {
|
25
|
+
document.querySelectorAll('#advancedScanOptionsSummary li').forEach(liElement => {
|
26
|
+
liElement.classList.add('d-none');
|
27
|
+
});
|
28
|
+
|
29
|
+
for (const key in options) {
|
30
|
+
if (options[key] === true) {
|
31
|
+
const liElement = document.getElementById(key);
|
32
|
+
if (liElement) {
|
33
|
+
liElement.classList.remove('d-none');
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
</script>
|
@@ -755,7 +755,8 @@
|
|
755
755
|
margin: 1.5rem 0 1rem 0;
|
756
756
|
}
|
757
757
|
|
758
|
-
button#wcagModalToggle
|
758
|
+
button#wcagModalToggle,
|
759
|
+
button#advancedScanOptionsSummaryTitle {
|
759
760
|
background: none;
|
760
761
|
border: 0;
|
761
762
|
padding: 0;
|
@@ -962,6 +963,26 @@
|
|
962
963
|
width: 1.125rem;
|
963
964
|
}
|
964
965
|
|
966
|
+
#advancedScanOptionsSummary li > svg {
|
967
|
+
margin-left: 2rem;
|
968
|
+
}
|
969
|
+
|
970
|
+
#advancedScanOptionsSummaryTitle::after {
|
971
|
+
content: '';
|
972
|
+
display: inline-block;
|
973
|
+
width: 12px;
|
974
|
+
height: 12px;
|
975
|
+
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9 16" fill="none"><path d="M1.03847 16C0.833084 16 0.632306 15.9388 0.461529 15.8241C0.290753 15.7095 0.157649 15.5465 0.0790493 15.3558C0.000449621 15.1651 -0.0201154 14.9553 0.0199549 14.7529C0.0600251 14.5505 0.158931 14.3645 0.304165 14.2186L6.49293 7.99975L0.304165 1.78088C0.109639 1.58514 0.000422347 1.31979 0.000518839 1.04315C0.000615331 0.766523 0.110018 0.501248 0.30468 0.30564C0.499341 0.110032 0.763331 9.70251e-05 1.03862 6.41929e-08C1.31392 -9.68968e-05 1.57798 0.109652 1.77278 0.305123L8.69586 7.26187C8.8906 7.45757 9 7.72299 9 7.99975C9 8.2765 8.8906 8.54192 8.69586 8.73763L1.77278 15.6944C1.67646 15.7914 1.562 15.8684 1.43598 15.9208C1.30996 15.9733 1.17487 16.0002 1.03847 16Z" fill="%23006B8C" transform="rotate(90, 4.5, 8)"/></svg>');
|
976
|
+
background-size: contain;
|
977
|
+
background-repeat: no-repeat;
|
978
|
+
transform: scaleY(1);
|
979
|
+
margin-left: 0.5rem;
|
980
|
+
}
|
981
|
+
|
982
|
+
#advancedScanOptionsSummaryTitle.chevron-rotated::after {
|
983
|
+
transform: scaleY(-1);
|
984
|
+
}
|
985
|
+
|
965
986
|
#footer {
|
966
987
|
padding: 0.75rem 1rem;
|
967
988
|
box-shadow: 0 -0.25rem 10px #736ccb1a;
|
@@ -25,6 +25,7 @@
|
|
25
25
|
<%- include('partials/header') %> <%- include('partials/main') %> <%-
|
26
26
|
include('partials/scripts/popper') %> <%- include('partials/scripts/bootstrap') %> <%-
|
27
27
|
include('partials/scripts/highlightjs') %> <%- include('partials/scripts/utils') %> <%-
|
28
|
+
include('partials/scripts/scanAboutScript') %> <%-
|
28
29
|
include('partials/scripts/categorySelectorDropdownScript') %> <%-
|
29
30
|
include('partials/scripts/categorySummary') %> <%- include('partials/scripts/ruleOffcanvas') %>
|
30
31
|
<%- include('partials/scripts/screenshotLightbox')%>
|