@surea11y/core 1.1.2 → 1.3.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 (160) hide show
  1. package/CHANGELOG.md +48 -4
  2. package/LICENSE +373 -21
  3. package/README.md +70 -1
  4. package/bin/core.js +240 -11
  5. package/docs/API_STABILITY.md +61 -0
  6. package/docs/BASELINE.md +66 -0
  7. package/docs/BINDING_AUTHORS_GUIDE.md +9 -9
  8. package/docs/CI_INTEGRATIONS.md +103 -0
  9. package/docs/CLI.md +80 -1
  10. package/docs/ENGINE_OPTIONS.md +4 -0
  11. package/docs/INTEGRATION.md +19 -1
  12. package/docs/OUTPUT_SCHEMA.md +2 -2
  13. package/docs/REPORT.md +33 -0
  14. package/docs/RULE_AUTHORING.md +31 -0
  15. package/docs/RULE_CATALOG.md +1 -1
  16. package/docs/SARIF.md +59 -0
  17. package/package.json +15 -4
  18. package/src/baseline.js +0 -0
  19. package/src/catalogs/composites.wcag.js +414 -450
  20. package/src/checks/automatic/area-alt-present.js +59 -25
  21. package/src/checks/automatic/aria-allowed-attr.js +193 -33
  22. package/src/checks/automatic/aria-allowed-role.js +21 -7
  23. package/src/checks/automatic/aria-braille-equivalent.js +32 -10
  24. package/src/checks/automatic/aria-conditional-attr.js +24 -7
  25. package/src/checks/automatic/aria-deprecated-role.js +22 -8
  26. package/src/checks/automatic/aria-hidden-body.js +50 -19
  27. package/src/checks/automatic/aria-hidden-focus.js +408 -53
  28. package/src/checks/automatic/aria-prohibited-attr.js +296 -22
  29. package/src/checks/automatic/aria-prohibited-children.js +125 -29
  30. package/src/checks/automatic/aria-required-attr.js +23 -8
  31. package/src/checks/automatic/aria-required-children.js +37 -14
  32. package/src/checks/automatic/aria-required-parent.js +48 -14
  33. package/src/checks/automatic/aria-role-name-present.js +47 -21
  34. package/src/checks/automatic/aria-roles-valid.js +22 -12
  35. package/src/checks/automatic/aria-valid-attr-value.js +28 -7
  36. package/src/checks/automatic/aria-valid-attr.js +17 -5
  37. package/src/checks/automatic/autocomplete-valid.js +74 -16
  38. package/src/checks/automatic/avoid-inline-spacing.js +20 -6
  39. package/src/checks/automatic/binary-control-name-present.js +60 -50
  40. package/src/checks/automatic/button-name-present.js +48 -18
  41. package/src/checks/automatic/bypass-blocks-present.js +50 -25
  42. package/src/checks/automatic/canvas-text-alternative-present.js +57 -26
  43. package/src/checks/automatic/combobox-name-present.js +38 -45
  44. package/src/checks/automatic/contrast-computable.js +361 -341
  45. package/src/checks/automatic/contrast-enhanced.js +487 -466
  46. package/src/checks/automatic/contrast-minimum.js +486 -465
  47. package/src/checks/automatic/css-orientation-lock.js +39 -9
  48. package/src/checks/automatic/definition-list-children-valid.js +40 -19
  49. package/src/checks/automatic/deprecated-elements-not-used.js +21 -7
  50. package/src/checks/automatic/dialog-name-present.js +37 -75
  51. package/src/checks/automatic/dlitem-parent-valid.js +23 -8
  52. package/src/checks/automatic/duplicate-id-aria.js +24 -6
  53. package/src/checks/automatic/embed-text-alternative-present.js +86 -35
  54. package/src/checks/automatic/form-control-programmatic-label-present.js +79 -196
  55. package/src/checks/automatic/form-control-single-label.js +47 -10
  56. package/src/checks/automatic/html-xml-lang-mismatch.js +43 -19
  57. package/src/checks/automatic/iframe-focusable-content.js +26 -11
  58. package/src/checks/automatic/iframe-name-present.js +31 -9
  59. package/src/checks/automatic/iframe-title-unique.js +29 -8
  60. package/src/checks/automatic/img-alt-present.js +47 -43
  61. package/src/checks/automatic/input-image-alt-present.js +143 -112
  62. package/src/checks/automatic/label-in-name.js +50 -22
  63. package/src/checks/automatic/language-page-present.js +117 -109
  64. package/src/checks/automatic/link-in-text-block.js +59 -19
  65. package/src/checks/automatic/link-name-present.js +45 -14
  66. package/src/checks/automatic/list-children-valid.js +26 -9
  67. package/src/checks/automatic/listbox-name-present.js +39 -19
  68. package/src/checks/automatic/listitem-parent-valid.js +18 -6
  69. package/src/checks/automatic/menuitem-name-present.js +39 -61
  70. package/src/checks/automatic/meta-refresh-no-exceptions.js +37 -8
  71. package/src/checks/automatic/meta-refresh-timing-absent.js +28 -6
  72. package/src/checks/automatic/meta-viewport-zoom-enabled.js +32 -7
  73. package/src/checks/automatic/meter-name-present.js +36 -33
  74. package/src/checks/automatic/nested-interactive-controls-absent.js +31 -10
  75. package/src/checks/automatic/object-text-alternative-present.js +91 -39
  76. package/src/checks/automatic/option-name-present.js +38 -21
  77. package/src/checks/automatic/page-title-present.js +26 -7
  78. package/src/checks/automatic/progressbar-name-present.js +41 -34
  79. package/src/checks/automatic/role-img-alt-present.js +209 -157
  80. package/src/checks/automatic/searchbox-name-present.js +39 -19
  81. package/src/checks/automatic/server-side-image-map-absent.js +23 -8
  82. package/src/checks/automatic/slider-name-present.js +40 -47
  83. package/src/checks/automatic/spinbutton-name-present.js +39 -19
  84. package/src/checks/automatic/summary-name-present.js +37 -17
  85. package/src/checks/automatic/svg-image-text-alternative-present.js +114 -47
  86. package/src/checks/automatic/svg-text-alternative-present.js +246 -226
  87. package/src/checks/automatic/tab-name-present.js +37 -60
  88. package/src/checks/automatic/table-headers-attr-valid.js +24 -8
  89. package/src/checks/automatic/table-th-has-data-cells.js +22 -8
  90. package/src/checks/automatic/target-size-minimum.js +118 -48
  91. package/src/checks/automatic/td-has-header.js +29 -11
  92. package/src/checks/automatic/textbox-name-present.js +39 -19
  93. package/src/checks/automatic/tooltip-name-present.js +37 -18
  94. package/src/checks/automatic/treeitem-name-present.js +38 -21
  95. package/src/checks/automatic/valid-lang.js +20 -6
  96. package/src/checks/automatic/video-poster-text-alternative-present.js +79 -36
  97. package/src/checks/manual/accesskeys-manual.js +14 -5
  98. package/src/checks/manual/area-alt-decorative-manual.js +192 -193
  99. package/src/checks/manual/area-alt-quality-manual.js +182 -141
  100. package/src/checks/manual/aria-checked-state-mismatch-manual.js +34 -11
  101. package/src/checks/manual/aria-text-manual.js +14 -6
  102. package/src/checks/manual/canvas-text-alternative-quality-manual.js +149 -114
  103. package/src/checks/manual/css-hidden-focus.js +196 -165
  104. package/src/checks/manual/embed-text-alternative-quality-manual.js +171 -160
  105. package/src/checks/manual/empty-heading-manual.js +24 -12
  106. package/src/checks/manual/empty-table-header-manual.js +21 -14
  107. package/src/checks/manual/focus-order-semantics-manual.js +45 -10
  108. package/src/checks/manual/form-control-programmatic-label-quality-manual.js +207 -246
  109. package/src/checks/manual/heading-order-manual.js +22 -12
  110. package/src/checks/manual/identical-links-same-purpose-manual.js +34 -12
  111. package/src/checks/manual/image-redundant-alt-manual.js +17 -7
  112. package/src/checks/manual/img-alt-decorative-manual.js +131 -96
  113. package/src/checks/manual/img-alt-quality-manual.js +176 -127
  114. package/src/checks/manual/input-image-alt-decorative-manual.js +125 -92
  115. package/src/checks/manual/input-image-alt-quality-manual.js +125 -92
  116. package/src/checks/manual/label-title-only-manual.js +14 -5
  117. package/src/checks/manual/landmark-banner-is-top-level-manual.js +95 -29
  118. package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +82 -29
  119. package/src/checks/manual/landmark-main-is-top-level-manual.js +64 -23
  120. package/src/checks/manual/landmark-no-duplicate-banner-manual.js +54 -23
  121. package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +54 -23
  122. package/src/checks/manual/landmark-no-duplicate-main-manual.js +17 -8
  123. package/src/checks/manual/landmark-one-main-manual.js +35 -21
  124. package/src/checks/manual/landmark-unique-manual.js +73 -45
  125. package/src/checks/manual/link-name-quality-manual.js +43 -12
  126. package/src/checks/manual/media-transcript-present-manual.js +35 -22
  127. package/src/checks/manual/meta-viewport-large-manual.js +25 -6
  128. package/src/checks/manual/mouse-only-event-handlers-manual.js +38 -11
  129. package/src/checks/manual/no-autoplay-audio-manual.js +20 -6
  130. package/src/checks/manual/object-text-alternative-quality-manual.js +175 -154
  131. package/src/checks/manual/p-as-heading-manual.js +22 -7
  132. package/src/checks/manual/page-has-heading-one-manual.js +40 -23
  133. package/src/checks/manual/page-title-patterns-manual.js +87 -51
  134. package/src/checks/manual/presentation-role-conflict-manual.js +59 -19
  135. package/src/checks/manual/region-manual.js +275 -62
  136. package/src/checks/manual/scope-attr-valid-manual.js +11 -9
  137. package/src/checks/manual/scrollable-region-focusable-manual.js +37 -11
  138. package/src/checks/manual/skip-link-manual.js +35 -12
  139. package/src/checks/manual/svg-text-alternative-quality-manual.js +206 -165
  140. package/src/checks/manual/tabindex-manual.js +11 -9
  141. package/src/checks/manual/table-duplicate-name-manual.js +17 -7
  142. package/src/checks/manual/table-fake-caption-manual.js +24 -7
  143. package/src/checks/manual/video-caption-manual.js +15 -4
  144. package/src/checks/manual-review.js +56 -12
  145. package/src/core/aria-helpers.js +1128 -823
  146. package/src/core/contrast-helpers.js +1217 -1062
  147. package/src/core/dom-helpers.js +4176 -3886
  148. package/src/core/dom-runner.js +720 -596
  149. package/src/core/frame-messaging.js +189 -138
  150. package/src/core/frame-scan.js +94 -82
  151. package/src/core/rollup-composites.js +94 -102
  152. package/src/core/rule-meta.js +71 -35
  153. package/src/core.js +37939 -29121
  154. package/src/i18n/en.js +1194 -887
  155. package/src/i18n/fr.js +1136 -793
  156. package/src/policy/contracts.js +13 -13
  157. package/src/policy/resolvePolicy.js +48 -44
  158. package/src/report.js +502 -0
  159. package/src/sarif.js +175 -0
  160. package/surea11y.browser.js +36042 -0
