testaro 78.0.8 → 78.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +94 -0
- package/.github/workflows/ci.yml +58 -0
- package/.github/workflows/publish.yml +49 -0
- package/.github/workflows/typescript.yml +35 -0
- package/AGENTS.md +2 -2
- package/CLAUDE.md +24 -18
- package/CONTAINERS.md +9 -6
- package/CONTRIBUTING.md +29 -3
- package/Dockerfile +1 -1
- package/README.md +73 -19
- package/UPGRADES.md +4 -0
- package/actSpecs-doc.md +13 -1
- package/actSpecs.js +29 -5
- package/call.js +6 -5
- package/docker-compose.yml +8 -2
- package/docs/checkpoint-scanning.md +199 -0
- package/docs/standard-result-outcome.md +165 -0
- package/env.example +44 -10
- package/eslint.config.mjs +101 -0
- package/netWatch.js +372 -222
- package/package.json +46 -31
- package/pour/README.md +47 -0
- package/pour/pour.min.js +8 -0
- package/procs/actDo.js +808 -0
- package/procs/catalog.d.ts +17 -0
- package/procs/catalog.js +312 -221
- package/procs/catalog.ts +398 -0
- package/procs/checkpoint.js +110 -0
- package/procs/config.d.ts +35 -0
- package/procs/config.js +62 -0
- package/procs/dateTime.js +2 -1
- package/procs/doActs.js +442 -849
- package/procs/doTestAct.js +27 -121
- package/procs/flow.js +221 -0
- package/procs/generateRuleRegistry.js +94 -0
- package/procs/getSource.d.ts +26 -0
- package/procs/job.js +115 -1
- package/procs/launch.d.ts +40 -0
- package/procs/launch.js +239 -108
- package/procs/nu.d.ts +48 -0
- package/procs/scope.js +196 -0
- package/procs/shoot.d.ts +33 -0
- package/procs/standard.d.ts +18 -0
- package/procs/standard.js +83 -0
- package/procs/standard.ts +126 -0
- package/procs/testAct.js +145 -0
- package/procs/testaro.d.ts +20 -0
- package/procs/testaro.js +236 -189
- package/procs/testaro.ts +321 -0
- package/procs/userPath.js +126 -0
- package/procs/xPath.d.ts +4 -0
- package/procs/xPath.js +81 -69
- package/procs/xPath.ts +106 -0
- package/procs/xPathScript.d.ts +6 -0
- package/procs/xPathScript.js +70 -0
- package/run.js +3 -2
- package/surea11y/README.md +44 -0
- package/surea11y/surea11y.browser.js +14 -0
- package/testaro/adbID.d.ts +3 -0
- package/testaro/adbID.js +38 -41
- package/testaro/adbID.ts +66 -0
- package/testaro/allCapStyle.d.ts +3 -0
- package/testaro/allCapStyle.js +31 -34
- package/testaro/allCapStyle.ts +55 -0
- package/testaro/allCaps.d.ts +16 -0
- package/testaro/allCaps.js +179 -151
- package/testaro/allCaps.ts +228 -0
- package/testaro/allHidden.d.ts +13 -0
- package/testaro/allHidden.js +29 -30
- package/testaro/allHidden.ts +50 -0
- package/testaro/allSlanted.d.ts +3 -0
- package/testaro/allSlanted.js +30 -33
- package/testaro/allSlanted.ts +54 -0
- package/testaro/altScheme.d.ts +3 -0
- package/testaro/altScheme.js +27 -30
- package/testaro/altScheme.ts +50 -0
- package/testaro/attVal.d.ts +3 -0
- package/testaro/attVal.js +20 -21
- package/testaro/attVal.ts +52 -0
- package/testaro/autocomplete.d.ts +3 -0
- package/testaro/autocomplete.js +59 -75
- package/testaro/autocomplete.ts +101 -0
- package/testaro/bulk.d.ts +13 -0
- package/testaro/bulk.js +32 -33
- package/testaro/bulk.ts +55 -0
- package/testaro/buttonMenu.d.ts +9 -0
- package/testaro/buttonMenu.js +317 -319
- package/testaro/buttonMenu.ts +391 -0
- package/testaro/captionLoc.d.ts +3 -0
- package/testaro/captionLoc.js +17 -20
- package/testaro/captionLoc.ts +40 -0
- package/testaro/datalistRef.d.ts +3 -0
- package/testaro/datalistRef.js +33 -36
- package/testaro/datalistRef.ts +55 -0
- package/testaro/distortion.d.ts +3 -0
- package/testaro/distortion.js +57 -26
- package/testaro/distortion.ts +81 -0
- package/testaro/docType.d.ts +15 -0
- package/testaro/docType.js +25 -25
- package/testaro/docType.ts +45 -0
- package/testaro/dupAtt.d.ts +16 -0
- package/testaro/dupAtt.js +113 -104
- package/testaro/dupAtt.ts +144 -0
- package/testaro/elements.d.ts +6 -0
- package/testaro/elements.js +153 -153
- package/testaro/elements.ts +215 -0
- package/testaro/embAc.d.ts +3 -0
- package/testaro/embAc.js +19 -20
- package/testaro/embAc.ts +40 -0
- package/testaro/focAll.d.ts +13 -0
- package/testaro/focAll.js +185 -191
- package/testaro/focAll.ts +217 -0
- package/testaro/focAndOp.d.ts +3 -0
- package/testaro/focAndOp.js +100 -104
- package/testaro/focAndOp.ts +128 -0
- package/testaro/focInd.d.ts +3 -0
- package/testaro/focInd.js +64 -66
- package/testaro/focInd.ts +96 -0
- package/testaro/focVis.d.ts +3 -0
- package/testaro/focVis.js +28 -30
- package/testaro/focVis.ts +52 -0
- package/testaro/headEl.d.ts +10 -0
- package/testaro/headEl.js +61 -62
- package/testaro/headEl.ts +82 -0
- package/testaro/headingAmb.d.ts +3 -0
- package/testaro/headingAmb.js +54 -63
- package/testaro/headingAmb.ts +72 -0
- package/testaro/hovInd.d.ts +12 -0
- package/testaro/hovInd.js +163 -130
- package/testaro/hovInd.ts +199 -0
- package/testaro/hover.d.ts +3 -0
- package/testaro/hover.js +155 -126
- package/testaro/hover.ts +154 -0
- package/testaro/hr.d.ts +3 -0
- package/testaro/hr.js +14 -17
- package/testaro/hr.ts +36 -0
- package/testaro/imageLink.d.ts +3 -0
- package/testaro/imageLink.js +17 -20
- package/testaro/imageLink.ts +42 -0
- package/testaro/labClash.d.ts +3 -0
- package/testaro/labClash.js +31 -33
- package/testaro/labClash.ts +54 -0
- package/testaro/legendLoc.d.ts +3 -0
- package/testaro/legendLoc.js +17 -20
- package/testaro/legendLoc.ts +42 -0
- package/testaro/lineHeight.d.ts +3 -0
- package/testaro/lineHeight.js +43 -48
- package/testaro/lineHeight.ts +70 -0
- package/testaro/linkAmb.d.ts +7 -0
- package/testaro/linkAmb.js +79 -80
- package/testaro/linkAmb.ts +105 -0
- package/testaro/linkExt.d.ts +3 -0
- package/testaro/linkExt.js +13 -16
- package/testaro/linkExt.ts +35 -0
- package/testaro/linkOldAtt.d.ts +3 -0
- package/testaro/linkOldAtt.js +25 -28
- package/testaro/linkOldAtt.ts +48 -0
- package/testaro/linkTo.d.ts +3 -0
- package/testaro/linkTo.js +22 -22
- package/testaro/linkTo.ts +43 -0
- package/testaro/linkUl.d.ts +3 -0
- package/testaro/linkUl.js +30 -34
- package/testaro/linkUl.ts +54 -0
- package/testaro/miniText.d.ts +3 -0
- package/testaro/miniText.js +41 -44
- package/testaro/miniText.ts +68 -0
- package/testaro/motion.d.ts +10 -0
- package/testaro/motion.js +92 -96
- package/testaro/motion.ts +125 -0
- package/testaro/nonTable.d.ts +3 -0
- package/testaro/nonTable.js +39 -45
- package/testaro/nonTable.ts +66 -0
- package/testaro/optRoleSel.d.ts +3 -0
- package/testaro/optRoleSel.js +16 -19
- package/testaro/optRoleSel.ts +41 -0
- package/testaro/phOnly.d.ts +3 -0
- package/testaro/phOnly.js +18 -21
- package/testaro/phOnly.ts +43 -0
- package/testaro/pseudoP.d.ts +3 -0
- package/testaro/pseudoP.js +36 -38
- package/testaro/pseudoP.ts +59 -0
- package/testaro/radioSet.d.ts +3 -0
- package/testaro/radioSet.js +57 -59
- package/testaro/radioSet.ts +79 -0
- package/testaro/registry.d.ts +63 -0
- package/testaro/registry.js +67 -0
- package/testaro/registry.ts +141 -0
- package/testaro/role.d.ts +3 -0
- package/testaro/role.js +27 -29
- package/testaro/role.ts +53 -0
- package/testaro/secHeading.d.ts +3 -0
- package/testaro/secHeading.js +30 -33
- package/testaro/secHeading.ts +53 -0
- package/testaro/styleDiff.d.ts +25 -0
- package/testaro/styleDiff.js +248 -252
- package/testaro/styleDiff.ts +303 -0
- package/testaro/tabNav.d.ts +33 -0
- package/testaro/tabNav.js +272 -342
- package/testaro/tabNav.ts +454 -0
- package/testaro/targetsNear.d.ts +9 -0
- package/testaro/targetsNear.js +130 -132
- package/testaro/targetsNear.ts +160 -0
- package/testaro/textNodes.d.ts +6 -0
- package/testaro/textNodes.js +139 -135
- package/testaro/textNodes.ts +185 -0
- package/testaro/textSem.d.ts +3 -0
- package/testaro/textSem.js +25 -28
- package/testaro/textSem.ts +47 -0
- package/testaro/title.d.ts +9 -0
- package/testaro/title.js +16 -13
- package/testaro/title.ts +31 -0
- package/testaro/titledEl.d.ts +3 -0
- package/testaro/titledEl.js +15 -18
- package/testaro/titledEl.ts +38 -0
- package/testaro/zIndex.d.ts +3 -0
- package/testaro/zIndex.js +19 -22
- package/testaro/zIndex.ts +42 -0
- package/tests/alfa.d.ts +45 -0
- package/tests/alfa.js +138 -141
- package/tests/alfa.ts +214 -0
- package/tests/aslint.d.ts +33 -0
- package/tests/aslint.js +272 -249
- package/tests/aslint.ts +301 -0
- package/tests/axe.d.ts +27 -0
- package/tests/axe.js +199 -200
- package/tests/axe.ts +277 -0
- package/tests/ed11y.d.ts +28 -0
- package/tests/ed11y.js +141 -99
- package/tests/ed11y.ts +178 -0
- package/tests/htmlcs.d.ts +21 -0
- package/tests/htmlcs.js +174 -140
- package/tests/htmlcs.ts +181 -0
- package/tests/ibm.d.ts +52 -0
- package/tests/ibm.js +164 -166
- package/tests/ibm.ts +251 -0
- package/tests/nuVal.d.ts +13 -0
- package/tests/nuVal.js +107 -112
- package/tests/nuVal.ts +145 -0
- package/tests/nuVnu.d.ts +15 -0
- package/tests/nuVnu.js +141 -111
- package/tests/nuVnu.ts +144 -0
- package/tests/pour.d.ts +31 -0
- package/tests/pour.js +242 -0
- package/tests/pour.ts +273 -0
- package/tests/qualWeb.d.ts +39 -0
- package/tests/qualWeb.js +302 -272
- package/tests/qualWeb.ts +415 -0
- package/tests/surea11y.d.ts +33 -0
- package/tests/surea11y.js +288 -0
- package/tests/surea11y.ts +334 -0
- package/tests/testaro.d.ts +25 -0
- package/tests/testaro.js +745 -652
- package/tests/testaro.ts +862 -0
- package/tests/wave.d.ts +46 -0
- package/tests/wave.js +166 -177
- package/tests/wave.ts +252 -0
- package/tsconfig.json +17 -0
- package/types.d.ts +243 -0
- package/types.js +10 -0
- package/types.ts +376 -0
- package/validation/act/README.md +46 -0
- package/validation/act/capture.js +424 -0
- package/validation/act/chromium-issue-draft.md +66 -0
- package/validation/act/fp-triage-2026-08-22.md +86 -0
- package/validation/act/isolation-notes.md +159 -0
- package/validation/act/playwright-issue-draft.md +73 -0
- package/validation/act/propose-mappings.js +0 -0
- package/validation/act/repro-cdp-raw.js +105 -0
- package/validation/act/repro-metarefresh.js +65 -0
- package/validation/act/score.js +184 -0
- package/validation/act/stage3a-stress-report.md +69 -0
- package/validation/act/stage3b-mapping-proposals.md +94 -0
- package/validation/act/stage3b-triage-draft.md +138 -0
- package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
- package/validation/executors/netWatch.js +180 -90
- package/validation/executors/test.js +17 -2
- package/validation/executors/tests.js +118 -10
- package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
- package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
- package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
- package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
- package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
- package/validation/knownFailures.json +1 -0
- package/validation/tests/jobProperties/adbID.json +27 -2
- package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
- package/validation/tests/jobProperties/allCaps.json +25 -0
- package/validation/tests/jobProperties/allHidden.json +133 -13
- package/validation/tests/jobProperties/allSlanted.json +3 -3
- package/validation/tests/jobProperties/altScheme.json +23 -8
- package/validation/tests/jobProperties/attVal.json +57 -57
- package/validation/tests/jobProperties/autocomplete.json +11 -1
- package/validation/tests/jobProperties/bulk.json +6 -1
- package/validation/tests/jobProperties/buttonMenu.json +63 -42
- package/validation/tests/jobProperties/captionLoc.json +1 -6
- package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
- package/validation/tests/jobProperties/checkpoint-page.json +397 -0
- package/validation/tests/jobProperties/checkpoint.json +395 -0
- package/validation/tests/jobProperties/datalistRef.json +20 -5
- package/validation/tests/jobProperties/distortion.json +28 -3
- package/validation/tests/jobProperties/docType.json +2 -2
- package/validation/tests/jobProperties/dupAtt.json +48 -33
- package/validation/tests/jobProperties/elements.json +28 -28
- package/validation/tests/jobProperties/embAc.json +36 -31
- package/validation/tests/jobProperties/focAndOp.json +284 -0
- package/validation/tests/jobProperties/focInd.json +34 -34
- package/validation/tests/jobProperties/focVis.json +1 -1
- package/validation/tests/jobProperties/hover.json +39 -37
- package/validation/tests/jobProperties/hr.json +13 -3
- package/validation/tests/jobProperties/imageLink.json +0 -5
- package/validation/tests/jobProperties/labClash.json +68 -33
- package/validation/tests/jobProperties/legendLoc.json +1 -6
- package/validation/tests/jobProperties/lineHeight.json +2 -2
- package/validation/tests/jobProperties/linkAmb.json +13 -18
- package/validation/tests/jobProperties/linkExt.json +1 -1
- package/validation/tests/jobProperties/linkOldAtt.json +12 -2
- package/validation/tests/jobProperties/linkTo.json +1 -1
- package/validation/tests/jobProperties/linkUl.json +65 -65
- package/validation/tests/jobProperties/miniText.json +7 -2
- package/validation/tests/jobProperties/motion.json +4 -50
- package/validation/tests/jobProperties/nonTable.json +48 -3
- package/validation/tests/jobProperties/optRoleSel.json +12 -2
- package/validation/tests/jobProperties/phOnly.json +6 -16
- package/validation/tests/jobProperties/pseudoP.json +17 -2
- package/validation/tests/jobProperties/radioSet.json +30 -30
- package/validation/tests/jobProperties/role.json +25 -5
- package/validation/tests/jobProperties/secHeading.json +26 -21
- package/validation/tests/jobProperties/styleDiff.json +35 -5
- package/validation/tests/jobProperties/tabNav.json +3 -1
- package/validation/tests/jobProperties/targetsNear.json +237 -0
- package/validation/tests/jobProperties/textNodes.json +29 -29
- package/validation/tests/jobProperties/textSem.json +26 -1
- package/validation/tests/jobProperties/title.json +12 -2
- package/validation/tests/jobProperties/titledEl.json +39 -9
- package/validation/tests/jobProperties/userPath.json +317 -0
- package/validation/tests/jobProperties/zIndex.json +40 -35
- package/validation/tests/targets/allCapStyle/index.html +26 -0
- package/validation/tests/targets/checkpoint/index.html +38 -0
- package/validation/tests/targets/datalistRef/index.html +1 -1
- package/validation/tests/targets/focAndOp/bad.html +29 -0
- package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
- package/validation/tests/targets/focInd/bad.html +2 -1
- package/validation/tests/targets/headEl/index.html +10 -1
- package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
- package/validation/tests/targets/userPath/index.html +42 -0
- package/validation/validateTest.js +61 -9
- package/.claude/settings.local.json +0 -11
- package/.eslintrc.json +0 -41
- package/htmlcs/.eslintrc.json +0 -67
- package/validation/tests/jobProperties/linkTitle.json +0 -127
- package/validation/tests/jobProperties/opFoc.json +0 -164
- package/validation/tests/jobProperties/targetSmall.json +0 -152
- package/validation/tests/jobProperties/targetTiny.json +0 -142
- package/validation/tests/targets/focOp/bad.html +0 -25
- package/validation/tests/targets/linkTitle/index.html +0 -24
- package/validation/tests/targets/opFoc/bad.html +0 -26
- package/validation/tests/targets/opFoc/good.html +0 -23
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
© 2026 Jeff Witt.
|
|
4
|
+
|
|
5
|
+
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
|
+
https://opensource.org/license/mit/ for details.
|
|
7
|
+
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.reporter = void 0;
|
|
45
|
+
// IMPORTS
|
|
46
|
+
const fs = __importStar(require("fs/promises"));
|
|
47
|
+
const xPath_1 = require("../procs/xPath");
|
|
48
|
+
const standard_1 = require("../procs/standard");
|
|
49
|
+
/*
|
|
50
|
+
surea11y
|
|
51
|
+
Implements the SureA11y core engine (https://github.com/SureA11y/core) WCAG ruleset.
|
|
52
|
+
Compiled to surea11y.js by tsc (issue #73); edit this file, not the emitted one.
|
|
53
|
+
|
|
54
|
+
Upstream publishes a self-contained standalone browser bundle, vendored verbatim at
|
|
55
|
+
../surea11y/surea11y.browser.js (MPL-2.0), exposing an `a11ycore` global with
|
|
56
|
+
`runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly)`. See
|
|
57
|
+
../surea11y/README.md for the pinned version and verified result shape.
|
|
58
|
+
|
|
59
|
+
Instance policy (engine-candidacy-pipeline.md, Stage 1): `fail`-tier occurrences become
|
|
60
|
+
instances with outcome `failed` at ordinalSeverity 2-3; automatic-rule `cantTell`-tier
|
|
61
|
+
occurrences become instances with outcome `cantTell` at 0-1 (the engine grades per occurrence
|
|
62
|
+
via `occurrenceOutcome`, which takes precedence over the rule-level outcome), carrying the
|
|
63
|
+
engine's `uncertainty` code and `needed` guidance when it supplies them; `pass` and
|
|
64
|
+
`notApplicable` results and all `type: 'manual'` rules are tallied in `data` only and never
|
|
65
|
+
become instances.
|
|
66
|
+
*/
|
|
67
|
+
// CONSTANTS
|
|
68
|
+
// Severity weights within a certainty band, mirroring tests/axe.ts.
|
|
69
|
+
const severityWeights = {
|
|
70
|
+
minor: 0,
|
|
71
|
+
moderate: 0,
|
|
72
|
+
serious: 1,
|
|
73
|
+
critical: 1
|
|
74
|
+
};
|
|
75
|
+
// FUNCTIONS
|
|
76
|
+
// Performs and reports the SureA11y tests.
|
|
77
|
+
const reporter = async (page, report, actIndex) => {
|
|
78
|
+
// Get the nonce, if any.
|
|
79
|
+
const act = report.acts[actIndex];
|
|
80
|
+
const { jobData } = report;
|
|
81
|
+
const scriptNonce = (jobData && jobData.lastScriptNonce);
|
|
82
|
+
// Initialize the act report.
|
|
83
|
+
let data = {};
|
|
84
|
+
const result = {
|
|
85
|
+
nativeResult: {},
|
|
86
|
+
standardResult: {}
|
|
87
|
+
};
|
|
88
|
+
const standard = report.standard !== 'no';
|
|
89
|
+
// If standard results are to be reported:
|
|
90
|
+
if (standard) {
|
|
91
|
+
// Initialize the standard result.
|
|
92
|
+
result.standardResult = (0, standard_1.getStandardResult)();
|
|
93
|
+
}
|
|
94
|
+
// Get the vendored tool bundle.
|
|
95
|
+
let script;
|
|
96
|
+
try {
|
|
97
|
+
script = await fs.readFile(`${__dirname}/../surea11y/surea11y.browser.js`, 'utf8');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
data.prevented = true;
|
|
101
|
+
data.error = 'ERROR: surea11y bundle missing (see surea11y/README.md for vendoring instructions)';
|
|
102
|
+
return { data, result };
|
|
103
|
+
}
|
|
104
|
+
// If the act is scoped to changed subtrees, the engine tests their nearest common ancestor
|
|
105
|
+
// (its context is one selector, resolved with querySelector).
|
|
106
|
+
const contextSelector = report.scope?.commonRoot || null;
|
|
107
|
+
// Perform the tests and populate the native result.
|
|
108
|
+
result.nativeResult = await page.evaluate(args => new Promise(async (resolve) => {
|
|
109
|
+
const { scriptNonce, script, contextSelector } = args;
|
|
110
|
+
// Detect page-CSP blocking of the injection (the violation event is
|
|
111
|
+
// dispatched as a task, so the check below waits a tick).
|
|
112
|
+
let cspBlocked = false;
|
|
113
|
+
document.addEventListener('securitypolicyviolation', event => {
|
|
114
|
+
if ((event.violatedDirective || '').startsWith('script-src')) {
|
|
115
|
+
cspBlocked = true;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
// Add the tool script to the page.
|
|
119
|
+
const toolScript = document.createElement('script');
|
|
120
|
+
if (scriptNonce) {
|
|
121
|
+
toolScript.nonce = scriptNonce;
|
|
122
|
+
console.log(`Added nonce ${scriptNonce} to tool script`);
|
|
123
|
+
}
|
|
124
|
+
toolScript.textContent = script;
|
|
125
|
+
document.body.insertAdjacentElement('beforeend', toolScript);
|
|
126
|
+
// If the bundle failed to define its global:
|
|
127
|
+
if (typeof a11ycore === 'undefined' || typeof a11ycore.runa11yCoreInPage !== 'function') {
|
|
128
|
+
// Let a pending securitypolicyviolation event fire before diagnosing.
|
|
129
|
+
await new Promise(tick => setTimeout(tick, 0));
|
|
130
|
+
// A CSP block is a per-page condition, not a browser fault — report it
|
|
131
|
+
// distinctly (wedge canaries must not match this message).
|
|
132
|
+
resolve({
|
|
133
|
+
prevented: true,
|
|
134
|
+
error: cspBlocked
|
|
135
|
+
? 'surea11y injection blocked by page CSP (script-src)'
|
|
136
|
+
: `a11ycore global not defined (contentType ${document.contentType}, `
|
|
137
|
+
+ `scriptChars ${script.length}, readyState ${document.readyState})`
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
// Run the engine on the whole page with default options — every loaded
|
|
143
|
+
// rule at the default WCAG 2.2 target, matching the axe and pour
|
|
144
|
+
// adapters' kitchen-sink default; testilo's taxonomy decides per rule
|
|
145
|
+
// what counts downstream. runa11yCoreInPage is synchronous; await
|
|
146
|
+
// tolerates a future promise-returning version.
|
|
147
|
+
const sureReport = await a11ycore.runa11yCoreInPage(location.href, contextSelector, {}, null);
|
|
148
|
+
// Dotted WCAG criterion (e.g. 1.4.3) from a rule's normative mappings.
|
|
149
|
+
const criterionOf = (meta) => {
|
|
150
|
+
const mappings = meta && meta.normativeMappings || [];
|
|
151
|
+
for (const mapping of mappings) {
|
|
152
|
+
const requirement = String(mapping.requirement || '').replace(/^SC\s*/i, '');
|
|
153
|
+
if (/^\d+\.\d+\.\d+$/.test(requirement)) {
|
|
154
|
+
return requirement;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return '';
|
|
158
|
+
};
|
|
159
|
+
const violations = [];
|
|
160
|
+
const incomplete = [];
|
|
161
|
+
// Tallied-only buckets.
|
|
162
|
+
let passRuleCount = 0;
|
|
163
|
+
let inapplicableRuleCount = 0;
|
|
164
|
+
let manualRuleCount = 0;
|
|
165
|
+
let manualOccurrenceCount = 0;
|
|
166
|
+
// For each atomic rule that ran:
|
|
167
|
+
(sureReport.checksResults || []).forEach(check => {
|
|
168
|
+
const { ruleId, outcome, type, occurrences } = check;
|
|
169
|
+
// Manual rules are advisory by definition: tally only.
|
|
170
|
+
if (type === 'manual') {
|
|
171
|
+
manualRuleCount++;
|
|
172
|
+
manualOccurrenceCount += (occurrences || []).length;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (outcome === 'pass') {
|
|
176
|
+
passRuleCount++;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// A notApplicable result may carry one scan-describing occurrence
|
|
180
|
+
// (empty selector) — never an element finding.
|
|
181
|
+
if (outcome === 'notApplicable') {
|
|
182
|
+
inapplicableRuleCount++;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// fail or cantTell: convert occurrences to flat findings, honoring
|
|
186
|
+
// per-occurrence grading when the rule tiered its findings.
|
|
187
|
+
(occurrences || []).forEach(occurrence => {
|
|
188
|
+
const tier = occurrence.occurrenceOutcome || outcome;
|
|
189
|
+
const cssPath = occurrence.selector || '';
|
|
190
|
+
let xPath = '';
|
|
191
|
+
try {
|
|
192
|
+
const element = cssPath && document.querySelector(cssPath);
|
|
193
|
+
if (element && typeof window.getXPath === 'function') {
|
|
194
|
+
xPath = window.getXPath(element) ?? '';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
// Unresolvable path: instance falls back to excerpt only.
|
|
199
|
+
}
|
|
200
|
+
// Reason for uncertainty: per occurrence, else per check.
|
|
201
|
+
const uncertainty = occurrence.uncertainty || check.uncertainty || {};
|
|
202
|
+
const finding = {
|
|
203
|
+
ruleID: ruleId || '',
|
|
204
|
+
what: (occurrence.summary || check.title || '').replace(/\s+/g, ' ').trim(),
|
|
205
|
+
wcag: criterionOf(check.meta),
|
|
206
|
+
severity: (check.severity || 'moderate').toLowerCase(),
|
|
207
|
+
uncertainty: tier === 'cantTell' ? (uncertainty.code || '') : '',
|
|
208
|
+
needed: tier === 'cantTell' ? (uncertainty.needed || '') : '',
|
|
209
|
+
html: (occurrence.html || '').slice(0, 500),
|
|
210
|
+
cssPath,
|
|
211
|
+
xPath
|
|
212
|
+
};
|
|
213
|
+
(tier === 'fail' ? violations : incomplete).push(finding);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
resolve({
|
|
217
|
+
engine: sureReport.engine || {},
|
|
218
|
+
violations,
|
|
219
|
+
incomplete,
|
|
220
|
+
passRuleCount,
|
|
221
|
+
inapplicableRuleCount,
|
|
222
|
+
manualRuleCount,
|
|
223
|
+
manualOccurrenceCount
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
resolve({
|
|
228
|
+
prevented: true,
|
|
229
|
+
error: error.message
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
;
|
|
233
|
+
}), {
|
|
234
|
+
scriptNonce,
|
|
235
|
+
script,
|
|
236
|
+
contextSelector
|
|
237
|
+
});
|
|
238
|
+
const { nativeResult } = result;
|
|
239
|
+
// If the tool ran and rules were selected:
|
|
240
|
+
if (!nativeResult.prevented && act.rules) {
|
|
241
|
+
// Remove results of other rules.
|
|
242
|
+
const { rules } = act;
|
|
243
|
+
nativeResult.violations = (nativeResult.violations || [])
|
|
244
|
+
.filter(finding => rules.includes(finding.ruleID));
|
|
245
|
+
nativeResult.incomplete = (nativeResult.incomplete || [])
|
|
246
|
+
.filter(finding => rules.includes(finding.ruleID));
|
|
247
|
+
}
|
|
248
|
+
// If the tool was prevented from running:
|
|
249
|
+
if (nativeResult.prevented) {
|
|
250
|
+
data.prevented = true;
|
|
251
|
+
data.error = nativeResult.error;
|
|
252
|
+
}
|
|
253
|
+
// Otherwise, if a standard result is to be reported:
|
|
254
|
+
else if (standard) {
|
|
255
|
+
const { standardResult } = result;
|
|
256
|
+
// Record the tallied-only bucket sizes.
|
|
257
|
+
data.passRuleCount = nativeResult.passRuleCount;
|
|
258
|
+
data.inapplicableRuleCount = nativeResult.inapplicableRuleCount;
|
|
259
|
+
data.manualRuleCount = nativeResult.manualRuleCount;
|
|
260
|
+
data.manualOccurrenceCount = nativeResult.manualOccurrenceCount;
|
|
261
|
+
// For each certainty band:
|
|
262
|
+
const bands = [
|
|
263
|
+
[nativeResult.incomplete || [], 0, 'cantTell'],
|
|
264
|
+
[nativeResult.violations || [], 2, 'failed']
|
|
265
|
+
];
|
|
266
|
+
bands.forEach(([findings, baseSeverity, outcome]) => {
|
|
267
|
+
// For each native-result finding:
|
|
268
|
+
findings.forEach(finding => {
|
|
269
|
+
// Add a standard-result instance, forwarding only a known uncertainty code.
|
|
270
|
+
const { ruleID, what, severity, uncertainty, needed, xPath } = finding;
|
|
271
|
+
(0, standard_1.addInstance)(standardResult, {
|
|
272
|
+
ruleID,
|
|
273
|
+
what,
|
|
274
|
+
ordinalSeverity: baseSeverity + (severityWeights[severity] ?? 0),
|
|
275
|
+
outcome,
|
|
276
|
+
uncertainty: standard_1.UNCERTAINTY_CODES.includes(uncertainty) ? uncertainty : undefined,
|
|
277
|
+
needed,
|
|
278
|
+
catalogIndex: (0, xPath_1.getXPathCatalogIndex)(report, xPath)
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
data,
|
|
285
|
+
result
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
exports.reporter = reporter;
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2026 Jeff Witt.
|
|
3
|
+
|
|
4
|
+
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
|
+
https://opensource.org/license/mit/ for details.
|
|
6
|
+
|
|
7
|
+
SPDX-License-Identifier: MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// IMPORTS
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs/promises';
|
|
13
|
+
import type {Page} from 'playwright';
|
|
14
|
+
import {getXPathCatalogIndex} from '../procs/xPath';
|
|
15
|
+
import {getStandardResult, addInstance, UNCERTAINTY_CODES} from '../procs/standard';
|
|
16
|
+
import type {Act, Outcome, Report, StandardResult} from '../types';
|
|
17
|
+
|
|
18
|
+
// TYPES
|
|
19
|
+
|
|
20
|
+
// The surea11y-act properties this reporter consumes.
|
|
21
|
+
interface SureAct extends Act {
|
|
22
|
+
rules?: string[];
|
|
23
|
+
}
|
|
24
|
+
// A surea11y severity (axe-style impact).
|
|
25
|
+
type SureSeverity = 'minor' | 'moderate' | 'serious' | 'critical';
|
|
26
|
+
// One element finding, flattened from the engine's per-check results.
|
|
27
|
+
interface SureFinding {
|
|
28
|
+
ruleID: string;
|
|
29
|
+
what: string;
|
|
30
|
+
wcag: string;
|
|
31
|
+
severity: SureSeverity;
|
|
32
|
+
uncertainty: string;
|
|
33
|
+
needed: string;
|
|
34
|
+
html: string;
|
|
35
|
+
cssPath: string;
|
|
36
|
+
xPath: string;
|
|
37
|
+
}
|
|
38
|
+
// The native result: flattened findings and tallies, or a prevention report.
|
|
39
|
+
interface SureNativeResult {
|
|
40
|
+
engine?: Record<string, unknown>;
|
|
41
|
+
violations?: SureFinding[];
|
|
42
|
+
incomplete?: SureFinding[];
|
|
43
|
+
passRuleCount?: number;
|
|
44
|
+
inapplicableRuleCount?: number;
|
|
45
|
+
manualRuleCount?: number;
|
|
46
|
+
manualOccurrenceCount?: number;
|
|
47
|
+
prevented?: boolean;
|
|
48
|
+
error?: string;
|
|
49
|
+
}
|
|
50
|
+
// An uncertainty report of the engine (per occurrence or per check).
|
|
51
|
+
interface SureUncertainty {
|
|
52
|
+
code?: string;
|
|
53
|
+
needed?: string;
|
|
54
|
+
}
|
|
55
|
+
// One occurrence of a check result (verified against @surea11y/core 1.7.0).
|
|
56
|
+
interface SureOccurrence {
|
|
57
|
+
selector?: string;
|
|
58
|
+
html?: string;
|
|
59
|
+
summary?: string;
|
|
60
|
+
occurrenceOutcome?: 'fail' | 'cantTell';
|
|
61
|
+
uncertainty?: SureUncertainty;
|
|
62
|
+
}
|
|
63
|
+
// One check result.
|
|
64
|
+
interface SureCheck {
|
|
65
|
+
ruleId?: string;
|
|
66
|
+
outcome?: 'pass' | 'fail' | 'cantTell' | 'notApplicable';
|
|
67
|
+
severity?: string;
|
|
68
|
+
type?: 'automatic' | 'manual';
|
|
69
|
+
title?: string;
|
|
70
|
+
occurrences?: SureOccurrence[];
|
|
71
|
+
uncertainty?: SureUncertainty;
|
|
72
|
+
meta?: {normativeMappings?: {requirement?: string}[]};
|
|
73
|
+
}
|
|
74
|
+
// The engine report.
|
|
75
|
+
interface SureReport {
|
|
76
|
+
engine?: Record<string, unknown>;
|
|
77
|
+
checksResults?: SureCheck[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
The a11ycore global that the vendored bundle defines in the page. Declared without a
|
|
82
|
+
value, so this declaration is erased at emit and the references inside the page.evaluate
|
|
83
|
+
callback remain bare page-global references.
|
|
84
|
+
*/
|
|
85
|
+
declare const a11ycore: {
|
|
86
|
+
runa11yCoreInPage: (
|
|
87
|
+
pageUrl: string, contextSelector: string | null, engineOptions: Record<string, unknown>, runOnly: unknown
|
|
88
|
+
) => SureReport | Promise<SureReport>;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
surea11y
|
|
93
|
+
Implements the SureA11y core engine (https://github.com/SureA11y/core) WCAG ruleset.
|
|
94
|
+
Compiled to surea11y.js by tsc (issue #73); edit this file, not the emitted one.
|
|
95
|
+
|
|
96
|
+
Upstream publishes a self-contained standalone browser bundle, vendored verbatim at
|
|
97
|
+
../surea11y/surea11y.browser.js (MPL-2.0), exposing an `a11ycore` global with
|
|
98
|
+
`runa11yCoreInPage(pageUrl, contextSelector, engineOptions, runOnly)`. See
|
|
99
|
+
../surea11y/README.md for the pinned version and verified result shape.
|
|
100
|
+
|
|
101
|
+
Instance policy (engine-candidacy-pipeline.md, Stage 1): `fail`-tier occurrences become
|
|
102
|
+
instances with outcome `failed` at ordinalSeverity 2-3; automatic-rule `cantTell`-tier
|
|
103
|
+
occurrences become instances with outcome `cantTell` at 0-1 (the engine grades per occurrence
|
|
104
|
+
via `occurrenceOutcome`, which takes precedence over the rule-level outcome), carrying the
|
|
105
|
+
engine's `uncertainty` code and `needed` guidance when it supplies them; `pass` and
|
|
106
|
+
`notApplicable` results and all `type: 'manual'` rules are tallied in `data` only and never
|
|
107
|
+
become instances.
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
// CONSTANTS
|
|
111
|
+
|
|
112
|
+
// Severity weights within a certainty band, mirroring tests/axe.ts.
|
|
113
|
+
const severityWeights: Record<SureSeverity, number> = {
|
|
114
|
+
minor: 0,
|
|
115
|
+
moderate: 0,
|
|
116
|
+
serious: 1,
|
|
117
|
+
critical: 1
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// FUNCTIONS
|
|
121
|
+
|
|
122
|
+
// Performs and reports the SureA11y tests.
|
|
123
|
+
export const reporter = async (page: Page, report: Report, actIndex: number) => {
|
|
124
|
+
// Get the nonce, if any.
|
|
125
|
+
const act = report.acts[actIndex] as SureAct;
|
|
126
|
+
const {jobData} = report;
|
|
127
|
+
const scriptNonce = (jobData && jobData.lastScriptNonce) as string | undefined;
|
|
128
|
+
// Initialize the act report.
|
|
129
|
+
let data: Record<string, unknown> = {};
|
|
130
|
+
const result: {nativeResult: SureNativeResult; standardResult: StandardResult} = {
|
|
131
|
+
nativeResult: {},
|
|
132
|
+
standardResult: {}
|
|
133
|
+
};
|
|
134
|
+
const standard = report.standard !== 'no';
|
|
135
|
+
// If standard results are to be reported:
|
|
136
|
+
if (standard) {
|
|
137
|
+
// Initialize the standard result.
|
|
138
|
+
result.standardResult = getStandardResult();
|
|
139
|
+
}
|
|
140
|
+
// Get the vendored tool bundle.
|
|
141
|
+
let script: string;
|
|
142
|
+
try {
|
|
143
|
+
script = await fs.readFile(`${__dirname}/../surea11y/surea11y.browser.js`, 'utf8');
|
|
144
|
+
}
|
|
145
|
+
catch(error) {
|
|
146
|
+
data.prevented = true;
|
|
147
|
+
data.error = 'ERROR: surea11y bundle missing (see surea11y/README.md for vendoring instructions)';
|
|
148
|
+
return {data, result};
|
|
149
|
+
}
|
|
150
|
+
// If the act is scoped to changed subtrees, the engine tests their nearest common ancestor
|
|
151
|
+
// (its context is one selector, resolved with querySelector).
|
|
152
|
+
const contextSelector = report.scope?.commonRoot || null;
|
|
153
|
+
// Perform the tests and populate the native result.
|
|
154
|
+
result.nativeResult = await page.evaluate(args => new Promise<SureNativeResult>(
|
|
155
|
+
async resolve => {
|
|
156
|
+
const {scriptNonce, script, contextSelector} = args;
|
|
157
|
+
// Detect page-CSP blocking of the injection (the violation event is
|
|
158
|
+
// dispatched as a task, so the check below waits a tick).
|
|
159
|
+
let cspBlocked = false;
|
|
160
|
+
document.addEventListener('securitypolicyviolation', event => {
|
|
161
|
+
if ((event.violatedDirective || '').startsWith('script-src')) {
|
|
162
|
+
cspBlocked = true;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// Add the tool script to the page.
|
|
166
|
+
const toolScript = document.createElement('script');
|
|
167
|
+
if (scriptNonce) {
|
|
168
|
+
toolScript.nonce = scriptNonce;
|
|
169
|
+
console.log(`Added nonce ${scriptNonce} to tool script`);
|
|
170
|
+
}
|
|
171
|
+
toolScript.textContent = script;
|
|
172
|
+
document.body.insertAdjacentElement('beforeend', toolScript);
|
|
173
|
+
// If the bundle failed to define its global:
|
|
174
|
+
if (typeof a11ycore === 'undefined' || typeof a11ycore.runa11yCoreInPage !== 'function') {
|
|
175
|
+
// Let a pending securitypolicyviolation event fire before diagnosing.
|
|
176
|
+
await new Promise(tick => setTimeout(tick, 0));
|
|
177
|
+
// A CSP block is a per-page condition, not a browser fault — report it
|
|
178
|
+
// distinctly (wedge canaries must not match this message).
|
|
179
|
+
resolve({
|
|
180
|
+
prevented: true,
|
|
181
|
+
error: cspBlocked
|
|
182
|
+
? 'surea11y injection blocked by page CSP (script-src)'
|
|
183
|
+
: `a11ycore global not defined (contentType ${document.contentType}, `
|
|
184
|
+
+ `scriptChars ${script.length}, readyState ${document.readyState})`
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
// Run the engine on the whole page with default options — every loaded
|
|
190
|
+
// rule at the default WCAG 2.2 target, matching the axe and pour
|
|
191
|
+
// adapters' kitchen-sink default; testilo's taxonomy decides per rule
|
|
192
|
+
// what counts downstream. runa11yCoreInPage is synchronous; await
|
|
193
|
+
// tolerates a future promise-returning version.
|
|
194
|
+
const sureReport = await a11ycore.runa11yCoreInPage(location.href, contextSelector, {}, null);
|
|
195
|
+
// Dotted WCAG criterion (e.g. 1.4.3) from a rule's normative mappings.
|
|
196
|
+
const criterionOf = (meta: SureCheck['meta']): string => {
|
|
197
|
+
const mappings = meta && meta.normativeMappings || [];
|
|
198
|
+
for (const mapping of mappings) {
|
|
199
|
+
const requirement = String(mapping.requirement || '').replace(/^SC\s*/i, '');
|
|
200
|
+
if (/^\d+\.\d+\.\d+$/.test(requirement)) {
|
|
201
|
+
return requirement;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return '';
|
|
205
|
+
};
|
|
206
|
+
const violations: SureFinding[] = [];
|
|
207
|
+
const incomplete: SureFinding[] = [];
|
|
208
|
+
// Tallied-only buckets.
|
|
209
|
+
let passRuleCount = 0;
|
|
210
|
+
let inapplicableRuleCount = 0;
|
|
211
|
+
let manualRuleCount = 0;
|
|
212
|
+
let manualOccurrenceCount = 0;
|
|
213
|
+
// For each atomic rule that ran:
|
|
214
|
+
(sureReport.checksResults || []).forEach(check => {
|
|
215
|
+
const {ruleId, outcome, type, occurrences} = check;
|
|
216
|
+
// Manual rules are advisory by definition: tally only.
|
|
217
|
+
if (type === 'manual') {
|
|
218
|
+
manualRuleCount++;
|
|
219
|
+
manualOccurrenceCount += (occurrences || []).length;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (outcome === 'pass') {
|
|
223
|
+
passRuleCount++;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
// A notApplicable result may carry one scan-describing occurrence
|
|
227
|
+
// (empty selector) — never an element finding.
|
|
228
|
+
if (outcome === 'notApplicable') {
|
|
229
|
+
inapplicableRuleCount++;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
// fail or cantTell: convert occurrences to flat findings, honoring
|
|
233
|
+
// per-occurrence grading when the rule tiered its findings.
|
|
234
|
+
(occurrences || []).forEach(occurrence => {
|
|
235
|
+
const tier = occurrence.occurrenceOutcome || outcome;
|
|
236
|
+
const cssPath = occurrence.selector || '';
|
|
237
|
+
let xPath = '';
|
|
238
|
+
try {
|
|
239
|
+
const element = cssPath && document.querySelector(cssPath);
|
|
240
|
+
if (element && typeof window.getXPath === 'function') {
|
|
241
|
+
xPath = window.getXPath(element) ?? '';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch(error) {
|
|
245
|
+
// Unresolvable path: instance falls back to excerpt only.
|
|
246
|
+
}
|
|
247
|
+
// Reason for uncertainty: per occurrence, else per check.
|
|
248
|
+
const uncertainty = occurrence.uncertainty || check.uncertainty || {};
|
|
249
|
+
const finding: SureFinding = {
|
|
250
|
+
ruleID: ruleId || '',
|
|
251
|
+
what: (occurrence.summary || check.title || '').replace(/\s+/g, ' ').trim(),
|
|
252
|
+
wcag: criterionOf(check.meta),
|
|
253
|
+
severity: (check.severity || 'moderate').toLowerCase() as SureSeverity,
|
|
254
|
+
uncertainty: tier === 'cantTell' ? (uncertainty.code || '') : '',
|
|
255
|
+
needed: tier === 'cantTell' ? (uncertainty.needed || '') : '',
|
|
256
|
+
html: (occurrence.html || '').slice(0, 500),
|
|
257
|
+
cssPath,
|
|
258
|
+
xPath
|
|
259
|
+
};
|
|
260
|
+
(tier === 'fail' ? violations : incomplete).push(finding);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
resolve({
|
|
264
|
+
engine: sureReport.engine || {},
|
|
265
|
+
violations,
|
|
266
|
+
incomplete,
|
|
267
|
+
passRuleCount,
|
|
268
|
+
inapplicableRuleCount,
|
|
269
|
+
manualRuleCount,
|
|
270
|
+
manualOccurrenceCount
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch(error) {
|
|
274
|
+
resolve({
|
|
275
|
+
prevented: true,
|
|
276
|
+
error: (error as Error).message
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
), {
|
|
281
|
+
scriptNonce,
|
|
282
|
+
script,
|
|
283
|
+
contextSelector
|
|
284
|
+
});
|
|
285
|
+
const {nativeResult} = result;
|
|
286
|
+
// If the tool ran and rules were selected:
|
|
287
|
+
if (! nativeResult.prevented && act.rules) {
|
|
288
|
+
// Remove results of other rules.
|
|
289
|
+
const {rules} = act;
|
|
290
|
+
nativeResult.violations = (nativeResult.violations || [])
|
|
291
|
+
.filter(finding => rules.includes(finding.ruleID));
|
|
292
|
+
nativeResult.incomplete = (nativeResult.incomplete || [])
|
|
293
|
+
.filter(finding => rules.includes(finding.ruleID));
|
|
294
|
+
}
|
|
295
|
+
// If the tool was prevented from running:
|
|
296
|
+
if (nativeResult.prevented) {
|
|
297
|
+
data.prevented = true;
|
|
298
|
+
data.error = nativeResult.error;
|
|
299
|
+
}
|
|
300
|
+
// Otherwise, if a standard result is to be reported:
|
|
301
|
+
else if (standard) {
|
|
302
|
+
const {standardResult} = result;
|
|
303
|
+
// Record the tallied-only bucket sizes.
|
|
304
|
+
data.passRuleCount = nativeResult.passRuleCount;
|
|
305
|
+
data.inapplicableRuleCount = nativeResult.inapplicableRuleCount;
|
|
306
|
+
data.manualRuleCount = nativeResult.manualRuleCount;
|
|
307
|
+
data.manualOccurrenceCount = nativeResult.manualOccurrenceCount;
|
|
308
|
+
// For each certainty band:
|
|
309
|
+
const bands: [SureFinding[], number, Outcome][] = [
|
|
310
|
+
[nativeResult.incomplete || [], 0, 'cantTell'],
|
|
311
|
+
[nativeResult.violations || [], 2, 'failed']
|
|
312
|
+
];
|
|
313
|
+
bands.forEach(([findings, baseSeverity, outcome]) => {
|
|
314
|
+
// For each native-result finding:
|
|
315
|
+
findings.forEach(finding => {
|
|
316
|
+
// Add a standard-result instance, forwarding only a known uncertainty code.
|
|
317
|
+
const {ruleID, what, severity, uncertainty, needed, xPath} = finding;
|
|
318
|
+
addInstance(standardResult, {
|
|
319
|
+
ruleID,
|
|
320
|
+
what,
|
|
321
|
+
ordinalSeverity: baseSeverity + (severityWeights[severity] ?? 0),
|
|
322
|
+
outcome,
|
|
323
|
+
uncertainty: (UNCERTAINTY_CODES as string[]).includes(uncertainty) ? uncertainty : undefined,
|
|
324
|
+
needed,
|
|
325
|
+
catalogIndex: getXPathCatalogIndex(report, xPath)
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
data,
|
|
332
|
+
result
|
|
333
|
+
};
|
|
334
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
import type { Outcome, Report, StandardInstance } from '../types';
|
|
3
|
+
export declare const reporter: (page: Page | undefined, report: Report, actIndex: number) => Promise<{
|
|
4
|
+
data: {
|
|
5
|
+
prevented: boolean;
|
|
6
|
+
error: string;
|
|
7
|
+
rulePreventions: Record<string, string>;
|
|
8
|
+
rulesInvalid: string[];
|
|
9
|
+
ruleTestTimes: [string, number][];
|
|
10
|
+
ruleData: Record<string, unknown>;
|
|
11
|
+
scope?: {
|
|
12
|
+
localRules: string[];
|
|
13
|
+
pageRules: string[];
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
result: {
|
|
17
|
+
nativeResult: Record<string, unknown>;
|
|
18
|
+
standardResult: {
|
|
19
|
+
prevented: boolean;
|
|
20
|
+
totals: number[];
|
|
21
|
+
outcomeTotals: Record<Outcome, number>;
|
|
22
|
+
instances: StandardInstance[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}>;
|