testilo 3.8.1 → 3.8.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/digest/dp13a/index.html +78 -0
- package/procs/digest/dp13a/index.js +126 -0
- package/procs/score/sp12b.js +321 -11
package/package.json
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<!DOCTYPE HTML>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<meta name="author" content="Testilo">
|
|
7
|
+
<meta name="creator" content="Testilo">
|
|
8
|
+
<meta name="publisher" name="Testilo">
|
|
9
|
+
<meta name="description" content="report of accessibility testing of a web page">
|
|
10
|
+
<meta name="keywords" content="accessibility a11y web testing">
|
|
11
|
+
<title>Accessibility test digest</title>
|
|
12
|
+
<link rel="icon" href="favicon.png">
|
|
13
|
+
<link rel="stylesheet" href="style.css">
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<main>
|
|
17
|
+
<header>
|
|
18
|
+
<h1>Accessibility test digest</h1>
|
|
19
|
+
<h2>Synopsis</h2>
|
|
20
|
+
<div id="synopsis">
|
|
21
|
+
<p><strong>Page</strong>: __org__</p>
|
|
22
|
+
<p><strong>URL</strong>: __url__</p>
|
|
23
|
+
<p><strong>Score</strong>: __totalScore__</p>
|
|
24
|
+
<p><strong>Tested by</strong>: Testaro, procedure <code>tp13</code></p>
|
|
25
|
+
<p><strong>Scored by</strong>: Testilo, procedure <code>sp12b</code></p>
|
|
26
|
+
<p><strong>Digested by</strong>: Testilo, procedure <code>dp13a</code></p>
|
|
27
|
+
</div>
|
|
28
|
+
</header>
|
|
29
|
+
<h2>Introduction</h2>
|
|
30
|
+
<p>The <a href="https://www.npmjs.com/package/testaro">Testaro</a> application used its <code>tp13</code> testing procedure to test the <a href="https://www.w3.org/WAI/fundamentals/accessibility-intro/"><dfn>accessibility</dfn></a> (barrier-free design and coding) of the __org__ web page at <a href="__url__">__url__</a> on __dateSlash__. The procedure performed 808 tests. Of these, 16 are custom tests defined by Testaro, and the others belong to these six other packages (programs that perform collections of tests):</p>
|
|
31
|
+
<ul>
|
|
32
|
+
<li><a href="https://github.com/Siteimprove/alfa">Alfa</a> by Siteimprove</li>
|
|
33
|
+
<li><a href="https://www.npmjs.com/package/axe-core">Axe-core</a> by Deque</li>
|
|
34
|
+
<li>
|
|
35
|
+
<a href="https://www.npmjs.com/package/html_codesniffer">HTML CodeSniffer</a> by Squiz Labs
|
|
36
|
+
</li>
|
|
37
|
+
<li><a href="https://github.com/IBMa/equal-access">Equal Access</a> by IBM</li>
|
|
38
|
+
<li><a href="https://tenon.io/documentation/apiv2.php">Tenon</a> by Level Access</li>
|
|
39
|
+
<li><a href="https://wave.webaim.org/api/">WAVE</a> by WebAIM</li>
|
|
40
|
+
</ul>
|
|
41
|
+
<p>Testaro produced a report enumerating the test results.</p>
|
|
42
|
+
<p><a href="https://www.npmjs.com/package/testilo">Testilo</a> processed the report and used the <code>sp12b</code> scoring procedure to compute partial and total scores for the page. The total score is __totalScore__ (where 0 is the best possible score). The scored report is appended below.</p>
|
|
43
|
+
<p>Finally, Testilo used procedure <code>dp13a</code> to produce this digest, briefly explaining how <code>sp12b</code> computed the scores.</p>
|
|
44
|
+
<h2>Score summary</h2>
|
|
45
|
+
<table class="allBorder secondCellRight">
|
|
46
|
+
<caption>Score components</caption>
|
|
47
|
+
<tbody class="headersLeft">
|
|
48
|
+
__scoreRows__
|
|
49
|
+
</tbody>
|
|
50
|
+
</table>
|
|
51
|
+
<h2>Issue summary</h2>
|
|
52
|
+
<h3>Special issues</h3>
|
|
53
|
+
__specialSummary__
|
|
54
|
+
<h3>Classified issues</h3>
|
|
55
|
+
__groupSummary__
|
|
56
|
+
<h2>Discussion</h2>
|
|
57
|
+
<p>Although there are widely accepted <a href="https://www.w3.org/WAI/standards-guidelines/">accessibility standards</a>, there is no unanimity about how to define, test, and quantify accessibility. The failures reported in this digest merit investigation as potential opportunities for improved accessibility. Investigation may lead you to conclude that some of the reported failures do not actually harm accessibility. Conversely, some substantial accessibility faults can escape detection by any of these tests. You may question the attempt to assign an accessibility score to a web page, or you may prefer weightings and formulas different from those used by <code>sp12b</code>. You can modify and extend Testaro and Testilo to fit other theories and priorities.</p>
|
|
58
|
+
<p>Here, in brief, is how <code>sp12a</code> computes a score for a page.</p>
|
|
59
|
+
<ul>
|
|
60
|
+
<li>It finds all the defects and warnings (let’s call them <q>issues</q>) recorded in the report.</li>
|
|
61
|
+
<li>It classifies them according to type. For example, a link that looks like the text around it is one issue category, while a video that has no captions is another issue category.</li>
|
|
62
|
+
<li>It also classifies the issues according to severity. For example, an issue that prevents a transaction is more severe than an issue that only complicates the transaction, and a warning about a possible issue is less severe than a definite finding of an issue. (Some packages rate the severity of each issue; for the other packages, <code>sp12b</code> assigns a severity weight to the issue type and uses that weight.)</li>
|
|
63
|
+
<li>It assigns quality ratings to particular tests that are judged abnormally reliable or unreliable.</li>
|
|
64
|
+
<li>It assigns a score to each issue reported by each test of each package.</li>
|
|
65
|
+
<li>It aggregates the issue scores, weighting them by severity, test quality, and redundancy. Redundancy occurs, and causes downweighting, when two or more packages contain tests that are designed to discover the same or mostly the same issues. So the score for a category is not simply the sum of the scores of the tests in that category.</li>
|
|
66
|
+
<li>It assigns a score for issues in the page logged by the browser.</li>
|
|
67
|
+
<li>It assigns an estimated score each time the page prevents one of the packages or one of the Testaro tests from being run on the page.</li>
|
|
68
|
+
<li>It adds the scores together to obtain a total score.</li>
|
|
69
|
+
</ul>
|
|
70
|
+
<p>The precise rules of <code>sp12b</code> are found in the <a href="https://github.com/jrpool/testilo/blob/main/procs/score/sp12b.js">code itself</a>.</p>
|
|
71
|
+
<h2>Report</h2>
|
|
72
|
+
<pre>__report__</pre>
|
|
73
|
+
<footer>
|
|
74
|
+
<p class="date">Produced <time itemprop="datePublished" datetime="__dateISO__">__dateSlash__</time></p>
|
|
75
|
+
</footer>
|
|
76
|
+
</main>
|
|
77
|
+
</body>
|
|
78
|
+
</html>
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
index: digester for scoring procedure sp12b.
|
|
3
|
+
Creator of parameters for substitution into index.html.
|
|
4
|
+
Usage example for selected files: node digest dp12b 35k1r
|
|
5
|
+
Usage example for all files in REPORTDIR_SCORED: node digest dp12b
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// CONSTANTS
|
|
9
|
+
|
|
10
|
+
// Newlines with indentations.
|
|
11
|
+
const joiner = '\n ';
|
|
12
|
+
const innerJoiner = '\n ';
|
|
13
|
+
const specialMessages = {
|
|
14
|
+
log: 'This is based on the amount of browser error logging and miscellaneous logging during the tests.',
|
|
15
|
+
preventions: 'This is based on tests that the page did not allow to be run. That impedes accessibility progress and risks interfering with tools that users with disabilities need.',
|
|
16
|
+
solos: 'This is based on issues reported by unclassified tests. Details are in the report.'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// FUNCTIONS
|
|
20
|
+
|
|
21
|
+
// Makes strings HTML-safe.
|
|
22
|
+
const htmlEscape = textOrNumber => textOrNumber
|
|
23
|
+
.toString()
|
|
24
|
+
.replace(/&/g, '&')
|
|
25
|
+
.replace(/</g, '<');
|
|
26
|
+
// Gets a row of the score-summary table.
|
|
27
|
+
const getScoreRow = (component, score) => `<tr><th>${component}</th><td>${score}</td></tr>`;
|
|
28
|
+
// Gets the start of a paragraph about a special score.
|
|
29
|
+
const getSpecialPStart = (summary, scoreID) =>
|
|
30
|
+
`<p><span class="componentID">${scoreID}</span>: Score ${summary[scoreID]}.`;
|
|
31
|
+
// Adds parameters to a query for a digest.
|
|
32
|
+
exports.makeQuery = (report, query) => {
|
|
33
|
+
// Add an HTML-safe copy of the host report to the query to be appended to the digest.
|
|
34
|
+
const {script, host, score} = report;
|
|
35
|
+
const reportJSON = JSON.stringify(report, null, 2);
|
|
36
|
+
const reportJSONSafe = htmlEscape(reportJSON);
|
|
37
|
+
query.report = reportJSONSafe;
|
|
38
|
+
// Add the job data to the query.
|
|
39
|
+
query.dateISO = report.endTime.slice(0, 10);
|
|
40
|
+
query.dateSlash = query.dateISO.replace(/-/g, '/');
|
|
41
|
+
if (host && host.what && host.which) {
|
|
42
|
+
query.org = host.what;
|
|
43
|
+
query.url = host.which;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const firstURLCommand = script.commands.find(command => command.type === 'url');
|
|
47
|
+
if (firstURLCommand && firstURLCommand.what && firstURLCommand.which) {
|
|
48
|
+
query.org = firstURLCommand.what;
|
|
49
|
+
query.url = firstURLCommand.which;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.log('ERROR: host missing or invalid');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const {groupDetails, summary} = score;
|
|
57
|
+
const {total, groups} = summary;
|
|
58
|
+
if (typeof total === 'number') {
|
|
59
|
+
query.totalScore = total;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log('ERROR: missing or invalid total score');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Add the total and any special rows of the score-summary table to the query.
|
|
66
|
+
const scoreRows = [];
|
|
67
|
+
const specialComponentIDs = ['log', 'preventions', 'solos'];
|
|
68
|
+
['total'].concat(specialComponentIDs).forEach(item => {
|
|
69
|
+
if (summary[item]) {
|
|
70
|
+
scoreRows.push(getScoreRow(item, summary[item]));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
// Add the group rows of the score-summary table to the query.
|
|
74
|
+
groups.forEach(group => {
|
|
75
|
+
scoreRows.push(getScoreRow(`${group.groupName}`, group.score));
|
|
76
|
+
});
|
|
77
|
+
query.scoreRows = scoreRows.join(innerJoiner);
|
|
78
|
+
// If the score has any special components:
|
|
79
|
+
const scoredSpecialIDs = specialComponentIDs.filter(item => summary[item]);
|
|
80
|
+
if (scoredSpecialIDs.length) {
|
|
81
|
+
// Add paragraphs about them for the issue summary to the query.
|
|
82
|
+
const specialPs = [];
|
|
83
|
+
scoredSpecialIDs.forEach(id => {
|
|
84
|
+
specialPs.push(`${getSpecialPStart(summary, id)} ${specialMessages[id]}`);
|
|
85
|
+
});
|
|
86
|
+
query.specialSummary = specialPs.join(joiner);
|
|
87
|
+
}
|
|
88
|
+
// Otherwise, i.e. if the score has no special components:
|
|
89
|
+
else {
|
|
90
|
+
// Add a paragraph stating this for the issue summary to the query.
|
|
91
|
+
query.specialSummary = '<p>No special issues contributed to the score.</p>'
|
|
92
|
+
}
|
|
93
|
+
// If the score has any classified issues as components:
|
|
94
|
+
if (groups.length) {
|
|
95
|
+
// Add paragraphs about them for the special summary to the query.
|
|
96
|
+
const groupSummaryItems = [];
|
|
97
|
+
groups.forEach(group => {
|
|
98
|
+
const {groupName, score} = group;
|
|
99
|
+
const groupP = `<p><span class="componentID">${groupName}</span>: Score ${score}. Issues reported by tests in this category:</p>`;
|
|
100
|
+
const groupListItems = [];
|
|
101
|
+
const groupData = groupDetails.groups[groupName];
|
|
102
|
+
const packageIDs = Object.keys(groupData);
|
|
103
|
+
packageIDs.forEach(packageID => {
|
|
104
|
+
const testIDs = Object.keys(groupData[packageID]);
|
|
105
|
+
testIDs.forEach(testID => {
|
|
106
|
+
const testData = groupData[packageID][testID];
|
|
107
|
+
const {score, what} = testData;
|
|
108
|
+
const listItem = `<li>Package <code>${packageID}</code>, test <code>${testID}</code>, score ${score} (${what})</li>`;
|
|
109
|
+
groupListItems.push(listItem);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
const groupList = [
|
|
113
|
+
'<ul>',
|
|
114
|
+
groupListItems.join('\n '),
|
|
115
|
+
'</ul>'
|
|
116
|
+
].join(joiner);
|
|
117
|
+
groupSummaryItems.push(groupP, groupList);
|
|
118
|
+
});
|
|
119
|
+
query.groupSummary = groupSummaryItems.join(joiner);
|
|
120
|
+
}
|
|
121
|
+
// Otherwise, i.e. if the score has no classified issues as components:
|
|
122
|
+
else {
|
|
123
|
+
// Add a paragraph stating this for the group summary to the query.
|
|
124
|
+
query.groupSummary = '<p>No classified issues contributed to the score.</p>'
|
|
125
|
+
}
|
|
126
|
+
};
|
package/procs/score/sp12b.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
sp12a
|
|
3
3
|
Testilo score proc 12b
|
|
4
4
|
|
|
5
|
-
Computes scores from Testaro script tp12 and adds them to a report.
|
|
5
|
+
Computes scores from Testaro script tp12 or tp13 and adds them to a report.
|
|
6
6
|
Usage examples:
|
|
7
7
|
node score sp12b 35k1r
|
|
8
8
|
node score sp12b
|
|
@@ -135,6 +135,10 @@ const groups = {
|
|
|
135
135
|
quality: 1,
|
|
136
136
|
what: 'Email input has no accessible name'
|
|
137
137
|
},
|
|
138
|
+
'e:AA.4_1_2.H91.InputFile.Name': {
|
|
139
|
+
quality: 1,
|
|
140
|
+
what: 'File input element has no accessible name'
|
|
141
|
+
},
|
|
138
142
|
'e:AA.4_1_2.H91.InputTel.Name': {
|
|
139
143
|
quality: 1,
|
|
140
144
|
what: 'Telephone input has no accessible name'
|
|
@@ -202,6 +206,10 @@ const groups = {
|
|
|
202
206
|
'image-alt': {
|
|
203
207
|
quality: 1,
|
|
204
208
|
what: 'Image has no text alternative'
|
|
209
|
+
},
|
|
210
|
+
'role-img-alt': {
|
|
211
|
+
quality: 1,
|
|
212
|
+
what: 'Element with role img has no text alternative'
|
|
205
213
|
}
|
|
206
214
|
},
|
|
207
215
|
htmlcs: {
|
|
@@ -410,6 +418,12 @@ const groups = {
|
|
|
410
418
|
objectNoText: {
|
|
411
419
|
weight: 4,
|
|
412
420
|
packages: {
|
|
421
|
+
alfa: {
|
|
422
|
+
r63: {
|
|
423
|
+
quality: 1,
|
|
424
|
+
what: 'Object element has no accessible name'
|
|
425
|
+
}
|
|
426
|
+
},
|
|
413
427
|
axe: {
|
|
414
428
|
'object-alt': {
|
|
415
429
|
quality: 1,
|
|
@@ -436,13 +450,24 @@ const groups = {
|
|
|
436
450
|
}
|
|
437
451
|
}
|
|
438
452
|
},
|
|
453
|
+
imageMapNoText: {
|
|
454
|
+
weight: 4,
|
|
455
|
+
packages: {
|
|
456
|
+
wave: {
|
|
457
|
+
'e:alt_map_missing': {
|
|
458
|
+
quality: 1,
|
|
459
|
+
what: 'Image that has hot spots has no alt attribute'
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
},
|
|
439
464
|
imageMapAreaNoText: {
|
|
440
465
|
weight: 4,
|
|
441
466
|
packages: {
|
|
442
467
|
axe: {
|
|
443
468
|
'area-alt': {
|
|
444
469
|
quality: 1,
|
|
445
|
-
what: 'Active area
|
|
470
|
+
what: 'Active area element has no text alternative'
|
|
446
471
|
}
|
|
447
472
|
},
|
|
448
473
|
htmlcs: {
|
|
@@ -469,6 +494,28 @@ const groups = {
|
|
|
469
494
|
}
|
|
470
495
|
}
|
|
471
496
|
},
|
|
497
|
+
objectBlurKeyboardRisk: {
|
|
498
|
+
weight: 1,
|
|
499
|
+
packages: {
|
|
500
|
+
htmlcs: {
|
|
501
|
+
'w:AA.2_1_2.F10': {
|
|
502
|
+
quality: 1,
|
|
503
|
+
what: 'Applet or plugin may fail to enable moving the focus away with the keyboard'
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
keyboardAccess: {
|
|
509
|
+
weight: 4,
|
|
510
|
+
packages: {
|
|
511
|
+
tenon: {
|
|
512
|
+
180: {
|
|
513
|
+
quality: 1,
|
|
514
|
+
what: 'Element is interactive but has a negative tabindex value'
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
},
|
|
472
519
|
eventKeyboardRisk: {
|
|
473
520
|
weight: 1,
|
|
474
521
|
packages: {
|
|
@@ -476,6 +523,18 @@ const groups = {
|
|
|
476
523
|
'w:AA.2_1_1.G90': {
|
|
477
524
|
quality: 1,
|
|
478
525
|
what: 'Event handler functionality may not be available by keyboard'
|
|
526
|
+
},
|
|
527
|
+
'w:AA.2_1_1.SCR20.MouseOut': {
|
|
528
|
+
quality: 1,
|
|
529
|
+
what: 'Mousing-out functionality may not be available by keyboard'
|
|
530
|
+
},
|
|
531
|
+
'w:AA.2_1_1.SCR20.MouseOver': {
|
|
532
|
+
quality: 1,
|
|
533
|
+
what: 'Mousing-over functionality may not be available by keyboard'
|
|
534
|
+
},
|
|
535
|
+
'w:AA.2_1_1.SCR20.MouseDown': {
|
|
536
|
+
quality: 1,
|
|
537
|
+
what: 'Mousing-down functionality may not be available by keyboard'
|
|
479
538
|
}
|
|
480
539
|
},
|
|
481
540
|
wave: {
|
|
@@ -619,6 +678,28 @@ const groups = {
|
|
|
619
678
|
}
|
|
620
679
|
}
|
|
621
680
|
},
|
|
681
|
+
acronymNoTitle: {
|
|
682
|
+
weight: 4,
|
|
683
|
+
packages: {
|
|
684
|
+
tenon: {
|
|
685
|
+
117: {
|
|
686
|
+
quality: 1,
|
|
687
|
+
what: 'acronym element has no useful title value (and is deprecated; use abbr)'
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
abbreviationNoTitle: {
|
|
693
|
+
weight: 4,
|
|
694
|
+
packages: {
|
|
695
|
+
tenon: {
|
|
696
|
+
233: {
|
|
697
|
+
quality: 1,
|
|
698
|
+
what: 'abbr element is first for its abbreviation but has no useful title value'
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
},
|
|
622
703
|
pdfLink: {
|
|
623
704
|
weight: 1,
|
|
624
705
|
packages: {
|
|
@@ -655,6 +736,12 @@ const groups = {
|
|
|
655
736
|
linkTextsSame: {
|
|
656
737
|
weight: 2,
|
|
657
738
|
packages: {
|
|
739
|
+
htmlcs: {
|
|
740
|
+
'e:AA.1_1_1.H2.EG3': {
|
|
741
|
+
quality: 1,
|
|
742
|
+
what: 'alt value of the link img element duplicates the text of a link beside it'
|
|
743
|
+
}
|
|
744
|
+
},
|
|
658
745
|
tenon: {
|
|
659
746
|
98: {
|
|
660
747
|
quality: 1,
|
|
@@ -663,7 +750,7 @@ const groups = {
|
|
|
663
750
|
}
|
|
664
751
|
}
|
|
665
752
|
},
|
|
666
|
-
|
|
753
|
+
nextLinkDestinationSame: {
|
|
667
754
|
weight: 2,
|
|
668
755
|
packages: {
|
|
669
756
|
tenon: {
|
|
@@ -674,6 +761,17 @@ const groups = {
|
|
|
674
761
|
}
|
|
675
762
|
}
|
|
676
763
|
},
|
|
764
|
+
linkDestinationsSame: {
|
|
765
|
+
weight: 2,
|
|
766
|
+
packages: {
|
|
767
|
+
tenon: {
|
|
768
|
+
132: {
|
|
769
|
+
quality: 1,
|
|
770
|
+
what: 'area element has the same href as another but a different alt'
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
},
|
|
677
775
|
linkConfusionRisk: {
|
|
678
776
|
weight: 1,
|
|
679
777
|
packages: {
|
|
@@ -729,6 +827,17 @@ const groups = {
|
|
|
729
827
|
}
|
|
730
828
|
}
|
|
731
829
|
},
|
|
830
|
+
selectNavSurpriseRisk: {
|
|
831
|
+
weight: 1,
|
|
832
|
+
packages: {
|
|
833
|
+
wave: {
|
|
834
|
+
'a:javascript_jumpmenu': {
|
|
835
|
+
quality: 1,
|
|
836
|
+
what: 'selection change may navigate to another page without notice'
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
},
|
|
732
841
|
buttonNoText: {
|
|
733
842
|
weight: 4,
|
|
734
843
|
packages: {
|
|
@@ -746,6 +855,10 @@ const groups = {
|
|
|
746
855
|
'button-name': {
|
|
747
856
|
quality: 1,
|
|
748
857
|
what: 'Button has no discernible text'
|
|
858
|
+
},
|
|
859
|
+
'input-button-name': {
|
|
860
|
+
quality: 1,
|
|
861
|
+
what: 'Input button has no discernible text'
|
|
749
862
|
}
|
|
750
863
|
},
|
|
751
864
|
htmlcs: {
|
|
@@ -753,9 +866,25 @@ const groups = {
|
|
|
753
866
|
quality: 1,
|
|
754
867
|
what: 'Link with button role has no accessible name'
|
|
755
868
|
},
|
|
869
|
+
'e:AA.4_1_2.H91.Div.Name': {
|
|
870
|
+
quality: 1,
|
|
871
|
+
what: 'div element with button role has no accessible name'
|
|
872
|
+
},
|
|
756
873
|
'e:AA.4_1_2.H91.Button.Name': {
|
|
757
874
|
quality: 1,
|
|
758
875
|
what: 'Button element has no accessible name'
|
|
876
|
+
},
|
|
877
|
+
'e:AA.4_1_2.H91.Img.Name': {
|
|
878
|
+
quality: 1,
|
|
879
|
+
what: 'img element with button role has no accessible name'
|
|
880
|
+
},
|
|
881
|
+
'e:AA.4_1_2.H91.InputButton.Name': {
|
|
882
|
+
quality: 1,
|
|
883
|
+
what: 'Button input element has no accessible name'
|
|
884
|
+
},
|
|
885
|
+
'e:AA.4_1_2.H91.Span.Name': {
|
|
886
|
+
quality: 1,
|
|
887
|
+
what: 'Element with button role has no accessible name'
|
|
759
888
|
}
|
|
760
889
|
},
|
|
761
890
|
wave: {
|
|
@@ -1009,6 +1138,10 @@ const groups = {
|
|
|
1009
1138
|
}
|
|
1010
1139
|
},
|
|
1011
1140
|
axe: {
|
|
1141
|
+
'aria-roles': {
|
|
1142
|
+
quality: 1,
|
|
1143
|
+
what: 'ARIA role has an invalid value'
|
|
1144
|
+
},
|
|
1012
1145
|
'aria-allowed-role': {
|
|
1013
1146
|
quality: 1,
|
|
1014
1147
|
what: 'ARIA role is not appropriate for the element'
|
|
@@ -1045,6 +1178,23 @@ const groups = {
|
|
|
1045
1178
|
}
|
|
1046
1179
|
}
|
|
1047
1180
|
},
|
|
1181
|
+
ariaMissing: {
|
|
1182
|
+
weight: 4,
|
|
1183
|
+
packages: {
|
|
1184
|
+
alfa: {
|
|
1185
|
+
r16: {
|
|
1186
|
+
quality: 1,
|
|
1187
|
+
what: 'Element does not have all required states and properties'
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
wave: {
|
|
1191
|
+
'e:aria_reference_broken': {
|
|
1192
|
+
quality: 1,
|
|
1193
|
+
what: 'Broken ARIA reference'
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
},
|
|
1048
1198
|
ariaBadAttribute: {
|
|
1049
1199
|
weight: 4,
|
|
1050
1200
|
packages: {
|
|
@@ -1455,6 +1605,12 @@ const groups = {
|
|
|
1455
1605
|
quality: 1,
|
|
1456
1606
|
what: 'Document has no headings'
|
|
1457
1607
|
}
|
|
1608
|
+
},
|
|
1609
|
+
wave: {
|
|
1610
|
+
'a:heading_missing': {
|
|
1611
|
+
quality: 1,
|
|
1612
|
+
what: 'Page has no headings'
|
|
1613
|
+
}
|
|
1458
1614
|
}
|
|
1459
1615
|
}
|
|
1460
1616
|
},
|
|
@@ -1531,6 +1687,14 @@ const groups = {
|
|
|
1531
1687
|
'w:AA.1_3_1.H49.U': {
|
|
1532
1688
|
quality: 1,
|
|
1533
1689
|
what: 'Special text is underlined nonsemantically'
|
|
1690
|
+
},
|
|
1691
|
+
'w:AA.1_3_1.H49.Center': {
|
|
1692
|
+
quality: 1,
|
|
1693
|
+
what: 'Special text is centered nonsemantically'
|
|
1694
|
+
},
|
|
1695
|
+
'w:AA.1_3_1.H49.Font': {
|
|
1696
|
+
quality: 1,
|
|
1697
|
+
what: 'Special text is designated nonsemantically with a (deprecated) font element'
|
|
1534
1698
|
}
|
|
1535
1699
|
}
|
|
1536
1700
|
}
|
|
@@ -1675,12 +1839,24 @@ const groups = {
|
|
|
1675
1839
|
accessKeyDuplicate: {
|
|
1676
1840
|
weight: 3,
|
|
1677
1841
|
packages: {
|
|
1842
|
+
axe: {
|
|
1843
|
+
accesskeys: {
|
|
1844
|
+
quality: 1,
|
|
1845
|
+
what: 'accesskey attribute value is not unique'
|
|
1846
|
+
}
|
|
1847
|
+
},
|
|
1678
1848
|
ibm: {
|
|
1679
1849
|
'v:WCAG20_Elem_UniqueAccessKey': {
|
|
1680
1850
|
quality: 1,
|
|
1681
1851
|
what: 'Accesskey attribute value on an element is not unique for the page'
|
|
1682
1852
|
}
|
|
1683
1853
|
},
|
|
1854
|
+
tenon: {
|
|
1855
|
+
101: {
|
|
1856
|
+
quality: 1,
|
|
1857
|
+
what: 'Duplicate accesskey value'
|
|
1858
|
+
}
|
|
1859
|
+
},
|
|
1684
1860
|
wave: {
|
|
1685
1861
|
'a:accesskey': {
|
|
1686
1862
|
quality: 1,
|
|
@@ -1771,6 +1947,12 @@ const groups = {
|
|
|
1771
1947
|
tableCaption: {
|
|
1772
1948
|
weight: 1,
|
|
1773
1949
|
packages: {
|
|
1950
|
+
axe: {
|
|
1951
|
+
'table-fake-caption': {
|
|
1952
|
+
quality: 1,
|
|
1953
|
+
what: 'Data or header cells are used for a table caption instead of a caption element'
|
|
1954
|
+
}
|
|
1955
|
+
},
|
|
1774
1956
|
htmlcs: {
|
|
1775
1957
|
'w:AA.1_3_1.H39.3.NoCaption': {
|
|
1776
1958
|
quality: 1,
|
|
@@ -1779,14 +1961,59 @@ const groups = {
|
|
|
1779
1961
|
}
|
|
1780
1962
|
}
|
|
1781
1963
|
},
|
|
1782
|
-
|
|
1964
|
+
cellHeadersNotInferrable: {
|
|
1783
1965
|
weight: 4,
|
|
1966
|
+
packages: {
|
|
1967
|
+
htmlcs: {
|
|
1968
|
+
'e:AA.1_3_1.H43.HeadersRequired': {
|
|
1969
|
+
quality: 1,
|
|
1970
|
+
what: 'Complex table requires headers attributes of cells'
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
},
|
|
1975
|
+
cellHeadersAmbiguityRisk: {
|
|
1976
|
+
weight: 3,
|
|
1977
|
+
packages: {
|
|
1978
|
+
htmlcs: {
|
|
1979
|
+
'w:AA.1_3_1.H43.ScopeAmbiguous': {
|
|
1980
|
+
quality: 1,
|
|
1981
|
+
what: 'Complex table requires headers attributes of cells instead of header scopes'
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
},
|
|
1986
|
+
tableCellHeaderless: {
|
|
1987
|
+
weight: 3,
|
|
1784
1988
|
packages: {
|
|
1785
1989
|
alfa: {
|
|
1786
1990
|
r77: {
|
|
1787
1991
|
quality: 1,
|
|
1788
1992
|
what: 'Table cell has no header'
|
|
1789
1993
|
}
|
|
1994
|
+
},
|
|
1995
|
+
axe: {
|
|
1996
|
+
'td-has-header': {
|
|
1997
|
+
quality: 1,
|
|
1998
|
+
what: 'Cell in table larger than 3 by 3 has no header'
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
},
|
|
2003
|
+
tableHeaderCelless: {
|
|
2004
|
+
weight: 4,
|
|
2005
|
+
packages: {
|
|
2006
|
+
alfa: {
|
|
2007
|
+
r46: {
|
|
2008
|
+
quality: 1,
|
|
2009
|
+
what: 'Header cell is not assigned to any cell'
|
|
2010
|
+
}
|
|
2011
|
+
},
|
|
2012
|
+
axe: {
|
|
2013
|
+
'th-has-data-cells': {
|
|
2014
|
+
quality: 1,
|
|
2015
|
+
what: 'Table header refers to no cell'
|
|
2016
|
+
}
|
|
1790
2017
|
}
|
|
1791
2018
|
}
|
|
1792
2019
|
},
|
|
@@ -1835,6 +2062,17 @@ const groups = {
|
|
|
1835
2062
|
}
|
|
1836
2063
|
}
|
|
1837
2064
|
},
|
|
2065
|
+
controlLabelInvisible: {
|
|
2066
|
+
weight: 4,
|
|
2067
|
+
packages: {
|
|
2068
|
+
axe: {
|
|
2069
|
+
'label-title-only': {
|
|
2070
|
+
quality: 1,
|
|
2071
|
+
what: 'Form element has no visible label'
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
},
|
|
1838
2076
|
titleAsLabel: {
|
|
1839
2077
|
weight: 3,
|
|
1840
2078
|
packages: {
|
|
@@ -1846,7 +2084,7 @@ const groups = {
|
|
|
1846
2084
|
}
|
|
1847
2085
|
}
|
|
1848
2086
|
},
|
|
1849
|
-
|
|
2087
|
+
visibleLabelNotName: {
|
|
1850
2088
|
weight: 3,
|
|
1851
2089
|
packages: {
|
|
1852
2090
|
alfa: {
|
|
@@ -2009,7 +2247,7 @@ const groups = {
|
|
|
2009
2247
|
}
|
|
2010
2248
|
}
|
|
2011
2249
|
},
|
|
2012
|
-
|
|
2250
|
+
asideNotTop: {
|
|
2013
2251
|
weight: 2,
|
|
2014
2252
|
packages: {
|
|
2015
2253
|
axe: {
|
|
@@ -2020,7 +2258,7 @@ const groups = {
|
|
|
2020
2258
|
}
|
|
2021
2259
|
}
|
|
2022
2260
|
},
|
|
2023
|
-
|
|
2261
|
+
mainNotTop: {
|
|
2024
2262
|
weight: 2,
|
|
2025
2263
|
packages: {
|
|
2026
2264
|
axe: {
|
|
@@ -2031,7 +2269,7 @@ const groups = {
|
|
|
2031
2269
|
}
|
|
2032
2270
|
}
|
|
2033
2271
|
},
|
|
2034
|
-
|
|
2272
|
+
mainNot1: {
|
|
2035
2273
|
weight: 2,
|
|
2036
2274
|
packages: {
|
|
2037
2275
|
axe: {
|
|
@@ -2046,7 +2284,7 @@ const groups = {
|
|
|
2046
2284
|
}
|
|
2047
2285
|
}
|
|
2048
2286
|
},
|
|
2049
|
-
|
|
2287
|
+
banners: {
|
|
2050
2288
|
weight: 2,
|
|
2051
2289
|
packages: {
|
|
2052
2290
|
axe: {
|
|
@@ -2057,6 +2295,17 @@ const groups = {
|
|
|
2057
2295
|
}
|
|
2058
2296
|
}
|
|
2059
2297
|
},
|
|
2298
|
+
bannerNotTop: {
|
|
2299
|
+
weight: 2,
|
|
2300
|
+
packages: {
|
|
2301
|
+
axe: {
|
|
2302
|
+
'landmark-banner-is-top-level': {
|
|
2303
|
+
quality: 1,
|
|
2304
|
+
what: 'banner landmark is contained in another landmark'
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
},
|
|
2060
2309
|
footerMultiple: {
|
|
2061
2310
|
weight: 2,
|
|
2062
2311
|
packages: {
|
|
@@ -2150,6 +2399,10 @@ const groups = {
|
|
|
2150
2399
|
'w:AA.1_3_1.F68.Hidden': {
|
|
2151
2400
|
quality: 1,
|
|
2152
2401
|
what: 'Hidden form field is needlessly labeled.'
|
|
2402
|
+
},
|
|
2403
|
+
'w:AA.1_3_1.F68.HiddenAttr': {
|
|
2404
|
+
quality: 1,
|
|
2405
|
+
what: 'Form field with a hidden attribute is needlessly labeled.'
|
|
2153
2406
|
}
|
|
2154
2407
|
}
|
|
2155
2408
|
}
|
|
@@ -2195,6 +2448,12 @@ const groups = {
|
|
|
2195
2448
|
quality: 1,
|
|
2196
2449
|
what: 'Incompatible label types'
|
|
2197
2450
|
}
|
|
2451
|
+
},
|
|
2452
|
+
wave: {
|
|
2453
|
+
'e:label_multiple': {
|
|
2454
|
+
quality: 1,
|
|
2455
|
+
what: 'Form control has more than one label associated with it'
|
|
2456
|
+
}
|
|
2198
2457
|
}
|
|
2199
2458
|
}
|
|
2200
2459
|
},
|
|
@@ -2230,6 +2489,21 @@ const groups = {
|
|
|
2230
2489
|
}
|
|
2231
2490
|
}
|
|
2232
2491
|
},
|
|
2492
|
+
nonWebLink: {
|
|
2493
|
+
weight: 1,
|
|
2494
|
+
packages: {
|
|
2495
|
+
wave: {
|
|
2496
|
+
'a:link_excel': {
|
|
2497
|
+
quality: 1,
|
|
2498
|
+
what: 'Link to Microsoft Excel workbook'
|
|
2499
|
+
},
|
|
2500
|
+
'a:link_word': {
|
|
2501
|
+
quality: 1,
|
|
2502
|
+
what: 'Link to Microsoft Word document'
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
},
|
|
2233
2507
|
linkVague: {
|
|
2234
2508
|
weight: 3,
|
|
2235
2509
|
packages: {
|
|
@@ -2308,6 +2582,17 @@ const groups = {
|
|
|
2308
2582
|
}
|
|
2309
2583
|
}
|
|
2310
2584
|
},
|
|
2585
|
+
autoplay: {
|
|
2586
|
+
weight: 2,
|
|
2587
|
+
packages: {
|
|
2588
|
+
axe: {
|
|
2589
|
+
'no-autoplay-audio': {
|
|
2590
|
+
quality: 1,
|
|
2591
|
+
what: 'video or audio element plays automatically'
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
},
|
|
2311
2596
|
inconsistentStyles: {
|
|
2312
2597
|
weight: 1,
|
|
2313
2598
|
packages: {
|
|
@@ -2358,6 +2643,17 @@ const groups = {
|
|
|
2358
2643
|
}
|
|
2359
2644
|
}
|
|
2360
2645
|
},
|
|
2646
|
+
audioCaptionMissing: {
|
|
2647
|
+
weight: 4,
|
|
2648
|
+
packages: {
|
|
2649
|
+
axe: {
|
|
2650
|
+
'audio-caption': {
|
|
2651
|
+
quality: 1,
|
|
2652
|
+
what: 'audio element has no captions track'
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
},
|
|
2361
2657
|
videoCaptionMissing: {
|
|
2362
2658
|
weight: 4,
|
|
2363
2659
|
packages: {
|
|
@@ -2377,6 +2673,10 @@ const groups = {
|
|
|
2377
2673
|
quality: 1,
|
|
2378
2674
|
what: 'video or audio element may have no or incorrect captions, transcript, or audio description'
|
|
2379
2675
|
},
|
|
2676
|
+
'a:audio_video': {
|
|
2677
|
+
quality: 1,
|
|
2678
|
+
what: 'audio or video file or link may have no or incorrect captions, transcript, or audio description'
|
|
2679
|
+
},
|
|
2380
2680
|
'a:youtube_video': {
|
|
2381
2681
|
quality: 1,
|
|
2382
2682
|
what: 'YouTube video may have no or incorrect captions'
|
|
@@ -2384,7 +2684,7 @@ const groups = {
|
|
|
2384
2684
|
}
|
|
2385
2685
|
}
|
|
2386
2686
|
},
|
|
2387
|
-
|
|
2687
|
+
notKeyboardScrollable: {
|
|
2388
2688
|
weight: 4,
|
|
2389
2689
|
packages: {
|
|
2390
2690
|
alfa: {
|
|
@@ -2392,6 +2692,12 @@ const groups = {
|
|
|
2392
2692
|
quality: 1,
|
|
2393
2693
|
what: 'Element is scrollable but not by keyboard'
|
|
2394
2694
|
}
|
|
2695
|
+
},
|
|
2696
|
+
axe: {
|
|
2697
|
+
'scrollable-region-focusable': {
|
|
2698
|
+
quality: 1,
|
|
2699
|
+
what: 'Element is scrollable but has no keyboard access'
|
|
2700
|
+
}
|
|
2395
2701
|
}
|
|
2396
2702
|
}
|
|
2397
2703
|
},
|
|
@@ -2427,6 +2733,10 @@ const groups = {
|
|
|
2427
2733
|
}
|
|
2428
2734
|
},
|
|
2429
2735
|
axe: {
|
|
2736
|
+
'bypass': {
|
|
2737
|
+
quality: 1,
|
|
2738
|
+
what: 'Page has no means to bypass repeated blocks'
|
|
2739
|
+
},
|
|
2430
2740
|
'skip-link': {
|
|
2431
2741
|
quality: 1,
|
|
2432
2742
|
what: 'Skip-link target is not focusable or does not exist'
|
|
@@ -2474,7 +2784,7 @@ const groups = {
|
|
|
2474
2784
|
}
|
|
2475
2785
|
},
|
|
2476
2786
|
obsoleteElement: {
|
|
2477
|
-
weight:
|
|
2787
|
+
weight: 3,
|
|
2478
2788
|
packages: {
|
|
2479
2789
|
alfa: {
|
|
2480
2790
|
r70: {
|