@@ -45,7 +45,13 @@ const meta = {
45
45
  tags: ['wcag2a', 'wcag131', 'structure', 'atomic', 'automatic'],
46
46
  wcagSc: ['1.3.1'],
47
47
  normativeMappings: [
48
- { standard: 'WCAG', version: '2.2', requirement: '1.3.1', title: 'Info and Relationships', conformanceLevel: 'A' }
48
+ {
49
+ standard: 'WCAG',
50
+ version: '2.2',
51
+ requirement: '1.3.1',
52
+ title: 'Info and Relationships',
53
+ conformanceLevel: 'A'
54
+ }
49
55
  ],
50
56
  defaultSeverity: 'serious',
51
57
  category: 'perceivable',
@@ -55,11 +61,13 @@ const meta = {
55
61
  };
56
62
 
57
63
  function runInPage(ctx) {
58
- const { document, helpers, rule } = ctx;
64
+ const { helpers, rule } = ctx;
59
65
 
60
66
  const MIN_SIZE = 4;
61
67
 
62
- function trim(v) { return (v == null ? '' : String(v)).trim(); }
68
+ function trim(v) {
69
+ return (v == null ? '' : String(v)).trim();
70
+ }
63
71
 
64
72
  const tables = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
65
73
 
@@ -76,11 +84,13 @@ function runInPage(ctx) {
76
84
  const maxCols = rowCells.reduce((m, cells) => Math.max(m, cells.length), 0);
77
85
  if (maxCols < MIN_SIZE) continue;
78
86
 
79
- const hasSpan = rowCells.some((cells) => cells.some((c) => {
80
- const cs = Number.parseInt(c.getAttribute('colspan') || '1', 10);
81
- const rs = Number.parseInt(c.getAttribute('rowspan') || '1', 10);
82
- return (Number.isFinite(cs) && cs > 1) || (Number.isFinite(rs) && rs > 1);
83
- }));
87
+ const hasSpan = rowCells.some((cells) =>
88
+ cells.some((c) => {
89
+ const cs = Number.parseInt(c.getAttribute('colspan') || '1', 10);
90
+ const rs = Number.parseInt(c.getAttribute('rowspan') || '1', 10);
91
+ return (Number.isFinite(cs) && cs > 1) || (Number.isFinite(rs) && rs > 1);
92
+ })
93
+ );
84
94
  if (hasSpan) continue;
85
95
 
86
96
  applicableCount += 1;
@@ -118,12 +128,15 @@ function runInPage(ctx) {
118
128
  if (hasRowHeaderBefore(r, c)) continue;
119
129
 
120
130
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(cell) : 'html';
121
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(cell) : (cell.outerHTML || '');
131
+ const html = helpers.getOuterHtmlSnippet
132
+ ? helpers.getOuterHtmlSnippet(cell)
133
+ : cell.outerHTML || '';
122
134
 
123
135
  occurrences.push({
124
136
  selector: stableSelector,
125
137
  html,
126
- summary: 'This data cell has no associated header (no headers attribute, no column <th> above it, no row <th> to its left).',
138
+ summary:
139
+ 'This data cell has no associated header (no headers attribute, no column <th> above it, no row <th> to its left).',
127
140
  hint: 'Add a headers attribute referencing the relevant <th> id(s), or restructure the table so this cell has an implicit row/column header.',
128
141
  i18n: {
129
142
  summaryKey: 'tdHasHeader_summary_fail',
@@ -142,7 +155,12 @@ function runInPage(ctx) {
142
155
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
143
156
  }
144
157
  if (occurrences.length) {
145
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
158
+ return {
159
+ ruleId: rule.ruleId,
160
+ outcome: 'fail',
161
+ severity: rule.defaultSeverity || 'serious',
162
+ occurrences
163
+ };
146
164
  }
147
165
  return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
148
166
  }
@@ -10,10 +10,16 @@ const meta = {
10
10
  descriptionKey: 'textboxNamePresent_description'
11
11
  },
12
12
  helpUrl: null,
13
- tags: ["wcag2a", "wcag412", "forms", "atomic", "automatic", "name", "textbox"],
13
+ tags: ['wcag2a', 'wcag412', 'forms', 'atomic', 'automatic', 'name', 'textbox'],
14
14
  wcagSc: ['4.1.2'],
15
15
  normativeMappings: [
16
- { standard: 'WCAG', version: '2.2', requirement: '4.1.2', title: 'Name, Role, Value', conformanceLevel: 'A' }
16
+ {
17
+ standard: 'WCAG',
18
+ version: '2.2',
19
+ requirement: '4.1.2',
20
+ title: 'Name, Role, Value',
21
+ conformanceLevel: 'A'
22
+ }
17
23
  ],
18
24
  defaultSeverity: 'serious',
19
25
  category: 'robust',
@@ -24,20 +30,22 @@ const meta = {
24
30
 
25
31
  function runInPage(ctx) {
26
32
  const { document, helpers, rule } = ctx;
27
- const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
- ? helpers.getEligibilityInfo
29
- : null;
30
-
33
+ const getEligibilityInfo =
34
+ helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
31
35
 
32
36
  function normalizeWs(s) {
33
- return String(s || '').replace(/\s+/g, ' ').trim();
37
+ return String(s || '')
38
+ .replace(/\s+/g, ' ')
39
+ .trim();
34
40
  }
35
41
 
36
42
  function getAttr(el, name) {
37
43
  try {
38
44
  if (!el || !el.getAttribute) return '';
39
45
  return normalizeWs(el.getAttribute(name));
40
- } catch { return ''; }
46
+ } catch {
47
+ return '';
48
+ }
41
49
  }
42
50
 
43
51
  function getConservativeSubtreeText(document, container) {
@@ -52,7 +60,7 @@ function runInPage(ctx) {
52
60
  const info = helpers.getContentNameInfo(container, ctx);
53
61
  return info && info.present ? info.value : '';
54
62
  }
55
- const t = (container && container.textContent) ? String(container.textContent) : '';
63
+ const t = container && container.textContent ? String(container.textContent) : '';
56
64
  return t.replace(/\s+/g, ' ').trim();
57
65
  }
58
66
 
@@ -100,7 +108,8 @@ function runInPage(ctx) {
100
108
  }
101
109
 
102
110
  function isEligibleAcc(helpers, el, ctx) {
103
- const fn = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
111
+ const fn =
112
+ helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
104
113
  if (!fn) return true;
105
114
  try {
106
115
  const r = fn(el, ctx);
@@ -126,12 +135,13 @@ function runInPage(ctx) {
126
135
  return map;
127
136
  }
128
137
 
129
-
130
138
  const occurrences = [];
131
139
  let applicableCount = 0;
132
140
 
133
- const selector = "[role=\"textbox\"]";
134
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
141
+ const selector = '[role="textbox"]';
142
+ const nodes = helpers.queryAllSmart
143
+ ? helpers.queryAllSmart(selector)
144
+ : helpers.queryAll(selector);
135
145
 
136
146
  // Precompute label[for] map for textbox elements that are labelable
137
147
  // native form controls (e.g. <input role="textbox">).
@@ -200,16 +210,22 @@ function runInPage(ctx) {
200
210
  if (res.ok) continue;
201
211
 
202
212
  const eligInfo = getEligibilityInfo
203
- ? (() => { try { return getEligibilityInfo(el, ctx, { targetSet: 'acc' }); } catch { return null; } })()
204
- : null;
213
+ ? (() => {
214
+ try {
215
+ return getEligibilityInfo(el, ctx, { targetSet: 'acc' });
216
+ } catch {
217
+ return null;
218
+ }
219
+ })()
220
+ : null;
205
221
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
206
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
222
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
207
223
 
208
224
  occurrences.push({
209
225
  selector: stableSelector,
210
226
  html,
211
227
  summary: 'This element has no accessible name.',
212
- hint: 'Provide aria-label or aria-labelledby (preferred), or provide visible text that is not hidden from assistive technologies.',
228
+ hint: "Provide aria-label, aria-labelledby, or a title attribute — visible text content is not exposed as this textbox's accessible name.",
213
229
  i18n: {
214
230
  summaryKey: 'textboxNamePresent_summary_fail',
215
231
  hintKey: 'textboxNamePresent_hint_fail',
@@ -222,12 +238,16 @@ function runInPage(ctx) {
222
238
  });
223
239
  }
224
240
 
225
-
226
241
  if (applicableCount === 0) {
227
242
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
228
243
  }
229
244
  if (occurrences.length) {
230
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
245
+ return {
246
+ ruleId: rule.ruleId,
247
+ outcome: 'fail',
248
+ severity: rule.defaultSeverity || 'minor',
249
+ occurrences
250
+ };
231
251
  }
232
252
  return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
233
253
  }
@@ -13,7 +13,13 @@ const meta = {
13
13
  tags: ['wcag2a', 'wcag412', 'navigation', 'atomic', 'automatic', 'name'],
14
14
  wcagSc: ['4.1.2'],
15
15
  normativeMappings: [
16
- { standard: 'WCAG', version: '2.2', requirement: '4.1.2', title: 'Name, Role, Value', conformanceLevel: 'A' }
16
+ {
17
+ standard: 'WCAG',
18
+ version: '2.2',
19
+ requirement: '4.1.2',
20
+ title: 'Name, Role, Value',
21
+ conformanceLevel: 'A'
22
+ }
17
23
  ],
18
24
  defaultSeverity: 'serious',
19
25
  category: 'robust',
@@ -24,20 +30,22 @@ const meta = {
24
30
 
25
31
  function runInPage(ctx) {
26
32
  const { document, helpers, rule } = ctx;
27
- const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
- ? helpers.getEligibilityInfo
29
- : null;
30
-
33
+ const getEligibilityInfo =
34
+ helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
31
35
 
32
36
  function normalizeWs(s) {
33
- return String(s || '').replace(/\s+/g, ' ').trim();
37
+ return String(s || '')
38
+ .replace(/\s+/g, ' ')
39
+ .trim();
34
40
  }
35
41
 
36
42
  function getAttr(el, name) {
37
43
  try {
38
44
  if (!el || !el.getAttribute) return '';
39
45
  return normalizeWs(el.getAttribute(name));
40
- } catch { return ''; }
46
+ } catch {
47
+ return '';
48
+ }
41
49
  }
42
50
 
43
51
  function getConservativeSubtreeText(document, container) {
@@ -52,7 +60,7 @@ function runInPage(ctx) {
52
60
  const info = helpers.getContentNameInfo(container, ctx);
53
61
  return info && info.present ? info.value : '';
54
62
  }
55
- const t = (container && container.textContent) ? String(container.textContent) : '';
63
+ const t = container && container.textContent ? String(container.textContent) : '';
56
64
  return t.replace(/\s+/g, ' ').trim();
57
65
  }
58
66
 
@@ -74,7 +82,8 @@ function runInPage(ctx) {
74
82
  }
75
83
 
76
84
  function isEligibleAcc(helpers, el, ctx) {
77
- const fn = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
85
+ const fn =
86
+ helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
78
87
  if (!fn) return true;
79
88
  try {
80
89
  const r = fn(el, ctx);
@@ -85,13 +94,13 @@ function runInPage(ctx) {
85
94
  }
86
95
  }
87
96
 
88
-
89
97
  const occurrences = [];
90
98
  let applicableCount = 0;
91
99
 
92
100
  const selector = '[role="tooltip"]';
93
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
94
-
101
+ const nodes = helpers.queryAllSmart
102
+ ? helpers.queryAllSmart(selector)
103
+ : helpers.queryAll(selector);
95
104
 
96
105
  function evaluate(el) {
97
106
  const ariaLabel = getAttr(el, 'aria-label');
@@ -122,10 +131,16 @@ function runInPage(ctx) {
122
131
  if (res.ok) continue;
123
132
 
124
133
  const eligInfo = getEligibilityInfo
125
- ? (() => { try { return getEligibilityInfo(el, ctx, { targetSet: 'acc' }); } catch { return null; } })()
126
- : null;
134
+ ? (() => {
135
+ try {
136
+ return getEligibilityInfo(el, ctx, { targetSet: 'acc' });
137
+ } catch {
138
+ return null;
139
+ }
140
+ })()
141
+ : null;
127
142
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
128
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
143
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
129
144
 
130
145
  occurrences.push({
131
146
  selector: stableSelector,
@@ -144,14 +159,18 @@ function runInPage(ctx) {
144
159
  });
145
160
  }
146
161
 
147
-
148
162
  if (applicableCount === 0) {
149
163
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
150
164
  }
151
165
  if (occurrences.length) {
152
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
166
+ return {
167
+ ruleId: rule.ruleId,
168
+ outcome: 'fail',
169
+ severity: rule.defaultSeverity || 'minor',
170
+ occurrences
171
+ };
153
172
  }
154
173
  return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
155
174
  }
156
175
 
157
- module.exports = { id, meta, runInPage };
176
+ module.exports = { id, meta, runInPage };
@@ -10,10 +10,16 @@ const meta = {
10
10
  descriptionKey: 'treeitemNamePresent_description'
11
11
  },
12
12
  helpUrl: null,
13
- tags: ["wcag2a", "wcag412", "navigation", "atomic", "automatic", "name", "treeitem"],
13
+ tags: ['wcag2a', 'wcag412', 'navigation', 'atomic', 'automatic', 'name', 'treeitem'],
14
14
  wcagSc: ['4.1.2'],
15
15
  normativeMappings: [
16
- { standard: 'WCAG', version: '2.2', requirement: '4.1.2', title: 'Name, Role, Value', conformanceLevel: 'A' }
16
+ {
17
+ standard: 'WCAG',
18
+ version: '2.2',
19
+ requirement: '4.1.2',
20
+ title: 'Name, Role, Value',
21
+ conformanceLevel: 'A'
22
+ }
17
23
  ],
18
24
  defaultSeverity: 'serious',
19
25
  category: 'robust',
@@ -24,20 +30,22 @@ const meta = {
24
30
 
25
31
  function runInPage(ctx) {
26
32
  const { document, helpers, rule } = ctx;
27
- const getEligibilityInfo = helpers && typeof helpers.getEligibilityInfo === 'function'
28
- ? helpers.getEligibilityInfo
29
- : null;
30
-
33
+ const getEligibilityInfo =
34
+ helpers && typeof helpers.getEligibilityInfo === 'function' ? helpers.getEligibilityInfo : null;
31
35
 
32
36
  function normalizeWs(s) {
33
- return String(s || '').replace(/\s+/g, ' ').trim();
37
+ return String(s || '')
38
+ .replace(/\s+/g, ' ')
39
+ .trim();
34
40
  }
35
41
 
36
42
  function getAttr(el, name) {
37
43
  try {
38
44
  if (!el || !el.getAttribute) return '';
39
45
  return normalizeWs(el.getAttribute(name));
40
- } catch { return ''; }
46
+ } catch {
47
+ return '';
48
+ }
41
49
  }
42
50
 
43
51
  function getConservativeSubtreeText(document, container) {
@@ -52,7 +60,7 @@ function runInPage(ctx) {
52
60
  const info = helpers.getContentNameInfo(container, ctx);
53
61
  return info && info.present ? info.value : '';
54
62
  }
55
- const t = (container && container.textContent) ? String(container.textContent) : '';
63
+ const t = container && container.textContent ? String(container.textContent) : '';
56
64
  return t.replace(/\s+/g, ' ').trim();
57
65
  }
58
66
 
@@ -74,7 +82,8 @@ function runInPage(ctx) {
74
82
  }
75
83
 
76
84
  function isEligibleAcc(helpers, el, ctx) {
77
- const fn = helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
85
+ const fn =
86
+ helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
78
87
  if (!fn) return true;
79
88
  try {
80
89
  const r = fn(el, ctx);
@@ -85,13 +94,13 @@ function runInPage(ctx) {
85
94
  }
86
95
  }
87
96
 
88
-
89
97
  const occurrences = [];
90
98
  let applicableCount = 0;
91
99
 
92
- const selector = "[role=\"treeitem\"]";
93
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart(selector) : helpers.queryAll(selector);
94
-
100
+ const selector = '[role="treeitem"]';
101
+ const nodes = helpers.queryAllSmart
102
+ ? helpers.queryAllSmart(selector)
103
+ : helpers.queryAll(selector);
95
104
 
96
105
  function hasName(el) {
97
106
  const ariaLabel = getAttr(el, 'aria-label');
@@ -103,11 +112,9 @@ function runInPage(ctx) {
103
112
  const title = getAttr(el, 'title');
104
113
  if (title) return { ok: true, method: 'title' };
105
114
 
106
-
107
115
  const t = getConservativeSubtreeText(document, el);
108
116
  if (t) return { ok: true, method: 'content' };
109
117
 
110
-
111
118
  return { ok: false, method: 'none' };
112
119
  }
113
120
 
@@ -121,10 +128,16 @@ function runInPage(ctx) {
121
128
  if (res.ok) continue;
122
129
 
123
130
  const eligInfo = getEligibilityInfo
124
- ? (() => { try { return getEligibilityInfo(el, ctx, { targetSet: 'acc' }); } catch { return null; } })()
125
- : null;
131
+ ? (() => {
132
+ try {
133
+ return getEligibilityInfo(el, ctx, { targetSet: 'acc' });
134
+ } catch {
135
+ return null;
136
+ }
137
+ })()
138
+ : null;
126
139
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
127
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
140
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
128
141
 
129
142
  occurrences.push({
130
143
  selector: stableSelector,
@@ -143,12 +156,16 @@ function runInPage(ctx) {
143
156
  });
144
157
  }
145
158
 
146
-
147
159
  if (applicableCount === 0) {
148
160
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
149
161
  }
150
162
  if (occurrences.length) {
151
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'minor', occurrences };
163
+ return {
164
+ ruleId: rule.ruleId,
165
+ outcome: 'fail',
166
+ severity: rule.defaultSeverity || 'minor',
167
+ occurrences
168
+ };
152
169
  }
153
170
  return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
154
171
  }
@@ -28,7 +28,8 @@ const id = 'valid-lang';
28
28
 
29
29
  const meta = {
30
30
  title: 'Element lang attribute must be syntactically valid',
31
- description: 'Checks that any element (other than the root <html>) with a non-empty lang attribute uses a syntactically valid language tag.',
31
+ description:
32
+ 'Checks that any element (other than the root <html>) with a non-empty lang attribute uses a syntactically valid language tag.',
32
33
  i18n: {
33
34
  titleKey: 'validLang_title',
34
35
  descriptionKey: 'validLang_description'
@@ -37,7 +38,13 @@ const meta = {
37
38
  tags: ['wcag2aa', 'wcag312', 'structure', 'language', 'atomic', 'automatic'],
38
39
  wcagSc: ['3.1.2'],
39
40
  normativeMappings: [
40
- { standard: 'WCAG', version: '2.2', requirement: '3.1.2', title: 'Language of Parts', conformanceLevel: 'AA' }
41
+ {
42
+ standard: 'WCAG',
43
+ version: '2.2',
44
+ requirement: '3.1.2',
45
+ title: 'Language of Parts',
46
+ conformanceLevel: 'AA'
47
+ }
41
48
  ],
42
49
  defaultSeverity: 'moderate',
43
50
  category: 'understandable',
@@ -47,11 +54,13 @@ const meta = {
47
54
  };
48
55
 
49
56
  function runInPage(ctx) {
50
- const { document, helpers, rule } = ctx;
57
+ const { helpers, rule } = ctx;
51
58
 
52
59
  const BCP47_RE = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})*$/;
53
60
 
54
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[lang]') : helpers.queryAll('[lang]');
61
+ const nodes = helpers.queryAllSmart
62
+ ? helpers.queryAllSmart('[lang]')
63
+ : helpers.queryAll('[lang]');
55
64
 
56
65
  const occurrences = [];
57
66
  let applicableCount = 0;
@@ -69,7 +78,7 @@ function runInPage(ctx) {
69
78
 
70
79
  const tag = el.tagName.toLowerCase();
71
80
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
72
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
81
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
73
82
 
74
83
  occurrences.push({
75
84
  selector: stableSelector,
@@ -91,7 +100,12 @@ function runInPage(ctx) {
91
100
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
92
101
  }
93
102
  if (occurrences.length) {
94
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'moderate', occurrences };
103
+ return {
104
+ ruleId: rule.ruleId,
105
+ outcome: 'fail',
106
+ severity: rule.defaultSeverity || 'moderate',
107
+ occurrences
108
+ };
95
109
  }
96
110
  return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
97
111
  }