testaro 67.0.0 → 68.0.0
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/LICENSE +4 -16
- package/README.md +10 -2
- package/UPGRADES.md +1 -1
- package/dirWatch.js +2 -3
- package/ed11y/editoria11y.min.js +109 -690
- package/ed11y/editoria11y210.min.js +747 -0
- package/netWatch.js +6 -6
- package/package.json +1 -1
- package/procs/aslint.js +2 -2
- package/procs/catalog.js +190 -0
- package/procs/{dateOf.js → dateTime.js} +6 -4
- package/procs/doActs.js +1227 -0
- package/procs/doTestAct.js +63 -29
- package/procs/error.js +53 -0
- package/procs/job.js +64 -38
- package/procs/launch.js +596 -0
- package/procs/nu.js +3 -18
- package/procs/shoot.js +18 -2
- package/procs/testaro.js +102 -125
- package/procs/xPath.js +62 -0
- package/run.js +42 -1938
- package/scratch/README.md +9 -0
- package/testaro/adbID.js +3 -3
- package/testaro/allCaps.js +4 -5
- package/testaro/allHidden.js +19 -18
- package/testaro/allSlanted.js +4 -5
- package/testaro/altScheme.js +3 -3
- package/testaro/attVal.js +19 -35
- package/testaro/autocomplete.js +65 -62
- package/testaro/bulk.js +21 -20
- package/testaro/buttonMenu.js +112 -33
- package/testaro/captionLoc.js +3 -3
- package/testaro/datalistRef.js +4 -5
- package/testaro/distortion.js +3 -3
- package/testaro/docType.js +6 -9
- package/testaro/dupAtt.js +12 -25
- package/testaro/elements.js +4 -3
- package/testaro/embAc.js +4 -2
- package/testaro/focAll.js +6 -13
- package/testaro/focAndOp.js +3 -3
- package/testaro/focInd.js +3 -3
- package/testaro/focVis.js +4 -3
- package/testaro/headEl.js +5 -12
- package/testaro/headingAmb.js +45 -88
- package/testaro/hovInd.js +5 -5
- package/testaro/hover.js +44 -8
- package/testaro/hr.js +4 -4
- package/testaro/imageLink.js +3 -3
- package/testaro/labClash.js +3 -3
- package/testaro/legendLoc.js +3 -3
- package/testaro/lineHeight.js +3 -3
- package/testaro/linkAmb.js +25 -17
- package/testaro/linkExt.js +5 -5
- package/testaro/linkOldAtt.js +4 -3
- package/testaro/linkTo.js +4 -3
- package/testaro/linkUl.js +4 -5
- package/testaro/miniText.js +4 -3
- package/testaro/motion.js +3 -22
- package/testaro/nonTable.js +4 -5
- package/testaro/optRoleSel.js +3 -3
- package/testaro/phOnly.js +3 -3
- package/testaro/pseudoP.js +5 -5
- package/testaro/radioSet.js +4 -5
- package/testaro/role.js +4 -5
- package/testaro/secHeading.js +4 -5
- package/testaro/shoot0.js +3 -2
- package/testaro/shoot1.js +3 -2
- package/testaro/styleDiff.js +5 -12
- package/testaro/tabNav.js +30 -118
- package/testaro/targetSmall.js +30 -15
- package/testaro/textNodes.js +3 -1
- package/testaro/textSem.js +4 -5
- package/testaro/title.js +4 -2
- package/testaro/titledEl.js +3 -3
- package/testaro/zIndex.js +3 -3
- package/tests/alfa.js +28 -54
- package/tests/aslint.js +20 -53
- package/tests/axe.js +76 -13
- package/tests/ed11y.js +69 -141
- package/tests/htmlcs.js +69 -38
- package/tests/ibm.js +54 -9
- package/tests/nuVal.js +65 -12
- package/tests/nuVnu.js +76 -26
- package/tests/qualWeb.js +89 -44
- package/tests/testaro.js +288 -273
- package/tests/wave.js +142 -117
- package/tests/wax.js +61 -42
- package/procs/getLocatorData.js +0 -192
- package/procs/identify.js +0 -250
- package/procs/isInlineLink.js +0 -42
- package/procs/screenShot.js +0 -32
- package/procs/standardize.js +0 -524
- package/procs/target.js +0 -90
- package/procs/tellServer.js +0 -43
- package/scripts/dumpAlts.js +0 -28
package/tests/htmlcs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
3
|
+
© 2025–2026 Jonathan Robert Pool.
|
|
4
4
|
|
|
5
5
|
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
6
|
https://opensource.org/license/mit/ for details.
|
|
@@ -15,11 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
// IMPORTS
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
const {addTestaroIDs} = require('../procs/testaro');
|
|
20
|
-
// Module to get location data from an element.
|
|
21
|
-
const {getElementData} = require('../procs/getLocatorData');
|
|
22
|
-
// Module to handle files.
|
|
18
|
+
const {getAttributeXPath, getXPathCatalogIndex} = require('../procs/xPath');
|
|
23
19
|
const fs = require('fs/promises');
|
|
24
20
|
|
|
25
21
|
// FUNCTIONS
|
|
@@ -28,27 +24,47 @@ const fs = require('fs/promises');
|
|
|
28
24
|
exports.reporter = async (page, report, actIndex) => {
|
|
29
25
|
const act = report.acts[actIndex];
|
|
30
26
|
const {rules} = act;
|
|
27
|
+
// Initialize the act report.
|
|
31
28
|
const data = {};
|
|
32
|
-
const result = {
|
|
29
|
+
const result = {
|
|
30
|
+
nativeResult: {
|
|
31
|
+
totals: {
|
|
32
|
+
failed: 0,
|
|
33
|
+
cantTell: 0
|
|
34
|
+
},
|
|
35
|
+
error: [],
|
|
36
|
+
warning: []
|
|
37
|
+
},
|
|
38
|
+
standardResult: {}
|
|
39
|
+
};
|
|
40
|
+
const standard = report.standard !== 'no';
|
|
41
|
+
// If standard results are to be reported:
|
|
42
|
+
if (standard) {
|
|
43
|
+
// Initialize the standard result.
|
|
44
|
+
result.standardResult = {
|
|
45
|
+
prevented: false,
|
|
46
|
+
totals: [0, 0, 0, 0],
|
|
47
|
+
instances: []
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const {nativeResult, standardResult} = result;
|
|
33
51
|
// Get the HTMLCS script.
|
|
34
52
|
const scriptText = await fs.readFile(`${__dirname}/../htmlcs/HTMLCS.js`, 'utf8');
|
|
35
53
|
const scriptNonce = report.jobData && report.jobData.lastScriptNonce;
|
|
36
|
-
// Annotate all elements on the page with unique identifiers.
|
|
37
|
-
await addTestaroIDs(page);
|
|
38
54
|
let messageStrings = [];
|
|
39
|
-
//
|
|
55
|
+
// For each class of standards to be tested for:
|
|
40
56
|
for (const actStandard of ['WCAG2AAA']) {
|
|
41
57
|
const nextViolations = await page.evaluate(args => {
|
|
42
|
-
|
|
58
|
+
const actStandard = args[0];
|
|
59
|
+
const rules = args[1];
|
|
43
60
|
const scriptText = args[2];
|
|
44
61
|
const scriptNonce = args[3];
|
|
45
62
|
const script = document.createElement('script');
|
|
46
63
|
script.nonce = scriptNonce;
|
|
47
64
|
script.textContent = scriptText;
|
|
65
|
+
// Add the HTMLCS script to the page.
|
|
48
66
|
document.head.insertAdjacentElement('beforeend', script);
|
|
49
67
|
// If only some rules are to be employed:
|
|
50
|
-
const actStandard = args[0];
|
|
51
|
-
const rules = args[1];
|
|
52
68
|
if (rules && Array.isArray(rules) && rules.length) {
|
|
53
69
|
// Redefine WCAG 2 AAA as including only them.
|
|
54
70
|
if (! window.HTMLCS_WCAG2AAA) {
|
|
@@ -64,56 +80,71 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
64
80
|
catch(error) {
|
|
65
81
|
console.log(`ERROR executing HTMLCS_RUNNER on ${document.URL} (${error.message})`);
|
|
66
82
|
}
|
|
83
|
+
// Return the reported violations of that standard.
|
|
67
84
|
return violations;
|
|
68
85
|
}, [actStandard, rules, scriptText, scriptNonce]);
|
|
69
|
-
|
|
86
|
+
// If all reported violations of the standard are validly described:
|
|
87
|
+
if (nextViolations?.every(violation => typeof violation === 'string')) {
|
|
88
|
+
// Add their descriptions to the violation descriptions.
|
|
70
89
|
messageStrings.push(... nextViolations);
|
|
71
90
|
}
|
|
91
|
+
// Otherwise, i.e. if any reported violations are invalidly described:
|
|
72
92
|
else {
|
|
93
|
+
// Report this.
|
|
73
94
|
data.prevented = true;
|
|
74
95
|
data.error = 'ERROR executing HTMLCS_RUNNER in the page';
|
|
75
96
|
break;
|
|
76
97
|
}
|
|
77
98
|
}
|
|
99
|
+
// If no error was thrown:
|
|
78
100
|
if (! data.prevented) {
|
|
79
101
|
// Sort the violations by class and standard.
|
|
80
102
|
messageStrings.sort();
|
|
81
103
|
// Remove any duplicate violations.
|
|
82
104
|
messageStrings = [... new Set(messageStrings)];
|
|
83
|
-
// Initialize the result.
|
|
84
|
-
result.Error = {};
|
|
85
|
-
result.Warning = {};
|
|
86
105
|
// For each violation:
|
|
87
106
|
for (const string of messageStrings) {
|
|
88
107
|
// Split its message into severity class, rule ID, tagname, ID, rule description, and excerpt.
|
|
89
108
|
const parts = string.split(/\|/, 6);
|
|
90
109
|
const partCount = parts.length;
|
|
110
|
+
// If the message partitions are too few:
|
|
91
111
|
if (partCount < 6) {
|
|
112
|
+
// Report this.
|
|
92
113
|
console.log(`ERROR: Violation string ${string} has too few parts`);
|
|
93
114
|
}
|
|
94
|
-
//
|
|
95
|
-
else if (['Error'
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
result
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
// Otherwise, if the message reports an error:
|
|
116
|
+
else if (parts[0] === 'Error') {
|
|
117
|
+
// Add the rest of its message to the native-result errors.
|
|
118
|
+
nativeResult.error.push(parts.slice(1));
|
|
119
|
+
// Increment the error total.
|
|
120
|
+
nativeResult.totals.failed++;
|
|
121
|
+
}
|
|
122
|
+
// Otherwise, if the message reports a warning:
|
|
123
|
+
else if (parts[0] === 'Warning') {
|
|
124
|
+
// Add the rest of its message to the native-result warnings.
|
|
125
|
+
nativeResult.warning.push(parts.slice(1));
|
|
126
|
+
// Increment the warning total.
|
|
127
|
+
nativeResult.totals.cantTell++;
|
|
128
|
+
}
|
|
129
|
+
// If standard results are to be reported:
|
|
130
|
+
if (standard) {
|
|
131
|
+
const instance = {
|
|
132
|
+
ruleID: parts[1],
|
|
133
|
+
what: parts[4],
|
|
134
|
+
ordinalSeverity: parts[0] === 'Warning' ? 0 : 2,
|
|
135
|
+
count: 1
|
|
136
|
+
};
|
|
137
|
+
const xPath = getAttributeXPath(parts[5]) || '/html';
|
|
138
|
+
const catalogIndex = getXPathCatalogIndex(report.catalog, xPath);
|
|
139
|
+
instance.catalogIndex = catalogIndex ?? '';
|
|
140
|
+
if (! catalogIndex) {
|
|
141
|
+
instance.pathID = xPath;
|
|
142
|
+
}
|
|
143
|
+
standardResult.instances.push(instance);
|
|
115
144
|
}
|
|
116
145
|
}
|
|
146
|
+
standardResult.totals[0] = nativeResult.totals.cantTell;
|
|
147
|
+
standardResult.totals[2] = nativeResult.totals.failed;
|
|
117
148
|
}
|
|
118
149
|
return {
|
|
119
150
|
data,
|
package/tests/ibm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
3
|
+
© 2025–2026 Jonathan Robert Pool.
|
|
4
4
|
|
|
5
5
|
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
6
|
https://opensource.org/license/mit/ for details.
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
// IMPORTS
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
const {getAttributeXPath, getXPathCatalogIndex} = require('../procs/xPath');
|
|
24
24
|
const accessibilityChecker = require('accessibility-checker');
|
|
25
25
|
const {getCompliance} = accessibilityChecker;
|
|
26
26
|
|
|
@@ -121,17 +121,33 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
121
121
|
const act = report.acts[actIndex];
|
|
122
122
|
const {withItems, withNewContent, rules} = act;
|
|
123
123
|
const contentType = withNewContent ? 'new' : 'existing';
|
|
124
|
+
// Initialize the act report.
|
|
125
|
+
const result = {
|
|
126
|
+
nativeResult: {},
|
|
127
|
+
standardResult: {}
|
|
128
|
+
};
|
|
129
|
+
const standard = report.standard !== 'no';
|
|
130
|
+
// If standard results are to be reported:
|
|
131
|
+
if (standard) {
|
|
132
|
+
// Initialize the standard result.
|
|
133
|
+
result.standardResult = {
|
|
134
|
+
prevented: false,
|
|
135
|
+
totals: [0, 0, 0, 0],
|
|
136
|
+
instances: []
|
|
137
|
+
};
|
|
138
|
+
}
|
|
124
139
|
try {
|
|
125
140
|
const typeContent = contentType === 'existing' ? page : page.url();
|
|
126
141
|
// Conduct the tests.
|
|
127
142
|
const runReport = await run(typeContent);
|
|
128
|
-
const
|
|
143
|
+
const actReport = runReport.report;
|
|
129
144
|
// If there were results:
|
|
130
|
-
if (
|
|
145
|
+
if (actReport) {
|
|
131
146
|
// Trim them.
|
|
132
|
-
|
|
133
|
-
const {
|
|
134
|
-
|
|
147
|
+
result.nativeResult = trimActReport(actReport, withItems, rules);
|
|
148
|
+
const {nativeResult, standardResult} = result;
|
|
149
|
+
const {error, totals} = nativeResult;
|
|
150
|
+
// If they were not trimmable:
|
|
135
151
|
if (error) {
|
|
136
152
|
// Return an act report with this error.
|
|
137
153
|
return {
|
|
@@ -142,10 +158,39 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
142
158
|
result: {}
|
|
143
159
|
}
|
|
144
160
|
}
|
|
145
|
-
// Otherwise, i.e. if
|
|
161
|
+
// Otherwise, i.e. if they were trimmable, and if standard results are to be reported:
|
|
162
|
+
if (standard) {
|
|
163
|
+
// Populate the totals of the standard result.
|
|
164
|
+
standardResult.totals = [totals.recommendation, 0, totals.violation, 0];
|
|
165
|
+
// For each item of the native result:
|
|
166
|
+
nativeResult.items.forEach(item => {
|
|
167
|
+
// Populate a standard instance.
|
|
168
|
+
const standardItem = {
|
|
169
|
+
ruleID: item.ruleId,
|
|
170
|
+
what: item.message,
|
|
171
|
+
ordinalSeverity: item.level === 'recommendation' ? 0 : 2,
|
|
172
|
+
count: 1
|
|
173
|
+
};
|
|
174
|
+
// Get the XPath from the added attribute, because path.dom is wrong.
|
|
175
|
+
const xPath = getAttributeXPath(item.snippet);
|
|
176
|
+
const catalogIndex = getXPathCatalogIndex(report.catalog, xPath);
|
|
177
|
+
// If a catalog index was found:
|
|
178
|
+
if (catalogIndex) {
|
|
179
|
+
// Add it to the standard instance.
|
|
180
|
+
standardItem.catalogIndex = catalogIndex;
|
|
181
|
+
}
|
|
182
|
+
// Otherwise, if no catalog index was found but the item has an XPath:
|
|
183
|
+
else if (xPath) {
|
|
184
|
+
// Add the XPath to the standard instance.
|
|
185
|
+
standardItem.pathID = xPath;
|
|
186
|
+
}
|
|
187
|
+
// Add the standard instance to the standard result.
|
|
188
|
+
standardResult.instances.push(standardItem);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
146
191
|
return {
|
|
147
192
|
data: {},
|
|
148
|
-
result
|
|
193
|
+
result
|
|
149
194
|
};
|
|
150
195
|
}
|
|
151
196
|
// Otherwise, i.e. if there was only an error, return it in an act report.
|
package/tests/nuVal.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jonathan Robert Pool.
|
|
3
4
|
|
|
4
5
|
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
6
|
https://opensource.org/license/mit/ for details.
|
|
@@ -15,8 +16,8 @@
|
|
|
15
16
|
|
|
16
17
|
// IMPORTS
|
|
17
18
|
|
|
18
|
-
// Module to get the content.
|
|
19
19
|
const {curate, getContent} = require('../procs/nu');
|
|
20
|
+
const {getAttributeXPath, getXPathCatalogIndex} = require('../procs/xPath');
|
|
20
21
|
|
|
21
22
|
// FUNCTIONS
|
|
22
23
|
|
|
@@ -24,27 +25,44 @@ const {curate, getContent} = require('../procs/nu');
|
|
|
24
25
|
exports.reporter = async (page, report, actIndex) => {
|
|
25
26
|
const act = report.acts[actIndex];
|
|
26
27
|
const {rules, withSource} = act;
|
|
27
|
-
//
|
|
28
|
-
const data =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// Initialize the act report.
|
|
29
|
+
const data = {};
|
|
30
|
+
const result = {
|
|
31
|
+
nativeResult: {},
|
|
32
|
+
standardResult: {}
|
|
33
|
+
};
|
|
34
|
+
const standard = report.standard !== 'no';
|
|
35
|
+
// If standard results are to be reported:
|
|
36
|
+
if (standard) {
|
|
37
|
+
// Initialize the standard result.
|
|
38
|
+
result.standardResult = {
|
|
39
|
+
prevented: false,
|
|
40
|
+
totals: [0, 0, 0, 0],
|
|
41
|
+
instances: []
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const {standardResult} = result;
|
|
45
|
+
// Get the content.
|
|
46
|
+
const content = await getContent(page, withSource);
|
|
47
|
+
const {testTarget} = content;
|
|
48
|
+
// If it was obtained and contains a test target:
|
|
49
|
+
if (testTarget) {
|
|
33
50
|
const fetchOptions = {
|
|
34
51
|
method: 'post',
|
|
35
52
|
headers: {
|
|
36
53
|
'User-Agent': 'Mozilla/5.0',
|
|
37
54
|
'Content-Type': 'text/html; charset=utf-8'
|
|
38
|
-
}
|
|
55
|
+
},
|
|
56
|
+
body: testTarget
|
|
39
57
|
};
|
|
40
58
|
const nuURL = 'https://validator.w3.org/nu/?parser=html&out=json';
|
|
59
|
+
let nuData;
|
|
41
60
|
try {
|
|
42
|
-
fetchOptions.body = data.testTarget;
|
|
43
61
|
// Get a Nu Html Checker report from the W3C validator service.
|
|
44
62
|
nuResponse = await fetch(nuURL, fetchOptions);
|
|
45
63
|
// If the acquisition succeeded:
|
|
46
64
|
if (nuResponse.ok) {
|
|
47
|
-
// Get the response body as
|
|
65
|
+
// Get the response body as an object.
|
|
48
66
|
nuData = await nuResponse.json();
|
|
49
67
|
}
|
|
50
68
|
// Otherwise, i.e. if the request failed:
|
|
@@ -64,8 +82,43 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
64
82
|
data.prevented = true;
|
|
65
83
|
data.error = message;
|
|
66
84
|
};
|
|
67
|
-
// Postprocess the response data.
|
|
68
|
-
result = await curate(
|
|
85
|
+
// Postprocess the response data and add the postprocessed data to the native result.
|
|
86
|
+
result.nativeResult = await curate(data, nuData, rules);
|
|
87
|
+
// If standard results are to be reported:
|
|
88
|
+
if (standard) {
|
|
89
|
+
// For each message in the native result:
|
|
90
|
+
result.nativeResult.messages.forEach(message => {
|
|
91
|
+
const ordinalSeverity = message.type === 'info' ? 0 : 3;
|
|
92
|
+
// Increment the applicable standard-result total.
|
|
93
|
+
standardResult.totals[ordinalSeverity]++;
|
|
94
|
+
// Initialize a standard instance.
|
|
95
|
+
const standardInstance = {
|
|
96
|
+
ruleID: message.message,
|
|
97
|
+
what: message.message,
|
|
98
|
+
ordinalSeverity,
|
|
99
|
+
count: 1,
|
|
100
|
+
};
|
|
101
|
+
// Get the XPath of the element from its extract.
|
|
102
|
+
const xPath = getAttributeXPath(message.extract);
|
|
103
|
+
// If the acquisition succeeded:
|
|
104
|
+
if (xPath) {
|
|
105
|
+
// Get the catalog index of the element from the XPath.
|
|
106
|
+
const catalogIndex = getXPathCatalogIndex(report.catalog, xPath);
|
|
107
|
+
// If the acquisition succeeded:
|
|
108
|
+
if (catalogIndex) {
|
|
109
|
+
// Add the catalog index to the standard instance.
|
|
110
|
+
standardInstance.catalogIndex = catalogIndex;
|
|
111
|
+
}
|
|
112
|
+
// Otherwise, i.e. if the acquisition failed:
|
|
113
|
+
else {
|
|
114
|
+
// Add the XPath of the standard instance as its pathID.
|
|
115
|
+
standardInstance.pathID = xPath;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Add the standard instance to the standard result.
|
|
119
|
+
standardResult.instances.push(standardInstance);
|
|
120
|
+
})
|
|
121
|
+
}
|
|
69
122
|
}
|
|
70
123
|
// Otherwise, i.e. if the page content was not obtained:
|
|
71
124
|
else {
|
package/tests/nuVnu.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
3
|
+
© 2025–2026 Jonathan Robert Pool.
|
|
4
4
|
|
|
5
5
|
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
6
|
https://opensource.org/license/mit/ for details.
|
|
@@ -16,14 +16,11 @@
|
|
|
16
16
|
|
|
17
17
|
// IMPORTS
|
|
18
18
|
|
|
19
|
-
// Module to perform file operations.
|
|
20
19
|
const fs = require('fs/promises');
|
|
21
|
-
// Module to define the operating-system temporary-file directory.
|
|
22
20
|
const os = require('os');
|
|
23
|
-
// Module to run tests.
|
|
24
21
|
const {vnu} = require('vnu-jar');
|
|
25
|
-
// Module to get the content.
|
|
26
22
|
const {curate, getContent} = require('../procs/nu');
|
|
23
|
+
const {getAttributeXPath, getXPathCatalogIndex} = require('../procs/xPath');
|
|
27
24
|
|
|
28
25
|
// CONSTANTS
|
|
29
26
|
|
|
@@ -33,6 +30,23 @@ const tmpDir = os.tmpdir();
|
|
|
33
30
|
|
|
34
31
|
// Conducts and reports the Nu Html Checker tests.
|
|
35
32
|
exports.reporter = async (page, report, actIndex) => {
|
|
33
|
+
// Initialize the act report.
|
|
34
|
+
const data = {};
|
|
35
|
+
const result = {
|
|
36
|
+
nativeResult: {},
|
|
37
|
+
standardResult: {}
|
|
38
|
+
};
|
|
39
|
+
const standard = report.standard !== 'no';
|
|
40
|
+
// If standard results are to be reported:
|
|
41
|
+
if (standard) {
|
|
42
|
+
// Initialize the standard result.
|
|
43
|
+
result.standardResult = {
|
|
44
|
+
prevented: false,
|
|
45
|
+
totals: [0, 0, 0, 0],
|
|
46
|
+
instances: []
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const {standardResult} = result;
|
|
36
50
|
// Get the nuVal act, if it exists.
|
|
37
51
|
const nuValAct = report.acts.find(act => act.type === 'test' && act.which === 'nuVal');
|
|
38
52
|
// If it does not exist or it exists but was prevented:
|
|
@@ -40,13 +54,13 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
40
54
|
const act = report.acts[actIndex];
|
|
41
55
|
const {rules, withSource} = act;
|
|
42
56
|
// Get the content.
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
// If it was obtained:
|
|
46
|
-
if (
|
|
57
|
+
const content = await getContent(page, withSource);
|
|
58
|
+
const {testTarget} = content;
|
|
59
|
+
// If it was obtained and contains a test target:
|
|
60
|
+
if (testTarget) {
|
|
47
61
|
const pagePath = `${tmpDir}/nuVnu-page-${report.id}.html`;
|
|
48
|
-
// Save
|
|
49
|
-
await fs.writeFile(pagePath,
|
|
62
|
+
// Save the test target in a temporary file.
|
|
63
|
+
await fs.writeFile(pagePath, testTarget);
|
|
50
64
|
let nuData;
|
|
51
65
|
try {
|
|
52
66
|
// Get Nu Html Checker output on it.
|
|
@@ -56,7 +70,7 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
56
70
|
catch (error) {
|
|
57
71
|
const errorMessage = error.message;
|
|
58
72
|
try {
|
|
59
|
-
|
|
73
|
+
// Parse it as JSON, i.e. a benign nuVnu result with at least 1 violation.
|
|
60
74
|
nuData = JSON.parse(error.message);
|
|
61
75
|
}
|
|
62
76
|
// If parsing it as JSON fails:
|
|
@@ -68,14 +82,50 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
68
82
|
}
|
|
69
83
|
// Delete the temporary file.
|
|
70
84
|
await fs.unlink(pagePath);
|
|
71
|
-
// Postprocess the result.
|
|
72
|
-
result = await curate(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
result
|
|
76
|
-
|
|
85
|
+
// Postprocess the output and add the postprocessed output to the native result.
|
|
86
|
+
result.nativeResult = await curate(data, nuData, rules);
|
|
87
|
+
// If standard results are to be reported:
|
|
88
|
+
if (standard) {
|
|
89
|
+
// For each message in the native result:
|
|
90
|
+
result.nativeResult.messages.forEach(message => {
|
|
91
|
+
const ordinalSeverity = message.type === 'info' ? 0 : 3;
|
|
92
|
+
// Increment the applicable standard-result total.
|
|
93
|
+
standardResult.totals[ordinalSeverity]++;
|
|
94
|
+
// Initialize a standard instance.
|
|
95
|
+
const standardInstance = {
|
|
96
|
+
ruleID: message.message,
|
|
97
|
+
what: message.message,
|
|
98
|
+
ordinalSeverity,
|
|
99
|
+
count: 1,
|
|
100
|
+
};
|
|
101
|
+
// Get the XPath of the element from its extract.
|
|
102
|
+
const xPath = getAttributeXPath(message.extract);
|
|
103
|
+
// If the acquisition succeeded:
|
|
104
|
+
if (xPath) {
|
|
105
|
+
// Get the catalog index of the element from the XPath.
|
|
106
|
+
const catalogIndex = getXPathCatalogIndex(report.catalog, xPath);
|
|
107
|
+
// If the acquisition succeeded:
|
|
108
|
+
if (catalogIndex) {
|
|
109
|
+
// Add the catalog index to the standard instance.
|
|
110
|
+
standardInstance.catalogIndex = catalogIndex;
|
|
111
|
+
}
|
|
112
|
+
// Otherwise, i.e. if the acquisition failed:
|
|
113
|
+
else {
|
|
114
|
+
// Add the XPath of the standard instance as its pathID.
|
|
115
|
+
standardInstance.pathID = xPath;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Otherwise, i.e. if the acquisition failed:
|
|
119
|
+
else {
|
|
120
|
+
// Add the extract of the native instance to the standard instance.
|
|
121
|
+
standardInstance.excerpt = message.extract ?? '';
|
|
122
|
+
}
|
|
123
|
+
// Add the standard instance to the standard result.
|
|
124
|
+
standardResult.instances.push(standardInstance);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
77
127
|
}
|
|
78
|
-
// Otherwise, i.e. if the content was not obtained:
|
|
128
|
+
// Otherwise, i.e. if the content was not obtained or contains no test target:
|
|
79
129
|
else {
|
|
80
130
|
// Report this.
|
|
81
131
|
data.prevented = true;
|
|
@@ -85,12 +135,12 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
85
135
|
// Otherwise, i.e. if the nuVal act exists and succeeded:
|
|
86
136
|
else {
|
|
87
137
|
// Abort this act and report this.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
skipped: true,
|
|
91
|
-
reason: 'nuVal succeeded'
|
|
92
|
-
},
|
|
93
|
-
result: {}
|
|
94
|
-
};
|
|
138
|
+
data.skipped = true;
|
|
139
|
+
data.reason = 'nuVal succeeded';
|
|
95
140
|
}
|
|
141
|
+
// Return the data and result.
|
|
142
|
+
return {
|
|
143
|
+
data,
|
|
144
|
+
result
|
|
145
|
+
};
|
|
96
146
|
};
|