@govtechsg/oobee 0.10.34 → 0.10.39
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/.vscode/settings.json +1 -1
- package/DETAILS.md +58 -42
- package/INTEGRATION.md +142 -53
- package/README.md +15 -0
- package/__mocks__/mock-report.html +1 -1
- package/exclusions.txt +4 -1
- package/package.json +2 -2
- package/src/constants/cliFunctions.ts +0 -7
- package/src/constants/common.ts +39 -1
- package/src/constants/constants.ts +9 -8
- package/src/constants/itemTypeDescription.ts +3 -3
- package/src/crawlers/commonCrawlerFunc.ts +67 -214
- package/src/crawlers/crawlDomain.ts +6 -2
- package/src/crawlers/crawlLocalFile.ts +2 -0
- package/src/crawlers/crawlSitemap.ts +5 -3
- package/src/crawlers/custom/escapeCssSelector.ts +10 -0
- package/src/crawlers/custom/evaluateAltText.ts +13 -0
- package/src/crawlers/custom/extractAndGradeText.ts +0 -2
- package/src/crawlers/custom/extractText.ts +28 -0
- package/src/crawlers/custom/findElementByCssSelector.ts +46 -0
- package/src/crawlers/custom/flagUnlabelledClickableElements.ts +1006 -901
- package/src/crawlers/custom/framesCheck.ts +51 -0
- package/src/crawlers/custom/getAxeConfiguration.ts +126 -0
- package/src/crawlers/custom/gradeReadability.ts +30 -0
- package/src/crawlers/custom/xPathToCss.ts +178 -0
- package/src/mergeAxeResults.ts +503 -132
- package/src/npmIndex.ts +130 -62
- package/src/static/ejs/partials/components/ruleOffcanvas.ejs +1 -1
- package/src/static/ejs/partials/components/scanAbout.ejs +1 -1
- package/src/static/ejs/partials/components/summaryScanResults.ejs +1 -1
- package/src/static/ejs/partials/components/wcagCompliance.ejs +3 -2
- package/src/static/ejs/partials/footer.ejs +13 -7
- package/src/static/ejs/partials/scripts/reportSearch.ejs +112 -74
- package/src/static/ejs/partials/scripts/ruleOffcanvas.ejs +2 -2
- package/src/static/ejs/partials/scripts/utils.ejs +1 -1
- package/src/static/ejs/partials/summaryMain.ejs +6 -6
- package/src/static/ejs/report.ejs +5 -5
- package/src/utils.ts +29 -10
- package/src/xPathToCssCypress.ts +178 -0
- package/src/crawlers/customAxeFunctions.ts +0 -82
package/src/mergeAxeResults.ts
CHANGED
@@ -13,7 +13,7 @@ import zlib from 'zlib';
|
|
13
13
|
import { Base64Encode } from 'base64-stream';
|
14
14
|
import { pipeline } from 'stream/promises';
|
15
15
|
import constants, { ScannerTypes } from './constants/constants.js';
|
16
|
-
import { urlWithoutAuth
|
16
|
+
import { urlWithoutAuth } from './constants/common.js';
|
17
17
|
import {
|
18
18
|
createScreenshotsFolder,
|
19
19
|
getStoragePath,
|
@@ -34,19 +34,21 @@ export type ItemsInfo = {
|
|
34
34
|
displayNeedsReview?: boolean;
|
35
35
|
};
|
36
36
|
|
37
|
-
type PageInfo = {
|
38
|
-
items
|
37
|
+
export type PageInfo = {
|
38
|
+
items?: ItemsInfo[];
|
39
39
|
itemsCount?: number;
|
40
40
|
pageTitle: string;
|
41
|
-
url
|
41
|
+
url: string;
|
42
|
+
actualUrl: string;
|
42
43
|
pageImagePath?: string;
|
43
44
|
pageIndex?: number;
|
44
|
-
metadata
|
45
|
+
metadata?: string;
|
45
46
|
};
|
46
47
|
|
47
48
|
export type RuleInfo = {
|
48
49
|
totalItems: number;
|
49
50
|
pagesAffected: PageInfo[];
|
51
|
+
pagesAffectedCount: number;
|
50
52
|
rule: string;
|
51
53
|
description: string;
|
52
54
|
axeImpact: string;
|
@@ -74,7 +76,6 @@ type AllIssues = {
|
|
74
76
|
deviceChosen: string;
|
75
77
|
formatAboutStartTime: (dateString: any) => string;
|
76
78
|
isCustomFlow: boolean;
|
77
|
-
viewport: string;
|
78
79
|
pagesScanned: PageInfo[];
|
79
80
|
pagesNotScanned: PageInfo[];
|
80
81
|
totalPagesScanned: number;
|
@@ -85,14 +86,17 @@ type AllIssues = {
|
|
85
86
|
topTenIssues: Array<any>;
|
86
87
|
wcagViolations: string[];
|
87
88
|
customFlowLabel: string;
|
88
|
-
|
89
|
+
oobeeAppVersion: string;
|
89
90
|
items: {
|
90
91
|
mustFix: Category;
|
91
92
|
goodToFix: Category;
|
92
93
|
needsReview: Category;
|
93
94
|
passed: Category;
|
94
95
|
};
|
95
|
-
cypressScanAboutMetadata:
|
96
|
+
cypressScanAboutMetadata: {
|
97
|
+
browser?: string;
|
98
|
+
viewport?: { width: number; height: number };
|
99
|
+
};
|
96
100
|
wcagLinks: { [key: string]: string };
|
97
101
|
[key: string]: any;
|
98
102
|
advancedScanOptionsSummaryItems: { [key: string]: boolean };
|
@@ -160,13 +164,11 @@ const writeCsv = async (allIssues, storagePath) => {
|
|
160
164
|
pagesAffected,
|
161
165
|
helpUrl: learnMore,
|
162
166
|
} = rule;
|
163
|
-
|
164
|
-
const clausesArr = conformance.filter(
|
165
|
-
clause => !['wcag2a', 'wcag2aa', 'wcag2aaa'].includes(clause),
|
166
|
-
);
|
167
|
-
pagesAffected.sort((a, b) => a.url.localeCompare(b.url));
|
167
|
+
|
168
168
|
// format clauses as a string
|
169
|
-
const wcagConformance =
|
169
|
+
const wcagConformance = conformance.join(',');
|
170
|
+
|
171
|
+
pagesAffected.sort((a, b) => a.url.localeCompare(b.url));
|
170
172
|
|
171
173
|
pagesAffected.forEach(affectedPage => {
|
172
174
|
const { url, items } = affectedPage;
|
@@ -239,8 +241,8 @@ const writeCsv = async (allIssues, storagePath) => {
|
|
239
241
|
issueId: 'error-pages-skipped',
|
240
242
|
issueDescription: 'Page was skipped during the scan',
|
241
243
|
wcagConformance: '',
|
242
|
-
url: page.url || '',
|
243
|
-
pageTitle: '',
|
244
|
+
url: page.url || page || '',
|
245
|
+
pageTitle: 'Error',
|
244
246
|
context: '',
|
245
247
|
howToFix: '',
|
246
248
|
axeImpact: '',
|
@@ -544,86 +546,93 @@ const writeLargeScanItemsJsonToFile = async (obj: object, filePath: string) => {
|
|
544
546
|
|
545
547
|
keys.forEach((key, i) => {
|
546
548
|
const value = obj[key];
|
547
|
-
queueWrite(` "${key}": {\n`);
|
548
|
-
|
549
|
-
const { rules, ...otherProperties } = value;
|
550
|
-
|
551
|
-
// Write other properties
|
552
|
-
Object.entries(otherProperties).forEach(([propKey, propValue], j) => {
|
553
|
-
const propValueString =
|
554
|
-
propValue === null ||
|
555
|
-
typeof propValue === 'function' ||
|
556
|
-
typeof propValue === 'undefined'
|
557
|
-
? 'null'
|
558
|
-
: JSON.stringify(propValue);
|
559
|
-
queueWrite(` "${propKey}": ${propValueString}`);
|
560
|
-
if (j < Object.keys(otherProperties).length - 1 || (rules && rules.length >= 0)) {
|
561
|
-
queueWrite(',\n');
|
562
|
-
} else {
|
563
|
-
queueWrite('\n');
|
564
|
-
}
|
565
|
-
});
|
566
|
-
|
567
|
-
if (rules && Array.isArray(rules)) {
|
568
|
-
queueWrite(' "rules": [\n');
|
569
|
-
|
570
|
-
rules.forEach((rule, j) => {
|
571
|
-
queueWrite(' {\n');
|
572
|
-
const { pagesAffected, ...otherRuleProperties } = rule;
|
573
|
-
|
574
|
-
Object.entries(otherRuleProperties).forEach(([ruleKey, ruleValue], k) => {
|
575
|
-
const ruleValueString =
|
576
|
-
ruleValue === null ||
|
577
|
-
typeof ruleValue === 'function' ||
|
578
|
-
typeof ruleValue === 'undefined'
|
579
|
-
? 'null'
|
580
|
-
: JSON.stringify(ruleValue);
|
581
|
-
queueWrite(` "${ruleKey}": ${ruleValueString}`);
|
582
|
-
if (k < Object.keys(otherRuleProperties).length - 1 || pagesAffected) {
|
583
|
-
queueWrite(',\n');
|
584
|
-
} else {
|
585
|
-
queueWrite('\n');
|
586
|
-
}
|
587
|
-
});
|
588
|
-
|
589
|
-
if (pagesAffected && Array.isArray(pagesAffected)) {
|
590
|
-
queueWrite(' "pagesAffected": [\n');
|
591
|
-
|
592
|
-
pagesAffected.forEach((page, p) => {
|
593
|
-
const pageJson = JSON.stringify(page, null, 2)
|
594
|
-
.split('\n')
|
595
|
-
.map((line, idx) => (idx === 0 ? ` ${line}` : ` ${line}`))
|
596
|
-
.join('\n');
|
597
549
|
|
598
|
-
|
550
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
551
|
+
queueWrite(` "${key}": ${JSON.stringify(value)}`);
|
552
|
+
} else {
|
553
|
+
queueWrite(` "${key}": {\n`);
|
554
|
+
|
555
|
+
const { rules, ...otherProperties } = value;
|
556
|
+
|
557
|
+
// Write other properties
|
558
|
+
Object.entries(otherProperties).forEach(([propKey, propValue], j) => {
|
559
|
+
const propValueString =
|
560
|
+
propValue === null ||
|
561
|
+
typeof propValue === 'function' ||
|
562
|
+
typeof propValue === 'undefined'
|
563
|
+
? 'null'
|
564
|
+
: JSON.stringify(propValue);
|
565
|
+
queueWrite(` "${propKey}": ${propValueString}`);
|
566
|
+
if (j < Object.keys(otherProperties).length - 1 || (rules && rules.length >= 0)) {
|
567
|
+
queueWrite(',\n');
|
568
|
+
} else {
|
569
|
+
queueWrite('\n');
|
570
|
+
}
|
571
|
+
});
|
599
572
|
|
600
|
-
|
573
|
+
if (rules && Array.isArray(rules)) {
|
574
|
+
queueWrite(' "rules": [\n');
|
575
|
+
|
576
|
+
rules.forEach((rule, j) => {
|
577
|
+
queueWrite(' {\n');
|
578
|
+
const { pagesAffected, ...otherRuleProperties } = rule;
|
579
|
+
|
580
|
+
Object.entries(otherRuleProperties).forEach(([ruleKey, ruleValue], k) => {
|
581
|
+
const ruleValueString =
|
582
|
+
ruleValue === null ||
|
583
|
+
typeof ruleValue === 'function' ||
|
584
|
+
typeof ruleValue === 'undefined'
|
585
|
+
? 'null'
|
586
|
+
: JSON.stringify(ruleValue);
|
587
|
+
queueWrite(` "${ruleKey}": ${ruleValueString}`);
|
588
|
+
if (k < Object.keys(otherRuleProperties).length - 1 || pagesAffected) {
|
601
589
|
queueWrite(',\n');
|
602
590
|
} else {
|
603
591
|
queueWrite('\n');
|
604
592
|
}
|
605
593
|
});
|
606
594
|
|
607
|
-
|
608
|
-
|
595
|
+
if (pagesAffected && Array.isArray(pagesAffected)) {
|
596
|
+
queueWrite(' "pagesAffected": [\n');
|
609
597
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
598
|
+
pagesAffected.forEach((page, p) => {
|
599
|
+
const pageJson = JSON.stringify(page, null, 2)
|
600
|
+
.split('\n')
|
601
|
+
.map((line, idx) => (idx === 0 ? ` ${line}` : ` ${line}`))
|
602
|
+
.join('\n');
|
603
|
+
|
604
|
+
queueWrite(pageJson);
|
605
|
+
|
606
|
+
if (p < pagesAffected.length - 1) {
|
607
|
+
queueWrite(',\n');
|
608
|
+
} else {
|
609
|
+
queueWrite('\n');
|
610
|
+
}
|
611
|
+
});
|
612
|
+
|
613
|
+
queueWrite(' ]');
|
614
|
+
}
|
615
|
+
|
616
|
+
queueWrite('\n }');
|
617
|
+
if (j < rules.length - 1) {
|
618
|
+
queueWrite(',\n');
|
619
|
+
} else {
|
620
|
+
queueWrite('\n');
|
621
|
+
}
|
622
|
+
});
|
623
|
+
|
624
|
+
queueWrite(' ]');
|
625
|
+
}
|
626
|
+
queueWrite('\n }');
|
627
|
+
}
|
628
|
+
|
629
|
+
if (i < keys.length - 1) {
|
630
|
+
queueWrite(',\n');
|
631
|
+
} else {
|
632
|
+
queueWrite('\n');
|
633
|
+
}
|
617
634
|
|
618
|
-
queueWrite(' ]');
|
619
|
-
}
|
620
635
|
|
621
|
-
queueWrite('\n }');
|
622
|
-
if (i < keys.length - 1) {
|
623
|
-
queueWrite(',\n');
|
624
|
-
} else {
|
625
|
-
queueWrite('\n');
|
626
|
-
}
|
627
636
|
});
|
628
637
|
|
629
638
|
queueWrite('}\n');
|
@@ -725,6 +734,14 @@ const writeJsonAndBase64Files = async (
|
|
725
734
|
scanItemsBase64FilePath: string;
|
726
735
|
scanItemsSummaryJsonFilePath: string;
|
727
736
|
scanItemsSummaryBase64FilePath: string;
|
737
|
+
scanItemsMiniReportJsonFilePath: string;
|
738
|
+
scanItemsMiniReportBase64FilePath: string;
|
739
|
+
scanIssuesSummaryJsonFilePath: string;
|
740
|
+
scanIssuesSummaryBase64FilePath: string;
|
741
|
+
scanPagesDetailJsonFilePath: string;
|
742
|
+
scanPagesDetailBase64FilePath: string;
|
743
|
+
scanPagesSummaryJsonFilePath: string;
|
744
|
+
scanPagesSummaryBase64FilePath: string;
|
728
745
|
scanDataJsonFileSize: number;
|
729
746
|
scanItemsJsonFileSize: number;
|
730
747
|
}> => {
|
@@ -732,7 +749,33 @@ const writeJsonAndBase64Files = async (
|
|
732
749
|
const { jsonFilePath: scanDataJsonFilePath, base64FilePath: scanDataBase64FilePath } =
|
733
750
|
await writeJsonFileAndCompressedJsonFile(rest, storagePath, 'scanData');
|
734
751
|
const { jsonFilePath: scanItemsJsonFilePath, base64FilePath: scanItemsBase64FilePath } =
|
735
|
-
await writeJsonFileAndCompressedJsonFile(items, storagePath, 'scanItems');
|
752
|
+
await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...items }, storagePath, 'scanItems');
|
753
|
+
|
754
|
+
// Add pagesAffectedCount to each rule in scanItemsMiniReport (items) and sort them in descending order of pagesAffectedCount
|
755
|
+
['mustFix', 'goodToFix', 'needsReview', 'passed'].forEach((category) => {
|
756
|
+
if (items[category].rules && Array.isArray(items[category].rules)) {
|
757
|
+
items[category].rules.forEach((rule) => {
|
758
|
+
rule.pagesAffectedCount = Array.isArray(rule.pagesAffected)
|
759
|
+
? rule.pagesAffected.length
|
760
|
+
: 0;
|
761
|
+
});
|
762
|
+
|
763
|
+
// Sort in descending order of pagesAffectedCount
|
764
|
+
items[category].rules.sort((a, b) => (b.pagesAffectedCount || 0) - (a.pagesAffectedCount || 0));
|
765
|
+
}
|
766
|
+
});
|
767
|
+
|
768
|
+
// Refactor scanIssuesSummary to reuse the scanItemsMiniReport structure by stripping out pagesAffected
|
769
|
+
const scanIssuesSummary = {
|
770
|
+
mustFix: items.mustFix.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
771
|
+
goodToFix: items.goodToFix.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
772
|
+
needsReview: items.needsReview.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
773
|
+
passed: items.passed.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
774
|
+
};
|
775
|
+
|
776
|
+
// Write out the scanIssuesSummary JSON using the new structure
|
777
|
+
const { jsonFilePath: scanIssuesSummaryJsonFilePath, base64FilePath: scanIssuesSummaryBase64FilePath } =
|
778
|
+
await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...scanIssuesSummary }, storagePath, 'scanIssuesSummary');
|
736
779
|
|
737
780
|
// scanItemsSummary
|
738
781
|
// the below mutates the original items object, since it is expensive to clone
|
@@ -777,7 +820,7 @@ const writeJsonAndBase64Files = async (
|
|
777
820
|
topTenIssues,
|
778
821
|
} = rest;
|
779
822
|
|
780
|
-
const
|
823
|
+
const summaryItemsMini = {
|
781
824
|
...items,
|
782
825
|
pagesScanned,
|
783
826
|
topTenPagesWithMostIssues,
|
@@ -789,10 +832,277 @@ const writeJsonAndBase64Files = async (
|
|
789
832
|
topTenIssues,
|
790
833
|
};
|
791
834
|
|
835
|
+
const {
|
836
|
+
jsonFilePath: scanItemsMiniReportJsonFilePath,
|
837
|
+
base64FilePath: scanItemsMiniReportBase64FilePath,
|
838
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...summaryItemsMini }, storagePath, 'scanItemsSummaryMiniReport');
|
839
|
+
const summaryItems = {
|
840
|
+
mustFix: {
|
841
|
+
totalItems: items.mustFix?.totalItems || 0,
|
842
|
+
totalRuleIssues: items.mustFix?.totalRuleIssues || 0,
|
843
|
+
},
|
844
|
+
goodToFix: {
|
845
|
+
totalItems: items.goodToFix?.totalItems || 0,
|
846
|
+
totalRuleIssues: items.goodToFix?.totalRuleIssues || 0,
|
847
|
+
},
|
848
|
+
needsReview: {
|
849
|
+
totalItems: items.needsReview?.totalItems || 0,
|
850
|
+
totalRuleIssues: items.needsReview?.totalRuleIssues || 0,
|
851
|
+
},
|
852
|
+
topTenPagesWithMostIssues,
|
853
|
+
wcagLinks,
|
854
|
+
wcagPassPercentage,
|
855
|
+
totalPagesScanned,
|
856
|
+
totalPagesNotScanned,
|
857
|
+
topTenIssues,
|
858
|
+
};
|
859
|
+
|
792
860
|
const {
|
793
861
|
jsonFilePath: scanItemsSummaryJsonFilePath,
|
794
862
|
base64FilePath: scanItemsSummaryBase64FilePath,
|
795
|
-
} = await writeJsonFileAndCompressedJsonFile(summaryItems, storagePath, 'scanItemsSummary');
|
863
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...summaryItems }, storagePath, 'scanItemsSummary');
|
864
|
+
|
865
|
+
// -----------------------------------------------------------------------------
|
866
|
+
// --- Scan Pages Summary and Scan Pages Detail ---
|
867
|
+
// -----------------------------------------------------------------------------
|
868
|
+
|
869
|
+
// 1) Gather your "scanned" pages from allIssues
|
870
|
+
const allScannedPages = Array.isArray(allIssues.pagesScanned)
|
871
|
+
? allIssues.pagesScanned
|
872
|
+
: [];
|
873
|
+
|
874
|
+
// Define which categories map to which occurrence property
|
875
|
+
const mustFixCategory = "mustFix"; // => occurrencesMustFix
|
876
|
+
const goodToFixCategory = "goodToFix"; // => occurrencesGoodToFix
|
877
|
+
const needsReviewCategory = "needsReview"; // => occurrencesNeedsReview
|
878
|
+
const passedCategory = "passed"; // => occurrencesPassed
|
879
|
+
|
880
|
+
type RuleData = {
|
881
|
+
ruleId: string;
|
882
|
+
wagConformance: string[];
|
883
|
+
occurrencesMustFix: number;
|
884
|
+
occurrencesGoodToFix: number;
|
885
|
+
occurrencesNeedsReview: number;
|
886
|
+
occurrencesPassed: number;
|
887
|
+
};
|
888
|
+
|
889
|
+
type PageData = {
|
890
|
+
pageTitle: string;
|
891
|
+
url: string;
|
892
|
+
// Summaries
|
893
|
+
totalOccurrencesFailedIncludingNeedsReview: number; // mustFix + goodToFix + needsReview
|
894
|
+
totalOccurrencesFailedExcludingNeedsReview: number; // mustFix + goodToFix
|
895
|
+
totalOccurrencesNeedsReview: number; // needsReview
|
896
|
+
totalOccurrencesPassed: number; // passed only
|
897
|
+
typesOfIssues: Record<string, RuleData>;
|
898
|
+
};
|
899
|
+
|
900
|
+
// 2) We'll accumulate pages in a map keyed by URL
|
901
|
+
const pagesMap: Record<string, PageData> = {};
|
902
|
+
|
903
|
+
// 3) Build pagesMap by iterating over each category in allIssues.items
|
904
|
+
Object.entries(allIssues.items).forEach(([categoryName, categoryData]) => {
|
905
|
+
if (!categoryData?.rules) return; // no rules in this category? skip
|
906
|
+
|
907
|
+
categoryData.rules.forEach((rule) => {
|
908
|
+
const { rule: ruleId, conformance = [] } = rule;
|
909
|
+
|
910
|
+
rule.pagesAffected.forEach((p) => {
|
911
|
+
const { url, pageTitle, itemsCount = 0 } = p;
|
912
|
+
|
913
|
+
// Ensure the page is in pagesMap
|
914
|
+
if (!pagesMap[url]) {
|
915
|
+
pagesMap[url] = {
|
916
|
+
pageTitle,
|
917
|
+
url,
|
918
|
+
totalOccurrencesFailedIncludingNeedsReview: 0,
|
919
|
+
totalOccurrencesFailedExcludingNeedsReview: 0,
|
920
|
+
totalOccurrencesNeedsReview: 0,
|
921
|
+
totalOccurrencesPassed: 0,
|
922
|
+
typesOfIssues: {},
|
923
|
+
};
|
924
|
+
}
|
925
|
+
|
926
|
+
// Ensure the rule is present for this page
|
927
|
+
if (!pagesMap[url].typesOfIssues[ruleId]) {
|
928
|
+
pagesMap[url].typesOfIssues[ruleId] = {
|
929
|
+
ruleId,
|
930
|
+
wagConformance: conformance,
|
931
|
+
occurrencesMustFix: 0,
|
932
|
+
occurrencesGoodToFix: 0,
|
933
|
+
occurrencesNeedsReview: 0,
|
934
|
+
occurrencesPassed: 0,
|
935
|
+
};
|
936
|
+
}
|
937
|
+
|
938
|
+
// Depending on the category, increment the relevant occurrence counts
|
939
|
+
if (categoryName === mustFixCategory) {
|
940
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesMustFix += itemsCount;
|
941
|
+
pagesMap[url].totalOccurrencesFailedIncludingNeedsReview += itemsCount;
|
942
|
+
pagesMap[url].totalOccurrencesFailedExcludingNeedsReview += itemsCount;
|
943
|
+
} else if (categoryName === goodToFixCategory) {
|
944
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesGoodToFix += itemsCount;
|
945
|
+
pagesMap[url].totalOccurrencesFailedIncludingNeedsReview += itemsCount;
|
946
|
+
pagesMap[url].totalOccurrencesFailedExcludingNeedsReview += itemsCount;
|
947
|
+
} else if (categoryName === needsReviewCategory) {
|
948
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesNeedsReview += itemsCount;
|
949
|
+
pagesMap[url].totalOccurrencesFailedIncludingNeedsReview += itemsCount;
|
950
|
+
pagesMap[url].totalOccurrencesNeedsReview += itemsCount;
|
951
|
+
} else if (categoryName === passedCategory) {
|
952
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesPassed += itemsCount;
|
953
|
+
pagesMap[url].totalOccurrencesPassed += itemsCount;
|
954
|
+
}
|
955
|
+
});
|
956
|
+
});
|
957
|
+
});
|
958
|
+
|
959
|
+
// 4) Separate scanned pages into “affected” vs. “notAffected”
|
960
|
+
// - "affected" => totalOccurrencesFailedIncludingNeedsReview > 0
|
961
|
+
// - "notAffected" => totalOccurrencesFailedIncludingNeedsReview = 0 (only passed issues)
|
962
|
+
// or pages that never appeared in pagesMap at all
|
963
|
+
|
964
|
+
const pagesInMap = Object.values(pagesMap); // All pages that have some record in pagesMap
|
965
|
+
const pagesInMapUrls = new Set(Object.keys(pagesMap));
|
966
|
+
|
967
|
+
// (a) Pages that appear in pagesMap BUT have only passed (no mustFix/goodToFix/needsReview)
|
968
|
+
const pagesAllPassed = pagesInMap.filter(
|
969
|
+
(p) => p.totalOccurrencesFailedIncludingNeedsReview === 0
|
970
|
+
);
|
971
|
+
|
972
|
+
// (b) Pages that do NOT appear in pagesMap at all => scanned but no items found
|
973
|
+
// (This can happen if a page had 0 occurrences across all categories.)
|
974
|
+
const pagesNoEntries = allScannedPages
|
975
|
+
.filter((sp) => !pagesInMapUrls.has(sp.url))
|
976
|
+
.map((sp) => ({
|
977
|
+
// We'll create a PageData with everything zeroed out
|
978
|
+
pageTitle: sp.pageTitle,
|
979
|
+
url: sp.url,
|
980
|
+
totalOccurrencesFailedIncludingNeedsReview: 0,
|
981
|
+
totalOccurrencesFailedExcludingNeedsReview: 0,
|
982
|
+
totalOccurrencesNeedsReview: 0,
|
983
|
+
totalOccurrencesPassed: 0,
|
984
|
+
typesOfIssues: {},
|
985
|
+
}));
|
986
|
+
|
987
|
+
// Combine these into "notAffected"
|
988
|
+
const pagesNotAffectedRaw = [...pagesAllPassed, ...pagesNoEntries];
|
989
|
+
|
990
|
+
// "affected" pages => have at least 1 mustFix/goodToFix/needsReview
|
991
|
+
const pagesAffectedRaw = pagesInMap.filter(
|
992
|
+
(p) => p.totalOccurrencesFailedIncludingNeedsReview > 0
|
993
|
+
);
|
994
|
+
|
995
|
+
// 5) Transform both arrays to final shapes
|
996
|
+
|
997
|
+
function transformPageData(page: PageData) {
|
998
|
+
const typesOfIssuesArray = Object.values(page.typesOfIssues);
|
999
|
+
|
1000
|
+
// Compute sums for each failing category
|
1001
|
+
const mustFixSum = typesOfIssuesArray.reduce((acc, r) => acc + r.occurrencesMustFix, 0);
|
1002
|
+
const goodToFixSum = typesOfIssuesArray.reduce((acc, r) => acc + r.occurrencesGoodToFix, 0);
|
1003
|
+
const needsReviewSum = typesOfIssuesArray.reduce((acc, r) => acc + r.occurrencesNeedsReview, 0);
|
1004
|
+
|
1005
|
+
// Build categoriesPresent based on nonzero failing counts
|
1006
|
+
const categoriesPresent: string[] = [];
|
1007
|
+
if (mustFixSum > 0) categoriesPresent.push("mustFix");
|
1008
|
+
if (goodToFixSum > 0) categoriesPresent.push("goodToFix");
|
1009
|
+
if (needsReviewSum > 0) categoriesPresent.push("needsReview");
|
1010
|
+
|
1011
|
+
// Count how many rules have failing issues (either mustFix or goodToFix)
|
1012
|
+
const failedRuleCount = typesOfIssuesArray.filter(
|
1013
|
+
(r) => (r.occurrencesMustFix || 0) + (r.occurrencesGoodToFix || 0) > 0
|
1014
|
+
).length;
|
1015
|
+
|
1016
|
+
const typesOfIssuesExcludingNeedsReviewCount = failedRuleCount;
|
1017
|
+
const occurrencesExclusiveToNeedsReview =
|
1018
|
+
page.totalOccurrencesFailedExcludingNeedsReview === 0 &&
|
1019
|
+
page.totalOccurrencesFailedIncludingNeedsReview > 0;
|
1020
|
+
|
1021
|
+
// Aggregate wcag conformance values only for rules with failing issues.
|
1022
|
+
const allConformance = typesOfIssuesArray.reduce((acc, curr) => {
|
1023
|
+
const nonPassedCount =
|
1024
|
+
(curr.occurrencesMustFix || 0) +
|
1025
|
+
(curr.occurrencesGoodToFix || 0) +
|
1026
|
+
(curr.occurrencesNeedsReview || 0);
|
1027
|
+
if (nonPassedCount > 0) {
|
1028
|
+
return acc.concat(curr.wagConformance || []);
|
1029
|
+
}
|
1030
|
+
return acc;
|
1031
|
+
}, []);
|
1032
|
+
// Remove duplicates.
|
1033
|
+
const conformance = Array.from(new Set(allConformance));
|
1034
|
+
|
1035
|
+
return {
|
1036
|
+
pageTitle: page.pageTitle,
|
1037
|
+
url: page.url,
|
1038
|
+
totalOccurrencesFailedIncludingNeedsReview: page.totalOccurrencesFailedIncludingNeedsReview,
|
1039
|
+
totalOccurrencesFailedExcludingNeedsReview: page.totalOccurrencesFailedExcludingNeedsReview,
|
1040
|
+
totalOccurrencesMustFix: mustFixSum,
|
1041
|
+
totalOccurrencesGoodToFix: goodToFixSum,
|
1042
|
+
totalOccurrencesNeedsReview: needsReviewSum,
|
1043
|
+
totalOccurrencesPassed: page.totalOccurrencesPassed,
|
1044
|
+
occurrencesExclusiveToNeedsReview,
|
1045
|
+
typesOfIssuesCount: failedRuleCount,
|
1046
|
+
typesOfIssuesExcludingNeedsReviewCount,
|
1047
|
+
categoriesPresent,
|
1048
|
+
conformance,
|
1049
|
+
typesOfIssues: typesOfIssuesArray, // full details for scanPagesDetail
|
1050
|
+
};
|
1051
|
+
}
|
1052
|
+
|
1053
|
+
const pagesAffected = pagesAffectedRaw.map(transformPageData);
|
1054
|
+
const pagesNotAffected = pagesNotAffectedRaw.map(transformPageData);
|
1055
|
+
|
1056
|
+
// 6) SORT pages by typesOfIssuesCount (descending) for both arrays
|
1057
|
+
pagesAffected.sort((a, b) => b.typesOfIssuesCount - a.typesOfIssuesCount);
|
1058
|
+
pagesNotAffected.sort((a, b) => b.typesOfIssuesCount - a.typesOfIssuesCount);
|
1059
|
+
|
1060
|
+
// 7) Compute scanned/ skipped counts
|
1061
|
+
const scannedPagesCount = pagesAffected.length + pagesNotAffected.length;
|
1062
|
+
const pagesNotScannedCount = Array.isArray(allIssues.pagesNotScanned)
|
1063
|
+
? allIssues.pagesNotScanned.length
|
1064
|
+
: 0;
|
1065
|
+
|
1066
|
+
// 8) Build scanPagesDetail (keeping full typesOfIssues)
|
1067
|
+
const scanPagesDetail = {
|
1068
|
+
pagesAffected,
|
1069
|
+
pagesNotAffected, // these pages are scanned but have no "fail/review" issues
|
1070
|
+
scannedPagesCount,
|
1071
|
+
pagesNotScanned: Array.isArray(allIssues.pagesNotScanned)
|
1072
|
+
? allIssues.pagesNotScanned
|
1073
|
+
: [],
|
1074
|
+
pagesNotScannedCount,
|
1075
|
+
};
|
1076
|
+
|
1077
|
+
// 9) Build scanPagesSummary (remove “typesOfIssues” from both groups, but keep other fields)
|
1078
|
+
function stripTypesOfIssues(page: ReturnType<typeof transformPageData>) {
|
1079
|
+
const { typesOfIssues, ...rest } = page;
|
1080
|
+
return rest;
|
1081
|
+
}
|
1082
|
+
|
1083
|
+
const summaryPagesAffected = pagesAffected.map(stripTypesOfIssues);
|
1084
|
+
const summaryPagesNotAffected = pagesNotAffected.map(stripTypesOfIssues);
|
1085
|
+
|
1086
|
+
const scanPagesSummary = {
|
1087
|
+
pagesAffected: summaryPagesAffected,
|
1088
|
+
pagesNotAffected: summaryPagesNotAffected,
|
1089
|
+
scannedPagesCount,
|
1090
|
+
pagesNotScanned: Array.isArray(allIssues.pagesNotScanned)
|
1091
|
+
? allIssues.pagesNotScanned
|
1092
|
+
: [],
|
1093
|
+
pagesNotScannedCount,
|
1094
|
+
};
|
1095
|
+
|
1096
|
+
// 10) Write out the detail and summary JSON files
|
1097
|
+
const {
|
1098
|
+
jsonFilePath: scanPagesDetailJsonFilePath,
|
1099
|
+
base64FilePath: scanPagesDetailBase64FilePath
|
1100
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...scanPagesDetail }, storagePath, 'scanPagesDetail');
|
1101
|
+
|
1102
|
+
const {
|
1103
|
+
jsonFilePath: scanPagesSummaryJsonFilePath,
|
1104
|
+
base64FilePath: scanPagesSummaryBase64FilePath
|
1105
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...scanPagesSummary }, storagePath, 'scanPagesSummary');
|
796
1106
|
|
797
1107
|
return {
|
798
1108
|
scanDataJsonFilePath,
|
@@ -801,6 +1111,14 @@ const writeJsonAndBase64Files = async (
|
|
801
1111
|
scanItemsBase64FilePath,
|
802
1112
|
scanItemsSummaryJsonFilePath,
|
803
1113
|
scanItemsSummaryBase64FilePath,
|
1114
|
+
scanItemsMiniReportJsonFilePath,
|
1115
|
+
scanItemsMiniReportBase64FilePath,
|
1116
|
+
scanIssuesSummaryJsonFilePath,
|
1117
|
+
scanIssuesSummaryBase64FilePath,
|
1118
|
+
scanPagesDetailJsonFilePath,
|
1119
|
+
scanPagesDetailBase64FilePath,
|
1120
|
+
scanPagesSummaryJsonFilePath,
|
1121
|
+
scanPagesSummaryBase64FilePath,
|
804
1122
|
scanDataJsonFileSize: fs.statSync(scanDataJsonFilePath).size,
|
805
1123
|
scanItemsJsonFileSize: fs.statSync(scanItemsJsonFilePath).size,
|
806
1124
|
};
|
@@ -985,6 +1303,8 @@ const getTopTenIssues = allIssues => {
|
|
985
1303
|
const categories = ['mustFix', 'goodToFix'];
|
986
1304
|
const rulesWithCounts = [];
|
987
1305
|
|
1306
|
+
// This is no longer required and shall not be maintained in future
|
1307
|
+
/*
|
988
1308
|
const conformanceLevels = {
|
989
1309
|
wcag2a: 'A',
|
990
1310
|
wcag2aa: 'AA',
|
@@ -992,20 +1312,24 @@ const getTopTenIssues = allIssues => {
|
|
992
1312
|
wcag22aa: 'AA',
|
993
1313
|
wcag2aaa: 'AAA',
|
994
1314
|
};
|
1315
|
+
*/
|
995
1316
|
|
996
1317
|
categories.forEach(category => {
|
997
1318
|
const rules = allIssues.items[category]?.rules || [];
|
998
1319
|
|
999
1320
|
rules.forEach(rule => {
|
1321
|
+
// This is not needed anymore since we want to have the clause number too
|
1322
|
+
/*
|
1000
1323
|
const wcagLevel = rule.conformance[0];
|
1001
1324
|
const aLevel = conformanceLevels[wcagLevel] || wcagLevel;
|
1325
|
+
*/
|
1002
1326
|
|
1003
1327
|
rulesWithCounts.push({
|
1004
1328
|
category,
|
1005
1329
|
ruleId: rule.rule,
|
1006
1330
|
description: rule.description,
|
1007
1331
|
axeImpact: rule.axeImpact,
|
1008
|
-
conformance:
|
1332
|
+
conformance: rule.conformance,
|
1009
1333
|
totalItems: rule.totalItems,
|
1010
1334
|
});
|
1011
1335
|
});
|
@@ -1017,48 +1341,71 @@ const getTopTenIssues = allIssues => {
|
|
1017
1341
|
};
|
1018
1342
|
|
1019
1343
|
const flattenAndSortResults = (allIssues: AllIssues, isCustomFlow: boolean) => {
|
1344
|
+
// Create a map that will sum items only from mustFix, goodToFix, and needsReview.
|
1020
1345
|
const urlOccurrencesMap = new Map<string, number>();
|
1021
1346
|
|
1022
|
-
|
1347
|
+
// Iterate over all categories; update the map only if the category is not "passed"
|
1348
|
+
['mustFix', 'goodToFix', 'needsReview', 'passed'].forEach((category) => {
|
1349
|
+
// Accumulate totalItems regardless of category.
|
1023
1350
|
allIssues.totalItems += allIssues.items[category].totalItems;
|
1024
1351
|
|
1025
1352
|
allIssues.items[category].rules = Object.entries(allIssues.items[category].rules)
|
1026
|
-
.map(ruleEntry => {
|
1353
|
+
.map((ruleEntry) => {
|
1027
1354
|
const [rule, ruleInfo] = ruleEntry as [string, RuleInfo];
|
1028
1355
|
ruleInfo.pagesAffected = Object.entries(ruleInfo.pagesAffected)
|
1029
|
-
.map(pageEntry => {
|
1356
|
+
.map((pageEntry) => {
|
1030
1357
|
if (isCustomFlow) {
|
1031
1358
|
const [pageIndex, pageInfo] = pageEntry as unknown as [number, PageInfo];
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1359
|
+
// Only update the occurrences map if not passed.
|
1360
|
+
if (category !== 'passed') {
|
1361
|
+
urlOccurrencesMap.set(
|
1362
|
+
pageInfo.url!,
|
1363
|
+
(urlOccurrencesMap.get(pageInfo.url!) || 0) + pageInfo.items.length
|
1364
|
+
);
|
1365
|
+
}
|
1036
1366
|
return { pageIndex, ...pageInfo };
|
1367
|
+
} else {
|
1368
|
+
const [url, pageInfo] = pageEntry as unknown as [string, PageInfo];
|
1369
|
+
if (category !== 'passed') {
|
1370
|
+
urlOccurrencesMap.set(
|
1371
|
+
url,
|
1372
|
+
(urlOccurrencesMap.get(url) || 0) + pageInfo.items.length
|
1373
|
+
);
|
1374
|
+
}
|
1375
|
+
return { url, ...pageInfo };
|
1037
1376
|
}
|
1038
|
-
const [url, pageInfo] = pageEntry as unknown as [string, PageInfo];
|
1039
|
-
urlOccurrencesMap.set(url, (urlOccurrencesMap.get(url) || 0) + pageInfo.items.length);
|
1040
|
-
return { url, ...pageInfo };
|
1041
1377
|
})
|
1378
|
+
// Sort pages so that those with the most items come first
|
1042
1379
|
.sort((page1, page2) => page2.items.length - page1.items.length);
|
1043
1380
|
return { rule, ...ruleInfo };
|
1044
1381
|
})
|
1382
|
+
// Sort the rules by totalItems (descending)
|
1045
1383
|
.sort((rule1, rule2) => rule2.totalItems - rule1.totalItems);
|
1046
1384
|
});
|
1047
1385
|
|
1048
|
-
|
1049
|
-
|
1050
|
-
issue.totalOccurrences = urlOccurrencesMap.get(issue.url) || 0;
|
1051
|
-
});
|
1052
|
-
};
|
1053
|
-
|
1054
|
-
allIssues.topFiveMostIssues.sort((page1, page2) => page2.totalIssues - page1.totalIssues);
|
1055
|
-
allIssues.topFiveMostIssues = allIssues.topFiveMostIssues.slice(0, 5);
|
1386
|
+
// Sort top pages (assumes topFiveMostIssues is already populated)
|
1387
|
+
allIssues.topFiveMostIssues.sort((p1, p2) => p2.totalIssues - p1.totalIssues);
|
1056
1388
|
allIssues.topTenPagesWithMostIssues = allIssues.topFiveMostIssues.slice(0, 10);
|
1057
|
-
|
1389
|
+
allIssues.topFiveMostIssues = allIssues.topFiveMostIssues.slice(0, 5);
|
1390
|
+
|
1391
|
+
// Update each issue in topTenPagesWithMostIssues with the computed occurrences,
|
1392
|
+
// excluding passed items.
|
1393
|
+
updateIssuesWithOccurrences(allIssues.topTenPagesWithMostIssues, urlOccurrencesMap);
|
1394
|
+
|
1395
|
+
// Get and assign the topTenIssues (using your existing helper)
|
1058
1396
|
const topTenIssues = getTopTenIssues(allIssues);
|
1059
1397
|
allIssues.topTenIssues = topTenIssues;
|
1060
1398
|
};
|
1061
1399
|
|
1400
|
+
// Helper: Update totalOccurrences for each issue using our urlOccurrencesMap.
|
1401
|
+
// For pages that have only passed items, the map will return undefined, so default to 0.
|
1402
|
+
function updateIssuesWithOccurrences(issuesList: any[], urlOccurrencesMap: Map<string, number>) {
|
1403
|
+
issuesList.forEach((issue) => {
|
1404
|
+
issue.totalOccurrences = urlOccurrencesMap.get(issue.url) || 0;
|
1405
|
+
});
|
1406
|
+
}
|
1407
|
+
|
1408
|
+
|
1062
1409
|
const createRuleIdJson = allIssues => {
|
1063
1410
|
const compiledRuleJson = {};
|
1064
1411
|
|
@@ -1087,7 +1434,7 @@ const createRuleIdJson = allIssues => {
|
|
1087
1434
|
return compiledRuleJson;
|
1088
1435
|
};
|
1089
1436
|
|
1090
|
-
const moveElemScreenshots = (randomToken, storagePath) => {
|
1437
|
+
const moveElemScreenshots = (randomToken: string, storagePath: string) => {
|
1091
1438
|
const currentScreenshotsPath = `${randomToken}/elemScreenshots`;
|
1092
1439
|
const resultsScreenshotsPath = `${storagePath}/elemScreenshots`;
|
1093
1440
|
if (fs.existsSync(currentScreenshotsPath)) {
|
@@ -1096,20 +1443,33 @@ const moveElemScreenshots = (randomToken, storagePath) => {
|
|
1096
1443
|
};
|
1097
1444
|
|
1098
1445
|
const generateArtifacts = async (
|
1099
|
-
randomToken,
|
1100
|
-
urlScanned,
|
1101
|
-
scanType,
|
1102
|
-
viewport,
|
1103
|
-
pagesScanned,
|
1104
|
-
pagesNotScanned,
|
1105
|
-
customFlowLabel,
|
1106
|
-
cypressScanAboutMetadata
|
1107
|
-
|
1108
|
-
|
1446
|
+
randomToken: string,
|
1447
|
+
urlScanned: string,
|
1448
|
+
scanType: ScannerTypes,
|
1449
|
+
viewport: string,
|
1450
|
+
pagesScanned: PageInfo[],
|
1451
|
+
pagesNotScanned: PageInfo[],
|
1452
|
+
customFlowLabel: string,
|
1453
|
+
cypressScanAboutMetadata: {
|
1454
|
+
browser?: string;
|
1455
|
+
viewport: { width: number; height: number };
|
1456
|
+
},
|
1457
|
+
scanDetails: {
|
1458
|
+
startTime: Date;
|
1459
|
+
endTime: Date;
|
1460
|
+
deviceChosen: string;
|
1461
|
+
isIncludeScreenshots: boolean;
|
1462
|
+
isAllowSubdomains: string;
|
1463
|
+
isEnableCustomChecks: string[];
|
1464
|
+
isEnableWcagAaa: string[];
|
1465
|
+
isSlowScanMode: number;
|
1466
|
+
isAdhereRobots: boolean;
|
1467
|
+
},
|
1468
|
+
zip: string = undefined, // optional
|
1109
1469
|
generateJsonFiles = false,
|
1110
1470
|
) => {
|
1111
1471
|
const intermediateDatasetsPath = `${randomToken}/datasets/${randomToken}`;
|
1112
|
-
const
|
1472
|
+
const oobeeAppVersion = getVersion();
|
1113
1473
|
const storagePath = getStoragePath(randomToken);
|
1114
1474
|
|
1115
1475
|
urlScanned =
|
@@ -1117,7 +1477,7 @@ const generateArtifacts = async (
|
|
1117
1477
|
? urlScanned
|
1118
1478
|
: urlWithoutAuth(urlScanned);
|
1119
1479
|
|
1120
|
-
const formatAboutStartTime = dateString => {
|
1480
|
+
const formatAboutStartTime = (dateString: string) => {
|
1121
1481
|
const utcStartTimeDate = new Date(dateString);
|
1122
1482
|
const formattedStartTime = utcStartTimeDate.toLocaleTimeString('en-GB', {
|
1123
1483
|
year: 'numeric',
|
@@ -1170,7 +1530,7 @@ const generateArtifacts = async (
|
|
1170
1530
|
topTenIssues: [],
|
1171
1531
|
wcagViolations: [],
|
1172
1532
|
customFlowLabel,
|
1173
|
-
|
1533
|
+
oobeeAppVersion,
|
1174
1534
|
items: {
|
1175
1535
|
mustFix: {
|
1176
1536
|
description: itemTypeDescription.mustFix,
|
@@ -1234,7 +1594,7 @@ const generateArtifacts = async (
|
|
1234
1594
|
'',
|
1235
1595
|
`Must Fix: ${allIssues.items.mustFix.rules.length} ${Object.keys(allIssues.items.mustFix.rules).length === 1 ? 'issue' : 'issues'} / ${allIssues.items.mustFix.totalItems} ${allIssues.items.mustFix.totalItems === 1 ? 'occurrence' : 'occurrences'}`,
|
1236
1596
|
`Good to Fix: ${allIssues.items.goodToFix.rules.length} ${Object.keys(allIssues.items.goodToFix.rules).length === 1 ? 'issue' : 'issues'} / ${allIssues.items.goodToFix.totalItems} ${allIssues.items.goodToFix.totalItems === 1 ? 'occurrence' : 'occurrences'}`,
|
1237
|
-
`
|
1597
|
+
`Manual Review Required: ${allIssues.items.needsReview.rules.length} ${Object.keys(allIssues.items.needsReview.rules).length === 1 ? 'issue' : 'issues'} / ${allIssues.items.needsReview.totalItems} ${allIssues.items.needsReview.totalItems === 1 ? 'occurrence' : 'occurrences'}`,
|
1238
1598
|
`Passed: ${allIssues.items.passed.totalItems} ${allIssues.items.passed.totalItems === 1 ? 'occurrence' : 'occurrences'}`,
|
1239
1599
|
]);
|
1240
1600
|
|
@@ -1244,7 +1604,7 @@ const generateArtifacts = async (
|
|
1244
1604
|
createScreenshotsFolder(randomToken);
|
1245
1605
|
}
|
1246
1606
|
|
1247
|
-
allIssues.wcagPassPercentage = getWcagPassPercentage(allIssues.wcagViolations);
|
1607
|
+
allIssues.wcagPassPercentage = getWcagPassPercentage(allIssues.wcagViolations, allIssues.advancedScanOptionsSummaryItems.showEnableWcagAaa);
|
1248
1608
|
consoleLogger.info(
|
1249
1609
|
`advancedScanOptionsSummaryItems is ${allIssues.advancedScanOptionsSummaryItems}`,
|
1250
1610
|
);
|
@@ -1293,6 +1653,14 @@ const generateArtifacts = async (
|
|
1293
1653
|
scanItemsBase64FilePath,
|
1294
1654
|
scanItemsSummaryJsonFilePath,
|
1295
1655
|
scanItemsSummaryBase64FilePath,
|
1656
|
+
scanItemsMiniReportJsonFilePath,
|
1657
|
+
scanItemsMiniReportBase64FilePath,
|
1658
|
+
scanIssuesSummaryJsonFilePath,
|
1659
|
+
scanIssuesSummaryBase64FilePath,
|
1660
|
+
scanPagesDetailJsonFilePath,
|
1661
|
+
scanPagesDetailBase64FilePath,
|
1662
|
+
scanPagesSummaryJsonFilePath,
|
1663
|
+
scanPagesSummaryBase64FilePath,
|
1296
1664
|
scanDataJsonFileSize,
|
1297
1665
|
scanItemsJsonFileSize,
|
1298
1666
|
} = await writeJsonAndBase64Files(allIssues, storagePath);
|
@@ -1306,12 +1674,13 @@ const generateArtifacts = async (
|
|
1306
1674
|
storagePath,
|
1307
1675
|
);
|
1308
1676
|
await writeSummaryHTML(allIssues, storagePath);
|
1677
|
+
|
1309
1678
|
await writeHTML(
|
1310
1679
|
allIssues,
|
1311
1680
|
storagePath,
|
1312
1681
|
'report',
|
1313
1682
|
scanDataBase64FilePath,
|
1314
|
-
resultsTooBig ?
|
1683
|
+
resultsTooBig ? scanItemsMiniReportBase64FilePath : scanItemsBase64FilePath,
|
1315
1684
|
);
|
1316
1685
|
|
1317
1686
|
if (!generateJsonFiles) {
|
@@ -1322,6 +1691,14 @@ const generateArtifacts = async (
|
|
1322
1691
|
scanItemsBase64FilePath,
|
1323
1692
|
scanItemsSummaryJsonFilePath,
|
1324
1693
|
scanItemsSummaryBase64FilePath,
|
1694
|
+
scanItemsMiniReportJsonFilePath,
|
1695
|
+
scanItemsMiniReportBase64FilePath,
|
1696
|
+
scanIssuesSummaryJsonFilePath,
|
1697
|
+
scanIssuesSummaryBase64FilePath,
|
1698
|
+
scanPagesDetailJsonFilePath,
|
1699
|
+
scanPagesDetailBase64FilePath,
|
1700
|
+
scanPagesSummaryJsonFilePath,
|
1701
|
+
scanPagesSummaryBase64FilePath,
|
1325
1702
|
]);
|
1326
1703
|
}
|
1327
1704
|
|
@@ -1345,13 +1722,7 @@ const generateArtifacts = async (
|
|
1345
1722
|
`Results directory is at ${storagePath}`,
|
1346
1723
|
];
|
1347
1724
|
|
1348
|
-
if (process.env.
|
1349
|
-
messageToDisplay.push(
|
1350
|
-
'Reports have been further broken down according to their respective impact level.',
|
1351
|
-
);
|
1352
|
-
}
|
1353
|
-
|
1354
|
-
if (process.send && process.env.OOBEE_VERBOSE && process.env.REPORT_BREAKDOWN != '1') {
|
1725
|
+
if (process.send && process.env.OOBEE_VERBOSE) {
|
1355
1726
|
const zipFileNameMessage = {
|
1356
1727
|
type: 'zipFileName',
|
1357
1728
|
payload: `${constants.cliZipFileName}`,
|