@surea11y/core 1.2.0 → 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 (157) hide show
  1. package/CHANGELOG.md +37 -7
  2. package/LICENSE +373 -21
  3. package/README.md +67 -1
  4. package/bin/core.js +144 -19
  5. package/docs/API_STABILITY.md +1 -1
  6. package/docs/BINDING_AUTHORS_GUIDE.md +9 -9
  7. package/docs/CI_INTEGRATIONS.md +103 -0
  8. package/docs/CLI.md +54 -1
  9. package/docs/ENGINE_OPTIONS.md +2 -0
  10. package/docs/INTEGRATION.md +18 -0
  11. package/docs/OUTPUT_SCHEMA.md +1 -1
  12. package/docs/RULE_CATALOG.md +1 -1
  13. package/docs/SARIF.md +59 -0
  14. package/package.json +15 -4
  15. package/src/baseline.js +0 -0
  16. package/src/catalogs/composites.wcag.js +414 -450
  17. package/src/checks/automatic/area-alt-present.js +59 -25
  18. package/src/checks/automatic/aria-allowed-attr.js +193 -33
  19. package/src/checks/automatic/aria-allowed-role.js +21 -7
  20. package/src/checks/automatic/aria-braille-equivalent.js +32 -10
  21. package/src/checks/automatic/aria-conditional-attr.js +24 -7
  22. package/src/checks/automatic/aria-deprecated-role.js +22 -8
  23. package/src/checks/automatic/aria-hidden-body.js +42 -19
  24. package/src/checks/automatic/aria-hidden-focus.js +408 -53
  25. package/src/checks/automatic/aria-prohibited-attr.js +296 -22
  26. package/src/checks/automatic/aria-prohibited-children.js +55 -16
  27. package/src/checks/automatic/aria-required-attr.js +23 -8
  28. package/src/checks/automatic/aria-required-children.js +37 -14
  29. package/src/checks/automatic/aria-required-parent.js +48 -14
  30. package/src/checks/automatic/aria-role-name-present.js +47 -21
  31. package/src/checks/automatic/aria-roles-valid.js +22 -12
  32. package/src/checks/automatic/aria-valid-attr-value.js +28 -7
  33. package/src/checks/automatic/aria-valid-attr.js +17 -5
  34. package/src/checks/automatic/autocomplete-valid.js +74 -16
  35. package/src/checks/automatic/avoid-inline-spacing.js +20 -6
  36. package/src/checks/automatic/binary-control-name-present.js +60 -50
  37. package/src/checks/automatic/button-name-present.js +48 -18
  38. package/src/checks/automatic/bypass-blocks-present.js +42 -25
  39. package/src/checks/automatic/canvas-text-alternative-present.js +57 -26
  40. package/src/checks/automatic/combobox-name-present.js +38 -45
  41. package/src/checks/automatic/contrast-computable.js +361 -341
  42. package/src/checks/automatic/contrast-enhanced.js +487 -466
  43. package/src/checks/automatic/contrast-minimum.js +486 -465
  44. package/src/checks/automatic/css-orientation-lock.js +30 -8
  45. package/src/checks/automatic/definition-list-children-valid.js +40 -19
  46. package/src/checks/automatic/deprecated-elements-not-used.js +21 -7
  47. package/src/checks/automatic/dialog-name-present.js +37 -75
  48. package/src/checks/automatic/dlitem-parent-valid.js +23 -8
  49. package/src/checks/automatic/duplicate-id-aria.js +24 -6
  50. package/src/checks/automatic/embed-text-alternative-present.js +86 -35
  51. package/src/checks/automatic/form-control-programmatic-label-present.js +79 -196
  52. package/src/checks/automatic/form-control-single-label.js +47 -10
  53. package/src/checks/automatic/html-xml-lang-mismatch.js +34 -18
  54. package/src/checks/automatic/iframe-focusable-content.js +26 -11
  55. package/src/checks/automatic/iframe-name-present.js +31 -9
  56. package/src/checks/automatic/iframe-title-unique.js +29 -8
  57. package/src/checks/automatic/img-alt-present.js +47 -43
  58. package/src/checks/automatic/input-image-alt-present.js +143 -112
  59. package/src/checks/automatic/label-in-name.js +50 -22
  60. package/src/checks/automatic/language-page-present.js +109 -109
  61. package/src/checks/automatic/link-in-text-block.js +59 -19
  62. package/src/checks/automatic/link-name-present.js +45 -14
  63. package/src/checks/automatic/list-children-valid.js +26 -9
  64. package/src/checks/automatic/listbox-name-present.js +39 -19
  65. package/src/checks/automatic/listitem-parent-valid.js +18 -6
  66. package/src/checks/automatic/menuitem-name-present.js +39 -61
  67. package/src/checks/automatic/meta-refresh-no-exceptions.js +28 -7
  68. package/src/checks/automatic/meta-refresh-timing-absent.js +20 -6
  69. package/src/checks/automatic/meta-viewport-zoom-enabled.js +24 -7
  70. package/src/checks/automatic/meter-name-present.js +36 -33
  71. package/src/checks/automatic/nested-interactive-controls-absent.js +31 -10
  72. package/src/checks/automatic/object-text-alternative-present.js +91 -39
  73. package/src/checks/automatic/option-name-present.js +38 -21
  74. package/src/checks/automatic/page-title-present.js +17 -6
  75. package/src/checks/automatic/progressbar-name-present.js +41 -34
  76. package/src/checks/automatic/role-img-alt-present.js +209 -157
  77. package/src/checks/automatic/searchbox-name-present.js +39 -19
  78. package/src/checks/automatic/server-side-image-map-absent.js +23 -8
  79. package/src/checks/automatic/slider-name-present.js +40 -47
  80. package/src/checks/automatic/spinbutton-name-present.js +39 -19
  81. package/src/checks/automatic/summary-name-present.js +37 -17
  82. package/src/checks/automatic/svg-image-text-alternative-present.js +114 -47
  83. package/src/checks/automatic/svg-text-alternative-present.js +246 -226
  84. package/src/checks/automatic/tab-name-present.js +37 -60
  85. package/src/checks/automatic/table-headers-attr-valid.js +24 -8
  86. package/src/checks/automatic/table-th-has-data-cells.js +22 -8
  87. package/src/checks/automatic/target-size-minimum.js +118 -48
  88. package/src/checks/automatic/td-has-header.js +29 -11
  89. package/src/checks/automatic/textbox-name-present.js +39 -19
  90. package/src/checks/automatic/tooltip-name-present.js +37 -18
  91. package/src/checks/automatic/treeitem-name-present.js +38 -21
  92. package/src/checks/automatic/valid-lang.js +20 -6
  93. package/src/checks/automatic/video-poster-text-alternative-present.js +79 -36
  94. package/src/checks/manual/accesskeys-manual.js +14 -5
  95. package/src/checks/manual/area-alt-decorative-manual.js +192 -193
  96. package/src/checks/manual/area-alt-quality-manual.js +182 -141
  97. package/src/checks/manual/aria-checked-state-mismatch-manual.js +34 -11
  98. package/src/checks/manual/aria-text-manual.js +14 -6
  99. package/src/checks/manual/canvas-text-alternative-quality-manual.js +149 -114
  100. package/src/checks/manual/css-hidden-focus.js +196 -165
  101. package/src/checks/manual/embed-text-alternative-quality-manual.js +171 -160
  102. package/src/checks/manual/empty-heading-manual.js +24 -7
  103. package/src/checks/manual/empty-table-header-manual.js +17 -6
  104. package/src/checks/manual/focus-order-semantics-manual.js +45 -10
  105. package/src/checks/manual/form-control-programmatic-label-quality-manual.js +207 -246
  106. package/src/checks/manual/heading-order-manual.js +22 -7
  107. package/src/checks/manual/identical-links-same-purpose-manual.js +34 -12
  108. package/src/checks/manual/image-redundant-alt-manual.js +17 -7
  109. package/src/checks/manual/img-alt-decorative-manual.js +131 -96
  110. package/src/checks/manual/img-alt-quality-manual.js +176 -127
  111. package/src/checks/manual/input-image-alt-decorative-manual.js +125 -92
  112. package/src/checks/manual/input-image-alt-quality-manual.js +125 -92
  113. package/src/checks/manual/label-title-only-manual.js +14 -5
  114. package/src/checks/manual/landmark-banner-is-top-level-manual.js +66 -16
  115. package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +53 -16
  116. package/src/checks/manual/landmark-main-is-top-level-manual.js +35 -10
  117. package/src/checks/manual/landmark-no-duplicate-banner-manual.js +29 -10
  118. package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +29 -10
  119. package/src/checks/manual/landmark-no-duplicate-main-manual.js +17 -8
  120. package/src/checks/manual/landmark-one-main-manual.js +26 -20
  121. package/src/checks/manual/landmark-unique-manual.js +41 -15
  122. package/src/checks/manual/link-name-quality-manual.js +43 -12
  123. package/src/checks/manual/media-transcript-present-manual.js +35 -22
  124. package/src/checks/manual/meta-viewport-large-manual.js +16 -5
  125. package/src/checks/manual/mouse-only-event-handlers-manual.js +38 -11
  126. package/src/checks/manual/no-autoplay-audio-manual.js +20 -6
  127. package/src/checks/manual/object-text-alternative-quality-manual.js +175 -154
  128. package/src/checks/manual/p-as-heading-manual.js +22 -7
  129. package/src/checks/manual/page-has-heading-one-manual.js +31 -22
  130. package/src/checks/manual/page-title-patterns-manual.js +78 -50
  131. package/src/checks/manual/presentation-role-conflict-manual.js +59 -19
  132. package/src/checks/manual/region-manual.js +241 -48
  133. package/src/checks/manual/scope-attr-valid-manual.js +10 -3
  134. package/src/checks/manual/scrollable-region-focusable-manual.js +37 -11
  135. package/src/checks/manual/skip-link-manual.js +35 -12
  136. package/src/checks/manual/svg-text-alternative-quality-manual.js +206 -165
  137. package/src/checks/manual/tabindex-manual.js +10 -3
  138. package/src/checks/manual/table-duplicate-name-manual.js +17 -7
  139. package/src/checks/manual/table-fake-caption-manual.js +24 -7
  140. package/src/checks/manual/video-caption-manual.js +15 -4
  141. package/src/checks/manual-review.js +56 -12
  142. package/src/core/aria-helpers.js +1127 -886
  143. package/src/core/contrast-helpers.js +1217 -1062
  144. package/src/core/dom-helpers.js +4175 -3917
  145. package/src/core/dom-runner.js +720 -604
  146. package/src/core/frame-messaging.js +189 -138
  147. package/src/core/frame-scan.js +94 -82
  148. package/src/core/rollup-composites.js +94 -102
  149. package/src/core/rule-meta.js +58 -41
  150. package/src/core.js +36552 -29111
  151. package/src/i18n/en.js +1194 -889
  152. package/src/i18n/fr.js +1136 -795
  153. package/src/policy/contracts.js +13 -13
  154. package/src/policy/resolvePolicy.js +48 -44
  155. package/src/report.js +63 -43
  156. package/src/sarif.js +175 -0
  157. package/surea11y.browser.js +36042 -0
