testaro 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +502 -0
  3. package/aceconfig.js +7 -0
  4. package/commands.js +249 -0
  5. package/index.js +1248 -0
  6. package/package.json +39 -0
  7. package/procs/score/asp09.js +555 -0
  8. package/procs/test/allText.js +76 -0
  9. package/procs/test/allVis.js +17 -0
  10. package/procs/test/linksByType.js +90 -0
  11. package/procs/test/textOf.txt +73 -0
  12. package/scoring/correlation.js +74 -0
  13. package/scoring/correlations.json +327 -0
  14. package/scoring/data.json +26021 -0
  15. package/scoring/dupCounts.js +39 -0
  16. package/scoring/dupCounts.json +112 -0
  17. package/scoring/duplications.json +253 -0
  18. package/scoring/issues.json +304 -0
  19. package/scoring/packageData.js +171 -0
  20. package/scoring/packageIssues.js +34 -0
  21. package/scoring/rulesetData.json +15 -0
  22. package/tests/aatt.js +64 -0
  23. package/tests/alfa.js +107 -0
  24. package/tests/axe.js +109 -0
  25. package/tests/bulk.js +21 -0
  26. package/tests/embAc.js +36 -0
  27. package/tests/focAll.js +62 -0
  28. package/tests/focInd.js +99 -0
  29. package/tests/focOp.js +132 -0
  30. package/tests/hover.js +195 -0
  31. package/tests/ibm.js +89 -0
  32. package/tests/labClash.js +157 -0
  33. package/tests/linkUl.js +65 -0
  34. package/tests/menuNav.js +254 -0
  35. package/tests/motion.js +115 -0
  36. package/tests/radioSet.js +87 -0
  37. package/tests/role.js +164 -0
  38. package/tests/styleDiff.js +146 -0
  39. package/tests/tabNav.js +282 -0
  40. package/tests/wave.js +44 -0
  41. package/tests/zIndex.js +49 -0
  42. package/validation/batches/sample.json +13 -0
  43. package/validation/executors/sample.js +11 -0
  44. package/validation/scripts/app/sample.json +21 -0
  45. package/validation/scripts/test/bulk.json +39 -0
  46. package/validation/scripts/test/embAc.json +45 -0
  47. package/validation/scripts/test/focAll.json +59 -0
  48. package/validation/scripts/test/focInd.json +55 -0
  49. package/validation/scripts/test/focOp.json +53 -0
  50. package/validation/scripts/test/hover.json +47 -0
  51. package/validation/scripts/test/labClash.json +43 -0
  52. package/validation/scripts/test/linkUl.json +62 -0
  53. package/validation/scripts/test/menuNav.json +97 -0
  54. package/validation/scripts/test/motion.json +53 -0
  55. package/validation/scripts/test/radioSet.json +43 -0
  56. package/validation/scripts/test/role.json +42 -0
  57. package/validation/scripts/test/styleDiff.json +61 -0
  58. package/validation/scripts/test/tabNav.json +97 -0
  59. package/validation/scripts/test/zIndex.json +40 -0
  60. package/validation/targets/bulk/bad.html +48 -0
  61. package/validation/targets/bulk/good.html +15 -0
  62. package/validation/targets/embAc/bad.html +21 -0
  63. package/validation/targets/embAc/good.html +15 -0
  64. package/validation/targets/focAll/good.html +15 -0
  65. package/validation/targets/focAll/less.html +15 -0
  66. package/validation/targets/focAll/more.html +16 -0
  67. package/validation/targets/focInd/bad.html +31 -0
  68. package/validation/targets/focInd/good.html +22 -0
  69. package/validation/targets/focOp/bad.html +18 -0
  70. package/validation/targets/focOp/good.html +15 -0
  71. package/validation/targets/hover/bad.html +19 -0
  72. package/validation/targets/hover/good.html +15 -0
  73. package/validation/targets/labClash/bad.html +20 -0
  74. package/validation/targets/labClash/good.html +18 -0
  75. package/validation/targets/linkUl/bad.html +16 -0
  76. package/validation/targets/linkUl/good.html +30 -0
  77. package/validation/targets/linkUl/na.html +20 -0
  78. package/validation/targets/menuNav/bad.html +106 -0
  79. package/validation/targets/menuNav/bad.js +348 -0
  80. package/validation/targets/menuNav/good.html +106 -0
  81. package/validation/targets/menuNav/good.js +365 -0
  82. package/validation/targets/menuNav/style.css +22 -0
  83. package/validation/targets/motion/bad.css +15 -0
  84. package/validation/targets/motion/bad.html +16 -0
  85. package/validation/targets/motion/good.html +15 -0
  86. package/validation/targets/radioSet/bad.html +34 -0
  87. package/validation/targets/radioSet/good.html +27 -0
  88. package/validation/targets/role/bad.html +26 -0
  89. package/validation/targets/role/good.html +22 -0
  90. package/validation/targets/styleDiff/bad.html +35 -0
  91. package/validation/targets/styleDiff/good.html +36 -0
  92. package/validation/targets/tabNav/bad.html +51 -0
  93. package/validation/targets/tabNav/bad.js +35 -0
  94. package/validation/targets/tabNav/good.html +53 -0
  95. package/validation/targets/tabNav/good.js +83 -0
  96. package/validation/targets/tabNav/goodMoz.js +206 -0
  97. package/validation/targets/tabNav/style.css +34 -0
  98. package/validation/targets/zIndex/bad.html +17 -0
  99. package/validation/targets/zIndex/good.html +15 -0
