@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.
- package/CHANGELOG.md +37 -7
- package/LICENSE +373 -21
- package/README.md +67 -1
- package/bin/core.js +144 -19
- package/docs/API_STABILITY.md +1 -1
- package/docs/BINDING_AUTHORS_GUIDE.md +9 -9
- package/docs/CI_INTEGRATIONS.md +103 -0
- package/docs/CLI.md +54 -1
- package/docs/ENGINE_OPTIONS.md +2 -0
- package/docs/INTEGRATION.md +18 -0
- package/docs/OUTPUT_SCHEMA.md +1 -1
- package/docs/RULE_CATALOG.md +1 -1
- package/docs/SARIF.md +59 -0
- package/package.json +15 -4
- package/src/baseline.js +0 -0
- package/src/catalogs/composites.wcag.js +414 -450
- package/src/checks/automatic/area-alt-present.js +59 -25
- package/src/checks/automatic/aria-allowed-attr.js +193 -33
- package/src/checks/automatic/aria-allowed-role.js +21 -7
- package/src/checks/automatic/aria-braille-equivalent.js +32 -10
- package/src/checks/automatic/aria-conditional-attr.js +24 -7
- package/src/checks/automatic/aria-deprecated-role.js +22 -8
- package/src/checks/automatic/aria-hidden-body.js +42 -19
- package/src/checks/automatic/aria-hidden-focus.js +408 -53
- package/src/checks/automatic/aria-prohibited-attr.js +296 -22
- package/src/checks/automatic/aria-prohibited-children.js +55 -16
- package/src/checks/automatic/aria-required-attr.js +23 -8
- package/src/checks/automatic/aria-required-children.js +37 -14
- package/src/checks/automatic/aria-required-parent.js +48 -14
- package/src/checks/automatic/aria-role-name-present.js +47 -21
- package/src/checks/automatic/aria-roles-valid.js +22 -12
- package/src/checks/automatic/aria-valid-attr-value.js +28 -7
- package/src/checks/automatic/aria-valid-attr.js +17 -5
- package/src/checks/automatic/autocomplete-valid.js +74 -16
- package/src/checks/automatic/avoid-inline-spacing.js +20 -6
- package/src/checks/automatic/binary-control-name-present.js +60 -50
- package/src/checks/automatic/button-name-present.js +48 -18
- package/src/checks/automatic/bypass-blocks-present.js +42 -25
- package/src/checks/automatic/canvas-text-alternative-present.js +57 -26
- package/src/checks/automatic/combobox-name-present.js +38 -45
- package/src/checks/automatic/contrast-computable.js +361 -341
- package/src/checks/automatic/contrast-enhanced.js +487 -466
- package/src/checks/automatic/contrast-minimum.js +486 -465
- package/src/checks/automatic/css-orientation-lock.js +30 -8
- package/src/checks/automatic/definition-list-children-valid.js +40 -19
- package/src/checks/automatic/deprecated-elements-not-used.js +21 -7
- package/src/checks/automatic/dialog-name-present.js +37 -75
- package/src/checks/automatic/dlitem-parent-valid.js +23 -8
- package/src/checks/automatic/duplicate-id-aria.js +24 -6
- package/src/checks/automatic/embed-text-alternative-present.js +86 -35
- package/src/checks/automatic/form-control-programmatic-label-present.js +79 -196
- package/src/checks/automatic/form-control-single-label.js +47 -10
- package/src/checks/automatic/html-xml-lang-mismatch.js +34 -18
- package/src/checks/automatic/iframe-focusable-content.js +26 -11
- package/src/checks/automatic/iframe-name-present.js +31 -9
- package/src/checks/automatic/iframe-title-unique.js +29 -8
- package/src/checks/automatic/img-alt-present.js +47 -43
- package/src/checks/automatic/input-image-alt-present.js +143 -112
- package/src/checks/automatic/label-in-name.js +50 -22
- package/src/checks/automatic/language-page-present.js +109 -109
- package/src/checks/automatic/link-in-text-block.js +59 -19
- package/src/checks/automatic/link-name-present.js +45 -14
- package/src/checks/automatic/list-children-valid.js +26 -9
- package/src/checks/automatic/listbox-name-present.js +39 -19
- package/src/checks/automatic/listitem-parent-valid.js +18 -6
- package/src/checks/automatic/menuitem-name-present.js +39 -61
- package/src/checks/automatic/meta-refresh-no-exceptions.js +28 -7
- package/src/checks/automatic/meta-refresh-timing-absent.js +20 -6
- package/src/checks/automatic/meta-viewport-zoom-enabled.js +24 -7
- package/src/checks/automatic/meter-name-present.js +36 -33
- package/src/checks/automatic/nested-interactive-controls-absent.js +31 -10
- package/src/checks/automatic/object-text-alternative-present.js +91 -39
- package/src/checks/automatic/option-name-present.js +38 -21
- package/src/checks/automatic/page-title-present.js +17 -6
- package/src/checks/automatic/progressbar-name-present.js +41 -34
- package/src/checks/automatic/role-img-alt-present.js +209 -157
- package/src/checks/automatic/searchbox-name-present.js +39 -19
- package/src/checks/automatic/server-side-image-map-absent.js +23 -8
- package/src/checks/automatic/slider-name-present.js +40 -47
- package/src/checks/automatic/spinbutton-name-present.js +39 -19
- package/src/checks/automatic/summary-name-present.js +37 -17
- package/src/checks/automatic/svg-image-text-alternative-present.js +114 -47
- package/src/checks/automatic/svg-text-alternative-present.js +246 -226
- package/src/checks/automatic/tab-name-present.js +37 -60
- package/src/checks/automatic/table-headers-attr-valid.js +24 -8
- package/src/checks/automatic/table-th-has-data-cells.js +22 -8
- package/src/checks/automatic/target-size-minimum.js +118 -48
- package/src/checks/automatic/td-has-header.js +29 -11
- package/src/checks/automatic/textbox-name-present.js +39 -19
- package/src/checks/automatic/tooltip-name-present.js +37 -18
- package/src/checks/automatic/treeitem-name-present.js +38 -21
- package/src/checks/automatic/valid-lang.js +20 -6
- package/src/checks/automatic/video-poster-text-alternative-present.js +79 -36
- package/src/checks/manual/accesskeys-manual.js +14 -5
- package/src/checks/manual/area-alt-decorative-manual.js +192 -193
- package/src/checks/manual/area-alt-quality-manual.js +182 -141
- package/src/checks/manual/aria-checked-state-mismatch-manual.js +34 -11
- package/src/checks/manual/aria-text-manual.js +14 -6
- package/src/checks/manual/canvas-text-alternative-quality-manual.js +149 -114
- package/src/checks/manual/css-hidden-focus.js +196 -165
- package/src/checks/manual/embed-text-alternative-quality-manual.js +171 -160
- package/src/checks/manual/empty-heading-manual.js +24 -7
- package/src/checks/manual/empty-table-header-manual.js +17 -6
- package/src/checks/manual/focus-order-semantics-manual.js +45 -10
- package/src/checks/manual/form-control-programmatic-label-quality-manual.js +207 -246
- package/src/checks/manual/heading-order-manual.js +22 -7
- package/src/checks/manual/identical-links-same-purpose-manual.js +34 -12
- package/src/checks/manual/image-redundant-alt-manual.js +17 -7
- package/src/checks/manual/img-alt-decorative-manual.js +131 -96
- package/src/checks/manual/img-alt-quality-manual.js +176 -127
- package/src/checks/manual/input-image-alt-decorative-manual.js +125 -92
- package/src/checks/manual/input-image-alt-quality-manual.js +125 -92
- package/src/checks/manual/label-title-only-manual.js +14 -5
- package/src/checks/manual/landmark-banner-is-top-level-manual.js +66 -16
- package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +53 -16
- package/src/checks/manual/landmark-main-is-top-level-manual.js +35 -10
- package/src/checks/manual/landmark-no-duplicate-banner-manual.js +29 -10
- package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +29 -10
- package/src/checks/manual/landmark-no-duplicate-main-manual.js +17 -8
- package/src/checks/manual/landmark-one-main-manual.js +26 -20
- package/src/checks/manual/landmark-unique-manual.js +41 -15
- package/src/checks/manual/link-name-quality-manual.js +43 -12
- package/src/checks/manual/media-transcript-present-manual.js +35 -22
- package/src/checks/manual/meta-viewport-large-manual.js +16 -5
- package/src/checks/manual/mouse-only-event-handlers-manual.js +38 -11
- package/src/checks/manual/no-autoplay-audio-manual.js +20 -6
- package/src/checks/manual/object-text-alternative-quality-manual.js +175 -154
- package/src/checks/manual/p-as-heading-manual.js +22 -7
- package/src/checks/manual/page-has-heading-one-manual.js +31 -22
- package/src/checks/manual/page-title-patterns-manual.js +78 -50
- package/src/checks/manual/presentation-role-conflict-manual.js +59 -19
- package/src/checks/manual/region-manual.js +241 -48
- package/src/checks/manual/scope-attr-valid-manual.js +10 -3
- package/src/checks/manual/scrollable-region-focusable-manual.js +37 -11
- package/src/checks/manual/skip-link-manual.js +35 -12
- package/src/checks/manual/svg-text-alternative-quality-manual.js +206 -165
- package/src/checks/manual/tabindex-manual.js +10 -3
- package/src/checks/manual/table-duplicate-name-manual.js +17 -7
- package/src/checks/manual/table-fake-caption-manual.js +24 -7
- package/src/checks/manual/video-caption-manual.js +15 -4
- package/src/checks/manual-review.js +56 -12
- package/src/core/aria-helpers.js +1127 -886
- package/src/core/contrast-helpers.js +1217 -1062
- package/src/core/dom-helpers.js +4175 -3917
- package/src/core/dom-runner.js +720 -604
- package/src/core/frame-messaging.js +189 -138
- package/src/core/frame-scan.js +94 -82
- package/src/core/rollup-composites.js +94 -102
- package/src/core/rule-meta.js +58 -41
- package/src/core.js +36552 -29111
- package/src/i18n/en.js +1194 -889
- package/src/i18n/fr.js +1136 -795
- package/src/policy/contracts.js +13 -13
- package/src/policy/resolvePolicy.js +48 -44
- package/src/report.js +63 -43
- package/src/sarif.js +175 -0
- 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
|
-
{
|
|
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 {
|
|
62
|
+
const { helpers, rule } = ctx;
|
|
57
63
|
|
|
58
|
-
function trim(v) {
|
|
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) :
|
|
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:
|
|
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: {
|
|
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 {
|
|
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:
|
|
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
|
-
{
|
|
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 {
|
|
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
|
|
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) :
|
|
84
|
+
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
|
|
76
85
|
const guidance = ariaHelpers.getDeprecatedRoleGuidance
|
|
77
|
-
|
|
78
|
-
|
|
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 {
|
|
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:
|
|
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
|
-
{
|
|
37
|
-
|
|
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
|
|
88
|
+
const html = helpers.getOuterHtmlSnippet
|
|
89
|
+
? helpers.getOuterHtmlSnippet(body)
|
|
90
|
+
: (body.outerHTML || '').slice(0, 200);
|
|
76
91
|
|
|
77
|
-
const occurrences = [
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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 {
|
|
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 };
|