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/procs/standardize.js
DELETED
|
@@ -1,524 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
4
|
-
|
|
5
|
-
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
|
-
https://opensource.org/license/mit/ for details.
|
|
7
|
-
|
|
8
|
-
SPDX-License-Identifier: MIT
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/*
|
|
12
|
-
standardize.js
|
|
13
|
-
Converts test results to the standard format.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
// IMPORTS
|
|
17
|
-
|
|
18
|
-
const {cap} = require('./job');
|
|
19
|
-
|
|
20
|
-
// FUNCTIONS
|
|
21
|
-
|
|
22
|
-
// Returns whether an id attribute value is valid without character escaping.
|
|
23
|
-
const isBadID = id => /[^-\w]|^\d|^--|^-\d/.test(id);
|
|
24
|
-
// Returns a tag name and the value of an id attribute from a substring of HTML code.
|
|
25
|
-
const getIdentifiers = exports.getIdentifiers = code => {
|
|
26
|
-
// Normalize the code.
|
|
27
|
-
code = code.replace(/\s+/g, ' ').replace(/\\"/g, '"');
|
|
28
|
-
// Get the first start tag of an element, if any.
|
|
29
|
-
const startTagData = code.match(/^.*?<([a-zA-][^>]*)/);
|
|
30
|
-
// If there is any:
|
|
31
|
-
if (startTagData) {
|
|
32
|
-
// Get the tag name.
|
|
33
|
-
const tagNameArray = startTagData[1].match(/^[A-Za-z0-9]+/);
|
|
34
|
-
const tagName = tagNameArray ? tagNameArray[0].toUpperCase() : '';
|
|
35
|
-
// Get the value of the id attribute, if any.
|
|
36
|
-
const identifierData = startTagData[1].match(/ id="([^"]+)"/);
|
|
37
|
-
const id = identifierData ? identifierData[1] : '';
|
|
38
|
-
// Return the tag name and the value of the id attribute, if any.
|
|
39
|
-
return [tagName, id];
|
|
40
|
-
}
|
|
41
|
-
return ['', ''];
|
|
42
|
-
};
|
|
43
|
-
// Converts issue instances at an axe certainty level.
|
|
44
|
-
const doAxe = (result, standardResult, certainty) => {
|
|
45
|
-
if (result.details && result.details[certainty]) {
|
|
46
|
-
result.details[certainty].forEach(rule => {
|
|
47
|
-
rule.nodes.forEach(node => {
|
|
48
|
-
const whatSet = new Set([
|
|
49
|
-
rule.help,
|
|
50
|
-
... node.any.map(anyItem => anyItem.message),
|
|
51
|
-
... node.all.map(allItem => allItem.message)
|
|
52
|
-
]);
|
|
53
|
-
const severityWeights = {
|
|
54
|
-
minor: 0,
|
|
55
|
-
moderate: 0,
|
|
56
|
-
serious: 1,
|
|
57
|
-
critical: 1
|
|
58
|
-
};
|
|
59
|
-
const ordinalSeverity = severityWeights[node.impact] + (certainty === 'violations' ? 2 : 0);
|
|
60
|
-
const identifiers = getIdentifiers(node.html);
|
|
61
|
-
const instance = {
|
|
62
|
-
ruleID: rule.id,
|
|
63
|
-
what: Array.from(whatSet.values()).join('; '),
|
|
64
|
-
ordinalSeverity,
|
|
65
|
-
tagName: identifiers[0],
|
|
66
|
-
id: identifiers[1],
|
|
67
|
-
location: {
|
|
68
|
-
doc: 'dom',
|
|
69
|
-
type: 'selector',
|
|
70
|
-
spec: node.target && node.target.length ? node.target[0] : ''
|
|
71
|
-
},
|
|
72
|
-
excerpt: cap(node.html),
|
|
73
|
-
boxID: '',
|
|
74
|
-
pathID: ''
|
|
75
|
-
};
|
|
76
|
-
standardResult.instances.push(instance);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
// Converts issue instances at an htmlcs severity level.
|
|
82
|
-
const doHTMLCS = (result, standardResult, severity) => {
|
|
83
|
-
if (result[severity]) {
|
|
84
|
-
Object.keys(result[severity]).forEach(ruleID => {
|
|
85
|
-
const ruleData = result[severity][ruleID];
|
|
86
|
-
Object.keys(ruleData).forEach(what => {
|
|
87
|
-
ruleData[what].forEach(item => {
|
|
88
|
-
const {tagName, id, excerpt, notInDOM, boxID, pathID} = item;
|
|
89
|
-
const instance = {
|
|
90
|
-
ruleID,
|
|
91
|
-
what,
|
|
92
|
-
ordinalSeverity: ['Warning', '', '', 'Error'].indexOf(severity),
|
|
93
|
-
tagName: tagName.toUpperCase(),
|
|
94
|
-
id: isBadID(id.slice(1)) ? '' : id.slice(1),
|
|
95
|
-
location: {
|
|
96
|
-
doc: notInDOM ? 'notInDOM' : 'dom',
|
|
97
|
-
type: '',
|
|
98
|
-
spec: ''
|
|
99
|
-
},
|
|
100
|
-
excerpt: cap(excerpt),
|
|
101
|
-
boxID,
|
|
102
|
-
pathID
|
|
103
|
-
};
|
|
104
|
-
standardResult.instances.push(instance);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
// Converts issue instances from a nuVal or nuVnu result.
|
|
111
|
-
const doNu = (withSource, result, standardResult) => {
|
|
112
|
-
const items = result && result.messages;
|
|
113
|
-
// If there are any messages:
|
|
114
|
-
if (items && items.length) {
|
|
115
|
-
// For each one:
|
|
116
|
-
items.forEach(item => {
|
|
117
|
-
const {
|
|
118
|
-
extract,
|
|
119
|
-
firstColumn,
|
|
120
|
-
lastColumn,
|
|
121
|
-
lastLine,
|
|
122
|
-
message,
|
|
123
|
-
subType,
|
|
124
|
-
type,
|
|
125
|
-
elementLocation
|
|
126
|
-
} = item;
|
|
127
|
-
const identifiers = getIdentifiers(extract);
|
|
128
|
-
if (! identifiers[0] && message) {
|
|
129
|
-
const tagNameLCArray = message.match(
|
|
130
|
-
/^Element ([^ ]+)|^An (img) element| (meta|script) element| element (script)| tag (script)/
|
|
131
|
-
);
|
|
132
|
-
if (tagNameLCArray && tagNameLCArray[1]) {
|
|
133
|
-
identifiers[0] = tagNameLCArray[1].toUpperCase();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
let spec = '';
|
|
137
|
-
const locationSegments = [lastLine, firstColumn, lastColumn];
|
|
138
|
-
if (locationSegments.every(segment => typeof segment === 'number')) {
|
|
139
|
-
spec = locationSegments.join(':');
|
|
140
|
-
}
|
|
141
|
-
const {notInDOM} = elementLocation;
|
|
142
|
-
const instance = {
|
|
143
|
-
ruleID: message,
|
|
144
|
-
what: message,
|
|
145
|
-
ordinalSeverity: -1,
|
|
146
|
-
tagName: identifiers[0],
|
|
147
|
-
id: identifiers[1],
|
|
148
|
-
location: {
|
|
149
|
-
doc: withSource ? 'source' : (notInDOM ? 'notInDOM' : 'dom'),
|
|
150
|
-
type: 'code',
|
|
151
|
-
spec
|
|
152
|
-
},
|
|
153
|
-
excerpt: cap(extract),
|
|
154
|
-
boxID: elementLocation?.boxID || '',
|
|
155
|
-
pathID: elementLocation?.pathID || ''
|
|
156
|
-
};
|
|
157
|
-
if (type === 'info' && subType === 'warning') {
|
|
158
|
-
instance.ordinalSeverity = 0;
|
|
159
|
-
}
|
|
160
|
-
else if (type === 'error') {
|
|
161
|
-
instance.ordinalSeverity = subType === 'fatal' ? 3 : 2;
|
|
162
|
-
}
|
|
163
|
-
standardResult.instances.push(instance);
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
// Converts issue instances from a nuVal result.
|
|
168
|
-
const doNuVal = (withSource, result, standardResult) => {
|
|
169
|
-
doNu(withSource, result, standardResult);
|
|
170
|
-
};
|
|
171
|
-
// Converts issue instances from a nuVnu result.
|
|
172
|
-
const doNuVnu = (withSource, result, standardResult) => {
|
|
173
|
-
doNu(withSource, result, standardResult);
|
|
174
|
-
};
|
|
175
|
-
// Converts instances of a qualWeb rule class.
|
|
176
|
-
const doQualWeb = (result, standardResult, ruleClassName) => {
|
|
177
|
-
if (result.modules && result.modules[ruleClassName]) {
|
|
178
|
-
const ruleClass = result.modules[ruleClassName];
|
|
179
|
-
const severities = {
|
|
180
|
-
'best-practices': {
|
|
181
|
-
warning: 0,
|
|
182
|
-
failed: 1
|
|
183
|
-
},
|
|
184
|
-
'wcag-techniques': {
|
|
185
|
-
warning: 0,
|
|
186
|
-
failed: 2
|
|
187
|
-
},
|
|
188
|
-
'act-rules': {
|
|
189
|
-
warning: 1,
|
|
190
|
-
failed: 3
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
Object.keys(ruleClass.assertions).forEach(ruleID => {
|
|
194
|
-
const ruleResult = ruleClass.assertions[ruleID];
|
|
195
|
-
ruleResult.results.forEach(item => {
|
|
196
|
-
item.elements.forEach(element => {
|
|
197
|
-
const {htmlCode} = element;
|
|
198
|
-
const identifiers = getIdentifiers(htmlCode);
|
|
199
|
-
const instance = {
|
|
200
|
-
ruleID,
|
|
201
|
-
what: item.description,
|
|
202
|
-
ordinalSeverity: severities[ruleClassName][item.verdict] || 0,
|
|
203
|
-
tagName: identifiers[0],
|
|
204
|
-
id: identifiers[1],
|
|
205
|
-
location: {
|
|
206
|
-
doc: 'dom',
|
|
207
|
-
type: 'selector',
|
|
208
|
-
spec: element.pointer
|
|
209
|
-
},
|
|
210
|
-
excerpt: cap(htmlCode),
|
|
211
|
-
boxID: element.locationData?.boxID || '',
|
|
212
|
-
pathID: element.locationData?.pathID || ''
|
|
213
|
-
};
|
|
214
|
-
standardResult.instances.push(instance);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
// Converts instances of a wave rule category.
|
|
221
|
-
const doWAVE = (result, standardResult, categoryName) => {
|
|
222
|
-
// If results for the category are reported:
|
|
223
|
-
if (result.categories && result.categories[categoryName]) {
|
|
224
|
-
const category = result.categories[categoryName];
|
|
225
|
-
const ordinalSeverity = categoryName === 'alert' ? 0 : 3;
|
|
226
|
-
const {items} = category;
|
|
227
|
-
// If any violations in the category are reported:
|
|
228
|
-
if (items) {
|
|
229
|
-
// For each rule violated:
|
|
230
|
-
Object.keys(items).forEach(ruleID => {
|
|
231
|
-
const item = items[ruleID];
|
|
232
|
-
// For each violation:
|
|
233
|
-
item.selectors.forEach(violationFacts => {
|
|
234
|
-
let tagName = '';
|
|
235
|
-
let id = '';
|
|
236
|
-
const finalTerm = violationFacts[0].replace(/^.+\s/, '');
|
|
237
|
-
// If the selector is an element ID:
|
|
238
|
-
if (finalTerm.includes('#')) {
|
|
239
|
-
const finalArray = finalTerm.split('#');
|
|
240
|
-
// Use it to get the element tagname and ID
|
|
241
|
-
tagName = finalArray[0].replace(/:.*/, '');
|
|
242
|
-
id = isBadID(finalArray[1]) ? '' : finalArray[1];
|
|
243
|
-
}
|
|
244
|
-
// Otherwise, i.e. if the selector is not an element ID:
|
|
245
|
-
else {
|
|
246
|
-
// Get the element tagname from it.
|
|
247
|
-
tagName = finalTerm.replace(/:.*/, '') || 'HTML';
|
|
248
|
-
}
|
|
249
|
-
const {wcag} = item;
|
|
250
|
-
// Get a violation description suffix from the WCAG data of the rule.
|
|
251
|
-
const wcagSuffix = wcag.length
|
|
252
|
-
? ` (${wcag.map(wcagItem => wcagItem.name).join('; ')})`
|
|
253
|
-
: '';
|
|
254
|
-
// Get a standard instance.
|
|
255
|
-
const instance = {
|
|
256
|
-
ruleID,
|
|
257
|
-
what: `${item.description}${wcagSuffix}`,
|
|
258
|
-
ordinalSeverity,
|
|
259
|
-
tagName,
|
|
260
|
-
id,
|
|
261
|
-
location: {
|
|
262
|
-
doc: 'dom',
|
|
263
|
-
type: 'selector',
|
|
264
|
-
spec: violationFacts[0] || 'html'
|
|
265
|
-
},
|
|
266
|
-
excerpt: violationFacts[1],
|
|
267
|
-
boxID: '',
|
|
268
|
-
pathID: ''
|
|
269
|
-
};
|
|
270
|
-
// Add it to the standard result.
|
|
271
|
-
standardResult.instances.push(instance);
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
// Populates the initialized standard result of an act with the act data and result.
|
|
278
|
-
const convert = (toolName, data, result, standardResult) => {
|
|
279
|
-
// If the act data state that the act was prevented:
|
|
280
|
-
if (data.prevented) {
|
|
281
|
-
// Add that to the standard result and disregard tool-specific conversions.
|
|
282
|
-
standardResult.prevented = true;
|
|
283
|
-
}
|
|
284
|
-
// alfa, aslint
|
|
285
|
-
else if (['alfa', 'aslint'].includes(toolName) && result.standardResult) {
|
|
286
|
-
// Move the results to standard locations.
|
|
287
|
-
Object.assign(result, result.nativeResult);
|
|
288
|
-
Object.assign(standardResult, result.standardResult);
|
|
289
|
-
delete result.nativeResult;
|
|
290
|
-
delete result.standardResult;
|
|
291
|
-
}
|
|
292
|
-
// axe
|
|
293
|
-
else if (
|
|
294
|
-
toolName === 'axe'
|
|
295
|
-
&& result
|
|
296
|
-
&& result.totals
|
|
297
|
-
&& (result.totals.rulesWarned || result.totals.rulesViolated)
|
|
298
|
-
) {
|
|
299
|
-
doAxe(result, standardResult, 'incomplete');
|
|
300
|
-
doAxe(result, standardResult, 'violations');
|
|
301
|
-
}
|
|
302
|
-
// ed11y
|
|
303
|
-
else if (
|
|
304
|
-
toolName === 'ed11y'
|
|
305
|
-
&& result
|
|
306
|
-
&& ['imageAlts', 'violations', 'errorCount', 'warningCount']
|
|
307
|
-
.every(key => result[key] !== undefined)
|
|
308
|
-
) {
|
|
309
|
-
// For each violation:
|
|
310
|
-
result.violations.forEach(violation => {
|
|
311
|
-
const {test, content, tagName, id, loc, excerpt, boxID, pathID} = violation;
|
|
312
|
-
if (['test', 'content'].every(key => key)) {
|
|
313
|
-
// Standardize the what property.
|
|
314
|
-
let what = '';
|
|
315
|
-
if (content.includes('<p>This')) {
|
|
316
|
-
what = content.replace(/^.*?<p>(This.+?)<\/p> *<p>(.*?)<\/p>.*/, '$1 $2');
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
what = content.replace(/^.*?<p>(.+?)<\/p>.*/, '$1');
|
|
320
|
-
}
|
|
321
|
-
// Add a standard instance to the standard result.
|
|
322
|
-
standardResult.instances.push({
|
|
323
|
-
ruleID: test,
|
|
324
|
-
what,
|
|
325
|
-
ordinalSeverity: 0,
|
|
326
|
-
tagName,
|
|
327
|
-
id: isBadID(id) ? '' : id,
|
|
328
|
-
location: {
|
|
329
|
-
doc: 'dom',
|
|
330
|
-
type: 'box',
|
|
331
|
-
spec: loc
|
|
332
|
-
},
|
|
333
|
-
excerpt,
|
|
334
|
-
boxID,
|
|
335
|
-
pathID
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
// htmlcs
|
|
341
|
-
else if (toolName === 'htmlcs' && result) {
|
|
342
|
-
doHTMLCS(result, standardResult, 'Warning');
|
|
343
|
-
doHTMLCS(result, standardResult, 'Error');
|
|
344
|
-
}
|
|
345
|
-
// ibm
|
|
346
|
-
else if (toolName === 'ibm' && result.totals) {
|
|
347
|
-
if (result.items) {
|
|
348
|
-
result.items.forEach(item => {
|
|
349
|
-
const identifiers = getIdentifiers(item.snippet);
|
|
350
|
-
if (! identifiers[0] && item.path && item.path.dom) {
|
|
351
|
-
const tagNameArray = item.path.dom.match(/^.+\/([^/[]+)/s);
|
|
352
|
-
if (tagNameArray && tagNameArray.length === 2) {
|
|
353
|
-
identifiers[0] = tagNameArray[1].toUpperCase();
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
const instance = {
|
|
357
|
-
ruleID: item.ruleId,
|
|
358
|
-
what: item.message,
|
|
359
|
-
ordinalSeverity: ['', 'recommendation', '', 'violation'].indexOf(item.level),
|
|
360
|
-
tagName: identifiers[0],
|
|
361
|
-
id: identifiers[1],
|
|
362
|
-
location: {
|
|
363
|
-
doc: 'dom',
|
|
364
|
-
type: 'xpath',
|
|
365
|
-
spec: item.path.dom
|
|
366
|
-
},
|
|
367
|
-
excerpt: cap(item.snippet),
|
|
368
|
-
boxID: '',
|
|
369
|
-
pathID: ''
|
|
370
|
-
};
|
|
371
|
-
standardResult.instances.push(instance);
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// nuVal
|
|
376
|
-
else if (toolName === 'nuVal' && result) {
|
|
377
|
-
doNuVal(data.withSource, result, standardResult);
|
|
378
|
-
}
|
|
379
|
-
// nuVnu
|
|
380
|
-
else if (toolName === 'nuVnu' && result) {
|
|
381
|
-
doNuVnu(data.withSource, result, standardResult);
|
|
382
|
-
}
|
|
383
|
-
// qualWeb
|
|
384
|
-
else if (
|
|
385
|
-
toolName === 'qualWeb'
|
|
386
|
-
&& result.modules
|
|
387
|
-
&& (
|
|
388
|
-
result.modules['act-rules']
|
|
389
|
-
|| result.modules['wcag-techniques']
|
|
390
|
-
|| result.modules['best-practices']
|
|
391
|
-
)
|
|
392
|
-
) {
|
|
393
|
-
if (result.modules['act-rules']) {
|
|
394
|
-
doQualWeb(result, standardResult, 'act-rules');
|
|
395
|
-
}
|
|
396
|
-
if (result.modules['wcag-techniques']) {
|
|
397
|
-
doQualWeb(result, standardResult, 'wcag-techniques');
|
|
398
|
-
}
|
|
399
|
-
if (result.modules['best-practices']) {
|
|
400
|
-
doQualWeb(result, standardResult, 'best-practices');
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
// testaro
|
|
404
|
-
else if (toolName === 'testaro') {
|
|
405
|
-
// Initialize a record of instance totals by rule and severity.
|
|
406
|
-
data.ruleTotals = {};
|
|
407
|
-
// For each violated rule:
|
|
408
|
-
const rules = result ? Object.keys(result) : [];
|
|
409
|
-
rules.forEach(rule => {
|
|
410
|
-
// Copy its instances to the standard result.
|
|
411
|
-
const ruleResult = result[rule];
|
|
412
|
-
ruleResult.standardInstances ??= [];
|
|
413
|
-
standardResult.instances.push(... ruleResult.standardInstances);
|
|
414
|
-
// Initialize a record of its sample-ratio-weighted totals.
|
|
415
|
-
data.ruleTotals[rule] = [0, 0, 0, 0];
|
|
416
|
-
// Add those totals to the record and to the standard result.
|
|
417
|
-
ruleResult.totals ??= [0, 0, 0, 0];
|
|
418
|
-
for (const index in ruleResult.totals) {
|
|
419
|
-
const ruleTotal = ruleResult.totals[index];
|
|
420
|
-
data.ruleTotals[rule][index] += ruleTotal;
|
|
421
|
-
standardResult.totals[index] += ruleTotal;
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
const preventionCount = result.preventions && result.preventions.length;
|
|
425
|
-
if (preventionCount) {
|
|
426
|
-
standardResult.instances.push({
|
|
427
|
-
ruleID: 'testPrevention',
|
|
428
|
-
what: 'Page prevented tests from being performed',
|
|
429
|
-
ordinalSeverity: 3,
|
|
430
|
-
count: preventionCount,
|
|
431
|
-
tagName: '',
|
|
432
|
-
id: '',
|
|
433
|
-
location: '',
|
|
434
|
-
excerpt: '',
|
|
435
|
-
boxID: '',
|
|
436
|
-
pathID: ''
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
// wave
|
|
441
|
-
else if (
|
|
442
|
-
toolName === 'wave'
|
|
443
|
-
&& result.categories
|
|
444
|
-
&& (
|
|
445
|
-
result.categories.error
|
|
446
|
-
|| result.categories.contrast
|
|
447
|
-
|| result.categories.alert
|
|
448
|
-
)
|
|
449
|
-
) {
|
|
450
|
-
const {categories} = result;
|
|
451
|
-
standardResult.totals = [0, 0, 0, 0];
|
|
452
|
-
standardResult.totals[0] = categories.alert.count;
|
|
453
|
-
standardResult.totals[3] = (categories.error.count || 0) + (categories.contrast.count || 0);
|
|
454
|
-
['error', 'contrast', 'alert'].forEach(categoryName => {
|
|
455
|
-
doWAVE(result, standardResult, categoryName);
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
// wax
|
|
459
|
-
else if (toolName === 'wax' && result.violations && result.violations.length) {
|
|
460
|
-
// For each violation:
|
|
461
|
-
result.violations.forEach(violation => {
|
|
462
|
-
// Get its standard instance properties.
|
|
463
|
-
const element = violation.element.replace(/\s+/g, ' ');
|
|
464
|
-
const {boxID, description, message, notInDOM, pathID, severity} = violation;
|
|
465
|
-
const ordinalSeverity = ['Minor', 'Moderate', '', 'Severe'].indexOf(severity);
|
|
466
|
-
const tagNameCandidate = element.replace(/^<| .*$/g, '');
|
|
467
|
-
const tagName = /^[a-zA-Z0-9]+$/.test(tagNameCandidate) ? tagNameCandidate.toUpperCase() : '';
|
|
468
|
-
let id = '';
|
|
469
|
-
const location = {};
|
|
470
|
-
if (notInDOM) {
|
|
471
|
-
location.doc = 'notInDOM';
|
|
472
|
-
location.type = '';
|
|
473
|
-
location.spec = '';
|
|
474
|
-
}
|
|
475
|
-
else if (tagName) {
|
|
476
|
-
const idTerm = element
|
|
477
|
-
.replace(/>.*$/, '')
|
|
478
|
-
.split(' ')
|
|
479
|
-
.slice(1)
|
|
480
|
-
.find(term => term.startsWith('id="'));
|
|
481
|
-
if (idTerm) {
|
|
482
|
-
const idCandidate = idTerm.slice(4, -1);
|
|
483
|
-
if (! idCandidate.includes('"')) {
|
|
484
|
-
id = idCandidate;
|
|
485
|
-
location.doc = 'dom';
|
|
486
|
-
location.type = 'selector';
|
|
487
|
-
location.spec = `#${id}`;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
const instance = {
|
|
492
|
-
ruleID: message,
|
|
493
|
-
what: description,
|
|
494
|
-
ordinalSeverity,
|
|
495
|
-
tagName,
|
|
496
|
-
id,
|
|
497
|
-
location,
|
|
498
|
-
excerpt: element,
|
|
499
|
-
boxID,
|
|
500
|
-
pathID
|
|
501
|
-
};
|
|
502
|
-
// Add the instance to the standard result.
|
|
503
|
-
standardResult.instances.push(instance);
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
// Populate the totals of the standard result if the tool is not Alfa, Testaro or WAVE.
|
|
507
|
-
if (! ['alfa', 'testaro', 'wave'].includes(toolName)) {
|
|
508
|
-
standardResult.instances.forEach(instance => {
|
|
509
|
-
standardResult.totals[instance.ordinalSeverity] += instance.count || 1;
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
// Round the totals of the standard result.
|
|
513
|
-
standardResult.totals = standardResult.totals.map(total => Math.round(total));
|
|
514
|
-
};
|
|
515
|
-
// Populates the initialized standard result of an act.
|
|
516
|
-
exports.standardize = act => {
|
|
517
|
-
const {which, data, result, standardResult} = act;
|
|
518
|
-
if (which && result && standardResult) {
|
|
519
|
-
convert(which, data, result, standardResult);
|
|
520
|
-
}
|
|
521
|
-
else {
|
|
522
|
-
console.log(`ERROR: Result of incomplete ${which || 'unknown'} act cannot be standardized`);
|
|
523
|
-
}
|
|
524
|
-
};
|
package/procs/target.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
4
|
-
|
|
5
|
-
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
|
-
https://opensource.org/license/mit/ for details.
|
|
7
|
-
|
|
8
|
-
SPDX-License-Identifier: MIT
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/*
|
|
12
|
-
target
|
|
13
|
-
Utilities for Testaro targetSmall and targetTiny tests.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
// ########## IMPORTS
|
|
17
|
-
|
|
18
|
-
// Module to classify links.
|
|
19
|
-
const {isInlineLink} = require('./isInlineLink');
|
|
20
|
-
|
|
21
|
-
// ########## FUNCTIONS
|
|
22
|
-
|
|
23
|
-
// Returns data about a target if it is displayed and illicitly small.
|
|
24
|
-
exports.isTooSmall = async (loc, min) => {
|
|
25
|
-
let sizeData = null;
|
|
26
|
-
// If the target is not an inline link:
|
|
27
|
-
if (!(await isInlineLink(loc))) {
|
|
28
|
-
// Get data on it if it is too small.
|
|
29
|
-
sizeData = await loc.evaluate((el, min) => {
|
|
30
|
-
// Get its styles.
|
|
31
|
-
const styleDec = window.getComputedStyle(el);
|
|
32
|
-
const displayStyle = styleDec.display;
|
|
33
|
-
// If it is hidden (which should be impossible because of the selector):
|
|
34
|
-
if (displayStyle === 'none') {
|
|
35
|
-
// Exempt it.
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
// Otherwise, i.e. if it is displayed:
|
|
39
|
-
else {
|
|
40
|
-
// Gets the width and height of an element.
|
|
41
|
-
const getDims = el => {
|
|
42
|
-
const rect = el.getBoundingClientRect();
|
|
43
|
-
const widthR = rect && rect.width || 0;
|
|
44
|
-
const heightR = rect && rect.height || 0;
|
|
45
|
-
const widthP = el.offsetWidth || 0;
|
|
46
|
-
const width = Math.max(widthP, Math.round(widthR));
|
|
47
|
-
const heightP = el.offsetHeight || 0;
|
|
48
|
-
const height = Math.max(heightP, Math.round(heightR));
|
|
49
|
-
return [width, height];
|
|
50
|
-
};
|
|
51
|
-
const tagName = el.tagName;
|
|
52
|
-
// Get the width and height of the target.
|
|
53
|
-
const elDims = getDims(el);
|
|
54
|
-
// If either dimension is too small:
|
|
55
|
-
if (elDims.some(dim => dim < min)) {
|
|
56
|
-
// Get the parent element of the target.
|
|
57
|
-
const elParent = el.parentElement;
|
|
58
|
-
// If the parent element exists:
|
|
59
|
-
if (elParent) {
|
|
60
|
-
// Get the child elements of the parent, including the target.
|
|
61
|
-
const elGeneration = Array.from(elParent.children);
|
|
62
|
-
// If the target has no siblings of its type:
|
|
63
|
-
if (elGeneration.filter(peer => peer.tagName === tagName).length === 1) {
|
|
64
|
-
// Get the width and height of the parent.
|
|
65
|
-
const parentDims = getDims(elParent);
|
|
66
|
-
// For each dimension of the target:
|
|
67
|
-
elDims.forEach((elDim, index) => {
|
|
68
|
-
// If it is too small and smaller than that of the parent:
|
|
69
|
-
if (elDim < min && parentDims[index] > elDim) {
|
|
70
|
-
// Replace it with the dimension of the parent (a substitute for distance).
|
|
71
|
-
elDims[index] = parentDims[index];
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
// Otherwise, i.e. if it does not exist:
|
|
77
|
-
else {
|
|
78
|
-
console.log(`WARNING: Target ${tagName} (${el.innerHTML}) has no parent element`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
// Get whether it is too small.
|
|
82
|
-
const isSmall = elDims.some(dim => dim < min);
|
|
83
|
-
// Get its data if too small or its compliant status.
|
|
84
|
-
return isSmall ? {tagName, width: elDims[0], height: elDims[1]} : null;
|
|
85
|
-
}
|
|
86
|
-
}, min);
|
|
87
|
-
}
|
|
88
|
-
// Return data about the target or its compliant status.
|
|
89
|
-
return sizeData;
|
|
90
|
-
};
|
package/procs/tellServer.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
|
|
4
|
-
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
|
-
https://opensource.org/license/mit/ for details.
|
|
6
|
-
|
|
7
|
-
SPDX-License-Identifier: MIT
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/*
|
|
11
|
-
tellServer
|
|
12
|
-
Send a notice to an observer.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
// CONSTANTS
|
|
16
|
-
|
|
17
|
-
const httpClient = require('http');
|
|
18
|
-
const httpsClient = require('https');
|
|
19
|
-
const agent = process.env.AGENT;
|
|
20
|
-
|
|
21
|
-
// FUNCTIONS
|
|
22
|
-
|
|
23
|
-
// Sends a notice to an observer.
|
|
24
|
-
exports.tellServer = (report, messageParams, logMessage) => {
|
|
25
|
-
const {serverID} = report.sources;
|
|
26
|
-
const observerURL = typeof serverID === 'number'
|
|
27
|
-
? process.env[`NETWATCH_URL_${serverID}_OBSERVE`]
|
|
28
|
-
: '';
|
|
29
|
-
if (observerURL) {
|
|
30
|
-
const whoParams = `agent=${agent}&jobID=${report.id || ''}`;
|
|
31
|
-
const wholeURL = `${observerURL}?${whoParams}&${messageParams}`;
|
|
32
|
-
const client = wholeURL.startsWith('https://') ? httpsClient : httpClient;
|
|
33
|
-
client.request(wholeURL)
|
|
34
|
-
// If the notification threw an error:
|
|
35
|
-
.on('error', error => {
|
|
36
|
-
// Report the error.
|
|
37
|
-
const errorMessage = 'ERROR notifying the server';
|
|
38
|
-
console.log(`${errorMessage} (${error.message})`);
|
|
39
|
-
})
|
|
40
|
-
.end();
|
|
41
|
-
console.log(`${logMessage} (server notified)`);
|
|
42
|
-
}
|
|
43
|
-
};
|
package/scripts/dumpAlts.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
4
|
-
|
|
5
|
-
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
|
-
https://opensource.org/license/mit/ for details.
|
|
7
|
-
|
|
8
|
-
SPDX-License-Identifier: MIT
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const { chromium } = require('playwright');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
|
|
14
|
-
(async () => {
|
|
15
|
-
const browser = await chromium.launch();
|
|
16
|
-
const page = await browser.newPage();
|
|
17
|
-
const target = `file://${path.resolve(__dirname, '../validation/tests/targets/altScheme/index.html')}`;
|
|
18
|
-
console.log('Opening', target);
|
|
19
|
-
await page.goto(target, { waitUntil: 'domcontentloaded' });
|
|
20
|
-
const images = await page.$$eval('img', imgs => imgs.map(img => ({
|
|
21
|
-
id: img.id || null,
|
|
22
|
-
alt: img.getAttribute('alt'),
|
|
23
|
-
src: img.getAttribute('src'),
|
|
24
|
-
href: img.getAttribute('href')
|
|
25
|
-
})));
|
|
26
|
-
console.log(JSON.stringify(images, null, 2));
|
|
27
|
-
await browser.close();
|
|
28
|
-
})();
|