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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "3.8.1",
3
+ "version": "3.8.2",
4
4
  "description": "Client that scores and digests Testaro reports",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -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&rsquo;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, '&amp;')
25
+ .replace(/</g, '&lt;');
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
+ };
@@ -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 elements has no text alternative'
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
- linkDestinationsSame: {
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
- tableCellHeaderless: {
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
- invisibleLabel: {
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
- asideTopLandmark: {
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
- mainTopLandmark: {
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
- mainLandmark: {
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
- bannerLandmark: {
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
- notScrollable: {
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: 1,
2787
+ weight: 3,
2478
2788
  packages: {
2479
2789
  alfa: {
2480
2790
  r70: {