@@ -43,7 +43,13 @@ const meta = {
43
43
  tags: ['wcag2a', 'wcag412', 'aria', 'structure', 'atomic', 'automatic'],
44
44
  wcagSc: ['4.1.2'],
45
45
  normativeMappings: [
46
- { standard: 'WCAG', version: '2.2', requirement: '4.1.2', title: 'Name, Role, Value', conformanceLevel: 'A' }
46
+ {
47
+ standard: 'WCAG',
48
+ version: '2.2',
49
+ requirement: '4.1.2',
50
+ title: 'Name, Role, Value',
51
+ conformanceLevel: 'A'
52
+ }
47
53
  ],
48
54
  defaultSeverity: 'serious',
49
55
  category: 'robust',
@@ -53,9 +59,11 @@ const meta = {
53
59
  };
54
60
 
55
61
  function runInPage(ctx) {
56
- const { document, helpers, rule } = ctx;
62
+ const { helpers, rule } = ctx;
57
63
 
58
- function trim(v) { return (v == null ? '' : String(v)).trim(); }
64
+ function trim(v) {
65
+ return (v == null ? '' : String(v)).trim();
66
+ }
59
67
 
60
68
  const TRUTHY_INVALID_VALUES = new Set(['true', 'grammar', 'spelling']);
61
69
 
@@ -78,13 +86,14 @@ function runInPage(ctx) {
78
86
  if (TRUTHY_INVALID_VALUES.has(invalidValue)) continue;
79
87
 
80
88
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
81
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
89
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
82
90
  const tag = (el.tagName || '').toLowerCase();
83
91
 
84
92
  occurrences.push({
85
93
  selector: stableSelector,
86
94
  html,
87
- summary: 'This element has aria-errormessage but aria-invalid is missing or "false", so the error message is not exposed.',
95
+ summary:
96
+ 'This element has aria-errormessage but aria-invalid is missing or "false", so the error message is not exposed.',
88
97
  hint: 'Set aria-invalid to "true" (or "grammar"/"spelling") whenever aria-errormessage should be exposed to assistive technology.',
89
98
  i18n: {
90
99
  summaryKey: 'ariaConditionalAttr_summary_fail',
@@ -92,7 +101,10 @@ function runInPage(ctx) {
92
101
  params: { element: tag, ariaInvalid: invalidValue || '(absent)' }
93
102
  },
94
103
  data: {
95
- details: { reasonCode: 'ARIA_ERRORMESSAGE_WITHOUT_TRUTHY_INVALID', ariaInvalid: invalidValue }
104
+ details: {
105
+ reasonCode: 'ARIA_ERRORMESSAGE_WITHOUT_TRUTHY_INVALID',
106
+ ariaInvalid: invalidValue
107
+ }
96
108
  }
97
109
  });
98
110
  }
@@ -101,7 +113,12 @@ function runInPage(ctx) {
101
113
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
102
114
  }
103
115
  if (occurrences.length) {
104
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'serious', occurrences };
116
+ return {
117
+ ruleId: rule.ruleId,
118
+ outcome: 'fail',
119
+ severity: rule.defaultSeverity || 'serious',
120
+ occurrences
121
+ };
105
122
  }
106
123
  return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
107
124
  }
@@ -26,7 +26,8 @@ const id = 'aria-deprecated-role';
26
26
 
27
27
  const meta = {
28
28
  title: 'role attribute must not use a deprecated or author-prohibited ARIA role',
29
- description: 'Checks that an explicit role="" attribute does not use a role deprecated by the WAI-ARIA specification, or one reserved for user-agent-internal use only (e.g. role="generic").',
29
+ description:
30
+ 'Checks that an explicit role="" attribute does not use a role deprecated by the WAI-ARIA specification, or one reserved for user-agent-internal use only (e.g. role="generic").',
30
31
  i18n: {
31
32
  titleKey: 'ariaDeprecatedRole_title',
32
33
  descriptionKey: 'ariaDeprecatedRole_description'
@@ -35,7 +36,13 @@ const meta = {
35
36
  tags: ['wcag2a', 'wcag412', 'aria', 'structure', 'atomic', 'automatic'],
36
37
  wcagSc: ['4.1.2'],
37
38
  normativeMappings: [
38
- { standard: 'WCAG', version: '2.2', requirement: '4.1.2', title: 'Name, Role, Value', conformanceLevel: 'A' }
39
+ {
40
+ standard: 'WCAG',
41
+ version: '2.2',
42
+ requirement: '4.1.2',
43
+ title: 'Name, Role, Value',
44
+ conformanceLevel: 'A'
45
+ }
39
46
  ],
40
47
  defaultSeverity: 'moderate',
41
48
  category: 'robust',
@@ -45,14 +52,16 @@ const meta = {
45
52
  };
46
53
 
47
54
  function runInPage(ctx) {
48
- const { document, helpers, rule } = ctx;
55
+ const { helpers, rule } = ctx;
49
56
 
50
57
  const ariaHelpers = helpers && helpers.aria ? helpers.aria : null;
51
58
  if (!ariaHelpers) {
52
59
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
53
60
  }
54
61
 
55
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('[role]') : helpers.queryAll('[role]');
62
+ const nodes = helpers.queryAllSmart
63
+ ? helpers.queryAllSmart('[role]')
64
+ : helpers.queryAll('[role]');
56
65
 
57
66
  const occurrences = [];
58
67
  let applicableCount = 0;
@@ -72,10 +81,10 @@ function runInPage(ctx) {
72
81
  if (!ariaHelpers.isDeprecatedRole(role)) continue;
73
82
 
74
83
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
75
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
84
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
76
85
  const guidance = ariaHelpers.getDeprecatedRoleGuidance
77
- ? ariaHelpers.getDeprecatedRoleGuidance(role)
78
- : 'Replace the deprecated role with its recommended replacement.';
86
+ ? ariaHelpers.getDeprecatedRoleGuidance(role)
87
+ : 'Replace the deprecated role with its recommended replacement.';
79
88
 
80
89
  occurrences.push({
81
90
  selector: stableSelector,
@@ -97,7 +106,12 @@ function runInPage(ctx) {
97
106
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
98
107
  }
99
108
  if (occurrences.length) {
100
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'moderate', occurrences };
109
+ return {
110
+ ruleId: rule.ruleId,
111
+ outcome: 'fail',
112
+ severity: rule.defaultSeverity || 'moderate',
113
+ occurrences
114
+ };
101
115
  }
102
116
  return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
103
117
  }
@@ -24,7 +24,8 @@ const id = 'aria-hidden-body';
24
24
 
25
25
  const meta = {
26
26
  title: 'The document <body> must not be aria-hidden',
27
- description: 'Checks that <body> does not have aria-hidden="true", which would remove the entire page from the accessibility tree.',
27
+ description:
28
+ 'Checks that <body> does not have aria-hidden="true", which would remove the entire page from the accessibility tree.',
28
29
  i18n: {
29
30
  titleKey: 'ariaHiddenBody_title',
30
31
  descriptionKey: 'ariaHiddenBody_description'
@@ -33,8 +34,20 @@ const meta = {
33
34
  tags: ['wcag2a', 'wcag131', 'wcag412', 'structure', 'atomic', 'automatic'],
34
35
  wcagSc: ['1.3.1', '4.1.2'],
35
36
  normativeMappings: [
36
- { standard: 'WCAG', version: '2.2', requirement: '1.3.1', title: 'Info and Relationships', conformanceLevel: 'A' },
37
- { standard: 'WCAG', version: '2.2', requirement: '4.1.2', title: 'Name, Role, Value', conformanceLevel: 'A' }
37
+ {
38
+ standard: 'WCAG',
39
+ version: '2.2',
40
+ requirement: '1.3.1',
41
+ title: 'Info and Relationships',
42
+ conformanceLevel: 'A'
43
+ },
44
+ {
45
+ standard: 'WCAG',
46
+ version: '2.2',
47
+ requirement: '4.1.2',
48
+ title: 'Name, Role, Value',
49
+ conformanceLevel: 'A'
50
+ }
38
51
  ],
39
52
  defaultSeverity: 'critical',
40
53
  category: 'perceivable',
@@ -72,24 +85,34 @@ function runInPage(ctx) {
72
85
  }
73
86
 
74
87
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(body) : 'body';
75
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(body) : (body.outerHTML || '').slice(0, 200);
88
+ const html = helpers.getOuterHtmlSnippet
89
+ ? helpers.getOuterHtmlSnippet(body)
90
+ : (body.outerHTML || '').slice(0, 200);
76
91
 
77
- const occurrences = [{
78
- selector: stableSelector,
79
- html,
80
- summary: 'The document body has aria-hidden="true", which hides the entire page from assistive technologies.',
81
- hint: 'Remove aria-hidden from <body>. Hide specific elements instead, if that was the intent.',
82
- i18n: {
83
- summaryKey: 'ariaHiddenBody_summary_fail',
84
- hintKey: 'ariaHiddenBody_hint_fail',
85
- params: {}
86
- },
87
- data: {
88
- details: { reasonCode: 'ARIA_HIDDEN_BODY' }
92
+ const occurrences = [
93
+ {
94
+ selector: stableSelector,
95
+ html,
96
+ summary:
97
+ 'The document body has aria-hidden="true", which hides the entire page from assistive technologies.',
98
+ hint: 'Remove aria-hidden from <body>. Hide specific elements instead, if that was the intent.',
99
+ i18n: {
100
+ summaryKey: 'ariaHiddenBody_summary_fail',
101
+ hintKey: 'ariaHiddenBody_hint_fail',
102
+ params: {}
103
+ },
104
+ data: {
105
+ details: { reasonCode: 'ARIA_HIDDEN_BODY' }
106
+ }
89
107
  }
90
- }];
108
+ ];
91
109
 
92
- return { ruleId: rule.ruleId, outcome: 'fail', severity: rule.defaultSeverity || 'critical', occurrences };
110
+ return {
111
+ ruleId: rule.ruleId,
112
+ outcome: 'fail',
113
+ severity: rule.defaultSeverity || 'critical',
114
+ occurrences
115
+ };
93
116
  }
94
117
 
95
- module.exports = { id, meta, runInPage, applicability };
118
+ module.exports = { id, meta, runInPage, applicability };