@@ -0,0 +1,90 @@
1
+ // Returns an object classifying the links in a page by layout.
2
+ exports.linksByType = async page => await page.evaluateHandle(() => {
3
+ // FUNCTION DEFINITIONS START
4
+ // Returns whether an element has fluid display.
5
+ const hasFluidDisplay = element => {
6
+ const display = window.getComputedStyle(element).display;
7
+ return display.startsWith('inline') || display.startsWith('flex');
8
+ };
9
+ // Returns whether an element and its children have fluid display.
10
+ const isFluid = element => {
11
+ if (hasFluidDisplay(element)) {
12
+ return Array.from(element.children).every(child => hasFluidDisplay(child));
13
+ }
14
+ else {
15
+ return false;
16
+ }
17
+ };
18
+ // Returns whether all siblings of an element have fluid display.
19
+ const hasFluidSiblings = element => {
20
+ const preSib = element.previousElementSibling;
21
+ if (preSib) {
22
+ const postSib = element.nextElementSibling;
23
+ if (postSib) {
24
+ return isFluid(preSib) && isFluid(postSib);
25
+ }
26
+ else {
27
+ return isFluid(preSib);
28
+ }
29
+ }
30
+ else {
31
+ const postSib = element.nextElementSibling;
32
+ if (postSib) {
33
+ return isFluid(postSib);
34
+ }
35
+ else {
36
+ return true;
37
+ }
38
+ }
39
+ };
40
+ // Removes spacing characters from a text.
41
+ const despace = text => text.replace(/\s/g, '');
42
+ // Returns whether an element has text that is less than its nearest nonfluid ancestor’s.
43
+ const hasAdjacentText = element => {
44
+ // Recursively returns the first ancestor element with nonfluid display.
45
+ const blockOf = node => {
46
+ const parentElement = node.parentElement;
47
+ if (hasFluidDisplay(parentElement)) {
48
+ return blockOf(parentElement);
49
+ }
50
+ else {
51
+ return parentElement;
52
+ }
53
+ };
54
+ // Identify the text of the element.
55
+ const elementText = despace(element.textContent);
56
+ // Identify the text of its nearest nonfluid ancestor.
57
+ const blockText = despace(blockOf(element).textContent);
58
+ // If the element has any text:
59
+ if (elementText) {
60
+ // Return whether it is less than its nearest nonfluid ancestor’s.
61
+ return despace(blockText).length > elementText.length;
62
+ }
63
+ // Otherwise, i.e. if the element has no text:
64
+ else {
65
+ // Return no.
66
+ return false;
67
+ }
68
+ };
69
+ // FUNCTION DEFINITIONS END
70
+ // Get the links in the page.
71
+ const links = Array
72
+ .from(document.body.getElementsByTagName('a'))
73
+ .filter(element => ! element.hasAttribute('role'));
74
+ // Initialize an object classifying the links.
75
+ const linkTypes = {
76
+ inline: [],
77
+ block: []
78
+ };
79
+ // Populate it.
80
+ links.forEach(link => {
81
+ if (isFluid(link) && hasFluidSiblings(link) && hasAdjacentText(link)) {
82
+ linkTypes.inline.push(link);
83
+ }
84
+ else {
85
+ linkTypes.block.push(link);
86
+ }
87
+ });
88
+ // Return it.
89
+ return linkTypes;
90
+ });
@@ -0,0 +1,73 @@
1
+ /*
2
+ Returns the text associated with an element. Argument: element.
3
+ The content of this file can be used as the second argument in a Function constructor,
4
+ with 'element' being the first argument.
5
+ */
6
+ // Identify the element, if specified, or else the focused element.
7
+ const el = element || document.activeElement;
8
+ // Initialize an array of its texts.
9
+ const texts = [];
10
+ // FUNCTION DEFINITION START
11
+ // Removes excess spacing from a string.
12
+ const debloat = text => text.trim().replace(/\s+/g, ' ');
13
+ // FUNCTION DEFINITION END
14
+ // Add any attribute label to the array.
15
+ const ariaLabel = el.getAttribute('aria-label');
16
+ if (ariaLabel) {
17
+ const trimmedLabel = debloat(ariaLabel);
18
+ if (trimmedLabel) {
19
+ texts.push(trimmedLabel);
20
+ }
21
+ }
22
+ // Add any explicit and implicit labels to the array.
23
+ const labelNodeList = el.labels;
24
+ if (labelNodeList && labelNodeList.length) {
25
+ const labels = Array.from(labelNodeList);
26
+ const labelTexts = labels
27
+ .map(label => label.textContent && debloat(label.textContent))
28
+ .filter(text => text);
29
+ if (labelTexts.length) {
30
+ texts.push(...labelTexts);
31
+ }
32
+ }
33
+ // Add any referenced labels to the array.
34
+ if (el.hasAttribute('aria-labelledby')) {
35
+ const labelerIDs = el.getAttribute('aria-labelledby').split(/\s+/);
36
+ labelerIDs.forEach(id => {
37
+ const labeler = document.getElementById(id);
38
+ if (labeler) {
39
+ const labelerText = debloat(labeler.textContent);
40
+ if (labelerText) {
41
+ texts.push(labelerText);
42
+ }
43
+ }
44
+ });
45
+ }
46
+ // Add any image text alternatives to the array.
47
+ const altTexts = Array
48
+ .from(element.querySelectorAll('img[alt]:not([alt=""])'))
49
+ .map(img => debloat(img.alt))
50
+ .join('; ');
51
+ if (altTexts.length) {
52
+ texts.push(altTexts);
53
+ }
54
+ // Add any text content of the element to the array.
55
+ const ownText = element.textContent;
56
+ if (ownText) {
57
+ const minText = debloat(ownText);
58
+ if (minText) {
59
+ texts.push(minText);
60
+ }
61
+ }
62
+ // Identify a concatenation of the texts.
63
+ let textChain = texts.join('; ');
64
+ // If it is empty:
65
+ if (! textChain) {
66
+ // Substitute the text content of its parent element, if any.
67
+ textChain = `{${debloat(element.parentElement.textContent)}}`;
68
+ if (textChain === '{}') {
69
+ textChain = '';
70
+ }
71
+ }
72
+ // Return a concatenation of the texts in the array.
73
+ return textChain;
@@ -0,0 +1,74 @@
1
+ /*
2
+ correlation
3
+ Compiles a list of the correlations between distinct-package issue types and creates a file,
4
+ correlations.json, containing the list.
5
+ */
6
+ const fs = require('fs');
7
+ const compile = () => {
8
+ const issuesJSON = fs.readFileSync('scoring/package/issues.json', 'utf8');
9
+ const issues = JSON.parse(issuesJSON);
10
+ const dataJSON = fs.readFileSync('scoring/package/data.json', 'utf8');
11
+ const reportData = JSON.parse(dataJSON);
12
+ const reports = Object.values(reportData);
13
+ // Initialize the list.
14
+ const data = {
15
+ aatt_alfa: {},
16
+ aatt_axe: {},
17
+ aatt_ibm: {},
18
+ aatt_wave: {},
19
+ alfa_axe: {},
20
+ alfa_ibm: {},
21
+ alfa_wave: {},
22
+ axe_ibm: {},
23
+ axe_wave: {},
24
+ ibm_wave: {}
25
+ };
26
+ // For each pair of packages:
27
+ const packagePairs = Object.keys(data);
28
+ packagePairs.forEach(packagePair => {
29
+ console.log(`=== Starting package pair ${packagePair}`);
30
+ const packages = packagePair.split('_');
31
+ // Identify the reports containing results from both packages.
32
+ const pairReports = reports.filter(report => report[packages[0]] && report[packages[1]]);
33
+ // For each pair of issues:
34
+ issues[packages[0]].forEach(issueA => {
35
+ issues[packages[1]].forEach(issueB => {
36
+ // Initialize an array of score pairs.
37
+ const scorePairs = [];
38
+ // For each applicable report:
39
+ pairReports.forEach(report => {
40
+ // Add the scores for the issues to the array of score pairs.
41
+ const scorePair = [report[packages[0]][issueA] || 0, report[packages[1]][issueB] || 0];
42
+ scorePairs.push(scorePair);
43
+ });
44
+ // Get the correlation between the issues.
45
+ const aSum = scorePairs.reduce((sum, current) => sum + current[0], 0);
46
+ const bSum = scorePairs.reduce((sum, current) => sum + current[1], 0);
47
+ const abSum = scorePairs.reduce((sum, current) => sum + current[0] * current[1], 0);
48
+ const aSqSum = scorePairs.reduce((sum, current) => sum + current[0] ** 2, 0);
49
+ const bSqSum = scorePairs.reduce((sum, current) => sum + current[1] ** 2, 0);
50
+ const n = scorePairs.length;
51
+ const correlation
52
+ = (abSum - aSum * bSum / n) / n
53
+ / (Math.sqrt(aSqSum / n - (aSum / n) ** 2) * Math.sqrt(bSqSum / n - (bSum / n) ** 2));
54
+ // If the correlation is large enough:
55
+ if (correlation > 0.7) {
56
+ const roundedCorr = correlation.toFixed(2);
57
+ // Record it and the count of non-zero scores.
58
+ const nonZero = scorePairs.reduce(
59
+ (count, current) => count + current.filter(score => score).length, 0
60
+ );
61
+ const corrPlusNZ = `${roundedCorr} (${nonZero})`;
62
+ if (data[packagePair][issueA]) {
63
+ data[packagePair][issueA][issueB] = corrPlusNZ;
64
+ }
65
+ else {
66
+ data[packagePair][issueA] = {[issueB]: corrPlusNZ};
67
+ }
68
+ }
69
+ });
70
+ });
71
+ });
72
+ return data;
73
+ };
74
+ fs.writeFileSync('scoring/package/correlations.json', JSON.stringify(compile(), null, 2));
@@ -0,0 +1,327 @@
1
+ {
2
+ "aatt_alfa": {
3
+ "e:F77": {
4
+ "r3": "0.89 (253)"
5
+ },
6
+ "e:H36": {
7
+ "r28": "0.98 (6)"
8
+ },
9
+ "e:H37": {
10
+ "r2": "0.72 (221)"
11
+ },
12
+ "e:H57": {
13
+ "r4": "0.81 (66)"
14
+ },
15
+ "e:H58": {
16
+ "r7": "0.82 (5)"
17
+ },
18
+ "w:G141": {
19
+ "r53": "0.89 (427)"
20
+ },
21
+ "w:H98": {
22
+ "r10": "0.95 (3)"
23
+ }
24
+ },
25
+ "aatt_axe": {
26
+ "e:H36": {
27
+ "input-image-alt": "0.98 (6)"
28
+ },
29
+ "e:H57": {
30
+ "html-has-lang": "0.73 (75)"
31
+ },
32
+ "e:H58": {
33
+ "valid-lang": "0.82 (5)"
34
+ },
35
+ "w:G141": {
36
+ "heading-order": "0.79 (515)"
37
+ }
38
+ },
39
+ "aatt_ibm": {
40
+ "e:ARIA4+F92": {
41
+ "v:Rpt_Aria_ValidRole": "0.96 (38)"
42
+ },
43
+ "e:ARIA6+H53": {
44
+ "v:WCAG20_Object_HasText": "1.00 (6)"
45
+ },
46
+ "e:H24": {
47
+ "v:WCAG20_Area_HasAlt": "0.79 (4)"
48
+ },
49
+ "e:H36": {
50
+ "v:WCAG20_Input_ExplicitLabelImage": "0.98 (6)"
51
+ },
52
+ "w:F10": {
53
+ "v:WCAG20_Object_HasText": "1.00 (6)"
54
+ },
55
+ "w:H65": {
56
+ "v:Rpt_Aria_ValidRole": "0.92 (16)"
57
+ }
58
+ },
59
+ "aatt_wave": {
60
+ "e:G1+G123+G124": {
61
+ "a:link_internal_broken": "0.94 (109)"
62
+ },
63
+ "e:H24": {
64
+ "e:alt_area_missing": "0.73 (5)"
65
+ },
66
+ "e:H36": {
67
+ "e:alt_input_missing": "0.91 (7)"
68
+ },
69
+ "e:H37": {
70
+ "e:alt_missing": "0.75 (274)"
71
+ },
72
+ "e:H57": {
73
+ "e:language_missing": "0.72 (85)"
74
+ },
75
+ "w:G141": {
76
+ "a:heading_skipped": "0.85 (535)"
77
+ },
78
+ "w:G90": {
79
+ "a:event_handler": "0.76 (109)"
80
+ },
81
+ "w:H44": {
82
+ "a:label_orphaned": "0.71 (67)"
83
+ },
84
+ "w:H49": {
85
+ "a:image_title": "0.70 (38)",
86
+ "e:alt_spacer_missing": "0.78 (36)",
87
+ "e:th_empty": "0.93 (13)"
88
+ }
89
+ },
90
+ "alfa_axe": {
91
+ "r11": {
92
+ "link-name": "0.71 (457)"
93
+ },
94
+ "r12": {
95
+ "aria-command-name": "0.93 (51)",
96
+ "dlitem": "0.93 (46)"
97
+ },
98
+ "r2": {
99
+ "image-alt": "0.70 (249)"
100
+ },
101
+ "r20": {
102
+ "aria-command-name": "0.93 (18)",
103
+ "dlitem": "0.93 (13)"
104
+ },
105
+ "r28": {
106
+ "input-image-alt": "1.00 (6)"
107
+ },
108
+ "r3": {
109
+ "duplicate-id": "0.75 (291)"
110
+ },
111
+ "r4": {
112
+ "html-has-lang": "0.80 (74)"
113
+ },
114
+ "r40": {
115
+ "aria-command-name": "0.89 (8)",
116
+ "dlitem": "0.89 (3)"
117
+ },
118
+ "r42": {
119
+ "aria-required-children": "0.91 (72)",
120
+ "aria-required-parent": "0.96 (51)",
121
+ "listitem": "0.73 (71)"
122
+ },
123
+ "r43": {
124
+ "svg-img-alt": "0.98 (4)"
125
+ },
126
+ "r47": {
127
+ "meta-viewport": "0.81 (157)"
128
+ },
129
+ "r5": {
130
+ "html-lang-valid": "1.00 (4)"
131
+ },
132
+ "r53": {
133
+ "heading-order": "0.92 (371)"
134
+ },
135
+ "r68": {
136
+ "aria-required-children": "0.73 (122)",
137
+ "aria-required-parent": "0.78 (101)"
138
+ },
139
+ "r7": {
140
+ "valid-lang": "1.00 (4)"
141
+ },
142
+ "r83": {
143
+ "definition-list": "0.89 (162)",
144
+ "landmark-main-is-top-level": "0.78 (167)"
145
+ },
146
+ "r93": {
147
+ "avoid-inline-spacing": "0.81 (6)"
148
+ }
149
+ },
150
+ "alfa_ibm": {
151
+ "r12": {
152
+ "v:Rpt_Aria_RequiredParent_Native_Host_Sematics": "0.84 (54)"
153
+ },
154
+ "r13": {
155
+ "v:WCAG20_Frame_HasTitle": "0.70 (116)"
156
+ },
157
+ "r20": {
158
+ "v:Rpt_Aria_RequiredParent_Native_Host_Sematics": "0.84 (20)"
159
+ },
160
+ "r28": {
161
+ "v:WCAG20_Input_ExplicitLabelImage": "0.93 (6)"
162
+ },
163
+ "r40": {
164
+ "v:Rpt_Aria_RequiredParent_Native_Host_Sematics": "0.81 (12)"
165
+ },
166
+ "r5": {
167
+ "v:WCAG20_Elem_Lang_Valid": "0.71 (6)"
168
+ }
169
+ },
170
+ "alfa_wave": {
171
+ "r28": {
172
+ "a:select_missing_label": "0.73 (39)",
173
+ "e:alt_input_missing": "0.92 (7)"
174
+ },
175
+ "r4": {
176
+ "e:language_missing": "0.71 (82)"
177
+ },
178
+ "r53": {
179
+ "a:heading_skipped": "0.90 (387)"
180
+ },
181
+ "r83": {
182
+ "a:youtube_video": "0.88 (232)"
183
+ }
184
+ },
185
+ "axe_ibm": {
186
+ "area-alt": {
187
+ "v:HAAC_Img_UsemapAlt": "0.89 (3)"
188
+ },
189
+ "aria-allowed-role": {
190
+ "v:aria_semantics_role": "0.74 (97)"
191
+ },
192
+ "aria-command-name": {
193
+ "v:Rpt_Aria_RequiredParent_Native_Host_Sematics": "0.78 (18)"
194
+ },
195
+ "aria-required-attr": {
196
+ "v:Rpt_Aria_RequiredProperties": "0.82 (5)"
197
+ },
198
+ "aria-valid-attr": {
199
+ "v:Rpt_Aria_ValidProperty": "0.94 (14)"
200
+ },
201
+ "autocomplete-valid": {
202
+ "v:WCAG21_Input_Autocomplete": "0.74 (19)"
203
+ },
204
+ "color-contrast": {
205
+ "v:IBMA_Color_Contrast_WCAG2AA": "0.76 (590)"
206
+ },
207
+ "dlitem": {
208
+ "v:Rpt_Aria_RequiredParent_Native_Host_Sematics": "0.78 (13)"
209
+ },
210
+ "duplicate-id-aria": {
211
+ "v:WCAG21_Label_Accessible": "0.73 (134)"
212
+ },
213
+ "empty-heading": {
214
+ "v:RPT_Header_HasContent": "0.86 (148)"
215
+ },
216
+ "frame-title": {
217
+ "v:WCAG20_Frame_HasTitle": "0.73 (128)"
218
+ },
219
+ "html-lang-valid": {
220
+ "v:WCAG20_Elem_Lang_Valid": "0.77 (8)"
221
+ },
222
+ "image-alt": {
223
+ "v:WCAG20_Img_HasAlt": "0.84 (305)"
224
+ },
225
+ "image-redundant-alt": {
226
+ "v:WCAG20_Img_LinkTextNotRedundant": "0.82 (57)"
227
+ },
228
+ "input-image-alt": {
229
+ "v:WCAG20_Input_ExplicitLabelImage": "0.93 (6)"
230
+ },
231
+ "landmark-complementary-is-top-level": {
232
+ "v:Rpt_Aria_ComplementaryRequiredLabel_Implicit": "0.82 (82)",
233
+ "v:Rpt_Aria_MultipleComplementaryLandmarks_Implicit": "0.83 (50)"
234
+ },
235
+ "landmark-contentinfo-is-top-level": {
236
+ "v:Rpt_Aria_MultipleContentinfoInSiblingSet_Implicit": "0.73 (30)",
237
+ "v:Rpt_Aria_MultipleContentinfoLandmarks_Implicit": "0.86 (49)"
238
+ },
239
+ "landmark-main-is-top-level": {
240
+ "r:Rpt_Aria_MultipleMainsVisibleLabel_Implicit": "0.71 (15)",
241
+ "v:Rpt_Aria_MultipleMainsRequireLabel_Implicit_2": "0.73 (15)"
242
+ },
243
+ "landmark-no-duplicate-banner": {
244
+ "v:Rpt_Aria_MultipleBannerLandmarks_Implicit": "0.73 (80)"
245
+ },
246
+ "landmark-no-duplicate-main": {
247
+ "r:Rpt_Aria_MultipleMainsVisibleLabel_Implicit": "1.00 (18)",
248
+ "v:Rpt_Aria_MultipleMainsRequireLabel_Implicit_2": "0.95 (18)"
249
+ },
250
+ "link-name": {
251
+ "v:WCAG20_A_HasText": "0.75 (492)"
252
+ }
253
+ },
254
+ "axe_wave": {
255
+ "area-alt": {
256
+ "e:alt_area_missing": "0.77 (5)"
257
+ },
258
+ "definition-list": {
259
+ "a:youtube_video": "0.98 (83)"
260
+ },
261
+ "document-title": {
262
+ "e:title_invalid": "0.71 (3)"
263
+ },
264
+ "empty-heading": {
265
+ "e:heading_empty": "0.74 (175)"
266
+ },
267
+ "heading-order": {
268
+ "a:heading_skipped": "0.90 (463)"
269
+ },
270
+ "html-has-lang": {
271
+ "e:language_missing": "0.87 (91)"
272
+ },
273
+ "input-image-alt": {
274
+ "e:alt_input_missing": "0.92 (7)"
275
+ },
276
+ "landmark-main-is-top-level": {
277
+ "a:youtube_video": "0.87 (88)"
278
+ },
279
+ "object-alt": {
280
+ "a:plugin": "1.00 (6)"
281
+ },
282
+ "page-has-heading-one": {
283
+ "a:h1_missing": "0.79 (206)"
284
+ },
285
+ "select-name": {
286
+ "a:select_missing_label": "0.79 (71)"
287
+ }
288
+ },
289
+ "ibm_wave": {
290
+ "v:HAAC_Aria_ImgAlt": {
291
+ "a:tabindex": "0.84 (57)"
292
+ },
293
+ "v:IBMA_Color_Contrast_WCAG2AA": {
294
+ "c:contrast": "0.73 (693)"
295
+ },
296
+ "v:RPT_Header_HasContent": {
297
+ "e:heading_empty": "0.84 (168)"
298
+ },
299
+ "v:RPT_Table_DataHeadingsAria": {
300
+ "a:table_layout": "0.73 (60)"
301
+ },
302
+ "v:Rpt_Aria_MultipleToolbarUniqueLabel": {
303
+ "a:tabindex": "0.89 (44)"
304
+ },
305
+ "v:Rpt_Aria_ValidIdRef": {
306
+ "e:aria_reference_broken": "0.81 (131)"
307
+ },
308
+ "v:WCAG20_Area_HasAlt": {
309
+ "e:alt_area_missing": "0.80 (5)"
310
+ },
311
+ "v:WCAG20_Elem_UniqueAccessKey": {
312
+ "a:accesskey": "0.78 (15)"
313
+ },
314
+ "v:WCAG20_Img_HasAlt": {
315
+ "e:alt_missing": "0.87 (296)"
316
+ },
317
+ "v:WCAG20_Img_PresentationImgHasNonNullAlt": {
318
+ "a:alt_redundant": "0.75 (75)"
319
+ },
320
+ "v:WCAG20_Input_ExplicitLabelImage": {
321
+ "e:alt_input_missing": "0.87 (7)"
322
+ },
323
+ "v:WCAG20_Input_RadioChkInFieldSet": {
324
+ "a:fieldset_missing": "0.71 (57)"
325
+ }
326
+ }
327
+ }