testaro 60.10.3 → 60.12.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/UPGRADES.md +66 -0
- package/package.json +1 -1
- package/procs/screenShot.js +1 -1
- package/procs/testaro.js +76 -0
- package/testaro/adbID.js +37 -68
- package/testaro/altScheme.js +25 -39
- package/testaro/captionLoc.js +19 -33
- package/testaro/datalistRef.js +34 -16
- package/testaro/embAc.js +15 -33
- package/testaro/focAndOp.js +146 -0
- package/testaro/focInd.js +57 -72
- package/testaro/lineHeight.js +25 -38
- package/testaro/miniText.js +31 -26
- package/tests/testaro.js +6 -13
- package/validation/tests/targets/datalistRef/index.html +3 -10
- package/procs/operable.js +0 -108
- package/testaro/focOp.js +0 -75
- package/testaro/opFoc.js +0 -76
package/UPGRADES.md
CHANGED
|
@@ -3239,3 +3239,69 @@ Given your goal to move on with [testaro](cci:7://file:///Users/pool/Users/pool/
|
|
|
3239
3239
|
- Plan separately for a future **element-matching module** that consumes all tools’ outputs and decides “these instances refer to the same DOM element”.
|
|
3240
3240
|
|
|
3241
3241
|
That way, you get immediate performance and implementation wins in [testaro](cci:7://file:///Users/pool/Users/pool/Documents/Topics/work/testaro:0:0-0:0), without locking yourself into the fragile assumption that “XPath strings must match exactly across all tools.
|
|
3242
|
+
|
|
3243
|
+
## Sampling and performance
|
|
3244
|
+
|
|
3245
|
+
Refactoring Testaro tests to eliminate sampling of elements began in December 2025. Initial results suggest that refactoring decreases elapsed test times despite the fact that all applicable elements are examined rather than only a sample.
|
|
3246
|
+
|
|
3247
|
+
In a run by the Kilotest server on the [home page of the Open Source Collective](https://opencollective.com/opensource), with about 2700 visible elements,the elapsed times of Testaro tests were:
|
|
3248
|
+
|
|
3249
|
+
```json
|
|
3250
|
+
"ruleTestTimes": {
|
|
3251
|
+
"allCaps": 11,
|
|
3252
|
+
"opFoc": 10,
|
|
3253
|
+
"allSlanted": 9,
|
|
3254
|
+
"hovInd": 9,
|
|
3255
|
+
"focOp": 8,
|
|
3256
|
+
"targetSmall": 7,
|
|
3257
|
+
"focAll": 7,
|
|
3258
|
+
"focVis": 6,
|
|
3259
|
+
"distortion": 5,
|
|
3260
|
+
"linkAmb": 5,
|
|
3261
|
+
"titledEl": 5,
|
|
3262
|
+
"zIndex": 5,
|
|
3263
|
+
"targetTiny": 4,
|
|
3264
|
+
"shoot1": 4,
|
|
3265
|
+
"linkTitle": 3,
|
|
3266
|
+
"hover": 3,
|
|
3267
|
+
"shoot0": 2,
|
|
3268
|
+
"adbID": 2,
|
|
3269
|
+
"linkUl": 2,
|
|
3270
|
+
"buttonMenu": 2,
|
|
3271
|
+
"focInd": 2,
|
|
3272
|
+
"tabNav": 2,
|
|
3273
|
+
"dupAtt": 0,
|
|
3274
|
+
"imageLink": 1,
|
|
3275
|
+
"labClash": 1,
|
|
3276
|
+
"allHidden": 0,
|
|
3277
|
+
"altScheme": 0,
|
|
3278
|
+
"autocomplete": 0,
|
|
3279
|
+
"bulk": 0,
|
|
3280
|
+
"captionLoc": 0,
|
|
3281
|
+
"datalistRef": 0,
|
|
3282
|
+
"docType": 0,
|
|
3283
|
+
"embAc": 0,
|
|
3284
|
+
"headEl": 0,
|
|
3285
|
+
"headingAmb": 0,
|
|
3286
|
+
"hr": 0,
|
|
3287
|
+
"legendLoc": 0,
|
|
3288
|
+
"lineHeight": 0,
|
|
3289
|
+
"linkExt": 0,
|
|
3290
|
+
"linkOldAtt": 0,
|
|
3291
|
+
"linkTo": 0,
|
|
3292
|
+
"miniText": 0,
|
|
3293
|
+
"nonTable": 0,
|
|
3294
|
+
"optRoleSel": 0,
|
|
3295
|
+
"phOnly": 0,
|
|
3296
|
+
"pseudoP": 0,
|
|
3297
|
+
"radioSet": 0,
|
|
3298
|
+
"role": 0,
|
|
3299
|
+
"secHeading": 0,
|
|
3300
|
+
"styleDiff": 0,
|
|
3301
|
+
"textSem": 0
|
|
3302
|
+
}
|
|
3303
|
+
```
|
|
3304
|
+
|
|
3305
|
+
All of the tests with elapsed times longer than 2 seconds were not yet refactored. Some of the refactored tests applied `checkVisibility` to all `body` descendant elements.
|
|
3306
|
+
|
|
3307
|
+
Credit for the speed improvement in refactored tests is apparently owed to the encapsulation of the entire test logic in a browser function, versus the repeated element-by-element execution of the same logic in Node.js with Playwright methods.
|
package/package.json
CHANGED
package/procs/screenShot.js
CHANGED
package/procs/testaro.js
CHANGED
|
@@ -157,3 +157,79 @@ exports.simplify = async (page, withItems, ruleData) => {
|
|
|
157
157
|
// Return the result.
|
|
158
158
|
return result;
|
|
159
159
|
};
|
|
160
|
+
// Performs a standard test.
|
|
161
|
+
exports.doTest = async (
|
|
162
|
+
page,
|
|
163
|
+
withItems,
|
|
164
|
+
ruleID,
|
|
165
|
+
candidateSelector,
|
|
166
|
+
whats,
|
|
167
|
+
severity,
|
|
168
|
+
summaryTagName,
|
|
169
|
+
getBadWhatString
|
|
170
|
+
) => {
|
|
171
|
+
// Return totals and standard instances for the rule.
|
|
172
|
+
return await page.evaluate(args => {
|
|
173
|
+
const [
|
|
174
|
+
withItems,
|
|
175
|
+
ruleID,
|
|
176
|
+
candidateSelector,
|
|
177
|
+
whats,
|
|
178
|
+
severity,
|
|
179
|
+
summaryTagName,
|
|
180
|
+
getBadWhatString
|
|
181
|
+
] = args;
|
|
182
|
+
// Get all candidates.
|
|
183
|
+
const candidates = document.body.querySelectorAll(candidateSelector);
|
|
184
|
+
let violationCount = 0;
|
|
185
|
+
const instances = [];
|
|
186
|
+
// Get a violation function.
|
|
187
|
+
const getBadWhat = eval(`(${getBadWhatString})`);
|
|
188
|
+
// For each candidate:
|
|
189
|
+
candidates.forEach(element => {
|
|
190
|
+
const violationWhat = getBadWhat(element);
|
|
191
|
+
// If it violates the rule:
|
|
192
|
+
if (violationWhat) {
|
|
193
|
+
// Increment the violation count.
|
|
194
|
+
violationCount++;
|
|
195
|
+
// If itemization is required:
|
|
196
|
+
if (withItems) {
|
|
197
|
+
const violationWhatStart = violationWhat.slice(0, 2);
|
|
198
|
+
let ruleSeverity = severity;
|
|
199
|
+
let ruleWhat = violationWhat
|
|
200
|
+
// If this violation has a custom severity:
|
|
201
|
+
if (/[0-3]:/.test(violationWhatStart)) {
|
|
202
|
+
// Get it and remove it from the violation description.
|
|
203
|
+
ruleSeverity = Number(violationWhat[0]);
|
|
204
|
+
ruleWhat = violationWhat.slice(2);
|
|
205
|
+
}
|
|
206
|
+
// Add an instance to the instances.
|
|
207
|
+
instances.push(
|
|
208
|
+
window.getInstance(element, ruleID, ruleWhat, 1, ruleSeverity)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
// If there are any violations and itemization is not required:
|
|
214
|
+
if (violationCount && ! withItems) {
|
|
215
|
+
// Add a summary instance to the instances.
|
|
216
|
+
instances.push(
|
|
217
|
+
window.getInstance(null, ruleID, whats, violationCount, severity, summaryTagName)
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
data: {},
|
|
222
|
+
totals: [0, 0, 0, violationCount],
|
|
223
|
+
standardInstances: instances
|
|
224
|
+
}
|
|
225
|
+
}, [
|
|
226
|
+
withItems,
|
|
227
|
+
ruleID,
|
|
228
|
+
candidateSelector,
|
|
229
|
+
whats,
|
|
230
|
+
severity,
|
|
231
|
+
summaryTagName,
|
|
232
|
+
getBadWhatString
|
|
233
|
+
]
|
|
234
|
+
);
|
|
235
|
+
};
|
package/testaro/adbID.js
CHANGED
|
@@ -32,81 +32,50 @@
|
|
|
32
32
|
the implementation of a test for a similar rule in the Tenon tool.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
+
// IMPORTS
|
|
36
|
+
|
|
37
|
+
const {doTest} = require('../procs/testaro');
|
|
38
|
+
|
|
35
39
|
// FUNCTIONS
|
|
36
40
|
|
|
37
41
|
// Runs the test and returns the result.
|
|
38
42
|
exports.reporter = async (page, withItems) => {
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
// Get
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
|
|
43
|
+
// Define a violation function for execution in the browser.
|
|
44
|
+
const getBadWhat = element => {
|
|
45
|
+
// Get the IDs in the aria-describedby attribute of the element.
|
|
46
|
+
const IDs = element.getAttribute('aria-describedby').trim().split(/\s+/).filter(Boolean);
|
|
47
|
+
// If there are none:
|
|
48
|
+
if (! IDs.length) {
|
|
49
|
+
// Return a violation description.
|
|
50
|
+
return 'Element has an aria-describedby attribute with no value';
|
|
51
|
+
}
|
|
52
|
+
// Otherwise, i.e. if there is at least 1 ID:
|
|
53
|
+
else {
|
|
54
|
+
// For each ID:
|
|
55
|
+
for (const id of IDs) {
|
|
56
|
+
// Get the element with that ID.
|
|
57
|
+
const describer = document.getElementById(id);
|
|
58
|
+
// If it doesn't exist:
|
|
59
|
+
if (! describer) {
|
|
60
|
+
// Return a violation description.
|
|
61
|
+
return `No element has the aria-describedby ID ${id}`;
|
|
58
62
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (! describer) {
|
|
68
|
-
// Increment the violation count.
|
|
69
|
-
violationCount++;
|
|
70
|
-
// If itemization is required:
|
|
71
|
-
if (withItems) {
|
|
72
|
-
const what = `No element has the aria-describedby ID ${id}`;
|
|
73
|
-
// Add an instance to the instances.
|
|
74
|
-
instances.push(window.getInstance(element, 'adbID', what, 1, 3));
|
|
75
|
-
}
|
|
76
|
-
// Stop checking the element.
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
// Otherwise, i.e. if it exists:
|
|
80
|
-
else {
|
|
81
|
-
// Get the elements with that ID.
|
|
82
|
-
const sameIDElements = document.querySelectorAll(`#${id}`);
|
|
83
|
-
// If there is more than one:
|
|
84
|
-
if (sameIDElements.length > 1) {
|
|
85
|
-
// Increment the violation count.
|
|
86
|
-
violationCount++;
|
|
87
|
-
// If itemization is required:
|
|
88
|
-
if (withItems) {
|
|
89
|
-
const what = `Multiple elements share the aria-describedby ID ${id}`;
|
|
90
|
-
// Add an instance to the instances.
|
|
91
|
-
instances.push(window.getInstance(element, 'adbID', what, 1, 2));
|
|
92
|
-
}
|
|
93
|
-
// Stop checking the element.
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
63
|
+
// Otherwise, i.e. if it exists:
|
|
64
|
+
else {
|
|
65
|
+
// Get the elements with that ID.
|
|
66
|
+
const sameIDElements = document.querySelectorAll(`#${id}`);
|
|
67
|
+
// If there is more than one:
|
|
68
|
+
if (sameIDElements.length > 1) {
|
|
69
|
+
// Return a violation description.
|
|
70
|
+
return `Multiple elements share the aria-describedby ID ${id}`;
|
|
96
71
|
}
|
|
97
72
|
}
|
|
98
73
|
}
|
|
99
|
-
});
|
|
100
|
-
// If there were any violations and itemization is not required:
|
|
101
|
-
if (violationCount && ! withItems) {
|
|
102
|
-
const what = 'Elements have aria-describedby attributes with missing or invalid id values';
|
|
103
|
-
// Add a summary instance to the instances.
|
|
104
|
-
instances.push(window.getInstance(null, 'adbID', what, violationCount, 3));
|
|
105
74
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
75
|
+
};
|
|
76
|
+
const whats = 'Elements have aria-describedby attributes with missing or invalid id values';
|
|
77
|
+
// Perform the test and return the result.
|
|
78
|
+
return doTest(
|
|
79
|
+
page, withItems, 'adbID', '[aria-describedby]', whats, 3, null, getBadWhat.toString()
|
|
80
|
+
);
|
|
112
81
|
};
|
package/testaro/altScheme.js
CHANGED
|
@@ -29,49 +29,35 @@
|
|
|
29
29
|
Identify img elements whose alt attribute is a URL or file name.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
+
// IMPORTS
|
|
33
|
+
|
|
34
|
+
const {doTest} = require('../procs/testaro');
|
|
35
|
+
|
|
32
36
|
// FUNCTIONS
|
|
33
37
|
|
|
34
38
|
// Runs the test and returns the result.
|
|
35
39
|
exports.reporter = async (page, withItems) => {
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
// Get
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
// Increment the violation count.
|
|
52
|
-
violationCount++;
|
|
53
|
-
// If itemization is required:
|
|
54
|
-
if (withItems) {
|
|
55
|
-
const valueType = isURL && isFileName
|
|
56
|
-
? 'the URL of an image file'
|
|
57
|
-
: (isURL ? 'a URL' : 'a file name');
|
|
58
|
-
const what = `img element has an alt attribute with ${valueType} as its value`;
|
|
59
|
-
// Add an instance to the instances.
|
|
60
|
-
instances.push(window.getInstance(element, 'altScheme', what, 1, 2));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
40
|
+
// Define a violation function for execution in the browser.
|
|
41
|
+
const getBadWhat = element => {
|
|
42
|
+
// Get the value of the alt attribute of the element.
|
|
43
|
+
const alt = (element.getAttribute('alt') || '').trim();
|
|
44
|
+
// If it is non-empty:
|
|
45
|
+
if (alt) {
|
|
46
|
+
const isURL = /^(?:https?:|file:|ftp:)\S+$/i.test(alt);
|
|
47
|
+
const isFileName = /favicon|^\S+\.(?:png|jpe?g|gif|svg|webp|ico)$/i.test(alt);
|
|
48
|
+
// If it is a URL or file name:
|
|
49
|
+
if (isURL || isFileName) {
|
|
50
|
+
const valueType = isURL && isFileName
|
|
51
|
+
? 'the URL of an image file'
|
|
52
|
+
: (isURL ? 'a URL' : 'a file name');
|
|
53
|
+
// Return a violation description.
|
|
54
|
+
return `img element has an alt attribute with ${valueType} as its value`;
|
|
63
55
|
}
|
|
64
|
-
});
|
|
65
|
-
// If there were any violations and itemization is not required:
|
|
66
|
-
if (violationCount && ! withItems) {
|
|
67
|
-
const what = 'img elements have alt attributes with URL or filename values';
|
|
68
|
-
// Add a summary instance to the instances.
|
|
69
|
-
instances.push(window.getInstance(null, 'altScheme', what, violationCount, 2, 'IMG'));
|
|
70
56
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
};
|
|
58
|
+
const whats = 'img elements have alt attributes with URL or filename values';
|
|
59
|
+
// Perform the test and return the result.
|
|
60
|
+
return doTest(
|
|
61
|
+
page, withItems, 'altScheme', 'img[alt]', whats, 1, 'IMG', getBadWhat.toString()
|
|
62
|
+
);
|
|
77
63
|
};
|
package/testaro/captionLoc.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
3
|
© 2025 Juan S. Casado. All rights reserved.
|
|
4
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
4
5
|
|
|
5
6
|
MIT License
|
|
6
7
|
|
|
@@ -25,43 +26,28 @@
|
|
|
25
26
|
|
|
26
27
|
/*
|
|
27
28
|
captionLoc
|
|
28
|
-
Report caption elements that are not the first
|
|
29
|
+
Report caption elements that are not the first children of table elements.
|
|
29
30
|
*/
|
|
30
31
|
|
|
32
|
+
// IMPORTS
|
|
33
|
+
|
|
34
|
+
const {doTest} = require('../procs/testaro');
|
|
35
|
+
|
|
31
36
|
// FUNCTIONS
|
|
32
37
|
|
|
33
38
|
exports.reporter = async (page, withItems) => {
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
candidates.forEach(element => {
|
|
42
|
-
const parent = element.parentElement;
|
|
43
|
-
// If the element is not the first child of a table element:
|
|
44
|
-
if (! parent || parent.tagName !== 'TABLE' || parent.firstElementChild !== el) {
|
|
45
|
-
// Increment the violation count.
|
|
46
|
-
violationCount++;
|
|
47
|
-
// If itemization is required:
|
|
48
|
-
if (withItems) {
|
|
49
|
-
const what = 'caption element is not the first child of a table element';
|
|
50
|
-
// Add an instance to the instances.
|
|
51
|
-
instances.push(window.getInstance(element, 'captionLoc', what, 1, 3));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
// If there are any violations and itemization is not required:
|
|
56
|
-
if (violationCount && ! withItems) {
|
|
57
|
-
const what = 'caption elements are not the first children of table elements';
|
|
58
|
-
// Add a summary instance to the instances.
|
|
59
|
-
instances.push(window.getInstance(null, 'captionLoc', what, violationCount, 3, 'caption'));
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
data: {},
|
|
63
|
-
totals: [0, 0, 0, violationCount],
|
|
64
|
-
standardInstances: instances
|
|
39
|
+
// Define a violation function for execution in the browser.
|
|
40
|
+
const getBadWhat = element => {
|
|
41
|
+
const parent = element.parentElement;
|
|
42
|
+
// If the element is not the first child of a table element:
|
|
43
|
+
if (! parent || parent.tagName !== 'TABLE' || parent.firstElementChild !== element) {
|
|
44
|
+
// Return a violation description.
|
|
45
|
+
return 'caption element is not the first child of a table element';
|
|
65
46
|
}
|
|
66
|
-
}
|
|
47
|
+
};
|
|
48
|
+
const whats = 'caption elements are not the first children of table elements';
|
|
49
|
+
// Perform the test and return the result.
|
|
50
|
+
return doTest(
|
|
51
|
+
page, withItems, 'captionLoc', 'caption', whats, 3, 'CAPTION', getBadWhat.toString()
|
|
52
|
+
);
|
|
67
53
|
};
|
package/testaro/datalistRef.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
© 2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
3
|
© 2025 Juan S. Casado. All rights reserved.
|
|
4
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
4
5
|
|
|
5
6
|
MIT License
|
|
6
7
|
|
|
@@ -28,22 +29,39 @@
|
|
|
28
29
|
Report inputs whose list attribute references a missing or ambiguous datalist
|
|
29
30
|
*/
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
// IMPORTS
|
|
33
|
+
|
|
34
|
+
const {doTest} = require('../procs/testaro');
|
|
35
|
+
|
|
36
|
+
// FUNCTIONS
|
|
32
37
|
|
|
33
38
|
exports.reporter = async (page, withItems) => {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
const getBadWhat = element => {
|
|
40
|
+
// Get the ID of the datalist element referenced by the list attribute of the element.
|
|
41
|
+
const listID = element.getAttribute('list');
|
|
42
|
+
// If the element has a list attribute with a non-empty value:
|
|
43
|
+
if (listID) {
|
|
44
|
+
// Get the element it references.
|
|
45
|
+
const listElement = document.getElementById(listID);
|
|
46
|
+
// If no such element exists:
|
|
47
|
+
if (! listElement) {
|
|
48
|
+
// Return a violation description.
|
|
49
|
+
return 'input element list attribute references a missing element';
|
|
50
|
+
}
|
|
51
|
+
// Otherwise, if the element it references is not a datalist:
|
|
52
|
+
if (listElement.tagName.toLowerCase() !== 'datalist') {
|
|
53
|
+
// Return a violation description.
|
|
54
|
+
return 'input element list attribute references a non-datalist element';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Otherwise, i.e. if it has no list attribute with a non-empty value:
|
|
58
|
+
else {
|
|
59
|
+
// Return a violation description.
|
|
60
|
+
return 'input element list attribute is empty';
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const whats = 'list attributes of input elements are empty or IDs of no or non-datalist elements';
|
|
64
|
+
return doTest(
|
|
65
|
+
page, withItems, 'datalistRef', 'input[list]', whats, 3, 'INPUT', getBadWhat.toString()
|
|
66
|
+
);
|
|
49
67
|
};
|
package/testaro/embAc.js
CHANGED
|
@@ -30,41 +30,23 @@
|
|
|
30
30
|
non-obvious what a user will activate with a click.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// IMPORTS
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
const {init, getRuleResult} = require('../procs/testaro');
|
|
35
|
+
const {doTest} = require('../procs/testaro');
|
|
37
36
|
|
|
38
|
-
//
|
|
37
|
+
// FUNCTIONS
|
|
39
38
|
|
|
40
|
-
// Runs the test and returns the result.
|
|
41
39
|
exports.reporter = async (page, withItems) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
});
|
|
55
|
-
let param = 'a link or button';
|
|
56
|
-
if (embedderTagName === 'A') {
|
|
57
|
-
param = 'a link';
|
|
58
|
-
}
|
|
59
|
-
else if (embedderTagName === 'BUTTON') {
|
|
60
|
-
param = 'a button';
|
|
61
|
-
}
|
|
62
|
-
all.locs.push([loc, param]);
|
|
63
|
-
}
|
|
64
|
-
// Populate and return the result.
|
|
65
|
-
const whats = [
|
|
66
|
-
'Interactive element is embedded in __param__',
|
|
67
|
-
'Interactive elements are contained by links or buttons'
|
|
68
|
-
];
|
|
69
|
-
return await getRuleResult(withItems, all, 'embAc', whats, 2);
|
|
40
|
+
const getBadWhat = element => {
|
|
41
|
+
// Get whether the embedding element is a link or a button.
|
|
42
|
+
const embedder = element.parentElement.closest('a, button');
|
|
43
|
+
const embedderWhat = embedder.tagName.toLowerCase() === 'a' ? 'a link' : 'a button';
|
|
44
|
+
// Return a violation description.
|
|
45
|
+
return `interactive element is embedded in ${embedderWhat}`;
|
|
46
|
+
};
|
|
47
|
+
const selector = ['a', 'button', 'input', 'select']
|
|
48
|
+
.map(tag => `a ${tag}, button ${tag}`)
|
|
49
|
+
.join(', ');
|
|
50
|
+
const whats = 'interactive elements are embedded in links or buttons';
|
|
51
|
+
return doTest(page, withItems, 'embAc', selector, whats, 2, null, getBadWhat.toString());
|
|
70
52
|
};
|