testaro 14.7.5 → 14.9.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/README.md +30 -29
- package/package.json +1 -1
- package/standardize.js +16 -9
- package/testaro/allHidden.js +1 -1
- package/testaro/attVal.js +2 -2
- package/testaro/autocomplete.js +2 -2
- package/testaro/bulk.js +1 -1
- package/testaro/docType.js +1 -1
- package/testaro/dupAtt.js +2 -2
- package/testaro/embAc.js +2 -2
- package/testaro/filter.js +2 -2
- package/testaro/focAll.js +1 -1
- package/testaro/focInd.js +3 -3
- package/testaro/focOp.js +6 -9
- package/testaro/focVis.js +2 -2
- package/testaro/hover.js +2 -2
- package/testaro/labClash.js +3 -3
- package/testaro/linkTo.js +2 -2
- package/testaro/linkUl.js +2 -2
- package/testaro/menuNav.js +2 -2
- package/testaro/miniText.js +2 -2
- package/testaro/motion.js +1 -1
- package/testaro/nonTable.js +2 -2
- package/testaro/radioSet.js +2 -2
- package/testaro/role.js +2 -2
- package/testaro/styleDiff.js +1 -1
- package/testaro/tabNav.js +2 -2
- package/testaro/titledEl.js +2 -2
- package/testaro/zIndex.js +2 -2
- package/tests/continuum.js +2 -2
- package/tests/testaro.js +2 -1
package/README.md
CHANGED
|
@@ -6,9 +6,11 @@ Ensemble testing for web accessibility
|
|
|
6
6
|
|
|
7
7
|
Testaro is a collection of web accessibility testing tools.
|
|
8
8
|
|
|
9
|
-
The
|
|
9
|
+
The purposes of Testaro are to:
|
|
10
|
+
- provide programmatic access to accessibility tests defined by several tools
|
|
11
|
+
- facilitate the integration of the reports of the tools into a unified report
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
The need for multi-tool integration, and its costs, are discussed in [Accessibility Metatesting: Comparing Nine Testing Tools](https://arxiv.org/abs/2304.07591).
|
|
12
14
|
|
|
13
15
|
Testaro launches and controls web browsers, performing operations, conducting tests, and recording results.
|
|
14
16
|
|
|
@@ -52,27 +54,19 @@ Testaro performs tests of these tools:
|
|
|
52
54
|
|
|
53
55
|
The [BBC Accessibility Standards Checker](https://github.com/bbc/bbc-a11y) is not a formal dependency, but some of the tests in the Testaro tool are adaptations of tests of that tool.
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
- Alfa: 103
|
|
57
|
-
- Axe-core: 138
|
|
58
|
-
- Continuum Community Edition: 267
|
|
59
|
-
- Equal Access: 163
|
|
60
|
-
- HTML CodeSniffer: 98
|
|
61
|
-
- Nu Html Checker: 147
|
|
62
|
-
- QualWeb core: 121
|
|
63
|
-
- Tenon: 180
|
|
64
|
-
- WAVE: 110
|
|
65
|
-
- Testaro: 29
|
|
66
|
-
- total: 1356
|
|
57
|
+
## Rules
|
|
67
58
|
|
|
68
|
-
|
|
59
|
+
Each tool accessed with Testaro defines _rules_ and tests _targets_ for compliance with its rules. The counts of the rules range from about 30, for Testaro itself, to about 270, for Continuum Community Edition. In total, the ten tools define about 1350 rules. Some of the tools are under active development, and their rule counts change over time.
|
|
69
60
|
|
|
70
|
-
##
|
|
61
|
+
## Job data
|
|
71
62
|
|
|
72
|
-
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
63
|
+
A report produced by Testaro discloses:
|
|
64
|
+
- raw results of tests conducted by tools
|
|
65
|
+
- standardized results of tests conducted by tools
|
|
66
|
+
- process data, including statistics on:
|
|
67
|
+
- latency (how long a time each tool takes to run its tests)
|
|
68
|
+
- test prevention (the failure of tools to run on particular targets)
|
|
69
|
+
- logging (browser messaging, including about document errors, during testing)
|
|
76
70
|
|
|
77
71
|
## Code organization
|
|
78
72
|
|
|
@@ -188,30 +182,31 @@ Job properties:
|
|
|
188
182
|
#### Introduction
|
|
189
183
|
|
|
190
184
|
While each tool produces a _tool report_ of the results of its tests, Testaro also produces a _job report_, combining all of the tool reports of a job.
|
|
191
|
-
- Tools append their reports to their acts in a job. For example, if one act in a job specifies some Continuum tests to be run, Continuum appends its report to that act. In that way, the act now describes not only the Continuum tests that were run, but also the results of those tests.
|
|
192
|
-
- Testaro further elaborates a job by reporting comprehensive results
|
|
185
|
+
- Tools append their reports to their acts in a job. For example, if one act in a job specifies some Continuum tests to be run, Continuum appends its report to that act. In that way, the act now describes not only the Continuum tests that were run, but also the results of those tests. Testaro does some pruning of tool reports, removing content that is judged unlikely to be useful. You can examine and modify the pruning algorithms in the modules located in the `tests` directory.
|
|
186
|
+
- Testaro further elaborates a job by reporting comprehensive results in a job report. Testaro can add more facts to each of the tool reports and also adds whole-job facts, in a `jobData` property, to the job.
|
|
193
187
|
|
|
194
188
|
#### Formats
|
|
195
189
|
|
|
196
|
-
##### Tool formats
|
|
190
|
+
##### Tool-report formats
|
|
197
191
|
|
|
198
192
|
The tools listed above as dependencies write their tool reports in various formats. They differ in how they organize multiple instances of the same problem, how they classify severity and certainty, how they point to the locations of problems, how they name problems, etc.
|
|
199
193
|
|
|
200
194
|
##### Standard format
|
|
201
195
|
|
|
202
|
-
Testaro helps overcome this format diversity by offering to represent the main facts in
|
|
196
|
+
Testaro helps overcome this format diversity by offering to represent the main facts in each tool report in a single standardized format.
|
|
203
197
|
|
|
204
198
|
In the conceptual scheme underlying the format standardization of Testaro, each tool has its own set of _rules_, where a rule is an algorithm for evaluating a target and determining whether instances of some kind of problem exist in it. With standardization, Testaro reports, in a uniform way, the outcomes from the application of rules by tools to a target.
|
|
205
199
|
|
|
206
|
-
If the `STANDARD` environment variable has the value `also` (which it has by default) or `only`, Testaro converts some data in each tool report to a standard format. That permits you to ignore the format idiosyncrasies of the tools. If `STANDARD` has the value `also`, the job report includes both formats. If the value is `only`, the job report includes only the standard format. If the value is `no`, the job report includes only the original format of each tool.
|
|
200
|
+
If the `STANDARD` environment variable has the value `also` (which it has by default) or `only`, Testaro converts some data in each tool report to a standard format. That permits you to ignore the format idiosyncrasies of the tools. If `STANDARD` has the value `also`, the job report includes both formats. If the value is `only`, the job report includes only the standard format. If the value is `no`, the job report includes only the original format of each tool report.
|
|
207
201
|
|
|
208
|
-
The standard format of each tool report has
|
|
202
|
+
The standard format of each tool report has these properties:
|
|
203
|
+
- `prevented`: `true` if the tool failed to run on the page, or otherwise omitted.
|
|
209
204
|
- `totals`: an array of 4 integers, representing the counts of problem instances classified by the tool into 4 ordinal degrees of severity. For example, `[2, 13, 0, 5]` would mean that the tool reported 2 instances at the lowest severity, 13 at the next-lowest, none at the third-lowest, and 5 at the highest.
|
|
210
205
|
- `instances`: an array of objects describing facts about issue instances reported by the tool. This object has these properties, some of which have empty strings as values when the tool does not provide values:
|
|
211
206
|
- `ruleID`: a code identifying a rule
|
|
212
207
|
- `what`: a description of the rule
|
|
213
208
|
- `count` (optional): the count of instances if this instance represents multiple instances
|
|
214
|
-
- `ordinalSeverity`: how the tool ranks the severity of the instance, on a 4-point ordinal scale
|
|
209
|
+
- `ordinalSeverity`: how the tool ranks the severity of the instance, on a 4-point ordinal scale from 0 to 3
|
|
215
210
|
- `tagName`: upper-case tagName of the affected element
|
|
216
211
|
- `id`: value of the `id` property of that element
|
|
217
212
|
- `location`: an object with three properties:
|
|
@@ -229,7 +224,7 @@ standardResult: {
|
|
|
229
224
|
{
|
|
230
225
|
ruleID: 'rule01',
|
|
231
226
|
what: 'Button type invalid',
|
|
232
|
-
ordinalSeverity:
|
|
227
|
+
ordinalSeverity: 2,
|
|
233
228
|
tagName: 'BUTTON'
|
|
234
229
|
id: '',
|
|
235
230
|
location: {
|
|
@@ -272,7 +267,13 @@ standardResult: {
|
|
|
272
267
|
|
|
273
268
|
If a tool has the option to be used without itemization and is being so used, the `instances` array may be empty.
|
|
274
269
|
|
|
275
|
-
This standard format
|
|
270
|
+
This standard format reflects some judgments. For example:
|
|
271
|
+
- The `ordinalSeverity` property of an instance may have required interpretation. Tools may report severity, certainty, both, or neither; they may classify severity or certainty ordinally or metrically; and, if they classify ordinally, their scales may have more or fewer than 4 ranks. Testaro coerces each tool’s severity and/or certainty classification into a 4-rank ordinal classification. This classification is deemed to express the most common pattern among the tools.
|
|
272
|
+
- The `tagName` property of an instance may not always be obvious, because in some cases the rule being tested for requires a relationship among more than one element (e.g., “An X element may not have a Y element as its parent”).
|
|
273
|
+
|
|
274
|
+
You are not dependent on the judgments incorporated into the standard format, because Testaro can give you the original reports from the tools.
|
|
275
|
+
|
|
276
|
+
The standard format does not express opinions issue classifications. A rule ID identifies something deemed to be an issue by a tool. Useful reporting from multi-tool testing still requires the classification of tool **rules** into **issues**. If tool `A` has `alt-incomplete` as a rule ID and tool `B` has `image_alt_stub` as a rule ID, Testaro does not decide whether those are really the same issue or different issues. That decision belongs to you. The standardization of tool reports by Testaro eliminates some of the drudgery in issue classification, but not any of the judgment required for issue classification.
|
|
276
277
|
|
|
277
278
|
### Acts
|
|
278
279
|
|
package/package.json
CHANGED
package/standardize.js
CHANGED
|
@@ -116,7 +116,7 @@ const doNuVal = (result, standardResult, docType) => {
|
|
|
116
116
|
identifiers[0] = tagNameLCArray[1].toUpperCase();
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
// Include the message twice
|
|
119
|
+
// Include the message twice. A scoring procedure may replace the ruleID with a pattern.
|
|
120
120
|
const instance = {
|
|
121
121
|
ruleID: item.message,
|
|
122
122
|
what: item.message,
|
|
@@ -225,26 +225,29 @@ const doWAVE = (result, standardResult, categoryName) => {
|
|
|
225
225
|
};
|
|
226
226
|
// Converts a result.
|
|
227
227
|
const convert = (toolName, result, standardResult) => {
|
|
228
|
+
// Prevention.
|
|
229
|
+
if (result.prevented) {
|
|
230
|
+
standardResult.prevented = true;
|
|
231
|
+
}
|
|
228
232
|
// alfa
|
|
229
|
-
if (toolName === 'alfa' && result.totals) {
|
|
233
|
+
else if (toolName === 'alfa' && result.totals) {
|
|
230
234
|
standardResult.totals = [result.totals.warnings, 0, 0, result.totals.failures];
|
|
231
235
|
result.items.forEach(item => {
|
|
232
236
|
const {codeLines} = item.target;
|
|
233
237
|
const code = Array.isArray(codeLines) ? codeLines.join(' ') : '';
|
|
234
238
|
const identifiers = getIdentifiers(code);
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
239
|
+
const tagNameArray = item.target
|
|
240
|
+
&& item.target.path
|
|
241
|
+
&& item.target.path.match(/^.*\/([a-z]+)\[\d+\]/);
|
|
242
|
+
if (tagNameArray && tagNameArray.length === 2) {
|
|
243
|
+
identifiers[0] = tagNameArray[1].toUpperCase();
|
|
241
244
|
}
|
|
242
245
|
const {rule, target} = item;
|
|
243
246
|
const instance = {
|
|
244
247
|
ruleID: rule.ruleID,
|
|
245
248
|
what: rule.ruleSummary,
|
|
246
249
|
ordinalSeverity: ['cantTell', '', '', 'failed'].indexOf(item.verdict),
|
|
247
|
-
tagName:
|
|
250
|
+
tagName: identifiers[0],
|
|
248
251
|
id: identifiers[1],
|
|
249
252
|
location: {
|
|
250
253
|
doc: 'dom',
|
|
@@ -453,6 +456,10 @@ const convert = (toolName, result, standardResult) => {
|
|
|
453
456
|
doWAVE(result, standardResult, categoryName);
|
|
454
457
|
});
|
|
455
458
|
}
|
|
459
|
+
// Any tool with no reported rule violations:
|
|
460
|
+
else {
|
|
461
|
+
standardResult.totals = [0, 0, 0, 0];
|
|
462
|
+
}
|
|
456
463
|
};
|
|
457
464
|
// Converts the results.
|
|
458
465
|
exports.standardize = act => {
|
package/testaro/allHidden.js
CHANGED
|
@@ -76,7 +76,7 @@ exports.reporter = async page => {
|
|
|
76
76
|
if (data[hider][region]) {
|
|
77
77
|
// Add a standard instance for that combination.
|
|
78
78
|
standardInstances.push({
|
|
79
|
-
|
|
79
|
+
ruleID: 'allHidden',
|
|
80
80
|
what: `${reportables[region][1]} ${reportables[hider][1]}`,
|
|
81
81
|
ordinalSeverity: reportables[region][0] + reportables[hider][0] || 0,
|
|
82
82
|
tagName: region.tagName,
|
package/testaro/attVal.js
CHANGED
|
@@ -48,7 +48,7 @@ exports.reporter = async (page, withItems, attributeName, areLicit, values) => {
|
|
|
48
48
|
data.items = badAttributeData;
|
|
49
49
|
badAttributeData.forEach(item => {
|
|
50
50
|
standardInstances.push({
|
|
51
|
-
|
|
51
|
+
ruleID: 'attVal',
|
|
52
52
|
what:
|
|
53
53
|
`${item.tagName} element has attribute ${attributeName} with illicit value ${item.attributeValue}`,
|
|
54
54
|
ordinalSeverity: 2,
|
|
@@ -65,7 +65,7 @@ exports.reporter = async (page, withItems, attributeName, areLicit, values) => {
|
|
|
65
65
|
}
|
|
66
66
|
else if (data.total) {
|
|
67
67
|
standardInstances.push({
|
|
68
|
-
|
|
68
|
+
ruleID: 'attVal',
|
|
69
69
|
what: 'Elements have attributes with illicit values',
|
|
70
70
|
count: data.total,
|
|
71
71
|
ordinalSeverity: 2,
|
package/testaro/autocomplete.js
CHANGED
|
@@ -74,7 +74,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
74
74
|
if (data.items) {
|
|
75
75
|
data.items.forEach(item => {
|
|
76
76
|
standardInstances.push({
|
|
77
|
-
|
|
77
|
+
ruleID: 'autocomplete',
|
|
78
78
|
what: `Input is missing the required autocomplete attribute with value ${item[0]}`,
|
|
79
79
|
ordinalSeverity: 2,
|
|
80
80
|
tagName: 'INPUT',
|
|
@@ -90,7 +90,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
90
90
|
}
|
|
91
91
|
else if (data.total) {
|
|
92
92
|
standardInstances.push({
|
|
93
|
-
|
|
93
|
+
ruleID: 'autocomplete',
|
|
94
94
|
what: 'Inputs are missing required autocomplete attributes',
|
|
95
95
|
ordinalSeverity: 2,
|
|
96
96
|
tagName: '',
|
package/testaro/bulk.js
CHANGED
|
@@ -23,7 +23,7 @@ exports.reporter = async page => {
|
|
|
23
23
|
data,
|
|
24
24
|
totals: [count, 0, 0, 0],
|
|
25
25
|
standardInstances: data.visibleElements < 200 ? [] : [{
|
|
26
|
-
|
|
26
|
+
ruleID: 'bulk',
|
|
27
27
|
what: 'Page contains a large number of visible elements',
|
|
28
28
|
count,
|
|
29
29
|
ordinalSeverity: 0,
|
package/testaro/docType.js
CHANGED
|
@@ -14,7 +14,7 @@ exports.reporter = async page => {
|
|
|
14
14
|
data: {docHasType},
|
|
15
15
|
totals: [0, 0, 0, docHasType ? 0 : 1],
|
|
16
16
|
standardInstances: docHasType ? [] : [{
|
|
17
|
-
|
|
17
|
+
ruleID: 'docType',
|
|
18
18
|
what: 'Document has no standard HTML doctype preamble',
|
|
19
19
|
ordinalSeverity: 3,
|
|
20
20
|
tagName: 'HTML',
|
package/testaro/dupAtt.js
CHANGED
|
@@ -84,7 +84,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
84
84
|
if (data.items) {
|
|
85
85
|
data.items.forEach(item => {
|
|
86
86
|
standardInstances.push({
|
|
87
|
-
|
|
87
|
+
ruleID: 'dupAtt',
|
|
88
88
|
what: `Element ${item.tagName} has 2 attributes with the same name`,
|
|
89
89
|
ordinalSeverity: 2,
|
|
90
90
|
tagName: item.tagName,
|
|
@@ -100,7 +100,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
100
100
|
}
|
|
101
101
|
else if (data.total) {
|
|
102
102
|
standardInstances.push({
|
|
103
|
-
|
|
103
|
+
ruleID: 'dupAtt',
|
|
104
104
|
what: 'In some elements 2 attributes have the same name',
|
|
105
105
|
ordinalSeverity: 2,
|
|
106
106
|
location: {
|
package/testaro/embAc.js
CHANGED
|
@@ -45,7 +45,7 @@ exports.reporter = async (page, withItems) => await page.$$eval(
|
|
|
45
45
|
data.items = items;
|
|
46
46
|
items.forEach(item => {
|
|
47
47
|
standardInstances.push({
|
|
48
|
-
|
|
48
|
+
ruleID: 'embAc',
|
|
49
49
|
what: `${item.embeddedElement} element is embedded in a link or button`,
|
|
50
50
|
ordinalSeverity: 2,
|
|
51
51
|
tagName: item.embeddedElement,
|
|
@@ -61,7 +61,7 @@ exports.reporter = async (page, withItems) => await page.$$eval(
|
|
|
61
61
|
}
|
|
62
62
|
else if (total) {
|
|
63
63
|
standardInstances.push({
|
|
64
|
-
|
|
64
|
+
ruleID: 'embAc',
|
|
65
65
|
what: 'Interactive elements are contained by links or buttons',
|
|
66
66
|
count: total,
|
|
67
67
|
ordinalSeverity: 2,
|
package/testaro/filter.js
CHANGED
|
@@ -53,7 +53,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
53
53
|
if (data.items) {
|
|
54
54
|
data.items.forEach(item => {
|
|
55
55
|
standardInstances.push({
|
|
56
|
-
|
|
56
|
+
ruleID: 'filterStyle',
|
|
57
57
|
what: `${item.tagName} element has a filter style that impacts ${item.impact} elements`,
|
|
58
58
|
ordinalSeverity: 2,
|
|
59
59
|
tagName: item.toUpperCase(),
|
|
@@ -69,7 +69,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
69
69
|
}
|
|
70
70
|
else if (data.totals.styledElements) {
|
|
71
71
|
standardInstances.push({
|
|
72
|
-
|
|
72
|
+
ruleID: 'filterStyle',
|
|
73
73
|
what: 'Elements have filter styles impacting other elements',
|
|
74
74
|
ordinalSeverity: 2,
|
|
75
75
|
tagName: '',
|
package/testaro/focAll.js
CHANGED
|
@@ -65,7 +65,7 @@ exports.reporter = async page => {
|
|
|
65
65
|
data,
|
|
66
66
|
totals: [0, 0, Math.abs(data.discrepancy), 0],
|
|
67
67
|
standardInstances: data.discrepancy ? [{
|
|
68
|
-
|
|
68
|
+
ruleID: 'focAll',
|
|
69
69
|
what: 'Some focusable elements are not Tab-focusable or vice versa',
|
|
70
70
|
count: Math.abs(data.discrepancy),
|
|
71
71
|
ordinalSeverity: 2,
|
package/testaro/focInd.js
CHANGED
|
@@ -174,7 +174,7 @@ exports.reporter = async (page, withItems, revealAll = false, allowedDelay = 250
|
|
|
174
174
|
data.items[issueName].forEach(item => {
|
|
175
175
|
const qualifier = issueName === 'nonoutline' ? 'a non-outline' : 'no';
|
|
176
176
|
standardInstances.push({
|
|
177
|
-
issueID:
|
|
177
|
+
issueID: 'focInd',
|
|
178
178
|
what: `${item.tagName} element has ${qualifier} focus indicator`,
|
|
179
179
|
ordinalSeverity: issueName === 'nonoutline' ? 2 : 3,
|
|
180
180
|
tagName: item.tagName,
|
|
@@ -192,7 +192,7 @@ exports.reporter = async (page, withItems, revealAll = false, allowedDelay = 250
|
|
|
192
192
|
else {
|
|
193
193
|
if (types.missing.total) {
|
|
194
194
|
standardInstances.push({
|
|
195
|
-
issueID: 'focInd
|
|
195
|
+
issueID: 'focInd',
|
|
196
196
|
what: 'Elements have missing focus indicators',
|
|
197
197
|
count: types.missing.total,
|
|
198
198
|
ordinalSeverity: 3,
|
|
@@ -208,7 +208,7 @@ exports.reporter = async (page, withItems, revealAll = false, allowedDelay = 250
|
|
|
208
208
|
}
|
|
209
209
|
if (types.nonoutline.total) {
|
|
210
210
|
standardInstances.push({
|
|
211
|
-
issueID: 'focInd
|
|
211
|
+
issueID: 'focInd',
|
|
212
212
|
what: 'Elements have non-outline focus indicators',
|
|
213
213
|
count: types.nonoutline.total,
|
|
214
214
|
ordinalSeverity: 2,
|
package/testaro/focOp.js
CHANGED
|
@@ -150,17 +150,14 @@ exports.reporter = async (page, withItems) => {
|
|
|
150
150
|
const standardInstances = [];
|
|
151
151
|
if (data.items && data.items.onlyFocusable && data.items.onlyOperable) {
|
|
152
152
|
['onlyFocusable', 'onlyOperable'].forEach(issue => {
|
|
153
|
-
const issueID = issue === 'onlyFocusable'
|
|
154
|
-
? 'focOp-focusable-inoperable'
|
|
155
|
-
: 'focOp-operable-nonfocusable';
|
|
156
153
|
const gripe = issue === 'onlyFocusable'
|
|
157
154
|
? 'is focusable but not operable'
|
|
158
155
|
: 'is operable but not focusable';
|
|
159
156
|
const ordinalSeverity = issue === 'onlyFocusable' ? 2 : 3;
|
|
160
157
|
data.items[issue].forEach(item => {
|
|
161
158
|
standardInstances.push({
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
ruleID: 'focOp',
|
|
160
|
+
complaint: `${item.tagName} element ${gripe}`,
|
|
164
161
|
ordinalSeverity,
|
|
165
162
|
tagName: item.tagName,
|
|
166
163
|
id: item.id,
|
|
@@ -177,8 +174,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
177
174
|
else {
|
|
178
175
|
if (totals[2]) {
|
|
179
176
|
standardInstances.push({
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
ruleID: 'focOp',
|
|
178
|
+
complaint: 'Focusable elements are inoperable',
|
|
182
179
|
count: totals[2],
|
|
183
180
|
ordinalSeverity: 2,
|
|
184
181
|
tagName: '',
|
|
@@ -193,8 +190,8 @@ exports.reporter = async (page, withItems) => {
|
|
|
193
190
|
}
|
|
194
191
|
if (totals[3]) {
|
|
195
192
|
standardInstances.push({
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
ruleID: 'focOp',
|
|
194
|
+
complaint: 'Operable elements are nonfocusable',
|
|
198
195
|
count: totals[3],
|
|
199
196
|
ordinalSeverity: 3,
|
|
200
197
|
tagName: '',
|
package/testaro/focVis.js
CHANGED
|
@@ -33,7 +33,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
33
33
|
if (data.items) {
|
|
34
34
|
data.items.forEach(item => {
|
|
35
35
|
standardInstances.push({
|
|
36
|
-
|
|
36
|
+
ruleID: 'focVis',
|
|
37
37
|
what: 'Visible link is above or to the left of the display',
|
|
38
38
|
ordinalSeverity: 2,
|
|
39
39
|
tagName: 'A',
|
|
@@ -49,7 +49,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
49
49
|
}
|
|
50
50
|
else if (data.total) {
|
|
51
51
|
standardInstances.push({
|
|
52
|
-
|
|
52
|
+
ruleID: 'focVis',
|
|
53
53
|
what: 'Visible links are above or to the left of the display',
|
|
54
54
|
count: data.total,
|
|
55
55
|
ordinalSeverity: 2,
|
package/testaro/hover.js
CHANGED
|
@@ -406,7 +406,7 @@ exports.reporter = async (page, withItems, sampleSize = -1) => {
|
|
|
406
406
|
Object.keys(data.items).forEach(issue => {
|
|
407
407
|
data.items[issue].forEach(item => {
|
|
408
408
|
standardInstances.push({
|
|
409
|
-
|
|
409
|
+
ruleID: `hover-${issue}`,
|
|
410
410
|
what: what[issue],
|
|
411
411
|
count: data.totals[issue],
|
|
412
412
|
ordinalSeverity: severity[issue],
|
|
@@ -424,7 +424,7 @@ exports.reporter = async (page, withItems, sampleSize = -1) => {
|
|
|
424
424
|
}
|
|
425
425
|
else if (totals.some(total => total)) {
|
|
426
426
|
standardInstances.push({
|
|
427
|
-
|
|
427
|
+
ruleID: 'hover',
|
|
428
428
|
what: 'Hovering has unexpected impacts',
|
|
429
429
|
count: Object.values(data.totals).reduce((total, current) => total + current),
|
|
430
430
|
ordinalSeverity: totals.reduce((max, current, index) => current ? index : max, 0),
|
package/testaro/labClash.js
CHANGED
|
@@ -164,7 +164,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
164
164
|
excerptTail = item.content || '';
|
|
165
165
|
}
|
|
166
166
|
standardInstances.push({
|
|
167
|
-
|
|
167
|
+
ruleID: 'labClash',
|
|
168
168
|
what: `Element ${item.tagName} ${diagnosis}`,
|
|
169
169
|
ordinalSeverity: issue === 'mislabeled' ? 2 : 3,
|
|
170
170
|
tagName: item.tagName,
|
|
@@ -182,7 +182,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
182
182
|
else {
|
|
183
183
|
if (data.totals.unlabeled) {
|
|
184
184
|
standardInstances.push({
|
|
185
|
-
|
|
185
|
+
ruleID: 'labClash',
|
|
186
186
|
what: 'Element labels are missing',
|
|
187
187
|
count: data.totals.unlabeled,
|
|
188
188
|
ordinalSeverity: 3,
|
|
@@ -198,7 +198,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
198
198
|
}
|
|
199
199
|
if (data.totals.mislabeled) {
|
|
200
200
|
standardInstances.push({
|
|
201
|
-
|
|
201
|
+
ruleID: 'labClash',
|
|
202
202
|
what: 'Element labels are conflicting',
|
|
203
203
|
count: data.totals.mislabeled,
|
|
204
204
|
ordinalSeverity: 2,
|
package/testaro/linkTo.js
CHANGED
|
@@ -27,7 +27,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
27
27
|
data.items = badLinkData;
|
|
28
28
|
data.items.forEach(item => {
|
|
29
29
|
standardInstances.push({
|
|
30
|
-
|
|
30
|
+
ruleID: 'linkTo',
|
|
31
31
|
what: 'Element a has no href attribute',
|
|
32
32
|
ordinalSeverity: 2,
|
|
33
33
|
tagName: 'A',
|
|
@@ -43,7 +43,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
43
43
|
}
|
|
44
44
|
else if (data.total) {
|
|
45
45
|
standardInstances.push({
|
|
46
|
-
|
|
46
|
+
ruleID: 'linkTo',
|
|
47
47
|
what: 'Links are missing href attributes',
|
|
48
48
|
count: data.total,
|
|
49
49
|
ordinalSeverity: 2,
|
package/testaro/linkUl.js
CHANGED
|
@@ -79,7 +79,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
79
79
|
if (data.items && data.items.notUnderlined) {
|
|
80
80
|
data.items.notUnderlined.forEach(item => {
|
|
81
81
|
standardInstances.push({
|
|
82
|
-
|
|
82
|
+
ruleID: 'linkUl',
|
|
83
83
|
what: 'Link is inline but has no underline',
|
|
84
84
|
ordinalSeverity: 1,
|
|
85
85
|
tagName: 'A',
|
|
@@ -95,7 +95,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
95
95
|
}
|
|
96
96
|
else if (adjacent.total - adjacent.underlined > 0) {
|
|
97
97
|
standardInstances.push({
|
|
98
|
-
|
|
98
|
+
ruleID: 'linkUl',
|
|
99
99
|
what: 'Inline links are missing underlines',
|
|
100
100
|
count: adjacent.total - adjacent.underlined,
|
|
101
101
|
ordinalSeverity: 1,
|
package/testaro/menuNav.js
CHANGED
|
@@ -275,7 +275,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
275
275
|
if (data.menuItems && data.menuItems.incorrect) {
|
|
276
276
|
data.menuItems.incorrect.forEach(item => {
|
|
277
277
|
standardInstances.push({
|
|
278
|
-
|
|
278
|
+
ruleID: 'menuNav',
|
|
279
279
|
what: `Menu item responds nonstandardly to ${item.navigationErrors.join(', ')}`,
|
|
280
280
|
count: item.navigationErrors.length,
|
|
281
281
|
ordinalSeverity: 1,
|
|
@@ -292,7 +292,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
292
292
|
}
|
|
293
293
|
else if (data.totals.menuItems.incorrect) {
|
|
294
294
|
standardInstances.push({
|
|
295
|
-
|
|
295
|
+
ruleID: 'menuNav',
|
|
296
296
|
what: 'Menus and menu items have nonstandard navigation',
|
|
297
297
|
count: data.totals.navigations.all.incorrect,
|
|
298
298
|
ordinalSeverity: 1,
|
package/testaro/miniText.js
CHANGED
|
@@ -50,7 +50,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
50
50
|
data.items = miniTexts;
|
|
51
51
|
miniTexts.forEach(text => {
|
|
52
52
|
standardInstances.push({
|
|
53
|
-
|
|
53
|
+
ruleID: 'miniText',
|
|
54
54
|
what: 'Text font is smaller than 11 pixels',
|
|
55
55
|
ordinalSeverity: 2,
|
|
56
56
|
tagName: text.tagName,
|
|
@@ -66,7 +66,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
66
66
|
}
|
|
67
67
|
else if (data.total) {
|
|
68
68
|
standardInstances.push({
|
|
69
|
-
|
|
69
|
+
ruleID: 'miniText',
|
|
70
70
|
what: 'Texts have fonts smaller than 11 pixels',
|
|
71
71
|
count: data.total,
|
|
72
72
|
ordinalSeverity: 2,
|
package/testaro/motion.js
CHANGED
|
@@ -124,7 +124,7 @@ exports.reporter = async (page, withItems, delay = 2500, interval = 2500, count
|
|
|
124
124
|
0
|
|
125
125
|
],
|
|
126
126
|
standardInstances: count ? [{
|
|
127
|
-
|
|
127
|
+
ruleID: 'motion',
|
|
128
128
|
what: 'Content moves or changes without user request',
|
|
129
129
|
count,
|
|
130
130
|
ordinalSeverity: 2,
|
package/testaro/nonTable.js
CHANGED
|
@@ -68,7 +68,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
68
68
|
data.items = badTableTexts;
|
|
69
69
|
data.items.forEach(item => {
|
|
70
70
|
standardInstances.push({
|
|
71
|
-
|
|
71
|
+
ruleID: 'nonTable',
|
|
72
72
|
what: 'Table is misused to arrange content',
|
|
73
73
|
ordinalSeverity: 0,
|
|
74
74
|
tagName: 'TABLE',
|
|
@@ -84,7 +84,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
84
84
|
}
|
|
85
85
|
else if (data.total) {
|
|
86
86
|
standardInstances.push({
|
|
87
|
-
|
|
87
|
+
ruleID: 'nonTable',
|
|
88
88
|
what: 'Tables are misused to arrange content',
|
|
89
89
|
count: data.total,
|
|
90
90
|
ordinalSeverity: 0,
|
package/testaro/radioSet.js
CHANGED
|
@@ -88,7 +88,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
88
88
|
}));
|
|
89
89
|
items.notInSet.forEach(item => {
|
|
90
90
|
standardInstances.push({
|
|
91
|
-
|
|
91
|
+
ruleID: 'radioSet',
|
|
92
92
|
what: 'Radio button and its peers are not in a fieldset with a legend',
|
|
93
93
|
ordinalSeverity: 2,
|
|
94
94
|
tagName: 'INPUT',
|
|
@@ -104,7 +104,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
104
104
|
}
|
|
105
105
|
else if (loneRadios > 0) {
|
|
106
106
|
standardInstances.push({
|
|
107
|
-
|
|
107
|
+
ruleID: 'radioSet',
|
|
108
108
|
what: 'Radio buttons are not validly grouped in fieldsets with legends',
|
|
109
109
|
count: loneRadios,
|
|
110
110
|
ordinalSeverity: 2,
|
package/testaro/role.js
CHANGED
|
@@ -486,7 +486,7 @@ exports.reporter = async page => await page.$eval('body', body => {
|
|
|
486
486
|
const redCount = pairTotals.redundant;
|
|
487
487
|
if (redCount) {
|
|
488
488
|
standardInstances.push({
|
|
489
|
-
|
|
489
|
+
ruleID: 'role',
|
|
490
490
|
what: `${tagName} elements have redundant explicit role ${role} (count: ${redCount})`,
|
|
491
491
|
count: redCount,
|
|
492
492
|
ordinalSeverity: 1,
|
|
@@ -503,7 +503,7 @@ exports.reporter = async page => await page.$eval('body', body => {
|
|
|
503
503
|
const badCount = pairTotals.bad;
|
|
504
504
|
if (badCount) {
|
|
505
505
|
standardInstances.push({
|
|
506
|
-
|
|
506
|
+
ruleID: 'role',
|
|
507
507
|
what:
|
|
508
508
|
`${tagName} elements have invalid or native-replaceable explicit role ${role} (count: ${badCount})`,
|
|
509
509
|
count: badCount,
|
package/testaro/styleDiff.js
CHANGED
|
@@ -174,7 +174,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
174
174
|
const extraCount = elementSubtotals.length - 1;
|
|
175
175
|
totals[severity] += extraCount;
|
|
176
176
|
standardInstances.push({
|
|
177
|
-
|
|
177
|
+
ruleID: 'styleDiff',
|
|
178
178
|
what: `${currentData[1]} have ${elementSubtotals.length} different styles`,
|
|
179
179
|
count: extraCount,
|
|
180
180
|
ordinalSeverity: severity,
|
package/testaro/tabNav.js
CHANGED
|
@@ -339,7 +339,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
339
339
|
if (data.tabElements && data.tabElements.incorrect) {
|
|
340
340
|
data.tabElements.incorrect.forEach(item => {
|
|
341
341
|
standardInstances.push({
|
|
342
|
-
|
|
342
|
+
ruleID: 'tabNav',
|
|
343
343
|
what:
|
|
344
344
|
`${item.tagName} element responds nonstandardly to ${item.navigationErrors.join(', ')}`,
|
|
345
345
|
count: item.navigationErrors.length,
|
|
@@ -357,7 +357,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
357
357
|
}
|
|
358
358
|
else if (data.totals.navigations.all.incorrect) {
|
|
359
359
|
standardInstances.push({
|
|
360
|
-
|
|
360
|
+
ruleID: 'tabNav',
|
|
361
361
|
what: 'Tablists have nonstandard navigation',
|
|
362
362
|
count: data.totals.navigations.all.incorrect,
|
|
363
363
|
ordinalSeverity: 1,
|
package/testaro/titledEl.js
CHANGED
|
@@ -30,7 +30,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
30
30
|
data.items = badTitleElements;
|
|
31
31
|
badTitleElements.forEach(element => {
|
|
32
32
|
standardInstances.push({
|
|
33
|
-
|
|
33
|
+
ruleID: 'titledEl',
|
|
34
34
|
what: `${element.tagName} element has a title attribute`,
|
|
35
35
|
ordinalSeverity: 2,
|
|
36
36
|
tagName: element.tagName,
|
|
@@ -46,7 +46,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
46
46
|
}
|
|
47
47
|
else if (data.total) {
|
|
48
48
|
standardInstances.push({
|
|
49
|
-
|
|
49
|
+
ruleID: 'titledEl',
|
|
50
50
|
what: 'Ineligible elements have title attributes',
|
|
51
51
|
count: data.total,
|
|
52
52
|
ordinalSeverity: 2,
|
package/testaro/zIndex.js
CHANGED
|
@@ -52,7 +52,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
52
52
|
if (data.items) {
|
|
53
53
|
data.items.forEach(item => {
|
|
54
54
|
standardInstances.push({
|
|
55
|
-
|
|
55
|
+
ruleID: 'zIndex',
|
|
56
56
|
what: `${item.tagName} element has a non-default Z index`,
|
|
57
57
|
ordinalSeverity: 0,
|
|
58
58
|
tagName: item.tagName,
|
|
@@ -68,7 +68,7 @@ exports.reporter = async (page, withItems) => {
|
|
|
68
68
|
}
|
|
69
69
|
else if (data.totals.total) {
|
|
70
70
|
standardInstances.push({
|
|
71
|
-
|
|
71
|
+
ruleID: 'zIndex',
|
|
72
72
|
what: 'Elements have non-default Z indexes',
|
|
73
73
|
count: data.totals.total,
|
|
74
74
|
ordinalSeverity: 0,
|
package/tests/continuum.js
CHANGED
|
@@ -28,9 +28,9 @@ exports.reporter = async (page, options) => {
|
|
|
28
28
|
continuum.setUp(null, null, window);
|
|
29
29
|
// If a set of rules to be employed was specified:
|
|
30
30
|
let bigResultPromise;
|
|
31
|
-
if (rules && Array.isArray(rules) && rules.length && rules.every(rule => typeof rule === '
|
|
31
|
+
if (rules && Array.isArray(rules) && rules.length && rules.every(rule => typeof rule === 'string')) {
|
|
32
32
|
// Run the tests for them.
|
|
33
|
-
bigResultPromise = continuum.runTests(rules);
|
|
33
|
+
bigResultPromise = continuum.runTests(rules.map(rule => Number.parseInt(rule)));
|
|
34
34
|
}
|
|
35
35
|
// Otherwise, i.e. if no rules were specified:
|
|
36
36
|
else {
|
package/tests/testaro.js
CHANGED
|
@@ -26,7 +26,8 @@ const evalRules = {
|
|
|
26
26
|
motion: 'motion without user request',
|
|
27
27
|
nonTable: 'table elements used for layout',
|
|
28
28
|
radioSet: 'radio buttons not grouped into standard field sets',
|
|
29
|
-
|
|
29
|
+
roleBad: 'invalid and native-replacing explicit roles',
|
|
30
|
+
roleRedundant: 'redundant explicit roles',
|
|
30
31
|
styleDiff: 'style inconsistencies',
|
|
31
32
|
tabNav: 'nonstandard keyboard navigation between elements with the tab role',
|
|
32
33
|
titledEl: 'title attributes on inappropriate elements',
|