testilo 3.8.6 → 3.9.2
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/package.json +1 -1
- package/procs/score/sp14a.js +117 -3
- package/procs/score/sp15a.js +4667 -0
|
@@ -0,0 +1,4667 @@
|
|
|
1
|
+
/*
|
|
2
|
+
sp15a
|
|
3
|
+
Testilo score proc 15a
|
|
4
|
+
|
|
5
|
+
Computes scores from Testaro script tp15 and adds them to a report.
|
|
6
|
+
Usage examples:
|
|
7
|
+
node score sp15a 35k1r
|
|
8
|
+
node score sp15a
|
|
9
|
+
|
|
10
|
+
This proc applies specified weights to the component scores before summing them. An issue reported
|
|
11
|
+
by a test is given a score. That score is determined by:
|
|
12
|
+
Whether the issue is reported as an error or a warning.
|
|
13
|
+
How important the issue is, if the test package is “pre-weighted” (axe, tenon, and testaro)
|
|
14
|
+
Whether the test belongs to a group or is a “solo” test.
|
|
15
|
+
How heavily the group is weighted, if the test package is not pre-weighted and the test belongs
|
|
16
|
+
to a group
|
|
17
|
+
|
|
18
|
+
The scores of solo tests are added together, multiplied by the soloWeight multiplier, and
|
|
19
|
+
contributed to the total score.
|
|
20
|
+
|
|
21
|
+
The scores of grouped tests are aggregated into a group score before being contributed to the
|
|
22
|
+
total score. The group score is the sum of (1) an absolute score, assigned because the group has
|
|
23
|
+
at least one test with a non-zero score, (2) the largest score among the tests of the group
|
|
24
|
+
multiplied by a multiplier, and (3) the sum of the scores from the other tests of the group
|
|
25
|
+
multiplied by a smaller multiplier. These three amounts are given by the groupWeights object.
|
|
26
|
+
|
|
27
|
+
Browser logging produces a log score, and the prevention of tests produces a prevention score.
|
|
28
|
+
They, too, are added to the total score.
|
|
29
|
+
|
|
30
|
+
Each grouped test has a “quality” property, typically set to 1. The value of this property can be
|
|
31
|
+
modified when the test is found to be higher or lower in quality than usual.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// CONSTANTS
|
|
35
|
+
|
|
36
|
+
const scoreProcID = 'sp15a';
|
|
37
|
+
// Define the configuration disclosures.
|
|
38
|
+
const logWeights = {
|
|
39
|
+
logCount: 0.5,
|
|
40
|
+
logSize: 0.01,
|
|
41
|
+
errorLogCount: 1,
|
|
42
|
+
errorLogSize: 0.02,
|
|
43
|
+
prohibitedCount: 15,
|
|
44
|
+
visitTimeoutCount: 10,
|
|
45
|
+
visitRejectionCount: 10
|
|
46
|
+
};
|
|
47
|
+
const soloWeight = 2;
|
|
48
|
+
const groupWeights = {
|
|
49
|
+
absolute: 2,
|
|
50
|
+
largest: 1,
|
|
51
|
+
smaller: 0.4
|
|
52
|
+
};
|
|
53
|
+
const preventionWeights = {
|
|
54
|
+
testaro: 50,
|
|
55
|
+
other: 100
|
|
56
|
+
};
|
|
57
|
+
const otherPackages = ['alfa', 'axe', 'continuum', 'htmlcs', 'ibm', 'nuVal', 'tenon', 'wave'];
|
|
58
|
+
const preWeightedPackages = ['axe', 'tenon', 'testaro'];
|
|
59
|
+
const testMatchers = {
|
|
60
|
+
nuVal: [
|
|
61
|
+
/^CSS: “background-image”: .+ is not a “background-image” value.*$/,
|
|
62
|
+
/^CSS: “background”: .+ is not a “color” value.*$/,
|
|
63
|
+
/^CSS: “cursor”: .+ is not a “cursor” value.*$/,
|
|
64
|
+
/^CSS: “transform”: .+ is not a “transform” value.*$/,
|
|
65
|
+
/^Duplicate ID .+$|^The first occurrence of ID .+ was here.*$/,
|
|
66
|
+
/^Start tag .+ seen but an element of the same type was already open.*$/,
|
|
67
|
+
/^End tag .+ violates nesting rules.*$/,
|
|
68
|
+
/^Attribute .+ is not serializable as XML 1\.0.*$/,
|
|
69
|
+
/^Attribute .+ not allowed on element “meta” at this point.*$/,
|
|
70
|
+
/^Attribute .+ not allowed on element .+ at this point.*$/,
|
|
71
|
+
/^Bad value .+ for attribute .+ on element “meta”.*$/,
|
|
72
|
+
/^Bad value .+ for attribute .+ on element .+$/,
|
|
73
|
+
/^Attribute .+ not allowed here.*$/,
|
|
74
|
+
/^CSS: .+: Property .+ doesn't exist.*$/,
|
|
75
|
+
/^CSS: .+: only “0” can be a “length”. You must put a unit after your number.*$/,
|
|
76
|
+
/^Element .+ not allowed as child of element .+ in this context.*$/
|
|
77
|
+
]
|
|
78
|
+
};
|
|
79
|
+
const groups = {
|
|
80
|
+
ignorable: {
|
|
81
|
+
weight: 0,
|
|
82
|
+
packages: {
|
|
83
|
+
nuVal: {
|
|
84
|
+
'Element “mediaelementwrapper” not allowed as child of element “div” in this context. (Suppressing further errors from this subtree.)': {
|
|
85
|
+
quality: 0,
|
|
86
|
+
what: 'Bug in nuVal'
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
duplicateID: {
|
|
92
|
+
weight: 3,
|
|
93
|
+
packages: {
|
|
94
|
+
alfa: {
|
|
95
|
+
r3: {
|
|
96
|
+
quality: 1,
|
|
97
|
+
what: 'Element id attribute value is not unique'
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
axe: {
|
|
101
|
+
'duplicate-id': {
|
|
102
|
+
quality: 1,
|
|
103
|
+
what: 'id attribute value is not unique'
|
|
104
|
+
},
|
|
105
|
+
'duplicate-id-active': {
|
|
106
|
+
quality: 1,
|
|
107
|
+
what: 'id attribute value of the active element is not unique'
|
|
108
|
+
},
|
|
109
|
+
'duplicate-id-aria': {
|
|
110
|
+
quality: 1,
|
|
111
|
+
what: 'id attribute used in ARIA or in a label has a value that is not unique'
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
continuum: {
|
|
115
|
+
94: {
|
|
116
|
+
quality: 1,
|
|
117
|
+
what: 'Elements contains an id attribute set to a value that is not unique in the DOM'
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
htmlcs: {
|
|
121
|
+
'e:AA.4_1_1.F77': {
|
|
122
|
+
quality: 1,
|
|
123
|
+
what: 'Duplicate id attribute value'
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
ibm: {
|
|
127
|
+
RPT_Elem_UniqueId: {
|
|
128
|
+
quality: 1,
|
|
129
|
+
what: 'Element id attribute value is not unique within the document'
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
nuVal: {
|
|
133
|
+
'^Duplicate ID .+$|^The first occurrence of ID .+ was here.*$': {
|
|
134
|
+
quality: 1,
|
|
135
|
+
what: 'Duplicate id'
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
componentNoText: {
|
|
141
|
+
weight: 4,
|
|
142
|
+
packages: {
|
|
143
|
+
ibm: {
|
|
144
|
+
Rpt_Aria_WidgetLabels_Implicit: {
|
|
145
|
+
quality: 1,
|
|
146
|
+
what: 'Interactive component has no programmatically associated name'
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
regionNoText: {
|
|
152
|
+
weight: 4,
|
|
153
|
+
packages: {
|
|
154
|
+
alfa: {
|
|
155
|
+
r40: {
|
|
156
|
+
quality: 1,
|
|
157
|
+
what: 'Region has no accessible name'
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
continuum: {
|
|
161
|
+
1010: {
|
|
162
|
+
quality: 1,
|
|
163
|
+
what: 'Element with a region role has no mechanism that allows an accessible name to be calculated'
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
ibm: {
|
|
167
|
+
Rpt_Aria_RegionLabel_Implicit: {
|
|
168
|
+
quality: 1,
|
|
169
|
+
what: 'Element with a region role has no label that describes its purpose'
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
formFieldNoText: {
|
|
175
|
+
weight: 4,
|
|
176
|
+
packages: {
|
|
177
|
+
alfa: {
|
|
178
|
+
r8: {
|
|
179
|
+
quality: 1,
|
|
180
|
+
what: 'Form field has no accessible name'
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
inputNoText: {
|
|
186
|
+
weight: 4,
|
|
187
|
+
packages: {
|
|
188
|
+
axe: {
|
|
189
|
+
'aria-input-field-name': {
|
|
190
|
+
quality: 1,
|
|
191
|
+
what: 'ARIA input field has no accessible name'
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
continuum: {
|
|
195
|
+
118: {
|
|
196
|
+
quality: 1,
|
|
197
|
+
what: 'Text input element has no mechanism that allows an accessible name to be calculated'
|
|
198
|
+
},
|
|
199
|
+
370: {
|
|
200
|
+
quality: 1,
|
|
201
|
+
what: 'Search input element has no mechanism that allows an accessible name to be calculated'
|
|
202
|
+
},
|
|
203
|
+
509: {
|
|
204
|
+
quality: 1,
|
|
205
|
+
what: 'element with a textbox role has no mechanism that allows an accessible name to be calculated'
|
|
206
|
+
},
|
|
207
|
+
510: {
|
|
208
|
+
quality: 1,
|
|
209
|
+
what: 'element with a combobox role has no mechanism that allows an accessible name to be calculated'
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
htmlcs: {
|
|
213
|
+
'e:AA.4_1_2.H91.InputText.Name': {
|
|
214
|
+
quality: 1,
|
|
215
|
+
what: 'Text input has no accessible name'
|
|
216
|
+
},
|
|
217
|
+
'e:AA.4_1_2.H91.InputEmail.Name': {
|
|
218
|
+
quality: 1,
|
|
219
|
+
what: 'Email input has no accessible name'
|
|
220
|
+
},
|
|
221
|
+
'e:AA.4_1_2.H91.InputFile.Name': {
|
|
222
|
+
quality: 1,
|
|
223
|
+
what: 'File input element has no accessible name'
|
|
224
|
+
},
|
|
225
|
+
'e:AA.4_1_2.H91.InputTel.Name': {
|
|
226
|
+
quality: 1,
|
|
227
|
+
what: 'Telephone input has no accessible name'
|
|
228
|
+
},
|
|
229
|
+
'e:AA.4_1_2.H91.InputNumber.Name': {
|
|
230
|
+
quality: 1,
|
|
231
|
+
what: 'Number input has no accessible name'
|
|
232
|
+
},
|
|
233
|
+
'e:AA.4_1_2.H91.InputSearch.Name': {
|
|
234
|
+
quality: 1,
|
|
235
|
+
what: 'Search input has no accessible name'
|
|
236
|
+
},
|
|
237
|
+
'e:AA.4_1_2.H91.InputCheckbox.Name': {
|
|
238
|
+
quality: 1,
|
|
239
|
+
what: 'Checkbox input has no accessible name'
|
|
240
|
+
},
|
|
241
|
+
'e:AA.4_1_2.H91.InputRadio.Name': {
|
|
242
|
+
quality: 1,
|
|
243
|
+
what: 'Radio input has no accessible name'
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
inputOnlyPlaceholder: {
|
|
249
|
+
weight: 3,
|
|
250
|
+
packages: {
|
|
251
|
+
continuum: {
|
|
252
|
+
863: {
|
|
253
|
+
quality: 1,
|
|
254
|
+
what: 'input has an accessible name that depends on a placeholder'
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
imageInputNoText: {
|
|
260
|
+
weight: 4,
|
|
261
|
+
packages: {
|
|
262
|
+
alfa: {
|
|
263
|
+
r28: {
|
|
264
|
+
quality: 1,
|
|
265
|
+
what: 'Image input element has no accessible name'
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
axe: {
|
|
269
|
+
'input-image-alt': {
|
|
270
|
+
quality: 1,
|
|
271
|
+
what: 'Image button has no text alternative'
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
htmlcs: {
|
|
275
|
+
'e:H36': {
|
|
276
|
+
quality: 1,
|
|
277
|
+
what: 'Image submit button has no alt attribute'
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
ibm: {
|
|
281
|
+
WCAG20_Input_ExplicitLabelImage: {
|
|
282
|
+
quality: 1,
|
|
283
|
+
what: 'Input element of type image has no text alternative'
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
wave: {
|
|
287
|
+
'e:alt_input_missing': {
|
|
288
|
+
quality: 1,
|
|
289
|
+
what: 'Image button has no alternative text'
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
figureNoText: {
|
|
295
|
+
weight: 4,
|
|
296
|
+
packages: {
|
|
297
|
+
ibm: {
|
|
298
|
+
HAAC_Figure_label: {
|
|
299
|
+
quality: 1,
|
|
300
|
+
what: 'figure element has no associated label'
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
imageNoText: {
|
|
306
|
+
weight: 4,
|
|
307
|
+
packages: {
|
|
308
|
+
alfa: {
|
|
309
|
+
r2: {
|
|
310
|
+
quality: 1,
|
|
311
|
+
what: 'Image has no accessible name'
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
axe: {
|
|
315
|
+
'image-alt': {
|
|
316
|
+
quality: 1,
|
|
317
|
+
what: 'Image has no text alternative'
|
|
318
|
+
},
|
|
319
|
+
'role-img-alt': {
|
|
320
|
+
quality: 1,
|
|
321
|
+
what: 'Element with role img has no text alternative'
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
continuum: {
|
|
325
|
+
87: {
|
|
326
|
+
quality: 1,
|
|
327
|
+
what: 'element with an image, graphics-symbol, or graphics-document role has no mechanism to calculate an accessible name'
|
|
328
|
+
},
|
|
329
|
+
89: {
|
|
330
|
+
quality: 1,
|
|
331
|
+
what: 'img element has no mechanism that allows an accessible name to be calculated'
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
htmlcs: {
|
|
335
|
+
'e:AA.1_1_1.H37': {
|
|
336
|
+
quality: 1,
|
|
337
|
+
what: 'img element has no alt attribute'
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
ibm: {
|
|
341
|
+
HAAC_Aria_ImgAlt: {
|
|
342
|
+
quality: 1,
|
|
343
|
+
what: 'Element with an img role has no non-empty label'
|
|
344
|
+
},
|
|
345
|
+
WCAG20_Img_HasAlt: {
|
|
346
|
+
quality: 1,
|
|
347
|
+
what: 'Image has no alt attribute conveying its meaning, or alt="" if decorative'
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
nuVal: {
|
|
351
|
+
'An “img” element must have an “alt” attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.': {
|
|
352
|
+
quality: 1,
|
|
353
|
+
what: 'img element has no alt attribute'
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
wave: {
|
|
357
|
+
'e:alt_missing': {
|
|
358
|
+
quality: 1,
|
|
359
|
+
what: 'Text alternative is missing'
|
|
360
|
+
},
|
|
361
|
+
'e:alt_spacer_missing': {
|
|
362
|
+
quality: 1,
|
|
363
|
+
what: 'Spacer image has no text alternative'
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
decorativeImageAltBad: {
|
|
369
|
+
weight: 4,
|
|
370
|
+
packages: {
|
|
371
|
+
ibm: {
|
|
372
|
+
WCAG20_Img_PresentationImgHasNonNullAlt: {
|
|
373
|
+
quality: 1,
|
|
374
|
+
what: 'Image designated as decorative has no alt=""'
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
imageTextBad: {
|
|
380
|
+
weight: 3,
|
|
381
|
+
packages: {
|
|
382
|
+
alfa: {
|
|
383
|
+
r39: {
|
|
384
|
+
quality: 1,
|
|
385
|
+
what: 'Image text alternative is the filename instead'
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
imageNoSource: {
|
|
391
|
+
weight: 4,
|
|
392
|
+
packages: {
|
|
393
|
+
nuVal: {
|
|
394
|
+
'Element “img” is missing required attribute “src”.': {
|
|
395
|
+
quality: 1,
|
|
396
|
+
what: 'img element has no src attribute'
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
backgroundBad: {
|
|
402
|
+
weight: 4,
|
|
403
|
+
packages: {
|
|
404
|
+
nuVal: {
|
|
405
|
+
'^CSS: “background”: .+ is not a “color” value.*$': {
|
|
406
|
+
quality: 1,
|
|
407
|
+
what: 'CSS background color is misdefined'
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
backgroundImageBad: {
|
|
413
|
+
weight: 4,
|
|
414
|
+
packages: {
|
|
415
|
+
nuVal: {
|
|
416
|
+
'^CSS: “background-image”: .+ is not a “background-image” value.*$': {
|
|
417
|
+
quality: 1,
|
|
418
|
+
what: 'CSS background image is misdefined'
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
inputAlt: {
|
|
424
|
+
weight: 4,
|
|
425
|
+
packages: {
|
|
426
|
+
continuum: {
|
|
427
|
+
15: {
|
|
428
|
+
quality: 1,
|
|
429
|
+
what: 'input element has an alt attribute'
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
imagesSameAlt: {
|
|
435
|
+
weight: 1,
|
|
436
|
+
packages: {
|
|
437
|
+
wave: {
|
|
438
|
+
'a:alt_duplicate': {
|
|
439
|
+
quality: 1,
|
|
440
|
+
what: 'Two images near each other have the same text alternative'
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
imageTextLong: {
|
|
446
|
+
weight: 2,
|
|
447
|
+
packages: {
|
|
448
|
+
wave: {
|
|
449
|
+
'a:alt_long': {
|
|
450
|
+
quality: 1,
|
|
451
|
+
what: 'Long text alternative'
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
imageTextRisk: {
|
|
457
|
+
weight: 1,
|
|
458
|
+
packages: {
|
|
459
|
+
continuum: {
|
|
460
|
+
234: {
|
|
461
|
+
quality: 1,
|
|
462
|
+
what: 'img element has a suspicious calculated accessible name value'
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
wave: {
|
|
466
|
+
'a:alt_suspicious': {
|
|
467
|
+
quality: 1,
|
|
468
|
+
what: 'Image text alternative is suspicious'
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
decorativeImageRisk: {
|
|
474
|
+
weight: 1,
|
|
475
|
+
packages: {
|
|
476
|
+
htmlcs: {
|
|
477
|
+
'w:AA.1_1_1.H67.2': {
|
|
478
|
+
quality: 1,
|
|
479
|
+
what: 'Image marked as decorative may be informative'
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
decorativeElementExposed: {
|
|
485
|
+
weight: 1,
|
|
486
|
+
packages: {
|
|
487
|
+
alfa: {
|
|
488
|
+
r67: {
|
|
489
|
+
quality: 1,
|
|
490
|
+
what: 'Image marked as decorative is in the accessibility tree or has no none/presentation role'
|
|
491
|
+
},
|
|
492
|
+
r86: {
|
|
493
|
+
quality: 1,
|
|
494
|
+
what: 'Element marked as decorative is in the accessibility tree or has no none/presentation role'
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
pageLanguage: {
|
|
500
|
+
weight: 4,
|
|
501
|
+
packages: {
|
|
502
|
+
alfa: {
|
|
503
|
+
r4: {
|
|
504
|
+
quality: 1,
|
|
505
|
+
what: 'Lang attribute missing, empty, or only whitespace'
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
axe: {
|
|
509
|
+
'html-has-lang': {
|
|
510
|
+
quality: 1,
|
|
511
|
+
what: 'html element has no lang attribute'
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
continuum: {
|
|
515
|
+
101: {
|
|
516
|
+
quality: 1,
|
|
517
|
+
what: 'root html element has no lang attribute'
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
htmlcs: {
|
|
521
|
+
'e:AA.3_1_1.H57.2': {
|
|
522
|
+
quality: 1,
|
|
523
|
+
what: 'html element has no lang or xml:lang attribute'
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
ibm: {
|
|
527
|
+
WCAG20_Html_HasLang: {
|
|
528
|
+
quality: 1,
|
|
529
|
+
what: 'Page detected as HTML, but has no lang attribute'
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
wave: {
|
|
533
|
+
'e:language_missing': {
|
|
534
|
+
quality: 1,
|
|
535
|
+
what: 'Language missing or invalid'
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
pageLanguageBad: {
|
|
541
|
+
weight: 4,
|
|
542
|
+
packages: {
|
|
543
|
+
alfa: {
|
|
544
|
+
r5: {
|
|
545
|
+
quality: 1,
|
|
546
|
+
what: 'lang attribute has no valid primary language tag'
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
axe: {
|
|
550
|
+
'html-lang-valid': {
|
|
551
|
+
quality: 1,
|
|
552
|
+
what: 'html element has no valid value for the lang attribute'
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
htmlcs: {
|
|
556
|
+
'e:AA.3_1_1.H57.3.Lang': {
|
|
557
|
+
quality: 1,
|
|
558
|
+
what: 'Language specified in the lang attribute of the document does not appear to be well-formed'
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
ibm: {
|
|
562
|
+
WCAG20_Elem_Lang_Valid: {
|
|
563
|
+
quality: 1,
|
|
564
|
+
what: 'lang attribute does not include a valid primary language'
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
elementLanguageBad: {
|
|
570
|
+
weight: 4,
|
|
571
|
+
packages: {
|
|
572
|
+
htmlcs: {
|
|
573
|
+
'e:AA.3_1_2.H58.1.Lang': {
|
|
574
|
+
quality: 1,
|
|
575
|
+
what: 'Language specified in the lang attribute of the element does not appear to be well-formed'
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
languageChange: {
|
|
581
|
+
weight: 3,
|
|
582
|
+
packages: {
|
|
583
|
+
alfa: {
|
|
584
|
+
r7: {
|
|
585
|
+
quality: 1,
|
|
586
|
+
what: 'lang attribute has no valid primary language subtag'
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
axe: {
|
|
590
|
+
'valid-lang': {
|
|
591
|
+
quality: 1,
|
|
592
|
+
what: 'lang attribute has no valid value'
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
htmlcs: {
|
|
596
|
+
'e:WCAG2AAA.Principle3.Guideline3_1.3_1_2.H58': {
|
|
597
|
+
quality: 1,
|
|
598
|
+
what: 'Change in language is not marked'
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
dialogNoText: {
|
|
604
|
+
weight: 4,
|
|
605
|
+
packages: {
|
|
606
|
+
axe: {
|
|
607
|
+
'aria-dialog-name': {
|
|
608
|
+
quality: 1,
|
|
609
|
+
what: 'ARIA dialog or alertdialog node has no accessible name'
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
continuum: {
|
|
613
|
+
736: {
|
|
614
|
+
quality: 1,
|
|
615
|
+
what: 'Element with a dialog role has no mechanism that allows an accessible name to be calculated'
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
applicationNoText: {
|
|
621
|
+
weight: 4,
|
|
622
|
+
packages: {
|
|
623
|
+
ibm: {
|
|
624
|
+
Rpt_Aria_ApplicationLandmarkLabel: {
|
|
625
|
+
quality: 1,
|
|
626
|
+
what: 'Element with an application role has no purpose label'
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
objectNoText: {
|
|
632
|
+
weight: 4,
|
|
633
|
+
packages: {
|
|
634
|
+
alfa: {
|
|
635
|
+
r63: {
|
|
636
|
+
quality: 1,
|
|
637
|
+
what: 'Object element has no accessible name'
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
axe: {
|
|
641
|
+
'object-alt': {
|
|
642
|
+
quality: 1,
|
|
643
|
+
what: 'Object element has no text alternative'
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
continuum: {
|
|
647
|
+
249: {
|
|
648
|
+
quality: 1,
|
|
649
|
+
what: 'object element has no mechanism that allows an accessible name to be calculated'
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
htmlcs: {
|
|
653
|
+
'e:ARIA6+H53': {
|
|
654
|
+
quality: 1,
|
|
655
|
+
what: 'Object element contains no text alternative'
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
ibm: {
|
|
659
|
+
WCAG20_Object_HasText: {
|
|
660
|
+
quality: 1,
|
|
661
|
+
what: 'Object element has no text alternative'
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
wave: {
|
|
665
|
+
'a:plugin': {
|
|
666
|
+
quality: 1,
|
|
667
|
+
what: 'An unidentified plugin is present'
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
videoNoText: {
|
|
673
|
+
weight: 4,
|
|
674
|
+
packages: {
|
|
675
|
+
continuum: {
|
|
676
|
+
252: {
|
|
677
|
+
quality: 1,
|
|
678
|
+
what: 'video element has no mechanism that allows an accessible name to be calculated'
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
},
|
|
683
|
+
imageMapNoText: {
|
|
684
|
+
weight: 4,
|
|
685
|
+
packages: {
|
|
686
|
+
wave: {
|
|
687
|
+
'e:alt_map_missing': {
|
|
688
|
+
quality: 1,
|
|
689
|
+
what: 'Image that has hot spots has no alt attribute'
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
imageMapAreaNoText: {
|
|
695
|
+
weight: 4,
|
|
696
|
+
packages: {
|
|
697
|
+
axe: {
|
|
698
|
+
'area-alt': {
|
|
699
|
+
quality: 1,
|
|
700
|
+
what: 'Active area element has no text alternative'
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
htmlcs: {
|
|
704
|
+
'e:AA.1_1_1.H24': {
|
|
705
|
+
quality: 1,
|
|
706
|
+
what: 'Area element in an image map has no alt attribute'
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
ibm: {
|
|
710
|
+
HAAC_Img_UsemapAlt: {
|
|
711
|
+
quality: 1,
|
|
712
|
+
what: 'Image map or child area has no text alternative'
|
|
713
|
+
},
|
|
714
|
+
'WCAG20_Area_HasAlt': {
|
|
715
|
+
quality: 1,
|
|
716
|
+
what: 'Area element in an image map has no text alternative'
|
|
717
|
+
}
|
|
718
|
+
},
|
|
719
|
+
wave: {
|
|
720
|
+
'e:alt_area_missing': {
|
|
721
|
+
quality: 1,
|
|
722
|
+
what: 'Image map area has no alternative text'
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
objectBlurKeyboardRisk: {
|
|
728
|
+
weight: 1,
|
|
729
|
+
packages: {
|
|
730
|
+
htmlcs: {
|
|
731
|
+
'w:AA.2_1_2.F10': {
|
|
732
|
+
quality: 1,
|
|
733
|
+
what: 'Applet or plugin may fail to enable moving the focus away with the keyboard'
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
keyboardAccess: {
|
|
739
|
+
weight: 4,
|
|
740
|
+
packages: {
|
|
741
|
+
tenon: {
|
|
742
|
+
180: {
|
|
743
|
+
quality: 1,
|
|
744
|
+
what: 'Element is interactive but has a negative tabindex value'
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
eventKeyboardRisk: {
|
|
750
|
+
weight: 1,
|
|
751
|
+
packages: {
|
|
752
|
+
htmlcs: {
|
|
753
|
+
'w:AA.2_1_1.G90': {
|
|
754
|
+
quality: 1,
|
|
755
|
+
what: 'Event handler functionality may not be available by keyboard'
|
|
756
|
+
},
|
|
757
|
+
'w:AA.2_1_1.SCR20.MouseOut': {
|
|
758
|
+
quality: 1,
|
|
759
|
+
what: 'Mousing-out functionality may not be available by keyboard'
|
|
760
|
+
},
|
|
761
|
+
'w:AA.2_1_1.SCR20.MouseOver': {
|
|
762
|
+
quality: 1,
|
|
763
|
+
what: 'Mousing-over functionality may not be available by keyboard'
|
|
764
|
+
},
|
|
765
|
+
'w:AA.2_1_1.SCR20.MouseDown': {
|
|
766
|
+
quality: 1,
|
|
767
|
+
what: 'Mousing-down functionality may not be available by keyboard'
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
wave: {
|
|
771
|
+
'a:event_handler': {
|
|
772
|
+
quality: 1,
|
|
773
|
+
what: 'Device-dependent event handler'
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
},
|
|
778
|
+
internalLinkBroken: {
|
|
779
|
+
weight: 4,
|
|
780
|
+
packages: {
|
|
781
|
+
htmlcs: {
|
|
782
|
+
'e:AA.2_4_1.G1,G123,G124.NoSuchID': {
|
|
783
|
+
quality: 1,
|
|
784
|
+
what: 'Internal link references a nonexistent destination'
|
|
785
|
+
}
|
|
786
|
+
},
|
|
787
|
+
wave: {
|
|
788
|
+
'a:label_orphaned': {
|
|
789
|
+
quality: 1,
|
|
790
|
+
what: 'Orphaned form label'
|
|
791
|
+
},
|
|
792
|
+
'a:link_internal_broken': {
|
|
793
|
+
quality: 1,
|
|
794
|
+
what: 'Broken same-page link'
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
labelForWrongRisk: {
|
|
800
|
+
weight: 1,
|
|
801
|
+
packages: {
|
|
802
|
+
htmlcs: {
|
|
803
|
+
'w:AA.1_3_1.H44.NotFormControl': {
|
|
804
|
+
quality: 1,
|
|
805
|
+
what: 'Label for attribute may reference the wrong element, because it is not a form control'
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
ariaLabelWrongRisk: {
|
|
811
|
+
weight: 1,
|
|
812
|
+
packages: {
|
|
813
|
+
nuVal: {
|
|
814
|
+
'Possible misuse of “aria-label”. (If you disagree with this warning, file an issue report or send e-mail to www-validator@w3.org.)': {
|
|
815
|
+
quality: 1,
|
|
816
|
+
what: 'aria-label attribute may be misused'
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
activeDescendantBadID: {
|
|
822
|
+
weight: 4,
|
|
823
|
+
packages: {
|
|
824
|
+
continuum: {
|
|
825
|
+
290: {
|
|
826
|
+
quality: 1,
|
|
827
|
+
what: 'aria-activedescendant attribute is set to an invalid or duplicate id'
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
ibm: {
|
|
831
|
+
HAAC_ActiveDescendantCheck: {
|
|
832
|
+
quality: 1,
|
|
833
|
+
what: 'aria-activedescendant property does not reference the id of a non-empty, non-hidden active child element'
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
controlleeBadID: {
|
|
839
|
+
weight: 4,
|
|
840
|
+
packages: {
|
|
841
|
+
continuum: {
|
|
842
|
+
85: {
|
|
843
|
+
quality: 1,
|
|
844
|
+
what: 'aria-controls attribute references an invalid or duplicate ID'
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
descriptionBadID: {
|
|
850
|
+
weight: 4,
|
|
851
|
+
packages: {
|
|
852
|
+
continuum: {
|
|
853
|
+
83: {
|
|
854
|
+
quality: 1,
|
|
855
|
+
what: 'aria-describedby attribute references an invalid or duplicate ID'
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
},
|
|
860
|
+
labelFollows: {
|
|
861
|
+
weight: 1,
|
|
862
|
+
packages: {
|
|
863
|
+
ibm: {
|
|
864
|
+
WCAG20_Input_LabelBefore: {
|
|
865
|
+
quality: 1,
|
|
866
|
+
what: 'Text input or select element label follows the input control'
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
labelBadID: {
|
|
872
|
+
weight: 4,
|
|
873
|
+
packages: {
|
|
874
|
+
continuum: {
|
|
875
|
+
95: {
|
|
876
|
+
quality: 1,
|
|
877
|
+
what: 'element has an aria-labelledby value that includes an invalid or duplicate id'
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
htmlcs: {
|
|
881
|
+
'w:AA.1_3_1.H44.NonExistentFragment': {
|
|
882
|
+
quality: 1,
|
|
883
|
+
what: 'Label for attribute references a nonexistent element'
|
|
884
|
+
},
|
|
885
|
+
'w:AA.1_3_1.ARIA16,ARIA9': {
|
|
886
|
+
quality: 1,
|
|
887
|
+
what: 'aria-labelledby attribute references a nonexistent element'
|
|
888
|
+
},
|
|
889
|
+
'w:AA.4_1_2.ARIA16,ARIA9': {
|
|
890
|
+
quality: 1,
|
|
891
|
+
what: 'aria-labelledby attribute references a nonexistent element'
|
|
892
|
+
}
|
|
893
|
+
},
|
|
894
|
+
ibm: {
|
|
895
|
+
WCAG20_Label_RefValid: {
|
|
896
|
+
quality: 1,
|
|
897
|
+
what: 'for attribute does not reference a non-empty, unique id attribute of an input element'
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
nuVal: {
|
|
901
|
+
'The “aria-labelledby” attribute must point to an element in the same document.': {
|
|
902
|
+
quality: 1,
|
|
903
|
+
what: 'aria-labelledby attribute references an element not in the document'
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
wave: {
|
|
907
|
+
'a:label_orphaned': {
|
|
908
|
+
quality: 1,
|
|
909
|
+
what: 'Orphaned form label'
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
haspopupBad: {
|
|
915
|
+
weight: 4,
|
|
916
|
+
packages: {
|
|
917
|
+
ibm: {
|
|
918
|
+
combobox_haspopup: {
|
|
919
|
+
quality: 1,
|
|
920
|
+
what: 'aria-haspopup value is invalid for the role of the controlled or owned element'
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
ownerConflict: {
|
|
926
|
+
weight: 4,
|
|
927
|
+
packages: {
|
|
928
|
+
continuum: {
|
|
929
|
+
360: {
|
|
930
|
+
quality: 1,
|
|
931
|
+
what: 'Element and another element have aria-owns attributes with identical id values'
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
},
|
|
936
|
+
linkNoText: {
|
|
937
|
+
weight: 4,
|
|
938
|
+
packages: {
|
|
939
|
+
alfa: {
|
|
940
|
+
r11: {
|
|
941
|
+
quality: 1,
|
|
942
|
+
what: 'Link has no accessible name'
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
axe: {
|
|
946
|
+
'link-name': {
|
|
947
|
+
quality: 1,
|
|
948
|
+
what: 'Link has no discernible text'
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
continuum: {
|
|
952
|
+
237: {
|
|
953
|
+
quality: 1,
|
|
954
|
+
what: 'a element has no mechanism that allows an accessible name value to be calculated'
|
|
955
|
+
}
|
|
956
|
+
},
|
|
957
|
+
htmlcs: {
|
|
958
|
+
'e:AA.1_1_1.H30.2': {
|
|
959
|
+
quality: 1,
|
|
960
|
+
what: 'img element is the only link content but has no text alternative'
|
|
961
|
+
},
|
|
962
|
+
'w:AA.4_1_2.H91.A.Empty': {
|
|
963
|
+
quality: 1,
|
|
964
|
+
what: 'Link element has an id attribute but no href attribute or text'
|
|
965
|
+
},
|
|
966
|
+
'e:AA.4_1_2.H91.A.EmptyNoId': {
|
|
967
|
+
quality: 1,
|
|
968
|
+
what: 'Link has no name or id attribute or value'
|
|
969
|
+
},
|
|
970
|
+
'w:AA.4_1_2.H91.A.EmptyWithName': {
|
|
971
|
+
quality: 1,
|
|
972
|
+
what: 'Link has a name attribute but no href attribute or text'
|
|
973
|
+
},
|
|
974
|
+
'e:AA.4_1_2.H91.A.NoContent': {
|
|
975
|
+
quality: 1,
|
|
976
|
+
what: 'Link has an href attribute but no text'
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
ibm: {
|
|
980
|
+
WCAG20_A_HasText: {
|
|
981
|
+
quality: 1,
|
|
982
|
+
what: 'Hyperlink has no text description'
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
tenon: {
|
|
986
|
+
57: {
|
|
987
|
+
quality: 1,
|
|
988
|
+
what: 'Link has no text inside it'
|
|
989
|
+
},
|
|
990
|
+
91: {
|
|
991
|
+
quality: 1,
|
|
992
|
+
what: 'Link has a background image but no text inside it'
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
wave: {
|
|
996
|
+
'e:link_empty': {
|
|
997
|
+
quality: 1,
|
|
998
|
+
what: 'Link contains no text'
|
|
999
|
+
},
|
|
1000
|
+
'e:alt_link_missing': {
|
|
1001
|
+
quality: 1,
|
|
1002
|
+
what: 'Linked image has no text alternative'
|
|
1003
|
+
},
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
linkBrokenRisk: {
|
|
1008
|
+
weight: 2,
|
|
1009
|
+
packages: {
|
|
1010
|
+
htmlcs: {
|
|
1011
|
+
'w:AA.4_1_2.H91.A.Placeholder': {
|
|
1012
|
+
quality: 1,
|
|
1013
|
+
what: 'Link has text but no href, id, or name attribute'
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
acronymNoTitle: {
|
|
1019
|
+
weight: 4,
|
|
1020
|
+
packages: {
|
|
1021
|
+
tenon: {
|
|
1022
|
+
117: {
|
|
1023
|
+
quality: 1,
|
|
1024
|
+
what: 'acronym element has no useful title value (and is deprecated; use abbr)'
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
abbreviationNoTitle: {
|
|
1030
|
+
weight: 4,
|
|
1031
|
+
packages: {
|
|
1032
|
+
tenon: {
|
|
1033
|
+
233: {
|
|
1034
|
+
quality: 1,
|
|
1035
|
+
what: 'abbr element is first for its abbreviation but has no useful title value'
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
},
|
|
1040
|
+
pdfLink: {
|
|
1041
|
+
weight: 1,
|
|
1042
|
+
packages: {
|
|
1043
|
+
wave: {
|
|
1044
|
+
'a:link_pdf': {
|
|
1045
|
+
quality: 1,
|
|
1046
|
+
what: 'Link to PDF document'
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
},
|
|
1051
|
+
destinationLink: {
|
|
1052
|
+
weight: 2,
|
|
1053
|
+
packages: {
|
|
1054
|
+
htmlcs: {
|
|
1055
|
+
'w:AA.4_1_2.H91.A.NoHref': {
|
|
1056
|
+
quality: 1,
|
|
1057
|
+
what: 'Link is misused as a link destination'
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
testaro: {
|
|
1061
|
+
linkTo: {
|
|
1062
|
+
quality: 1,
|
|
1063
|
+
what: 'Link has no href attribute'
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
},
|
|
1068
|
+
textAreaNoText: {
|
|
1069
|
+
weight: 4,
|
|
1070
|
+
packages: {
|
|
1071
|
+
htmlcs: {
|
|
1072
|
+
'e:AA.4_1_2.H91.Textarea.Name': {
|
|
1073
|
+
quality: 1,
|
|
1074
|
+
what: 'textarea element has no accessible name'
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
},
|
|
1079
|
+
linkTextsSame: {
|
|
1080
|
+
weight: 2,
|
|
1081
|
+
packages: {
|
|
1082
|
+
htmlcs: {
|
|
1083
|
+
'e:AA.1_1_1.H2.EG3': {
|
|
1084
|
+
quality: 1,
|
|
1085
|
+
what: 'alt value of the link img element duplicates the text of a link beside it'
|
|
1086
|
+
}
|
|
1087
|
+
},
|
|
1088
|
+
tenon: {
|
|
1089
|
+
98: {
|
|
1090
|
+
quality: 1,
|
|
1091
|
+
what: 'Links have the same text but different destinations'
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
nextLinkDestinationSame: {
|
|
1097
|
+
weight: 2,
|
|
1098
|
+
packages: {
|
|
1099
|
+
tenon: {
|
|
1100
|
+
184: {
|
|
1101
|
+
quality: 1,
|
|
1102
|
+
what: 'Adjacent links point to the same destination'
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
linkDestinationsSame: {
|
|
1108
|
+
weight: 2,
|
|
1109
|
+
packages: {
|
|
1110
|
+
tenon: {
|
|
1111
|
+
132: {
|
|
1112
|
+
quality: 1,
|
|
1113
|
+
what: 'area element has the same href as another but a different alt'
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
linkConfusionRisk: {
|
|
1119
|
+
weight: 1,
|
|
1120
|
+
packages: {
|
|
1121
|
+
axe: {
|
|
1122
|
+
'identical-links-same-purpose': {
|
|
1123
|
+
quality: 1,
|
|
1124
|
+
what: 'Links with the same accessible name may serve dissimilar purposes'
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
},
|
|
1129
|
+
linkPair: {
|
|
1130
|
+
weight: 2,
|
|
1131
|
+
packages: {
|
|
1132
|
+
wave: {
|
|
1133
|
+
'a:link_redundant': {
|
|
1134
|
+
quality: 1,
|
|
1135
|
+
what: 'Adjacent links go to the same URL'
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
},
|
|
1140
|
+
formNewWindow: {
|
|
1141
|
+
weight: 2,
|
|
1142
|
+
packages: {
|
|
1143
|
+
tenon: {
|
|
1144
|
+
214: {
|
|
1145
|
+
quality: 1,
|
|
1146
|
+
what: 'Form submission opens a new window'
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
linkForcesNewWindow: {
|
|
1152
|
+
weight: 3,
|
|
1153
|
+
packages: {
|
|
1154
|
+
tenon: {
|
|
1155
|
+
218: {
|
|
1156
|
+
quality: 1,
|
|
1157
|
+
what: 'Link opens in a new window without user control'
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
},
|
|
1162
|
+
linkWindowSurpriseRisk: {
|
|
1163
|
+
weight: 1,
|
|
1164
|
+
packages: {
|
|
1165
|
+
htmlcs: {
|
|
1166
|
+
'w:WCAG2AAA.Principle3.Guideline3_2.3_2_5.H83.3': {
|
|
1167
|
+
quality: 1,
|
|
1168
|
+
what: 'Link may open in a new window without notice'
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
},
|
|
1173
|
+
selectNavSurpriseRisk: {
|
|
1174
|
+
weight: 1,
|
|
1175
|
+
packages: {
|
|
1176
|
+
wave: {
|
|
1177
|
+
'a:javascript_jumpmenu': {
|
|
1178
|
+
quality: 1,
|
|
1179
|
+
what: 'selection change may navigate to another page without notice'
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
},
|
|
1184
|
+
buttonAlt: {
|
|
1185
|
+
weight: 4,
|
|
1186
|
+
packages: {
|
|
1187
|
+
nuVal: {
|
|
1188
|
+
'Attribute “alt” not allowed on element “button” at this point.': {
|
|
1189
|
+
quality: 1,
|
|
1190
|
+
what: 'button element has an alt attribute'
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
},
|
|
1195
|
+
buttonNoText: {
|
|
1196
|
+
weight: 4,
|
|
1197
|
+
packages: {
|
|
1198
|
+
alfa: {
|
|
1199
|
+
r12: {
|
|
1200
|
+
quality: 1,
|
|
1201
|
+
what: 'Button has no accessible name'
|
|
1202
|
+
}
|
|
1203
|
+
},
|
|
1204
|
+
axe: {
|
|
1205
|
+
'aria-command-name': {
|
|
1206
|
+
quality: 1,
|
|
1207
|
+
what: 'ARIA command has no accessible name'
|
|
1208
|
+
},
|
|
1209
|
+
'button-name': {
|
|
1210
|
+
quality: 1,
|
|
1211
|
+
what: 'Button has no discernible text'
|
|
1212
|
+
},
|
|
1213
|
+
'input-button-name': {
|
|
1214
|
+
quality: 1,
|
|
1215
|
+
what: 'Input button has no discernible text'
|
|
1216
|
+
}
|
|
1217
|
+
},
|
|
1218
|
+
continuum: {
|
|
1219
|
+
224: {
|
|
1220
|
+
quality: 1,
|
|
1221
|
+
what: 'button element has no mechanism that allows an accessible name to be calculated'
|
|
1222
|
+
}
|
|
1223
|
+
},
|
|
1224
|
+
htmlcs: {
|
|
1225
|
+
'e:AA.4_1_2.H91.A.Name': {
|
|
1226
|
+
quality: 1,
|
|
1227
|
+
what: 'Link with button role has no accessible name'
|
|
1228
|
+
},
|
|
1229
|
+
'e:AA.4_1_2.H91.Div.Name': {
|
|
1230
|
+
quality: 1,
|
|
1231
|
+
what: 'div element with button role has no accessible name'
|
|
1232
|
+
},
|
|
1233
|
+
'e:AA.4_1_2.H91.Button.Name': {
|
|
1234
|
+
quality: 1,
|
|
1235
|
+
what: 'Button element has no accessible name'
|
|
1236
|
+
},
|
|
1237
|
+
'e:AA.4_1_2.H91.Img.Name': {
|
|
1238
|
+
quality: 1,
|
|
1239
|
+
what: 'img element with button role has no accessible name'
|
|
1240
|
+
},
|
|
1241
|
+
'e:AA.4_1_2.H91.InputButton.Name': {
|
|
1242
|
+
quality: 1,
|
|
1243
|
+
what: 'Button input element has no accessible name'
|
|
1244
|
+
},
|
|
1245
|
+
'e:AA.4_1_2.H91.Span.Name': {
|
|
1246
|
+
quality: 1,
|
|
1247
|
+
what: 'Element with button role has no accessible name'
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
wave: {
|
|
1251
|
+
'e:button_empty': {
|
|
1252
|
+
quality: 1,
|
|
1253
|
+
what: 'Button is empty or has no value text'
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
},
|
|
1258
|
+
parentMissing: {
|
|
1259
|
+
weight: 4,
|
|
1260
|
+
packages: {
|
|
1261
|
+
alfa: {
|
|
1262
|
+
r42: {
|
|
1263
|
+
quality: 1,
|
|
1264
|
+
what: 'Element is not owned by an element of its required context role'
|
|
1265
|
+
}
|
|
1266
|
+
},
|
|
1267
|
+
axe: {
|
|
1268
|
+
'aria-required-parent': {
|
|
1269
|
+
quality: 1,
|
|
1270
|
+
what: 'ARIA role is not contained by a required parent'
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
ibm: {
|
|
1274
|
+
Rpt_Aria_RequiredParent_Native_Host_Sematics: {
|
|
1275
|
+
quality: 1,
|
|
1276
|
+
what: 'Element is not contained within a role-valid element'
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
svgImageNoText: {
|
|
1282
|
+
weight: 4,
|
|
1283
|
+
packages: {
|
|
1284
|
+
alfa: {
|
|
1285
|
+
r43: {
|
|
1286
|
+
quality: 1,
|
|
1287
|
+
what: 'SVG image element has no accessible name'
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
axe: {
|
|
1291
|
+
'svg-img-alt': {
|
|
1292
|
+
quality: 1,
|
|
1293
|
+
what: 'svg element with an img role has no text alternative'
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
continuum: {
|
|
1297
|
+
123: {
|
|
1298
|
+
quality: 1,
|
|
1299
|
+
what: 'svg element has no mechanism that allows an accessible name to be calculated'
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
cssBansRotate: {
|
|
1305
|
+
weight: 4,
|
|
1306
|
+
packages: {
|
|
1307
|
+
axe: {
|
|
1308
|
+
'css-orientation-lock': {
|
|
1309
|
+
quality: 1,
|
|
1310
|
+
what: 'CSS media query locks display orientation'
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
},
|
|
1315
|
+
textRotated: {
|
|
1316
|
+
weight: 2,
|
|
1317
|
+
packages: {
|
|
1318
|
+
tenon: {
|
|
1319
|
+
271: {
|
|
1320
|
+
quality: 1,
|
|
1321
|
+
what: 'Text is needlessly rotated 60+ degrees or more, hurting comprehension'
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
metaBansZoom: {
|
|
1327
|
+
weight: 4,
|
|
1328
|
+
packages: {
|
|
1329
|
+
alfa: {
|
|
1330
|
+
r47: {
|
|
1331
|
+
quality: 1,
|
|
1332
|
+
what: 'Meta element restricts zooming'
|
|
1333
|
+
}
|
|
1334
|
+
},
|
|
1335
|
+
axe: {
|
|
1336
|
+
'meta-viewport': {
|
|
1337
|
+
quality: 1,
|
|
1338
|
+
what: 'Zooming and scaling are disabled'
|
|
1339
|
+
},
|
|
1340
|
+
'meta-viewport-large': {
|
|
1341
|
+
quality: 1,
|
|
1342
|
+
what: 'User cannot zoom and scale the text up to 500%'
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
continuum: {
|
|
1346
|
+
55: {
|
|
1347
|
+
quality: 1,
|
|
1348
|
+
what: 'meta element in the head stops a user from scaling the viewport size'
|
|
1349
|
+
},
|
|
1350
|
+
59: {
|
|
1351
|
+
quality: 1,
|
|
1352
|
+
what: 'meta element in the head sets the viewport maximum-scale to less than 2'
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
},
|
|
1357
|
+
childMissing: {
|
|
1358
|
+
weight: 4,
|
|
1359
|
+
packages: {
|
|
1360
|
+
alfa: {
|
|
1361
|
+
r68: {
|
|
1362
|
+
quality: 1,
|
|
1363
|
+
what: 'Element does not own an element required by its semantic role'
|
|
1364
|
+
}
|
|
1365
|
+
},
|
|
1366
|
+
axe: {
|
|
1367
|
+
'aria-required-children': {
|
|
1368
|
+
quality: 1,
|
|
1369
|
+
what: 'ARIA role does not contain a required child'
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
presentationChild: {
|
|
1375
|
+
weight: 4,
|
|
1376
|
+
packages: {
|
|
1377
|
+
htmlcs: {
|
|
1378
|
+
'e:AA.1_3_1.F92,ARIA4': {
|
|
1379
|
+
quality: 1,
|
|
1380
|
+
what: 'Element has presentation role but semantic child'
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
fontSizeAbsolute: {
|
|
1386
|
+
weight: 2,
|
|
1387
|
+
packages: {
|
|
1388
|
+
alfa: {
|
|
1389
|
+
r74: {
|
|
1390
|
+
quality: 1,
|
|
1391
|
+
what: 'Paragraph text has an absolute font size'
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
},
|
|
1396
|
+
fontSmall: {
|
|
1397
|
+
weight: 3,
|
|
1398
|
+
packages: {
|
|
1399
|
+
alfa: {
|
|
1400
|
+
r75: {
|
|
1401
|
+
quality: 1,
|
|
1402
|
+
what: 'Font size is smaller than 9 pixels'
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1405
|
+
tenon: {
|
|
1406
|
+
134: {
|
|
1407
|
+
quality: 1,
|
|
1408
|
+
what: 'Text is very small'
|
|
1409
|
+
}
|
|
1410
|
+
},
|
|
1411
|
+
testaro: {
|
|
1412
|
+
miniText: {
|
|
1413
|
+
quality: 1,
|
|
1414
|
+
what: 'Text node has a font smaller than 11 pixels'
|
|
1415
|
+
}
|
|
1416
|
+
},
|
|
1417
|
+
wave: {
|
|
1418
|
+
'a:text_small': {
|
|
1419
|
+
quality: 1,
|
|
1420
|
+
what: 'Text is very small'
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
leadingFrozen: {
|
|
1426
|
+
weight: 4,
|
|
1427
|
+
packages: {
|
|
1428
|
+
alfa: {
|
|
1429
|
+
r93: {
|
|
1430
|
+
quality: 1,
|
|
1431
|
+
what: 'Style attribute with !important prevents adjusting line height'
|
|
1432
|
+
}
|
|
1433
|
+
},
|
|
1434
|
+
axe: {
|
|
1435
|
+
'avoid-inline-spacing': {
|
|
1436
|
+
quality: 1,
|
|
1437
|
+
what: 'Inline text spacing is not adjustable with a custom stylesheet'
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
},
|
|
1442
|
+
leadingAbsolute: {
|
|
1443
|
+
weight: 2,
|
|
1444
|
+
packages: {
|
|
1445
|
+
alfa: {
|
|
1446
|
+
r80: {
|
|
1447
|
+
quality: 1,
|
|
1448
|
+
what: 'Paragraph text has an absolute line height'
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
},
|
|
1453
|
+
noLeading: {
|
|
1454
|
+
weight: 3,
|
|
1455
|
+
packages: {
|
|
1456
|
+
alfa: {
|
|
1457
|
+
r73: {
|
|
1458
|
+
quality: 1,
|
|
1459
|
+
what: 'Paragraph of text has insufficient line height'
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
},
|
|
1464
|
+
leadingClipsText: {
|
|
1465
|
+
weight: 4,
|
|
1466
|
+
packages: {
|
|
1467
|
+
tenon: {
|
|
1468
|
+
144: {
|
|
1469
|
+
quality: 1,
|
|
1470
|
+
what: 'Line height is insufficent to properly display the computed font size'
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
},
|
|
1475
|
+
overflowHidden: {
|
|
1476
|
+
weight: 4,
|
|
1477
|
+
packages: {
|
|
1478
|
+
alfa: {
|
|
1479
|
+
r83: {
|
|
1480
|
+
quality: 1,
|
|
1481
|
+
what: 'Overflow is hidden or clipped if the text is enlarged'
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
},
|
|
1486
|
+
titleBad: {
|
|
1487
|
+
weight: 4,
|
|
1488
|
+
packages: {
|
|
1489
|
+
nuVal: {
|
|
1490
|
+
'Element “title” not allowed as child of element “body” in this context. (Suppressing further errors from this subtree.)': {
|
|
1491
|
+
quality: 1,
|
|
1492
|
+
what: 'title element is a child of the body element'
|
|
1493
|
+
}
|
|
1494
|
+
},
|
|
1495
|
+
testaro: {
|
|
1496
|
+
titleEl: {
|
|
1497
|
+
quality: 1,
|
|
1498
|
+
what: 'title attribute belongs to an inappropriate element'
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
},
|
|
1503
|
+
linkElementBad: {
|
|
1504
|
+
weight: 4,
|
|
1505
|
+
packages: {
|
|
1506
|
+
nuVal: {
|
|
1507
|
+
'A “link” element must not appear as a descendant of a “body” element unless the “link” element has an “itemprop” attribute or has a “rel” attribute whose value contains “dns-prefetch”, “modulepreload”, “pingback”, “preconnect”, “prefetch”, “preload”, “prerender”, or “stylesheet”.': {
|
|
1508
|
+
quality: 1,
|
|
1509
|
+
what: 'link element with a body ancestor has no itemprop or valid rel attribute'
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
},
|
|
1514
|
+
metaBad: {
|
|
1515
|
+
weight: 3,
|
|
1516
|
+
packages: {
|
|
1517
|
+
nuVal: {
|
|
1518
|
+
'^Attribute .+ not allowed on element “meta” at this point.*$': {
|
|
1519
|
+
quality: 1,
|
|
1520
|
+
what: 'Attribute is not allowed on a meta element here'
|
|
1521
|
+
},
|
|
1522
|
+
'Element “meta” is missing one or more of the following attributes: “charset”, “content”, “http-equiv”, “itemprop”, “name”, “property”.': {
|
|
1523
|
+
quality: 1,
|
|
1524
|
+
what: 'meta element is missing a charset, content, http-equiv, itemprop, name, or property attribute'
|
|
1525
|
+
},
|
|
1526
|
+
'A document must not include more than one “meta” element with its “name” attribute set to the value “description”.': {
|
|
1527
|
+
quality: 1,
|
|
1528
|
+
what: 'meta element with name="description" is not the only one'
|
|
1529
|
+
},
|
|
1530
|
+
'A “meta” element with an “http-equiv” attribute whose value is “X-UA-Compatible” must have a “content” attribute with the value “IE=edge”.': {
|
|
1531
|
+
quality: 1,
|
|
1532
|
+
what: 'meta element with http-equiv="X-UA-Compatible" has no content="IE=edge"'
|
|
1533
|
+
},
|
|
1534
|
+
'Element “meta” is missing one or more of the following attributes: “itemprop”, “property”.': {
|
|
1535
|
+
quality: 1,
|
|
1536
|
+
what: 'meta element is missing an itemprop or property attribute'
|
|
1537
|
+
},
|
|
1538
|
+
'A “charset” attribute on a “meta” element found after the first 1024 bytes.': {
|
|
1539
|
+
quality: 1,
|
|
1540
|
+
what: 'charset attribute on a meta element appears after 1024 bytes'
|
|
1541
|
+
},
|
|
1542
|
+
'^Bad value .+ for attribute .+ on element “meta”.*$': {
|
|
1543
|
+
quality: 1,
|
|
1544
|
+
what: 'attribute of a meta element has an invalid value'
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
},
|
|
1549
|
+
scriptDeferBad: {
|
|
1550
|
+
weight: 4,
|
|
1551
|
+
packages: {
|
|
1552
|
+
nuVal: {
|
|
1553
|
+
'Element “script” must not have attribute “defer” unless attribute “src” is also specified.': {
|
|
1554
|
+
quality: 1,
|
|
1555
|
+
what: 'script element has a defer attribute without a src attribute'
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
},
|
|
1560
|
+
itemTypeBad: {
|
|
1561
|
+
weight: 4,
|
|
1562
|
+
packages: {
|
|
1563
|
+
nuVal: {
|
|
1564
|
+
'The “itemtype” attribute must not be specified on elements that do not have an “itemscope” attribute specified.': {
|
|
1565
|
+
quality: 1,
|
|
1566
|
+
what: 'Element has an itemtype attribute without an itemscope attribute'
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
},
|
|
1571
|
+
iframeTitleBad: {
|
|
1572
|
+
weight: 4,
|
|
1573
|
+
packages: {
|
|
1574
|
+
alfa: {
|
|
1575
|
+
r13: {
|
|
1576
|
+
quality: 1,
|
|
1577
|
+
what: 'iframe has no accessible name'
|
|
1578
|
+
}
|
|
1579
|
+
},
|
|
1580
|
+
axe: {
|
|
1581
|
+
'frame-title': {
|
|
1582
|
+
quality: 1,
|
|
1583
|
+
what: 'Frame has no accessible name'
|
|
1584
|
+
},
|
|
1585
|
+
'frame-title-unique': {
|
|
1586
|
+
quality: 1,
|
|
1587
|
+
what: 'Frame title attribute is not unique'
|
|
1588
|
+
}
|
|
1589
|
+
},
|
|
1590
|
+
continuum: {
|
|
1591
|
+
228: {
|
|
1592
|
+
quality: 1,
|
|
1593
|
+
what: 'iframe has no mechanism that allows an accessible name to be calculated'
|
|
1594
|
+
}
|
|
1595
|
+
},
|
|
1596
|
+
htmlcs: {
|
|
1597
|
+
'e:AA.2_4_1.H64.1': {
|
|
1598
|
+
quality: 1,
|
|
1599
|
+
what: 'iframe element has no non-empty title attribute'
|
|
1600
|
+
}
|
|
1601
|
+
},
|
|
1602
|
+
ibm: {
|
|
1603
|
+
WCAG20_Frame_HasTitle: {
|
|
1604
|
+
quality: 1,
|
|
1605
|
+
what: 'Inline frame has an empty or nonunique title attribute'
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
},
|
|
1610
|
+
roleBad: {
|
|
1611
|
+
weight: 3,
|
|
1612
|
+
packages: {
|
|
1613
|
+
alfa: {
|
|
1614
|
+
r21: {
|
|
1615
|
+
quality: 1,
|
|
1616
|
+
what: 'Element does not have a valid role'
|
|
1617
|
+
}
|
|
1618
|
+
},
|
|
1619
|
+
axe: {
|
|
1620
|
+
'aria-roles': {
|
|
1621
|
+
quality: 1,
|
|
1622
|
+
what: 'ARIA role has an invalid value'
|
|
1623
|
+
},
|
|
1624
|
+
'aria-allowed-role': {
|
|
1625
|
+
quality: 1,
|
|
1626
|
+
what: 'ARIA role is not appropriate for the element'
|
|
1627
|
+
}
|
|
1628
|
+
},
|
|
1629
|
+
continuum: {
|
|
1630
|
+
37: {
|
|
1631
|
+
quality: 1,
|
|
1632
|
+
what: 'a element has a role attribute that is not allowed'
|
|
1633
|
+
},
|
|
1634
|
+
44: {
|
|
1635
|
+
quality: 1,
|
|
1636
|
+
what: 'hr element has a role attribute'
|
|
1637
|
+
},
|
|
1638
|
+
176: {
|
|
1639
|
+
quality: 1,
|
|
1640
|
+
what: 'label element has a role attribute'
|
|
1641
|
+
},
|
|
1642
|
+
319: {
|
|
1643
|
+
quality: 1,
|
|
1644
|
+
what: 'ol element has a role attribute that is not allowed'
|
|
1645
|
+
},
|
|
1646
|
+
325: {
|
|
1647
|
+
quality: 1,
|
|
1648
|
+
what: 'ul element has a role attribute that is not allowed'
|
|
1649
|
+
},
|
|
1650
|
+
412: {
|
|
1651
|
+
quality: 1,
|
|
1652
|
+
what: 'element has a role attribute set to an invalid ARIA role value'
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
ibm: {
|
|
1656
|
+
aria_semantics_role: {
|
|
1657
|
+
quality: 1,
|
|
1658
|
+
what: 'ARIA role is not valid for the element to which it is assigned'
|
|
1659
|
+
},
|
|
1660
|
+
element_tabbable_role_valid: {
|
|
1661
|
+
quality: 1,
|
|
1662
|
+
what: 'Tabbable element has a non-widget role'
|
|
1663
|
+
},
|
|
1664
|
+
Rpt_Aria_ContentinfoWithNoMain_Implicit: {
|
|
1665
|
+
quality: 1,
|
|
1666
|
+
what: 'Element has a contentinfo role when no element has a main role'
|
|
1667
|
+
},
|
|
1668
|
+
Rpt_Aria_ValidRole: {
|
|
1669
|
+
quality: 1,
|
|
1670
|
+
what: 'Element has an invalid role'
|
|
1671
|
+
},
|
|
1672
|
+
Rpt_Aria_EventHandlerMissingRole_Native_Host_Sematics: {
|
|
1673
|
+
quality: 1,
|
|
1674
|
+
what: 'Element has an event handler but no valid ARIA role'
|
|
1675
|
+
},
|
|
1676
|
+
table_aria_descendants: {
|
|
1677
|
+
quality: 1,
|
|
1678
|
+
what: 'Table structure element specifies an explicit role within the table container'
|
|
1679
|
+
}
|
|
1680
|
+
},
|
|
1681
|
+
nuVal: {
|
|
1682
|
+
'Bad value “dialog” for attribute “role” on element “li”.': {
|
|
1683
|
+
quality: 1,
|
|
1684
|
+
what: 'dialog role is not valid for an li element'
|
|
1685
|
+
}
|
|
1686
|
+
},
|
|
1687
|
+
testaro: {
|
|
1688
|
+
role: {
|
|
1689
|
+
quality: 1,
|
|
1690
|
+
what: 'Nonexistent or implicit-overriding role'
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
},
|
|
1695
|
+
roleRedundant: {
|
|
1696
|
+
weight: 1,
|
|
1697
|
+
packages: {
|
|
1698
|
+
ibm: {
|
|
1699
|
+
aria_role_redundant: {
|
|
1700
|
+
quality: 1,
|
|
1701
|
+
what: 'Explicitly assigned ARIA role is redundant with the implicit role of the element'
|
|
1702
|
+
}
|
|
1703
|
+
},
|
|
1704
|
+
nuVal: {
|
|
1705
|
+
'The “banner” role is unnecessary for element “header”.': {
|
|
1706
|
+
quality: 1,
|
|
1707
|
+
what: 'banner role is redundant for a header element'
|
|
1708
|
+
},
|
|
1709
|
+
'The “contentinfo” role is unnecessary for element “footer”.': {
|
|
1710
|
+
quality: 1,
|
|
1711
|
+
what: 'contentinfo role is redundant for a footer element'
|
|
1712
|
+
},
|
|
1713
|
+
'The “main” role is unnecessary for element “main”.': {
|
|
1714
|
+
quality: 1,
|
|
1715
|
+
what: 'main role is redundant for a main element'
|
|
1716
|
+
},
|
|
1717
|
+
'The “navigation” role is unnecessary for element “nav”.': {
|
|
1718
|
+
quality: 1,
|
|
1719
|
+
what: 'navigation role is redundant for a nav element'
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
roleMissingAttribute: {
|
|
1725
|
+
weight: 4,
|
|
1726
|
+
packages: {
|
|
1727
|
+
axe: {
|
|
1728
|
+
'aria-required-attr': {
|
|
1729
|
+
quality: 1,
|
|
1730
|
+
what: 'Required ARIA attribute is not provided'
|
|
1731
|
+
}
|
|
1732
|
+
},
|
|
1733
|
+
ibm: {
|
|
1734
|
+
Rpt_Aria_RequiredProperties: {
|
|
1735
|
+
quality: 1,
|
|
1736
|
+
what: 'ARIA role on an element does not have a required attribute'
|
|
1737
|
+
}
|
|
1738
|
+
},
|
|
1739
|
+
nuVal: {
|
|
1740
|
+
'Element “a” is missing required attribute “aria-valuenow”.': {
|
|
1741
|
+
quality: 1,
|
|
1742
|
+
what: 'a element has no aria-valuenow attribute'
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
},
|
|
1747
|
+
ariaMissing: {
|
|
1748
|
+
weight: 4,
|
|
1749
|
+
packages: {
|
|
1750
|
+
alfa: {
|
|
1751
|
+
r16: {
|
|
1752
|
+
quality: 1,
|
|
1753
|
+
what: 'Element does not have all required states and properties'
|
|
1754
|
+
}
|
|
1755
|
+
},
|
|
1756
|
+
continuum: {
|
|
1757
|
+
1040: {
|
|
1758
|
+
quality: 1,
|
|
1759
|
+
what: 'element with a combobox role has no aria-controls or no aria-expanded attribute'
|
|
1760
|
+
},
|
|
1761
|
+
1042: {
|
|
1762
|
+
quality: 1,
|
|
1763
|
+
what: 'element with an option role has no aria-selected attribute'
|
|
1764
|
+
},
|
|
1765
|
+
1043: {
|
|
1766
|
+
quality: 1,
|
|
1767
|
+
what: 'element with a radio role has no aria-checked attribute'
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
wave: {
|
|
1771
|
+
'e:aria_reference_broken': {
|
|
1772
|
+
quality: 1,
|
|
1773
|
+
what: 'Broken ARIA reference'
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
},
|
|
1778
|
+
ariaBadAttribute: {
|
|
1779
|
+
weight: 4,
|
|
1780
|
+
packages: {
|
|
1781
|
+
alfa: {
|
|
1782
|
+
r18: {
|
|
1783
|
+
quality: 1,
|
|
1784
|
+
what: 'ARIA state or property is not allowed for the element on which it is specified'
|
|
1785
|
+
},
|
|
1786
|
+
r19: {
|
|
1787
|
+
quality: 1,
|
|
1788
|
+
what: 'ARIA state or property has an invalid value'
|
|
1789
|
+
},
|
|
1790
|
+
r20: {
|
|
1791
|
+
quality: 1,
|
|
1792
|
+
what: 'ARIA attribute is not defined'
|
|
1793
|
+
}
|
|
1794
|
+
},
|
|
1795
|
+
axe: {
|
|
1796
|
+
'aria-valid-attr': {
|
|
1797
|
+
quality: 1,
|
|
1798
|
+
what: 'ARIA attribute has an invalid name'
|
|
1799
|
+
},
|
|
1800
|
+
'aria-valid-attr-value': {
|
|
1801
|
+
quality: 1,
|
|
1802
|
+
what: 'ARIA attribute has an invalid value'
|
|
1803
|
+
},
|
|
1804
|
+
'aria-allowed-attr': {
|
|
1805
|
+
quality: 1,
|
|
1806
|
+
what: 'ARIA attribute is invalid for the role of its element'
|
|
1807
|
+
},
|
|
1808
|
+
'aria-roledescription': {
|
|
1809
|
+
quality: 1,
|
|
1810
|
+
what: 'aria-roledescription is on an element with no semantic role'
|
|
1811
|
+
}
|
|
1812
|
+
},
|
|
1813
|
+
continuum: {
|
|
1814
|
+
16: {
|
|
1815
|
+
quality: 1,
|
|
1816
|
+
what: 'Element has an aria-multiline attribute, which is not allowed'
|
|
1817
|
+
},
|
|
1818
|
+
38: {
|
|
1819
|
+
quality: 1,
|
|
1820
|
+
what: 'Element has an aria-pressed attribute, which is not allowed'
|
|
1821
|
+
},
|
|
1822
|
+
64: {
|
|
1823
|
+
quality: 1,
|
|
1824
|
+
what: 'Element has an aria-valuemax attribute that is not set to an integer'
|
|
1825
|
+
},
|
|
1826
|
+
257: {
|
|
1827
|
+
quality: 1,
|
|
1828
|
+
what: 'Element has an aria-checked attribute, which is not allowed'
|
|
1829
|
+
},
|
|
1830
|
+
260: {
|
|
1831
|
+
quality: 1,
|
|
1832
|
+
what: 'Element has an aria-level attribute, which is not allowed'
|
|
1833
|
+
},
|
|
1834
|
+
264: {
|
|
1835
|
+
quality: 1,
|
|
1836
|
+
what: 'Element has an aria-selected attribute, which is not allowed'
|
|
1837
|
+
},
|
|
1838
|
+
270: {
|
|
1839
|
+
quality: 1,
|
|
1840
|
+
what: 'Element has an aria-required attribute, which is not allowed'
|
|
1841
|
+
},
|
|
1842
|
+
281: {
|
|
1843
|
+
quality: 1,
|
|
1844
|
+
what: 'Element has an aria-expanded attribute, which is not allowed'
|
|
1845
|
+
},
|
|
1846
|
+
282: {
|
|
1847
|
+
quality: 1,
|
|
1848
|
+
what: 'Element has an aria-autocomplete attribute, which is not allowed'
|
|
1849
|
+
},
|
|
1850
|
+
283: {
|
|
1851
|
+
quality: 1,
|
|
1852
|
+
what: 'Element has an aria-activedescendant attribute, which is not allowed'
|
|
1853
|
+
},
|
|
1854
|
+
331: {
|
|
1855
|
+
quality: 1,
|
|
1856
|
+
what: 'Element has an aria-owns attribute set to a non-null value'
|
|
1857
|
+
},
|
|
1858
|
+
333: {
|
|
1859
|
+
quality: 1,
|
|
1860
|
+
what: 'Element with a textbox role has an aria-owns attribute, which is not allowed'
|
|
1861
|
+
},
|
|
1862
|
+
1066: {
|
|
1863
|
+
quality: 1,
|
|
1864
|
+
what: 'Element has an ARIA attribute which is not valid'
|
|
1865
|
+
}
|
|
1866
|
+
},
|
|
1867
|
+
ibm: {
|
|
1868
|
+
aria_semantics_attribute: {
|
|
1869
|
+
quality: 1,
|
|
1870
|
+
what: 'ARIA attributes is invalid for the element or ARIA role to which it is assigned'
|
|
1871
|
+
},
|
|
1872
|
+
Rpt_Aria_ValidProperty: {
|
|
1873
|
+
quality: 1,
|
|
1874
|
+
what: 'ARIA attribute is invalid for the role'
|
|
1875
|
+
},
|
|
1876
|
+
Rpt_Aria_ValidPropertyValue: {
|
|
1877
|
+
quality: 1,
|
|
1878
|
+
what: 'ARIA property value is invalid'
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1881
|
+
nuVal: {
|
|
1882
|
+
'The “aria-hidden” attribute must not be specified on the “noscript” element.': {
|
|
1883
|
+
quality: 1,
|
|
1884
|
+
what: 'noscript element has an aria-hidden attribute'
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
ariaRedundant: {
|
|
1890
|
+
weight: 1,
|
|
1891
|
+
packages: {
|
|
1892
|
+
ibm: {
|
|
1893
|
+
aria_attribute_redundant: {
|
|
1894
|
+
quality: 1,
|
|
1895
|
+
what: 'ARIA attribute is used when there is a corresponding HTML attribute'
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
},
|
|
1900
|
+
ariaReferenceBad: {
|
|
1901
|
+
weight: 4,
|
|
1902
|
+
packages: {
|
|
1903
|
+
ibm: {
|
|
1904
|
+
Rpt_Aria_ValidIdRef: {
|
|
1905
|
+
quality: 1,
|
|
1906
|
+
what: 'ARIA property does not reference the non-empty unique id of a visible element'
|
|
1907
|
+
}
|
|
1908
|
+
},
|
|
1909
|
+
wave: {
|
|
1910
|
+
'e:aria_reference_broken': {
|
|
1911
|
+
quality: 1,
|
|
1912
|
+
what: 'Broken ARIA reference'
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
},
|
|
1917
|
+
autocompleteBad: {
|
|
1918
|
+
weight: 3,
|
|
1919
|
+
packages: {
|
|
1920
|
+
alfa: {
|
|
1921
|
+
r10: {
|
|
1922
|
+
quality: 1,
|
|
1923
|
+
what: 'Autocomplete attribute has no valid value'
|
|
1924
|
+
}
|
|
1925
|
+
},
|
|
1926
|
+
axe: {
|
|
1927
|
+
'autocomplete-valid': {
|
|
1928
|
+
quality: 1,
|
|
1929
|
+
what: 'Autocomplete attribute is used incorrectly'
|
|
1930
|
+
}
|
|
1931
|
+
},
|
|
1932
|
+
htmlcs: {
|
|
1933
|
+
'e:AA.1_3_5.H98': {
|
|
1934
|
+
quality: 1,
|
|
1935
|
+
what: 'Autocomplete attribute and the input type are mismatched'
|
|
1936
|
+
}
|
|
1937
|
+
},
|
|
1938
|
+
ibm: {
|
|
1939
|
+
WCAG21_Input_Autocomplete: {
|
|
1940
|
+
quality: 1,
|
|
1941
|
+
what: 'Autocomplete attribute token is not appropriate for the input form field'
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
},
|
|
1946
|
+
autocompleteRisk: {
|
|
1947
|
+
weight: 1,
|
|
1948
|
+
packages: {
|
|
1949
|
+
htmlcs: {
|
|
1950
|
+
'w:AA.1_3_5.H98': {
|
|
1951
|
+
quality: 1,
|
|
1952
|
+
what: 'Element contains a potentially faulty value in its autocomplete attribute'
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
},
|
|
1957
|
+
contrastAA: {
|
|
1958
|
+
weight: 3,
|
|
1959
|
+
packages: {
|
|
1960
|
+
alfa: {
|
|
1961
|
+
r69: {
|
|
1962
|
+
quality: 1,
|
|
1963
|
+
what: 'Text outside widget has subminimum contrast'
|
|
1964
|
+
}
|
|
1965
|
+
},
|
|
1966
|
+
axe: {
|
|
1967
|
+
'color-contrast': {
|
|
1968
|
+
quality: 1,
|
|
1969
|
+
what: 'Element has insufficient color contrast'
|
|
1970
|
+
}
|
|
1971
|
+
},
|
|
1972
|
+
htmlcs: {
|
|
1973
|
+
'e:AA.1_4_3.G145.Fail': {
|
|
1974
|
+
quality: 1,
|
|
1975
|
+
what: 'Contrast between the text and its background is less than 3:1.'
|
|
1976
|
+
},
|
|
1977
|
+
'e:AA.1_4_3.G18.Fail': {
|
|
1978
|
+
quality: 1,
|
|
1979
|
+
what: 'Contrast between the text and its background is less than 4.5:1'
|
|
1980
|
+
}
|
|
1981
|
+
},
|
|
1982
|
+
ibm: {
|
|
1983
|
+
IBMA_Color_Contrast_WCAG2AA: {
|
|
1984
|
+
quality: 1,
|
|
1985
|
+
what: 'Contrast ratio of text with background does not meet WCAG 2.1 AA'
|
|
1986
|
+
}
|
|
1987
|
+
},
|
|
1988
|
+
wave: {
|
|
1989
|
+
'c:contrast': {
|
|
1990
|
+
quality: 1,
|
|
1991
|
+
what: 'Very low contrast'
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
},
|
|
1996
|
+
contrastAAA: {
|
|
1997
|
+
weight: 1,
|
|
1998
|
+
packages: {
|
|
1999
|
+
alfa: {
|
|
2000
|
+
r66: {
|
|
2001
|
+
quality: 1,
|
|
2002
|
+
what: 'Text contrast less than AAA requires'
|
|
2003
|
+
}
|
|
2004
|
+
},
|
|
2005
|
+
axe: {
|
|
2006
|
+
'color-contrast-enhanced': {
|
|
2007
|
+
quality: 1,
|
|
2008
|
+
what: 'Element has insufficient color contrast (Level AAA)'
|
|
2009
|
+
}
|
|
2010
|
+
},
|
|
2011
|
+
htmlcs: {
|
|
2012
|
+
'e:WCAG2AAA.Principle1.Guideline1_4.1_4_3.G18': {
|
|
2013
|
+
quality: 1,
|
|
2014
|
+
what: 'Insufficient contrast'
|
|
2015
|
+
}
|
|
2016
|
+
},
|
|
2017
|
+
tenon: {
|
|
2018
|
+
95: {
|
|
2019
|
+
quality: 1,
|
|
2020
|
+
what: 'Element has insufficient color contrast (Level AAA)'
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
},
|
|
2025
|
+
contrastRisk: {
|
|
2026
|
+
weight: 1,
|
|
2027
|
+
packages: {
|
|
2028
|
+
htmlcs: {
|
|
2029
|
+
'w:AA.1_4_3_F24.F24.BGColour': {
|
|
2030
|
+
quality: 1,
|
|
2031
|
+
what: 'Inline background color may lack a complementary foreground color'
|
|
2032
|
+
},
|
|
2033
|
+
'w:AA.1_4_3_F24.F24.FGColour': {
|
|
2034
|
+
quality: 1,
|
|
2035
|
+
what: 'Inline foreground color may lack a complementary background color'
|
|
2036
|
+
},
|
|
2037
|
+
'w:AA.1_4_3.G18.Abs': {
|
|
2038
|
+
quality: 1,
|
|
2039
|
+
what: 'Contrast between the absolutely positioned text and its background may be inadequate'
|
|
2040
|
+
},
|
|
2041
|
+
'w:AA.1_4_3.G18.Alpha': {
|
|
2042
|
+
quality: 1,
|
|
2043
|
+
what: 'Contrast between the text and its background may be less than 4.5:1, given the transparency'
|
|
2044
|
+
},
|
|
2045
|
+
'w:AA.1_4_3.G145.Abs': {
|
|
2046
|
+
quality: 1,
|
|
2047
|
+
what: 'Contrast between the absolutely positioned large text and its background may be less than 3:1'
|
|
2048
|
+
},
|
|
2049
|
+
'w:AA.1_4_3.G145.Alpha': {
|
|
2050
|
+
quality: 1,
|
|
2051
|
+
what: 'Contrast between the text and its background may be less than 3:1, given the transparency'
|
|
2052
|
+
},
|
|
2053
|
+
'w:AA.1_4_3.G145.BgImage': {
|
|
2054
|
+
quality: 1,
|
|
2055
|
+
what: 'Contrast between the text and its background image may be less than 3:1'
|
|
2056
|
+
},
|
|
2057
|
+
'w:AA.1_4_3.G18.BgImage': {
|
|
2058
|
+
quality: 1,
|
|
2059
|
+
what: 'Contrast between the text and its background image may be less than 4.5:1'
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
},
|
|
2064
|
+
idEmpty: {
|
|
2065
|
+
weight: 4,
|
|
2066
|
+
packages: {
|
|
2067
|
+
nuVal: {
|
|
2068
|
+
'Bad value “” for attribute “id” on element “a”: An ID must not be the empty string.': {
|
|
2069
|
+
quality: 1,
|
|
2070
|
+
what: 'id attribute has an empty value'
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
},
|
|
2075
|
+
headingEmpty: {
|
|
2076
|
+
weight: 3,
|
|
2077
|
+
packages: {
|
|
2078
|
+
alfa: {
|
|
2079
|
+
r64: {
|
|
2080
|
+
quality: 1,
|
|
2081
|
+
what: 'Heading has no non-empty accessible name'
|
|
2082
|
+
}
|
|
2083
|
+
},
|
|
2084
|
+
axe: {
|
|
2085
|
+
'empty-heading': {
|
|
2086
|
+
quality: 1,
|
|
2087
|
+
what: 'Heading empty'
|
|
2088
|
+
}
|
|
2089
|
+
},
|
|
2090
|
+
htmlcs: {
|
|
2091
|
+
'e:AA.1_3_1.H42.2': {
|
|
2092
|
+
quality: 1,
|
|
2093
|
+
what: 'Heading empty'
|
|
2094
|
+
}
|
|
2095
|
+
},
|
|
2096
|
+
ibm: {
|
|
2097
|
+
RPT_Header_HasContent: {
|
|
2098
|
+
quality: 1,
|
|
2099
|
+
what: 'Heading element provides no descriptive text'
|
|
2100
|
+
}
|
|
2101
|
+
},
|
|
2102
|
+
nuVal: {
|
|
2103
|
+
'Empty heading.': {
|
|
2104
|
+
quality: 1,
|
|
2105
|
+
what: 'Empty heading'
|
|
2106
|
+
}
|
|
2107
|
+
},
|
|
2108
|
+
wave: {
|
|
2109
|
+
'e:heading_empty': {
|
|
2110
|
+
quality: 1,
|
|
2111
|
+
what: 'Empty heading'
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
},
|
|
2116
|
+
headingOfNothing: {
|
|
2117
|
+
weight: 2,
|
|
2118
|
+
packages: {
|
|
2119
|
+
alfa: {
|
|
2120
|
+
r78: {
|
|
2121
|
+
quality: 1,
|
|
2122
|
+
what: 'No content between two headings of the same level'
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
},
|
|
2127
|
+
typeRedundant: {
|
|
2128
|
+
weight: 1,
|
|
2129
|
+
packages: {
|
|
2130
|
+
nuVal: {
|
|
2131
|
+
'The “type” attribute is unnecessary for JavaScript resources.': {
|
|
2132
|
+
quality: 1,
|
|
2133
|
+
what: 'type attribute is unnecessary for a JavaScript resource'
|
|
2134
|
+
},
|
|
2135
|
+
'The “type” attribute for the “style” element is not needed and should be omitted.': {
|
|
2136
|
+
quality: 1,
|
|
2137
|
+
what: 'type attribute is unnecessary for a style element'
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
},
|
|
2142
|
+
imageTextRedundant: {
|
|
2143
|
+
weight: 1,
|
|
2144
|
+
packages: {
|
|
2145
|
+
axe: {
|
|
2146
|
+
'image-redundant-alt': {
|
|
2147
|
+
quality: 1,
|
|
2148
|
+
what: 'Text of a button or link is repeated in the image alternative'
|
|
2149
|
+
}
|
|
2150
|
+
},
|
|
2151
|
+
ibm: {
|
|
2152
|
+
WCAG20_Img_LinkTextNotRedundant: {
|
|
2153
|
+
quality: 1,
|
|
2154
|
+
what: 'Text alternative for the image in a link repeats text of the same or an adjacent link'
|
|
2155
|
+
}
|
|
2156
|
+
},
|
|
2157
|
+
tenon: {
|
|
2158
|
+
138: {
|
|
2159
|
+
quality: 1,
|
|
2160
|
+
what: 'Image link alternative text repeats text in the link'
|
|
2161
|
+
}
|
|
2162
|
+
},
|
|
2163
|
+
wave: {
|
|
2164
|
+
'a:alt_redundant': {
|
|
2165
|
+
quality: 1,
|
|
2166
|
+
what: 'Redundant text alternative'
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
},
|
|
2171
|
+
decorativeTitle: {
|
|
2172
|
+
weight: 1,
|
|
2173
|
+
packages: {
|
|
2174
|
+
htmlcs: {
|
|
2175
|
+
'e:AA.1_1_1.H67.1': {
|
|
2176
|
+
quality: 1,
|
|
2177
|
+
what: 'img element has an empty alt attribute but has a nonempty title attribute'
|
|
2178
|
+
}
|
|
2179
|
+
},
|
|
2180
|
+
ibm: {
|
|
2181
|
+
WCAG20_Img_TitleEmptyWhenAltNull: {
|
|
2182
|
+
quality: 1,
|
|
2183
|
+
what: 'Image alt attribute is empty, but its title attribute is not'
|
|
2184
|
+
}
|
|
2185
|
+
},
|
|
2186
|
+
wave: {
|
|
2187
|
+
'a:image_title': {
|
|
2188
|
+
quality: 1,
|
|
2189
|
+
what: 'Image has a title attribute value but no alt value'
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
},
|
|
2194
|
+
titleRedundant: {
|
|
2195
|
+
weight: 1,
|
|
2196
|
+
packages: {
|
|
2197
|
+
tenon: {
|
|
2198
|
+
79: {
|
|
2199
|
+
quality: 1,
|
|
2200
|
+
what: 'Link has a title attribute that is the same as the text inside the link'
|
|
2201
|
+
}
|
|
2202
|
+
},
|
|
2203
|
+
wave: {
|
|
2204
|
+
'a:title_redundant': {
|
|
2205
|
+
quality: 1,
|
|
2206
|
+
what: 'Title attribute text is the same as text or alternative text'
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
},
|
|
2211
|
+
titleEmpty: {
|
|
2212
|
+
weight: 1,
|
|
2213
|
+
packages: {
|
|
2214
|
+
htmlcs: {
|
|
2215
|
+
'w:AA.1_3_1.H65': {
|
|
2216
|
+
quality: 0.5,
|
|
2217
|
+
what: 'Value of the title attribute of the form control is empty or only whitespace'
|
|
2218
|
+
},
|
|
2219
|
+
'w:AA.4_1_2.H65': {
|
|
2220
|
+
quality: 0.5,
|
|
2221
|
+
what: 'Value of the title attribute of the form control is empty or only whitespace'
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
},
|
|
2226
|
+
docType: {
|
|
2227
|
+
weight: 1,
|
|
2228
|
+
packages: {
|
|
2229
|
+
testaro: {
|
|
2230
|
+
docType: {
|
|
2231
|
+
quality: 1,
|
|
2232
|
+
what: 'document has no doctype property'
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
2237
|
+
pageTitle: {
|
|
2238
|
+
weight: 3,
|
|
2239
|
+
packages: {
|
|
2240
|
+
alfa: {
|
|
2241
|
+
r1: {
|
|
2242
|
+
quality: 1,
|
|
2243
|
+
what: 'Document has no valid title element'
|
|
2244
|
+
}
|
|
2245
|
+
},
|
|
2246
|
+
axe: {
|
|
2247
|
+
'document-title': {
|
|
2248
|
+
quality: 1,
|
|
2249
|
+
what: 'Document contains no title element'
|
|
2250
|
+
}
|
|
2251
|
+
},
|
|
2252
|
+
continuum: {
|
|
2253
|
+
884: {
|
|
2254
|
+
quality: 1,
|
|
2255
|
+
what: 'DOM contains no document title element'
|
|
2256
|
+
}
|
|
2257
|
+
},
|
|
2258
|
+
htmlcs: {
|
|
2259
|
+
'e:AA.2_4_2.H25.1.NoTitleEl': {
|
|
2260
|
+
quality: 1,
|
|
2261
|
+
what: 'Document head element contains no non-empty title element'
|
|
2262
|
+
}
|
|
2263
|
+
},
|
|
2264
|
+
ibm: {
|
|
2265
|
+
WCAG20_Doc_HasTitle: {
|
|
2266
|
+
quality: 1,
|
|
2267
|
+
what: 'Page has no subject-identifying title'
|
|
2268
|
+
}
|
|
2269
|
+
},
|
|
2270
|
+
nuVal: {
|
|
2271
|
+
'Element “head” is missing a required instance of child element “title”.': {
|
|
2272
|
+
quality: 1,
|
|
2273
|
+
what: 'head element has no child title element'
|
|
2274
|
+
}
|
|
2275
|
+
},
|
|
2276
|
+
wave: {
|
|
2277
|
+
'e:title_invalid': {
|
|
2278
|
+
quality: 1,
|
|
2279
|
+
what: 'Missing or uninformative page title'
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
},
|
|
2284
|
+
headingStructure: {
|
|
2285
|
+
weight: 2,
|
|
2286
|
+
packages: {
|
|
2287
|
+
alfa: {
|
|
2288
|
+
r53: {
|
|
2289
|
+
quality: 1,
|
|
2290
|
+
what: 'Heading skips one or more levels'
|
|
2291
|
+
}
|
|
2292
|
+
},
|
|
2293
|
+
axe: {
|
|
2294
|
+
'heading-order': {
|
|
2295
|
+
quality: 1,
|
|
2296
|
+
what: 'Heading levels do not increase by only one'
|
|
2297
|
+
}
|
|
2298
|
+
},
|
|
2299
|
+
htmlcs: {
|
|
2300
|
+
'w:AA.1_3_1_A.G141': {
|
|
2301
|
+
quality: 1,
|
|
2302
|
+
what: 'Heading level is incorrect'
|
|
2303
|
+
}
|
|
2304
|
+
},
|
|
2305
|
+
nuVal: {
|
|
2306
|
+
'Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).': {
|
|
2307
|
+
quality: 1,
|
|
2308
|
+
what: 'Page contains more than 1 h1 element'
|
|
2309
|
+
}
|
|
2310
|
+
},
|
|
2311
|
+
tenon: {
|
|
2312
|
+
155: {
|
|
2313
|
+
quality: 1,
|
|
2314
|
+
what: 'Headings are not structured in a hierarchical manner'
|
|
2315
|
+
}
|
|
2316
|
+
},
|
|
2317
|
+
wave: {
|
|
2318
|
+
'a:heading_skipped': {
|
|
2319
|
+
quality: 1,
|
|
2320
|
+
what: 'Skipped heading level'
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
},
|
|
2325
|
+
headingLevelless: {
|
|
2326
|
+
weight: 1,
|
|
2327
|
+
packages: {
|
|
2328
|
+
continuum: {
|
|
2329
|
+
71: {
|
|
2330
|
+
quality: 1,
|
|
2331
|
+
what: 'element with a heading role has no aria-level attribute'
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
},
|
|
2336
|
+
noHeading: {
|
|
2337
|
+
weight: 3,
|
|
2338
|
+
packages: {
|
|
2339
|
+
alfa: {
|
|
2340
|
+
r59: {
|
|
2341
|
+
quality: 1,
|
|
2342
|
+
what: 'Document has no headings'
|
|
2343
|
+
}
|
|
2344
|
+
},
|
|
2345
|
+
wave: {
|
|
2346
|
+
'a:heading_missing': {
|
|
2347
|
+
quality: 1,
|
|
2348
|
+
what: 'Page has no headings'
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
},
|
|
2353
|
+
h1Missing: {
|
|
2354
|
+
weight: 2,
|
|
2355
|
+
packages: {
|
|
2356
|
+
alfa: {
|
|
2357
|
+
r61: {
|
|
2358
|
+
quality: 1,
|
|
2359
|
+
what: 'First heading is not h1'
|
|
2360
|
+
}
|
|
2361
|
+
},
|
|
2362
|
+
axe: {
|
|
2363
|
+
'page-has-heading-one': {
|
|
2364
|
+
quality: 1,
|
|
2365
|
+
what: 'Page contains no level-one heading'
|
|
2366
|
+
}
|
|
2367
|
+
},
|
|
2368
|
+
wave: {
|
|
2369
|
+
'a:h1_missing': {
|
|
2370
|
+
quality: 1,
|
|
2371
|
+
what: 'Missing first level heading'
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
},
|
|
2376
|
+
sectionHeadingless: {
|
|
2377
|
+
weight: 1,
|
|
2378
|
+
packages: {
|
|
2379
|
+
nuVal: {
|
|
2380
|
+
'Section lacks heading. Consider using “h2”-“h6” elements to add identifying headings to all sections.': {
|
|
2381
|
+
quality: 1,
|
|
2382
|
+
what: 'section has no heading'
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
},
|
|
2387
|
+
justification: {
|
|
2388
|
+
weight: 1,
|
|
2389
|
+
packages: {
|
|
2390
|
+
alfa: {
|
|
2391
|
+
r71: {
|
|
2392
|
+
quality: 1,
|
|
2393
|
+
what: 'Paragraph text is fully justified'
|
|
2394
|
+
}
|
|
2395
|
+
},
|
|
2396
|
+
tenon: {
|
|
2397
|
+
36: {
|
|
2398
|
+
quality: 1,
|
|
2399
|
+
what: 'Text is fully justified'
|
|
2400
|
+
}
|
|
2401
|
+
},
|
|
2402
|
+
wave: {
|
|
2403
|
+
'a:text_justified': {
|
|
2404
|
+
quality: 1,
|
|
2405
|
+
what: 'Text is justified'
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
},
|
|
2410
|
+
nonSemanticText: {
|
|
2411
|
+
weight: 2,
|
|
2412
|
+
packages: {
|
|
2413
|
+
htmlcs: {
|
|
2414
|
+
'w:AA.1_3_1.H49.AlignAttr': {
|
|
2415
|
+
quality: 1,
|
|
2416
|
+
what: 'Special text is aligned nonsemantically'
|
|
2417
|
+
},
|
|
2418
|
+
'w:AA.1_3_1.H49.B': {
|
|
2419
|
+
quality: 1,
|
|
2420
|
+
what: 'Special text is bolded nonsemantically'
|
|
2421
|
+
},
|
|
2422
|
+
'w:AA.1_3_1.H49.I': {
|
|
2423
|
+
quality: 1,
|
|
2424
|
+
what: 'Special text is italicized nonsemantically'
|
|
2425
|
+
},
|
|
2426
|
+
'w:AA.1_3_1.H49.Big': {
|
|
2427
|
+
quality: 1,
|
|
2428
|
+
what: 'Special text is enlarged nonsemantically'
|
|
2429
|
+
},
|
|
2430
|
+
'w:AA.1_3_1.H49.Small': {
|
|
2431
|
+
quality: 1,
|
|
2432
|
+
what: 'Special text is made small nonsemantically'
|
|
2433
|
+
},
|
|
2434
|
+
'w:AA.1_3_1.H49.U': {
|
|
2435
|
+
quality: 1,
|
|
2436
|
+
what: 'Special text is underlined nonsemantically'
|
|
2437
|
+
},
|
|
2438
|
+
'w:AA.1_3_1.H49.Center': {
|
|
2439
|
+
quality: 1,
|
|
2440
|
+
what: 'Special text is centered nonsemantically'
|
|
2441
|
+
},
|
|
2442
|
+
'w:AA.1_3_1.H49.Font': {
|
|
2443
|
+
quality: 1,
|
|
2444
|
+
what: 'Special text is designated nonsemantically with a (deprecated) font element'
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
},
|
|
2449
|
+
pseudoParagraphRisk: {
|
|
2450
|
+
weight: 1,
|
|
2451
|
+
packages: {
|
|
2452
|
+
tenon: {
|
|
2453
|
+
242: {
|
|
2454
|
+
quality: 1,
|
|
2455
|
+
what: 'Multiple consecutive br elements may simulate paragraphs'
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
},
|
|
2460
|
+
pseudoHeadingRisk: {
|
|
2461
|
+
weight: 1,
|
|
2462
|
+
packages: {
|
|
2463
|
+
axe: {
|
|
2464
|
+
'p-as-heading': {
|
|
2465
|
+
quality: 1,
|
|
2466
|
+
what: 'Styled p element may be misused as a heading'
|
|
2467
|
+
}
|
|
2468
|
+
},
|
|
2469
|
+
htmlcs: {
|
|
2470
|
+
'w:AA.1_3_1.H42': {
|
|
2471
|
+
quality: 1,
|
|
2472
|
+
what: 'Heading coding is not used but the element may be intended as a heading'
|
|
2473
|
+
}
|
|
2474
|
+
},
|
|
2475
|
+
wave: {
|
|
2476
|
+
'a:heading_possible': {
|
|
2477
|
+
quality: 1,
|
|
2478
|
+
what: 'Possible heading'
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
},
|
|
2483
|
+
pseudoLinkRisk: {
|
|
2484
|
+
weight: 1,
|
|
2485
|
+
packages: {
|
|
2486
|
+
tenon: {
|
|
2487
|
+
129: {
|
|
2488
|
+
quality: 1,
|
|
2489
|
+
what: 'CSS underline on text that is not a link'
|
|
2490
|
+
}
|
|
2491
|
+
},
|
|
2492
|
+
wave: {
|
|
2493
|
+
'a:underline': {
|
|
2494
|
+
quality: 1,
|
|
2495
|
+
what: 'CSS underline on text that is not a link'
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
},
|
|
2500
|
+
listChild: {
|
|
2501
|
+
weight: 4,
|
|
2502
|
+
packages: {
|
|
2503
|
+
axe: {
|
|
2504
|
+
list: {
|
|
2505
|
+
quality: 1,
|
|
2506
|
+
what: 'List element ul or ol has a child element other than li, script, and template'
|
|
2507
|
+
},
|
|
2508
|
+
'definition-list': {
|
|
2509
|
+
quality: 1,
|
|
2510
|
+
what: 'List element dl has a child element other than properly ordered dt and dt group, script, template, and div'
|
|
2511
|
+
}
|
|
2512
|
+
},
|
|
2513
|
+
continuum: {
|
|
2514
|
+
246: {
|
|
2515
|
+
quality: 1,
|
|
2516
|
+
what: 'ul element does not contain only li, script, template, or listitem-role elements as direct child elements'
|
|
2517
|
+
}
|
|
2518
|
+
},
|
|
2519
|
+
ibm: {
|
|
2520
|
+
HAAC_List_Group_ListItem: {
|
|
2521
|
+
quality: 1,
|
|
2522
|
+
what: 'List component with a group role has a non-listitem child'
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
},
|
|
2527
|
+
listItemOrphan: {
|
|
2528
|
+
weight: 4,
|
|
2529
|
+
packages: {
|
|
2530
|
+
axe: {
|
|
2531
|
+
listitem: {
|
|
2532
|
+
quality: 1,
|
|
2533
|
+
what: 'li element is not contained by a ul or ol element'
|
|
2534
|
+
}
|
|
2535
|
+
},
|
|
2536
|
+
continuum: {
|
|
2537
|
+
99: {
|
|
2538
|
+
quality: 1,
|
|
2539
|
+
what: 'li element has no ul, ol, or list-role parent'
|
|
2540
|
+
},
|
|
2541
|
+
385: {
|
|
2542
|
+
quality: 1,
|
|
2543
|
+
what: 'list item has no ul, ol, or list-role parent or owner'
|
|
2544
|
+
}
|
|
2545
|
+
},
|
|
2546
|
+
nuVal: {
|
|
2547
|
+
'Element “li” not allowed as child of element “div” in this context. (Suppressing further errors from this subtree.)': {
|
|
2548
|
+
quality: 1,
|
|
2549
|
+
what: 'li element is a child of a div element'
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
},
|
|
2554
|
+
pseudoOrderedListRisk: {
|
|
2555
|
+
weight: 1,
|
|
2556
|
+
packages: {
|
|
2557
|
+
htmlcs: {
|
|
2558
|
+
'w:AA.1_3_1.H48.2': {
|
|
2559
|
+
quality: 1,
|
|
2560
|
+
what: 'Ordered list may fail to be coded as such'
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
},
|
|
2565
|
+
pseudoNavListRisk: {
|
|
2566
|
+
weight: 1,
|
|
2567
|
+
packages: {
|
|
2568
|
+
htmlcs: {
|
|
2569
|
+
'w:AA.1_3_1.H48': {
|
|
2570
|
+
quality: 1,
|
|
2571
|
+
what: 'Navigation links are not coded as a list'
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
},
|
|
2576
|
+
selectNoText: {
|
|
2577
|
+
weight: 3,
|
|
2578
|
+
packages: {
|
|
2579
|
+
axe: {
|
|
2580
|
+
'select-name': {
|
|
2581
|
+
quality: 1,
|
|
2582
|
+
what: 'select element has no accessible name'
|
|
2583
|
+
}
|
|
2584
|
+
},
|
|
2585
|
+
continuum: {
|
|
2586
|
+
114: {
|
|
2587
|
+
quality: 1,
|
|
2588
|
+
what: 'select element has no mechanism that allows an accessible name to be calculated'
|
|
2589
|
+
}
|
|
2590
|
+
},
|
|
2591
|
+
htmlcs: {
|
|
2592
|
+
'e:AA.4_1_2.H91.Select.Name': {
|
|
2593
|
+
quality: 1,
|
|
2594
|
+
what: 'Select element has no accessible name'
|
|
2595
|
+
},
|
|
2596
|
+
'w:AA.4_1_2.H91.Select.Value': {
|
|
2597
|
+
quality: 1,
|
|
2598
|
+
what: 'Select element value has no accessible name'
|
|
2599
|
+
}
|
|
2600
|
+
},
|
|
2601
|
+
wave: {
|
|
2602
|
+
'a:select_missing_label': {
|
|
2603
|
+
quality: 1,
|
|
2604
|
+
what: 'Select element has no label'
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
},
|
|
2609
|
+
selectFlatRisk: {
|
|
2610
|
+
weight: 1,
|
|
2611
|
+
packages: {
|
|
2612
|
+
htmlcs: {
|
|
2613
|
+
'w:AA.1_3_1.H85.2': {
|
|
2614
|
+
quality: 1,
|
|
2615
|
+
what: 'Selection list may contain groups of related options that are not grouped with optgroup'
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
},
|
|
2620
|
+
accessKeyDuplicate: {
|
|
2621
|
+
weight: 3,
|
|
2622
|
+
packages: {
|
|
2623
|
+
axe: {
|
|
2624
|
+
accesskeys: {
|
|
2625
|
+
quality: 1,
|
|
2626
|
+
what: 'accesskey attribute value is not unique'
|
|
2627
|
+
}
|
|
2628
|
+
},
|
|
2629
|
+
ibm: {
|
|
2630
|
+
WCAG20_Elem_UniqueAccessKey: {
|
|
2631
|
+
quality: 1,
|
|
2632
|
+
what: 'Accesskey attribute value on an element is not unique for the page'
|
|
2633
|
+
}
|
|
2634
|
+
},
|
|
2635
|
+
tenon: {
|
|
2636
|
+
101: {
|
|
2637
|
+
quality: 1,
|
|
2638
|
+
what: 'Duplicate accesskey value'
|
|
2639
|
+
}
|
|
2640
|
+
},
|
|
2641
|
+
wave: {
|
|
2642
|
+
'a:accesskey': {
|
|
2643
|
+
quality: 1,
|
|
2644
|
+
what: 'Accesskey'
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
},
|
|
2649
|
+
fieldSetMissing: {
|
|
2650
|
+
weight: 2,
|
|
2651
|
+
packages: {
|
|
2652
|
+
ibm: {
|
|
2653
|
+
WCAG20_Input_RadioChkInFieldSet: {
|
|
2654
|
+
quality: 1,
|
|
2655
|
+
what: 'Input is in a different group than another with the name'
|
|
2656
|
+
}
|
|
2657
|
+
},
|
|
2658
|
+
testaro: {
|
|
2659
|
+
radioSet: {
|
|
2660
|
+
quality: 1,
|
|
2661
|
+
what: 'No or invalid grouping of radio buttons in fieldsets'
|
|
2662
|
+
}
|
|
2663
|
+
},
|
|
2664
|
+
wave: {
|
|
2665
|
+
'a:fieldset_missing': {
|
|
2666
|
+
quality: 1,
|
|
2667
|
+
what: 'fieldset element is missing'
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
},
|
|
2672
|
+
fieldSetRisk: {
|
|
2673
|
+
weight: 1,
|
|
2674
|
+
packages: {
|
|
2675
|
+
htmlcs: {
|
|
2676
|
+
'w:AA.1_3_1.H71.SameName': {
|
|
2677
|
+
quality: 1,
|
|
2678
|
+
what: 'Radio buttons or check boxes may require a group description via a fieldset element'
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
},
|
|
2683
|
+
legendMissing: {
|
|
2684
|
+
weight: 2,
|
|
2685
|
+
packages: {
|
|
2686
|
+
htmlcs: {
|
|
2687
|
+
'e:AA.1_3_1.H71.NoLegend': {
|
|
2688
|
+
quality: 1,
|
|
2689
|
+
what: 'Fieldset has no legend element'
|
|
2690
|
+
}
|
|
2691
|
+
},
|
|
2692
|
+
ibm: {
|
|
2693
|
+
WCAG20_Fieldset_HasLegend: {
|
|
2694
|
+
quality: 1,
|
|
2695
|
+
what: 'fieldset element has no single, non-empty legend as a label'
|
|
2696
|
+
}
|
|
2697
|
+
},
|
|
2698
|
+
wave: {
|
|
2699
|
+
'a:legend_missing': {
|
|
2700
|
+
quality: 1,
|
|
2701
|
+
what: 'Fieldset has no legend element'
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
},
|
|
2706
|
+
groupName: {
|
|
2707
|
+
weight: 3,
|
|
2708
|
+
packages: {
|
|
2709
|
+
alfa: {
|
|
2710
|
+
r60: {
|
|
2711
|
+
quality: 1,
|
|
2712
|
+
what: 'Form-control group has no accessible name'
|
|
2713
|
+
}
|
|
2714
|
+
},
|
|
2715
|
+
htmlcs: {
|
|
2716
|
+
'e:AA.4_1_2.H91.Fieldset.Name': {
|
|
2717
|
+
quality: 1,
|
|
2718
|
+
what: 'Fieldset has no accessible name'
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
},
|
|
2723
|
+
layoutTable: {
|
|
2724
|
+
weight: 2,
|
|
2725
|
+
packages: {
|
|
2726
|
+
testaro: {
|
|
2727
|
+
nonTable: {
|
|
2728
|
+
quality: 1,
|
|
2729
|
+
what: 'table element fails the structural requirements for tabular data'
|
|
2730
|
+
}
|
|
2731
|
+
},
|
|
2732
|
+
wave: {
|
|
2733
|
+
'a:table_layout': {
|
|
2734
|
+
quality: 1,
|
|
2735
|
+
what: 'table element is misused to arrange content'
|
|
2736
|
+
}
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
},
|
|
2740
|
+
tableCaption: {
|
|
2741
|
+
weight: 1,
|
|
2742
|
+
packages: {
|
|
2743
|
+
axe: {
|
|
2744
|
+
'table-fake-caption': {
|
|
2745
|
+
quality: 1,
|
|
2746
|
+
what: 'Data or header cells are used for a table caption instead of a caption element'
|
|
2747
|
+
}
|
|
2748
|
+
},
|
|
2749
|
+
htmlcs: {
|
|
2750
|
+
'w:AA.1_3_1.H39.3.NoCaption': {
|
|
2751
|
+
quality: 1,
|
|
2752
|
+
what: 'Table has no caption element'
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
},
|
|
2757
|
+
cellHeadersNotInferrable: {
|
|
2758
|
+
weight: 4,
|
|
2759
|
+
packages: {
|
|
2760
|
+
htmlcs: {
|
|
2761
|
+
'e:AA.1_3_1.H43.HeadersRequired': {
|
|
2762
|
+
quality: 1,
|
|
2763
|
+
what: 'Complex table requires headers attributes of cells'
|
|
2764
|
+
}
|
|
2765
|
+
},
|
|
2766
|
+
ibm: {
|
|
2767
|
+
Valerie_Table_DataCellRelationships: {
|
|
2768
|
+
quality: 1,
|
|
2769
|
+
what: 'Not all th and td elements in the complex table have header or scope attributes'
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
},
|
|
2774
|
+
cellHeadersAmbiguityRisk: {
|
|
2775
|
+
weight: 3,
|
|
2776
|
+
packages: {
|
|
2777
|
+
htmlcs: {
|
|
2778
|
+
'w:AA.1_3_1.H43.ScopeAmbiguous': {
|
|
2779
|
+
quality: 1,
|
|
2780
|
+
what: 'Complex table requires headers attributes of cells instead of header scopes'
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
},
|
|
2785
|
+
tableHeaderless: {
|
|
2786
|
+
weight: 3,
|
|
2787
|
+
packages: {
|
|
2788
|
+
continuum: {
|
|
2789
|
+
387: {
|
|
2790
|
+
quality: 1,
|
|
2791
|
+
what: 'table element contains no th element or element with a rowheader or columnheader role'
|
|
2792
|
+
}
|
|
2793
|
+
},
|
|
2794
|
+
ibm: {
|
|
2795
|
+
RPT_Table_DataHeadingsAria: {
|
|
2796
|
+
quality: 1,
|
|
2797
|
+
what: 'Data table does not identify headers'
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
},
|
|
2802
|
+
tableCellHeaderless: {
|
|
2803
|
+
weight: 3,
|
|
2804
|
+
packages: {
|
|
2805
|
+
alfa: {
|
|
2806
|
+
r77: {
|
|
2807
|
+
quality: 1,
|
|
2808
|
+
what: 'Table cell has no header'
|
|
2809
|
+
}
|
|
2810
|
+
},
|
|
2811
|
+
axe: {
|
|
2812
|
+
'td-has-header': {
|
|
2813
|
+
quality: 1,
|
|
2814
|
+
what: 'Cell in table larger than 3 by 3 has no header'
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
},
|
|
2819
|
+
tableHeaderCelless: {
|
|
2820
|
+
weight: 4,
|
|
2821
|
+
packages: {
|
|
2822
|
+
alfa: {
|
|
2823
|
+
r46: {
|
|
2824
|
+
quality: 1,
|
|
2825
|
+
what: 'Header cell is not assigned to any cell'
|
|
2826
|
+
}
|
|
2827
|
+
},
|
|
2828
|
+
axe: {
|
|
2829
|
+
'th-has-data-cells': {
|
|
2830
|
+
quality: 1,
|
|
2831
|
+
what: 'Table header refers to no cell'
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
},
|
|
2836
|
+
TableHeaderScopeRisk: {
|
|
2837
|
+
weight: 1,
|
|
2838
|
+
packages: {
|
|
2839
|
+
htmlcs: {
|
|
2840
|
+
'e:AA.1_3_1.H63.1': {
|
|
2841
|
+
quality: 1,
|
|
2842
|
+
what: 'Not all th elements in the table have a scope attribute, so an inferred scope may be incorrect'
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
},
|
|
2847
|
+
tableHeaderEmpty: {
|
|
2848
|
+
weight: 2,
|
|
2849
|
+
packages: {
|
|
2850
|
+
wave: {
|
|
2851
|
+
'e:th_empty': {
|
|
2852
|
+
quality: 1,
|
|
2853
|
+
what: 'th (table header) contains no text'
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
},
|
|
2858
|
+
controlNoText: {
|
|
2859
|
+
weight: 4,
|
|
2860
|
+
packages: {
|
|
2861
|
+
axe: {
|
|
2862
|
+
label: {
|
|
2863
|
+
quality: 1,
|
|
2864
|
+
what: 'Form element has no label'
|
|
2865
|
+
}
|
|
2866
|
+
},
|
|
2867
|
+
htmlcs: {
|
|
2868
|
+
'e:AA.1_3_1.F68': {
|
|
2869
|
+
quality: 1,
|
|
2870
|
+
what: 'Form control has no label'
|
|
2871
|
+
}
|
|
2872
|
+
},
|
|
2873
|
+
ibm: {
|
|
2874
|
+
WCAG20_Input_ExplicitLabel: {
|
|
2875
|
+
quality: 1,
|
|
2876
|
+
what: 'Form control has no associated label'
|
|
2877
|
+
}
|
|
2878
|
+
},
|
|
2879
|
+
wave: {
|
|
2880
|
+
'e:label_missing': {
|
|
2881
|
+
quality: 1,
|
|
2882
|
+
what: 'form element has no label'
|
|
2883
|
+
}
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
},
|
|
2887
|
+
controlLabelInvisible: {
|
|
2888
|
+
weight: 4,
|
|
2889
|
+
packages: {
|
|
2890
|
+
axe: {
|
|
2891
|
+
'label-title-only': {
|
|
2892
|
+
quality: 1,
|
|
2893
|
+
what: 'Form element has no visible label'
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
},
|
|
2898
|
+
titleAsLabel: {
|
|
2899
|
+
weight: 3,
|
|
2900
|
+
packages: {
|
|
2901
|
+
wave: {
|
|
2902
|
+
'a:label_title': {
|
|
2903
|
+
quality: 1,
|
|
2904
|
+
what: 'Form control has a title but no label'
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
},
|
|
2909
|
+
visibleLabelNotName: {
|
|
2910
|
+
weight: 3,
|
|
2911
|
+
packages: {
|
|
2912
|
+
alfa: {
|
|
2913
|
+
r14: {
|
|
2914
|
+
quality: 1,
|
|
2915
|
+
what: 'Visible label is not in the accessible name'
|
|
2916
|
+
}
|
|
2917
|
+
},
|
|
2918
|
+
axe: {
|
|
2919
|
+
'label-content-name-mismatch': {
|
|
2920
|
+
quality: 1,
|
|
2921
|
+
what: 'Element visible text is not part of its accessible name'
|
|
2922
|
+
}
|
|
2923
|
+
},
|
|
2924
|
+
htmlcs: {
|
|
2925
|
+
'w:AA.2_5_3.F96': {
|
|
2926
|
+
quality: 1,
|
|
2927
|
+
what: 'Visible label is not in the accessible name'
|
|
2928
|
+
}
|
|
2929
|
+
},
|
|
2930
|
+
ibm: {
|
|
2931
|
+
WCAG21_Label_Accessible: {
|
|
2932
|
+
quality: 1,
|
|
2933
|
+
what: 'Accessible name does not match or contain the visible label text'
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
},
|
|
2938
|
+
targetSize: {
|
|
2939
|
+
weight: 2,
|
|
2940
|
+
packages: {
|
|
2941
|
+
tenon: {
|
|
2942
|
+
152: {
|
|
2943
|
+
quality: 1,
|
|
2944
|
+
what: 'Actionable element is smaller than the minimum required size'
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
},
|
|
2949
|
+
visibleBulk: {
|
|
2950
|
+
weight: 1,
|
|
2951
|
+
packages: {
|
|
2952
|
+
testaro: {
|
|
2953
|
+
bulk: {
|
|
2954
|
+
quality: 1,
|
|
2955
|
+
what: 'Page contains many visible elements'
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
},
|
|
2960
|
+
activeEmbedding: {
|
|
2961
|
+
weight: 3,
|
|
2962
|
+
packages: {
|
|
2963
|
+
axe: {
|
|
2964
|
+
'nested-interactive': {
|
|
2965
|
+
quality: 1,
|
|
2966
|
+
what: 'Interactive controls are nested'
|
|
2967
|
+
}
|
|
2968
|
+
},
|
|
2969
|
+
continuum: {
|
|
2970
|
+
22: {
|
|
2971
|
+
quality: 1,
|
|
2972
|
+
what: 'Link contains an input, keygen, select, textarea, or button'
|
|
2973
|
+
}
|
|
2974
|
+
},
|
|
2975
|
+
testaro: {
|
|
2976
|
+
embAc: {
|
|
2977
|
+
quality: 1,
|
|
2978
|
+
what: 'Active element is embedded in a link or button'
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
},
|
|
2983
|
+
tabFocusability: {
|
|
2984
|
+
weight: 4,
|
|
2985
|
+
packages: {
|
|
2986
|
+
ibm: {
|
|
2987
|
+
Rpt_Aria_MissingFocusableChild: {
|
|
2988
|
+
quality: 1,
|
|
2989
|
+
what: 'UI component has no focusable child element for keyboard access'
|
|
2990
|
+
}
|
|
2991
|
+
},
|
|
2992
|
+
testaro: {
|
|
2993
|
+
focAll: {
|
|
2994
|
+
quality: 0.5,
|
|
2995
|
+
what: 'Discrepancy between elements that should be and that are Tab-focusable'
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
},
|
|
3000
|
+
focusIndication: {
|
|
3001
|
+
weight: 4,
|
|
3002
|
+
packages: {
|
|
3003
|
+
alfa: {
|
|
3004
|
+
r65: {
|
|
3005
|
+
quality: 1,
|
|
3006
|
+
what: 'Element in sequential focus order has no visible focus'
|
|
3007
|
+
}
|
|
3008
|
+
},
|
|
3009
|
+
testaro: {
|
|
3010
|
+
focInd: {
|
|
3011
|
+
quality: 1,
|
|
3012
|
+
what: 'Focused element displaying no or nostandard focus indicator'
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
},
|
|
3017
|
+
allCaps: {
|
|
3018
|
+
weight: 1,
|
|
3019
|
+
packages: {
|
|
3020
|
+
alfa: {
|
|
3021
|
+
r72: {
|
|
3022
|
+
quality: 1,
|
|
3023
|
+
what: 'Paragraph text is uppercased'
|
|
3024
|
+
}
|
|
3025
|
+
},
|
|
3026
|
+
tenon: {
|
|
3027
|
+
153: {
|
|
3028
|
+
quality: 1,
|
|
3029
|
+
what: 'Long string of text is in all caps'
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
},
|
|
3034
|
+
allItalics: {
|
|
3035
|
+
weight: 1,
|
|
3036
|
+
packages: {
|
|
3037
|
+
alfa: {
|
|
3038
|
+
r85: {
|
|
3039
|
+
quality: 1,
|
|
3040
|
+
what: 'Text of the paragraph is all italic'
|
|
3041
|
+
}
|
|
3042
|
+
},
|
|
3043
|
+
tenon: {
|
|
3044
|
+
154: {
|
|
3045
|
+
quality: 1,
|
|
3046
|
+
what: 'Long string of text is italic'
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
},
|
|
3051
|
+
noLandmarks: {
|
|
3052
|
+
weight: 2,
|
|
3053
|
+
packages: {
|
|
3054
|
+
wave: {
|
|
3055
|
+
'a:region_missing': {
|
|
3056
|
+
quality: 1,
|
|
3057
|
+
what: 'Page has no regions or ARIA landmarks'
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
},
|
|
3062
|
+
contentBeyondLandmarks: {
|
|
3063
|
+
weight: 2,
|
|
3064
|
+
packages: {
|
|
3065
|
+
alfa: {
|
|
3066
|
+
r57: {
|
|
3067
|
+
quality: 1,
|
|
3068
|
+
what: 'Perceivable text content is not included in any landmark'
|
|
3069
|
+
}
|
|
3070
|
+
},
|
|
3071
|
+
axe: {
|
|
3072
|
+
region: {
|
|
3073
|
+
quality: 1,
|
|
3074
|
+
what: 'Some page content is not contained by landmarks'
|
|
3075
|
+
}
|
|
3076
|
+
},
|
|
3077
|
+
ibm: {
|
|
3078
|
+
Rpt_Aria_OrphanedContent_Native_Host_Sematics: {
|
|
3079
|
+
quality: 1,
|
|
3080
|
+
what: 'Content does not reside within an element with a landmark role'
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
},
|
|
3085
|
+
footerTopLandmark: {
|
|
3086
|
+
weight: 1,
|
|
3087
|
+
packages: {
|
|
3088
|
+
axe: {
|
|
3089
|
+
'landmark-contentinfo-is-top-level': {
|
|
3090
|
+
quality: 1,
|
|
3091
|
+
what: 'contentinfo landmark (footer) is contained in another landmark'
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
},
|
|
3096
|
+
asideNotTop: {
|
|
3097
|
+
weight: 2,
|
|
3098
|
+
packages: {
|
|
3099
|
+
axe: {
|
|
3100
|
+
'landmark-complementary-is-top-level': {
|
|
3101
|
+
quality: 1,
|
|
3102
|
+
what: 'complementary landmark (aside) is contained in another landmark'
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
},
|
|
3107
|
+
mainNotTop: {
|
|
3108
|
+
weight: 2,
|
|
3109
|
+
packages: {
|
|
3110
|
+
axe: {
|
|
3111
|
+
'landmark-main-is-top-level': {
|
|
3112
|
+
quality: 1,
|
|
3113
|
+
what: 'main landmark is contained in another landmark'
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
},
|
|
3118
|
+
mainConfusion: {
|
|
3119
|
+
weight: 3,
|
|
3120
|
+
packages: {
|
|
3121
|
+
ibm: {
|
|
3122
|
+
Rpt_Aria_MultipleMainsRequireLabel_Implicit_2: {
|
|
3123
|
+
quality: 1,
|
|
3124
|
+
what: 'Element with main role has no unique label among the main-role elements'
|
|
3125
|
+
},
|
|
3126
|
+
Rpt_Aria_MultipleMainsVisibleLabel_Implicit: {
|
|
3127
|
+
quality: 1,
|
|
3128
|
+
what: 'Element with main role has no unique visible label among the main-role elements'
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
},
|
|
3133
|
+
mainNot1: {
|
|
3134
|
+
weight: 2,
|
|
3135
|
+
packages: {
|
|
3136
|
+
axe: {
|
|
3137
|
+
'landmark-one-main': {
|
|
3138
|
+
quality: 1,
|
|
3139
|
+
what: 'page has no main landmark'
|
|
3140
|
+
},
|
|
3141
|
+
'landmark-no-duplicate-main': {
|
|
3142
|
+
quality: 1,
|
|
3143
|
+
what: 'page has more than 1 main landmark'
|
|
3144
|
+
}
|
|
3145
|
+
},
|
|
3146
|
+
continuum: {
|
|
3147
|
+
809: {
|
|
3148
|
+
quality: 1,
|
|
3149
|
+
what: 'More than 1 main element is located in the body element'
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
},
|
|
3154
|
+
banners: {
|
|
3155
|
+
weight: 2,
|
|
3156
|
+
packages: {
|
|
3157
|
+
axe: {
|
|
3158
|
+
'landmark-no-duplicate-banner': {
|
|
3159
|
+
quality: 1,
|
|
3160
|
+
what: 'Page has more than 1 banner landmark'
|
|
3161
|
+
}
|
|
3162
|
+
},
|
|
3163
|
+
ibm: {
|
|
3164
|
+
Rpt_Aria_OneBannerInSiblingSet_Implicit: {
|
|
3165
|
+
quality: 1,
|
|
3166
|
+
what: 'Multiple elements with a banner role are on the page'
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
},
|
|
3171
|
+
bannerNotTop: {
|
|
3172
|
+
weight: 2,
|
|
3173
|
+
packages: {
|
|
3174
|
+
axe: {
|
|
3175
|
+
'landmark-banner-is-top-level': {
|
|
3176
|
+
quality: 1,
|
|
3177
|
+
what: 'banner landmark is contained in another landmark'
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
},
|
|
3182
|
+
footerConfusion: {
|
|
3183
|
+
weight: 3,
|
|
3184
|
+
packages: {
|
|
3185
|
+
ibm: {
|
|
3186
|
+
Rpt_Aria_MultipleContentinfoLandmarks_Implicit: {
|
|
3187
|
+
quality: 1,
|
|
3188
|
+
what: 'Element with a contentinfo role has no unique purpose label among the contentinfo-role elements'
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
},
|
|
3193
|
+
footerMultiple: {
|
|
3194
|
+
weight: 2,
|
|
3195
|
+
packages: {
|
|
3196
|
+
axe: {
|
|
3197
|
+
'landmark-no-duplicate-contentinfo': {
|
|
3198
|
+
quality: 1,
|
|
3199
|
+
what: 'Page has more than 1 contentinfo landmark (footer)'
|
|
3200
|
+
}
|
|
3201
|
+
},
|
|
3202
|
+
ibm: {
|
|
3203
|
+
Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit: {
|
|
3204
|
+
quality: 1,
|
|
3205
|
+
what: 'Page, document, or application has more than one element with a contentinfo role'
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
},
|
|
3210
|
+
landmarkConfusion: {
|
|
3211
|
+
weight: 3,
|
|
3212
|
+
packages: {
|
|
3213
|
+
axe: {
|
|
3214
|
+
'landmark-unique': {
|
|
3215
|
+
quality: 1,
|
|
3216
|
+
what: 'Landmark has a role and an accessible name that are identical to another'
|
|
3217
|
+
}
|
|
3218
|
+
},
|
|
3219
|
+
ibm: {
|
|
3220
|
+
landmark_name_unique: {
|
|
3221
|
+
quality: 1,
|
|
3222
|
+
what: 'Landmark has no unique aria-labelledby or aria-label among landmarks in the same parent region'
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
},
|
|
3227
|
+
articleConfusion: {
|
|
3228
|
+
weight: 3,
|
|
3229
|
+
packages: {
|
|
3230
|
+
ibm: {
|
|
3231
|
+
Rpt_Aria_MultipleArticleRoles_Implicit: {
|
|
3232
|
+
quality: 1,
|
|
3233
|
+
what: 'Element with an article role has no unique purpose label among the article-role elements'
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
},
|
|
3238
|
+
formConfusion: {
|
|
3239
|
+
weight: 3,
|
|
3240
|
+
packages: {
|
|
3241
|
+
ibm: {
|
|
3242
|
+
Rpt_Aria_MultipleFormLandmarks_Implicit: {
|
|
3243
|
+
quality: 1,
|
|
3244
|
+
what: 'Element with a form role has no unique purpose label among the form-role elements'
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
},
|
|
3249
|
+
applicationConfusion: {
|
|
3250
|
+
weight: 3,
|
|
3251
|
+
packages: {
|
|
3252
|
+
ibm: {
|
|
3253
|
+
Rpt_Aria_MultipleApplicationLandmarks: {
|
|
3254
|
+
quality: 1,
|
|
3255
|
+
what: 'Element with an application role has no unique purpose label among the application-role elements'
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
},
|
|
3260
|
+
asideConfusion: {
|
|
3261
|
+
weight: 3,
|
|
3262
|
+
packages: {
|
|
3263
|
+
continuum: {
|
|
3264
|
+
527: {
|
|
3265
|
+
quality: 1,
|
|
3266
|
+
what: 'aside element has an accessible name that is non-unique among the aside elements'
|
|
3267
|
+
}
|
|
3268
|
+
},
|
|
3269
|
+
ibm: {
|
|
3270
|
+
Rpt_Aria_MultipleComplementaryLandmarks_Implicit: {
|
|
3271
|
+
quality: 1,
|
|
3272
|
+
what: 'Element with a complementary role has no unique purpose label among the complementary-role elements'
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
},
|
|
3277
|
+
bannerConfusion: {
|
|
3278
|
+
weight: 3,
|
|
3279
|
+
packages: {
|
|
3280
|
+
ibm: {
|
|
3281
|
+
Rpt_Aria_MultipleBannerLandmarks_Implicit: {
|
|
3282
|
+
quality: 1,
|
|
3283
|
+
what: 'Element with a banner role has no unique purpose label among the banner-role elements'
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
},
|
|
3288
|
+
navConfusion: {
|
|
3289
|
+
weight: 3,
|
|
3290
|
+
packages: {
|
|
3291
|
+
continuum: {
|
|
3292
|
+
531: {
|
|
3293
|
+
quality: 1,
|
|
3294
|
+
what: 'nav element has an accessible name that is non-unique among the nav elements'
|
|
3295
|
+
}
|
|
3296
|
+
},
|
|
3297
|
+
ibm: {
|
|
3298
|
+
Rpt_Aria_MultipleNavigationLandmarks_Implicit: {
|
|
3299
|
+
quality: 1,
|
|
3300
|
+
what: 'Element with a navigation role has no unique purpose label among the navigation-role elements'
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
},
|
|
3305
|
+
regionConfusion: {
|
|
3306
|
+
weight: 3,
|
|
3307
|
+
packages: {
|
|
3308
|
+
ibm: {
|
|
3309
|
+
Rpt_Aria_MultipleRegionsUniqueLabel_Implicit: {
|
|
3310
|
+
quality: 1,
|
|
3311
|
+
what: 'Element with a region role has no unique label among the region-role elements'
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
},
|
|
3316
|
+
searchConfusion: {
|
|
3317
|
+
weight: 3,
|
|
3318
|
+
packages: {
|
|
3319
|
+
ibm: {
|
|
3320
|
+
Rpt_Aria_MultipleSearchLandmarks: {
|
|
3321
|
+
quality: 1,
|
|
3322
|
+
what: 'Element with a search role has no unique purpose label among the search-role elements'
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
},
|
|
3327
|
+
asideNoText: {
|
|
3328
|
+
weight: 3,
|
|
3329
|
+
packages: {
|
|
3330
|
+
continuum: {
|
|
3331
|
+
532: {
|
|
3332
|
+
quality: 1,
|
|
3333
|
+
what: 'aside element is not the only aside element but has no accessible name'
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
},
|
|
3338
|
+
complementaryNoText: {
|
|
3339
|
+
weight: 1,
|
|
3340
|
+
packages: {
|
|
3341
|
+
ibm: {
|
|
3342
|
+
Rpt_Aria_ComplementaryRequiredLabel_Implicit: {
|
|
3343
|
+
quality: 1,
|
|
3344
|
+
what: 'Element has a complementary role but has no label'
|
|
3345
|
+
},
|
|
3346
|
+
Rpt_Aria_ComplementaryLandmarkLabel_Implicit: {
|
|
3347
|
+
quality: 1,
|
|
3348
|
+
what: 'Element with a complementary role has no visible purpose label'
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3352
|
+
},
|
|
3353
|
+
navNoText: {
|
|
3354
|
+
weight: 3,
|
|
3355
|
+
packages: {
|
|
3356
|
+
continuum: {
|
|
3357
|
+
533: {
|
|
3358
|
+
quality: 1,
|
|
3359
|
+
what: 'nav element is not the only nav element but has no accessible name'
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
},
|
|
3364
|
+
labelNoText: {
|
|
3365
|
+
weight: 4,
|
|
3366
|
+
packages: {
|
|
3367
|
+
ibm: {
|
|
3368
|
+
Valerie_Label_HasContent: {
|
|
3369
|
+
quality: 1,
|
|
3370
|
+
what: 'label element has no non-empty purpose-descriptive text'
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
},
|
|
3375
|
+
focusableOperable: {
|
|
3376
|
+
weight: 3,
|
|
3377
|
+
packages: {
|
|
3378
|
+
testaro: {
|
|
3379
|
+
focOp: {
|
|
3380
|
+
quality: 1,
|
|
3381
|
+
what: 'Operable elements that cannot be Tab-focused and vice versa'
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
},
|
|
3386
|
+
focusableRole: {
|
|
3387
|
+
weight: 3,
|
|
3388
|
+
packages: {
|
|
3389
|
+
axe: {
|
|
3390
|
+
'focus-order-semantics': {
|
|
3391
|
+
quality: 1,
|
|
3392
|
+
what: 'Focusable element has no active role'
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
},
|
|
3397
|
+
focusableHidden: {
|
|
3398
|
+
weight: 4,
|
|
3399
|
+
packages: {
|
|
3400
|
+
alfa: {
|
|
3401
|
+
r17: {
|
|
3402
|
+
quality: 1,
|
|
3403
|
+
what: 'Tab-focusable element is or has an ancestor that is aria-hidden'
|
|
3404
|
+
}
|
|
3405
|
+
},
|
|
3406
|
+
axe: {
|
|
3407
|
+
'aria-hidden-focus': {
|
|
3408
|
+
quality: 1,
|
|
3409
|
+
what: 'ARIA hidden element is focusable or contains a focusable element'
|
|
3410
|
+
},
|
|
3411
|
+
'presentation-role-conflict': {
|
|
3412
|
+
quality: 1,
|
|
3413
|
+
what: 'Element has a none/presentation role but is focusable or has a global ARIA state or property'
|
|
3414
|
+
}
|
|
3415
|
+
},
|
|
3416
|
+
continuum: {
|
|
3417
|
+
790: {
|
|
3418
|
+
quality: 1,
|
|
3419
|
+
what: 'Element with an explicit or implicit nonnegative tabindex attribute is directly aria-hidden'
|
|
3420
|
+
}
|
|
3421
|
+
},
|
|
3422
|
+
ibm: {
|
|
3423
|
+
aria_hidden_focus_misuse: {
|
|
3424
|
+
quality: 1,
|
|
3425
|
+
what: 'Focusable element is within the subtree of an element with aria-hidden set to true'
|
|
3426
|
+
}
|
|
3427
|
+
},
|
|
3428
|
+
tenon: {
|
|
3429
|
+
189: {
|
|
3430
|
+
quality: 1,
|
|
3431
|
+
what: 'Element is typically used for interaction but has a presentation role'
|
|
3432
|
+
},
|
|
3433
|
+
194: {
|
|
3434
|
+
quality: 1,
|
|
3435
|
+
what: 'Visible element is focusable but has a presentation role or aria-hidden=true attribute'
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
},
|
|
3440
|
+
focusedAway: {
|
|
3441
|
+
weight: 3,
|
|
3442
|
+
packages: {
|
|
3443
|
+
testaro: {
|
|
3444
|
+
focVis: {
|
|
3445
|
+
quality: 1,
|
|
3446
|
+
what: 'Element when focused is off the display'
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
},
|
|
3451
|
+
focusableDescendants: {
|
|
3452
|
+
weight: 4,
|
|
3453
|
+
packages: {
|
|
3454
|
+
alfa: {
|
|
3455
|
+
r90: {
|
|
3456
|
+
quality: 1,
|
|
3457
|
+
what: 'Element has a role making its children presentational but contains a focusable element'
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
},
|
|
3462
|
+
labeledHidden: {
|
|
3463
|
+
weight: 2,
|
|
3464
|
+
packages: {
|
|
3465
|
+
htmlcs: {
|
|
3466
|
+
'w:AA.1_3_1.F68.Hidden': {
|
|
3467
|
+
quality: 1,
|
|
3468
|
+
what: 'Hidden form field is needlessly labeled.'
|
|
3469
|
+
},
|
|
3470
|
+
'w:AA.1_3_1.F68.HiddenAttr': {
|
|
3471
|
+
quality: 1,
|
|
3472
|
+
what: 'Form field with a hidden attribute is needlessly labeled.'
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
},
|
|
3477
|
+
hiddenContentRisk: {
|
|
3478
|
+
weight: 1,
|
|
3479
|
+
packages: {
|
|
3480
|
+
axe: {
|
|
3481
|
+
'hidden-content': {
|
|
3482
|
+
quality: 1,
|
|
3483
|
+
what: 'Some content is hidden and therefore may not be testable for accessibility'
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
},
|
|
3488
|
+
frameContentRisk: {
|
|
3489
|
+
weight: 1,
|
|
3490
|
+
packages: {
|
|
3491
|
+
axe: {
|
|
3492
|
+
'frame-tested': {
|
|
3493
|
+
quality: 0.2,
|
|
3494
|
+
what: 'Some content is in an iframe and therefore may not be testable for accessibility'
|
|
3495
|
+
}
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
},
|
|
3499
|
+
frameSandboxRisk: {
|
|
3500
|
+
weight: 2,
|
|
3501
|
+
packages: {
|
|
3502
|
+
nuVal: {
|
|
3503
|
+
'Potentially bad value “allow-scripts allow-same-origin” for attribute “sandbox” on element “iframe”: Setting both “allow-scripts” and “allow-same-origin” is not recommended, because it effectively enables an embedded page to break out of all sandboxing.': {
|
|
3504
|
+
quality: 1,
|
|
3505
|
+
what: 'iframe element has vulnerable sandbox="allow-scripts allow-same-origin"'
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
},
|
|
3510
|
+
hoverSurprise: {
|
|
3511
|
+
weight: 1,
|
|
3512
|
+
packages: {
|
|
3513
|
+
testaro: {
|
|
3514
|
+
hover: {
|
|
3515
|
+
quality: 1,
|
|
3516
|
+
what: 'Content changes caused by hovering'
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
},
|
|
3521
|
+
labelClash: {
|
|
3522
|
+
weight: 2,
|
|
3523
|
+
packages: {
|
|
3524
|
+
testaro: {
|
|
3525
|
+
labClash: {
|
|
3526
|
+
quality: 1,
|
|
3527
|
+
what: 'Incompatible label types'
|
|
3528
|
+
}
|
|
3529
|
+
},
|
|
3530
|
+
ibm: {
|
|
3531
|
+
RPT_Label_UniqueFor: {
|
|
3532
|
+
quality: 1,
|
|
3533
|
+
what: 'Form control does not have exactly one label'
|
|
3534
|
+
}
|
|
3535
|
+
},
|
|
3536
|
+
wave: {
|
|
3537
|
+
'e:label_multiple': {
|
|
3538
|
+
quality: 1,
|
|
3539
|
+
what: 'Form control has more than one label associated with it'
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
},
|
|
3544
|
+
labelEmpty: {
|
|
3545
|
+
weight: 3,
|
|
3546
|
+
packages: {
|
|
3547
|
+
htmlcs: {
|
|
3548
|
+
'w:AA.1_3_1.ARIA6': {
|
|
3549
|
+
quality: 1,
|
|
3550
|
+
what: 'Value of the aria-label attribute of the form control is empty or only whitespace'
|
|
3551
|
+
},
|
|
3552
|
+
'w:AA.4_1_2.ARIA6': {
|
|
3553
|
+
quality: 1,
|
|
3554
|
+
what: 'Value of the aria-label attribute of the form control is empty or only whitespace'
|
|
3555
|
+
}
|
|
3556
|
+
},
|
|
3557
|
+
wave: {
|
|
3558
|
+
'e:label_empty': {
|
|
3559
|
+
quality: 1,
|
|
3560
|
+
what: 'Empty form label'
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
}
|
|
3564
|
+
},
|
|
3565
|
+
linkComprehensionRisk: {
|
|
3566
|
+
weight: 1,
|
|
3567
|
+
packages: {
|
|
3568
|
+
wave: {
|
|
3569
|
+
'a:link_suspicious': {
|
|
3570
|
+
quality: 1,
|
|
3571
|
+
what: 'Suspicious link text'
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
},
|
|
3576
|
+
attributeBad: {
|
|
3577
|
+
weight: 4,
|
|
3578
|
+
packages: {
|
|
3579
|
+
nuVal: {
|
|
3580
|
+
'^Attribute .+ not allowed on element .+ at this point.*$': {
|
|
3581
|
+
quality: 1,
|
|
3582
|
+
what: 'attribute not allowed on this element'
|
|
3583
|
+
},
|
|
3584
|
+
'^Bad value .+ for attribute .+ on element .+$': {
|
|
3585
|
+
quality: 1,
|
|
3586
|
+
what: 'attribute on this element has an invalid value'
|
|
3587
|
+
},
|
|
3588
|
+
'^Attribute .+ not allowed here.*$': {
|
|
3589
|
+
quality: 1,
|
|
3590
|
+
what: 'Attribute not allowed here'
|
|
3591
|
+
},
|
|
3592
|
+
'^Attribute .+ is not serializable as XML 1\\.0.*$': {
|
|
3593
|
+
quality: 1,
|
|
3594
|
+
what: 'Attribute is invalidly nonserializable'
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
},
|
|
3599
|
+
nonWebLink: {
|
|
3600
|
+
weight: 1,
|
|
3601
|
+
packages: {
|
|
3602
|
+
continuum: {
|
|
3603
|
+
141: {
|
|
3604
|
+
quality: 1,
|
|
3605
|
+
what: 'a element has an href attribute set to an image file reference'
|
|
3606
|
+
}
|
|
3607
|
+
},
|
|
3608
|
+
wave: {
|
|
3609
|
+
'a:link_excel': {
|
|
3610
|
+
quality: 1,
|
|
3611
|
+
what: 'Link to Microsoft Excel workbook'
|
|
3612
|
+
},
|
|
3613
|
+
'a:link_word': {
|
|
3614
|
+
quality: 1,
|
|
3615
|
+
what: 'Link to Microsoft Word document'
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
},
|
|
3620
|
+
linkVague: {
|
|
3621
|
+
weight: 3,
|
|
3622
|
+
packages: {
|
|
3623
|
+
tenon: {
|
|
3624
|
+
73: {
|
|
3625
|
+
quality: 1,
|
|
3626
|
+
what: 'Link text is too generic to communicate the purpose or destination'
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
},
|
|
3631
|
+
linkIndication: {
|
|
3632
|
+
weight: 2,
|
|
3633
|
+
packages: {
|
|
3634
|
+
alfa: {
|
|
3635
|
+
r62: {
|
|
3636
|
+
quality: 1,
|
|
3637
|
+
what: 'Inline link is not distinct from the surrounding text except by color'
|
|
3638
|
+
}
|
|
3639
|
+
},
|
|
3640
|
+
axe: {
|
|
3641
|
+
'link-in-text-block': {
|
|
3642
|
+
quality: 1,
|
|
3643
|
+
what: 'Link is not distinct from surrounding text without reliance on color'
|
|
3644
|
+
}
|
|
3645
|
+
},
|
|
3646
|
+
testaro: {
|
|
3647
|
+
linkUl: {
|
|
3648
|
+
quality: 1,
|
|
3649
|
+
what: 'Non-underlined adjacent links'
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
},
|
|
3654
|
+
menuNavigation: {
|
|
3655
|
+
weight: 2,
|
|
3656
|
+
packages: {
|
|
3657
|
+
testaro: {
|
|
3658
|
+
menuNav: {
|
|
3659
|
+
quality: 1,
|
|
3660
|
+
what: 'Nonstandard keyboard navigation among focusable menu items'
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
},
|
|
3665
|
+
menuItemless: {
|
|
3666
|
+
weight: 4,
|
|
3667
|
+
packages: {
|
|
3668
|
+
wave: {
|
|
3669
|
+
'e:aria_menu_broken': {
|
|
3670
|
+
quality: 1,
|
|
3671
|
+
what: 'ARIA menu does not contain required menu items'
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
},
|
|
3676
|
+
tabNavigation: {
|
|
3677
|
+
weight: 2,
|
|
3678
|
+
packages: {
|
|
3679
|
+
testaro: {
|
|
3680
|
+
tabNav: {
|
|
3681
|
+
quality: 1,
|
|
3682
|
+
what: 'Nonstandard keyboard navigation among tabs'
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
},
|
|
3687
|
+
spontaneousMotion: {
|
|
3688
|
+
weight: 2,
|
|
3689
|
+
packages: {
|
|
3690
|
+
testaro: {
|
|
3691
|
+
motion: {
|
|
3692
|
+
quality: 1,
|
|
3693
|
+
what: 'Change of visible content not requested by user'
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
}
|
|
3697
|
+
},
|
|
3698
|
+
autoplay: {
|
|
3699
|
+
weight: 2,
|
|
3700
|
+
packages: {
|
|
3701
|
+
axe: {
|
|
3702
|
+
'no-autoplay-audio': {
|
|
3703
|
+
quality: 1,
|
|
3704
|
+
what: 'video or audio element plays automatically'
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
},
|
|
3709
|
+
divParentBad: {
|
|
3710
|
+
weight: 4,
|
|
3711
|
+
packages: {
|
|
3712
|
+
nuVal: {
|
|
3713
|
+
'Element “div” not allowed as child of element “button” in this context. (Suppressing further errors from this subtree.)': {
|
|
3714
|
+
quality: 1,
|
|
3715
|
+
what: 'div element has a button element as its parent'
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
}
|
|
3719
|
+
},
|
|
3720
|
+
pParentBad: {
|
|
3721
|
+
weight: 4,
|
|
3722
|
+
packages: {
|
|
3723
|
+
nuVal: {
|
|
3724
|
+
'Element “p” not allowed as child of element “strong” in this context. (Suppressing further errors from this subtree.)': {
|
|
3725
|
+
quality: 1,
|
|
3726
|
+
what: 'p element has a strong element as its parent'
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3730
|
+
},
|
|
3731
|
+
styleParentBad: {
|
|
3732
|
+
weight: 4,
|
|
3733
|
+
packages: {
|
|
3734
|
+
nuVal: {
|
|
3735
|
+
'Element “style” not allowed as child of element “body” in this context. (Suppressing further errors from this subtree.)': {
|
|
3736
|
+
quality: 1,
|
|
3737
|
+
what: 'style element not allowed as a child of the body element'
|
|
3738
|
+
},
|
|
3739
|
+
'Element “style” not allowed as child of element “div” in this context. (Suppressing further errors from this subtree.)': {
|
|
3740
|
+
quality: 1,
|
|
3741
|
+
what: 'style element not allowed as a child of this div element'
|
|
3742
|
+
},
|
|
3743
|
+
'Element “style” not allowed as child of element “main” in this context. (Suppressing further errors from this subtree.)': {
|
|
3744
|
+
quality: 1,
|
|
3745
|
+
what: 'style element not allowed as a child of this main element'
|
|
3746
|
+
},
|
|
3747
|
+
'Element “style” not allowed as child of element “footer” in this context. (Suppressing further errors from this subtree.)': {
|
|
3748
|
+
quality: 1,
|
|
3749
|
+
what: 'style element not allowed as a child of this footer element'
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
},
|
|
3754
|
+
inconsistentStyles: {
|
|
3755
|
+
weight: 1,
|
|
3756
|
+
packages: {
|
|
3757
|
+
testaro: {
|
|
3758
|
+
styleDiff: {
|
|
3759
|
+
quality: 1,
|
|
3760
|
+
what: 'Heading, link, and button style inconsistencies'
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
},
|
|
3765
|
+
zIndexNotZero: {
|
|
3766
|
+
weight: 1,
|
|
3767
|
+
packages: {
|
|
3768
|
+
testaro: {
|
|
3769
|
+
zIndex: {
|
|
3770
|
+
quality: 1,
|
|
3771
|
+
what: 'Layering with nondefault z-index values'
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
},
|
|
3776
|
+
tabIndexPositive: {
|
|
3777
|
+
weight: 1,
|
|
3778
|
+
packages: {
|
|
3779
|
+
axe: {
|
|
3780
|
+
tabindex: {
|
|
3781
|
+
quality: 1,
|
|
3782
|
+
what: 'Positive tabIndex risks creating a confusing focus order'
|
|
3783
|
+
}
|
|
3784
|
+
},
|
|
3785
|
+
wave: {
|
|
3786
|
+
'a:tabindex': {
|
|
3787
|
+
quality: 1,
|
|
3788
|
+
what: 'tabIndex value positive'
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
},
|
|
3793
|
+
tabIndexMissing: {
|
|
3794
|
+
weight: 4,
|
|
3795
|
+
packages: {
|
|
3796
|
+
continuum: {
|
|
3797
|
+
337: {
|
|
3798
|
+
quality: 1,
|
|
3799
|
+
what: 'Enabled element with a button role has no nonpositive tabindex attribute'
|
|
3800
|
+
},
|
|
3801
|
+
356: {
|
|
3802
|
+
quality: 1,
|
|
3803
|
+
what: 'Enabled element with a textbox role has no nonpositive tabindex attribute'
|
|
3804
|
+
}
|
|
3805
|
+
},
|
|
3806
|
+
tenon: {
|
|
3807
|
+
190: {
|
|
3808
|
+
quality: 1,
|
|
3809
|
+
what: 'Interactive item is not natively actionable, but has no tabindex=0 attribute'
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
},
|
|
3814
|
+
trackNoLabel: {
|
|
3815
|
+
weight: 4,
|
|
3816
|
+
packages: {
|
|
3817
|
+
continuum: {
|
|
3818
|
+
40: {
|
|
3819
|
+
quality: 1,
|
|
3820
|
+
what: 'captions track element has no label attribute set to a text value'
|
|
3821
|
+
},
|
|
3822
|
+
368: {
|
|
3823
|
+
quality: 1,
|
|
3824
|
+
what: 'subtitle track element has no label attribute set to a text value'
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
},
|
|
3829
|
+
audioCaptionMissing: {
|
|
3830
|
+
weight: 4,
|
|
3831
|
+
packages: {
|
|
3832
|
+
axe: {
|
|
3833
|
+
'audio-caption': {
|
|
3834
|
+
quality: 1,
|
|
3835
|
+
what: 'audio element has no captions track'
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
},
|
|
3840
|
+
videoCaptionMissing: {
|
|
3841
|
+
weight: 4,
|
|
3842
|
+
packages: {
|
|
3843
|
+
axe: {
|
|
3844
|
+
'video-caption': {
|
|
3845
|
+
quality: 1,
|
|
3846
|
+
what: 'video element has no captions'
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
},
|
|
3851
|
+
videoCaptionRisk: {
|
|
3852
|
+
weight: 1,
|
|
3853
|
+
packages: {
|
|
3854
|
+
wave: {
|
|
3855
|
+
'a:html5_video_audio': {
|
|
3856
|
+
quality: 1,
|
|
3857
|
+
what: 'video or audio element may have no or incorrect captions, transcript, or audio description'
|
|
3858
|
+
},
|
|
3859
|
+
'a:audio_video': {
|
|
3860
|
+
quality: 1,
|
|
3861
|
+
what: 'audio or video file or link may have no or incorrect captions, transcript, or audio description'
|
|
3862
|
+
},
|
|
3863
|
+
'a:youtube_video': {
|
|
3864
|
+
quality: 1,
|
|
3865
|
+
what: 'YouTube video may have no or incorrect captions'
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
},
|
|
3870
|
+
notKeyboardScrollable: {
|
|
3871
|
+
weight: 4,
|
|
3872
|
+
packages: {
|
|
3873
|
+
alfa: {
|
|
3874
|
+
r84: {
|
|
3875
|
+
quality: 1,
|
|
3876
|
+
what: 'Element is scrollable but not by keyboard'
|
|
3877
|
+
}
|
|
3878
|
+
},
|
|
3879
|
+
axe: {
|
|
3880
|
+
'scrollable-region-focusable': {
|
|
3881
|
+
quality: 1,
|
|
3882
|
+
what: 'Element is scrollable but has no keyboard access'
|
|
3883
|
+
}
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
},
|
|
3887
|
+
horizontalScrolling: {
|
|
3888
|
+
weight: 3,
|
|
3889
|
+
packages: {
|
|
3890
|
+
tenon: {
|
|
3891
|
+
28: {
|
|
3892
|
+
quality: 1,
|
|
3893
|
+
what: 'Layout or sizing of the page causes horizontal scrolling'
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
},
|
|
3898
|
+
scrollRisk: {
|
|
3899
|
+
weight: 1,
|
|
3900
|
+
packages: {
|
|
3901
|
+
htmlcs: {
|
|
3902
|
+
'w:AA.1_4_10.C32,C31,C33,C38,SCR34,G206': {
|
|
3903
|
+
quality: 1,
|
|
3904
|
+
what: 'Fixed-position element may force bidirectional scrolling'
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
},
|
|
3909
|
+
skipRepeatedContent: {
|
|
3910
|
+
weight: 3,
|
|
3911
|
+
packages: {
|
|
3912
|
+
alfa: {
|
|
3913
|
+
'r87': {
|
|
3914
|
+
quality: 0.5,
|
|
3915
|
+
what: 'First focusable element is not a link to the main content'
|
|
3916
|
+
}
|
|
3917
|
+
},
|
|
3918
|
+
axe: {
|
|
3919
|
+
'bypass': {
|
|
3920
|
+
quality: 1,
|
|
3921
|
+
what: 'Page has no means to bypass repeated blocks'
|
|
3922
|
+
},
|
|
3923
|
+
'skip-link': {
|
|
3924
|
+
quality: 1,
|
|
3925
|
+
what: 'Skip-link target is not focusable or does not exist'
|
|
3926
|
+
}
|
|
3927
|
+
},
|
|
3928
|
+
ibm: {
|
|
3929
|
+
WCAG20_Body_FirstASkips_Native_Host_Sematics: {
|
|
3930
|
+
quality: 0.5,
|
|
3931
|
+
what: 'Page provides no way to skip directly to the main content'
|
|
3932
|
+
}
|
|
3933
|
+
},
|
|
3934
|
+
wave: {
|
|
3935
|
+
'e:link_skip_broken': {
|
|
3936
|
+
quality: 1,
|
|
3937
|
+
what: 'Skip-navigation link has no target or is not keyboard accessible'
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
}
|
|
3941
|
+
},
|
|
3942
|
+
submitButton: {
|
|
3943
|
+
weight: 3,
|
|
3944
|
+
packages: {
|
|
3945
|
+
htmlcs: {
|
|
3946
|
+
'e:AA.3_2_2.H32.2': {
|
|
3947
|
+
quality: 1,
|
|
3948
|
+
what: 'Form has no submit button'
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
},
|
|
3953
|
+
fragmentaryNoticeRisk: {
|
|
3954
|
+
weight: 2,
|
|
3955
|
+
packages: {
|
|
3956
|
+
alfa: {
|
|
3957
|
+
r54: {
|
|
3958
|
+
quality: 1,
|
|
3959
|
+
what: 'Assertive region is not atomic'
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
},
|
|
3964
|
+
noScriptRisk: {
|
|
3965
|
+
weight: 1,
|
|
3966
|
+
packages: {
|
|
3967
|
+
wave: {
|
|
3968
|
+
'a:noscript': {
|
|
3969
|
+
quality: 1,
|
|
3970
|
+
what: 'noscript element may fail to contain an accessible equivalent or alternative'
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
},
|
|
3975
|
+
obsolete: {
|
|
3976
|
+
weight: 3,
|
|
3977
|
+
packages: {
|
|
3978
|
+
alfa: {
|
|
3979
|
+
r70: {
|
|
3980
|
+
quality: 1,
|
|
3981
|
+
what: 'Element is obsolete or deprecated'
|
|
3982
|
+
}
|
|
3983
|
+
},
|
|
3984
|
+
htmlcs: {
|
|
3985
|
+
'e:AA.1_3_1.H49.AlignAttr': {
|
|
3986
|
+
quality: 1,
|
|
3987
|
+
what: 'align attribute is obsolete'
|
|
3988
|
+
},
|
|
3989
|
+
'e:AA.1_3_1.H49.Center': {
|
|
3990
|
+
quality: 1,
|
|
3991
|
+
what: 'center element is obsolete'
|
|
3992
|
+
},
|
|
3993
|
+
'e:AA.1_3_1.H49.Font': {
|
|
3994
|
+
quality: 1,
|
|
3995
|
+
what: 'font element is obsolete'
|
|
3996
|
+
}
|
|
3997
|
+
},
|
|
3998
|
+
ibm: {
|
|
3999
|
+
aria_attribute_deprecated: {
|
|
4000
|
+
quality: 1,
|
|
4001
|
+
what: 'ARIA role or attribute is deprecated'
|
|
4002
|
+
},
|
|
4003
|
+
combobox_version: {
|
|
4004
|
+
quality: 1,
|
|
4005
|
+
what: 'combobox design pattern is invalid for ARIA 1.2'
|
|
4006
|
+
},
|
|
4007
|
+
element_attribute_deprecated: {
|
|
4008
|
+
quality: 1,
|
|
4009
|
+
what: 'Element or attribute is obsolete'
|
|
4010
|
+
}
|
|
4011
|
+
},
|
|
4012
|
+
nuVal: {
|
|
4013
|
+
'The “charset” attribute on the “script” element is obsolete.': {
|
|
4014
|
+
quality: 1,
|
|
4015
|
+
what: 'charset attribute is obsolete on a script element'
|
|
4016
|
+
},
|
|
4017
|
+
'The only allowed value for the “charset” attribute for the “script” element is “utf-8”. (But the attribute is not needed and should be omitted altogether.)': {
|
|
4018
|
+
quality: 1,
|
|
4019
|
+
what: 'charset attribute has a value other than utf-8 and is unnecessary'
|
|
4020
|
+
},
|
|
4021
|
+
'The “language” attribute on the “script” element is obsolete. You can safely omit it.': {
|
|
4022
|
+
quality: 1,
|
|
4023
|
+
what: 'language attribute is obsolete on a script element'
|
|
4024
|
+
},
|
|
4025
|
+
'The “language” attribute on the “script” element is obsolete. Use the “type” attribute instead.': {
|
|
4026
|
+
quality: 1,
|
|
4027
|
+
what: 'language attribute is obsolete on a script element'
|
|
4028
|
+
},
|
|
4029
|
+
'The “frameborder” attribute on the “iframe” element is obsolete. Use CSS instead.': {
|
|
4030
|
+
quality: 1,
|
|
4031
|
+
what: 'frameborder attribute is obsolete'
|
|
4032
|
+
},
|
|
4033
|
+
'The “name” attribute is obsolete. Consider putting an “id” attribute on the nearest container instead.': {
|
|
4034
|
+
quality: 1,
|
|
4035
|
+
what: 'name attribute is obsolete'
|
|
4036
|
+
},
|
|
4037
|
+
'The “allowtransparency” attribute on the “iframe” element is obsolete. Use CSS instead.': {
|
|
4038
|
+
quality: 1,
|
|
4039
|
+
what: 'allowtransparency attribute on an iframe element is obsolete'
|
|
4040
|
+
},
|
|
4041
|
+
'The “scrolling” attribute on the “iframe” element is obsolete. Use CSS instead.': {
|
|
4042
|
+
quality: 1,
|
|
4043
|
+
what: 'scrolling attribute on an iframe element is obsolete'
|
|
4044
|
+
}
|
|
4045
|
+
},
|
|
4046
|
+
wave: {
|
|
4047
|
+
'a:longdesc': {
|
|
4048
|
+
quality: 1,
|
|
4049
|
+
what: 'longdesc attribute is obsolete'
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
}
|
|
4053
|
+
},
|
|
4054
|
+
parseError: {
|
|
4055
|
+
weight: 3,
|
|
4056
|
+
packages: {
|
|
4057
|
+
nuVal: {
|
|
4058
|
+
'CSS: “-webkit-box-flex”: Parse Error.': {
|
|
4059
|
+
quality: 1,
|
|
4060
|
+
what: 'Invalid -webkit-box-flex in CSS'
|
|
4061
|
+
},
|
|
4062
|
+
'CSS: “-webkit-flex”: Parse Error.': {
|
|
4063
|
+
quality: 1,
|
|
4064
|
+
what: 'Invalid -webkit-flex in CSS'
|
|
4065
|
+
},
|
|
4066
|
+
'CSS: “-ms-flex”: Parse Error.': {
|
|
4067
|
+
quality: 1,
|
|
4068
|
+
what: 'Invalid -ms-flex in CSS'
|
|
4069
|
+
},
|
|
4070
|
+
'CSS: “-moz-box-flex”: Parse Error.': {
|
|
4071
|
+
quality: 1,
|
|
4072
|
+
what: 'Invalid -moz-box-flex in CSS'
|
|
4073
|
+
},
|
|
4074
|
+
'CSS: “flex”: Parse Error.': {
|
|
4075
|
+
quality: 1,
|
|
4076
|
+
what: 'Invalid flex in CSS'
|
|
4077
|
+
},
|
|
4078
|
+
'^CSS: “cursor”: .+ is not a “cursor” value.*$': {
|
|
4079
|
+
quality: 1,
|
|
4080
|
+
what: 'Invalid cursor in CSS'
|
|
4081
|
+
},
|
|
4082
|
+
'^CSS: “transform”: .+ is not a “transform” value.*$': {
|
|
4083
|
+
quality: 1,
|
|
4084
|
+
what: 'Invalid transform in CSS'
|
|
4085
|
+
},
|
|
4086
|
+
'^CSS: .+: Property .+ doesn\'t exist.*$': {
|
|
4087
|
+
quality: 1,
|
|
4088
|
+
what: 'Invalid property in CSS'
|
|
4089
|
+
},
|
|
4090
|
+
'^CSS: .+: only “0” can be a “length”. You must put a unit after your number.*$': {
|
|
4091
|
+
quality: 1,
|
|
4092
|
+
what: 'Length in CSS is nonzero but has no unit'
|
|
4093
|
+
},
|
|
4094
|
+
'CSS: Parse Error.': {
|
|
4095
|
+
quality: 1,
|
|
4096
|
+
what: 'Invalid CSS'
|
|
4097
|
+
},
|
|
4098
|
+
'Stray end tag “head”.': {
|
|
4099
|
+
quality: 1,
|
|
4100
|
+
what: 'Invalid closing head tag'
|
|
4101
|
+
},
|
|
4102
|
+
'^Start tag .+ seen but an element of the same type was already open.*$': {
|
|
4103
|
+
quality: 1,
|
|
4104
|
+
what: 'Element is invalidly a descendant of another such element'
|
|
4105
|
+
},
|
|
4106
|
+
'^End tag .+ violates nesting rules.*$': {
|
|
4107
|
+
quality: 1,
|
|
4108
|
+
what: 'End tag violates nesting rules'
|
|
4109
|
+
},
|
|
4110
|
+
'^Element .+ not allowed as child of element .+ in this context.*$': {
|
|
4111
|
+
quality: 1,
|
|
4112
|
+
what: 'Element not allowed as a child of its parent here'
|
|
4113
|
+
}
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
4116
|
+
},
|
|
4117
|
+
encodingBad: {
|
|
4118
|
+
weight: 4,
|
|
4119
|
+
packages: {
|
|
4120
|
+
nuVal: {
|
|
4121
|
+
'Document uses the Unicode Private Use Area(s), which should not be used in publicly exchanged documents. (Charmod C073)': {
|
|
4122
|
+
quality: 1,
|
|
4123
|
+
what: 'Page includes a Unicode PUA character'
|
|
4124
|
+
}
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
},
|
|
4128
|
+
fatalError: {
|
|
4129
|
+
weight: 50,
|
|
4130
|
+
packages: {
|
|
4131
|
+
nuVal: {
|
|
4132
|
+
'Cannot recover after last error. Any further errors will be ignored.': {
|
|
4133
|
+
quality: 1,
|
|
4134
|
+
what: 'Testing was interrupted by a fatal error'
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
}
|
|
4139
|
+
};
|
|
4140
|
+
|
|
4141
|
+
// VARIABLES
|
|
4142
|
+
|
|
4143
|
+
let packageDetails = {};
|
|
4144
|
+
let groupDetails = {};
|
|
4145
|
+
let summary = {};
|
|
4146
|
+
let preventionScores = {};
|
|
4147
|
+
|
|
4148
|
+
// FUNCTIONS
|
|
4149
|
+
|
|
4150
|
+
// Initialize the variables.
|
|
4151
|
+
const init = () => {
|
|
4152
|
+
packageDetails = {};
|
|
4153
|
+
groupDetails = {
|
|
4154
|
+
groups: {},
|
|
4155
|
+
solos: {}
|
|
4156
|
+
};
|
|
4157
|
+
summary = {
|
|
4158
|
+
total: 0,
|
|
4159
|
+
log: 0,
|
|
4160
|
+
preventions: 0,
|
|
4161
|
+
solos: 0,
|
|
4162
|
+
groups: []
|
|
4163
|
+
};
|
|
4164
|
+
preventionScores = {};
|
|
4165
|
+
};
|
|
4166
|
+
|
|
4167
|
+
// Adds a score to the package details.
|
|
4168
|
+
const addDetail = (actWhich, testID, addition = 1) => {
|
|
4169
|
+
if (addition) {
|
|
4170
|
+
if (!packageDetails[actWhich]) {
|
|
4171
|
+
packageDetails[actWhich] = {};
|
|
4172
|
+
}
|
|
4173
|
+
if (!packageDetails[actWhich][testID]) {
|
|
4174
|
+
packageDetails[actWhich][testID] = 0;
|
|
4175
|
+
}
|
|
4176
|
+
packageDetails[actWhich][testID] += Math.round(addition);
|
|
4177
|
+
}
|
|
4178
|
+
};
|
|
4179
|
+
// Scores a report.
|
|
4180
|
+
exports.scorer = async report => {
|
|
4181
|
+
// Initialize the variables.
|
|
4182
|
+
init();
|
|
4183
|
+
// If there are any acts in the report:
|
|
4184
|
+
const {acts} = report;
|
|
4185
|
+
if (Array.isArray(acts)) {
|
|
4186
|
+
// If any of them are test acts:
|
|
4187
|
+
const testActs = acts.filter(act => act.type === 'test');
|
|
4188
|
+
if (testActs.length) {
|
|
4189
|
+
// For each test act:
|
|
4190
|
+
testActs.forEach(test => {
|
|
4191
|
+
const {which} = test;
|
|
4192
|
+
// Add scores to the package details.
|
|
4193
|
+
if (which === 'alfa') {
|
|
4194
|
+
const issues = test.result && test.result.items;
|
|
4195
|
+
if (issues && Array.isArray(issues)) {
|
|
4196
|
+
issues.forEach(issue => {
|
|
4197
|
+
const {verdict, rule} = issue;
|
|
4198
|
+
if (verdict && rule) {
|
|
4199
|
+
const {ruleID} = rule;
|
|
4200
|
+
if (ruleID) {
|
|
4201
|
+
// Add 4 per failure, 1 per warning (“cantTell”).
|
|
4202
|
+
addDetail(which, ruleID, verdict === 'failed' ? 4 : 1);
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
else if (which === 'axe') {
|
|
4209
|
+
const impactScores = {
|
|
4210
|
+
minor: 1,
|
|
4211
|
+
moderate: 2,
|
|
4212
|
+
serious: 3,
|
|
4213
|
+
critical: 4
|
|
4214
|
+
};
|
|
4215
|
+
const tests = test.result && test.result.details;
|
|
4216
|
+
if (tests) {
|
|
4217
|
+
const warnings = tests.incomplete;
|
|
4218
|
+
const {violations} = tests;
|
|
4219
|
+
[[warnings, 0.25], [violations, 1]].forEach(issueClass => {
|
|
4220
|
+
if (issueClass[0] && Array.isArray(issueClass[0])) {
|
|
4221
|
+
issueClass[0].forEach(issueType => {
|
|
4222
|
+
const {id, nodes} = issueType;
|
|
4223
|
+
if (id && nodes && Array.isArray(nodes)) {
|
|
4224
|
+
nodes.forEach(node => {
|
|
4225
|
+
const {impact} = node;
|
|
4226
|
+
if (impact) {
|
|
4227
|
+
// Add the impact score for a violation or 25% of it for a warning.
|
|
4228
|
+
addDetail(which, id, issueClass[1] * impactScores[impact]);
|
|
4229
|
+
}
|
|
4230
|
+
});
|
|
4231
|
+
}
|
|
4232
|
+
});
|
|
4233
|
+
}
|
|
4234
|
+
});
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
else if (which === 'continuum') {
|
|
4238
|
+
const issues = test.result;
|
|
4239
|
+
if (issues && Array.isArray(issues)) {
|
|
4240
|
+
issues.forEach(issue => {
|
|
4241
|
+
// Add 4 per violation.
|
|
4242
|
+
addDetail(which, issue.engineTestId, 4);
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
else if (which === 'htmlcs') {
|
|
4247
|
+
const issues = test.result;
|
|
4248
|
+
if (issues) {
|
|
4249
|
+
['Error', 'Warning'].forEach(issueClassName => {
|
|
4250
|
+
const classData = issues[issueClassName];
|
|
4251
|
+
if (classData) {
|
|
4252
|
+
const issueTypes = Object.keys(classData);
|
|
4253
|
+
issueTypes.forEach(issueTypeName => {
|
|
4254
|
+
const issueArrays = Object.values(classData[issueTypeName]);
|
|
4255
|
+
const issueCount = issueArrays.reduce((count, array) => count + array.length, 0);
|
|
4256
|
+
const classCode = issueClassName[0].toLowerCase();
|
|
4257
|
+
const code = `${classCode}:${issueTypeName}`;
|
|
4258
|
+
// Add 4 per error, 1 per warning.
|
|
4259
|
+
const weight = classCode === 'e' ? 4 : 1;
|
|
4260
|
+
addDetail(which, code, weight * issueCount);
|
|
4261
|
+
});
|
|
4262
|
+
}
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
else if (which === 'ibm') {
|
|
4267
|
+
const {result} = test;
|
|
4268
|
+
const {content, url} = result;
|
|
4269
|
+
if (content && url) {
|
|
4270
|
+
let preferredMode = 'content';
|
|
4271
|
+
if (
|
|
4272
|
+
content.error ||
|
|
4273
|
+
(content.totals &&
|
|
4274
|
+
content.totals.violation &&
|
|
4275
|
+
url.totals &&
|
|
4276
|
+
url.totals.violation &&
|
|
4277
|
+
url.totals.violation > content.totals.violation)
|
|
4278
|
+
) {
|
|
4279
|
+
preferredMode = 'url';
|
|
4280
|
+
}
|
|
4281
|
+
const {items} = result[preferredMode];
|
|
4282
|
+
if (items && Array.isArray(items)) {
|
|
4283
|
+
items.forEach(issue => {
|
|
4284
|
+
const {ruleId, level} = issue;
|
|
4285
|
+
if (ruleId && level) {
|
|
4286
|
+
// Add 4 per violation, 1 per warning (“recommendation”).
|
|
4287
|
+
addDetail(which, ruleId, level === 'violation' ? 4 : 1);
|
|
4288
|
+
}
|
|
4289
|
+
});
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
else if (which === 'nuVal') {
|
|
4294
|
+
const issues = test.result && test.result.messages;
|
|
4295
|
+
if (issues) {
|
|
4296
|
+
issues.forEach(issue => {
|
|
4297
|
+
// Add 4 per error, 1 per warning.
|
|
4298
|
+
const weight = issue.type === 'error' ? 4 : 1;
|
|
4299
|
+
addDetail(which, issue.message, weight);
|
|
4300
|
+
});
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
else if (which === 'tenon') {
|
|
4304
|
+
const issues =
|
|
4305
|
+
test.result && test.result.data && test.result.data.resultSet;
|
|
4306
|
+
if (issues && Array.isArray(issues)) {
|
|
4307
|
+
issues.forEach(issue => {
|
|
4308
|
+
const {tID, priority, certainty} = issue;
|
|
4309
|
+
if (tID && priority && certainty) {
|
|
4310
|
+
// Add 4 per issue if certainty and priority 100, less if less.
|
|
4311
|
+
addDetail(which, tID, certainty * priority / 2500);
|
|
4312
|
+
}
|
|
4313
|
+
});
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
else if (which === 'wave') {
|
|
4317
|
+
const classScores = {
|
|
4318
|
+
error: 4,
|
|
4319
|
+
contrast: 3,
|
|
4320
|
+
alert: 1
|
|
4321
|
+
};
|
|
4322
|
+
const issueClasses = test.result && test.result.categories;
|
|
4323
|
+
if (issueClasses) {
|
|
4324
|
+
['error', 'contrast', 'alert'].forEach(issueClass => {
|
|
4325
|
+
const {items} = issueClasses[issueClass];
|
|
4326
|
+
if (items) {
|
|
4327
|
+
const testIDs = Object.keys(items);
|
|
4328
|
+
if (testIDs.length) {
|
|
4329
|
+
testIDs.forEach(testID => {
|
|
4330
|
+
const {count} = items[testID];
|
|
4331
|
+
if (count) {
|
|
4332
|
+
// Add 4 per error, 3 per contrast error, 1 per warning (“alert”).
|
|
4333
|
+
addDetail(
|
|
4334
|
+
which, `${issueClass[0]}:${testID}`, count * classScores[issueClass]
|
|
4335
|
+
);
|
|
4336
|
+
}
|
|
4337
|
+
});
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
});
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
else if (which === 'bulk') {
|
|
4344
|
+
const count = test.result && test.result.visibleElements;
|
|
4345
|
+
if (typeof count === 'number') {
|
|
4346
|
+
// Add 1 per 300 visible elements beyond 300.
|
|
4347
|
+
addDetail('testaro', which, Math.max(0, count / 300 - 1));
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
else if (which === 'embAc') {
|
|
4351
|
+
const issueCounts = test.result && test.result.totals;
|
|
4352
|
+
if (issueCounts) {
|
|
4353
|
+
const counts = Object.values(issueCounts);
|
|
4354
|
+
const total = counts.reduce((sum, current) => sum + current);
|
|
4355
|
+
// Add 3 per embedded element.
|
|
4356
|
+
addDetail('testaro', which, 3 * total);
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
else if (which === 'focAll') {
|
|
4360
|
+
const discrepancy = test.result && test.result.discrepancy;
|
|
4361
|
+
if (discrepancy) {
|
|
4362
|
+
addDetail('testaro', which, 2 * Math.abs(discrepancy));
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
else if (which === 'focInd') {
|
|
4366
|
+
const issueTypes =
|
|
4367
|
+
test.result && test.result.totals && test.result.totals.types;
|
|
4368
|
+
if (issueTypes) {
|
|
4369
|
+
const missingCount = issueTypes.indicatorMissing
|
|
4370
|
+
&& issueTypes.indicatorMissing.total
|
|
4371
|
+
|| 0;
|
|
4372
|
+
const badCount = issueTypes.nonOutlinePresent
|
|
4373
|
+
&& issueTypes.nonOutlinePresent.total
|
|
4374
|
+
|| 0;
|
|
4375
|
+
// Add 3 per missing, 1 per non-outline focus indicator.
|
|
4376
|
+
addDetail('testaro', which, badCount + 3 * missingCount);
|
|
4377
|
+
}
|
|
4378
|
+
}
|
|
4379
|
+
else if (which === 'focOp') {
|
|
4380
|
+
const issueTypes =
|
|
4381
|
+
test.result && test.result.totals && test.result.totals.types;
|
|
4382
|
+
if (issueTypes) {
|
|
4383
|
+
const noOpCount = issueTypes.onlyFocusable && issueTypes.onlyFocusable.total || 0;
|
|
4384
|
+
const noFocCount = issueTypes.onlyOperable && issueTypes.onlyOperable.total || 0;
|
|
4385
|
+
// Add 2 per unfocusable, 0.5 per inoperable element.
|
|
4386
|
+
addDetail('testaro', which, 2 * noFocCount + 0.5 * noOpCount);
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
else if (which === 'hover') {
|
|
4390
|
+
const issues = test.result && test.result.totals;
|
|
4391
|
+
if (issues) {
|
|
4392
|
+
const {
|
|
4393
|
+
impactTriggers,
|
|
4394
|
+
additions,
|
|
4395
|
+
removals,
|
|
4396
|
+
opacityChanges,
|
|
4397
|
+
opacityImpact,
|
|
4398
|
+
unhoverables
|
|
4399
|
+
} = issues;
|
|
4400
|
+
// Add score with weights on hover-impact types.
|
|
4401
|
+
const score = 2 * impactTriggers
|
|
4402
|
+
+ 0.3 * additions
|
|
4403
|
+
+ removals
|
|
4404
|
+
+ 0.2 * opacityChanges
|
|
4405
|
+
+ 0.1 * opacityImpact
|
|
4406
|
+
+ unhoverables;
|
|
4407
|
+
if (score) {
|
|
4408
|
+
addDetail('testaro', which, score);
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
else if (which === 'labClash') {
|
|
4413
|
+
const mislabeledCount = test.result
|
|
4414
|
+
&& test.result.totals
|
|
4415
|
+
&& test.result.totals.mislabeled
|
|
4416
|
+
|| 0;
|
|
4417
|
+
// Add 1 per element with conflicting labels (ignoring unlabeled elements).
|
|
4418
|
+
addDetail('testaro', which, mislabeledCount);
|
|
4419
|
+
}
|
|
4420
|
+
else if (which === 'linkUl') {
|
|
4421
|
+
const totals = test.result && test.result.totals && test.result.totals.adjacent;
|
|
4422
|
+
if (totals) {
|
|
4423
|
+
const nonUl = totals.total - totals.underlined || 0;
|
|
4424
|
+
// Add 2 per non-underlined adjacent link.
|
|
4425
|
+
addDetail('testaro', which, 2 * nonUl);
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
else if (which === 'menuNav') {
|
|
4429
|
+
const issueCount = test.result
|
|
4430
|
+
&& test.result.totals
|
|
4431
|
+
&& test.result.totals.navigations
|
|
4432
|
+
&& test.result.totals.navigations.all
|
|
4433
|
+
&& test.result.totals.navigations.all.incorrect
|
|
4434
|
+
|| 0;
|
|
4435
|
+
// Add 2 per defect.
|
|
4436
|
+
addDetail('testaro', which, 2 * issueCount);
|
|
4437
|
+
}
|
|
4438
|
+
else if (which === 'motion') {
|
|
4439
|
+
const data = test.result;
|
|
4440
|
+
if (data) {
|
|
4441
|
+
const {
|
|
4442
|
+
meanLocalRatio,
|
|
4443
|
+
maxLocalRatio,
|
|
4444
|
+
globalRatio,
|
|
4445
|
+
meanPixelChange,
|
|
4446
|
+
maxPixelChange,
|
|
4447
|
+
changeFrequency
|
|
4448
|
+
} = data;
|
|
4449
|
+
const score = 2 * (meanLocalRatio - 1)
|
|
4450
|
+
+ (maxLocalRatio - 1)
|
|
4451
|
+
+ globalRatio - 1
|
|
4452
|
+
+ meanPixelChange / 10000
|
|
4453
|
+
+ maxPixelChange / 25000
|
|
4454
|
+
+ 3 * changeFrequency
|
|
4455
|
+
|| 0;
|
|
4456
|
+
addDetail('testaro', which, score);
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
else if (which === 'radioSet') {
|
|
4460
|
+
const totals = test.result && test.result.totals;
|
|
4461
|
+
if (totals) {
|
|
4462
|
+
const {total, inSet} = totals;
|
|
4463
|
+
const score = total - inSet || 0;
|
|
4464
|
+
// Add 1 per misgrouped radio button.
|
|
4465
|
+
addDetail('testaro', which, score);
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
else if (which === 'role') {
|
|
4469
|
+
const badCount = test.result && test.result.badRoleElements || 0;
|
|
4470
|
+
const redundantCount = test.result && test.result.redundantRoleElements || 0;
|
|
4471
|
+
// Add 2 per bad role and 1 per redundant role.
|
|
4472
|
+
addDetail('testaro', which, 2 * badCount + redundantCount);
|
|
4473
|
+
}
|
|
4474
|
+
else if (which === 'styleDiff') {
|
|
4475
|
+
const totals = test.result && test.result.totals;
|
|
4476
|
+
if (totals) {
|
|
4477
|
+
let score = 0;
|
|
4478
|
+
// For each element type that has any style diversity:
|
|
4479
|
+
Object.values(totals).forEach(typeData => {
|
|
4480
|
+
const {total, subtotals} = typeData;
|
|
4481
|
+
if (subtotals) {
|
|
4482
|
+
const styleCount = subtotals.length;
|
|
4483
|
+
const plurality = subtotals[0];
|
|
4484
|
+
const minorities = total - plurality;
|
|
4485
|
+
// Add 1 per style, 0.2 per element with any nonplurality style.
|
|
4486
|
+
score += styleCount + 0.2 * minorities;
|
|
4487
|
+
}
|
|
4488
|
+
});
|
|
4489
|
+
addDetail('testaro', which, score);
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
else if (which === 'tabNav') {
|
|
4493
|
+
const issueCount = test.result
|
|
4494
|
+
&& test.result.totals
|
|
4495
|
+
&& test.result.totals.navigations
|
|
4496
|
+
&& test.result.totals.navigations.all
|
|
4497
|
+
&& test.result.totals.navigations.all.incorrect
|
|
4498
|
+
|| 0;
|
|
4499
|
+
// Add 2 per defect.
|
|
4500
|
+
addDetail('testaro', which, 2 * issueCount);
|
|
4501
|
+
}
|
|
4502
|
+
else if (which === 'zIndex') {
|
|
4503
|
+
const issueCount = test.result && test.result.totals && test.result.totals.total || 0;
|
|
4504
|
+
// Add 1 per non-auto zIndex.
|
|
4505
|
+
addDetail('testaro', which, issueCount);
|
|
4506
|
+
}
|
|
4507
|
+
});
|
|
4508
|
+
// Get the prevention scores and add them to the summary.
|
|
4509
|
+
const actsPrevented = testActs.filter(test => test.result.prevented);
|
|
4510
|
+
actsPrevented.forEach(act => {
|
|
4511
|
+
if (otherPackages.includes(act.which)) {
|
|
4512
|
+
preventionScores[act.which] = preventionWeights.other;
|
|
4513
|
+
}
|
|
4514
|
+
else {
|
|
4515
|
+
preventionScores[`testaro-${act.which}`] = preventionWeights.testaro;
|
|
4516
|
+
}
|
|
4517
|
+
});
|
|
4518
|
+
const preventionScore = Object.values(preventionScores).reduce(
|
|
4519
|
+
(sum, current) => sum + current,
|
|
4520
|
+
0
|
|
4521
|
+
);
|
|
4522
|
+
const roundedScore = Math.round(preventionScore);
|
|
4523
|
+
summary.preventions = roundedScore;
|
|
4524
|
+
summary.total += roundedScore;
|
|
4525
|
+
// Reorganize the group data.
|
|
4526
|
+
const testGroups = {
|
|
4527
|
+
testaro: {},
|
|
4528
|
+
alfa: {},
|
|
4529
|
+
axe: {},
|
|
4530
|
+
continuum: {},
|
|
4531
|
+
htmlcs: {},
|
|
4532
|
+
ibm: {},
|
|
4533
|
+
nuVal: {},
|
|
4534
|
+
tenon: {},
|
|
4535
|
+
wave: {}
|
|
4536
|
+
};
|
|
4537
|
+
Object.keys(groups).forEach(groupName => {
|
|
4538
|
+
Object.keys(groups[groupName].packages).forEach(packageName => {
|
|
4539
|
+
Object.keys(groups[groupName].packages[packageName]).forEach(testID => {
|
|
4540
|
+
testGroups[packageName][testID] = groupName;
|
|
4541
|
+
});
|
|
4542
|
+
});
|
|
4543
|
+
});
|
|
4544
|
+
// Populate the group details with group and solo test scores.
|
|
4545
|
+
// For each package with any scores:
|
|
4546
|
+
Object.keys(packageDetails).forEach(packageName => {
|
|
4547
|
+
const matchers = testMatchers[packageName];
|
|
4548
|
+
let testClass = '';
|
|
4549
|
+
// For each test with any scores in the package:
|
|
4550
|
+
Object.keys(packageDetails[packageName]).forEach(testID => {
|
|
4551
|
+
// Determine whether the test is in a group.
|
|
4552
|
+
let groupName = testGroups[packageName][testID];
|
|
4553
|
+
// If not:
|
|
4554
|
+
if (! groupName) {
|
|
4555
|
+
// Determine whether the package has test classes and the class is in a group.
|
|
4556
|
+
testClass = matchers && matchers.find(matcher => matcher.test(testID));
|
|
4557
|
+
if (testClass) {
|
|
4558
|
+
testID = testClass.source;
|
|
4559
|
+
groupName = testGroups[packageName][testID];
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
// If the test or its class is in a group:
|
|
4563
|
+
if (groupName) {
|
|
4564
|
+
// Determine the preweighted or group-weighted score.
|
|
4565
|
+
if (! groupDetails.groups[groupName]) {
|
|
4566
|
+
groupDetails.groups[groupName] = {};
|
|
4567
|
+
}
|
|
4568
|
+
if (! groupDetails.groups[groupName][packageName]) {
|
|
4569
|
+
groupDetails.groups[groupName][packageName] = {};
|
|
4570
|
+
}
|
|
4571
|
+
let weightedScore = packageDetails[packageName][testID];
|
|
4572
|
+
if (!preWeightedPackages.includes(groupName)) {
|
|
4573
|
+
weightedScore *= groups[groupName].weight / 4;
|
|
4574
|
+
}
|
|
4575
|
+
// Adjust the score for the quality of the test.
|
|
4576
|
+
weightedScore *= groups[groupName].packages[packageName][testID].quality;
|
|
4577
|
+
// Round the score, but not to less than 1.
|
|
4578
|
+
const roundedScore = Math.max(Math.round(weightedScore), 1);
|
|
4579
|
+
// Add the rounded score and the test description to the group details.
|
|
4580
|
+
groupDetails.groups[groupName][packageName][testID] = {
|
|
4581
|
+
score: roundedScore,
|
|
4582
|
+
what: groups[groupName].packages[packageName][testID].what
|
|
4583
|
+
};
|
|
4584
|
+
}
|
|
4585
|
+
// Otherwise, if the package has varying test names and the test belongs to a class:
|
|
4586
|
+
else if (matchers && (testCode = matchers.find(matcher => matcher.test(testID)))) {
|
|
4587
|
+
|
|
4588
|
+
}
|
|
4589
|
+
// Otherwise, i.e. if the test is solo:
|
|
4590
|
+
else {
|
|
4591
|
+
if (! groupDetails.solos[packageName]) {
|
|
4592
|
+
groupDetails.solos[packageName] = {};
|
|
4593
|
+
}
|
|
4594
|
+
const roundedScore = Math.round(packageDetails[packageName][testID]);
|
|
4595
|
+
groupDetails.solos[packageName][testID] = roundedScore;
|
|
4596
|
+
}
|
|
4597
|
+
});
|
|
4598
|
+
});
|
|
4599
|
+
// Determine the group scores and add them to the summary.
|
|
4600
|
+
const groupNames = Object.keys(groupDetails.groups);
|
|
4601
|
+
const {absolute, largest, smaller} = groupWeights;
|
|
4602
|
+
// For each group with any scores:
|
|
4603
|
+
groupNames.forEach(groupName => {
|
|
4604
|
+
const scores = [];
|
|
4605
|
+
// For each package with any scores in the group:
|
|
4606
|
+
const groupPackageData = Object.values(groupDetails.groups[groupName]);
|
|
4607
|
+
groupPackageData.forEach(packageObj => {
|
|
4608
|
+
// Get the sum of the scores of the tests of the package in the group.
|
|
4609
|
+
const scoreSum = Object.values(packageObj).reduce(
|
|
4610
|
+
(sum, current) => sum + current.score,
|
|
4611
|
+
0
|
|
4612
|
+
);
|
|
4613
|
+
// Add the sum to the list of package scores in the group.
|
|
4614
|
+
scores.push(scoreSum);
|
|
4615
|
+
});
|
|
4616
|
+
// Sort the scores in descending order.
|
|
4617
|
+
scores.sort((a, b) => b - a);
|
|
4618
|
+
// Compute the sum of the absolute score and the weighted largest and other scores.
|
|
4619
|
+
const groupScore = absolute
|
|
4620
|
+
+ largest * scores[0]
|
|
4621
|
+
+ smaller * scores.slice(1).reduce((sum, current) => sum + current, 0);
|
|
4622
|
+
const roundedGroupScore = Math.round(groupScore);
|
|
4623
|
+
summary.groups.push({
|
|
4624
|
+
groupName,
|
|
4625
|
+
score: roundedGroupScore
|
|
4626
|
+
});
|
|
4627
|
+
summary.total += roundedGroupScore;
|
|
4628
|
+
});
|
|
4629
|
+
summary.groups.sort((a, b) => b.score - a.score);
|
|
4630
|
+
// Determine the solo score and add it to the summary.
|
|
4631
|
+
const soloPackageNames = Object.keys(groupDetails.solos);
|
|
4632
|
+
soloPackageNames.forEach(packageName => {
|
|
4633
|
+
const testIDs = Object.keys(groupDetails.solos[packageName]);
|
|
4634
|
+
testIDs.forEach(testID => {
|
|
4635
|
+
const score = soloWeight * groupDetails.solos[packageName][testID];
|
|
4636
|
+
summary.solos += score;
|
|
4637
|
+
summary.total += score;
|
|
4638
|
+
});
|
|
4639
|
+
});
|
|
4640
|
+
summary.solos = Math.round(summary.solos);
|
|
4641
|
+
summary.total = Math.round(summary.total);
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
// Get the log score.
|
|
4645
|
+
const logScore = logWeights.logCount * report.logCount
|
|
4646
|
+
+ logWeights.logSize * report.logSize +
|
|
4647
|
+
+ logWeights.errorLogCount * report.errorLogCount
|
|
4648
|
+
+ logWeights.errorLogSize * report.errorLogSize
|
|
4649
|
+
+ logWeights.prohibitedCount * report.prohibitedCount +
|
|
4650
|
+
+ logWeights.visitTimeoutCount * report.visitTimeoutCount +
|
|
4651
|
+
+ logWeights.visitRejectionCount * report.visitRejectionCount;
|
|
4652
|
+
const roundedLogScore = Math.round(logScore);
|
|
4653
|
+
summary.log = roundedLogScore;
|
|
4654
|
+
summary.total += roundedLogScore;
|
|
4655
|
+
// Add the score facts to the report.
|
|
4656
|
+
report.score = {
|
|
4657
|
+
scoreProcID,
|
|
4658
|
+
logWeights,
|
|
4659
|
+
soloWeight,
|
|
4660
|
+
groupWeights,
|
|
4661
|
+
preventionWeights,
|
|
4662
|
+
packageDetails,
|
|
4663
|
+
groupDetails,
|
|
4664
|
+
preventionScores,
|
|
4665
|
+
summary
|
|
4666
|
+
};
|
|
4667
|
+
};
|