eaa-kit 0.5.0 → 0.7.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/README.md +53 -5
- package/dist/astro/index.d.ts +1 -1
- package/dist/astro/index.js +1 -1
- package/dist/audit/runners/worker.js +1 -1
- package/dist/audit-Bwm1xBdJ.js +2 -0
- package/dist/{audit-B282A3EA.js → audit-DRjAY2iY.js} +243 -107
- package/dist/{baseline-CuAS2lMk.js → baseline-BpE7DbDq.js} +36 -9
- package/dist/baseline-rtnj3RHB.js +2 -0
- package/dist/cache-D4dOIuOi.js +219 -0
- package/dist/checklist-DDd9jewi.js +44 -0
- package/dist/cli/index.js +130 -17
- package/dist/{command-C3D7JWn6.js → command-3r4fht5f.js} +102 -10
- package/dist/{component-C3GL1Mnu.js → component-ChisoFyY.js} +1 -1
- package/dist/{component-DKd3EHOg.js → component-DcCn3BJS.js} +1 -1
- package/dist/{coverage-B_Y6l-Ra.js → coverage-cb5kPp48.js} +45 -290
- package/dist/coverage-n1X9CkiG.js +3 -0
- package/dist/{crawl-BUWPgaGW.js → crawl-BHkJmUkA.js} +42 -8
- package/dist/criteria-CnPJKGeb.js +286 -0
- package/dist/eleventy/index.d.ts +1 -1
- package/dist/eleventy/index.js +1 -1
- package/dist/{html-C4vktg0A.js → html-BIxxF3FC.js} +54 -30
- package/dist/{impact-DZt2oBCP.js → impact-luGCnN8F.js} +11 -1
- package/dist/index.d.ts +87 -1
- package/dist/index.js +3 -3
- package/dist/{init-CW7LfGT5.js → init-DrFfzjvm.js} +3 -3
- package/dist/jsdom-BUYuVJJm.js +3 -0
- package/dist/{jsdom-B--cEH-G.js → jsdom-CsW_xVcA.js} +11 -12
- package/dist/{json-DROX33kh.js → json-Bus6YhxB.js} +13 -17
- package/dist/{json-D_Mnnft5.js → json-CrQmoTj6.js} +1 -1
- package/dist/{load-yAR4wzez.js → load-BaWMa-G_.js} +11 -2
- package/dist/load-n62iXDq_.js +2 -0
- package/dist/nuxt/index.d.ts +1 -1
- package/dist/nuxt/index.js +1 -1
- package/dist/{playwright-BojtYVUa.js → playwright-ukCr_5o3.js} +3 -2
- package/dist/{pool-BO25OIez.js → pool-B4tEUa2S.js} +6 -2
- package/dist/{remediation-CMBIrnpN.js → remediation-C0klJblu.js} +35 -4
- package/dist/{render-DbGOVmhx.js → render-C1sUZZI_.js} +145 -6
- package/dist/{result-BWcYXeRs.js → result-B7_zVxAX.js} +24 -1
- package/dist/result-CRmjjoAA.js +2 -0
- package/dist/review-CdMK2GpQ.js +161 -0
- package/dist/review-DIbOTmOM.js +2 -0
- package/dist/{run-CtcEUhbe.d.ts → run-BK2blxMI.d.ts} +18 -0
- package/dist/{run-C2nKFcb-.js → run-DrS0Komf.js} +6 -2
- package/dist/{sarif-B-UBcVu8.js → sarif-BNCE0PNT.js} +24 -12
- package/dist/statement/templates/at.de.md +14 -0
- package/dist/statement/templates/at.en.md +14 -0
- package/dist/statement/templates/ch.de.md +14 -0
- package/dist/statement/templates/ch.en.md +14 -0
- package/dist/statement/templates/de.de.md +14 -0
- package/dist/statement/templates/de.en.md +14 -0
- package/dist/statement/templates/es.en.md +14 -0
- package/dist/statement/templates/es.es.md +15 -0
- package/dist/statement/templates/fr.en.md +14 -0
- package/dist/statement/templates/fr.fr.md +15 -0
- package/dist/statement/templates/it.en.md +14 -0
- package/dist/statement/templates/it.it.md +15 -0
- package/dist/statement/templates/nl.en.md +14 -0
- package/dist/statement/templates/nl.nl.md +15 -0
- package/dist/{text-CKKpzkYM.js → text-BF1LHMiV.js} +22 -2
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +1 -1
- package/dist/webpack/index.d.ts +1 -1
- package/dist/webpack/index.js +1 -1
- package/dist/worksheet-BIxMtbUd.js +64 -0
- package/package.json +3 -2
- package/dist/audit-DyHPmGeD.js +0 -2
- package/dist/baseline-0u4df12v.js +0 -2
- package/dist/jsdom-22Bkt65v.js +0 -3
- package/dist/load-CFq2VQtT.js +0 -2
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
//#region src/audit/criteria.ts
|
|
2
|
+
/**
|
|
3
|
+
* WCAG 2.2 at Levels A and AA, which is what this tool audits against.
|
|
4
|
+
*
|
|
5
|
+
* 4.1.1 Parsing is deliberately absent: it was removed in WCAG 2.2 and counting
|
|
6
|
+
* it would inflate the denominator with a criterion nobody has to meet.
|
|
7
|
+
*/
|
|
8
|
+
const WCAG22_AA_CRITERIA = [
|
|
9
|
+
{
|
|
10
|
+
number: "1.1.1",
|
|
11
|
+
title: "Non-text Content",
|
|
12
|
+
level: "A"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
number: "1.2.1",
|
|
16
|
+
title: "Audio-only and Video-only (Prerecorded)",
|
|
17
|
+
level: "A"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
number: "1.2.2",
|
|
21
|
+
title: "Captions (Prerecorded)",
|
|
22
|
+
level: "A"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
number: "1.2.3",
|
|
26
|
+
title: "Audio Description or Media Alternative (Prerecorded)",
|
|
27
|
+
level: "A"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
number: "1.2.4",
|
|
31
|
+
title: "Captions (Live)",
|
|
32
|
+
level: "AA"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
number: "1.2.5",
|
|
36
|
+
title: "Audio Description (Prerecorded)",
|
|
37
|
+
level: "AA"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
number: "1.3.1",
|
|
41
|
+
title: "Info and Relationships",
|
|
42
|
+
level: "A"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
number: "1.3.2",
|
|
46
|
+
title: "Meaningful Sequence",
|
|
47
|
+
level: "A"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
number: "1.3.3",
|
|
51
|
+
title: "Sensory Characteristics",
|
|
52
|
+
level: "A"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
number: "1.3.4",
|
|
56
|
+
title: "Orientation",
|
|
57
|
+
level: "AA"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
number: "1.3.5",
|
|
61
|
+
title: "Identify Input Purpose",
|
|
62
|
+
level: "AA"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
number: "1.4.1",
|
|
66
|
+
title: "Use of Color",
|
|
67
|
+
level: "A"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
number: "1.4.2",
|
|
71
|
+
title: "Audio Control",
|
|
72
|
+
level: "A"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
number: "1.4.3",
|
|
76
|
+
title: "Contrast (Minimum)",
|
|
77
|
+
level: "AA"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
number: "1.4.4",
|
|
81
|
+
title: "Resize Text",
|
|
82
|
+
level: "AA"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
number: "1.4.5",
|
|
86
|
+
title: "Images of Text",
|
|
87
|
+
level: "AA"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
number: "1.4.10",
|
|
91
|
+
title: "Reflow",
|
|
92
|
+
level: "AA"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
number: "1.4.11",
|
|
96
|
+
title: "Non-text Contrast",
|
|
97
|
+
level: "AA"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
number: "1.4.12",
|
|
101
|
+
title: "Text Spacing",
|
|
102
|
+
level: "AA"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
number: "1.4.13",
|
|
106
|
+
title: "Content on Hover or Focus",
|
|
107
|
+
level: "AA"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
number: "2.1.1",
|
|
111
|
+
title: "Keyboard",
|
|
112
|
+
level: "A"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
number: "2.1.2",
|
|
116
|
+
title: "No Keyboard Trap",
|
|
117
|
+
level: "A"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
number: "2.1.4",
|
|
121
|
+
title: "Character Key Shortcuts",
|
|
122
|
+
level: "A"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
number: "2.2.1",
|
|
126
|
+
title: "Timing Adjustable",
|
|
127
|
+
level: "A"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
number: "2.2.2",
|
|
131
|
+
title: "Pause, Stop, Hide",
|
|
132
|
+
level: "A"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
number: "2.3.1",
|
|
136
|
+
title: "Three Flashes or Below Threshold",
|
|
137
|
+
level: "A"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
number: "2.4.1",
|
|
141
|
+
title: "Bypass Blocks",
|
|
142
|
+
level: "A"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
number: "2.4.2",
|
|
146
|
+
title: "Page Titled",
|
|
147
|
+
level: "A"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
number: "2.4.3",
|
|
151
|
+
title: "Focus Order",
|
|
152
|
+
level: "A"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
number: "2.4.4",
|
|
156
|
+
title: "Link Purpose (In Context)",
|
|
157
|
+
level: "A"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
number: "2.4.5",
|
|
161
|
+
title: "Multiple Ways",
|
|
162
|
+
level: "AA"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
number: "2.4.6",
|
|
166
|
+
title: "Headings and Labels",
|
|
167
|
+
level: "AA"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
number: "2.4.7",
|
|
171
|
+
title: "Focus Visible",
|
|
172
|
+
level: "AA"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
number: "2.4.11",
|
|
176
|
+
title: "Focus Not Obscured (Minimum)",
|
|
177
|
+
level: "AA"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
number: "2.5.1",
|
|
181
|
+
title: "Pointer Gestures",
|
|
182
|
+
level: "A"
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
number: "2.5.2",
|
|
186
|
+
title: "Pointer Cancellation",
|
|
187
|
+
level: "A"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
number: "2.5.3",
|
|
191
|
+
title: "Label in Name",
|
|
192
|
+
level: "A"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
number: "2.5.4",
|
|
196
|
+
title: "Motion Actuation",
|
|
197
|
+
level: "A"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
number: "2.5.7",
|
|
201
|
+
title: "Dragging Movements",
|
|
202
|
+
level: "AA"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
number: "2.5.8",
|
|
206
|
+
title: "Target Size (Minimum)",
|
|
207
|
+
level: "AA"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
number: "3.1.1",
|
|
211
|
+
title: "Language of Page",
|
|
212
|
+
level: "A"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
number: "3.1.2",
|
|
216
|
+
title: "Language of Parts",
|
|
217
|
+
level: "AA"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
number: "3.2.1",
|
|
221
|
+
title: "On Focus",
|
|
222
|
+
level: "A"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
number: "3.2.2",
|
|
226
|
+
title: "On Input",
|
|
227
|
+
level: "A"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
number: "3.2.3",
|
|
231
|
+
title: "Consistent Navigation",
|
|
232
|
+
level: "AA"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
number: "3.2.4",
|
|
236
|
+
title: "Consistent Identification",
|
|
237
|
+
level: "AA"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
number: "3.2.6",
|
|
241
|
+
title: "Consistent Help",
|
|
242
|
+
level: "A"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
number: "3.3.1",
|
|
246
|
+
title: "Error Identification",
|
|
247
|
+
level: "A"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
number: "3.3.2",
|
|
251
|
+
title: "Labels or Instructions",
|
|
252
|
+
level: "A"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
number: "3.3.3",
|
|
256
|
+
title: "Error Suggestion",
|
|
257
|
+
level: "AA"
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
number: "3.3.4",
|
|
261
|
+
title: "Error Prevention (Legal, Financial, Data)",
|
|
262
|
+
level: "AA"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
number: "3.3.7",
|
|
266
|
+
title: "Redundant Entry",
|
|
267
|
+
level: "A"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
number: "3.3.8",
|
|
271
|
+
title: "Accessible Authentication (Minimum)",
|
|
272
|
+
level: "AA"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
number: "4.1.2",
|
|
276
|
+
title: "Name, Role, Value",
|
|
277
|
+
level: "A"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
number: "4.1.3",
|
|
281
|
+
title: "Status Messages",
|
|
282
|
+
level: "AA"
|
|
283
|
+
}
|
|
284
|
+
];
|
|
285
|
+
//#endregion
|
|
286
|
+
export { WCAG22_AA_CRITERIA as t };
|
package/dist/eleventy/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as IntegrationOptions, t as BuildAuditError } from "../run-
|
|
1
|
+
import { n as IntegrationOptions, t as BuildAuditError } from "../run-BK2blxMI.js";
|
|
2
2
|
//#region src/eleventy/index.d.ts
|
|
3
3
|
/** What `eleventy.after` is given. Only two of its fields are read. */
|
|
4
4
|
interface EleventyAfterEvent {
|
package/dist/eleventy/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { i as escapeText, n as count,
|
|
1
|
+
import { a as impactLabel, r as byImpactThenRule } from "./impact-luGCnN8F.js";
|
|
2
|
+
import { i as escapeText, n as count, r as escapeAttribute, s as standardsReference, t as collapse } from "./text-BF1LHMiV.js";
|
|
3
3
|
import { t as TOOL_VERSION } from "./version-B3v4rNoG.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { i as
|
|
7
|
-
import {
|
|
4
|
+
import { u as runEngine } from "./result-B7_zVxAX.js";
|
|
5
|
+
import { l as reviewSentence } from "./review-CdMK2GpQ.js";
|
|
6
|
+
import { a as isShared, c as missedParts, i as groupIssues, l as reusedPart, n as blindRules, o as issueTotals, r as coverageParts, s as discoveryLabel, t as remediationFor } from "./remediation-C0klJblu.js";
|
|
7
|
+
import { r as componentPath } from "./component-ChisoFyY.js";
|
|
8
|
+
import { a as manualCheckFor, o as understandingUrl, r as reviewSummary, t as buildCoverage } from "./coverage-cb5kPp48.js";
|
|
9
|
+
import { n as buildSummary } from "./json-Bus6YhxB.js";
|
|
8
10
|
import axe from "axe-core";
|
|
9
11
|
//#region src/audit/report/html.ts
|
|
10
12
|
/**
|
|
@@ -36,8 +38,8 @@ const MAX_NODES = 5;
|
|
|
36
38
|
/** Longest element markup shown before it is truncated. */
|
|
37
39
|
const MAX_SNIPPET = 200;
|
|
38
40
|
function buildHtmlReport(audits, options) {
|
|
39
|
-
const engine = audits
|
|
40
|
-
const
|
|
41
|
+
const engine = runEngine(audits);
|
|
42
|
+
const totals = buildSummary(audits, options.failOn);
|
|
41
43
|
const generatedAt = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
|
|
42
44
|
const title = `Accessibility audit · ${options.directory}`;
|
|
43
45
|
return `<!doctype html>
|
|
@@ -54,15 +56,15 @@ ${STYLES}
|
|
|
54
56
|
<body>
|
|
55
57
|
<main>
|
|
56
58
|
<h1>Accessibility audit</h1>
|
|
57
|
-
${verdict(
|
|
59
|
+
${verdict(totals, options)}
|
|
58
60
|
${scoreboard(audits)}
|
|
59
61
|
${issues(audits, options)}
|
|
60
62
|
${runDetails(audits, engine, generatedAt, options)}
|
|
61
63
|
${notMeasured(options)}
|
|
62
|
-
${summary(
|
|
64
|
+
${summary(totals, options)}
|
|
63
65
|
${pages(audits)}
|
|
64
66
|
${notEvaluated(audits)}
|
|
65
|
-
${coverageSection(audits)}
|
|
67
|
+
${coverageSection(audits, options)}
|
|
66
68
|
${footer()}
|
|
67
69
|
</main>
|
|
68
70
|
</body>
|
|
@@ -77,11 +79,11 @@ ${footer()}
|
|
|
77
79
|
* which would be an embarrassing thing for this document in particular to get
|
|
78
80
|
* wrong.
|
|
79
81
|
*/
|
|
80
|
-
function verdict(
|
|
81
|
-
const unaudited =
|
|
82
|
+
function verdict(totals, options) {
|
|
83
|
+
const unaudited = totals.pagesNotAudited;
|
|
82
84
|
if (unaudited > 0) return banner("broken", "Could not finish", `${count(unaudited, "page")} could not be audited, so this run reached no verdict.`);
|
|
83
|
-
if (failing > 0) return banner("fail", "Violations found", `${count(failing, "violation")} at or above ${escapeText(options.failOn)}.`);
|
|
84
|
-
const below =
|
|
85
|
+
if (totals.failing > 0) return banner("fail", "Violations found", `${count(totals.failing, "violation")} at or above ${escapeText(options.failOn)}.`);
|
|
86
|
+
const below = totals.violations - totals.failing;
|
|
85
87
|
if (below > 0) return banner("pass", "No violations at the threshold", `${count(below, "violation")} below ${escapeText(options.failOn)}, which do not fail the run.`);
|
|
86
88
|
const completeness = options.completeness;
|
|
87
89
|
if (completeness && !completeness.complete) return banner("partial", "No violations found, but the whole site was not measured", `${escapeText(missedParts(completeness).join(", "))}. This report describes ${count(completeness.audited, "page")}, not the site.`);
|
|
@@ -101,6 +103,8 @@ function runDetails(audits, engine, generatedAt, options) {
|
|
|
101
103
|
];
|
|
102
104
|
if (options.baseUrl) rows.splice(1, 0, ["Base URL", options.baseUrl]);
|
|
103
105
|
if (options.completeness) rows.splice(2, 0, ["Pages found via", discoveryLabel(options.completeness.discovery)]);
|
|
106
|
+
const reused = options.completeness ? reusedPart(options.completeness) : void 0;
|
|
107
|
+
if (reused !== void 0) rows.push(["Reused", reused]);
|
|
104
108
|
return `<h2>The run</h2>\n<dl class="run">\n${rows.map(([term, value]) => ` <div><dt>${escapeText(term)}</dt><dd>${escapeText(value)}</dd></div>`).join("\n")}\n</dl>`;
|
|
105
109
|
}
|
|
106
110
|
/** Pages named individually before the rest are counted. */
|
|
@@ -137,13 +141,12 @@ function notMeasured(options) {
|
|
|
137
141
|
* the kind of bug nobody notices until a client does, so there is one place
|
|
138
142
|
* that counts.
|
|
139
143
|
*/
|
|
140
|
-
function summary(
|
|
141
|
-
const totals = buildSummary(audits, options.failOn);
|
|
144
|
+
function summary(totals, options) {
|
|
142
145
|
const impacts = IMPACT_ORDER.filter((impact) => totals.byImpact[impact] > 0).map((impact) => ` <li><span class="badge ${escapeAttribute(impact)}">${escapeText(impact)}</span> ${totals.byImpact[impact]}</li>`).join("\n");
|
|
143
146
|
return `<h2>Summary</h2>
|
|
144
147
|
<ul class="counts">
|
|
145
148
|
<li><strong>${count(totals.violations, "violation")}</strong> on ${totals.pagesWithViolations} of ${count(totals.pages, "page")}, across ${count(totals.violatingElements, "element")}</li>
|
|
146
|
-
<li><strong>${failing}</strong> at or above ${escapeText(options.failOn)}</li>
|
|
149
|
+
<li><strong>${totals.failing}</strong> at or above ${escapeText(options.failOn)}</li>
|
|
147
150
|
<li><strong>${totals.needsReview}</strong> ${totals.needsReview === 1 ? "rule needs" : "rules need"} manual review</li>
|
|
148
151
|
<li><strong>${totals.notEvaluated}</strong> ${totals.notEvaluated === 1 ? "rule was" : "rules were"} not evaluated by this engine</li>
|
|
149
152
|
${totals.accepted > 0 ? ` <li><strong>${totals.accepted}</strong> ${totals.accepted === 1 ? "element is" : "elements are"} accepted by the baseline, and not counted above</li>` : ""}
|
|
@@ -236,8 +239,8 @@ ${manualBlock(ruleId, finding)}
|
|
|
236
239
|
* majority of WCAG, and a percentage would present a limit of automated testing
|
|
237
240
|
* as a property of this site.
|
|
238
241
|
*/
|
|
239
|
-
function coverageSection(audits) {
|
|
240
|
-
const coverage = buildCoverage(audits);
|
|
242
|
+
function coverageSection(audits, options) {
|
|
243
|
+
const coverage = buildCoverage(audits, void 0, options.review);
|
|
241
244
|
const rows = coverage.criteria.map((criterion) => {
|
|
242
245
|
const url = understandingUrl(criterion.number);
|
|
243
246
|
const name = escapeText(`${criterion.number} ${criterion.title}`);
|
|
@@ -245,7 +248,7 @@ function coverageSection(audits) {
|
|
|
245
248
|
return ` <tr class="${escapeAttribute(criterion.status)}">
|
|
246
249
|
<td>${linked}</td>
|
|
247
250
|
<td>${escapeText(criterion.level)}</td>
|
|
248
|
-
<td>${statusText(criterion)}</td>
|
|
251
|
+
<td>${statusText(criterion)}${reviewCell(criterion)}</td>
|
|
249
252
|
</tr>`;
|
|
250
253
|
}).join("\n");
|
|
251
254
|
return `<h2>Coverage of WCAG 2.2 AA</h2>
|
|
@@ -263,6 +266,7 @@ function coverageSection(audits) {
|
|
|
263
266
|
of WCAG cannot be automated, and a percentage here would present that limit of automated
|
|
264
267
|
testing as though it were a measurement of this site.
|
|
265
268
|
</p>
|
|
269
|
+
${reviewParagraph(coverage)}
|
|
266
270
|
<div class="scroll">
|
|
267
271
|
<table class="coverage">
|
|
268
272
|
<thead><tr><th>Success criterion</th><th>Level</th><th>This run</th></tr></thead>
|
|
@@ -272,6 +276,20 @@ ${rows}
|
|
|
272
276
|
</table>
|
|
273
277
|
</div>`;
|
|
274
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* What a person recorded, where a review was supplied.
|
|
281
|
+
*
|
|
282
|
+
* Its own paragraph, under the four counts and visibly apart from them: the
|
|
283
|
+
* counts are what an engine measured and this is what somebody says they
|
|
284
|
+
* checked. This document is the one that leaves the building, so the difference
|
|
285
|
+
* between the two has to survive being quoted.
|
|
286
|
+
*/
|
|
287
|
+
function reviewParagraph(coverage) {
|
|
288
|
+
const summary = reviewSummary(coverage);
|
|
289
|
+
if (summary === void 0) return "";
|
|
290
|
+
return `<p class="note review">${escapeText(summary)} eaa-kit cannot check that anything
|
|
291
|
+
recorded in a review is true; it reports it as the claim it is.</p>`;
|
|
292
|
+
}
|
|
275
293
|
function statusText(criterion) {
|
|
276
294
|
const browser = criterion.browserWouldAnswer ? " <em>(a real browser would answer this)</em>" : "";
|
|
277
295
|
switch (criterion.status) {
|
|
@@ -281,6 +299,16 @@ function statusText(criterion) {
|
|
|
281
299
|
case "no-automated-rule": return "No automated rule exists; a person must check it";
|
|
282
300
|
}
|
|
283
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* What a person recorded about this criterion, in the cell that says what the
|
|
304
|
+
* run reached. The same sentence the console report prints, from the same
|
|
305
|
+
* function, so two documents of one run cannot word it differently.
|
|
306
|
+
*/
|
|
307
|
+
function reviewCell(criterion) {
|
|
308
|
+
const review = criterion.review;
|
|
309
|
+
if (review === void 0) return "";
|
|
310
|
+
return `<br><span class="reviewed">${escapeText(reviewSentence(review))}</span>`;
|
|
311
|
+
}
|
|
284
312
|
function footer() {
|
|
285
313
|
return `<hr>
|
|
286
314
|
<footer>
|
|
@@ -303,12 +331,6 @@ function standards(finding) {
|
|
|
303
331
|
function standardsText(finding) {
|
|
304
332
|
return escapeText(standardsReference(finding.successCriteria, finding.enClauses));
|
|
305
333
|
}
|
|
306
|
-
function totalViolations(audits) {
|
|
307
|
-
return audits.reduce((total, audit) => total + audit.violations.length, 0);
|
|
308
|
-
}
|
|
309
|
-
function byImpactThenRule(a, b) {
|
|
310
|
-
return impactRank(a.impact) - impactRank(b.impact) || a.ruleId.localeCompare(b.ruleId);
|
|
311
|
-
}
|
|
312
334
|
const STYLES = `:root { color-scheme: light dark; }
|
|
313
335
|
body { margin: 0; background: #ffffff; color: #1a1a1a;
|
|
314
336
|
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; line-height: 1.6; }
|
|
@@ -362,6 +384,8 @@ table.coverage tr.evaluated td:last-child { color: #216e39; }
|
|
|
362
384
|
table.coverage em { font-style: normal; opacity: 0.75; }
|
|
363
385
|
ul.unreachable li { margin-bottom: 0.2rem; overflow-wrap: anywhere; }
|
|
364
386
|
code.selector { color: #4a4a4a; }
|
|
387
|
+
.reviewed { color: #4a4a4a; font-size: 0.95em; }
|
|
388
|
+
p.note.review { border-left: 3px solid #c9c9c9; padding-left: 0.75rem; }
|
|
365
389
|
p.clean { color: #216e39; }
|
|
366
390
|
p.note { font-size: 0.95rem; }
|
|
367
391
|
hr { border: 0; border-top: 1px solid #d4d4d4; margin: 3rem 0 1.5rem; }
|
|
@@ -401,7 +425,8 @@ ul.page-list { margin: 0.35rem 0 0; padding-left: 1.25rem; }
|
|
|
401
425
|
pre { background: #1e1e1e; }
|
|
402
426
|
li.finding { border-color: #3a3a3a; }
|
|
403
427
|
p.standards, p.coverage, .reason, li.more, code.selector, footer,
|
|
404
|
-
p.accepted-heading, ul.accepted { color: #b6b6b6; }
|
|
428
|
+
p.accepted-heading, ul.accepted, .reviewed { color: #b6b6b6; }
|
|
429
|
+
p.note.review { border-left-color: #555; }
|
|
405
430
|
p.clean { color: #7ee2a8; }
|
|
406
431
|
.verdict.pass { background: #10240f; border-color: #7ee2a8; }
|
|
407
432
|
.verdict.fail { background: #2b1111; border-color: #ff9d9d; }
|
|
@@ -467,8 +492,7 @@ function scoreboard(audits) {
|
|
|
467
492
|
function issues(audits, options) {
|
|
468
493
|
const found = groupIssues(audits);
|
|
469
494
|
if (found.length === 0) return "";
|
|
470
|
-
const elements
|
|
471
|
-
const occurrences = found.reduce((total, issue) => total + issue.occurrences, 0);
|
|
495
|
+
const { elements, occurrences } = issueTotals(found);
|
|
472
496
|
const intro = occurrences === elements ? `${count(elements, "distinct element")} to fix.` : `${count(occurrences, "violation")} across the site, from ${count(elements, "distinct element")}.`;
|
|
473
497
|
return `<h2 id="issues">What to fix</h2>
|
|
474
498
|
<p class="intro">${escapeText(intro)}</p>
|
|
@@ -36,6 +36,16 @@ function impactRank(impact) {
|
|
|
36
36
|
function impactLabel(impact) {
|
|
37
37
|
return impact !== null && isImpactLevel(impact) ? impact : "unclassified";
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Worst first, then by rule id so two runs of the same build agree.
|
|
41
|
+
*
|
|
42
|
+
* The order every format lists a page's findings in. Issues are sorted by
|
|
43
|
+
* `bySeverityThenReach` instead, which adds the reach a grouped issue has and a
|
|
44
|
+
* single page's findings do not.
|
|
45
|
+
*/
|
|
46
|
+
function byImpactThenRule(a, b) {
|
|
47
|
+
return impactRank(a.impact) - impactRank(b.impact) || a.ruleId.localeCompare(b.ruleId);
|
|
48
|
+
}
|
|
39
49
|
/** Violations at or above `threshold`, counted per rule per page. */
|
|
40
50
|
function countAtOrAbove(audits, threshold) {
|
|
41
51
|
let total = 0;
|
|
@@ -43,4 +53,4 @@ function countAtOrAbove(audits, threshold) {
|
|
|
43
53
|
return total;
|
|
44
54
|
}
|
|
45
55
|
//#endregion
|
|
46
|
-
export {
|
|
56
|
+
export { impactLabel as a, meetsThreshold as c, countAtOrAbove as i, IMPACT_LEVELS as n, impactRank as o, byImpactThenRule as r, isImpactLevel as s, DEFAULT_FAIL_ON as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -136,12 +136,21 @@ declare const auditSchema: Schema<ObjectOf<{
|
|
|
136
136
|
manual: OptionalSchema<boolean>;
|
|
137
137
|
/** List every WCAG 2.2 A/AA criterion and what the run reached on it. */
|
|
138
138
|
coverage: OptionalSchema<boolean>;
|
|
139
|
+
/** Path to a review record: what a person checked, for the criteria no engine reaches. */
|
|
140
|
+
review: OptionalSchema<string>;
|
|
141
|
+
/** Days after which a recorded review stops counting. */
|
|
142
|
+
reviewMaxAge: OptionalSchema<number>;
|
|
139
143
|
/**
|
|
140
144
|
* False is `--no-build`: never run the project's build or start its server to
|
|
141
145
|
* find something to audit. Written in the positive because that is the state
|
|
142
146
|
* being described, and because a config file has no flags to negate.
|
|
143
147
|
*/
|
|
144
148
|
build: OptionalSchema<boolean>;
|
|
149
|
+
/**
|
|
150
|
+
* False is `--no-cache`: audit every page, reusing nothing an earlier run
|
|
151
|
+
* measured. Written in the positive for the same reason `build` is.
|
|
152
|
+
*/
|
|
153
|
+
cache: OptionalSchema<boolean>;
|
|
145
154
|
}>>;
|
|
146
155
|
declare const configSchema: Schema<ObjectOf<{
|
|
147
156
|
site: Schema<ObjectOf<{
|
|
@@ -232,12 +241,21 @@ declare const configSchema: Schema<ObjectOf<{
|
|
|
232
241
|
manual: OptionalSchema<boolean>;
|
|
233
242
|
/** List every WCAG 2.2 A/AA criterion and what the run reached on it. */
|
|
234
243
|
coverage: OptionalSchema<boolean>;
|
|
244
|
+
/** Path to a review record: what a person checked, for the criteria no engine reaches. */
|
|
245
|
+
review: OptionalSchema<string>;
|
|
246
|
+
/** Days after which a recorded review stops counting. */
|
|
247
|
+
reviewMaxAge: OptionalSchema<number>;
|
|
235
248
|
/**
|
|
236
249
|
* False is `--no-build`: never run the project's build or start its server to
|
|
237
250
|
* find something to audit. Written in the positive because that is the state
|
|
238
251
|
* being described, and because a config file has no flags to negate.
|
|
239
252
|
*/
|
|
240
253
|
build: OptionalSchema<boolean>;
|
|
254
|
+
/**
|
|
255
|
+
* False is `--no-cache`: audit every page, reusing nothing an earlier run
|
|
256
|
+
* measured. Written in the positive for the same reason `build` is.
|
|
257
|
+
*/
|
|
258
|
+
cache: OptionalSchema<boolean>;
|
|
241
259
|
}>>;
|
|
242
260
|
}>>;
|
|
243
261
|
/**
|
|
@@ -349,6 +367,24 @@ declare class StatementError extends Error {
|
|
|
349
367
|
readonly name = "StatementError";
|
|
350
368
|
}
|
|
351
369
|
//#endregion
|
|
370
|
+
//#region src/audit/review.d.ts
|
|
371
|
+
declare const reviewSchema: Schema<ObjectOf<{
|
|
372
|
+
schemaVersion: Schema<number>;
|
|
373
|
+
/** Who carried out the review, for the record as a whole. */
|
|
374
|
+
reviewedBy: OptionalSchema<string>;
|
|
375
|
+
/** Keyed by success criterion number, e.g. '1.2.1'. */
|
|
376
|
+
criteria: DefaultedSchema<Record<string, ObjectOf<{
|
|
377
|
+
result: DefaultedSchema<"met" | "not-applicable" | "not-met" | "unreviewed">;
|
|
378
|
+
/** ISO date the check was done. Without it the entry cannot be aged. */
|
|
379
|
+
reviewedOn: OptionalSchema<string>;
|
|
380
|
+
/** Who did it, when that is not the person named on the record. */
|
|
381
|
+
reviewedBy: OptionalSchema<string>;
|
|
382
|
+
/** What was checked and what was found. Free text, for whoever reads it. */
|
|
383
|
+
note: OptionalSchema<string>;
|
|
384
|
+
}>>>;
|
|
385
|
+
}>>;
|
|
386
|
+
type ReviewRecord = Infer<typeof reviewSchema>;
|
|
387
|
+
//#endregion
|
|
352
388
|
//#region src/statement/findings.d.ts
|
|
353
389
|
/** One rule that failed, folded across every page it failed on. */
|
|
354
390
|
interface AuditFinding {
|
|
@@ -394,6 +430,47 @@ declare function summariseAuditReport(value: unknown, source?: string): AuditSum
|
|
|
394
430
|
/** Read and summarise a report written by `eaa-kit audit --format json`. */
|
|
395
431
|
declare function readAuditReport(file: string, cwd?: string): Promise<AuditSummary>;
|
|
396
432
|
//#endregion
|
|
433
|
+
//#region src/statement/evidence.d.ts
|
|
434
|
+
/**
|
|
435
|
+
* Whether the statement's claim survives contact with the evidence beside it.
|
|
436
|
+
*
|
|
437
|
+
* The README has said since 0.1 that "a statement claiming full conformance for
|
|
438
|
+
* a site that is not conformant is worse than no statement at all", and until
|
|
439
|
+
* now the tool did nothing about it: `compliance.status` came out of the config
|
|
440
|
+
* file, the audit report came in through `--audit`, and nothing ever put the
|
|
441
|
+
* two in the same room. A run could print a document claiming full conformance
|
|
442
|
+
* directly above the list of barriers that disproves it.
|
|
443
|
+
*
|
|
444
|
+
* This is the check that was missing. It is deliberately narrow: it does not
|
|
445
|
+
* decide whether a site is accessible, and it cannot — most of WCAG is not
|
|
446
|
+
* automatable and the whole tool is built around saying so. It refuses only
|
|
447
|
+
* claims the evidence in hand already contradicts, and warns where a document
|
|
448
|
+
* is citing evidence that no longer describes the site.
|
|
449
|
+
*
|
|
450
|
+
* Nothing here needs an audit report. Given none, only the dates are checked,
|
|
451
|
+
* and a project that has never run one gets exactly the statement it always
|
|
452
|
+
* got — the tool cannot audit what it was not shown.
|
|
453
|
+
*/
|
|
454
|
+
/** How much a problem matters. `refuses` stops the statement being written. */
|
|
455
|
+
type EvidenceSeverity = 'refuses' | 'warns';
|
|
456
|
+
interface EvidenceProblem {
|
|
457
|
+
severity: EvidenceSeverity;
|
|
458
|
+
/** One sentence stating the contradiction, and one naming the fix. */
|
|
459
|
+
message: string;
|
|
460
|
+
}
|
|
461
|
+
interface EvidenceInput {
|
|
462
|
+
config: EaaConfig;
|
|
463
|
+
/** From `--audit`. Without it only the dates can be checked. */
|
|
464
|
+
audit?: AuditSummary;
|
|
465
|
+
/** From `--review`. Read for this check alone; it does not reach the text. */
|
|
466
|
+
review?: ReviewRecord;
|
|
467
|
+
/** Injectable so the ageing checks can be tested without waiting. */
|
|
468
|
+
today?: Date;
|
|
469
|
+
}
|
|
470
|
+
declare function checkStatementEvidence(input: EvidenceInput): EvidenceProblem[];
|
|
471
|
+
/** Whether any problem is bad enough to stop the document being written. */
|
|
472
|
+
declare function refuses(problems: readonly EvidenceProblem[]): boolean;
|
|
473
|
+
//#endregion
|
|
397
474
|
//#region src/statement/html.d.ts
|
|
398
475
|
/**
|
|
399
476
|
* Markdown-to-HTML for exactly the subset the statement templates emit, and
|
|
@@ -447,6 +524,15 @@ interface RenderStatementOptions {
|
|
|
447
524
|
* config lists. Left out, the statement says only what the config says.
|
|
448
525
|
*/
|
|
449
526
|
audit?: AuditSummary;
|
|
527
|
+
/**
|
|
528
|
+
* What a person checked, from `eaa-kit checklist`.
|
|
529
|
+
*
|
|
530
|
+
* Reaches the document as one fact — how many of WCAG's success criteria were
|
|
531
|
+
* checked by hand, and when — and never as a verdict. What a review concluded
|
|
532
|
+
* about a criterion is a claim by a person, and the place for a claim in a
|
|
533
|
+
* statement is the barrier list, which a person writes.
|
|
534
|
+
*/
|
|
535
|
+
review?: ReviewRecord;
|
|
450
536
|
}
|
|
451
537
|
interface RenderedStatement {
|
|
452
538
|
markdown: string;
|
|
@@ -466,4 +552,4 @@ interface RenderedStatement {
|
|
|
466
552
|
*/
|
|
467
553
|
declare function renderStatement(config: EaaConfig, options?: RenderStatementOptions): Promise<RenderedStatement>;
|
|
468
554
|
//#endregion
|
|
469
|
-
export { ASSESSMENT_METHODS, type AssessmentMethod, type AuditFinding, type AuditSummary, COMPLIANCE_STATUSES, CONFIG_FILENAMES, COUNTRIES, type ComplianceStatus, ConfigError, type Country, type EaaConfig, type EaaConfigInput, type HtmlDocumentOptions, ISSUE_REASONS, type IssueReason, type KnownIssue, type LoadConfigOptions, type LoadedConfig, type RenderStatementOptions, type RenderedStatement, STATEMENT_LOCALES, StatementError, type StatementLocale, configSchema, defineConfig, findConfigFile, loadConfig, parseConfig, readAuditReport, renderStatement, summariseAuditReport, toHtmlBody, toHtmlDocument };
|
|
555
|
+
export { ASSESSMENT_METHODS, type AssessmentMethod, type AuditFinding, type AuditSummary, COMPLIANCE_STATUSES, CONFIG_FILENAMES, COUNTRIES, type ComplianceStatus, ConfigError, type Country, type EaaConfig, type EaaConfigInput, type EvidenceInput, type EvidenceProblem, type EvidenceSeverity, type HtmlDocumentOptions, ISSUE_REASONS, type IssueReason, type KnownIssue, type LoadConfigOptions, type LoadedConfig, type RenderStatementOptions, type RenderedStatement, STATEMENT_LOCALES, StatementError, type StatementLocale, checkStatementEvidence, configSchema, defineConfig, findConfigFile, loadConfig, parseConfig, readAuditReport, refuses, renderStatement, summariseAuditReport, toHtmlBody, toHtmlDocument };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as ASSESSMENT_METHODS, c as ConfigError, d as configSchema, f as defineConfig, i as loadConfig, l as ISSUE_REASONS, n as findConfigFile, o as COMPLIANCE_STATUSES, p as parseConfig, s as COUNTRIES, t as CONFIG_FILENAMES, u as STATEMENT_LOCALES } from "./load-
|
|
2
|
-
import { a as summariseAuditReport, i as readAuditReport, n as toHtmlBody, o as
|
|
3
|
-
export { ASSESSMENT_METHODS, COMPLIANCE_STATUSES, CONFIG_FILENAMES, COUNTRIES, ConfigError, ISSUE_REASONS, STATEMENT_LOCALES, StatementError, configSchema, defineConfig, findConfigFile, loadConfig, parseConfig, readAuditReport, renderStatement, summariseAuditReport, toHtmlBody, toHtmlDocument };
|
|
1
|
+
import { a as ASSESSMENT_METHODS, c as ConfigError, d as configSchema, f as defineConfig, i as loadConfig, l as ISSUE_REASONS, n as findConfigFile, o as COMPLIANCE_STATUSES, p as parseConfig, s as COUNTRIES, t as CONFIG_FILENAMES, u as STATEMENT_LOCALES } from "./load-BaWMa-G_.js";
|
|
2
|
+
import { a as summariseAuditReport, c as StatementError, i as readAuditReport, n as toHtmlBody, o as checkStatementEvidence, r as toHtmlDocument, s as refuses, t as renderStatement } from "./render-C1sUZZI_.js";
|
|
3
|
+
export { ASSESSMENT_METHODS, COMPLIANCE_STATUSES, CONFIG_FILENAMES, COUNTRIES, ConfigError, ISSUE_REASONS, STATEMENT_LOCALES, StatementError, checkStatementEvidence, configSchema, defineConfig, findConfigFile, loadConfig, parseConfig, readAuditReport, refuses, renderStatement, summariseAuditReport, toHtmlBody, toHtmlDocument };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as DEFAULT_FAIL_ON } from "./impact-
|
|
2
|
-
import { s as COUNTRIES, t as CONFIG_FILENAMES } from "./load-
|
|
1
|
+
import { t as DEFAULT_FAIL_ON } from "./impact-luGCnN8F.js";
|
|
2
|
+
import { s as COUNTRIES, t as CONFIG_FILENAMES } from "./load-BaWMa-G_.js";
|
|
3
3
|
import { t as exists } from "./fs-BmPtmFke.js";
|
|
4
|
-
import { l as warn, o as fail, s as note } from "./command-
|
|
4
|
+
import { l as warn, o as fail, s as note } from "./command-3r4fht5f.js";
|
|
5
5
|
import { readFile, writeFile } from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import pc from "picocolors";
|