testaro 78.0.6 → 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 +25 -19
- package/CONTAINERS.md +10 -6
- package/CONTRIBUTING.md +29 -3
- package/Dockerfile +1 -1
- package/README.md +75 -20
- 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 +373 -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 +313 -221
- package/procs/catalog.ts +398 -0
- package/procs/checkpoint.js +110 -0
- package/procs/config.d.ts +35 -0
- package/procs/config.js +63 -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/getSource.js +1 -0
- package/procs/job.js +116 -1
- package/procs/launch.d.ts +40 -0
- package/procs/launch.js +239 -108
- package/procs/nu.d.ts +48 -0
- package/procs/nu.js +1 -0
- package/procs/scope.js +196 -0
- package/procs/shoot.d.ts +33 -0
- package/procs/shoot.js +3 -1
- 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 +237 -189
- package/procs/testaro.ts +321 -0
- package/procs/userPath.js +126 -0
- package/procs/xPath.d.ts +4 -0
- package/procs/xPath.js +82 -64
- 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 +39 -41
- package/testaro/adbID.ts +66 -0
- package/testaro/allCapStyle.d.ts +3 -0
- package/testaro/allCapStyle.js +32 -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 +30 -30
- package/testaro/allHidden.ts +50 -0
- package/testaro/allSlanted.d.ts +3 -0
- package/testaro/allSlanted.js +31 -33
- package/testaro/allSlanted.ts +54 -0
- package/testaro/altScheme.d.ts +3 -0
- package/testaro/altScheme.js +28 -30
- package/testaro/altScheme.ts +50 -0
- package/testaro/attVal.d.ts +3 -0
- package/testaro/attVal.js +21 -21
- package/testaro/attVal.ts +52 -0
- package/testaro/autocomplete.d.ts +3 -0
- package/testaro/autocomplete.js +60 -75
- package/testaro/autocomplete.ts +101 -0
- package/testaro/bulk.d.ts +13 -0
- package/testaro/bulk.js +33 -33
- package/testaro/bulk.ts +55 -0
- package/testaro/buttonMenu.d.ts +9 -0
- package/testaro/buttonMenu.js +318 -319
- package/testaro/buttonMenu.ts +391 -0
- package/testaro/captionLoc.d.ts +3 -0
- package/testaro/captionLoc.js +18 -20
- package/testaro/captionLoc.ts +40 -0
- package/testaro/datalistRef.d.ts +3 -0
- package/testaro/datalistRef.js +34 -36
- package/testaro/datalistRef.ts +55 -0
- package/testaro/distortion.d.ts +3 -0
- package/testaro/distortion.js +58 -26
- package/testaro/distortion.ts +81 -0
- package/testaro/docType.d.ts +15 -0
- package/testaro/docType.js +26 -25
- package/testaro/docType.ts +45 -0
- package/testaro/dupAtt.d.ts +16 -0
- package/testaro/dupAtt.js +114 -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 +20 -20
- package/testaro/embAc.ts +40 -0
- package/testaro/focAll.d.ts +13 -0
- package/testaro/focAll.js +186 -191
- package/testaro/focAll.ts +217 -0
- package/testaro/focAndOp.d.ts +3 -0
- package/testaro/focAndOp.js +101 -104
- package/testaro/focAndOp.ts +128 -0
- package/testaro/focInd.d.ts +3 -0
- package/testaro/focInd.js +65 -66
- package/testaro/focInd.ts +96 -0
- package/testaro/focVis.d.ts +3 -0
- package/testaro/focVis.js +29 -30
- package/testaro/focVis.ts +52 -0
- package/testaro/headEl.d.ts +10 -0
- package/testaro/headEl.js +62 -62
- package/testaro/headEl.ts +82 -0
- package/testaro/headingAmb.d.ts +3 -0
- package/testaro/headingAmb.js +55 -63
- package/testaro/headingAmb.ts +72 -0
- package/testaro/hovInd.d.ts +12 -0
- package/testaro/hovInd.js +164 -130
- package/testaro/hovInd.ts +199 -0
- package/testaro/hover.d.ts +3 -0
- package/testaro/hover.js +156 -126
- package/testaro/hover.ts +154 -0
- package/testaro/hr.d.ts +3 -0
- package/testaro/hr.js +15 -17
- package/testaro/hr.ts +36 -0
- package/testaro/imageLink.d.ts +3 -0
- package/testaro/imageLink.js +18 -20
- package/testaro/imageLink.ts +42 -0
- package/testaro/labClash.d.ts +3 -0
- package/testaro/labClash.js +32 -33
- package/testaro/labClash.ts +54 -0
- package/testaro/legendLoc.d.ts +3 -0
- package/testaro/legendLoc.js +18 -20
- package/testaro/legendLoc.ts +42 -0
- package/testaro/lineHeight.d.ts +3 -0
- package/testaro/lineHeight.js +44 -48
- package/testaro/lineHeight.ts +70 -0
- package/testaro/linkAmb.d.ts +7 -0
- package/testaro/linkAmb.js +80 -80
- package/testaro/linkAmb.ts +105 -0
- package/testaro/linkExt.d.ts +3 -0
- package/testaro/linkExt.js +14 -16
- package/testaro/linkExt.ts +35 -0
- package/testaro/linkOldAtt.d.ts +3 -0
- package/testaro/linkOldAtt.js +26 -28
- package/testaro/linkOldAtt.ts +48 -0
- package/testaro/linkTo.d.ts +3 -0
- package/testaro/linkTo.js +23 -22
- package/testaro/linkTo.ts +43 -0
- package/testaro/linkUl.d.ts +3 -0
- package/testaro/linkUl.js +31 -34
- package/testaro/linkUl.ts +54 -0
- package/testaro/miniText.d.ts +3 -0
- package/testaro/miniText.js +42 -44
- package/testaro/miniText.ts +68 -0
- package/testaro/motion.d.ts +10 -0
- package/testaro/motion.js +93 -96
- package/testaro/motion.ts +125 -0
- package/testaro/nonTable.d.ts +3 -0
- package/testaro/nonTable.js +40 -45
- package/testaro/nonTable.ts +66 -0
- package/testaro/optRoleSel.d.ts +3 -0
- package/testaro/optRoleSel.js +17 -19
- package/testaro/optRoleSel.ts +41 -0
- package/testaro/phOnly.d.ts +3 -0
- package/testaro/phOnly.js +19 -21
- package/testaro/phOnly.ts +43 -0
- package/testaro/pseudoP.d.ts +3 -0
- package/testaro/pseudoP.js +37 -38
- package/testaro/pseudoP.ts +59 -0
- package/testaro/radioSet.d.ts +3 -0
- package/testaro/radioSet.js +58 -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 +28 -29
- package/testaro/role.ts +53 -0
- package/testaro/secHeading.d.ts +3 -0
- package/testaro/secHeading.js +31 -33
- package/testaro/secHeading.ts +53 -0
- package/testaro/styleDiff.d.ts +25 -0
- package/testaro/styleDiff.js +249 -252
- package/testaro/styleDiff.ts +303 -0
- package/testaro/tabNav.d.ts +33 -0
- package/testaro/tabNav.js +273 -342
- package/testaro/tabNav.ts +454 -0
- package/testaro/targetsNear.d.ts +9 -0
- package/testaro/targetsNear.js +131 -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 +26 -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 +16 -18
- package/testaro/titledEl.ts +38 -0
- package/testaro/zIndex.d.ts +3 -0
- package/testaro/zIndex.js +20 -22
- package/testaro/zIndex.ts +42 -0
- package/tests/alfa.d.ts +45 -0
- package/tests/alfa.js +139 -141
- package/tests/alfa.ts +214 -0
- package/tests/aslint.d.ts +33 -0
- package/tests/aslint.js +273 -249
- package/tests/aslint.ts +301 -0
- package/tests/axe.d.ts +27 -0
- package/tests/axe.js +200 -200
- package/tests/axe.ts +277 -0
- package/tests/ed11y.d.ts +28 -0
- package/tests/ed11y.js +142 -99
- package/tests/ed11y.ts +178 -0
- package/tests/htmlcs.d.ts +21 -0
- package/tests/htmlcs.js +175 -140
- package/tests/htmlcs.ts +181 -0
- package/tests/ibm.d.ts +52 -0
- package/tests/ibm.js +165 -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 +142 -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 +303 -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 +746 -652
- package/tests/testaro.ts +862 -0
- package/tests/wave.d.ts +46 -0
- package/tests/wave.js +167 -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 +62 -10
- package/.claude/settings.local.json +0 -11
- package/.eslintrc.json +0 -41
- package/htmlcs/.eslintrc.json +0 -67
- package/memory/MEMORY.md +0 -3
- package/memory/project_validation_pause.md +0 -10
- 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,199 @@
|
|
|
1
|
+
# Checkpoints: scanning across a serial action flow
|
|
2
|
+
|
|
3
|
+
**Status:** Phases 0, 1, and 1b shipped in Testaro 78.2 (2026-09-02); Phases 2 (`report.flow`),
|
|
4
|
+
3 (`scope: 'changed'`), and 4 (incremental pruning, selector acts, the user-path converter)
|
|
5
|
+
shipped in 78.3 (2026-09-02). The interaction modalities and the yra-monitor ingestion changes
|
|
6
|
+
remain follow-ups.
|
|
7
|
+
|
|
8
|
+
## Why
|
|
9
|
+
|
|
10
|
+
A job is a serial list of acts: launch a browser, interact with the page, test it. Until 78.2,
|
|
11
|
+
the interaction acts had no effect on any test: every `test` act ran in a child process that
|
|
12
|
+
launched its own browser and navigated to the job target, and the element catalog was built
|
|
13
|
+
once, before any act, for that same page. A job of the form launch → click → test therefore
|
|
14
|
+
tested the page as first loaded, and any test after a `url` act resolved its elements against
|
|
15
|
+
the wrong page's catalog.
|
|
16
|
+
|
|
17
|
+
The maintainer's goals: keep the serial action flow; keep or extend a running list of found
|
|
18
|
+
issues as the flow progresses; use explicit triggers for re-scanning rather than re-running
|
|
19
|
+
every tool after every action; and re-scan either the whole page or only the parts that
|
|
20
|
+
changed, as the case requires.
|
|
21
|
+
|
|
22
|
+
## The model
|
|
23
|
+
|
|
24
|
+
A **checkpoint** is a named page state reached by the flow, snapshotted and tested:
|
|
25
|
+
|
|
26
|
+
- **Checkpoint 0** is the job target as launched. `getCatalog` creates it before any act, as
|
|
27
|
+
it always built the catalog, and records it as `report.checkpoints[0]` (named `start`).
|
|
28
|
+
- A **`checkpoint` act** (`{type: 'checkpoint', which: '<name>'}`) creates the next one from
|
|
29
|
+
the live page in the parent process: catalog entries for the current state, page image(s)
|
|
30
|
+
if the job asks for them, the URL and title, an ARIA snapshot, and a DOM digest.
|
|
31
|
+
- A **`test` act belongs to the most recent checkpoint** (`act.checkpoint`), and every
|
|
32
|
+
standard instance it reports carries `checkpoint`.
|
|
33
|
+
|
|
34
|
+
Checkpoint mode is opt-in: it is on only when the job contains a `checkpoint` act. Legacy
|
|
35
|
+
jobs (no such act) run as before and produce today's report plus the `checkpoint: 0` fields;
|
|
36
|
+
if interaction acts precede a test act in a legacy job, `jobData.warnings` says that the act
|
|
37
|
+
tests checkpoint 0. In checkpoint mode, interaction acts followed by a test act without an
|
|
38
|
+
intervening checkpoint produce an implicit checkpoint (named `act<N>`) and a warning; the
|
|
39
|
+
warning never fails the job.
|
|
40
|
+
|
|
41
|
+
### Navigation and interaction checkpoints
|
|
42
|
+
|
|
43
|
+
A checkpoint is a **navigation checkpoint** when the page was reached by navigation alone
|
|
44
|
+
(a `launch` or `url` act) and no interaction act ran since; it is an **interaction
|
|
45
|
+
checkpoint** when acts on a page (button, link, text, press, reveal, …) reached it. The
|
|
46
|
+
distinction decides how a test act's browser reaches the state:
|
|
47
|
+
|
|
48
|
+
- Navigation checkpoint: the child navigates to the checkpoint's URL. Every tool runs.
|
|
49
|
+
- Interaction checkpoint: the child navigates to the URL the interaction started from
|
|
50
|
+
(`launchURL`) and **replays** the recorded interaction acts (`replay`, act indexes) before
|
|
51
|
+
running the tool. The replay happens inside `launchOnce` after navigation and before any
|
|
52
|
+
`data-xpath` stamping, so elements the acts reveal or create are stamped like the rest,
|
|
53
|
+
and every per-rule relaunch of a contaminating testaro rule replays automatically.
|
|
54
|
+
|
|
55
|
+
Replay is a re-enactment, not a restore. Server state, sessions, timers and randomness can
|
|
56
|
+
make the replayed DOM differ from the snapshot. Each test act records
|
|
57
|
+
`data.replay = {checkpoint, acts, elapsedMs, fidelity}` where `fidelity` is `exact` when
|
|
58
|
+
the replayed DOM's digest equals the checkpoint's, else `divergent`. A replay failure
|
|
59
|
+
prevents the test act with `checkpoint replay failed at act N (…)` and is not retried.
|
|
60
|
+
|
|
61
|
+
### What each tool can observe
|
|
62
|
+
|
|
63
|
+
`procs/job.js` `toolInputs` declares what a tool tests:
|
|
64
|
+
|
|
65
|
+
| input | tools | interaction checkpoints |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `page` | alfa, aslint, axe, ed11y, htmlcs, ibm, pour, surea11y, testaro | yes (replayed page) |
|
|
68
|
+
| `html` | qualWeb, nuVal, nuVnu (they read the live page's HTML) | yes (replayed page) |
|
|
69
|
+
| `url` | wave; nuVal and nuVnu with `withSource: true` | no: prevented with a stated reason |
|
|
70
|
+
|
|
71
|
+
### Report shape
|
|
72
|
+
|
|
73
|
+
- `report.checkpoints[k]`: `{index, name, implicit, actIndex, launchActIndex, launchURL,
|
|
74
|
+
replay, interaction: {modality}, kind: 'navigation' | 'interaction', url, title,
|
|
75
|
+
imageIndexes, catalogRange, elementCount, ariaSnapshot, domDigest, elapsedMs, testActs}`.
|
|
76
|
+
- `report.catalog` stays one flat map. Entries carry `checkpoint`; indexes come from a
|
|
77
|
+
monotonic job-time counter (`report.catalogNextIndex`), so entries of different checkpoints
|
|
78
|
+
never collide, and the job-time `report.pathIDs` is scoped per checkpoint so the same XPath
|
|
79
|
+
in two states maps to two entries. Pruning keeps only cited entries, as before.
|
|
80
|
+
- `report.images`: `images[0]` (and `[1]` at `imageScale > 1`) keep meaning checkpoint 0;
|
|
81
|
+
later checkpoints' images are indexed by `checkpoints[k].imageIndexes`. The testaro `motion`
|
|
82
|
+
rule compares against its checkpoint's image.
|
|
83
|
+
- `jobData.catalogData.checkpoints[k]`: element and entry counts per checkpoint.
|
|
84
|
+
- `report.flow` (two or more checkpoints): `{checkpoints: [{index, name, kind, url, actIndex,
|
|
85
|
+
testActs, tools, issueCount}], deltas: [{from, to, tools, notObserved, added, persisted,
|
|
86
|
+
removed, notRetested, structure, aria}]}`; each issue is `{tool, ruleID, pathID, startTag,
|
|
87
|
+
what, ordinalSeverity, outcome, count, actIndexes}`.
|
|
88
|
+
- Test acts: `scope` (`page` default or `changed`) and `data.scope = {requested, applied,
|
|
89
|
+
reason, roots, pathIDs, commonRoot, localRules?, pageRules?}`. Job-time only, deleted by
|
|
90
|
+
`pruneCatalog`: `report.scope`, `report.ruleScopeRoots`.
|
|
91
|
+
- Progress events: `checkpointStart` and `checkpointEnd` (with `kind` and `elapsedMs`);
|
|
92
|
+
`actEnd` carries `checkpoint`.
|
|
93
|
+
|
|
94
|
+
### Interaction modality (planned)
|
|
95
|
+
|
|
96
|
+
The act executors live in `procs/actDo.js` and take an `interaction` option
|
|
97
|
+
(`{modality: 'efficient'}` today), recorded on each checkpoint so replay uses the same
|
|
98
|
+
modality. Low-level events (`mousedown`/`mouseup`), emulated human mouse travel and key
|
|
99
|
+
pauses, and pass-to-pass comparisons between modalities are designed to slot in there.
|
|
100
|
+
|
|
101
|
+
## Phases
|
|
102
|
+
|
|
103
|
+
- **Phase 0 (shipped):** act-loop repairs (`next` acts, failed launches, the `url` and
|
|
104
|
+
`state` acts), `procs/actDo.js`, `procs/xPathScript.js`, `catalogPage`, checkpoint 0.
|
|
105
|
+
- **Phase 1 (shipped):** the `checkpoint` act, `procs/checkpoint.js`, replay in the
|
|
106
|
+
launcher, implicit checkpoints, tool gating, the `validation/tests/jobProperties/checkpoint`
|
|
107
|
+
validator with report-level expectations.
|
|
108
|
+
- **Phase 1b (shipped):** the `isolation` job property (or the `ISOLATION` environment
|
|
109
|
+
default): `process` (the default: a child process and browser per test act), `browser`
|
|
110
|
+
(one browser shared by the job's launches, a fresh context per test act, replay), `page`
|
|
111
|
+
(tools run in sequence in the job's process on the live checkpoint page, no replay;
|
|
112
|
+
contaminating testaro rules still get a fresh context with replay). `procs/testAct.js`
|
|
113
|
+
performs a test act for both the child process and the in-process levels; the launcher
|
|
114
|
+
keeps the shared browser and prepares a live page (XPath script or attributes, accessible
|
|
115
|
+
names) without changing its DOM. Recorded in `jobData.isolation`. Only `process` can kill a
|
|
116
|
+
tool that overruns its time limit; the others report the act as timed out and continue.
|
|
117
|
+
- **Phase 2 (shipped):** `report.flow` (`procs/flow.js`, `getFlow(report)`), added at job end
|
|
118
|
+
before pruning when the job has two or more checkpoints, and usable standalone on a stored
|
|
119
|
+
report (the structure diff then covers cited elements only). `flow.checkpoints[k]`
|
|
120
|
+
summarizes each checkpoint; `flow.deltas[k - 1]` compares checkpoints `k - 1` and `k`:
|
|
121
|
+
`added`, `persisted`, and `removed` issues with identity `tool | ruleID | pathID | startTag`
|
|
122
|
+
(box and text excluded, since layout and copy shift without the defect changing; a summary
|
|
123
|
+
instance has an empty element key), compared only for the `tools` that observed both
|
|
124
|
+
checkpoints with a non-prevented act (`notObserved` lists the rest); `structure`, the
|
|
125
|
+
catalog diff (`procs/scope.js`, `getStructureDiff`): `added`, `removed`, and `changed`
|
|
126
|
+
(start tag) XPaths, `textChanged` XPaths, and `roots`, the outermost changed elements (the
|
|
127
|
+
nearest surviving ancestor stands for a removed element; a text change counts only where no
|
|
128
|
+
descendant changed, because the catalog text of an element includes its descendants'); and
|
|
129
|
+
`aria`, a line diff of the ARIA snapshots (`diff`, the jsdiff library yra-monitor also uses;
|
|
130
|
+
at most 500 changed lines recorded, `truncated` otherwise).
|
|
131
|
+
- **Phase 3 (shipped):** `scope: 'page' | 'changed'` on test acts (`actSpecs.js`; a job with a
|
|
132
|
+
changed-scope act must have a checkpoint act). Before such an act the acts loop calls
|
|
133
|
+
`getChangedRoots(report, k)`, which converts the structure roots of the diff between
|
|
134
|
+
checkpoints `k - 1` and `k` to CSS selectors (`/html/body/main[1]/div[2]` becomes
|
|
135
|
+
`html > body > main:nth-of-type(1) > div:nth-of-type(2)`, exact because `getXPath`
|
|
136
|
+
subscripts among same-tag siblings, which is what `nth-of-type` counts) and gives them to
|
|
137
|
+
the tool as the job-time `report.scope` when the tool is in `toolScopes` (`procs/job.js`:
|
|
138
|
+
axe, surea11y, testaro). The act records `data.scope`; when the act cannot be scoped (no
|
|
139
|
+
previous checkpoint, no change, more than `maxRoots` = 50 roots, an XPath with no selector,
|
|
140
|
+
a tool without a root option) it tests the whole page with `applied: false` and a reason,
|
|
141
|
+
and `jobData.warnings` says so. axe takes the roots as its `include` context; surea11y takes
|
|
142
|
+
the nearest common ancestor of the roots (`commonRoot`), since its context is one selector;
|
|
143
|
+
the testaro tool sets `report.ruleScopeRoots` per rule to the roots for a rule with
|
|
144
|
+
`local: true` in `allRules` and to null otherwise, and `doTest` keeps only candidates inside
|
|
145
|
+
a root (`getBasicResult` rules, hover and role, are not local). The conservative first-pass
|
|
146
|
+
classification marks as not local: allHidden, bulk, docType, dupAtt, headEl, headingAmb,
|
|
147
|
+
secHeading, linkAmb, labClash, radioSet, styleDiff, targetsNear, title, motion, role, hover,
|
|
148
|
+
hovInd, focAll, focAndOp, focInd, focVis, tabNav, buttonMenu, elements, textNodes. Whether a
|
|
149
|
+
partial scan is valid is not automated; the author chooses per act, and the rule metadata
|
|
150
|
+
protects the choice. Limits: the diff is by XPath, so inserting one sibling shifts later
|
|
151
|
+
same-tag indexes and the changed set can grow to the container (correct but coarse); a
|
|
152
|
+
changed-scope act finds only defects inside the roots, so `flow` unions the findings of every
|
|
153
|
+
act at a checkpoint rather than trusting one act.
|
|
154
|
+
- **Phase 4 (shipped):** three parts.
|
|
155
|
+
- *Incremental pruning.* When checkpoint `k + 1` is created, `makeCheckpoint` first records
|
|
156
|
+
the structure diff between `k` and `k + 1` on the new checkpoint (job-time
|
|
157
|
+
`checkpoints[k + 1].structure`, which `getChangedRoots` and `getFlow` read instead of
|
|
158
|
+
recomputing, and which the acts loop deletes once `flow` holds it), then calls
|
|
159
|
+
`pruneCheckpoint(report, k)` (`procs/catalog.ts`), which deletes checkpoint `k`'s entries
|
|
160
|
+
that no test act of checkpoint `k` cites and drops its job-time XPath map. No later test
|
|
161
|
+
act can cite checkpoint `k`, since a test act belongs to the latest checkpoint, so the
|
|
162
|
+
temporary report that every test act reads stays bounded by one checkpoint's page plus
|
|
163
|
+
the cited entries of earlier ones. `catalogData.elementCount` is therefore the count of
|
|
164
|
+
entries ever made (`catalogNextIndex`), not the count at pruning time. A single-checkpoint
|
|
165
|
+
job is unaffected.
|
|
166
|
+
- *Selector acts.* The move acts (`button`, `checkbox`, `link`, `radio`, `search`, `select`,
|
|
167
|
+
`text`) accept a `selector` property, any Playwright selector (CSS, `text=`, `role=`,
|
|
168
|
+
`label=`), which replaces the act type's element selector and text matching; `index`
|
|
169
|
+
still picks among matches. A `text` act with `clear: true` replaces the input's value
|
|
170
|
+
instead of appending to it, and a `select` act whose `what` matches no option text falls
|
|
171
|
+
back to Playwright's value-or-label matching. Replay uses the same executors, so
|
|
172
|
+
selector acts replay like the rest.
|
|
173
|
+
- *User-path converter.* `procs/userPath.js` `getUserPathActs({startUrl, actions,
|
|
174
|
+
testActs, scanAtCheckpoints, what})` maps a recorded user path of the shape yra-monitor
|
|
175
|
+
stores (actions `click`, `fill`, `select`, `navigate`, `wait`, `checkpoint`, with
|
|
176
|
+
Playwright selectors) to a job's acts: launch, then one act per action (`button`, `text`
|
|
177
|
+
with `clear`, `select`, `url`, `state: idle` for a pause), and for each `checkpoint`
|
|
178
|
+
action a `checkpoint` act (named from its label, made unique) followed by a copy of each
|
|
179
|
+
requested test act, with `scope: 'changed'` downgraded to `page` at the first checkpoint.
|
|
180
|
+
The `userPath` validator runs the converter's output for a fill, select, checkpoint,
|
|
181
|
+
click, wait, checkpoint, navigate, checkpoint path. yra-monitor's user-path executor still
|
|
182
|
+
replays actions itself and runs a stub axe scan at checkpoints; replacing that with a
|
|
183
|
+
Testaro job built by this converter waits on the ingestion changes below.
|
|
184
|
+
- `flow` and scoped acts: a tool whose acts at a checkpoint were all scoped to changed
|
|
185
|
+
subtrees observed nothing outside them, so an earlier issue of that tool whose element
|
|
186
|
+
lies outside the roots is listed in the delta as `notRetested`, not `removed`.
|
|
187
|
+
|
|
188
|
+
## Relationship to yra-monitor
|
|
189
|
+
|
|
190
|
+
yra-monitor's DOM-difference feature captures outerHTML, a screenshot, element positions
|
|
191
|
+
and the ARIA snapshot per URL per scan and diffs them across scans. Checkpoints capture the
|
|
192
|
+
same artifacts per page state inside one job. The cross-scan history and comparison UI stay
|
|
193
|
+
downstream; a later consolidation can store Testaro's checkpoint-0 artifacts instead of
|
|
194
|
+
launching a separate capture browser.
|
|
195
|
+
|
|
196
|
+
Until yra-monitor carries `checkpoint` and the checkpoint URL on stored rows, adds a
|
|
197
|
+
checkpoint dimension to its unique-issue key and auto-resolve scoping, and stores one
|
|
198
|
+
screenshot per checkpoint, multi-checkpoint reports must not be fed through its scan
|
|
199
|
+
ingestion path.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Certainty in the standard result: the `outcome` field
|
|
2
|
+
|
|
3
|
+
**Status:** Phase A shipped in Testaro 78.1.0 (2026-09-01). Phases B–D are recommendations for
|
|
4
|
+
the Testilo fork and yra-monitor, and for a later Testaro major version.
|
|
5
|
+
**Companion:** yra-monitor `docs/plans/engine-candidacy-pipeline.md` (Stage-1 instance policy,
|
|
6
|
+
Stage-3b taxonomy mapping).
|
|
7
|
+
|
|
8
|
+
## Why
|
|
9
|
+
|
|
10
|
+
Rule engines report two different things about a finding: how bad it is (impact) and whether
|
|
11
|
+
they are sure it is a defect (certainty). Every Testaro adapter used to fold certainty into
|
|
12
|
+
`ordinalSeverity`, and each did it differently:
|
|
13
|
+
|
|
14
|
+
| tool | uncertain native value | ordinal | certain native value | ordinal |
|
|
15
|
+
| --- | --- | --- | --- | --- |
|
|
16
|
+
| axe, pour, surea11y | incomplete / cantTell | 0–1 | violation / fail | 2–3 |
|
|
17
|
+
| htmlcs | Warning | 0 | Error | 2 |
|
|
18
|
+
| ibm | recommendation | 0 | violation and potential violation | 2 |
|
|
19
|
+
| alfa | cantTell (rule ID rewritten) | 0 | failed | 2 |
|
|
20
|
+
| aslint | warning | 1 | error | 2 |
|
|
21
|
+
| qualWeb act-rules | warning | 1 | failed | 3 |
|
|
22
|
+
| qualWeb best-practices | warning | 0 | failed | 1 |
|
|
23
|
+
| wave | alert | 0 | error, contrast | 3 |
|
|
24
|
+
| ed11y | dismissable warning | 0 | error | 2 |
|
|
25
|
+
| nuVal, nuVnu | info | 0 | error | 3 |
|
|
26
|
+
|
|
27
|
+
So severity 0 meant "definitely minor" for some tools and "possibly critical" for others, and
|
|
28
|
+
nothing downstream could recover certainty except by the heuristic "ordinal 2 or above means
|
|
29
|
+
asserted". The ACT validation harness had to read native results to score sensitivity and
|
|
30
|
+
specificity by certainty band. The Testilo issue classification (tic) grew 57 `*Risk` issues to
|
|
31
|
+
hold uncertain findings at weight 1, and its scorer counted an axe `incomplete` contrast finding
|
|
32
|
+
exactly like an asserted violation because it maps rule IDs only.
|
|
33
|
+
|
|
34
|
+
The two new candidate engines make the loss visible: pour separates `violations`, `incomplete`,
|
|
35
|
+
and `manualReview`; surea11y grades per occurrence (`fail` / `cantTell`) and publishes six
|
|
36
|
+
machine-readable `uncertainty` codes with a `needed` sentence saying what a reviewer must
|
|
37
|
+
determine.
|
|
38
|
+
|
|
39
|
+
## The model
|
|
40
|
+
|
|
41
|
+
Four dimensions, kept separate:
|
|
42
|
+
|
|
43
|
+
| dimension | meaning | where it lives |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| outcome (per instance) | did the engine assert a failure, or could it not tell | standard instance, set by the adapter |
|
|
46
|
+
| rule certainty (per rule) | is the check deterministic, or a heuristic that can only say "suspicious" | tic tool-rule entry, as an override |
|
|
47
|
+
| impact (per instance) | how bad for a user | `ordinalSeverity`, once it means impact only |
|
|
48
|
+
| issue weight (per issue) | severity of the defect class | tic `weight` |
|
|
49
|
+
|
|
50
|
+
Certainty exists at two levels because engines both under-assert (axe `incomplete`) and
|
|
51
|
+
over-assert (a heuristic reported as an error). The instance level is Testaro's job; the rule
|
|
52
|
+
level is the taxonomy's.
|
|
53
|
+
|
|
54
|
+
## Phase A: Testaro 78.1.0 (shipped)
|
|
55
|
+
|
|
56
|
+
Every standard instance now has:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
{
|
|
60
|
+
ruleID, what, ordinalSeverity, count, catalogIndex, // unchanged
|
|
61
|
+
outcome: 'failed' | 'cantTell', // required; ACT Rules Format vocabulary
|
|
62
|
+
uncertainty: 'not-computable' | 'judgement-required' | 'runtime-dependent'
|
|
63
|
+
| 'spec-only' | 'equivalence-unknown' | 'out-of-scope', // optional, cantTell only
|
|
64
|
+
needed: '<what a reviewer must determine>' // optional, cantTell only, ≤ 300 chars
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
and `standardResult.outcomeTotals = {failed, cantTell}` (count-weighted).
|
|
69
|
+
|
|
70
|
+
- `procs/standard.ts` is the one place the shape is built (`getStandardResult`, `getInstance`,
|
|
71
|
+
`addInstance`, `pushInstance`). Adapters that compute `totals` from native counts (ibm, htmlcs,
|
|
72
|
+
ed11y, wave, testaro) use `pushInstance`; the rest use `addInstance`.
|
|
73
|
+
- `ordinalSeverity` and `totals` are unchanged for every tool. The golden diff on the pour and
|
|
74
|
+
surea11y validation jobs is identical after stripping the new fields.
|
|
75
|
+
- Per-tool mapping: axe/pour/surea11y violations → `failed`, incomplete → `cantTell`; htmlcs
|
|
76
|
+
Warning → `cantTell`; ibm potential violation, potential recommendation, and manual →
|
|
77
|
+
`cantTell`; alfa `cantTell` → `cantTell` (the adapter no longer rewrites the rule ID to
|
|
78
|
+
`cantTell` / `cantTellTextContrast` or prefixes `what`; r66 and r69 carry
|
|
79
|
+
`judgement-required`); aslint warning, qualWeb warning, wave alert, nuVal/nuVnu info →
|
|
80
|
+
`cantTell`; ed11y dismissable warning → `cantTell` with `judgement-required`.
|
|
81
|
+
- surea11y forwards `uncertainty.code` and `uncertainty.needed` (occurrence first, then check).
|
|
82
|
+
Its `confidence` value is dropped as non-portable.
|
|
83
|
+
- Testaro's own rules default to `failed`. An `allRules` entry may set `outcome` and
|
|
84
|
+
`uncertainty` (`allCaps` is `cantTell` / `judgement-required`, consistent with the earlier
|
|
85
|
+
ruling that AI confidence must not be encoded in `ordinalSeverity`). A violation description
|
|
86
|
+
may carry a prefix: `2:` severity only, `2?:` severity plus `cantTell`, `?:` `cantTell` at the
|
|
87
|
+
rule's default severity. `getBasicResult` accepts `outcome`, `uncertainty`, and `needed` on
|
|
88
|
+
each violation.
|
|
89
|
+
- `validation/act/capture.js` records `outcomeTotals` and per-instance `outcome` and
|
|
90
|
+
`uncertainty`, and marks a row as an error if any instance lacks a valid outcome.
|
|
91
|
+
|
|
92
|
+
Why `outcome` and not `certainty`: the ACT Rules Format and EARL already use `failed` /
|
|
93
|
+
`cantTell`, the ACT testcase feed the harness scores against uses `failed`, and a closed
|
|
94
|
+
two-value enum keeps consumer logic total. A graded `certainty` would invite scales no two
|
|
95
|
+
engines share and collide with surea11y's separate `confidence`.
|
|
96
|
+
|
|
97
|
+
Why `needed` is its own field: `what` is an identity key downstream (unique-issue rows, tsp
|
|
98
|
+
description sampling). Appending guidance to it would fracture identity across pages.
|
|
99
|
+
|
|
100
|
+
## Phase B: Testilo fork (recommended)
|
|
101
|
+
|
|
102
|
+
Tool-rule entries in the tic gain optional properties; issues gain two:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
tools: { htmlcs: { 'W-AAA.1_4_3.G18.BgImage': {
|
|
106
|
+
variable: false, quality: 1, what: '…',
|
|
107
|
+
outcome: 'cantTell', // override; 'cantTell' is the common case
|
|
108
|
+
uncertainty: 'not-computable', // rule-level default; the instance value wins
|
|
109
|
+
review: '…', // fallback "what to confirm" when the engine gives no `needed`
|
|
110
|
+
ignore: 'invalid' // replaces prose [invalid]/[irrelevant]/[duplicative]/[unreliable]
|
|
111
|
+
}}}
|
|
112
|
+
issue: { how: '<one imperative fix sentence>', group: 'landmarkNotTop' }
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- Precedence: `rule.outcome` > `instance.outcome` > legacy inference > `failed`. Legacy
|
|
116
|
+
inference (reports without `outcome`) is per tool: axe, pour, surea11y, htmlcs, ibm, wave,
|
|
117
|
+
qualWeb → `ordinalSeverity >= 2 ? failed : cantTell`; alfa → rule ID starts with `cantTell`;
|
|
118
|
+
testaro, ed11y, aslint, nuVal, nuVnu → `failed`.
|
|
119
|
+
- `[speculative]` becomes `outcome: 'cantTell'`; the other bracket tags become `ignore`. The
|
|
120
|
+
`ignorable` pseudo-issue dissolves into per-rule `ignore`. A loader assertion rejects any
|
|
121
|
+
`what` still carrying a bracket tag.
|
|
122
|
+
- Consolidate the 57 `*Risk` issues: merge into the base issue when one exists with the same fix
|
|
123
|
+
(7 name twins plus about 11 semantic twins such as contrastRisk → contrastAA/AAA by level);
|
|
124
|
+
rename (drop `Risk`) when the issue is the sole home of a defect class with a concrete fix
|
|
125
|
+
(pseudoHeadingRisk → pseudoHeading, which already holds axe `p-as-heading` and takes pour's
|
|
126
|
+
at `failed`; eventKeyboardRisk → eventKeyboard; labelConfusionRisk → labelPlacement with every
|
|
127
|
+
rule `cantTell` / `judgement-required`); keep three page-environment advisories
|
|
128
|
+
(applicationRisk, browserSupportRisk, noScriptRisk) with every rule `cantTell`. Export a
|
|
129
|
+
`migrations` map (`{contrastRisk: 'contrastAA', …}`) for downstream issue-ID rewrites.
|
|
130
|
+
- Scoring: `cantTellWeight = 0.25` per instance inside the quality-weighted count (a weight-1
|
|
131
|
+
Risk issue merged into a weight-4 base is score-neutral). Element-aware dedup: an element's
|
|
132
|
+
outcome is `failed` if any tool says so, corroborated when more than one does;
|
|
133
|
+
`effectiveCount = max(old per-tool max with the factor applied, element-union count)`.
|
|
134
|
+
Output stays additive (`instanceCounts` numeric; add `outcomeCounts`, `elementCounts`,
|
|
135
|
+
`details.elements`; `details.element` byte-identical).
|
|
136
|
+
- Granularity principle: one tic issue = one remediation class = one WCAG success criterion ×
|
|
137
|
+
one fix. `ruleID` and `what` carry the diagnosis; certainty is never a reason to split an
|
|
138
|
+
issue. Families get `group`; element-conditioned splits (pour `link-name` → image link vs text
|
|
139
|
+
link) are done in the adapter with a `.`-suffixed faceted rule ID, two tic entries each.
|
|
140
|
+
|
|
141
|
+
## Phase C: yra-monitor (recommended)
|
|
142
|
+
|
|
143
|
+
Types, `tic-manager` `resolveOutcome`, scoring-service passthrough, standardized rows and DB
|
|
144
|
+
columns (`outcome`, `uncertainty`, `needed`) with a promote-only conflict rule (`failed` wins),
|
|
145
|
+
issue-ID rewrite from `migrations`, a "Needs review" badge and filter, and for `cantTell` rows
|
|
146
|
+
the text `instance.needed ?? rule.review ?? 'Manual review required'` under a "To confirm"
|
|
147
|
+
heading.
|
|
148
|
+
|
|
149
|
+
## Phase D: Testaro 79 (recommended)
|
|
150
|
+
|
|
151
|
+
Redefine `ordinalSeverity` as impact only: 0 minor, 1 moderate, 2 serious, 3 critical, which is
|
|
152
|
+
what yra-monitor already labels it as. `totals` keeps its shape but its composition changes
|
|
153
|
+
(an axe incomplete + critical finding moves from `totals[1]` to `totals[3]`). Safe only after
|
|
154
|
+
Phases B and C, when every consumer reads `outcome`.
|
|
155
|
+
|
|
156
|
+
## Open decisions
|
|
157
|
+
|
|
158
|
+
1. `cantTellWeight = 0.25`: confirm against Stage-4 precision on surea11y's `cantTell` band.
|
|
159
|
+
2. The semantic Risk merges need a "same fix?" pass; videoCaptionRisk → videoNoText and
|
|
160
|
+
fieldSetRisk → fieldSetMissing are the least certain.
|
|
161
|
+
3. Weights for renamed heuristic issues once they can carry a `failed` band.
|
|
162
|
+
4. The alfa rule-ID change is visible to the current Testilo now: r66/r69 `cantTell` instances
|
|
163
|
+
map to contrastAAA/contrastAA at full weight until the scorer reads `outcome`. Pin Testaro
|
|
164
|
+
78.1 in the worker only together with the Phase-B Testilo.
|
|
165
|
+
5. Whether `unique_issues.severity` should roll up (`GREATEST`) on conflict.
|
package/env.example
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
WAVE_KEY=__placeholder__
|
|
3
3
|
# You can get an Anthropic API key at https://console.anthropic.com/.
|
|
4
4
|
ANTHROPIC_API_KEY=__placeholder__
|
|
5
|
-
# Name to identify this Testaro instance to a server.
|
|
6
|
-
AGENT=__placeholder__
|
|
7
5
|
# Whether to make the browser visible (normally false).
|
|
8
6
|
HEADED_BROWSER=false
|
|
9
7
|
# Whether to output even forked-job logging to the primary log (normally false).
|
|
@@ -12,23 +10,44 @@ DEBUG=false
|
|
|
12
10
|
WAITS=0
|
|
13
11
|
# See https://nodejs.org/api/cli.html#options for all permitted options.
|
|
14
12
|
NODE_OPTIONS='--trace-uncaught --trace-warnings'
|
|
15
|
-
#
|
|
13
|
+
# Whether to suppress a routine warning from QualWeb.
|
|
16
14
|
PUPPETEER_DISABLE_HEADLESS_WARNING=true
|
|
17
|
-
# Replace the next 2 placeholders with http://localhost:3000 if Kilotest and Testaro are on the same host or the URL of the server (e.g., https://kilotest.com) if they are on different hosts.
|
|
18
|
-
# URL to poll for available jobs when watching the network.
|
|
19
|
-
NETWATCH_URL_JOB=__placeholder__/api/testaro-agent/job
|
|
20
|
-
# URL to report results to when watching the network.
|
|
21
|
-
NETWATCH_URL_REPORT=__placeholder__/api/testaro-agent/report
|
|
22
|
-
# Password of this Testaro agent
|
|
23
|
-
NETWATCH_URL_AUTH=__placeholder__
|
|
24
15
|
# Directory (relative to project root) to watch for jobs.
|
|
25
16
|
JOBDIR=__placeholder__
|
|
26
17
|
# Directory (relative to project root) to write reports to when watching a directory for jobs.
|
|
27
18
|
REPORTDIR=__placeholder__
|
|
28
19
|
# Multiplier for time limits (normally 1).
|
|
29
20
|
TIMEOUT_MULTIPLIER=1
|
|
21
|
+
# Default isolation of test acts when a job does not specify one: process (a child process and
|
|
22
|
+
# browser per test act), browser (one browser per job, a fresh context per test act), or page
|
|
23
|
+
# (test acts share the live page of the current checkpoint). See docs/checkpoint-scanning.md.
|
|
24
|
+
ISOLATION=process
|
|
30
25
|
# Whether to abort the job when launch retries are exhausted or a test act crashes (default false).
|
|
31
26
|
ABORT_ASSERTIVELY=false
|
|
27
|
+
|
|
28
|
+
# Deployment options. A job property of the same meaning overrides each of these.
|
|
29
|
+
|
|
30
|
+
# Load state a navigation waits for before tools run: networkidle (default), load, or
|
|
31
|
+
# domcontentloaded. Retries relax it one step. networkidle never arrives on pages whose
|
|
32
|
+
# network never quiets (beacons, polling), so a deployment scanning such pages may prefer load.
|
|
33
|
+
NAV_WAIT_UNTIL=networkidle
|
|
34
|
+
# Navigation time limit in milliseconds (default 10000).
|
|
35
|
+
NAV_TIMEOUT=10000
|
|
36
|
+
# Whether a 4xx response (other than 408 and 429) ends launch retries at once (default false).
|
|
37
|
+
NAV_FAIL_FAST_4XX=false
|
|
38
|
+
# Scanner identity sent as the X-YRA-Scanner request header, so firewalls can recognize the
|
|
39
|
+
# scanner by a stable value (default none; jobs may set scannerId).
|
|
40
|
+
SCANNER_ID=__placeholder__
|
|
41
|
+
# Whether to scroll the full page after navigation so lazily loaded content is present before
|
|
42
|
+
# cataloguing, imaging, and each tool (default false; jobs may set scroll).
|
|
43
|
+
PRESCAN_SCROLL=false
|
|
44
|
+
# Launch retries per rule of the testaro tool, which relaunches per contaminating rule
|
|
45
|
+
# (default 2; raise for non-Chromium browsers, which navigate flakily on relaunch).
|
|
46
|
+
TESTARO_RULE_RETRIES=2
|
|
47
|
+
# Whether the qualWeb tool's browser runs stealth evasions and blocks ads and trackers
|
|
48
|
+
# (default true; a qualWeb test act may set stealth and adBlock).
|
|
49
|
+
QUALWEB_STEALTH=true
|
|
50
|
+
QUALWEB_ADBLOCK=true
|
|
32
51
|
# URL of any non-default (e.g., self-hosted) Nu Html Checker API (see ghcr.io/validator/validator).
|
|
33
52
|
TESTARO_NU_URL=__placeholder__
|
|
34
53
|
# Whether to launch the Playwright chromium browser without its sandbox (normally false).
|
|
@@ -37,6 +56,21 @@ TESTARO_NU_URL=__placeholder__
|
|
|
37
56
|
# or run containers with a seccomp profile that permits user-namespace cloning.
|
|
38
57
|
TESTARO_CHROMIUM_NO_SANDBOX=false
|
|
39
58
|
|
|
59
|
+
# netWatch variables
|
|
60
|
+
|
|
61
|
+
# Replace the next 2 placeholders with the URL of the server (e.g., http://localhost:3000 or https://kilotest.com), including the path required by the server.
|
|
62
|
+
# URL to poll for available jobs.
|
|
63
|
+
NETWATCH_URL_JOB=__placeholder__
|
|
64
|
+
# URL to send a job report.
|
|
65
|
+
NETWATCH_URL_REPORT=__placeholder__
|
|
66
|
+
# Type of authentication (none, pathBody, or header) required by the server.
|
|
67
|
+
NETWATCH_AUTH_TYPE=__placeholder__
|
|
68
|
+
# ID of this instance. Recorded in reports as sources.agent and used by header
|
|
69
|
+
# authentication (where it must not contain a colon).
|
|
70
|
+
NETWATCH_WORKER_ID=__placeholder__
|
|
71
|
+
# Password of this instance. Required when NETWATCH_AUTH_TYPE is pathBody or header.
|
|
72
|
+
NETWATCH_WORKER_SECRET=__placeholder__
|
|
73
|
+
|
|
40
74
|
## License
|
|
41
75
|
|
|
42
76
|
# © 2021–2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
@@ -0,0 +1,101 @@
|
|
|
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
|
+
/*
|
|
11
|
+
eslint.config.mjs
|
|
12
|
+
Flat ESLint configuration, replacing .eslintrc.json (unsupported since ESLint 9).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// IMPORTS
|
|
16
|
+
|
|
17
|
+
import js from '@eslint/js';
|
|
18
|
+
import globals from 'globals';
|
|
19
|
+
import {defineConfig, globalIgnores} from 'eslint/config';
|
|
20
|
+
|
|
21
|
+
// CONFIGURATION
|
|
22
|
+
|
|
23
|
+
export default defineConfig([
|
|
24
|
+
globalIgnores([
|
|
25
|
+
// Build output.
|
|
26
|
+
'dist/',
|
|
27
|
+
// Vendored HTML CodeSniffer bundle; not to be reformatted.
|
|
28
|
+
'htmlcs/HTMLCS.js',
|
|
29
|
+
// Vendored Editoria11y bundles.
|
|
30
|
+
'ed11y/',
|
|
31
|
+
// Vendored SureA11y standalone browser bundle (MPL-2.0; not to be modified).
|
|
32
|
+
'surea11y/surea11y.browser.js',
|
|
33
|
+
// Vendored Pour Engine bundle, built with esbuild (see pour/README.md).
|
|
34
|
+
'pour/pour.min.js',
|
|
35
|
+
// Files emitted by tsc from TypeScript sources (issue #73); lint the .ts sources instead.
|
|
36
|
+
'types.js',
|
|
37
|
+
'procs/catalog.js',
|
|
38
|
+
'procs/standard.js',
|
|
39
|
+
'procs/testaro.js',
|
|
40
|
+
'procs/xPath.js',
|
|
41
|
+
'testaro/*.js',
|
|
42
|
+
'tests/*.js',
|
|
43
|
+
'**/*.d.ts',
|
|
44
|
+
// Validation fixtures, some of which are intentionally defective.
|
|
45
|
+
'validation/tests/targets/'
|
|
46
|
+
]),
|
|
47
|
+
{
|
|
48
|
+
files: ['**/*.js'],
|
|
49
|
+
extends: [js.configs.recommended],
|
|
50
|
+
languageOptions: {
|
|
51
|
+
ecmaVersion: 2021,
|
|
52
|
+
sourceType: 'commonjs',
|
|
53
|
+
globals: {
|
|
54
|
+
...globals.browser,
|
|
55
|
+
...globals.node
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
rules: {
|
|
59
|
+
indent: [
|
|
60
|
+
'error',
|
|
61
|
+
2,
|
|
62
|
+
{
|
|
63
|
+
MemberExpression: 0,
|
|
64
|
+
ObjectExpression: 'first'
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
'linebreak-style': [
|
|
68
|
+
'error',
|
|
69
|
+
'unix'
|
|
70
|
+
],
|
|
71
|
+
quotes: [
|
|
72
|
+
'error',
|
|
73
|
+
'single'
|
|
74
|
+
],
|
|
75
|
+
semi: [
|
|
76
|
+
'error',
|
|
77
|
+
'always'
|
|
78
|
+
],
|
|
79
|
+
'no-use-before-define': [
|
|
80
|
+
'error'
|
|
81
|
+
],
|
|
82
|
+
'brace-style': [
|
|
83
|
+
'error',
|
|
84
|
+
'stroustrup'
|
|
85
|
+
],
|
|
86
|
+
/*
|
|
87
|
+
Transitional demotions to warnings, so linting can gate changes now.
|
|
88
|
+
These rules have pre-existing violations, some of them latent defects.
|
|
89
|
+
Each is to be restored to an error once its violations are repaired.
|
|
90
|
+
*/
|
|
91
|
+
'no-async-promise-executor': 'warn',
|
|
92
|
+
'no-const-assign': 'warn',
|
|
93
|
+
'no-constant-binary-expression': 'warn',
|
|
94
|
+
'no-empty': 'warn',
|
|
95
|
+
'no-undef': 'warn',
|
|
96
|
+
'no-unused-vars': 'warn',
|
|
97
|
+
'no-use-before-define': 'warn',
|
|
98
|
+
'no-useless-assignment': 'warn'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
]);
|