@surea11y/core 1.2.0 → 1.4.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 +81 -7
- package/LICENSE +373 -21
- package/README.md +175 -35
- package/bin/surea11y-core.js +20 -0
- package/docs/API_STABILITY.md +27 -1
- package/docs/BINDING_AUTHORS_GUIDE.md +9 -9
- package/docs/CI_INTEGRATIONS.md +103 -0
- package/docs/ENGINE_OPTIONS.md +2 -0
- package/docs/I18N.md +12 -9
- package/docs/INTEGRATION.md +19 -1
- package/docs/LIMITATIONS.md +1 -1
- package/docs/OUTPUT_SCHEMA.md +1 -1
- package/docs/REPORT.md +1 -1
- package/docs/RULE_CATALOG.md +1 -1
- package/docs/SARIF.md +59 -0
- package/package.json +63 -18
- package/src/baseline.js +0 -0
- package/src/checks/automatic/area-alt-present.js +63 -31
- package/src/checks/automatic/aria-allowed-attr.js +204 -80
- package/src/checks/automatic/aria-allowed-role.js +23 -7
- package/src/checks/automatic/aria-braille-equivalent.js +34 -10
- package/src/checks/automatic/aria-conditional-attr.js +32 -14
- package/src/checks/automatic/aria-deprecated-role.js +26 -11
- package/src/checks/automatic/aria-hidden-body.js +48 -23
- package/src/checks/automatic/aria-hidden-focus.js +420 -66
- package/src/checks/automatic/aria-prohibited-attr.js +327 -60
- package/src/checks/automatic/aria-prohibited-children.js +111 -103
- package/src/checks/automatic/aria-required-attr.js +29 -15
- package/src/checks/automatic/aria-required-children.js +44 -24
- package/src/checks/automatic/aria-required-parent.js +64 -35
- package/src/checks/automatic/aria-role-name-present.js +49 -21
- package/src/checks/automatic/aria-roles-valid.js +24 -12
- package/src/checks/automatic/aria-valid-attr-value.js +46 -22
- package/src/checks/automatic/aria-valid-attr.js +19 -5
- package/src/checks/automatic/autocomplete-valid.js +76 -16
- package/src/checks/automatic/avoid-inline-spacing.js +23 -8
- package/src/checks/automatic/binary-control-name-present.js +62 -50
- package/src/checks/automatic/button-name-present.js +54 -24
- package/src/checks/automatic/bypass-blocks-present.js +51 -32
- package/src/checks/automatic/canvas-text-alternative-present.js +59 -26
- package/src/checks/automatic/combobox-name-present.js +40 -45
- package/src/checks/automatic/contrast-computable.js +363 -341
- package/src/checks/automatic/contrast-enhanced.js +489 -466
- package/src/checks/automatic/contrast-minimum.js +488 -465
- package/src/checks/automatic/css-orientation-lock.js +51 -35
- package/src/checks/automatic/definition-list-children-valid.js +46 -25
- package/src/checks/automatic/deprecated-elements-not-used.js +25 -9
- package/src/checks/automatic/dialog-name-present.js +47 -85
- package/src/checks/automatic/dlitem-parent-valid.js +25 -8
- package/src/checks/automatic/duplicate-id-aria.js +28 -9
- package/src/checks/automatic/embed-text-alternative-present.js +88 -35
- package/src/checks/automatic/form-control-programmatic-label-present.js +81 -196
- package/src/checks/automatic/form-control-single-label.js +50 -14
- package/src/checks/automatic/html-xml-lang-mismatch.js +36 -18
- package/src/checks/automatic/iframe-focusable-content.js +265 -22
- package/src/checks/automatic/iframe-name-present.js +33 -9
- package/src/checks/automatic/iframe-title-unique.js +32 -9
- package/src/checks/automatic/img-alt-present.js +54 -52
- package/src/checks/automatic/input-image-alt-present.js +141 -112
- package/src/checks/automatic/label-in-name.js +65 -41
- package/src/checks/automatic/language-page-present.js +111 -109
- package/src/checks/automatic/link-in-text-block.js +61 -19
- package/src/checks/automatic/link-name-present.js +47 -14
- package/src/checks/automatic/list-children-valid.js +40 -33
- package/src/checks/automatic/listbox-name-present.js +41 -19
- package/src/checks/automatic/listitem-parent-valid.js +48 -13
- package/src/checks/automatic/menuitem-name-present.js +41 -61
- package/src/checks/automatic/meta-refresh-no-exceptions.js +32 -11
- package/src/checks/automatic/meta-refresh-timing-absent.js +22 -6
- package/src/checks/automatic/meta-viewport-zoom-enabled.js +26 -7
- package/src/checks/automatic/meter-name-present.js +40 -36
- package/src/checks/automatic/nested-interactive-controls-absent.js +58 -15
- package/src/checks/automatic/object-text-alternative-present.js +93 -39
- package/src/checks/automatic/option-name-present.js +40 -21
- package/src/checks/automatic/page-title-present.js +19 -6
- package/src/checks/automatic/progressbar-name-present.js +49 -44
- package/src/checks/automatic/role-img-alt-present.js +211 -159
- package/src/checks/automatic/searchbox-name-present.js +41 -19
- package/src/checks/automatic/server-side-image-map-absent.js +27 -11
- package/src/checks/automatic/slider-name-present.js +42 -47
- package/src/checks/automatic/spinbutton-name-present.js +41 -19
- package/src/checks/automatic/summary-name-present.js +39 -17
- package/src/checks/automatic/svg-image-text-alternative-present.js +116 -47
- package/src/checks/automatic/svg-text-alternative-present.js +262 -230
- package/src/checks/automatic/tab-name-present.js +39 -60
- package/src/checks/automatic/table-headers-attr-valid.js +27 -10
- package/src/checks/automatic/table-th-has-data-cells.js +24 -8
- package/src/checks/automatic/target-size-minimum.js +123 -48
- package/src/checks/automatic/td-has-header.js +53 -12
- package/src/checks/automatic/textbox-name-present.js +41 -19
- package/src/checks/automatic/tooltip-name-present.js +39 -18
- package/src/checks/automatic/treeitem-name-present.js +40 -21
- package/src/checks/automatic/valid-lang.js +22 -6
- package/src/checks/automatic/video-poster-text-alternative-present.js +81 -36
- package/src/checks/manual/accesskeys-manual.js +17 -6
- package/src/checks/manual/area-alt-decorative-manual.js +194 -193
- package/src/checks/manual/area-alt-quality-manual.js +184 -141
- package/src/checks/manual/aria-checked-state-mismatch-manual.js +48 -34
- package/src/checks/manual/aria-text-manual.js +20 -11
- package/src/checks/manual/canvas-text-alternative-quality-manual.js +151 -114
- package/src/checks/manual/css-hidden-focus.js +375 -169
- package/src/checks/manual/embed-text-alternative-quality-manual.js +178 -162
- package/src/checks/manual/empty-heading-manual.js +41 -24
- package/src/checks/manual/empty-table-header-manual.js +69 -31
- package/src/checks/manual/focus-order-semantics-manual.js +60 -13
- package/src/checks/manual/form-control-programmatic-label-quality-manual.js +209 -246
- package/src/checks/manual/heading-order-manual.js +50 -8
- package/src/checks/manual/identical-links-same-purpose-manual.js +36 -12
- package/src/checks/manual/image-redundant-alt-manual.js +38 -8
- package/src/checks/manual/img-alt-decorative-manual.js +133 -96
- package/src/checks/manual/img-alt-quality-manual.js +178 -127
- package/src/checks/manual/input-image-alt-decorative-manual.js +127 -92
- package/src/checks/manual/input-image-alt-quality-manual.js +127 -92
- package/src/checks/manual/label-title-only-manual.js +44 -28
- package/src/checks/manual/landmark-banner-is-top-level-manual.js +95 -38
- package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +85 -32
- package/src/checks/manual/landmark-main-is-top-level-manual.js +69 -27
- package/src/checks/manual/landmark-no-duplicate-banner-manual.js +45 -33
- package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +43 -31
- package/src/checks/manual/landmark-no-duplicate-main-manual.js +27 -21
- package/src/checks/manual/landmark-one-main-manual.js +38 -43
- package/src/checks/manual/landmark-unique-manual.js +78 -67
- package/src/checks/manual/link-name-quality-manual.js +45 -12
- package/src/checks/manual/media-transcript-present-manual.js +37 -22
- package/src/checks/manual/meta-viewport-large-manual.js +19 -6
- package/src/checks/manual/mouse-only-event-handlers-manual.js +40 -11
- package/src/checks/manual/no-autoplay-audio-manual.js +22 -6
- package/src/checks/manual/object-text-alternative-quality-manual.js +177 -154
- package/src/checks/manual/p-as-heading-manual.js +24 -7
- package/src/checks/manual/page-has-heading-one-manual.js +42 -32
- package/src/checks/manual/page-title-patterns-manual.js +80 -50
- package/src/checks/manual/presentation-role-conflict-manual.js +101 -47
- package/src/checks/manual/region-manual.js +244 -60
- package/src/checks/manual/scope-attr-valid-manual.js +13 -4
- package/src/checks/manual/scrollable-region-focusable-manual.js +39 -11
- package/src/checks/manual/skip-link-manual.js +42 -18
- package/src/checks/manual/svg-text-alternative-quality-manual.js +208 -165
- package/src/checks/manual/tabindex-manual.js +13 -4
- package/src/checks/manual/table-duplicate-name-manual.js +22 -11
- package/src/checks/manual/table-fake-caption-manual.js +48 -10
- package/src/checks/manual/video-caption-manual.js +17 -4
- package/src/checks/manual-review.js +58 -12
- package/src/core.js +41705 -29650
- package/src/index.js +2 -0
- package/src/report.js +109 -47
- package/src/sarif.js +190 -0
- package/surea11y.browser.js +37774 -0
- package/bin/core.js +0 -348
- package/docs/CLI.md +0 -75
- package/src/catalogs/composites.wcag.js +0 -490
- package/src/checks/rules-and-tags.full.csv +0 -19
- package/src/checks/rules-and-tags.full.json +0 -259
- package/src/core/aria-helpers.js +0 -970
- package/src/core/contrast-helpers.js +0 -1147
- package/src/core/dom-helpers.js +0 -4235
- package/src/core/dom-runner.js +0 -671
- package/src/core/frame-messaging.js +0 -210
- package/src/core/frame-scan.js +0 -178
- package/src/core/rollup-composites.js +0 -135
- package/src/core/rule-meta.js +0 -159
- package/src/coverage/wcag-facets.js +0 -1079
- package/src/coverage/wcag-version-map.js +0 -84
- package/src/i18n/en.js +0 -923
- package/src/i18n/fr.js +0 -844
- package/src/policy/contracts.js +0 -18
- package/src/policy/resolvePolicy.js +0 -55
- package/src/policy/schemas/engine-options.schema.json +0 -103
- package/src/policy/schemas/policy-contract.schema.json +0 -40
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: MPL-2.0 */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* @check table-duplicate-name
|
|
5
7
|
* @atomic true
|
|
6
8
|
* @summary A table's caption must not duplicate its summary attribute
|
|
7
|
-
* @standard Best Practices (
|
|
9
|
+
* @standard Best Practices (no formal WCAG Success Criterion — see ROADMAP.md Tier 1b)
|
|
8
10
|
* @applicability
|
|
9
11
|
* Applies to <table> elements that have both a <caption> with text
|
|
10
12
|
* content and a (deprecated but still encountered) summary attribute.
|
|
@@ -17,16 +19,16 @@
|
|
|
17
19
|
* - Not WCAG-normative — authored as an advisory, cantTell-capped
|
|
18
20
|
* `type: 'manual'` rule; see landmark-banner-is-top-level's
|
|
19
21
|
* header comment for the shared rationale/precedent.
|
|
20
|
-
* - Narrowly scoped to caption-vs-summary duplication specifically
|
|
21
|
-
*
|
|
22
|
-
* name quality" check.
|
|
22
|
+
* - Narrowly scoped to caption-vs-summary duplication specifically, not a
|
|
23
|
+
* general "table name quality" check.
|
|
23
24
|
*/
|
|
24
25
|
|
|
25
26
|
const id = 'table-duplicate-name';
|
|
26
27
|
|
|
27
28
|
const meta = {
|
|
28
29
|
title: 'Table caption must not duplicate its summary attribute',
|
|
29
|
-
description:
|
|
30
|
+
description:
|
|
31
|
+
"Checks that a <table>'s <caption> text is not identical to its (deprecated) summary attribute.",
|
|
30
32
|
i18n: {
|
|
31
33
|
titleKey: 'tableDuplicateName_title',
|
|
32
34
|
descriptionKey: 'tableDuplicateName_description'
|
|
@@ -43,13 +45,17 @@ const meta = {
|
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
function runInPage(ctx) {
|
|
46
|
-
const {
|
|
48
|
+
const { helpers, rule } = ctx;
|
|
47
49
|
|
|
48
50
|
function normalizeWs(s) {
|
|
49
|
-
return String(s || '')
|
|
51
|
+
return String(s || '')
|
|
52
|
+
.replace(/\s+/g, ' ')
|
|
53
|
+
.trim();
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
const nodes = helpers.queryAllSmart
|
|
56
|
+
const nodes = helpers.queryAllSmart
|
|
57
|
+
? helpers.queryAllSmart('table[summary]')
|
|
58
|
+
: helpers.queryAll('table[summary]');
|
|
53
59
|
|
|
54
60
|
const occurrences = [];
|
|
55
61
|
let applicableCount = 0;
|
|
@@ -68,12 +74,12 @@ function runInPage(ctx) {
|
|
|
68
74
|
if (captionText.toLowerCase() !== summary.toLowerCase()) continue;
|
|
69
75
|
|
|
70
76
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
71
|
-
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) :
|
|
77
|
+
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
|
|
72
78
|
|
|
73
79
|
occurrences.push({
|
|
74
80
|
selector: stableSelector,
|
|
75
81
|
html,
|
|
76
|
-
summary:
|
|
82
|
+
summary: "This table's caption duplicates its summary attribute.",
|
|
77
83
|
hint: 'Remove the redundant summary attribute, or make it provide different information than the caption.',
|
|
78
84
|
i18n: {
|
|
79
85
|
summaryKey: 'tableDuplicateName_summary_cantTell',
|
|
@@ -90,7 +96,12 @@ function runInPage(ctx) {
|
|
|
90
96
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
91
97
|
}
|
|
92
98
|
if (occurrences.length) {
|
|
93
|
-
return {
|
|
99
|
+
return {
|
|
100
|
+
ruleId: rule.ruleId,
|
|
101
|
+
outcome: 'cantTell',
|
|
102
|
+
severity: rule.defaultSeverity || 'minor',
|
|
103
|
+
occurrences
|
|
104
|
+
};
|
|
94
105
|
}
|
|
95
106
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
96
107
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: MPL-2.0 */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -32,7 +34,8 @@ const id = 'table-fake-caption';
|
|
|
32
34
|
|
|
33
35
|
const meta = {
|
|
34
36
|
title: "A table's first row should not stand in for a real <caption>",
|
|
35
|
-
description:
|
|
37
|
+
description:
|
|
38
|
+
'Flags tables with no <caption> whose first row has a single non-empty cell while other rows have multiple cells, for manual review of whether that cell is acting as a fake caption.',
|
|
36
39
|
i18n: {
|
|
37
40
|
titleKey: 'tableFakeCaption_title',
|
|
38
41
|
descriptionKey: 'tableFakeCaption_description'
|
|
@@ -41,7 +44,13 @@ const meta = {
|
|
|
41
44
|
tags: ['wcag2a', 'wcag131', 'structure', 'atomic', 'manual'],
|
|
42
45
|
wcagSc: ['1.3.1'],
|
|
43
46
|
normativeMappings: [
|
|
44
|
-
{
|
|
47
|
+
{
|
|
48
|
+
standard: 'WCAG',
|
|
49
|
+
version: '2.2',
|
|
50
|
+
requirement: '1.3.1',
|
|
51
|
+
title: 'Info and Relationships',
|
|
52
|
+
conformanceLevel: 'A'
|
|
53
|
+
}
|
|
45
54
|
],
|
|
46
55
|
defaultSeverity: 'minor',
|
|
47
56
|
category: 'perceivable',
|
|
@@ -51,9 +60,24 @@ const meta = {
|
|
|
51
60
|
};
|
|
52
61
|
|
|
53
62
|
function runInPage(ctx) {
|
|
54
|
-
const {
|
|
63
|
+
const { helpers, rule } = ctx;
|
|
64
|
+
|
|
65
|
+
function trim(v) {
|
|
66
|
+
return (v == null ? '' : String(v)).trim();
|
|
67
|
+
}
|
|
55
68
|
|
|
56
|
-
|
|
69
|
+
const isAccTreeEligible =
|
|
70
|
+
helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
|
|
71
|
+
|
|
72
|
+
function isEligible(node) {
|
|
73
|
+
if (!isAccTreeEligible) return true;
|
|
74
|
+
try {
|
|
75
|
+
const r = isAccTreeEligible(node, ctx);
|
|
76
|
+
return !!(r && r.eligible);
|
|
77
|
+
} catch {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
57
81
|
|
|
58
82
|
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
|
|
59
83
|
|
|
@@ -66,29 +90,38 @@ function runInPage(ctx) {
|
|
|
66
90
|
const hasCaption = !!(table.querySelector && table.querySelector('caption'));
|
|
67
91
|
if (hasCaption) continue;
|
|
68
92
|
|
|
69
|
-
|
|
93
|
+
// An aria-hidden row (or cell) isn't part of the AT-perceived table
|
|
94
|
+
// structure at all -- it must not be treated as the table's "first
|
|
95
|
+
// row" (or counted toward a row's cell count) for this positional
|
|
96
|
+
// heuristic.
|
|
97
|
+
const rows = Array.from(table.rows).filter(isEligible);
|
|
70
98
|
if (rows.length < 2) continue;
|
|
71
99
|
|
|
72
100
|
applicableCount += 1;
|
|
73
101
|
|
|
74
102
|
const firstRow = rows[0];
|
|
75
|
-
const firstRowCells = Array.from(firstRow.cells || []);
|
|
103
|
+
const firstRowCells = Array.from(firstRow.cells || []).filter(isEligible);
|
|
76
104
|
if (firstRowCells.length !== 1) continue;
|
|
77
105
|
|
|
78
106
|
const candidateCell = firstRowCells[0];
|
|
79
107
|
const candidateText = trim(candidateCell.textContent || '');
|
|
80
108
|
if (!candidateText) continue;
|
|
81
109
|
|
|
82
|
-
const hasMultiCellRow = rows
|
|
110
|
+
const hasMultiCellRow = rows
|
|
111
|
+
.slice(1)
|
|
112
|
+
.some((r) => Array.from(r.cells || []).filter(isEligible).length > 1);
|
|
83
113
|
if (!hasMultiCellRow) continue;
|
|
84
114
|
|
|
85
115
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(table) : 'html';
|
|
86
|
-
const html = helpers.getOuterHtmlSnippet
|
|
116
|
+
const html = helpers.getOuterHtmlSnippet
|
|
117
|
+
? helpers.getOuterHtmlSnippet(table)
|
|
118
|
+
: table.outerHTML || '';
|
|
87
119
|
|
|
88
120
|
const baseOccurrence = {
|
|
89
121
|
selector: stableSelector,
|
|
90
122
|
html,
|
|
91
|
-
summary:
|
|
123
|
+
summary:
|
|
124
|
+
'This table has no <caption>, but its first row is a single cell sitting above multi-cell rows — it may be acting as a fake caption.',
|
|
92
125
|
hint: 'If this cell is meant to describe the table, use a real <caption> element instead of a lone first-row cell.',
|
|
93
126
|
i18n: {
|
|
94
127
|
summaryKey: 'tableFakeCaption_summary_cantTell',
|
|
@@ -112,7 +145,12 @@ function runInPage(ctx) {
|
|
|
112
145
|
}
|
|
113
146
|
|
|
114
147
|
if (occurrences.length) {
|
|
115
|
-
return {
|
|
148
|
+
return {
|
|
149
|
+
ruleId: rule.ruleId,
|
|
150
|
+
outcome: 'cantTell',
|
|
151
|
+
severity: rule.defaultSeverity || 'minor',
|
|
152
|
+
occurrences
|
|
153
|
+
};
|
|
116
154
|
}
|
|
117
155
|
|
|
118
156
|
return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: MPL-2.0 */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -41,7 +43,13 @@ const meta = {
|
|
|
41
43
|
tags: ['wcag2a', 'wcag122', 'media', 'timebasedmedia', 'atomic', 'manual'],
|
|
42
44
|
wcagSc: ['1.2.2'],
|
|
43
45
|
normativeMappings: [
|
|
44
|
-
{
|
|
46
|
+
{
|
|
47
|
+
standard: 'WCAG',
|
|
48
|
+
version: '2.2',
|
|
49
|
+
requirement: '1.2.2',
|
|
50
|
+
title: 'Captions (Prerecorded)',
|
|
51
|
+
conformanceLevel: 'A'
|
|
52
|
+
}
|
|
45
53
|
],
|
|
46
54
|
defaultSeverity: 'moderate',
|
|
47
55
|
category: 'perceivable',
|
|
@@ -51,7 +59,7 @@ const meta = {
|
|
|
51
59
|
};
|
|
52
60
|
|
|
53
61
|
function runInPage(ctx) {
|
|
54
|
-
const {
|
|
62
|
+
const { helpers, rule } = ctx;
|
|
55
63
|
|
|
56
64
|
const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video') : helpers.queryAll('video');
|
|
57
65
|
|
|
@@ -77,7 +85,7 @@ function runInPage(ctx) {
|
|
|
77
85
|
if (hasCaptionsTrack) continue;
|
|
78
86
|
|
|
79
87
|
const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
|
|
80
|
-
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) :
|
|
88
|
+
const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
|
|
81
89
|
|
|
82
90
|
const baseOccurrence = {
|
|
83
91
|
selector: stableSelector,
|
|
@@ -106,7 +114,12 @@ function runInPage(ctx) {
|
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
if (occurrences.length) {
|
|
109
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
ruleId: rule.ruleId,
|
|
119
|
+
outcome: 'cantTell',
|
|
120
|
+
severity: rule.defaultSeverity || 'moderate',
|
|
121
|
+
occurrences
|
|
122
|
+
};
|
|
110
123
|
}
|
|
111
124
|
|
|
112
125
|
// Manual rules may only emit cantTell/notApplicable (never pass/fail):
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: MPL-2.0 */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -20,17 +22,59 @@ const meta = {
|
|
|
20
22
|
type: 'manual',
|
|
21
23
|
defaultConfidence: 'medium',
|
|
22
24
|
normativeMappings: [
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
{
|
|
26
|
+
standard: 'WCAG',
|
|
27
|
+
version: '2.2',
|
|
28
|
+
requirement: '2.1.1',
|
|
29
|
+
title: 'Keyboard',
|
|
30
|
+
conformanceLevel: 'A',
|
|
31
|
+
url: 'https://www.w3.org/TR/WCAG22/#keyboard'
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
standard: 'WCAG',
|
|
35
|
+
version: '2.2',
|
|
36
|
+
requirement: '2.4.3',
|
|
37
|
+
title: 'Focus Order',
|
|
38
|
+
conformanceLevel: 'A',
|
|
39
|
+
url: 'https://www.w3.org/TR/WCAG22/#focus-order'
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
standard: 'WCAG',
|
|
43
|
+
version: '2.2',
|
|
44
|
+
requirement: '2.4.7',
|
|
45
|
+
title: 'Focus Visible',
|
|
46
|
+
conformanceLevel: 'AA',
|
|
47
|
+
url: 'https://www.w3.org/TR/WCAG22/#focus-visible'
|
|
48
|
+
},
|
|
26
49
|
|
|
27
50
|
{ standard: 'EN 301 549', version: 'V3.2.1', requirement: '9.2.1.1', title: 'Keyboard' },
|
|
28
51
|
{ standard: 'EN 301 549', version: 'V3.2.1', requirement: '9.2.4.3', title: 'Focus Order' },
|
|
29
52
|
{ standard: 'EN 301 549', version: 'V3.2.1', requirement: '9.2.4.7', title: 'Focus Visible' },
|
|
30
53
|
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
{
|
|
55
|
+
standard: 'WCAG',
|
|
56
|
+
version: '2.2',
|
|
57
|
+
type: 'Understanding',
|
|
58
|
+
requirement: '2.1.1',
|
|
59
|
+
title: 'Understanding Keyboard',
|
|
60
|
+
url: 'https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
standard: 'WCAG',
|
|
64
|
+
version: '2.2',
|
|
65
|
+
type: 'Understanding',
|
|
66
|
+
requirement: '2.4.3',
|
|
67
|
+
title: 'Understanding Focus Order',
|
|
68
|
+
url: 'https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html'
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
standard: 'WCAG',
|
|
72
|
+
version: '2.2',
|
|
73
|
+
type: 'Understanding',
|
|
74
|
+
requirement: '2.4.7',
|
|
75
|
+
title: 'Understanding Focus Visible',
|
|
76
|
+
url: 'https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html'
|
|
77
|
+
}
|
|
34
78
|
]
|
|
35
79
|
};
|
|
36
80
|
|
|
@@ -43,7 +87,8 @@ const meta = {
|
|
|
43
87
|
function runInPage(ctx) {
|
|
44
88
|
const { document, root, helpers, rule } = ctx;
|
|
45
89
|
|
|
46
|
-
const getOuterHtmlSnippet =
|
|
90
|
+
const getOuterHtmlSnippet =
|
|
91
|
+
helpers && helpers.getOuterHtmlSnippet
|
|
47
92
|
? helpers.getOuterHtmlSnippet
|
|
48
93
|
: (el) => (el && el.outerHTML) || '';
|
|
49
94
|
|
|
@@ -66,10 +111,7 @@ function runInPage(ctx) {
|
|
|
66
111
|
}
|
|
67
112
|
|
|
68
113
|
const fallbackRoot =
|
|
69
|
-
|
|
70
|
-
document.documentElement ||
|
|
71
|
-
document.body ||
|
|
72
|
-
document.querySelector('html');
|
|
114
|
+
rootEl || document.documentElement || document.body || document.querySelector('html');
|
|
73
115
|
|
|
74
116
|
const html = getOuterHtmlSnippet(fallbackRoot);
|
|
75
117
|
|
|
@@ -82,7 +124,11 @@ function runInPage(ctx) {
|
|
|
82
124
|
selector: contextSelector || 'html',
|
|
83
125
|
html,
|
|
84
126
|
summary: 'Manual review required for keyboard navigation and focus order.',
|
|
85
|
-
i18n: {
|
|
127
|
+
i18n: {
|
|
128
|
+
summaryKey: 'manualReview_summary_cantTell',
|
|
129
|
+
hintKey: 'manualReview_hint_cantTell',
|
|
130
|
+
params: {}
|
|
131
|
+
}
|
|
86
132
|
}
|
|
87
133
|
]
|
|
88
134
|
};
|