@govtechsg/oobee 0.10.36 → 0.10.42
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 +3 -3
- package/INTEGRATION.md +142 -53
- package/README.md +17 -0
- package/REPORTS.md +362 -0
- 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/crawlers/commonCrawlerFunc.ts +95 -220
- package/src/crawlers/crawlDomain.ts +10 -23
- package/src/crawlers/crawlLocalFile.ts +2 -0
- package/src/crawlers/crawlSitemap.ts +6 -4
- 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 +982 -842
- 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/crawlers/pdfScanFunc.ts +67 -26
- package/src/mergeAxeResults.ts +535 -132
- package/src/npmIndex.ts +130 -62
- package/src/screenshotFunc/htmlScreenshotFunc.ts +1 -1
- package/src/screenshotFunc/pdfScreenshotFunc.ts +34 -1
- 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/footer.ejs +3 -3
- 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/summaryMain.ejs +3 -3
- package/src/static/ejs/report.ejs +3 -3
- package/src/utils.ts +289 -13
- package/src/xPathToCssCypress.ts +178 -0
- package/src/crawlers/customAxeFunctions.ts +0 -82
package/src/mergeAxeResults.ts
CHANGED
@@ -13,14 +13,16 @@ 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,
|
20
20
|
getVersion,
|
21
21
|
getWcagPassPercentage,
|
22
|
+
getProgressPercentage,
|
22
23
|
retryFunction,
|
23
24
|
zipResults,
|
25
|
+
getIssuesPercentage,
|
24
26
|
} from './utils.js';
|
25
27
|
import { consoleLogger, silentLogger } from './logs.js';
|
26
28
|
import itemTypeDescription from './constants/itemTypeDescription.js';
|
@@ -34,19 +36,21 @@ export type ItemsInfo = {
|
|
34
36
|
displayNeedsReview?: boolean;
|
35
37
|
};
|
36
38
|
|
37
|
-
type PageInfo = {
|
38
|
-
items
|
39
|
+
export type PageInfo = {
|
40
|
+
items?: ItemsInfo[];
|
39
41
|
itemsCount?: number;
|
40
42
|
pageTitle: string;
|
41
|
-
url
|
43
|
+
url: string;
|
44
|
+
actualUrl: string;
|
42
45
|
pageImagePath?: string;
|
43
46
|
pageIndex?: number;
|
44
|
-
metadata
|
47
|
+
metadata?: string;
|
45
48
|
};
|
46
49
|
|
47
50
|
export type RuleInfo = {
|
48
51
|
totalItems: number;
|
49
52
|
pagesAffected: PageInfo[];
|
53
|
+
pagesAffectedCount: number;
|
50
54
|
rule: string;
|
51
55
|
description: string;
|
52
56
|
axeImpact: string;
|
@@ -74,7 +78,6 @@ type AllIssues = {
|
|
74
78
|
deviceChosen: string;
|
75
79
|
formatAboutStartTime: (dateString: any) => string;
|
76
80
|
isCustomFlow: boolean;
|
77
|
-
viewport: string;
|
78
81
|
pagesScanned: PageInfo[];
|
79
82
|
pagesNotScanned: PageInfo[];
|
80
83
|
totalPagesScanned: number;
|
@@ -85,17 +88,27 @@ type AllIssues = {
|
|
85
88
|
topTenIssues: Array<any>;
|
86
89
|
wcagViolations: string[];
|
87
90
|
customFlowLabel: string;
|
88
|
-
|
91
|
+
oobeeAppVersion: string;
|
89
92
|
items: {
|
90
93
|
mustFix: Category;
|
91
94
|
goodToFix: Category;
|
92
95
|
needsReview: Category;
|
93
96
|
passed: Category;
|
94
97
|
};
|
95
|
-
cypressScanAboutMetadata:
|
98
|
+
cypressScanAboutMetadata: {
|
99
|
+
browser?: string;
|
100
|
+
viewport?: { width: number; height: number };
|
101
|
+
};
|
96
102
|
wcagLinks: { [key: string]: string };
|
97
103
|
[key: string]: any;
|
98
104
|
advancedScanOptionsSummaryItems: { [key: string]: boolean };
|
105
|
+
scanPagesDetail: {
|
106
|
+
pagesAffected: any[];
|
107
|
+
pagesNotAffected: any[];
|
108
|
+
scannedPagesCount: number;
|
109
|
+
pagesNotScanned: any[];
|
110
|
+
pagesNotScannedCount: number;
|
111
|
+
};
|
99
112
|
};
|
100
113
|
|
101
114
|
const filename = fileURLToPath(import.meta.url);
|
@@ -160,13 +173,11 @@ const writeCsv = async (allIssues, storagePath) => {
|
|
160
173
|
pagesAffected,
|
161
174
|
helpUrl: learnMore,
|
162
175
|
} = 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));
|
176
|
+
|
168
177
|
// format clauses as a string
|
169
|
-
const wcagConformance =
|
178
|
+
const wcagConformance = conformance.join(',');
|
179
|
+
|
180
|
+
pagesAffected.sort((a, b) => a.url.localeCompare(b.url));
|
170
181
|
|
171
182
|
pagesAffected.forEach(affectedPage => {
|
172
183
|
const { url, items } = affectedPage;
|
@@ -239,8 +250,8 @@ const writeCsv = async (allIssues, storagePath) => {
|
|
239
250
|
issueId: 'error-pages-skipped',
|
240
251
|
issueDescription: 'Page was skipped during the scan',
|
241
252
|
wcagConformance: '',
|
242
|
-
url: page.url || '',
|
243
|
-
pageTitle: '',
|
253
|
+
url: page.url || page || '',
|
254
|
+
pageTitle: 'Error',
|
244
255
|
context: '',
|
245
256
|
howToFix: '',
|
246
257
|
axeImpact: '',
|
@@ -544,86 +555,93 @@ const writeLargeScanItemsJsonToFile = async (obj: object, filePath: string) => {
|
|
544
555
|
|
545
556
|
keys.forEach((key, i) => {
|
546
557
|
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
558
|
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
559
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
560
|
+
queueWrite(` "${key}": ${JSON.stringify(value)}`);
|
561
|
+
} else {
|
562
|
+
queueWrite(` "${key}": {\n`);
|
563
|
+
|
564
|
+
const { rules, ...otherProperties } = value;
|
565
|
+
|
566
|
+
// Write other properties
|
567
|
+
Object.entries(otherProperties).forEach(([propKey, propValue], j) => {
|
568
|
+
const propValueString =
|
569
|
+
propValue === null ||
|
570
|
+
typeof propValue === 'function' ||
|
571
|
+
typeof propValue === 'undefined'
|
572
|
+
? 'null'
|
573
|
+
: JSON.stringify(propValue);
|
574
|
+
queueWrite(` "${propKey}": ${propValueString}`);
|
575
|
+
if (j < Object.keys(otherProperties).length - 1 || (rules && rules.length >= 0)) {
|
576
|
+
queueWrite(',\n');
|
577
|
+
} else {
|
578
|
+
queueWrite('\n');
|
579
|
+
}
|
580
|
+
});
|
599
581
|
|
600
|
-
|
582
|
+
if (rules && Array.isArray(rules)) {
|
583
|
+
queueWrite(' "rules": [\n');
|
584
|
+
|
585
|
+
rules.forEach((rule, j) => {
|
586
|
+
queueWrite(' {\n');
|
587
|
+
const { pagesAffected, ...otherRuleProperties } = rule;
|
588
|
+
|
589
|
+
Object.entries(otherRuleProperties).forEach(([ruleKey, ruleValue], k) => {
|
590
|
+
const ruleValueString =
|
591
|
+
ruleValue === null ||
|
592
|
+
typeof ruleValue === 'function' ||
|
593
|
+
typeof ruleValue === 'undefined'
|
594
|
+
? 'null'
|
595
|
+
: JSON.stringify(ruleValue);
|
596
|
+
queueWrite(` "${ruleKey}": ${ruleValueString}`);
|
597
|
+
if (k < Object.keys(otherRuleProperties).length - 1 || pagesAffected) {
|
601
598
|
queueWrite(',\n');
|
602
599
|
} else {
|
603
600
|
queueWrite('\n');
|
604
601
|
}
|
605
602
|
});
|
606
603
|
|
607
|
-
|
608
|
-
|
604
|
+
if (pagesAffected && Array.isArray(pagesAffected)) {
|
605
|
+
queueWrite(' "pagesAffected": [\n');
|
609
606
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
607
|
+
pagesAffected.forEach((page, p) => {
|
608
|
+
const pageJson = JSON.stringify(page, null, 2)
|
609
|
+
.split('\n')
|
610
|
+
.map((line, idx) => (idx === 0 ? ` ${line}` : ` ${line}`))
|
611
|
+
.join('\n');
|
612
|
+
|
613
|
+
queueWrite(pageJson);
|
614
|
+
|
615
|
+
if (p < pagesAffected.length - 1) {
|
616
|
+
queueWrite(',\n');
|
617
|
+
} else {
|
618
|
+
queueWrite('\n');
|
619
|
+
}
|
620
|
+
});
|
621
|
+
|
622
|
+
queueWrite(' ]');
|
623
|
+
}
|
624
|
+
|
625
|
+
queueWrite('\n }');
|
626
|
+
if (j < rules.length - 1) {
|
627
|
+
queueWrite(',\n');
|
628
|
+
} else {
|
629
|
+
queueWrite('\n');
|
630
|
+
}
|
631
|
+
});
|
632
|
+
|
633
|
+
queueWrite(' ]');
|
634
|
+
}
|
635
|
+
queueWrite('\n }');
|
636
|
+
}
|
637
|
+
|
638
|
+
if (i < keys.length - 1) {
|
639
|
+
queueWrite(',\n');
|
640
|
+
} else {
|
641
|
+
queueWrite('\n');
|
642
|
+
}
|
617
643
|
|
618
|
-
queueWrite(' ]');
|
619
|
-
}
|
620
644
|
|
621
|
-
queueWrite('\n }');
|
622
|
-
if (i < keys.length - 1) {
|
623
|
-
queueWrite(',\n');
|
624
|
-
} else {
|
625
|
-
queueWrite('\n');
|
626
|
-
}
|
627
645
|
});
|
628
646
|
|
629
647
|
queueWrite('}\n');
|
@@ -727,6 +745,12 @@ const writeJsonAndBase64Files = async (
|
|
727
745
|
scanItemsSummaryBase64FilePath: string;
|
728
746
|
scanItemsMiniReportJsonFilePath: string;
|
729
747
|
scanItemsMiniReportBase64FilePath: string;
|
748
|
+
scanIssuesSummaryJsonFilePath: string;
|
749
|
+
scanIssuesSummaryBase64FilePath: string;
|
750
|
+
scanPagesDetailJsonFilePath: string;
|
751
|
+
scanPagesDetailBase64FilePath: string;
|
752
|
+
scanPagesSummaryJsonFilePath: string;
|
753
|
+
scanPagesSummaryBase64FilePath: string;
|
730
754
|
scanDataJsonFileSize: number;
|
731
755
|
scanItemsJsonFileSize: number;
|
732
756
|
}> => {
|
@@ -734,7 +758,33 @@ const writeJsonAndBase64Files = async (
|
|
734
758
|
const { jsonFilePath: scanDataJsonFilePath, base64FilePath: scanDataBase64FilePath } =
|
735
759
|
await writeJsonFileAndCompressedJsonFile(rest, storagePath, 'scanData');
|
736
760
|
const { jsonFilePath: scanItemsJsonFilePath, base64FilePath: scanItemsBase64FilePath } =
|
737
|
-
await writeJsonFileAndCompressedJsonFile(items, storagePath, 'scanItems');
|
761
|
+
await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...items }, storagePath, 'scanItems');
|
762
|
+
|
763
|
+
// Add pagesAffectedCount to each rule in scanItemsMiniReport (items) and sort them in descending order of pagesAffectedCount
|
764
|
+
['mustFix', 'goodToFix', 'needsReview', 'passed'].forEach((category) => {
|
765
|
+
if (items[category].rules && Array.isArray(items[category].rules)) {
|
766
|
+
items[category].rules.forEach((rule) => {
|
767
|
+
rule.pagesAffectedCount = Array.isArray(rule.pagesAffected)
|
768
|
+
? rule.pagesAffected.length
|
769
|
+
: 0;
|
770
|
+
});
|
771
|
+
|
772
|
+
// Sort in descending order of pagesAffectedCount
|
773
|
+
items[category].rules.sort((a, b) => (b.pagesAffectedCount || 0) - (a.pagesAffectedCount || 0));
|
774
|
+
}
|
775
|
+
});
|
776
|
+
|
777
|
+
// Refactor scanIssuesSummary to reuse the scanItemsMiniReport structure by stripping out pagesAffected
|
778
|
+
const scanIssuesSummary = {
|
779
|
+
mustFix: items.mustFix.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
780
|
+
goodToFix: items.goodToFix.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
781
|
+
needsReview: items.needsReview.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
782
|
+
passed: items.passed.rules.map(({ pagesAffected, ...ruleInfo }) => ruleInfo),
|
783
|
+
};
|
784
|
+
|
785
|
+
// Write out the scanIssuesSummary JSON using the new structure
|
786
|
+
const { jsonFilePath: scanIssuesSummaryJsonFilePath, base64FilePath: scanIssuesSummaryBase64FilePath } =
|
787
|
+
await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...scanIssuesSummary }, storagePath, 'scanIssuesSummary');
|
738
788
|
|
739
789
|
// scanItemsSummary
|
740
790
|
// the below mutates the original items object, since it is expensive to clone
|
@@ -774,6 +824,8 @@ const writeJsonAndBase64Files = async (
|
|
774
824
|
pagesNotScanned,
|
775
825
|
wcagLinks,
|
776
826
|
wcagPassPercentage,
|
827
|
+
progressPercentage,
|
828
|
+
issuesPercentage,
|
777
829
|
totalPagesScanned,
|
778
830
|
totalPagesNotScanned,
|
779
831
|
topTenIssues,
|
@@ -786,6 +838,8 @@ const writeJsonAndBase64Files = async (
|
|
786
838
|
pagesNotScanned,
|
787
839
|
wcagLinks,
|
788
840
|
wcagPassPercentage,
|
841
|
+
progressPercentage,
|
842
|
+
issuesPercentage,
|
789
843
|
totalPagesScanned,
|
790
844
|
totalPagesNotScanned,
|
791
845
|
topTenIssues,
|
@@ -794,8 +848,8 @@ const writeJsonAndBase64Files = async (
|
|
794
848
|
const {
|
795
849
|
jsonFilePath: scanItemsMiniReportJsonFilePath,
|
796
850
|
base64FilePath: scanItemsMiniReportBase64FilePath,
|
797
|
-
} = await writeJsonFileAndCompressedJsonFile(summaryItemsMini, storagePath, 'scanItemsSummaryMiniReport');
|
798
|
-
|
851
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...summaryItemsMini }, storagePath, 'scanItemsSummaryMiniReport');
|
852
|
+
|
799
853
|
const summaryItems = {
|
800
854
|
mustFix: {
|
801
855
|
totalItems: items.mustFix?.totalItems || 0,
|
@@ -812,6 +866,8 @@ const writeJsonAndBase64Files = async (
|
|
812
866
|
topTenPagesWithMostIssues,
|
813
867
|
wcagLinks,
|
814
868
|
wcagPassPercentage,
|
869
|
+
progressPercentage,
|
870
|
+
issuesPercentage,
|
815
871
|
totalPagesScanned,
|
816
872
|
totalPagesNotScanned,
|
817
873
|
topTenIssues,
|
@@ -820,7 +876,17 @@ const writeJsonAndBase64Files = async (
|
|
820
876
|
const {
|
821
877
|
jsonFilePath: scanItemsSummaryJsonFilePath,
|
822
878
|
base64FilePath: scanItemsSummaryBase64FilePath,
|
823
|
-
} = await writeJsonFileAndCompressedJsonFile(summaryItems, storagePath, 'scanItemsSummary');
|
879
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...summaryItems }, storagePath, 'scanItemsSummary');
|
880
|
+
|
881
|
+
const {
|
882
|
+
jsonFilePath: scanPagesDetailJsonFilePath,
|
883
|
+
base64FilePath: scanPagesDetailBase64FilePath
|
884
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...allIssues.scanPagesDetail }, storagePath, 'scanPagesDetail');
|
885
|
+
|
886
|
+
const {
|
887
|
+
jsonFilePath: scanPagesSummaryJsonFilePath,
|
888
|
+
base64FilePath: scanPagesSummaryBase64FilePath
|
889
|
+
} = await writeJsonFileAndCompressedJsonFile({ oobeeAppVersion: allIssues.oobeeAppVersion, ...allIssues.scanPagesSummary }, storagePath, 'scanPagesSummary');
|
824
890
|
|
825
891
|
return {
|
826
892
|
scanDataJsonFilePath,
|
@@ -831,6 +897,12 @@ const writeJsonAndBase64Files = async (
|
|
831
897
|
scanItemsSummaryBase64FilePath,
|
832
898
|
scanItemsMiniReportJsonFilePath,
|
833
899
|
scanItemsMiniReportBase64FilePath,
|
900
|
+
scanIssuesSummaryJsonFilePath,
|
901
|
+
scanIssuesSummaryBase64FilePath,
|
902
|
+
scanPagesDetailJsonFilePath,
|
903
|
+
scanPagesDetailBase64FilePath,
|
904
|
+
scanPagesSummaryJsonFilePath,
|
905
|
+
scanPagesSummaryBase64FilePath,
|
834
906
|
scanDataJsonFileSize: fs.statSync(scanDataJsonFilePath).size,
|
835
907
|
scanItemsJsonFileSize: fs.statSync(scanItemsJsonFilePath).size,
|
836
908
|
};
|
@@ -1015,6 +1087,8 @@ const getTopTenIssues = allIssues => {
|
|
1015
1087
|
const categories = ['mustFix', 'goodToFix'];
|
1016
1088
|
const rulesWithCounts = [];
|
1017
1089
|
|
1090
|
+
// This is no longer required and shall not be maintained in future
|
1091
|
+
/*
|
1018
1092
|
const conformanceLevels = {
|
1019
1093
|
wcag2a: 'A',
|
1020
1094
|
wcag2aa: 'AA',
|
@@ -1022,20 +1096,24 @@ const getTopTenIssues = allIssues => {
|
|
1022
1096
|
wcag22aa: 'AA',
|
1023
1097
|
wcag2aaa: 'AAA',
|
1024
1098
|
};
|
1099
|
+
*/
|
1025
1100
|
|
1026
1101
|
categories.forEach(category => {
|
1027
1102
|
const rules = allIssues.items[category]?.rules || [];
|
1028
1103
|
|
1029
1104
|
rules.forEach(rule => {
|
1105
|
+
// This is not needed anymore since we want to have the clause number too
|
1106
|
+
/*
|
1030
1107
|
const wcagLevel = rule.conformance[0];
|
1031
1108
|
const aLevel = conformanceLevels[wcagLevel] || wcagLevel;
|
1109
|
+
*/
|
1032
1110
|
|
1033
1111
|
rulesWithCounts.push({
|
1034
1112
|
category,
|
1035
1113
|
ruleId: rule.rule,
|
1036
1114
|
description: rule.description,
|
1037
1115
|
axeImpact: rule.axeImpact,
|
1038
|
-
conformance:
|
1116
|
+
conformance: rule.conformance,
|
1039
1117
|
totalItems: rule.totalItems,
|
1040
1118
|
});
|
1041
1119
|
});
|
@@ -1047,48 +1125,71 @@ const getTopTenIssues = allIssues => {
|
|
1047
1125
|
};
|
1048
1126
|
|
1049
1127
|
const flattenAndSortResults = (allIssues: AllIssues, isCustomFlow: boolean) => {
|
1128
|
+
// Create a map that will sum items only from mustFix, goodToFix, and needsReview.
|
1050
1129
|
const urlOccurrencesMap = new Map<string, number>();
|
1051
1130
|
|
1052
|
-
|
1131
|
+
// Iterate over all categories; update the map only if the category is not "passed"
|
1132
|
+
['mustFix', 'goodToFix', 'needsReview', 'passed'].forEach((category) => {
|
1133
|
+
// Accumulate totalItems regardless of category.
|
1053
1134
|
allIssues.totalItems += allIssues.items[category].totalItems;
|
1054
1135
|
|
1055
1136
|
allIssues.items[category].rules = Object.entries(allIssues.items[category].rules)
|
1056
|
-
.map(ruleEntry => {
|
1137
|
+
.map((ruleEntry) => {
|
1057
1138
|
const [rule, ruleInfo] = ruleEntry as [string, RuleInfo];
|
1058
1139
|
ruleInfo.pagesAffected = Object.entries(ruleInfo.pagesAffected)
|
1059
|
-
.map(pageEntry => {
|
1140
|
+
.map((pageEntry) => {
|
1060
1141
|
if (isCustomFlow) {
|
1061
1142
|
const [pageIndex, pageInfo] = pageEntry as unknown as [number, PageInfo];
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1143
|
+
// Only update the occurrences map if not passed.
|
1144
|
+
if (category !== 'passed') {
|
1145
|
+
urlOccurrencesMap.set(
|
1146
|
+
pageInfo.url!,
|
1147
|
+
(urlOccurrencesMap.get(pageInfo.url!) || 0) + pageInfo.items.length
|
1148
|
+
);
|
1149
|
+
}
|
1066
1150
|
return { pageIndex, ...pageInfo };
|
1151
|
+
} else {
|
1152
|
+
const [url, pageInfo] = pageEntry as unknown as [string, PageInfo];
|
1153
|
+
if (category !== 'passed') {
|
1154
|
+
urlOccurrencesMap.set(
|
1155
|
+
url,
|
1156
|
+
(urlOccurrencesMap.get(url) || 0) + pageInfo.items.length
|
1157
|
+
);
|
1158
|
+
}
|
1159
|
+
return { url, ...pageInfo };
|
1067
1160
|
}
|
1068
|
-
const [url, pageInfo] = pageEntry as unknown as [string, PageInfo];
|
1069
|
-
urlOccurrencesMap.set(url, (urlOccurrencesMap.get(url) || 0) + pageInfo.items.length);
|
1070
|
-
return { url, ...pageInfo };
|
1071
1161
|
})
|
1162
|
+
// Sort pages so that those with the most items come first
|
1072
1163
|
.sort((page1, page2) => page2.items.length - page1.items.length);
|
1073
1164
|
return { rule, ...ruleInfo };
|
1074
1165
|
})
|
1166
|
+
// Sort the rules by totalItems (descending)
|
1075
1167
|
.sort((rule1, rule2) => rule2.totalItems - rule1.totalItems);
|
1076
1168
|
});
|
1077
1169
|
|
1078
|
-
|
1079
|
-
|
1080
|
-
issue.totalOccurrences = urlOccurrencesMap.get(issue.url) || 0;
|
1081
|
-
});
|
1082
|
-
};
|
1083
|
-
|
1084
|
-
allIssues.topFiveMostIssues.sort((page1, page2) => page2.totalIssues - page1.totalIssues);
|
1170
|
+
// Sort top pages (assumes topFiveMostIssues is already populated)
|
1171
|
+
allIssues.topFiveMostIssues.sort((p1, p2) => p2.totalIssues - p1.totalIssues);
|
1085
1172
|
allIssues.topTenPagesWithMostIssues = allIssues.topFiveMostIssues.slice(0, 10);
|
1086
1173
|
allIssues.topFiveMostIssues = allIssues.topFiveMostIssues.slice(0, 5);
|
1087
|
-
|
1174
|
+
|
1175
|
+
// Update each issue in topTenPagesWithMostIssues with the computed occurrences,
|
1176
|
+
// excluding passed items.
|
1177
|
+
updateIssuesWithOccurrences(allIssues.topTenPagesWithMostIssues, urlOccurrencesMap);
|
1178
|
+
|
1179
|
+
// Get and assign the topTenIssues (using your existing helper)
|
1088
1180
|
const topTenIssues = getTopTenIssues(allIssues);
|
1089
1181
|
allIssues.topTenIssues = topTenIssues;
|
1090
1182
|
};
|
1091
1183
|
|
1184
|
+
// Helper: Update totalOccurrences for each issue using our urlOccurrencesMap.
|
1185
|
+
// For pages that have only passed items, the map will return undefined, so default to 0.
|
1186
|
+
function updateIssuesWithOccurrences(issuesList: any[], urlOccurrencesMap: Map<string, number>) {
|
1187
|
+
issuesList.forEach((issue) => {
|
1188
|
+
issue.totalOccurrences = urlOccurrencesMap.get(issue.url) || 0;
|
1189
|
+
});
|
1190
|
+
}
|
1191
|
+
|
1192
|
+
|
1092
1193
|
const createRuleIdJson = allIssues => {
|
1093
1194
|
const compiledRuleJson = {};
|
1094
1195
|
|
@@ -1117,7 +1218,7 @@ const createRuleIdJson = allIssues => {
|
|
1117
1218
|
return compiledRuleJson;
|
1118
1219
|
};
|
1119
1220
|
|
1120
|
-
const moveElemScreenshots = (randomToken, storagePath) => {
|
1221
|
+
const moveElemScreenshots = (randomToken: string, storagePath: string) => {
|
1121
1222
|
const currentScreenshotsPath = `${randomToken}/elemScreenshots`;
|
1122
1223
|
const resultsScreenshotsPath = `${storagePath}/elemScreenshots`;
|
1123
1224
|
if (fs.existsSync(currentScreenshotsPath)) {
|
@@ -1125,21 +1226,300 @@ const moveElemScreenshots = (randomToken, storagePath) => {
|
|
1125
1226
|
}
|
1126
1227
|
};
|
1127
1228
|
|
1229
|
+
/**
|
1230
|
+
* Build allIssues.scanPagesDetail and allIssues.scanPagesSummary
|
1231
|
+
* by analyzing pagesScanned (including mustFix/goodToFix/etc.).
|
1232
|
+
*/
|
1233
|
+
function populateScanPagesDetail(allIssues: AllIssues): void {
|
1234
|
+
// --------------------------------------------
|
1235
|
+
// 1) Gather your "scanned" pages from allIssues
|
1236
|
+
// --------------------------------------------
|
1237
|
+
const allScannedPages = Array.isArray(allIssues.pagesScanned)
|
1238
|
+
? allIssues.pagesScanned
|
1239
|
+
: [];
|
1240
|
+
|
1241
|
+
// --------------------------------------------
|
1242
|
+
// 2) Define category constants (optional, just for clarity)
|
1243
|
+
// --------------------------------------------
|
1244
|
+
const mustFixCategory = "mustFix";
|
1245
|
+
const goodToFixCategory = "goodToFix";
|
1246
|
+
const needsReviewCategory = "needsReview";
|
1247
|
+
const passedCategory = "passed";
|
1248
|
+
|
1249
|
+
// --------------------------------------------
|
1250
|
+
// 3) Set up type declarations (if you want them local to this function)
|
1251
|
+
// --------------------------------------------
|
1252
|
+
type RuleData = {
|
1253
|
+
ruleId: string;
|
1254
|
+
wcagConformance: string[];
|
1255
|
+
occurrencesMustFix: number;
|
1256
|
+
occurrencesGoodToFix: number;
|
1257
|
+
occurrencesNeedsReview: number;
|
1258
|
+
occurrencesPassed: number;
|
1259
|
+
};
|
1260
|
+
|
1261
|
+
type PageData = {
|
1262
|
+
pageTitle: string;
|
1263
|
+
url: string;
|
1264
|
+
// Summaries
|
1265
|
+
totalOccurrencesFailedIncludingNeedsReview: number; // mustFix + goodToFix + needsReview
|
1266
|
+
totalOccurrencesFailedExcludingNeedsReview: number; // mustFix + goodToFix
|
1267
|
+
totalOccurrencesNeedsReview: number; // needsReview
|
1268
|
+
totalOccurrencesPassed: number; // passed only
|
1269
|
+
typesOfIssues: Record<string, RuleData>;
|
1270
|
+
};
|
1271
|
+
|
1272
|
+
// --------------------------------------------
|
1273
|
+
// 4) We'll accumulate pages in a map keyed by URL
|
1274
|
+
// --------------------------------------------
|
1275
|
+
const pagesMap: Record<string, PageData> = {};
|
1276
|
+
|
1277
|
+
// --------------------------------------------
|
1278
|
+
// 5) Build pagesMap by iterating over each category in allIssues.items
|
1279
|
+
// --------------------------------------------
|
1280
|
+
Object.entries(allIssues.items).forEach(([categoryName, categoryData]) => {
|
1281
|
+
if (!categoryData?.rules) return; // no rules in this category? skip
|
1282
|
+
|
1283
|
+
categoryData.rules.forEach(rule => {
|
1284
|
+
const { rule: ruleId, conformance = [] } = rule;
|
1285
|
+
|
1286
|
+
rule.pagesAffected.forEach(p => {
|
1287
|
+
const { url, pageTitle, items = [] } = p;
|
1288
|
+
const itemsCount = items.length;
|
1289
|
+
|
1290
|
+
// Ensure the page is in pagesMap
|
1291
|
+
if (!pagesMap[url]) {
|
1292
|
+
pagesMap[url] = {
|
1293
|
+
pageTitle,
|
1294
|
+
url,
|
1295
|
+
totalOccurrencesFailedIncludingNeedsReview: 0,
|
1296
|
+
totalOccurrencesFailedExcludingNeedsReview: 0,
|
1297
|
+
totalOccurrencesNeedsReview: 0,
|
1298
|
+
totalOccurrencesPassed: 0,
|
1299
|
+
typesOfIssues: {},
|
1300
|
+
};
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
// Ensure the rule is present for this page
|
1304
|
+
if (!pagesMap[url].typesOfIssues[ruleId]) {
|
1305
|
+
pagesMap[url].typesOfIssues[ruleId] = {
|
1306
|
+
ruleId,
|
1307
|
+
wcagConformance: conformance,
|
1308
|
+
occurrencesMustFix: 0,
|
1309
|
+
occurrencesGoodToFix: 0,
|
1310
|
+
occurrencesNeedsReview: 0,
|
1311
|
+
occurrencesPassed: 0,
|
1312
|
+
};
|
1313
|
+
}
|
1314
|
+
|
1315
|
+
// Depending on the category, increment the relevant occurrence counts
|
1316
|
+
if (categoryName === mustFixCategory) {
|
1317
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesMustFix += itemsCount;
|
1318
|
+
pagesMap[url].totalOccurrencesFailedIncludingNeedsReview += itemsCount;
|
1319
|
+
pagesMap[url].totalOccurrencesFailedExcludingNeedsReview += itemsCount;
|
1320
|
+
} else if (categoryName === goodToFixCategory) {
|
1321
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesGoodToFix += itemsCount;
|
1322
|
+
pagesMap[url].totalOccurrencesFailedIncludingNeedsReview += itemsCount;
|
1323
|
+
pagesMap[url].totalOccurrencesFailedExcludingNeedsReview += itemsCount;
|
1324
|
+
} else if (categoryName === needsReviewCategory) {
|
1325
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesNeedsReview += itemsCount;
|
1326
|
+
pagesMap[url].totalOccurrencesFailedIncludingNeedsReview += itemsCount;
|
1327
|
+
pagesMap[url].totalOccurrencesNeedsReview += itemsCount;
|
1328
|
+
} else if (categoryName === passedCategory) {
|
1329
|
+
pagesMap[url].typesOfIssues[ruleId].occurrencesPassed += itemsCount;
|
1330
|
+
pagesMap[url].totalOccurrencesPassed += itemsCount;
|
1331
|
+
}
|
1332
|
+
});
|
1333
|
+
});
|
1334
|
+
});
|
1335
|
+
|
1336
|
+
// --------------------------------------------
|
1337
|
+
// 6) Separate scanned pages into “affected” vs. “notAffected”
|
1338
|
+
// --------------------------------------------
|
1339
|
+
const pagesInMap = Object.values(pagesMap); // All pages that have some record in pagesMap
|
1340
|
+
const pagesInMapUrls = new Set(Object.keys(pagesMap));
|
1341
|
+
|
1342
|
+
// (a) Pages with only passed (no mustFix/goodToFix/needsReview)
|
1343
|
+
const pagesAllPassed = pagesInMap.filter(
|
1344
|
+
p => p.totalOccurrencesFailedIncludingNeedsReview === 0
|
1345
|
+
);
|
1346
|
+
|
1347
|
+
// (b) Pages that do NOT appear in pagesMap at all => scanned but no items found
|
1348
|
+
const pagesNoEntries = allScannedPages
|
1349
|
+
.filter(sp => !pagesInMapUrls.has(sp.url))
|
1350
|
+
.map(sp => ({
|
1351
|
+
pageTitle: sp.pageTitle,
|
1352
|
+
url: sp.url,
|
1353
|
+
totalOccurrencesFailedIncludingNeedsReview: 0,
|
1354
|
+
totalOccurrencesFailedExcludingNeedsReview: 0,
|
1355
|
+
totalOccurrencesNeedsReview: 0,
|
1356
|
+
totalOccurrencesPassed: 0,
|
1357
|
+
typesOfIssues: {},
|
1358
|
+
}));
|
1359
|
+
|
1360
|
+
// Combine these into "notAffected"
|
1361
|
+
const pagesNotAffectedRaw = [...pagesAllPassed, ...pagesNoEntries];
|
1362
|
+
|
1363
|
+
// "affected" pages => have at least 1 mustFix/goodToFix/needsReview
|
1364
|
+
const pagesAffectedRaw = pagesInMap.filter(
|
1365
|
+
p => p.totalOccurrencesFailedIncludingNeedsReview > 0
|
1366
|
+
);
|
1367
|
+
|
1368
|
+
// --------------------------------------------
|
1369
|
+
// 7) Transform both arrays to the final shape
|
1370
|
+
// --------------------------------------------
|
1371
|
+
function transformPageData(page: PageData) {
|
1372
|
+
const typesOfIssuesArray = Object.values(page.typesOfIssues);
|
1373
|
+
|
1374
|
+
// Compute sums for each failing category
|
1375
|
+
const mustFixSum = typesOfIssuesArray.reduce((acc, r) => acc + r.occurrencesMustFix, 0);
|
1376
|
+
const goodToFixSum = typesOfIssuesArray.reduce((acc, r) => acc + r.occurrencesGoodToFix, 0);
|
1377
|
+
const needsReviewSum = typesOfIssuesArray.reduce((acc, r) => acc + r.occurrencesNeedsReview, 0);
|
1378
|
+
|
1379
|
+
// Build categoriesPresent based on nonzero failing counts
|
1380
|
+
const categoriesPresent: string[] = [];
|
1381
|
+
if (mustFixSum > 0) categoriesPresent.push("mustFix");
|
1382
|
+
if (goodToFixSum > 0) categoriesPresent.push("goodToFix");
|
1383
|
+
if (needsReviewSum > 0) categoriesPresent.push("needsReview");
|
1384
|
+
|
1385
|
+
// Count how many rules have failing issues
|
1386
|
+
const failedRuleIds = new Set<string>();
|
1387
|
+
typesOfIssuesArray.forEach(r => {
|
1388
|
+
if ((r.occurrencesMustFix || 0) > 0 ||
|
1389
|
+
(r.occurrencesGoodToFix || 0) > 0 ||
|
1390
|
+
(r.occurrencesNeedsReview || 0) > 0) {
|
1391
|
+
failedRuleIds.add(r.ruleId); // Ensure ruleId is unique
|
1392
|
+
}
|
1393
|
+
});
|
1394
|
+
const failedRuleCount = failedRuleIds.size;
|
1395
|
+
|
1396
|
+
// Possibly these two for future convenience
|
1397
|
+
const typesOfIssuesExcludingNeedsReviewCount = typesOfIssuesArray.filter(
|
1398
|
+
r => (r.occurrencesMustFix || 0) + (r.occurrencesGoodToFix || 0) > 0
|
1399
|
+
).length;
|
1400
|
+
|
1401
|
+
const typesOfIssuesExclusiveToNeedsReviewCount = typesOfIssuesArray.filter(
|
1402
|
+
r =>
|
1403
|
+
(r.occurrencesNeedsReview || 0) > 0 &&
|
1404
|
+
(r.occurrencesMustFix || 0) === 0 &&
|
1405
|
+
(r.occurrencesGoodToFix || 0) === 0
|
1406
|
+
).length;
|
1407
|
+
|
1408
|
+
// Aggregate wcagConformance for rules that actually fail
|
1409
|
+
const allConformance = typesOfIssuesArray.reduce((acc, curr) => {
|
1410
|
+
const nonPassedCount =
|
1411
|
+
(curr.occurrencesMustFix || 0) +
|
1412
|
+
(curr.occurrencesGoodToFix || 0) +
|
1413
|
+
(curr.occurrencesNeedsReview || 0);
|
1414
|
+
|
1415
|
+
if (nonPassedCount > 0) {
|
1416
|
+
return acc.concat(curr.wcagConformance || []);
|
1417
|
+
}
|
1418
|
+
return acc;
|
1419
|
+
}, [] as string[]);
|
1420
|
+
// Remove duplicates
|
1421
|
+
const conformance = Array.from(new Set(allConformance));
|
1422
|
+
|
1423
|
+
return {
|
1424
|
+
pageTitle: page.pageTitle,
|
1425
|
+
url: page.url,
|
1426
|
+
totalOccurrencesFailedIncludingNeedsReview: page.totalOccurrencesFailedIncludingNeedsReview,
|
1427
|
+
totalOccurrencesFailedExcludingNeedsReview: page.totalOccurrencesFailedExcludingNeedsReview,
|
1428
|
+
totalOccurrencesMustFix: mustFixSum,
|
1429
|
+
totalOccurrencesGoodToFix: goodToFixSum,
|
1430
|
+
totalOccurrencesNeedsReview: needsReviewSum,
|
1431
|
+
totalOccurrencesPassed: page.totalOccurrencesPassed,
|
1432
|
+
typesOfIssuesExclusiveToNeedsReviewCount,
|
1433
|
+
typesOfIssuesCount: failedRuleCount,
|
1434
|
+
typesOfIssuesExcludingNeedsReviewCount,
|
1435
|
+
categoriesPresent,
|
1436
|
+
conformance,
|
1437
|
+
// Keep full detail for "scanPagesDetail"
|
1438
|
+
typesOfIssues: typesOfIssuesArray,
|
1439
|
+
};
|
1440
|
+
}
|
1441
|
+
|
1442
|
+
// Transform raw pages
|
1443
|
+
const pagesAffected = pagesAffectedRaw.map(transformPageData);
|
1444
|
+
const pagesNotAffected = pagesNotAffectedRaw.map(transformPageData);
|
1445
|
+
|
1446
|
+
// --------------------------------------------
|
1447
|
+
// 8) Sort pages by typesOfIssuesCount (descending) for both arrays
|
1448
|
+
// --------------------------------------------
|
1449
|
+
pagesAffected.sort((a, b) => b.typesOfIssuesCount - a.typesOfIssuesCount);
|
1450
|
+
pagesNotAffected.sort((a, b) => b.typesOfIssuesCount - a.typesOfIssuesCount);
|
1451
|
+
|
1452
|
+
// --------------------------------------------
|
1453
|
+
// 9) Compute scanned/ skipped counts
|
1454
|
+
// --------------------------------------------
|
1455
|
+
const scannedPagesCount = pagesAffected.length + pagesNotAffected.length;
|
1456
|
+
const pagesNotScannedCount = Array.isArray(allIssues.pagesNotScanned)
|
1457
|
+
? allIssues.pagesNotScanned.length
|
1458
|
+
: 0;
|
1459
|
+
|
1460
|
+
// --------------------------------------------
|
1461
|
+
// 10) Build scanPagesDetail (with full "typesOfIssues")
|
1462
|
+
// --------------------------------------------
|
1463
|
+
allIssues.scanPagesDetail = {
|
1464
|
+
pagesAffected,
|
1465
|
+
pagesNotAffected,
|
1466
|
+
scannedPagesCount,
|
1467
|
+
pagesNotScanned: Array.isArray(allIssues.pagesNotScanned)
|
1468
|
+
? allIssues.pagesNotScanned
|
1469
|
+
: [],
|
1470
|
+
pagesNotScannedCount,
|
1471
|
+
};
|
1472
|
+
|
1473
|
+
// --------------------------------------------
|
1474
|
+
// 11) Build scanPagesSummary (strip out "typesOfIssues")
|
1475
|
+
// --------------------------------------------
|
1476
|
+
function stripTypesOfIssues(page: ReturnType<typeof transformPageData>) {
|
1477
|
+
const { typesOfIssues, ...rest } = page;
|
1478
|
+
return rest;
|
1479
|
+
}
|
1480
|
+
|
1481
|
+
const summaryPagesAffected = pagesAffected.map(stripTypesOfIssues);
|
1482
|
+
const summaryPagesNotAffected = pagesNotAffected.map(stripTypesOfIssues);
|
1483
|
+
|
1484
|
+
allIssues.scanPagesSummary = {
|
1485
|
+
pagesAffected: summaryPagesAffected,
|
1486
|
+
pagesNotAffected: summaryPagesNotAffected,
|
1487
|
+
scannedPagesCount,
|
1488
|
+
pagesNotScanned: Array.isArray(allIssues.pagesNotScanned)
|
1489
|
+
? allIssues.pagesNotScanned
|
1490
|
+
: [],
|
1491
|
+
pagesNotScannedCount,
|
1492
|
+
};
|
1493
|
+
}
|
1494
|
+
|
1128
1495
|
const generateArtifacts = async (
|
1129
|
-
randomToken,
|
1130
|
-
urlScanned,
|
1131
|
-
scanType,
|
1132
|
-
viewport,
|
1133
|
-
pagesScanned,
|
1134
|
-
pagesNotScanned,
|
1135
|
-
customFlowLabel,
|
1136
|
-
cypressScanAboutMetadata
|
1137
|
-
|
1138
|
-
|
1496
|
+
randomToken: string,
|
1497
|
+
urlScanned: string,
|
1498
|
+
scanType: ScannerTypes,
|
1499
|
+
viewport: string,
|
1500
|
+
pagesScanned: PageInfo[],
|
1501
|
+
pagesNotScanned: PageInfo[],
|
1502
|
+
customFlowLabel: string,
|
1503
|
+
cypressScanAboutMetadata: {
|
1504
|
+
browser?: string;
|
1505
|
+
viewport: { width: number; height: number };
|
1506
|
+
},
|
1507
|
+
scanDetails: {
|
1508
|
+
startTime: Date;
|
1509
|
+
endTime: Date;
|
1510
|
+
deviceChosen: string;
|
1511
|
+
isIncludeScreenshots: boolean;
|
1512
|
+
isAllowSubdomains: string;
|
1513
|
+
isEnableCustomChecks: string[];
|
1514
|
+
isEnableWcagAaa: string[];
|
1515
|
+
isSlowScanMode: number;
|
1516
|
+
isAdhereRobots: boolean;
|
1517
|
+
},
|
1518
|
+
zip: string = undefined, // optional
|
1139
1519
|
generateJsonFiles = false,
|
1140
1520
|
) => {
|
1141
1521
|
const intermediateDatasetsPath = `${randomToken}/datasets/${randomToken}`;
|
1142
|
-
const
|
1522
|
+
const oobeeAppVersion = getVersion();
|
1143
1523
|
const storagePath = getStoragePath(randomToken);
|
1144
1524
|
|
1145
1525
|
urlScanned =
|
@@ -1147,7 +1527,7 @@ const generateArtifacts = async (
|
|
1147
1527
|
? urlScanned
|
1148
1528
|
: urlWithoutAuth(urlScanned);
|
1149
1529
|
|
1150
|
-
const formatAboutStartTime = dateString => {
|
1530
|
+
const formatAboutStartTime = (dateString: string) => {
|
1151
1531
|
const utcStartTimeDate = new Date(dateString);
|
1152
1532
|
const formattedStartTime = utcStartTimeDate.toLocaleTimeString('en-GB', {
|
1153
1533
|
year: 'numeric',
|
@@ -1200,7 +1580,7 @@ const generateArtifacts = async (
|
|
1200
1580
|
topTenIssues: [],
|
1201
1581
|
wcagViolations: [],
|
1202
1582
|
customFlowLabel,
|
1203
|
-
|
1583
|
+
oobeeAppVersion,
|
1204
1584
|
items: {
|
1205
1585
|
mustFix: {
|
1206
1586
|
description: itemTypeDescription.mustFix,
|
@@ -1229,6 +1609,13 @@ const generateArtifacts = async (
|
|
1229
1609
|
},
|
1230
1610
|
cypressScanAboutMetadata,
|
1231
1611
|
wcagLinks: constants.wcagLinks,
|
1612
|
+
scanPagesDetail: {
|
1613
|
+
pagesAffected: [],
|
1614
|
+
pagesNotAffected: [],
|
1615
|
+
scannedPagesCount: 0,
|
1616
|
+
pagesNotScanned: [],
|
1617
|
+
pagesNotScannedCount: 0,
|
1618
|
+
},
|
1232
1619
|
// Populate boolean values for id="advancedScanOptionsSummary"
|
1233
1620
|
advancedScanOptionsSummaryItems: {
|
1234
1621
|
showIncludeScreenshots: [true].includes(scanDetails.isIncludeScreenshots),
|
@@ -1274,10 +1661,18 @@ const generateArtifacts = async (
|
|
1274
1661
|
createScreenshotsFolder(randomToken);
|
1275
1662
|
}
|
1276
1663
|
|
1664
|
+
populateScanPagesDetail(allIssues);
|
1665
|
+
|
1277
1666
|
allIssues.wcagPassPercentage = getWcagPassPercentage(allIssues.wcagViolations, allIssues.advancedScanOptionsSummaryItems.showEnableWcagAaa);
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1667
|
+
allIssues.progressPercentage = getProgressPercentage(allIssues.scanPagesDetail, allIssues.advancedScanOptionsSummaryItems.showEnableWcagAaa);
|
1668
|
+
|
1669
|
+
allIssues.issuesPercentage = await getIssuesPercentage(
|
1670
|
+
allIssues.scanPagesDetail,
|
1671
|
+
allIssues.advancedScanOptionsSummaryItems.showEnableWcagAaa,
|
1672
|
+
allIssues.advancedScanOptionsSummaryItems.disableOobee);
|
1673
|
+
|
1674
|
+
// console.log(allIssues.progressPercentage);
|
1675
|
+
// console.log(allIssues.issuesPercentage);
|
1281
1676
|
|
1282
1677
|
const getAxeImpactCount = (allIssues: AllIssues) => {
|
1283
1678
|
const impactCount = {
|
@@ -1325,6 +1720,12 @@ const generateArtifacts = async (
|
|
1325
1720
|
scanItemsSummaryBase64FilePath,
|
1326
1721
|
scanItemsMiniReportJsonFilePath,
|
1327
1722
|
scanItemsMiniReportBase64FilePath,
|
1723
|
+
scanIssuesSummaryJsonFilePath,
|
1724
|
+
scanIssuesSummaryBase64FilePath,
|
1725
|
+
scanPagesDetailJsonFilePath,
|
1726
|
+
scanPagesDetailBase64FilePath,
|
1727
|
+
scanPagesSummaryJsonFilePath,
|
1728
|
+
scanPagesSummaryBase64FilePath,
|
1328
1729
|
scanDataJsonFileSize,
|
1329
1730
|
scanItemsJsonFileSize,
|
1330
1731
|
} = await writeJsonAndBase64Files(allIssues, storagePath);
|
@@ -1355,6 +1756,14 @@ const generateArtifacts = async (
|
|
1355
1756
|
scanItemsBase64FilePath,
|
1356
1757
|
scanItemsSummaryJsonFilePath,
|
1357
1758
|
scanItemsSummaryBase64FilePath,
|
1759
|
+
scanItemsMiniReportJsonFilePath,
|
1760
|
+
scanItemsMiniReportBase64FilePath,
|
1761
|
+
scanIssuesSummaryJsonFilePath,
|
1762
|
+
scanIssuesSummaryBase64FilePath,
|
1763
|
+
scanPagesDetailJsonFilePath,
|
1764
|
+
scanPagesDetailBase64FilePath,
|
1765
|
+
scanPagesSummaryJsonFilePath,
|
1766
|
+
scanPagesSummaryBase64FilePath,
|
1358
1767
|
]);
|
1359
1768
|
}
|
1360
1769
|
|
@@ -1378,13 +1787,7 @@ const generateArtifacts = async (
|
|
1378
1787
|
`Results directory is at ${storagePath}`,
|
1379
1788
|
];
|
1380
1789
|
|
1381
|
-
if (process.env.
|
1382
|
-
messageToDisplay.push(
|
1383
|
-
'Reports have been further broken down according to their respective impact level.',
|
1384
|
-
);
|
1385
|
-
}
|
1386
|
-
|
1387
|
-
if (process.send && process.env.OOBEE_VERBOSE && process.env.REPORT_BREAKDOWN != '1') {
|
1790
|
+
if (process.send && process.env.OOBEE_VERBOSE) {
|
1388
1791
|
const zipFileNameMessage = {
|
1389
1792
|
type: 'zipFileName',
|
1390
1793
|
payload: `${constants.cliZipFileName}`,
|