ultra11y 5.6.1 → 5.8.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.opencode/plugins/ultra11y.js +1 -1
- package/dist/cypress-plugin.d.ts +1 -1
- package/dist/cypress.d.ts +2 -2
- package/dist/{payload-BshGXnOO.d.ts → payload-DzQb84Kw.d.ts} +21 -2
- package/dist/playwright.d.ts +16 -2
- package/dist/playwright.mjs +105 -49
- package/package.json +1 -1
- package/scripts/ultra11y.mjs +1450 -1339
- package/skills/review-a11y/SKILL.md +1 -1
- package/skills/review-a11y/scripts/ultra11y.mjs +1450 -1339
- package/skills/ultra11y/SKILL.md +1 -1
- package/skills/ultra11y/references/e2e.md +25 -0
- package/skills/ultra11y/scripts/ultra11y.mjs +1450 -1339
- package/src/adjudicate-subjects.ts +87 -0
- package/src/adjudicate.ts +65 -12
- package/src/audit.ts +89 -0
- package/src/config.ts +44 -0
- package/src/integrations/payload.ts +22 -2
- package/src/integrations/playwright.ts +48 -4
- package/src/probes.ts +150 -50
- package/src/standards/derive.ts +24 -0
- package/src/types.ts +6 -1
- package/dist/payload-B6NDkLYJ.d.ts +0 -51
- package/dist/payload-D0VQYlGS.d.ts +0 -44
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "ultra11y",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Automatic accessibility review of the code under change before every commit, push and pull request, plus full WCAG 2.2 AA / RGAA audits and conformance deliverables on demand.",
|
|
13
|
-
"version": "5.
|
|
13
|
+
"version": "5.8.0",
|
|
14
14
|
"author": { "name": "maxgfr" },
|
|
15
15
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
16
16
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.8.0",
|
|
5
5
|
"author": { "name": "maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
3
|
"description": "Accessibility for WCAG 2.2 AA and country standards (RGAA…): an automatic review of the code under change before every commit, push and pull request, plus full audits and conformance deliverables on demand.",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.8.0",
|
|
5
5
|
"author": { "name": "maxgfr", "url": "https://github.com/maxgfr" },
|
|
6
6
|
"homepage": "https://github.com/maxgfr/ultra11y",
|
|
7
7
|
"repository": "https://github.com/maxgfr/ultra11y",
|
|
@@ -27,7 +27,7 @@ import { fileURLToPath } from "node:url";
|
|
|
27
27
|
// Ownership marker: `ultra11y install --opencode` refuses to overwrite a plugin file that
|
|
28
28
|
// does not carry it, so a user's own ultra11y.js is never clobbered.
|
|
29
29
|
const ULTRA11Y_OPENCODE_PLUGIN = "ULTRA11Y_OPENCODE_PLUGIN";
|
|
30
|
-
const ULTRA11Y_PLUGIN_VERSION = "5.
|
|
30
|
+
const ULTRA11Y_PLUGIN_VERSION = "5.8.0";
|
|
31
31
|
|
|
32
32
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
33
33
|
|
package/dist/cypress-plugin.d.ts
CHANGED
package/dist/cypress.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CheckOptions } from './payload-
|
|
2
|
-
export { s as slugify } from './payload-
|
|
1
|
+
import { C as CheckOptions } from './payload-DzQb84Kw.js';
|
|
2
|
+
export { s as slugify } from './payload-DzQb84Kw.js';
|
|
3
3
|
|
|
4
4
|
interface CypressCheckOptions extends CheckOptions {
|
|
5
5
|
/** Capture a viewport screenshot so the pixel tier can run. On by default — the plugin
|
|
@@ -61,8 +61,27 @@ interface CheckOptions {
|
|
|
61
61
|
* and a suite should opt into that. Everything is restored before it returns, and the
|
|
62
62
|
* snapshot has already been collected, so what they measure cannot alter what was recorded.
|
|
63
63
|
* Pass an array to run only some of them (by criterion: "1.4.4", "1.4.10", "1.4.12",
|
|
64
|
-
* "1.4.13", "2.4.7")
|
|
65
|
-
|
|
64
|
+
* "1.4.13", "2.4.7"), or an object to also set the bounds — how wide the reflow probe
|
|
65
|
+
* narrows to, how many focusables are worth tabbing through, how many hits are worth
|
|
66
|
+
* recording. 320px is normative and stays the default; the other two are judgements about
|
|
67
|
+
* YOUR pages, and a screen with 400 focusables should be able to say so rather than being
|
|
68
|
+
* quietly cut off at 120. */
|
|
69
|
+
probes?: boolean | string[] | ProbeTuning;
|
|
70
|
+
}
|
|
71
|
+
/** The bounds a repository may put on the live probes. Stated once, here, because the same
|
|
72
|
+
* list is read from `.ultra11yrc.json`, accepted at the call site and handed to the engine —
|
|
73
|
+
* three copies of a field list is three chances for one of them to fall behind.
|
|
74
|
+
*
|
|
75
|
+
* `budgetMs` and `actionTimeoutMs` are not tuning in the same sense as the others: they are
|
|
76
|
+
* what stops a probe from spending SOMEBODY ELSE'S test timeout. See ProbeLimits. */
|
|
77
|
+
interface ProbeTuning {
|
|
78
|
+
only?: string[];
|
|
79
|
+
reflowWidth?: number;
|
|
80
|
+
maxFocusables?: number;
|
|
81
|
+
maxHits?: number;
|
|
82
|
+
maxTriggers?: number;
|
|
83
|
+
actionTimeoutMs?: number;
|
|
84
|
+
budgetMs?: number;
|
|
66
85
|
}
|
|
67
86
|
|
|
68
87
|
export { type AuditLike as A, type CheckOptions as C, type FindingLike as F, type SnapshotPayload as S, slugify as s };
|
package/dist/playwright.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CheckOptions, A as AuditLike } from './payload-
|
|
1
|
+
import { C as CheckOptions, A as AuditLike } from './payload-DzQb84Kw.js';
|
|
2
2
|
|
|
3
3
|
interface PlaywrightPage {
|
|
4
4
|
evaluate(script: string): Promise<unknown>;
|
|
@@ -106,6 +106,20 @@ declare function samplePagesFor(opts?: Pick<SweepOptions, "cwd" | "only">): Samp
|
|
|
106
106
|
* `scan`, which needs a real address); a Playwright test wants the path, so its `baseURL`
|
|
107
107
|
* applies. Anything unparseable is already a path. */
|
|
108
108
|
declare function sweepTarget(url: string): string;
|
|
109
|
+
/** The defaults every swept page is checked with, and the one seam a test can reach without
|
|
110
|
+
* a Playwright runtime in the room.
|
|
111
|
+
*
|
|
112
|
+
* `probes: true` is the default HERE and opt-in on `checkA11y`, and the asymmetry is the
|
|
113
|
+
* point. `checkA11y` is called from a test that owns its page and may be asserting on focus
|
|
114
|
+
* or hover a line later; a sweep exists for nothing but recording the sample, and the probes
|
|
115
|
+
* are the only thing that can decide zoom, reflow, text spacing and content-on-hover on
|
|
116
|
+
* screens a cold scanner never reaches. Measured on egapro: 15 of 20 pages came from a sweep
|
|
117
|
+
* that never probed, and because conformity is an AND across every page in scope, those four
|
|
118
|
+
* criteria stayed « à évaluer » for the whole audit — including on the five pages that HAD
|
|
119
|
+
* been probed. Opt out with `check: { probes: false }`. */
|
|
120
|
+
declare function sweepCheckOptions(check?: SweepOptions["check"]): SweepOptions["check"] & {
|
|
121
|
+
failOn: false | undefined;
|
|
122
|
+
};
|
|
109
123
|
declare function sweepSample(opts?: SweepOptions): void;
|
|
110
124
|
|
|
111
|
-
export { AuditLike, CheckOptions, type PlaywrightCheckOptions, type SamplePageLike, type SweepOptions, checkA11y, samplePagesFor, sweepSample, sweepTarget, test };
|
|
125
|
+
export { AuditLike, CheckOptions, type PlaywrightCheckOptions, type SamplePageLike, type SweepOptions, checkA11y, samplePagesFor, sweepCheckOptions, sweepSample, sweepTarget, test };
|
package/dist/playwright.mjs
CHANGED
|
@@ -154,6 +154,22 @@ const __vis = (e) => {
|
|
|
154
154
|
};
|
|
155
155
|
const __html = (e) => (e.outerHTML || '').slice(0, 160);
|
|
156
156
|
`;
|
|
157
|
+
var PROBE_DEFAULTS = {
|
|
158
|
+
reflowWidth: 320,
|
|
159
|
+
maxFocusables: 120,
|
|
160
|
+
maxHits: 20,
|
|
161
|
+
maxTriggers: 60,
|
|
162
|
+
actionTimeoutMs: 1e3,
|
|
163
|
+
budgetMs: 2e4
|
|
164
|
+
};
|
|
165
|
+
function probeDeadline(budgetMs, now = Date.now) {
|
|
166
|
+
const end = now() + budgetMs;
|
|
167
|
+
return { out: () => now() >= end, left: () => Math.max(0, end - now()) };
|
|
168
|
+
}
|
|
169
|
+
function actionTimeout(limits, deadline) {
|
|
170
|
+
const left = deadline ? deadline.left() : limits.actionTimeoutMs;
|
|
171
|
+
return Math.max(1, Math.min(limits.actionTimeoutMs, left || limits.actionTimeoutMs));
|
|
172
|
+
}
|
|
157
173
|
var REFLOW_PROBE = `(() => {
|
|
158
174
|
const el = document.scrollingElement || document.documentElement;
|
|
159
175
|
return { horizontalScroll: el.scrollWidth > el.clientWidth + 2 };
|
|
@@ -193,7 +209,7 @@ var TEXT_SPACING_PROBE = `(() => { ${PRELUDE}
|
|
|
193
209
|
}
|
|
194
210
|
return hits;
|
|
195
211
|
})()`;
|
|
196
|
-
function focusSetupExpr(scope = "") {
|
|
212
|
+
function focusSetupExpr(scope = "", maxFocusables = PROBE_DEFAULTS.maxFocusables) {
|
|
197
213
|
const rootExpr = scope ? `document.querySelectorAll(${JSON.stringify(scope)})` : `[document.documentElement]`;
|
|
198
214
|
return `(() => { ${PRELUDE}
|
|
199
215
|
const sel = 'a[href],button:not([disabled]),input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"]),[role=button]:not([disabled])';
|
|
@@ -227,7 +243,7 @@ function focusSetupExpr(scope = "") {
|
|
|
227
243
|
proxy.setAttribute('data-u11y-fp', key);
|
|
228
244
|
window.__u11yF[key] = { rest: snap(proxy), sel: __sel(proxy), html: __html(proxy) };
|
|
229
245
|
n++;
|
|
230
|
-
if (n >=
|
|
246
|
+
if (n >= ${maxFocusables}) break;
|
|
231
247
|
}
|
|
232
248
|
return n;
|
|
233
249
|
})()`;
|
|
@@ -266,13 +282,14 @@ function hoverVisibleExpr(id, wantHidden = false) {
|
|
|
266
282
|
const j = JSON.stringify(id);
|
|
267
283
|
return `(() => { const t = document.getElementById(${j}); if (!t) return ${wantHidden ? "true" : "false"}; const s = getComputedStyle(t); const shown = s.display !== 'none' && s.visibility !== 'hidden' && t.getBoundingClientRect().height > 0; return ${wantHidden ? "!shown" : "shown"}; })()`;
|
|
268
284
|
}
|
|
269
|
-
async function probeFocusVisible(page, scope = "") {
|
|
270
|
-
const count = await page.evaluate(focusSetupExpr(scope));
|
|
285
|
+
async function probeFocusVisible(page, scope = "", limits = PROBE_DEFAULTS, deadline) {
|
|
286
|
+
const count = await page.evaluate(focusSetupExpr(scope, limits.maxFocusables));
|
|
271
287
|
if (!count) return [];
|
|
272
288
|
const hits = [];
|
|
273
289
|
const seen = /* @__PURE__ */ new Set();
|
|
274
|
-
const limit = Math.min(count + 2,
|
|
290
|
+
const limit = Math.min(count + 2, limits.maxFocusables + 10);
|
|
275
291
|
for (let i = 0; i < limit; i++) {
|
|
292
|
+
if (deadline?.out()) break;
|
|
276
293
|
await page.keyboard.press("Tab");
|
|
277
294
|
const r = await page.evaluate(FOCUS_CHECK_PROBE);
|
|
278
295
|
if (!r) continue;
|
|
@@ -289,12 +306,13 @@ async function probeFocusVisible(page, scope = "") {
|
|
|
289
306
|
}
|
|
290
307
|
return hits;
|
|
291
308
|
}
|
|
292
|
-
async function probeHover(page) {
|
|
309
|
+
async function probeHover(page, limits = PROBE_DEFAULTS, deadline) {
|
|
293
310
|
const triggers = await page.evaluate(HOVER_SETUP_PROBE);
|
|
294
311
|
const hits = [];
|
|
295
|
-
for (const tr of triggers) {
|
|
312
|
+
for (const tr of triggers.slice(0, Math.max(1, limits.maxTriggers))) {
|
|
313
|
+
if (deadline?.out()) break;
|
|
296
314
|
try {
|
|
297
|
-
await page.hover(`[data-u11y-h="${tr.key}"]
|
|
315
|
+
await page.hover(`[data-u11y-h="${tr.key}"]`, { timeout: actionTimeout(limits, deadline) });
|
|
298
316
|
} catch {
|
|
299
317
|
continue;
|
|
300
318
|
}
|
|
@@ -313,13 +331,15 @@ async function probeHover(page) {
|
|
|
313
331
|
detail: `Le contenu r\xE9v\xE9l\xE9 au survol (aria-describedby #${tr.target}) ne se masque pas avec \xC9chap \u2014 Contenu au survol ou au focus (1.4.13).`
|
|
314
332
|
});
|
|
315
333
|
}
|
|
316
|
-
if (hits.length >= 8) break;
|
|
334
|
+
if (hits.length >= Math.min(limits.maxHits, 8)) break;
|
|
317
335
|
}
|
|
318
336
|
return hits;
|
|
319
337
|
}
|
|
320
338
|
async function runLiveProbes(page, opts = {}) {
|
|
339
|
+
const limits = { ...PROBE_DEFAULTS, ...opts.limits };
|
|
321
340
|
const only = opts.only?.length ? new Set(opts.only) : null;
|
|
322
341
|
const want = (id) => only === null || only.has(id);
|
|
342
|
+
const deadline = probeDeadline(limits.budgetMs);
|
|
323
343
|
const canResize = typeof page.setViewportSize === "function" && typeof page.viewportSize === "function";
|
|
324
344
|
const canType = !!page.keyboard && typeof page.keyboard.press === "function";
|
|
325
345
|
const canHover = typeof page.hover === "function" && typeof page.waitForTimeout === "function";
|
|
@@ -330,45 +350,53 @@ async function runLiveProbes(page, opts = {}) {
|
|
|
330
350
|
const skip = (sc, why) => {
|
|
331
351
|
out.skipped?.push({ sc, why });
|
|
332
352
|
};
|
|
353
|
+
const bounded = async (sc, run) => {
|
|
354
|
+
if (deadline.out()) {
|
|
355
|
+
skip(sc, `the probe budget of ${limits.budgetMs}ms was already spent when this criterion came up`);
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
let timer;
|
|
359
|
+
const CUT = /* @__PURE__ */ Symbol("cut");
|
|
360
|
+
const started = Date.now();
|
|
361
|
+
try {
|
|
362
|
+
const raced = await Promise.race([
|
|
363
|
+
run().catch((e) => {
|
|
364
|
+
throw e;
|
|
365
|
+
}),
|
|
366
|
+
new Promise((resolve) => {
|
|
367
|
+
timer = setTimeout(() => resolve(CUT), Math.max(1, deadline.left()));
|
|
368
|
+
})
|
|
369
|
+
]);
|
|
370
|
+
if (raced === CUT) {
|
|
371
|
+
skip(sc, `the probe budget of ${limits.budgetMs}ms ran out after ${Date.now() - started}ms \u2014 this measurement did not complete`);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
return raced;
|
|
375
|
+
} catch (e) {
|
|
376
|
+
skip(sc, String(e?.message ?? e).slice(0, 160));
|
|
377
|
+
return null;
|
|
378
|
+
} finally {
|
|
379
|
+
if (timer) clearTimeout(timer);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
333
382
|
if (!canResize) skip("1.4.10", "the page object cannot resize its viewport");
|
|
334
383
|
if (!canType) skip("2.4.7", "the page object exposes no keyboard");
|
|
335
384
|
if (!canHover) skip("1.4.13", "the page object cannot hover");
|
|
336
385
|
if (!canStyle) skip("1.4.12", "the page object cannot inject a stylesheet");
|
|
337
|
-
if (want("
|
|
338
|
-
const r = await
|
|
339
|
-
skip("2.4.7", String(e?.message ?? e).slice(0, 160));
|
|
340
|
-
return null;
|
|
341
|
-
});
|
|
342
|
-
if (r) {
|
|
343
|
-
out.focusVisible = r;
|
|
344
|
-
out.probed.push("2.4.7");
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (want("1.4.13") && canHover && canType) {
|
|
348
|
-
const r = await probeHover(page).catch((e) => {
|
|
349
|
-
skip("1.4.13", String(e?.message ?? e).slice(0, 160));
|
|
350
|
-
return null;
|
|
351
|
-
});
|
|
386
|
+
if (want("1.4.4")) {
|
|
387
|
+
const r = await bounded("1.4.4", async () => await page.evaluate(REFLOW_ZOOM_PROBE));
|
|
352
388
|
if (r) {
|
|
353
|
-
out.
|
|
354
|
-
out.probed.push("1.4.
|
|
389
|
+
out.reflowZoom = r ?? [];
|
|
390
|
+
out.probed.push("1.4.4");
|
|
355
391
|
}
|
|
356
392
|
}
|
|
357
|
-
if (want("1.4.4")) {
|
|
358
|
-
out.reflowZoom = await page.evaluate(REFLOW_ZOOM_PROBE).catch(() => []) ?? [];
|
|
359
|
-
out.probed.push("1.4.4");
|
|
360
|
-
}
|
|
361
393
|
if (want("1.4.10") && canResize) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
skip("1.4.10", String(e?.message ?? e).slice(0, 160));
|
|
394
|
+
const narrowed = await bounded("1.4.10", async () => {
|
|
395
|
+
await page.setViewportSize({ width: limits.reflowWidth, height: restore.height });
|
|
396
|
+
return true;
|
|
366
397
|
});
|
|
367
398
|
if (narrowed) {
|
|
368
|
-
const r = await page.evaluate(REFLOW_PROBE)
|
|
369
|
-
skip("1.4.10", String(e?.message ?? e).slice(0, 160));
|
|
370
|
-
return null;
|
|
371
|
-
});
|
|
399
|
+
const r = await bounded("1.4.10", async () => await page.evaluate(REFLOW_PROBE));
|
|
372
400
|
await page.setViewportSize(restore).catch(() => {
|
|
373
401
|
});
|
|
374
402
|
if (r) {
|
|
@@ -378,15 +406,9 @@ async function runLiveProbes(page, opts = {}) {
|
|
|
378
406
|
}
|
|
379
407
|
}
|
|
380
408
|
if (want("1.4.12") && canStyle) {
|
|
381
|
-
const handle = await page.addStyleTag({ content: TEXT_SPACING_CSS })
|
|
382
|
-
skip("1.4.12", String(e?.message ?? e).slice(0, 160));
|
|
383
|
-
return null;
|
|
384
|
-
});
|
|
409
|
+
const handle = await bounded("1.4.12", () => page.addStyleTag({ content: TEXT_SPACING_CSS }));
|
|
385
410
|
if (handle) {
|
|
386
|
-
const r = await page.evaluate(TEXT_SPACING_PROBE)
|
|
387
|
-
skip("1.4.12", String(e?.message ?? e).slice(0, 160));
|
|
388
|
-
return null;
|
|
389
|
-
});
|
|
411
|
+
const r = await bounded("1.4.12", async () => await page.evaluate(TEXT_SPACING_PROBE));
|
|
390
412
|
await page.evaluate(REMOVE_TEXT_SPACING_STEP).catch(() => {
|
|
391
413
|
});
|
|
392
414
|
if (r) {
|
|
@@ -395,6 +417,20 @@ async function runLiveProbes(page, opts = {}) {
|
|
|
395
417
|
}
|
|
396
418
|
}
|
|
397
419
|
}
|
|
420
|
+
if (want("2.4.7") && canType) {
|
|
421
|
+
const r = await bounded("2.4.7", () => probeFocusVisible(page, "", limits, deadline));
|
|
422
|
+
if (r) {
|
|
423
|
+
out.focusVisible = r;
|
|
424
|
+
out.probed.push("2.4.7");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (want("1.4.13") && canHover && canType) {
|
|
428
|
+
const r = await bounded("1.4.13", () => probeHover(page, limits, deadline));
|
|
429
|
+
if (r) {
|
|
430
|
+
out.hover = r;
|
|
431
|
+
out.probed.push("1.4.13");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
398
434
|
return out;
|
|
399
435
|
}
|
|
400
436
|
var REMOVE_TEXT_SPACING_STEP = `(() => {
|
|
@@ -525,6 +561,23 @@ function writePagesReport(opts = {}, engine = enginePath()) {
|
|
|
525
561
|
}
|
|
526
562
|
|
|
527
563
|
// src/integrations/playwright.ts
|
|
564
|
+
function probeOptions(p) {
|
|
565
|
+
if (p === true || p === void 0) return fromConfig();
|
|
566
|
+
if (p === false) return {};
|
|
567
|
+
if (Array.isArray(p)) return { only: p, ...fromConfig() };
|
|
568
|
+
const { only, ...limits } = p;
|
|
569
|
+
return { ...only ? { only } : {}, limits };
|
|
570
|
+
}
|
|
571
|
+
function fromConfig() {
|
|
572
|
+
try {
|
|
573
|
+
const cfg = JSON.parse(readFileSync(join(process.cwd(), ".ultra11yrc.json"), "utf8"));
|
|
574
|
+
if (!cfg.probes) return {};
|
|
575
|
+
const { only, ...limits } = cfg.probes;
|
|
576
|
+
return { ...only ? { only } : {}, limits };
|
|
577
|
+
} catch {
|
|
578
|
+
return {};
|
|
579
|
+
}
|
|
580
|
+
}
|
|
528
581
|
async function checkA11y(page, opts = {}) {
|
|
529
582
|
const collected = await page.evaluate(COLLECT_SNAPSHOT);
|
|
530
583
|
let shot;
|
|
@@ -543,7 +596,7 @@ async function checkA11y(page, opts = {}) {
|
|
|
543
596
|
let probes;
|
|
544
597
|
if (opts.probes) {
|
|
545
598
|
try {
|
|
546
|
-
probes = await runLiveProbes(page,
|
|
599
|
+
probes = await runLiveProbes(page, probeOptions(opts.probes));
|
|
547
600
|
} catch (e) {
|
|
548
601
|
console.warn(
|
|
549
602
|
`ultra11y: the live probes failed on this page \u2014 ${e instanceof Error ? e.message : String(e)}. The snapshot was still recorded; the criteria they decide stay to assess.`
|
|
@@ -591,6 +644,9 @@ function sweepTarget(url) {
|
|
|
591
644
|
return url;
|
|
592
645
|
}
|
|
593
646
|
}
|
|
647
|
+
function sweepCheckOptions(check) {
|
|
648
|
+
return { failOn: false, probes: true, ...check };
|
|
649
|
+
}
|
|
594
650
|
function sweepSample(opts = {}) {
|
|
595
651
|
const pages = samplePagesFor(opts);
|
|
596
652
|
const t = test;
|
|
@@ -609,8 +665,7 @@ function sweepSample(opts = {}) {
|
|
|
609
665
|
`${target} answered HTTP ${status} \u2014 an error page at the requested address; nothing to record as "${p.name}"`
|
|
610
666
|
);
|
|
611
667
|
await checkA11y(page, {
|
|
612
|
-
|
|
613
|
-
...opts.check,
|
|
668
|
+
...sweepCheckOptions(opts.check),
|
|
614
669
|
as: p.id,
|
|
615
670
|
name: p.name,
|
|
616
671
|
...p.auth !== void 0 ? { auth: p.auth } : {},
|
|
@@ -624,6 +679,7 @@ function sweepSample(opts = {}) {
|
|
|
624
679
|
export {
|
|
625
680
|
checkA11y,
|
|
626
681
|
samplePagesFor,
|
|
682
|
+
sweepCheckOptions,
|
|
627
683
|
sweepSample,
|
|
628
684
|
sweepTarget,
|
|
629
685
|
test
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra11y",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "Audit HTML/CSS/JSX against WCAG 2.2 AA and produce a dated compliance report, or author/review accessible markup (native-HTML-first). A skills.sh agent skill: an install-free static engine (bundled, no npm install) plus the agent's judgment, with check/verify gates against hallucinated non-conformities, and pluggable in-repo standards packs (RGAA France first; Section 508 & EN 301 549 next).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.33.0",
|