testaro 60.11.0 → 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 +10 -1
- package/testaro/focAndOp.js +146 -0
- package/testaro/focInd.js +1 -0
- package/tests/testaro.js +6 -13
- package/procs/operable.js +0 -108
- package/testaro/focOp.js +0 -75
- package/testaro/miniText-orig.js +0 -62
- 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
|
@@ -194,9 +194,18 @@ exports.doTest = async (
|
|
|
194
194
|
violationCount++;
|
|
195
195
|
// If itemization is required:
|
|
196
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
|
+
}
|
|
197
206
|
// Add an instance to the instances.
|
|
198
207
|
instances.push(
|
|
199
|
-
window.getInstance(element, ruleID,
|
|
208
|
+
window.getInstance(element, ruleID, ruleWhat, 1, ruleSeverity)
|
|
200
209
|
);
|
|
201
210
|
}
|
|
202
211
|
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
4
|
+
|
|
5
|
+
MIT License
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
focAndOp
|
|
28
|
+
Related to Tenon rule 190.
|
|
29
|
+
|
|
30
|
+
This test reports discrepancies between Tab-focusability and operability. The standard practice
|
|
31
|
+
is to make focusable elements operable and operable elements focusable. If focusable elements are
|
|
32
|
+
not operable, users are likely to be surprised that nothing happens when they try to operate such
|
|
33
|
+
elements. Conversely, if operable elements are not focusable, users who navigate with a
|
|
34
|
+
keyboard are prevented from operating those elements. The test considers an element
|
|
35
|
+
Tab-focusable if its tabIndex property has the value 0. The test considers an element operable if
|
|
36
|
+
it has a non-inherited pointer cursor and is not a 'LABEL' element, has an operable tag name, has
|
|
37
|
+
an interactive explicit role, or has an 'onclick' attribute.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
// IMPORTS
|
|
41
|
+
|
|
42
|
+
const {doTest} = require('../procs/testaro');
|
|
43
|
+
|
|
44
|
+
// FUNCTIONS
|
|
45
|
+
|
|
46
|
+
// Runs the test and returns the result.
|
|
47
|
+
exports.reporter = async (page, withItems) => {
|
|
48
|
+
// Define a violation function for execution in the browser.
|
|
49
|
+
const getBadWhat = element => {
|
|
50
|
+
// Get whether the element is visible.
|
|
51
|
+
const isVisible = element.checkVisibility({
|
|
52
|
+
contentVisibilityAuto: true,
|
|
53
|
+
opacityProperty: true,
|
|
54
|
+
visibilityProperty: true
|
|
55
|
+
});
|
|
56
|
+
// If so:
|
|
57
|
+
if (isVisible) {
|
|
58
|
+
// Get whether it is focusable.
|
|
59
|
+
const isFocusable = element.tabIndex === 0;
|
|
60
|
+
// Get the operable tagnames.
|
|
61
|
+
const opTags = new Set(['A', 'BUTTON', 'IFRAME', 'INPUT','OPTION', 'SELECT', 'TEXTAREA']);
|
|
62
|
+
// Get the operable roles.
|
|
63
|
+
const opRoles = new Set([
|
|
64
|
+
'button',
|
|
65
|
+
'checkbox',
|
|
66
|
+
'combobox',
|
|
67
|
+
'composite',
|
|
68
|
+
'grid',
|
|
69
|
+
'gridcell',
|
|
70
|
+
'input',
|
|
71
|
+
'link',
|
|
72
|
+
'listbox',
|
|
73
|
+
'menu',
|
|
74
|
+
'menubar',
|
|
75
|
+
'menuitem',
|
|
76
|
+
'menuitemcheckbox',
|
|
77
|
+
'option',
|
|
78
|
+
'radio',
|
|
79
|
+
'radiogroup',
|
|
80
|
+
'scrollbar',
|
|
81
|
+
'searchbox',
|
|
82
|
+
'select',
|
|
83
|
+
'slider',
|
|
84
|
+
'spinbutton',
|
|
85
|
+
'switch',
|
|
86
|
+
'tab',
|
|
87
|
+
'tablist',
|
|
88
|
+
'textbox',
|
|
89
|
+
'tree',
|
|
90
|
+
'treegrid',
|
|
91
|
+
'treeitem',
|
|
92
|
+
'widget',
|
|
93
|
+
]);
|
|
94
|
+
// Initialize the operabilities of the element.
|
|
95
|
+
const opHow = [];
|
|
96
|
+
let hasPointer = false;
|
|
97
|
+
// If the element is not a label:
|
|
98
|
+
if (element.tagName !== 'LABEL') {
|
|
99
|
+
const styleDec = window.getComputedStyle(element);
|
|
100
|
+
hasPointer = styleDec.cursor === 'pointer';
|
|
101
|
+
// If it has a pointer cursor:
|
|
102
|
+
if (hasPointer) {
|
|
103
|
+
// Neutralize the cursor style of the parent element of the element.
|
|
104
|
+
element.parentElement.style.cursor = 'default';
|
|
105
|
+
// Get whether, after this, the element still has a pointer cursor.
|
|
106
|
+
hasPointer = styleDec.cursor === 'pointer';
|
|
107
|
+
// Add this to the operabilities of the element.
|
|
108
|
+
opHow.push('pointer cursor');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// If the element has a click event listener:
|
|
112
|
+
if (element.onclick) {
|
|
113
|
+
// Add this to the operabilities.
|
|
114
|
+
opHow.push('click listener');
|
|
115
|
+
}
|
|
116
|
+
// If the element has an operable explicit role:
|
|
117
|
+
const role = element.getAttribute('role');
|
|
118
|
+
if (opRoles.has(role)) {
|
|
119
|
+
// Add this to the operabilities.
|
|
120
|
+
opHow.push(`role ${role}`);
|
|
121
|
+
}
|
|
122
|
+
// If the element has an operable tagname:
|
|
123
|
+
const tagName = element.tagName;
|
|
124
|
+
if (opTags.has(tagName)) {
|
|
125
|
+
// Add this to the operabilities.
|
|
126
|
+
opHow.push(`tagname ${tagName}`);
|
|
127
|
+
}
|
|
128
|
+
const isOperable = opHow.length > 0;
|
|
129
|
+
// If the element is focusable but not operable:
|
|
130
|
+
if (isFocusable && ! isOperable) {
|
|
131
|
+
// Return a severity and violation description.
|
|
132
|
+
return '2:Element is Tab-focusable but not operable';
|
|
133
|
+
}
|
|
134
|
+
// Otherwise, if it is operable but not focusable:
|
|
135
|
+
else if (isOperable && ! isFocusable) {
|
|
136
|
+
// Return a severity and violation description.
|
|
137
|
+
return `3:Element is operable (${opHow.join(', ')}) but not Tab-focusable`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const whats = 'Elements are Tab-focusable but not operable or vice versa';
|
|
142
|
+
// Perform the test and return the result.
|
|
143
|
+
return doTest(
|
|
144
|
+
page, withItems, 'focAndOp', 'body *', whats, 2, null, getBadWhat.toString()
|
|
145
|
+
);
|
|
146
|
+
};
|
package/testaro/focInd.js
CHANGED
package/tests/testaro.js
CHANGED
|
@@ -385,17 +385,17 @@ const allRules = [
|
|
|
385
385
|
defaultOn: true
|
|
386
386
|
},
|
|
387
387
|
{
|
|
388
|
-
id: '
|
|
389
|
-
what: '
|
|
388
|
+
id: 'focAndOp',
|
|
389
|
+
what: 'Tab-focusable elements that are not operable or vice versa',
|
|
390
390
|
launchRole: 'waster',
|
|
391
|
-
timeOut:
|
|
391
|
+
timeOut: 5,
|
|
392
392
|
defaultOn: true
|
|
393
393
|
},
|
|
394
394
|
{
|
|
395
|
-
id: '
|
|
396
|
-
what: '
|
|
395
|
+
id: 'focInd',
|
|
396
|
+
what: 'missing and nonstandard focus indicators',
|
|
397
397
|
launchRole: 'waster',
|
|
398
|
-
timeOut:
|
|
398
|
+
timeOut: 10,
|
|
399
399
|
defaultOn: true
|
|
400
400
|
},
|
|
401
401
|
{
|
|
@@ -419,13 +419,6 @@ const allRules = [
|
|
|
419
419
|
timeOut: 10,
|
|
420
420
|
defaultOn: true
|
|
421
421
|
},
|
|
422
|
-
{
|
|
423
|
-
id: 'opFoc',
|
|
424
|
-
what: 'operable elements that are not Tab-focusable',
|
|
425
|
-
launchRole: 'waster',
|
|
426
|
-
timeOut: 10,
|
|
427
|
-
defaultOn: true
|
|
428
|
-
},
|
|
429
422
|
{
|
|
430
423
|
id: 'tabNav',
|
|
431
424
|
what: 'nonstandard keyboard navigation between elements with the tab role',
|
package/procs/operable.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
|
|
4
|
-
MIT License
|
|
5
|
-
|
|
6
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
in the Software without restriction, including without limitation the rights
|
|
9
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
furnished to do so, subject to the following conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
SOFTWARE.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
operable
|
|
27
|
-
Returns whether the element of a locator is operable., i.e. it has a non-inherited pointer cursor
|
|
28
|
-
and is not a 'LABEL' element, has an operable tag name, has an interactive explicit role, or has
|
|
29
|
-
an 'onclick' attribute. The isOperable function modifies the page.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
// ########## FUNCTIONS
|
|
33
|
-
|
|
34
|
-
// Gets whether an element is operable.
|
|
35
|
-
exports.isOperable = async loc => {
|
|
36
|
-
// Get whether and, if so, how the element is operable.
|
|
37
|
-
const operabilities = await loc.evaluate(el => {
|
|
38
|
-
// Operable tag names.
|
|
39
|
-
const opTags = new Set(['A', 'BUTTON', 'IFRAME', 'INPUT', 'SELECT', 'TEXTAREA']);
|
|
40
|
-
// Operable roles.
|
|
41
|
-
const opRoles = new Set([
|
|
42
|
-
'button',
|
|
43
|
-
'checkbox',
|
|
44
|
-
'combobox',
|
|
45
|
-
'composite',
|
|
46
|
-
'grid',
|
|
47
|
-
'gridcell',
|
|
48
|
-
'input',
|
|
49
|
-
'link',
|
|
50
|
-
'listbox',
|
|
51
|
-
'menu',
|
|
52
|
-
'menubar',
|
|
53
|
-
'menuitem',
|
|
54
|
-
'menuitemcheckbox',
|
|
55
|
-
'option',
|
|
56
|
-
'radio',
|
|
57
|
-
'radiogroup',
|
|
58
|
-
'scrollbar',
|
|
59
|
-
'searchbox',
|
|
60
|
-
'select',
|
|
61
|
-
'slider',
|
|
62
|
-
'spinbutton',
|
|
63
|
-
'switch',
|
|
64
|
-
'tab',
|
|
65
|
-
'tablist',
|
|
66
|
-
'textbox',
|
|
67
|
-
'tree',
|
|
68
|
-
'treegrid',
|
|
69
|
-
'treeitem',
|
|
70
|
-
'widget',
|
|
71
|
-
]);
|
|
72
|
-
// Initialize the operabilities.
|
|
73
|
-
const opHow = [];
|
|
74
|
-
// If the element is not a label and has a non-inherited pointer cursor:
|
|
75
|
-
let hasPointer = false;
|
|
76
|
-
if (el.tagName !== 'LABEL') {
|
|
77
|
-
const styleDec = window.getComputedStyle(el);
|
|
78
|
-
hasPointer = styleDec.cursor === 'pointer';
|
|
79
|
-
if (hasPointer) {
|
|
80
|
-
el.parentElement.style.cursor = 'default';
|
|
81
|
-
hasPointer = styleDec.cursor === 'pointer';
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (hasPointer) {
|
|
85
|
-
// Add this to the operabilities.
|
|
86
|
-
opHow.push('pointer cursor');
|
|
87
|
-
}
|
|
88
|
-
// If the element is clickable:
|
|
89
|
-
if (el.onclick) {
|
|
90
|
-
// Add this to the operabilities.
|
|
91
|
-
opHow.push('click listener');
|
|
92
|
-
}
|
|
93
|
-
// If the element has an operable explicit role:
|
|
94
|
-
const role = el.getAttribute('role');
|
|
95
|
-
if (opRoles.has(role)) {
|
|
96
|
-
// Add this to the operabilities.
|
|
97
|
-
opHow.push(`role ${role}`);
|
|
98
|
-
}
|
|
99
|
-
// If the element has an operable type:
|
|
100
|
-
const tagName = el.tagName;
|
|
101
|
-
if (opTags.has(tagName)) {
|
|
102
|
-
// Add this to the operabilities.
|
|
103
|
-
opHow.push(`tag name ${tagName}`);
|
|
104
|
-
}
|
|
105
|
-
return opHow;
|
|
106
|
-
});
|
|
107
|
-
return operabilities;
|
|
108
|
-
};
|
package/testaro/focOp.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
4
|
-
|
|
5
|
-
MIT License
|
|
6
|
-
|
|
7
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
in the Software without restriction, including without limitation the rights
|
|
10
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
furnished to do so, subject to the following conditions:
|
|
13
|
-
|
|
14
|
-
The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
copies or substantial portions of the Software.
|
|
16
|
-
|
|
17
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/*
|
|
27
|
-
focOp
|
|
28
|
-
Related to Tenon rule 190.
|
|
29
|
-
|
|
30
|
-
This test reports Tab-focusable elements that are not operable. The standard practice is to make
|
|
31
|
-
focusable elements operable. If focusable elements are not operable, users are likely to be
|
|
32
|
-
surprised that nothing happens when they try to operate such elements. The test considers an
|
|
33
|
-
element operable if it has a non-inherited pointer cursor and is not a 'LABEL' element, has an
|
|
34
|
-
operable tag name, has an interactive explicit role, or has an 'onclick' attribute. The test
|
|
35
|
-
considers an element Tab-focusable if its tabIndex property has the value 0.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
// ########## IMPORTS
|
|
39
|
-
|
|
40
|
-
// Module to perform common operations.
|
|
41
|
-
const {init, getRuleResult} = require('../procs/testaro');
|
|
42
|
-
// Module to get operabilities.
|
|
43
|
-
const {isOperable} = require('../procs/operable');
|
|
44
|
-
|
|
45
|
-
// ########## FUNCTIONS
|
|
46
|
-
|
|
47
|
-
// Runs the test and returns the result.
|
|
48
|
-
exports.reporter = async (page, withItems) => {
|
|
49
|
-
// Initialize the locators and result.
|
|
50
|
-
const all = await init(100, page, 'body *');
|
|
51
|
-
all.result.data.focusableCount = 0;
|
|
52
|
-
// For each locator:
|
|
53
|
-
for (const loc of all.allLocs) {
|
|
54
|
-
// Get whether its element is focusable.
|
|
55
|
-
const isFocusable = await loc.evaluate(el => el.tabIndex === 0);
|
|
56
|
-
// If it is:
|
|
57
|
-
if (isFocusable) {
|
|
58
|
-
// Add this to the report.
|
|
59
|
-
all.result.data.focusableCount++;
|
|
60
|
-
// Get whether it is operable.
|
|
61
|
-
const howOperable = await isOperable(loc);
|
|
62
|
-
// If it is not:
|
|
63
|
-
if (! howOperable.length) {
|
|
64
|
-
// Add the locator to the array of violators.
|
|
65
|
-
all.locs.push(loc);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Populate and return the result.
|
|
70
|
-
const whats = [
|
|
71
|
-
'Element is Tab-focusable but not operable', 'Elements are Tab-focusable but not operable'
|
|
72
|
-
];
|
|
73
|
-
const result = await getRuleResult(withItems, all, 'focOp', whats, 2);
|
|
74
|
-
return result;
|
|
75
|
-
};
|
package/testaro/miniText-orig.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
|
|
4
|
-
MIT License
|
|
5
|
-
|
|
6
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
-
in the Software without restriction, including without limitation the rights
|
|
9
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
furnished to do so, subject to the following conditions:
|
|
12
|
-
|
|
13
|
-
The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
copies or substantial portions of the Software.
|
|
15
|
-
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
SOFTWARE.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
miniText
|
|
27
|
-
Derived from the bbc-a11y textCannotBeTooSmall test.
|
|
28
|
-
Related to Tenon rule 134.
|
|
29
|
-
This test reports elements with font sizes smaller than 11 pixels.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
// Module to perform common operations.
|
|
33
|
-
const {init, getRuleResult} = require('../procs/testaro');
|
|
34
|
-
|
|
35
|
-
// ########## FUNCTIONS
|
|
36
|
-
|
|
37
|
-
// Runs the test and returns the result.
|
|
38
|
-
exports.reporter = async (page, withItems) => {
|
|
39
|
-
// Initialize the locators and result.
|
|
40
|
-
const all = await init(100, page, 'body *:not(script, style):visible', {hasText: /[^\s]+/});
|
|
41
|
-
// For each locator:
|
|
42
|
-
for (const loc of all.allLocs) {
|
|
43
|
-
// Get the font size of its element if less than 11 pixels.
|
|
44
|
-
const fontSize = await loc.evaluate(el => {
|
|
45
|
-
const styleDec = window.getComputedStyle(el);
|
|
46
|
-
const fontSizeString = styleDec.fontSize;
|
|
47
|
-
const fontSize = Number.parseFloat(fontSizeString);
|
|
48
|
-
return fontSize < 11 ? fontSize : null;
|
|
49
|
-
});
|
|
50
|
-
// If it violates the rule:
|
|
51
|
-
if (fontSize) {
|
|
52
|
-
// Add the locator to the array of violators.
|
|
53
|
-
all.locs.push([loc, fontSize]);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// Populate and return the result.
|
|
57
|
-
const whats = [
|
|
58
|
-
'Element has a font size of __param__ pixels, smaller than 11 pixels',
|
|
59
|
-
'Elements have font sizes smaller than 11 pixels'
|
|
60
|
-
];
|
|
61
|
-
return await getRuleResult(withItems, all, 'miniText', whats, 2);
|
|
62
|
-
};
|
package/testaro/opFoc.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool. All rights reserved.
|
|
4
|
-
|
|
5
|
-
MIT License
|
|
6
|
-
|
|
7
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
in the Software without restriction, including without limitation the rights
|
|
10
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
furnished to do so, subject to the following conditions:
|
|
13
|
-
|
|
14
|
-
The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
copies or substantial portions of the Software.
|
|
16
|
-
|
|
17
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/*
|
|
27
|
-
opFoc
|
|
28
|
-
Related to Tenon rule 190.
|
|
29
|
-
|
|
30
|
-
This test reports operable elements that are not Tab-focusable. The standard practice is to make
|
|
31
|
-
operable elements focusable. If operable elements are not focusable, users who navigate with a
|
|
32
|
-
keyboard are prevented from operating those elements. The test considers an element operable if
|
|
33
|
-
it has a non-inherited pointer cursor and is not a 'LABEL' element, has an operable tag name, has
|
|
34
|
-
an interactive explicit role, or has an 'onclick' attribute. The test considers an element
|
|
35
|
-
Tab-focusable if its tabIndex property has the value 0.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
// ########## IMPORTS
|
|
39
|
-
|
|
40
|
-
// Module to perform common operations.
|
|
41
|
-
const {init, getRuleResult} = require('../procs/testaro');
|
|
42
|
-
// Module to get operabilities.
|
|
43
|
-
const {isOperable} = require('../procs/operable');
|
|
44
|
-
|
|
45
|
-
// ########## FUNCTIONS
|
|
46
|
-
|
|
47
|
-
// Runs the test and returns the result.
|
|
48
|
-
exports.reporter = async (page, withItems) => {
|
|
49
|
-
// Initialize the locators and result.
|
|
50
|
-
const all = await init(100, page, 'body *');
|
|
51
|
-
all.result.data.operableCount = 0;
|
|
52
|
-
// For each locator:
|
|
53
|
-
for (const loc of all.allLocs) {
|
|
54
|
-
// Get whether and, if so, how its element is operable.
|
|
55
|
-
const operabilities = await isOperable(loc);
|
|
56
|
-
// If it is:
|
|
57
|
-
if (operabilities.length) {
|
|
58
|
-
// Add this to the report.
|
|
59
|
-
all.result.data.operableCount++;
|
|
60
|
-
// Get whether it is focusable.
|
|
61
|
-
const isFocusable = await loc.evaluate(el => el.tabIndex === 0);
|
|
62
|
-
// If it is not:
|
|
63
|
-
if (! isFocusable) {
|
|
64
|
-
// Add the locator to the array of violators.
|
|
65
|
-
all.locs.push([loc, operabilities.join(', ')]);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Populate and return the result.
|
|
70
|
-
const whats = [
|
|
71
|
-
'Element is operable (__param__) but not Tab-focusable',
|
|
72
|
-
'Elements are operable but not Tab-focusable'
|
|
73
|
-
];
|
|
74
|
-
const result = await getRuleResult(withItems, all, 'opFoc', whats, 3);
|
|
75
|
-
return result;
|
|
76
|
-
};
|