@surea11y/core 1.3.0 → 1.4.1
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 +87 -2
- package/README.md +109 -35
- package/bin/surea11y-core.js +20 -0
- package/docs/API_STABILITY.md +26 -0
- package/docs/ARIA_DEPRECATION.md +95 -0
- package/docs/CI_INTEGRATIONS.md +7 -7
- package/docs/ENGINE_OPTIONS.md +1 -1
- package/docs/I18N.md +12 -9
- package/docs/INTEGRATION.md +1 -1
- package/docs/LIMITATIONS.md +1 -1
- package/docs/REPORT.md +1 -1
- package/docs/RULE_CATALOG.md +6 -6
- package/package.json +52 -16
- package/src/baseline.js +0 -0
- package/src/checks/automatic/area-alt-present.js +4 -6
- package/src/checks/automatic/aria-allowed-attr.js +663 -134
- package/src/checks/automatic/aria-allowed-role.js +2 -0
- package/src/checks/automatic/aria-braille-equivalent.js +2 -0
- package/src/checks/automatic/aria-conditional-attr.js +8 -7
- package/src/checks/automatic/aria-deprecated-role.js +107 -38
- package/src/checks/automatic/aria-hidden-body.js +6 -4
- package/src/checks/automatic/aria-hidden-focus.js +12 -13
- package/src/checks/automatic/aria-prohibited-attr.js +98 -105
- package/src/checks/automatic/aria-prohibited-children.js +56 -87
- package/src/checks/automatic/aria-required-attr.js +6 -7
- package/src/checks/automatic/aria-required-children.js +7 -10
- package/src/checks/automatic/aria-required-parent.js +20 -25
- package/src/checks/automatic/aria-role-name-present.js +2 -0
- package/src/checks/automatic/aria-roles-valid.js +33 -6
- package/src/checks/automatic/aria-valid-attr-value.js +18 -15
- package/src/checks/automatic/aria-valid-attr.js +2 -0
- package/src/checks/automatic/autocomplete-valid.js +39 -1
- package/src/checks/automatic/avoid-inline-spacing.js +181 -20
- package/src/checks/automatic/binary-control-name-present.js +13 -3
- package/src/checks/automatic/button-name-present.js +54 -21
- package/src/checks/automatic/canvas-text-alternative-present.js +17 -8
- package/src/checks/automatic/combobox-name-present.js +10 -1
- package/src/checks/automatic/contrast-computable.js +2 -0
- package/src/checks/automatic/contrast-enhanced.js +2 -0
- package/src/checks/automatic/contrast-minimum.js +2 -0
- package/src/checks/automatic/css-orientation-lock.js +21 -27
- package/src/checks/automatic/definition-list-children-valid.js +6 -6
- package/src/checks/automatic/deprecated-elements-not-used.js +4 -2
- package/src/checks/automatic/dialog-name-present.js +18 -11
- package/src/checks/automatic/dlitem-parent-valid.js +2 -0
- package/src/checks/automatic/duplicate-id-aria.js +4 -3
- package/src/checks/automatic/embed-text-alternative-present.js +2 -0
- package/src/checks/automatic/form-control-programmatic-label-present.js +50 -4
- package/src/checks/automatic/form-control-single-label.js +110 -43
- package/src/checks/automatic/html-xml-lang-mismatch.js +2 -0
- package/src/checks/automatic/iframe-focusable-content.js +246 -18
- package/src/checks/automatic/iframe-name-present.js +2 -0
- package/src/checks/automatic/iframe-title-unique.js +3 -1
- package/src/checks/automatic/img-alt-present.js +23 -18
- package/src/checks/automatic/input-image-alt-present.js +101 -52
- package/src/checks/automatic/label-in-name.js +97 -27
- package/src/checks/automatic/language-page-present.js +7 -1
- package/src/checks/automatic/link-in-text-block.js +2 -0
- package/src/checks/automatic/link-name-present.js +52 -17
- package/src/checks/automatic/list-children-valid.js +14 -24
- package/src/checks/automatic/listbox-name-present.js +10 -1
- package/src/checks/automatic/listitem-parent-valid.js +30 -7
- package/src/checks/automatic/menuitem-name-present.js +10 -1
- package/src/checks/automatic/meta-refresh-no-exceptions.js +33 -4
- package/src/checks/automatic/meta-refresh-timing-absent.js +32 -4
- package/src/checks/automatic/meta-viewport-zoom-enabled.js +39 -15
- package/src/checks/automatic/meter-name-present.js +12 -4
- package/src/checks/automatic/nested-interactive-controls-absent.js +177 -25
- package/src/checks/automatic/object-text-alternative-present.js +16 -7
- package/src/checks/automatic/option-name-present.js +10 -1
- package/src/checks/automatic/page-title-present.js +2 -0
- package/src/checks/automatic/progressbar-name-present.js +16 -11
- package/src/checks/automatic/role-img-alt-present.js +4 -4
- package/src/checks/automatic/searchbox-name-present.js +10 -1
- package/src/checks/automatic/server-side-image-map-absent.js +4 -3
- package/src/checks/automatic/slider-name-present.js +13 -2
- package/src/checks/automatic/spinbutton-name-present.js +10 -1
- package/src/checks/automatic/summary-name-present.js +10 -1
- package/src/checks/automatic/svg-image-text-alternative-present.js +2 -0
- package/src/checks/automatic/svg-text-alternative-present.js +17 -5
- package/src/checks/automatic/tab-name-present.js +10 -1
- package/src/checks/automatic/table-headers-attr-valid.js +3 -2
- package/src/checks/automatic/table-th-has-data-cells.js +69 -6
- package/src/checks/automatic/target-size-minimum.js +5 -0
- package/src/checks/automatic/td-has-header.js +24 -1
- package/src/checks/automatic/textbox-name-present.js +10 -1
- package/src/checks/automatic/tooltip-name-present.js +10 -1
- package/src/checks/automatic/treeitem-name-present.js +10 -1
- package/src/checks/automatic/valid-lang.js +18 -3
- package/src/checks/automatic/video-poster-text-alternative-present.js +2 -0
- package/src/checks/manual/accesskeys-manual.js +3 -1
- package/src/checks/manual/area-alt-decorative-manual.js +2 -0
- package/src/checks/manual/area-alt-quality-manual.js +2 -0
- package/src/checks/manual/aria-checked-state-mismatch-manual.js +14 -23
- package/src/checks/manual/aria-text-manual.js +6 -5
- package/src/checks/manual/bypass-blocks-present-manual.js +279 -0
- package/src/checks/manual/canvas-text-alternative-quality-manual.js +2 -0
- package/src/checks/manual/css-hidden-focus.js +184 -9
- package/src/checks/manual/embed-text-alternative-quality-manual.js +18 -13
- package/src/checks/manual/empty-heading-manual.js +17 -17
- package/src/checks/manual/empty-table-header-manual.js +52 -25
- package/src/checks/manual/focus-order-semantics-manual.js +16 -4
- package/src/checks/manual/form-control-programmatic-label-quality-manual.js +2 -0
- package/src/checks/manual/heading-order-manual.js +28 -1
- package/src/checks/manual/identical-links-same-purpose-manual.js +2 -0
- package/src/checks/manual/image-redundant-alt-manual.js +21 -1
- package/src/checks/manual/img-alt-decorative-manual.js +2 -0
- package/src/checks/manual/img-alt-quality-manual.js +2 -0
- package/src/checks/manual/input-image-alt-decorative-manual.js +26 -0
- package/src/checks/manual/input-image-alt-quality-manual.js +2 -0
- package/src/checks/manual/label-title-only-manual.js +29 -22
- package/src/checks/manual/landmark-banner-is-top-level-manual.js +51 -55
- package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +44 -31
- package/src/checks/manual/landmark-main-is-top-level-manual.js +41 -24
- package/src/checks/manual/landmark-no-duplicate-banner-manual.js +16 -23
- package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +14 -21
- package/src/checks/manual/landmark-no-duplicate-main-manual.js +10 -13
- package/src/checks/manual/landmark-one-main-manual.js +12 -23
- package/src/checks/manual/landmark-unique-manual.js +37 -52
- package/src/checks/manual/link-name-quality-manual.js +2 -0
- package/src/checks/manual/media-transcript-present-manual.js +2 -0
- package/src/checks/manual/meta-viewport-large-manual.js +3 -1
- package/src/checks/manual/mouse-only-event-handlers-manual.js +2 -0
- package/src/checks/manual/no-autoplay-audio-manual.js +2 -0
- package/src/checks/manual/object-text-alternative-quality-manual.js +2 -0
- package/src/checks/manual/p-as-heading-manual.js +2 -0
- package/src/checks/manual/page-has-heading-one-manual.js +12 -11
- package/src/checks/manual/page-title-patterns-manual.js +2 -0
- package/src/checks/manual/presentation-role-conflict-manual.js +51 -37
- package/src/checks/manual/region-manual.js +27 -36
- package/src/checks/manual/scope-attr-valid-manual.js +3 -1
- package/src/checks/manual/scrollable-region-focusable-manual.js +2 -0
- package/src/checks/manual/skip-link-manual.js +7 -6
- package/src/checks/manual/svg-text-alternative-quality-manual.js +2 -0
- package/src/checks/manual/tabindex-manual.js +3 -1
- package/src/checks/manual/table-duplicate-name-manual.js +5 -4
- package/src/checks/manual/table-fake-caption-manual.js +24 -3
- package/src/checks/manual/video-caption-manual.js +2 -0
- package/src/checks/manual-review.js +2 -0
- package/src/core.js +11820 -3317
- package/src/index.js +2 -0
- package/src/report.js +51 -9
- package/src/sarif.js +20 -5
- package/surea11y.browser.js +4943 -1388
- package/bin/core.js +0 -473
- package/docs/CLI.md +0 -128
- package/src/catalogs/composites.wcag.js +0 -454
- package/src/checks/automatic/bypass-blocks-present.js +0 -215
- 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 -1211
- package/src/core/contrast-helpers.js +0 -1302
- package/src/core/dom-helpers.js +0 -4493
- package/src/core/dom-runner.js +0 -787
- package/src/core/frame-messaging.js +0 -261
- package/src/core/frame-scan.js +0 -190
- package/src/core/rollup-composites.js +0 -127
- package/src/core/rule-meta.js +0 -176
- package/src/coverage/wcag-facets.js +0 -1079
- package/src/coverage/wcag-version-map.js +0 -84
- package/src/i18n/en.js +0 -1228
- package/src/i18n/fr.js +0 -1185
- package/src/policy/contracts.js +0 -18
- package/src/policy/resolvePolicy.js +0 -59
- package/src/policy/schemas/engine-options.schema.json +0 -103
- package/src/policy/schemas/policy-contract.schema.json +0 -40
package/src/index.js
CHANGED
package/src/report.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: MPL-2.0 */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -56,6 +58,32 @@ function countByOutcome(checksResults) {
|
|
|
56
58
|
return counts;
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
function getOccurrenceOutcome(ruleResult, occurrence) {
|
|
62
|
+
const occurrenceOutcome =
|
|
63
|
+
occurrence &&
|
|
64
|
+
(occurrence.occurrenceOutcome === 'fail' || occurrence.occurrenceOutcome === 'cantTell'
|
|
65
|
+
? occurrence.occurrenceOutcome
|
|
66
|
+
: occurrence.outcome === 'fail' || occurrence.outcome === 'cantTell'
|
|
67
|
+
? occurrence.outcome
|
|
68
|
+
: null);
|
|
69
|
+
if (occurrenceOutcome) return occurrenceOutcome;
|
|
70
|
+
return (
|
|
71
|
+
ruleResult &&
|
|
72
|
+
(ruleResult.outcome === 'fail' || ruleResult.outcome === 'cantTell' ? ruleResult.outcome : null)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getCardOutcome(ruleResult) {
|
|
77
|
+
if (!ruleResult || !Array.isArray(ruleResult.occurrences)) return ruleResult.outcome;
|
|
78
|
+
let hasCantTell = false;
|
|
79
|
+
for (const occ of ruleResult.occurrences) {
|
|
80
|
+
const outcome = getOccurrenceOutcome(ruleResult, occ);
|
|
81
|
+
if (outcome === 'fail') return 'fail';
|
|
82
|
+
if (outcome === 'cantTell') hasCantTell = true;
|
|
83
|
+
}
|
|
84
|
+
return hasCantTell ? 'cantTell' : ruleResult.outcome;
|
|
85
|
+
}
|
|
86
|
+
|
|
59
87
|
// One plain-language headline + one horizontal stacked bar + a legend with
|
|
60
88
|
// icon+label+count (status color is never the only signal) -- the first
|
|
61
89
|
// thing a reader sees; exhaustive detail lives in the collapsed
|
|
@@ -190,33 +218,46 @@ const MAX_CARDS = 24;
|
|
|
190
218
|
function renderCards(checksResults) {
|
|
191
219
|
const withIssues = checksResults.filter(
|
|
192
220
|
(r) =>
|
|
193
|
-
(r.outcome === 'fail' || r.outcome === 'cantTell') &&
|
|
194
221
|
Array.isArray(r.occurrences) &&
|
|
195
|
-
r.occurrences.
|
|
222
|
+
r.occurrences.some((occ) => {
|
|
223
|
+
const outcome = getOccurrenceOutcome(r, occ);
|
|
224
|
+
return outcome === 'fail' || outcome === 'cantTell';
|
|
225
|
+
})
|
|
196
226
|
);
|
|
197
227
|
if (!withIssues.length) {
|
|
198
228
|
return '<p class="note">No fail/cantTell rules with occurrences on this scan.</p>';
|
|
199
229
|
}
|
|
200
230
|
|
|
201
231
|
const sorted = withIssues.slice().sort((a, b) => {
|
|
202
|
-
|
|
232
|
+
const aOutcome = getCardOutcome(a);
|
|
233
|
+
const bOutcome = getCardOutcome(b);
|
|
234
|
+
if (aOutcome !== bOutcome) return aOutcome === 'fail' ? -1 : 1;
|
|
203
235
|
return b.occurrences.length - a.occurrences.length;
|
|
204
236
|
});
|
|
205
237
|
const shown = sorted.slice(0, MAX_CARDS);
|
|
206
238
|
|
|
207
239
|
const cards = shown
|
|
208
240
|
.map((r) => {
|
|
209
|
-
const
|
|
210
|
-
const
|
|
241
|
+
const cardOutcome = getCardOutcome(r);
|
|
242
|
+
const info = OUTCOME_INFO[cardOutcome] || OUTCOME_INFO.notApplicable;
|
|
243
|
+
const occurrenceCounts = { fail: 0, cantTell: 0 };
|
|
244
|
+
for (const occ of r.occurrences) {
|
|
245
|
+
const outcome = getOccurrenceOutcome(r, occ);
|
|
246
|
+
if (outcome === 'fail' || outcome === 'cantTell') occurrenceCounts[outcome] += 1;
|
|
247
|
+
}
|
|
248
|
+
const representative =
|
|
249
|
+
r.occurrences.find((occ) => getOccurrenceOutcome(r, occ) === cardOutcome) ||
|
|
250
|
+
r.occurrences[0];
|
|
211
251
|
const wcagChips = ((r.meta && r.meta.normativeMappings) || [])
|
|
212
252
|
.map(
|
|
213
253
|
(m) =>
|
|
214
254
|
`<span class="chip" style="background:${STATUS.neutral.bg};color:${STATUS.neutral.color}">WCAG ${esc(m.requirement)}</span>`
|
|
215
255
|
)
|
|
216
256
|
.join('');
|
|
257
|
+
const hasMixedOutcomes = occurrenceCounts.fail > 0 && occurrenceCounts.cantTell > 0;
|
|
217
258
|
const countLabel =
|
|
218
259
|
r.occurrences.length > 1
|
|
219
|
-
? ` <span class="card-count">× ${r.occurrences.length.toLocaleString()}</span>`
|
|
260
|
+
? ` <span class="card-count">× ${r.occurrences.length.toLocaleString()}${hasMixedOutcomes ? ` (${occurrenceCounts.fail.toLocaleString()} fail / ${occurrenceCounts.cantTell.toLocaleString()} needs review)` : ''}</span>`
|
|
220
261
|
: '';
|
|
221
262
|
|
|
222
263
|
return `<div class="card">
|
|
@@ -226,8 +267,8 @@ function renderCards(checksResults) {
|
|
|
226
267
|
</div>
|
|
227
268
|
<div class="card-body">
|
|
228
269
|
<div class="card-meta">${wcagChips}</div>
|
|
229
|
-
<div class="card-selector"><span class="card-selector-label">Selector:</span> <code>${esc(
|
|
230
|
-
<div class="card-snippet">${esc(
|
|
270
|
+
<div class="card-selector"><span class="card-selector-label">Selector:</span> <code>${esc(representative.selector || '(none)')}</code></div>
|
|
271
|
+
<div class="card-snippet">${esc(representative.summary)}${representative.hint ? ` — ${esc(representative.hint)}` : ''}</div>
|
|
231
272
|
${r.occurrences.length > 1 ? `<p class="card-note">Selector/summary above are from one representative occurrence — ${r.occurrences.length.toLocaleString()} total on this rule.</p>` : ''}
|
|
232
273
|
</div>
|
|
233
274
|
</div>`;
|
|
@@ -250,9 +291,10 @@ function flattenOccurrences(checksResults) {
|
|
|
250
291
|
for (const r of checksResults) {
|
|
251
292
|
if (!Array.isArray(r.occurrences)) continue;
|
|
252
293
|
for (const occ of r.occurrences) {
|
|
294
|
+
const occurrenceOutcome = getOccurrenceOutcome(r, occ);
|
|
253
295
|
rows.push({
|
|
254
296
|
ruleId: r.ruleId,
|
|
255
|
-
outcome: r.outcome,
|
|
297
|
+
outcome: occurrenceOutcome || r.outcome,
|
|
256
298
|
severity: r.severity,
|
|
257
299
|
selector: occ.selector || '',
|
|
258
300
|
html: occ.html || '',
|
package/src/sarif.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
/* SPDX-License-Identifier: MPL-2.0 */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Renders one scan result as a SARIF 2.1.0 log (docs/SARIF.md) for GitHub
|
|
5
7
|
* Code Scanning / other SARIF-consuming dashboards -- a different output
|
|
6
|
-
* shape from --json's raw result, purpose-built for that ecosystem (
|
|
7
|
-
*
|
|
8
|
+
* shape from --json's raw result, purpose-built for that ecosystem (written
|
|
9
|
+
* by @surea11y/cli's --sarif flag, see its docs/CLI.md).
|
|
8
10
|
*
|
|
9
11
|
* Only fail/cantTell occurrences become SARIF results (a pass/notApplicable
|
|
10
12
|
* result has no occurrences at all -- see docs/OUTPUT_SCHEMA.md), the same
|
|
@@ -110,6 +112,18 @@ function buildResult(check, occurrence, level, artifactUri) {
|
|
|
110
112
|
};
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
function getOccurrenceOutcome(check, occurrence) {
|
|
116
|
+
const occurrenceOutcome =
|
|
117
|
+
occurrence &&
|
|
118
|
+
(occurrence.occurrenceOutcome === 'fail' || occurrence.occurrenceOutcome === 'cantTell'
|
|
119
|
+
? occurrence.occurrenceOutcome
|
|
120
|
+
: occurrence.outcome === 'fail' || occurrence.outcome === 'cantTell'
|
|
121
|
+
? occurrence.outcome
|
|
122
|
+
: null);
|
|
123
|
+
if (occurrenceOutcome) return occurrenceOutcome;
|
|
124
|
+
return check && (check.outcome === 'fail' || check.outcome === 'cantTell') ? check.outcome : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
113
127
|
function renderSarifReport(result, options = {}) {
|
|
114
128
|
const { toolVersion, informationUri, baselineEntries } = options;
|
|
115
129
|
const artifactUri = artifactUriFromResult(result);
|
|
@@ -133,7 +147,8 @@ function renderSarifReport(result, options = {}) {
|
|
|
133
147
|
for (const occurrence of check.occurrences) {
|
|
134
148
|
if (!occurrence) continue;
|
|
135
149
|
|
|
136
|
-
|
|
150
|
+
const occurrenceOutcome = getOccurrenceOutcome(check, occurrence);
|
|
151
|
+
if (occurrenceOutcome === 'fail') {
|
|
137
152
|
const reasonCode = getReasonCode(occurrence);
|
|
138
153
|
const html = typeof occurrence.html === 'string' ? occurrence.html : '';
|
|
139
154
|
const key = computeBaselineKey(check.ruleId, reasonCode, html);
|
|
@@ -143,7 +158,7 @@ function renderSarifReport(result, options = {}) {
|
|
|
143
158
|
continue; // already known via the baseline -- omit, don't re-gate
|
|
144
159
|
}
|
|
145
160
|
failResults.push(buildResult(check, occurrence, 'error', artifactUri));
|
|
146
|
-
} else {
|
|
161
|
+
} else if (occurrenceOutcome === 'cantTell') {
|
|
147
162
|
cantTellResults.push(buildResult(check, occurrence, 'warning', artifactUri));
|
|
148
163
|
}
|
|
149
164
|
}
|
|
@@ -157,7 +172,7 @@ function renderSarifReport(result, options = {}) {
|
|
|
157
172
|
tool: {
|
|
158
173
|
driver: {
|
|
159
174
|
name: 'surea11y',
|
|
160
|
-
informationUri: informationUri || 'https://github.com/
|
|
175
|
+
informationUri: informationUri || 'https://github.com/SureA11y/core',
|
|
161
176
|
version: toolVersion || '0.0.0',
|
|
162
177
|
rules
|
|
163
178
|
}
|