@a11y-skills/audit 0.2.0 → 0.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 +44 -0
- package/README.ja.md +52 -4
- package/README.md +53 -4
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +5 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/playwright/runAutoPlayDetection.js +8 -2
- package/dist/playwright/runAutocompleteAudit.js +40 -10
- package/dist/playwright/runAxeAudit.d.ts +4 -0
- package/dist/playwright/runAxeAudit.js +26 -30
- package/dist/playwright/runFocusIndicatorCheck.js +55 -12
- package/dist/playwright/runOrientationCheck.js +13 -7
- package/dist/playwright/runReflowCheck.js +18 -11
- package/dist/playwright/runTargetSizeCheck.js +42 -10
- package/dist/playwright/runTextSpacingCheck.js +52 -8
- package/dist/playwright/runTimeLimitDetector.js +36 -11
- package/dist/playwright/runZoomCheck.js +35 -11
- package/dist/schemas/index.d.ts +8 -1
- package/dist/schemas/index.js +388 -292
- package/dist/types.d.ts +137 -53
- package/dist/types.js +9 -0
- package/dist/utils/axe-format.d.ts +88 -0
- package/dist/utils/axe-format.js +361 -0
- package/dist/utils/layout.d.ts +2 -0
- package/dist/utils/layout.js +20 -1
- package/dist/utils/rule-registry.d.ts +216 -0
- package/dist/utils/rule-registry.js +220 -0
- package/dist/utils/test-harness.d.ts +2 -2
- package/dist/utils/test-harness.js +5 -6
- package/package.json +2 -1
package/dist/types.d.ts
CHANGED
|
@@ -1,50 +1,117 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type definitions for the WCAG audit checks shipped in @a11y-skills/audit.
|
|
3
|
+
*
|
|
4
|
+
* Every check returns the same axe-style envelope (`AuditCheckResult`):
|
|
5
|
+
* findings are normalized into `violations` / `incomplete` / `passes` /
|
|
6
|
+
* `inapplicable` rule arrays, while the check-specific evidence (measurements,
|
|
7
|
+
* screenshots, raw element records) lives under `details`.
|
|
8
|
+
*
|
|
9
|
+
* Classification policy: a finding is only a `violation` when the detection
|
|
10
|
+
* has no known blind spots and no WCAG exception could apply. Everything else
|
|
11
|
+
* (most heuristic detections) lands in `incomplete` — the manual-review queue.
|
|
3
12
|
*/
|
|
4
13
|
import type { AUDIT_DISCLAIMER } from './constants.js';
|
|
5
|
-
|
|
6
|
-
|
|
14
|
+
/** Identifier of the check that produced a result. */
|
|
15
|
+
export type CheckSource = 'axe-audit' | 'focus-indicator-check' | 'reflow-check' | 'target-size-check' | 'text-spacing-check' | 'zoom-200-check' | 'orientation-check' | 'autocomplete-audit' | 'time-limit-detector' | 'auto-play-detection';
|
|
16
|
+
export type NormalizedImpact = 'critical' | 'serious' | 'moderate' | 'minor';
|
|
17
|
+
/** One affected element (or the page itself, `target: ['html']`). */
|
|
18
|
+
export interface NormalizedNode {
|
|
19
|
+
/** CSS selector path. Page-level findings use `['html']`. */
|
|
7
20
|
target: string[];
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
21
|
+
/**
|
|
22
|
+
* outerHTML snippet (possibly truncated). When the source element's HTML
|
|
23
|
+
* could not be captured, a short synthetic representation is generated from
|
|
24
|
+
* the tag/role/name instead — never an empty string.
|
|
25
|
+
*/
|
|
26
|
+
html: string;
|
|
27
|
+
/** Whether `html` was truncated to the snippet length limit. */
|
|
28
|
+
htmlTruncated: boolean;
|
|
29
|
+
/** Human-readable description of why this node was flagged. */
|
|
30
|
+
failureSummary: string;
|
|
31
|
+
}
|
|
32
|
+
/** One rule's outcome, axe-style. */
|
|
33
|
+
export interface NormalizedRuleResult {
|
|
34
|
+
/** Namespaced rule id, e.g. `a11y-skills/focus-visible` (axe rules keep their own ids). */
|
|
11
35
|
id: string;
|
|
12
|
-
impact:
|
|
36
|
+
impact: NormalizedImpact | null;
|
|
13
37
|
description: string;
|
|
14
38
|
help: string;
|
|
39
|
+
/** W3C Understanding document (or axe docs for axe rules). */
|
|
15
40
|
helpUrl: string;
|
|
41
|
+
/** axe-style tags: `a11y-skills`, `wcag2aa` / `wcag21aa` / `wcag22aa`, `wcag247`-style SC tags. */
|
|
16
42
|
tags: string[];
|
|
17
|
-
nodes:
|
|
43
|
+
nodes: NormalizedNode[];
|
|
18
44
|
}
|
|
19
|
-
|
|
45
|
+
/** Rule-level counts derived from the four buckets (not from `details`). */
|
|
46
|
+
export interface AuditResultSummary {
|
|
47
|
+
/** Number of rules in `violations`. */
|
|
48
|
+
violationCount: number;
|
|
49
|
+
/** Number of rules in `incomplete`. */
|
|
50
|
+
incompleteCount: number;
|
|
51
|
+
/** Number of rules in `passes`. */
|
|
52
|
+
passCount: number;
|
|
53
|
+
/** Number of elements the check examined, when the check can count them. */
|
|
54
|
+
checkedNodes?: number;
|
|
55
|
+
}
|
|
56
|
+
/** Common envelope returned (and saved as JSON) by every check. */
|
|
57
|
+
export interface AuditCheckResult<TDetails> {
|
|
58
|
+
source: CheckSource;
|
|
20
59
|
url: string;
|
|
21
60
|
timestamp: string;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
61
|
+
/** Confirmed findings — detection has no blind spot and no exception can apply. */
|
|
62
|
+
violations: NormalizedRuleResult[];
|
|
63
|
+
/** Findings that need manual confirmation (heuristic detections, possible exceptions). */
|
|
64
|
+
incomplete: NormalizedRuleResult[];
|
|
65
|
+
/** Rules that ran and found nothing (nodes omitted). */
|
|
66
|
+
passes: NormalizedRuleResult[];
|
|
67
|
+
/** Rules that had nothing to examine on this page. */
|
|
68
|
+
inapplicable: NormalizedRuleResult[];
|
|
69
|
+
summary: AuditResultSummary;
|
|
70
|
+
/** Check-specific evidence; sufficient to re-derive the buckets above. */
|
|
71
|
+
details: TDetails;
|
|
27
72
|
disclaimer: typeof AUDIT_DISCLAIMER;
|
|
28
73
|
}
|
|
74
|
+
/** Execution configuration; rule/node data is fully held in the envelope buckets. */
|
|
75
|
+
export interface AxeAuditDetails {
|
|
76
|
+
/** axe-core tags the run was filtered by. */
|
|
77
|
+
tagsRun: string[];
|
|
78
|
+
/** Rule overrides forwarded to axe, if any. */
|
|
79
|
+
rulesOverride: Record<string, {
|
|
80
|
+
enabled: boolean;
|
|
81
|
+
}> | null;
|
|
82
|
+
/** Raw axe result counts (rule-level). */
|
|
83
|
+
violationRuleCount: number;
|
|
84
|
+
passRuleCount: number;
|
|
85
|
+
incompleteRuleCount: number;
|
|
86
|
+
inapplicableRuleCount: number;
|
|
87
|
+
}
|
|
88
|
+
export type AxeAuditResult = AuditCheckResult<AxeAuditDetails>;
|
|
29
89
|
export interface FocusRecord {
|
|
30
90
|
id: number;
|
|
31
91
|
tag: string;
|
|
32
92
|
role: string | null;
|
|
33
93
|
name: string;
|
|
94
|
+
selector: string;
|
|
95
|
+
html: string;
|
|
96
|
+
htmlTruncated: boolean;
|
|
34
97
|
hasFocusStyle: boolean;
|
|
35
98
|
diff: Record<string, string>;
|
|
36
99
|
}
|
|
100
|
+
/** Reference to an element captured in the browser context. */
|
|
101
|
+
export interface FocusElementRef {
|
|
102
|
+
tag: string;
|
|
103
|
+
role: string | null;
|
|
104
|
+
name: string;
|
|
105
|
+
selector: string;
|
|
106
|
+
html: string;
|
|
107
|
+
htmlTruncated: boolean;
|
|
108
|
+
}
|
|
37
109
|
/**
|
|
38
110
|
* WCAG 3.2.1 On Focus violation - context change triggered by focus
|
|
39
111
|
*/
|
|
40
112
|
export interface OnFocusViolation {
|
|
41
113
|
/** Element that triggered the navigation */
|
|
42
|
-
element:
|
|
43
|
-
tag: string;
|
|
44
|
-
role: string | null;
|
|
45
|
-
name: string;
|
|
46
|
-
selector: string;
|
|
47
|
-
};
|
|
114
|
+
element: FocusElementRef;
|
|
48
115
|
/** URL before focus */
|
|
49
116
|
fromUrl: string;
|
|
50
117
|
/** URL after focus (navigation target) */
|
|
@@ -52,20 +119,15 @@ export interface OnFocusViolation {
|
|
|
52
119
|
/** Type of context change */
|
|
53
120
|
changeType: 'navigation' | 'new-window' | 'dialog';
|
|
54
121
|
}
|
|
55
|
-
export interface
|
|
56
|
-
url: string;
|
|
122
|
+
export interface FocusCheckDetails {
|
|
57
123
|
totalFocusableElements: number;
|
|
58
124
|
elementsWithFocusStyle: number;
|
|
59
125
|
elementsWithoutFocusStyle: number;
|
|
60
|
-
/** WCAG 2.4.7
|
|
61
|
-
issues:
|
|
62
|
-
tag: string;
|
|
63
|
-
role: string | null;
|
|
64
|
-
name: string;
|
|
65
|
-
}>;
|
|
126
|
+
/** WCAG 2.4.7 findings (no computed-style change on focus) */
|
|
127
|
+
issues: FocusElementRef[];
|
|
66
128
|
/** WCAG 3.2.1 violations - focus triggered context change */
|
|
67
129
|
onFocusViolations: OnFocusViolation[];
|
|
68
|
-
/** WCAG 2.4.12
|
|
130
|
+
/** WCAG 2.4.11/2.4.12 findings - focus obscured by fixed/sticky elements */
|
|
69
131
|
focusObscuredIssues: FocusObscuredIssue[];
|
|
70
132
|
elementsWithObscuredFocus: number;
|
|
71
133
|
allElements: FocusRecord[];
|
|
@@ -78,6 +140,7 @@ export interface FocusCheckResult {
|
|
|
78
140
|
*/
|
|
79
141
|
screenshotPath: string;
|
|
80
142
|
}
|
|
143
|
+
export type FocusCheckResult = AuditCheckResult<FocusCheckDetails>;
|
|
81
144
|
/**
|
|
82
145
|
* Bounding rect for overlap calculations
|
|
83
146
|
*/
|
|
@@ -104,16 +167,11 @@ export interface FocusObscuredOverlap {
|
|
|
104
167
|
overlapArea: number;
|
|
105
168
|
}
|
|
106
169
|
/**
|
|
107
|
-
* WCAG 2.4.12
|
|
170
|
+
* WCAG 2.4.11/2.4.12 finding - focus indicator hidden by fixed/sticky content
|
|
108
171
|
*/
|
|
109
172
|
export interface FocusObscuredIssue {
|
|
110
173
|
/** The focused element that is obscured */
|
|
111
|
-
element:
|
|
112
|
-
tag: string;
|
|
113
|
-
role: string | null;
|
|
114
|
-
name: string;
|
|
115
|
-
selector: string;
|
|
116
|
-
};
|
|
174
|
+
element: FocusElementRef;
|
|
117
175
|
/** Bounding rect of the focused element */
|
|
118
176
|
elementRect: BoundingRect;
|
|
119
177
|
/** List of overlapping fixed/sticky elements */
|
|
@@ -124,6 +182,8 @@ export interface FocusObscuredIssue {
|
|
|
124
182
|
export interface ReflowIssue {
|
|
125
183
|
selector: string;
|
|
126
184
|
tagName: string;
|
|
185
|
+
html: string;
|
|
186
|
+
htmlTruncated: boolean;
|
|
127
187
|
rect: {
|
|
128
188
|
left: number;
|
|
129
189
|
right: number;
|
|
@@ -135,6 +195,8 @@ export interface ReflowIssue {
|
|
|
135
195
|
export interface ClippedTextElement {
|
|
136
196
|
selector: string;
|
|
137
197
|
tagName: string;
|
|
198
|
+
html: string;
|
|
199
|
+
htmlTruncated: boolean;
|
|
138
200
|
scrollWidth: number;
|
|
139
201
|
clientWidth: number;
|
|
140
202
|
scrollHeight: number;
|
|
@@ -142,8 +204,7 @@ export interface ClippedTextElement {
|
|
|
142
204
|
overflow: string;
|
|
143
205
|
overflowX: string;
|
|
144
206
|
}
|
|
145
|
-
export interface
|
|
146
|
-
url: string;
|
|
207
|
+
export interface ReflowCheckDetails {
|
|
147
208
|
viewport: {
|
|
148
209
|
width: number;
|
|
149
210
|
height: number;
|
|
@@ -154,6 +215,7 @@ export interface ReflowCheckResult {
|
|
|
154
215
|
overflowingElements: ReflowIssue[];
|
|
155
216
|
clippedTextElements: ClippedTextElement[];
|
|
156
217
|
}
|
|
218
|
+
export type ReflowCheckResult = AuditCheckResult<ReflowCheckDetails>;
|
|
157
219
|
/**
|
|
158
220
|
* Exception types for WCAG 2.5.8 Target Size (Minimum)
|
|
159
221
|
* - inline: Target is in a sentence or text block
|
|
@@ -163,11 +225,22 @@ export interface ReflowCheckResult {
|
|
|
163
225
|
* - essential-review: May be essential exception but requires manual review
|
|
164
226
|
*/
|
|
165
227
|
export type TargetSizeException = 'inline' | 'redundant' | 'ua-control' | 'spacing' | 'essential-review';
|
|
228
|
+
/**
|
|
229
|
+
* How thoroughly the SC 2.5.8 exceptions were assessed for a target.
|
|
230
|
+
* - ruled-out: every exception was checked and none applies — the finding is a
|
|
231
|
+
* confirmed violation
|
|
232
|
+
* - possible: a heuristic matched an exception; needs manual confirmation
|
|
233
|
+
* - not-assessed: the heuristics found no exception, but they cannot rule out
|
|
234
|
+
* the essential exception — needs manual confirmation
|
|
235
|
+
*/
|
|
236
|
+
export type TargetSizeExceptionAssessment = 'ruled-out' | 'possible' | 'not-assessed';
|
|
166
237
|
export interface TargetSizeIssue {
|
|
167
238
|
/** CSS selector for the element */
|
|
168
239
|
selector: string;
|
|
169
240
|
/** HTML tag name */
|
|
170
241
|
tagName: string;
|
|
242
|
+
html: string;
|
|
243
|
+
htmlTruncated: boolean;
|
|
171
244
|
/** ARIA role if present */
|
|
172
245
|
role: string | null;
|
|
173
246
|
/** Computed accessible name */
|
|
@@ -184,6 +257,8 @@ export interface TargetSizeIssue {
|
|
|
184
257
|
exception: TargetSizeException | null;
|
|
185
258
|
/** Human-readable exception details */
|
|
186
259
|
exceptionDetails: string | null;
|
|
260
|
+
/** Exception-coverage of the assessment (drives violation vs incomplete) */
|
|
261
|
+
exceptionAssessment: TargetSizeExceptionAssessment;
|
|
187
262
|
/** Link href for redundancy check */
|
|
188
263
|
href: string | null;
|
|
189
264
|
}
|
|
@@ -197,9 +272,7 @@ export interface TargetSizeSummary {
|
|
|
197
272
|
/** Number of targets with possible exceptions */
|
|
198
273
|
exceptedCount: number;
|
|
199
274
|
}
|
|
200
|
-
export interface
|
|
201
|
-
/** Page URL */
|
|
202
|
-
url: string;
|
|
275
|
+
export interface TargetSizeCheckDetails {
|
|
203
276
|
/** Total interactive elements checked */
|
|
204
277
|
totalTargetsChecked: number;
|
|
205
278
|
/** Elements failing AA threshold (< 24px) */
|
|
@@ -210,12 +283,15 @@ export interface TargetSizeCheckResult {
|
|
|
210
283
|
passedTargets: number;
|
|
211
284
|
/** Elements with possible exceptions */
|
|
212
285
|
exceptedTargets: TargetSizeIssue[];
|
|
213
|
-
/**
|
|
286
|
+
/** Per-target counts */
|
|
214
287
|
summary: TargetSizeSummary;
|
|
215
288
|
}
|
|
289
|
+
export type TargetSizeCheckResult = AuditCheckResult<TargetSizeCheckDetails>;
|
|
216
290
|
export interface TextSpacingIssue {
|
|
217
291
|
selector: string;
|
|
218
292
|
tagName: string;
|
|
293
|
+
html: string;
|
|
294
|
+
htmlTruncated: boolean;
|
|
219
295
|
beforeMetrics: {
|
|
220
296
|
scrollWidth: number;
|
|
221
297
|
scrollHeight: number;
|
|
@@ -233,22 +309,23 @@ export interface TextSpacingIssue {
|
|
|
233
309
|
overflowY: string;
|
|
234
310
|
issueType: 'horizontal-clip' | 'vertical-clip' | 'both';
|
|
235
311
|
}
|
|
236
|
-
export interface
|
|
237
|
-
url: string;
|
|
312
|
+
export interface TextSpacingCheckDetails {
|
|
238
313
|
clippedElements: TextSpacingIssue[];
|
|
239
314
|
totalElementsChecked: number;
|
|
240
315
|
}
|
|
316
|
+
export type TextSpacingCheckResult = AuditCheckResult<TextSpacingCheckDetails>;
|
|
241
317
|
export interface ZoomIssue {
|
|
242
318
|
selector: string;
|
|
243
319
|
tagName: string;
|
|
320
|
+
html: string;
|
|
321
|
+
htmlTruncated: boolean;
|
|
244
322
|
scrollWidth: number;
|
|
245
323
|
clientWidth: number;
|
|
246
324
|
scrollHeight: number;
|
|
247
325
|
clientHeight: number;
|
|
248
326
|
issueType: 'horizontal-scroll' | 'clipped-content';
|
|
249
327
|
}
|
|
250
|
-
export interface
|
|
251
|
-
url: string;
|
|
328
|
+
export interface ZoomCheckDetails {
|
|
252
329
|
zoomFactor: number;
|
|
253
330
|
viewport: {
|
|
254
331
|
width: number;
|
|
@@ -259,6 +336,7 @@ export interface ZoomCheckResult {
|
|
|
259
336
|
documentClientWidth: number;
|
|
260
337
|
clippedElements: ZoomIssue[];
|
|
261
338
|
}
|
|
339
|
+
export type ZoomCheckResult = AuditCheckResult<ZoomCheckDetails>;
|
|
262
340
|
export interface OrientationState {
|
|
263
341
|
lockMessageFound: boolean;
|
|
264
342
|
lockMessageText: string | null;
|
|
@@ -267,16 +345,18 @@ export interface OrientationState {
|
|
|
267
345
|
bodyHeight: number;
|
|
268
346
|
visibleTextLength: number;
|
|
269
347
|
}
|
|
270
|
-
export interface
|
|
271
|
-
url: string;
|
|
348
|
+
export interface OrientationCheckDetails {
|
|
272
349
|
portrait: OrientationState;
|
|
273
350
|
landscape: OrientationState;
|
|
274
351
|
hasOrientationLock: boolean;
|
|
275
352
|
lockDetectedIn: 'portrait' | 'landscape' | 'both' | 'none';
|
|
276
353
|
}
|
|
354
|
+
export type OrientationCheckResult = AuditCheckResult<OrientationCheckDetails>;
|
|
277
355
|
export interface AutocompleteIssue {
|
|
278
356
|
selector: string;
|
|
279
357
|
tagName: string;
|
|
358
|
+
html: string;
|
|
359
|
+
htmlTruncated: boolean;
|
|
280
360
|
inputType: string;
|
|
281
361
|
name: string | null;
|
|
282
362
|
id: string | null;
|
|
@@ -286,16 +366,18 @@ export interface AutocompleteIssue {
|
|
|
286
366
|
matchedBy: 'name' | 'id' | 'label' | 'placeholder';
|
|
287
367
|
issueType: 'missing' | 'invalid';
|
|
288
368
|
}
|
|
289
|
-
export interface
|
|
290
|
-
url: string;
|
|
369
|
+
export interface AutocompleteAuditDetails {
|
|
291
370
|
totalFieldsChecked: number;
|
|
292
371
|
missingAutocomplete: AutocompleteIssue[];
|
|
293
372
|
invalidAutocomplete: AutocompleteIssue[];
|
|
294
373
|
}
|
|
374
|
+
export type AutocompleteAuditResult = AuditCheckResult<AutocompleteAuditDetails>;
|
|
295
375
|
export interface MetaRefreshInfo {
|
|
296
376
|
content: string;
|
|
297
377
|
seconds: number;
|
|
298
378
|
url: string | null;
|
|
379
|
+
html: string;
|
|
380
|
+
htmlTruncated: boolean;
|
|
299
381
|
}
|
|
300
382
|
export interface TimerInfo {
|
|
301
383
|
type: 'setTimeout' | 'setInterval';
|
|
@@ -306,14 +388,16 @@ export interface CountdownIndicator {
|
|
|
306
388
|
selector: string;
|
|
307
389
|
text: string;
|
|
308
390
|
tagName: string;
|
|
391
|
+
html: string;
|
|
392
|
+
htmlTruncated: boolean;
|
|
309
393
|
}
|
|
310
|
-
export interface
|
|
311
|
-
url: string;
|
|
394
|
+
export interface TimeLimitDetectorDetails {
|
|
312
395
|
metaRefresh: MetaRefreshInfo[];
|
|
313
396
|
timers: TimerInfo[];
|
|
314
397
|
countdownIndicators: CountdownIndicator[];
|
|
315
398
|
hasTimeLimits: boolean;
|
|
316
399
|
}
|
|
400
|
+
export type TimeLimitDetectorResult = AuditCheckResult<TimeLimitDetectorDetails>;
|
|
317
401
|
export interface ScreenshotRecord {
|
|
318
402
|
time: string;
|
|
319
403
|
path: string;
|
|
@@ -354,8 +438,7 @@ export interface PauseVerificationResult {
|
|
|
354
438
|
pauseWorked: boolean | null;
|
|
355
439
|
error: string | null;
|
|
356
440
|
}
|
|
357
|
-
export interface
|
|
358
|
-
url: string;
|
|
441
|
+
export interface AutoPlayDetectionDetails {
|
|
359
442
|
screenshotRecords: ScreenshotRecord[];
|
|
360
443
|
comparisons: ComparisonResult[];
|
|
361
444
|
hasAutoPlayContent: boolean;
|
|
@@ -364,3 +447,4 @@ export interface AutoPlayDetectionResult {
|
|
|
364
447
|
pauseVerification: PauseVerificationResult;
|
|
365
448
|
recommendation: string;
|
|
366
449
|
}
|
|
450
|
+
export type AutoPlayDetectionResult = AuditCheckResult<AutoPlayDetectionDetails>;
|
package/dist/types.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type definitions for the WCAG audit checks shipped in @a11y-skills/audit.
|
|
3
|
+
*
|
|
4
|
+
* Every check returns the same axe-style envelope (`AuditCheckResult`):
|
|
5
|
+
* findings are normalized into `violations` / `incomplete` / `passes` /
|
|
6
|
+
* `inapplicable` rule arrays, while the check-specific evidence (measurements,
|
|
7
|
+
* screenshots, raw element records) lives under `details`.
|
|
8
|
+
*
|
|
9
|
+
* Classification policy: a finding is only a `violation` when the detection
|
|
10
|
+
* has no known blind spots and no WCAG exception could apply. Everything else
|
|
11
|
+
* (most heuristic detections) lands in `incomplete` — the manual-review queue.
|
|
3
12
|
*/
|
|
4
13
|
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure mappers from check-specific `details` to the axe-style buckets
|
|
3
|
+
* (`violations` / `incomplete` / `passes` / `inapplicable`), plus the envelope
|
|
4
|
+
* assembler and the opt-in cross-check merge.
|
|
5
|
+
*
|
|
6
|
+
* Everything here is browser-independent: the runners gather evidence into a
|
|
7
|
+
* details object, and these functions derive the normalized view from it.
|
|
8
|
+
* Because the details objects are part of the saved JSON, the buckets can be
|
|
9
|
+
* re-derived from a result file at any time.
|
|
10
|
+
*/
|
|
11
|
+
import type { AuditCheckResult, AuditResultSummary, AutocompleteAuditDetails, AutoPlayDetectionDetails, CheckSource, FocusCheckDetails, NormalizedRuleResult, OrientationCheckDetails, ReflowCheckDetails, TargetSizeCheckDetails, TextSpacingCheckDetails, TimeLimitDetectorDetails, ZoomCheckDetails } from '../types.js';
|
|
12
|
+
import { AUDIT_DISCLAIMER } from '../constants.js';
|
|
13
|
+
export interface NormalizedBuckets {
|
|
14
|
+
violations: NormalizedRuleResult[];
|
|
15
|
+
incomplete: NormalizedRuleResult[];
|
|
16
|
+
passes: NormalizedRuleResult[];
|
|
17
|
+
inapplicable: NormalizedRuleResult[];
|
|
18
|
+
/** Number of elements the check examined, when countable. */
|
|
19
|
+
checkedNodes?: number;
|
|
20
|
+
}
|
|
21
|
+
/** Assemble the common envelope from a check's details and buckets. */
|
|
22
|
+
export declare function buildAuditResult<TDetails>(args: {
|
|
23
|
+
source: CheckSource;
|
|
24
|
+
url: string;
|
|
25
|
+
details: TDetails;
|
|
26
|
+
buckets: NormalizedBuckets;
|
|
27
|
+
timestamp?: string;
|
|
28
|
+
}): AuditCheckResult<TDetails>;
|
|
29
|
+
export declare function normalizeFocusCheck(details: FocusCheckDetails): NormalizedBuckets;
|
|
30
|
+
export declare function normalizeReflowCheck(details: ReflowCheckDetails): NormalizedBuckets;
|
|
31
|
+
export declare function normalizeTargetSizeCheck(details: TargetSizeCheckDetails): NormalizedBuckets;
|
|
32
|
+
export declare function normalizeTextSpacingCheck(details: TextSpacingCheckDetails): NormalizedBuckets;
|
|
33
|
+
export declare function normalizeZoomCheck(details: ZoomCheckDetails): NormalizedBuckets;
|
|
34
|
+
export declare function normalizeOrientationCheck(details: OrientationCheckDetails): NormalizedBuckets;
|
|
35
|
+
export declare function normalizeAutocompleteAudit(details: AutocompleteAuditDetails): NormalizedBuckets;
|
|
36
|
+
export declare function normalizeTimeLimitDetector(details: TimeLimitDetectorDetails): NormalizedBuckets;
|
|
37
|
+
export declare function normalizeAutoPlayDetection(details: AutoPlayDetectionDetails): NormalizedBuckets;
|
|
38
|
+
/** Structural subset of an axe-core rule result (avoids a hard axe dependency). */
|
|
39
|
+
export interface RawAxeRule {
|
|
40
|
+
id: string;
|
|
41
|
+
impact?: string | null;
|
|
42
|
+
description: string;
|
|
43
|
+
help: string;
|
|
44
|
+
helpUrl: string;
|
|
45
|
+
tags: string[];
|
|
46
|
+
nodes: Array<{
|
|
47
|
+
html: string;
|
|
48
|
+
target: unknown[];
|
|
49
|
+
failureSummary?: string | undefined;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export interface RawAxeResults {
|
|
53
|
+
violations: RawAxeRule[];
|
|
54
|
+
incomplete: RawAxeRule[];
|
|
55
|
+
passes: RawAxeRule[];
|
|
56
|
+
inapplicable: RawAxeRule[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Normalize raw axe-core results into the common buckets. Must be fed the RAW
|
|
60
|
+
* `AxeResults` (before any reduction) — pass/incomplete details are not
|
|
61
|
+
* recoverable afterwards. Node lists are kept for violations and incomplete;
|
|
62
|
+
* passes/inapplicable carry rule metadata only.
|
|
63
|
+
*/
|
|
64
|
+
export declare function normalizeAxeResults(raw: RawAxeResults): NormalizedBuckets;
|
|
65
|
+
/** Combined view over several checks of the SAME page. */
|
|
66
|
+
export interface MergedAuditResult {
|
|
67
|
+
url: string;
|
|
68
|
+
/** Latest timestamp among the merged results. */
|
|
69
|
+
timestamp: string;
|
|
70
|
+
sources: CheckSource[];
|
|
71
|
+
violations: NormalizedRuleResult[];
|
|
72
|
+
incomplete: NormalizedRuleResult[];
|
|
73
|
+
passes: NormalizedRuleResult[];
|
|
74
|
+
inapplicable: NormalizedRuleResult[];
|
|
75
|
+
summary: AuditResultSummary;
|
|
76
|
+
disclaimer: typeof AUDIT_DISCLAIMER;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Merge several check results for the same URL into one normalized view.
|
|
80
|
+
*
|
|
81
|
+
* - Results with differing URLs are rejected (throws).
|
|
82
|
+
* - The same rule id is merged into one entry; nodes are deduplicated by
|
|
83
|
+
* `target` + `failureSummary`. Identical selectors inside different frames
|
|
84
|
+
* or shadow roots are NOT distinguished.
|
|
85
|
+
* - A rule appearing in several buckets is placed in the highest-priority one:
|
|
86
|
+
* violations > incomplete > passes > inapplicable.
|
|
87
|
+
*/
|
|
88
|
+
export declare function mergeNormalizedResults(results: Array<AuditCheckResult<unknown>>): MergedAuditResult;
|