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.
Files changed (361) hide show
  1. package/.gitattributes +94 -0
  2. package/.github/workflows/ci.yml +58 -0
  3. package/.github/workflows/publish.yml +49 -0
  4. package/.github/workflows/typescript.yml +35 -0
  5. package/AGENTS.md +2 -2
  6. package/CLAUDE.md +25 -19
  7. package/CONTAINERS.md +10 -6
  8. package/CONTRIBUTING.md +29 -3
  9. package/Dockerfile +1 -1
  10. package/README.md +75 -20
  11. package/UPGRADES.md +4 -0
  12. package/actSpecs-doc.md +13 -1
  13. package/actSpecs.js +29 -5
  14. package/call.js +6 -5
  15. package/docker-compose.yml +8 -2
  16. package/docs/checkpoint-scanning.md +199 -0
  17. package/docs/standard-result-outcome.md +165 -0
  18. package/env.example +44 -10
  19. package/eslint.config.mjs +101 -0
  20. package/netWatch.js +373 -222
  21. package/package.json +46 -31
  22. package/pour/README.md +47 -0
  23. package/pour/pour.min.js +8 -0
  24. package/procs/actDo.js +808 -0
  25. package/procs/catalog.d.ts +17 -0
  26. package/procs/catalog.js +313 -221
  27. package/procs/catalog.ts +398 -0
  28. package/procs/checkpoint.js +110 -0
  29. package/procs/config.d.ts +35 -0
  30. package/procs/config.js +63 -0
  31. package/procs/dateTime.js +2 -1
  32. package/procs/doActs.js +442 -849
  33. package/procs/doTestAct.js +27 -121
  34. package/procs/flow.js +221 -0
  35. package/procs/generateRuleRegistry.js +94 -0
  36. package/procs/getSource.d.ts +26 -0
  37. package/procs/getSource.js +1 -0
  38. package/procs/job.js +116 -1
  39. package/procs/launch.d.ts +40 -0
  40. package/procs/launch.js +239 -108
  41. package/procs/nu.d.ts +48 -0
  42. package/procs/nu.js +1 -0
  43. package/procs/scope.js +196 -0
  44. package/procs/shoot.d.ts +33 -0
  45. package/procs/shoot.js +3 -1
  46. package/procs/standard.d.ts +18 -0
  47. package/procs/standard.js +83 -0
  48. package/procs/standard.ts +126 -0
  49. package/procs/testAct.js +145 -0
  50. package/procs/testaro.d.ts +20 -0
  51. package/procs/testaro.js +237 -189
  52. package/procs/testaro.ts +321 -0
  53. package/procs/userPath.js +126 -0
  54. package/procs/xPath.d.ts +4 -0
  55. package/procs/xPath.js +82 -64
  56. package/procs/xPath.ts +106 -0
  57. package/procs/xPathScript.d.ts +6 -0
  58. package/procs/xPathScript.js +70 -0
  59. package/run.js +3 -2
  60. package/surea11y/README.md +44 -0
  61. package/surea11y/surea11y.browser.js +14 -0
  62. package/testaro/adbID.d.ts +3 -0
  63. package/testaro/adbID.js +39 -41
  64. package/testaro/adbID.ts +66 -0
  65. package/testaro/allCapStyle.d.ts +3 -0
  66. package/testaro/allCapStyle.js +32 -34
  67. package/testaro/allCapStyle.ts +55 -0
  68. package/testaro/allCaps.d.ts +16 -0
  69. package/testaro/allCaps.js +179 -151
  70. package/testaro/allCaps.ts +228 -0
  71. package/testaro/allHidden.d.ts +13 -0
  72. package/testaro/allHidden.js +30 -30
  73. package/testaro/allHidden.ts +50 -0
  74. package/testaro/allSlanted.d.ts +3 -0
  75. package/testaro/allSlanted.js +31 -33
  76. package/testaro/allSlanted.ts +54 -0
  77. package/testaro/altScheme.d.ts +3 -0
  78. package/testaro/altScheme.js +28 -30
  79. package/testaro/altScheme.ts +50 -0
  80. package/testaro/attVal.d.ts +3 -0
  81. package/testaro/attVal.js +21 -21
  82. package/testaro/attVal.ts +52 -0
  83. package/testaro/autocomplete.d.ts +3 -0
  84. package/testaro/autocomplete.js +60 -75
  85. package/testaro/autocomplete.ts +101 -0
  86. package/testaro/bulk.d.ts +13 -0
  87. package/testaro/bulk.js +33 -33
  88. package/testaro/bulk.ts +55 -0
  89. package/testaro/buttonMenu.d.ts +9 -0
  90. package/testaro/buttonMenu.js +318 -319
  91. package/testaro/buttonMenu.ts +391 -0
  92. package/testaro/captionLoc.d.ts +3 -0
  93. package/testaro/captionLoc.js +18 -20
  94. package/testaro/captionLoc.ts +40 -0
  95. package/testaro/datalistRef.d.ts +3 -0
  96. package/testaro/datalistRef.js +34 -36
  97. package/testaro/datalistRef.ts +55 -0
  98. package/testaro/distortion.d.ts +3 -0
  99. package/testaro/distortion.js +58 -26
  100. package/testaro/distortion.ts +81 -0
  101. package/testaro/docType.d.ts +15 -0
  102. package/testaro/docType.js +26 -25
  103. package/testaro/docType.ts +45 -0
  104. package/testaro/dupAtt.d.ts +16 -0
  105. package/testaro/dupAtt.js +114 -104
  106. package/testaro/dupAtt.ts +144 -0
  107. package/testaro/elements.d.ts +6 -0
  108. package/testaro/elements.js +153 -153
  109. package/testaro/elements.ts +215 -0
  110. package/testaro/embAc.d.ts +3 -0
  111. package/testaro/embAc.js +20 -20
  112. package/testaro/embAc.ts +40 -0
  113. package/testaro/focAll.d.ts +13 -0
  114. package/testaro/focAll.js +186 -191
  115. package/testaro/focAll.ts +217 -0
  116. package/testaro/focAndOp.d.ts +3 -0
  117. package/testaro/focAndOp.js +101 -104
  118. package/testaro/focAndOp.ts +128 -0
  119. package/testaro/focInd.d.ts +3 -0
  120. package/testaro/focInd.js +65 -66
  121. package/testaro/focInd.ts +96 -0
  122. package/testaro/focVis.d.ts +3 -0
  123. package/testaro/focVis.js +29 -30
  124. package/testaro/focVis.ts +52 -0
  125. package/testaro/headEl.d.ts +10 -0
  126. package/testaro/headEl.js +62 -62
  127. package/testaro/headEl.ts +82 -0
  128. package/testaro/headingAmb.d.ts +3 -0
  129. package/testaro/headingAmb.js +55 -63
  130. package/testaro/headingAmb.ts +72 -0
  131. package/testaro/hovInd.d.ts +12 -0
  132. package/testaro/hovInd.js +164 -130
  133. package/testaro/hovInd.ts +199 -0
  134. package/testaro/hover.d.ts +3 -0
  135. package/testaro/hover.js +156 -126
  136. package/testaro/hover.ts +154 -0
  137. package/testaro/hr.d.ts +3 -0
  138. package/testaro/hr.js +15 -17
  139. package/testaro/hr.ts +36 -0
  140. package/testaro/imageLink.d.ts +3 -0
  141. package/testaro/imageLink.js +18 -20
  142. package/testaro/imageLink.ts +42 -0
  143. package/testaro/labClash.d.ts +3 -0
  144. package/testaro/labClash.js +32 -33
  145. package/testaro/labClash.ts +54 -0
  146. package/testaro/legendLoc.d.ts +3 -0
  147. package/testaro/legendLoc.js +18 -20
  148. package/testaro/legendLoc.ts +42 -0
  149. package/testaro/lineHeight.d.ts +3 -0
  150. package/testaro/lineHeight.js +44 -48
  151. package/testaro/lineHeight.ts +70 -0
  152. package/testaro/linkAmb.d.ts +7 -0
  153. package/testaro/linkAmb.js +80 -80
  154. package/testaro/linkAmb.ts +105 -0
  155. package/testaro/linkExt.d.ts +3 -0
  156. package/testaro/linkExt.js +14 -16
  157. package/testaro/linkExt.ts +35 -0
  158. package/testaro/linkOldAtt.d.ts +3 -0
  159. package/testaro/linkOldAtt.js +26 -28
  160. package/testaro/linkOldAtt.ts +48 -0
  161. package/testaro/linkTo.d.ts +3 -0
  162. package/testaro/linkTo.js +23 -22
  163. package/testaro/linkTo.ts +43 -0
  164. package/testaro/linkUl.d.ts +3 -0
  165. package/testaro/linkUl.js +31 -34
  166. package/testaro/linkUl.ts +54 -0
  167. package/testaro/miniText.d.ts +3 -0
  168. package/testaro/miniText.js +42 -44
  169. package/testaro/miniText.ts +68 -0
  170. package/testaro/motion.d.ts +10 -0
  171. package/testaro/motion.js +93 -96
  172. package/testaro/motion.ts +125 -0
  173. package/testaro/nonTable.d.ts +3 -0
  174. package/testaro/nonTable.js +40 -45
  175. package/testaro/nonTable.ts +66 -0
  176. package/testaro/optRoleSel.d.ts +3 -0
  177. package/testaro/optRoleSel.js +17 -19
  178. package/testaro/optRoleSel.ts +41 -0
  179. package/testaro/phOnly.d.ts +3 -0
  180. package/testaro/phOnly.js +19 -21
  181. package/testaro/phOnly.ts +43 -0
  182. package/testaro/pseudoP.d.ts +3 -0
  183. package/testaro/pseudoP.js +37 -38
  184. package/testaro/pseudoP.ts +59 -0
  185. package/testaro/radioSet.d.ts +3 -0
  186. package/testaro/radioSet.js +58 -59
  187. package/testaro/radioSet.ts +79 -0
  188. package/testaro/registry.d.ts +63 -0
  189. package/testaro/registry.js +67 -0
  190. package/testaro/registry.ts +141 -0
  191. package/testaro/role.d.ts +3 -0
  192. package/testaro/role.js +28 -29
  193. package/testaro/role.ts +53 -0
  194. package/testaro/secHeading.d.ts +3 -0
  195. package/testaro/secHeading.js +31 -33
  196. package/testaro/secHeading.ts +53 -0
  197. package/testaro/styleDiff.d.ts +25 -0
  198. package/testaro/styleDiff.js +249 -252
  199. package/testaro/styleDiff.ts +303 -0
  200. package/testaro/tabNav.d.ts +33 -0
  201. package/testaro/tabNav.js +273 -342
  202. package/testaro/tabNav.ts +454 -0
  203. package/testaro/targetsNear.d.ts +9 -0
  204. package/testaro/targetsNear.js +131 -132
  205. package/testaro/targetsNear.ts +160 -0
  206. package/testaro/textNodes.d.ts +6 -0
  207. package/testaro/textNodes.js +139 -135
  208. package/testaro/textNodes.ts +185 -0
  209. package/testaro/textSem.d.ts +3 -0
  210. package/testaro/textSem.js +26 -28
  211. package/testaro/textSem.ts +47 -0
  212. package/testaro/title.d.ts +9 -0
  213. package/testaro/title.js +16 -13
  214. package/testaro/title.ts +31 -0
  215. package/testaro/titledEl.d.ts +3 -0
  216. package/testaro/titledEl.js +16 -18
  217. package/testaro/titledEl.ts +38 -0
  218. package/testaro/zIndex.d.ts +3 -0
  219. package/testaro/zIndex.js +20 -22
  220. package/testaro/zIndex.ts +42 -0
  221. package/tests/alfa.d.ts +45 -0
  222. package/tests/alfa.js +139 -141
  223. package/tests/alfa.ts +214 -0
  224. package/tests/aslint.d.ts +33 -0
  225. package/tests/aslint.js +273 -249
  226. package/tests/aslint.ts +301 -0
  227. package/tests/axe.d.ts +27 -0
  228. package/tests/axe.js +200 -200
  229. package/tests/axe.ts +277 -0
  230. package/tests/ed11y.d.ts +28 -0
  231. package/tests/ed11y.js +142 -99
  232. package/tests/ed11y.ts +178 -0
  233. package/tests/htmlcs.d.ts +21 -0
  234. package/tests/htmlcs.js +175 -140
  235. package/tests/htmlcs.ts +181 -0
  236. package/tests/ibm.d.ts +52 -0
  237. package/tests/ibm.js +165 -166
  238. package/tests/ibm.ts +251 -0
  239. package/tests/nuVal.d.ts +13 -0
  240. package/tests/nuVal.js +107 -112
  241. package/tests/nuVal.ts +145 -0
  242. package/tests/nuVnu.d.ts +15 -0
  243. package/tests/nuVnu.js +142 -111
  244. package/tests/nuVnu.ts +144 -0
  245. package/tests/pour.d.ts +31 -0
  246. package/tests/pour.js +242 -0
  247. package/tests/pour.ts +273 -0
  248. package/tests/qualWeb.d.ts +39 -0
  249. package/tests/qualWeb.js +303 -272
  250. package/tests/qualWeb.ts +415 -0
  251. package/tests/surea11y.d.ts +33 -0
  252. package/tests/surea11y.js +288 -0
  253. package/tests/surea11y.ts +334 -0
  254. package/tests/testaro.d.ts +25 -0
  255. package/tests/testaro.js +746 -652
  256. package/tests/testaro.ts +862 -0
  257. package/tests/wave.d.ts +46 -0
  258. package/tests/wave.js +167 -177
  259. package/tests/wave.ts +252 -0
  260. package/tsconfig.json +17 -0
  261. package/types.d.ts +243 -0
  262. package/types.js +10 -0
  263. package/types.ts +376 -0
  264. package/validation/act/README.md +46 -0
  265. package/validation/act/capture.js +424 -0
  266. package/validation/act/chromium-issue-draft.md +66 -0
  267. package/validation/act/fp-triage-2026-08-22.md +86 -0
  268. package/validation/act/isolation-notes.md +159 -0
  269. package/validation/act/playwright-issue-draft.md +73 -0
  270. package/validation/act/propose-mappings.js +0 -0
  271. package/validation/act/repro-cdp-raw.js +105 -0
  272. package/validation/act/repro-metarefresh.js +65 -0
  273. package/validation/act/score.js +184 -0
  274. package/validation/act/stage3a-stress-report.md +69 -0
  275. package/validation/act/stage3b-mapping-proposals.md +94 -0
  276. package/validation/act/stage3b-triage-draft.md +138 -0
  277. package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
  278. package/validation/executors/netWatch.js +180 -90
  279. package/validation/executors/test.js +17 -2
  280. package/validation/executors/tests.js +118 -10
  281. package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
  282. package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
  283. package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
  284. package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
  285. package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
  286. package/validation/knownFailures.json +1 -0
  287. package/validation/tests/jobProperties/adbID.json +27 -2
  288. package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
  289. package/validation/tests/jobProperties/allCaps.json +25 -0
  290. package/validation/tests/jobProperties/allHidden.json +133 -13
  291. package/validation/tests/jobProperties/allSlanted.json +3 -3
  292. package/validation/tests/jobProperties/altScheme.json +23 -8
  293. package/validation/tests/jobProperties/attVal.json +57 -57
  294. package/validation/tests/jobProperties/autocomplete.json +11 -1
  295. package/validation/tests/jobProperties/bulk.json +6 -1
  296. package/validation/tests/jobProperties/buttonMenu.json +63 -42
  297. package/validation/tests/jobProperties/captionLoc.json +1 -6
  298. package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
  299. package/validation/tests/jobProperties/checkpoint-page.json +397 -0
  300. package/validation/tests/jobProperties/checkpoint.json +395 -0
  301. package/validation/tests/jobProperties/datalistRef.json +20 -5
  302. package/validation/tests/jobProperties/distortion.json +28 -3
  303. package/validation/tests/jobProperties/docType.json +2 -2
  304. package/validation/tests/jobProperties/dupAtt.json +48 -33
  305. package/validation/tests/jobProperties/elements.json +28 -28
  306. package/validation/tests/jobProperties/embAc.json +36 -31
  307. package/validation/tests/jobProperties/focAndOp.json +284 -0
  308. package/validation/tests/jobProperties/focInd.json +34 -34
  309. package/validation/tests/jobProperties/focVis.json +1 -1
  310. package/validation/tests/jobProperties/hover.json +39 -37
  311. package/validation/tests/jobProperties/hr.json +13 -3
  312. package/validation/tests/jobProperties/imageLink.json +0 -5
  313. package/validation/tests/jobProperties/labClash.json +68 -33
  314. package/validation/tests/jobProperties/legendLoc.json +1 -6
  315. package/validation/tests/jobProperties/lineHeight.json +2 -2
  316. package/validation/tests/jobProperties/linkAmb.json +13 -18
  317. package/validation/tests/jobProperties/linkExt.json +1 -1
  318. package/validation/tests/jobProperties/linkOldAtt.json +12 -2
  319. package/validation/tests/jobProperties/linkTo.json +1 -1
  320. package/validation/tests/jobProperties/linkUl.json +65 -65
  321. package/validation/tests/jobProperties/miniText.json +7 -2
  322. package/validation/tests/jobProperties/motion.json +4 -50
  323. package/validation/tests/jobProperties/nonTable.json +48 -3
  324. package/validation/tests/jobProperties/optRoleSel.json +12 -2
  325. package/validation/tests/jobProperties/phOnly.json +6 -16
  326. package/validation/tests/jobProperties/pseudoP.json +17 -2
  327. package/validation/tests/jobProperties/radioSet.json +30 -30
  328. package/validation/tests/jobProperties/role.json +25 -5
  329. package/validation/tests/jobProperties/secHeading.json +26 -21
  330. package/validation/tests/jobProperties/styleDiff.json +35 -5
  331. package/validation/tests/jobProperties/tabNav.json +3 -1
  332. package/validation/tests/jobProperties/targetsNear.json +237 -0
  333. package/validation/tests/jobProperties/textNodes.json +29 -29
  334. package/validation/tests/jobProperties/textSem.json +26 -1
  335. package/validation/tests/jobProperties/title.json +12 -2
  336. package/validation/tests/jobProperties/titledEl.json +39 -9
  337. package/validation/tests/jobProperties/userPath.json +317 -0
  338. package/validation/tests/jobProperties/zIndex.json +40 -35
  339. package/validation/tests/targets/allCapStyle/index.html +26 -0
  340. package/validation/tests/targets/checkpoint/index.html +38 -0
  341. package/validation/tests/targets/datalistRef/index.html +1 -1
  342. package/validation/tests/targets/focAndOp/bad.html +29 -0
  343. package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
  344. package/validation/tests/targets/focInd/bad.html +2 -1
  345. package/validation/tests/targets/headEl/index.html +10 -1
  346. package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
  347. package/validation/tests/targets/userPath/index.html +42 -0
  348. package/validation/validateTest.js +62 -10
  349. package/.claude/settings.local.json +0 -11
  350. package/.eslintrc.json +0 -41
  351. package/htmlcs/.eslintrc.json +0 -67
  352. package/memory/MEMORY.md +0 -3
  353. package/memory/project_validation_pause.md +0 -10
  354. package/validation/tests/jobProperties/linkTitle.json +0 -127
  355. package/validation/tests/jobProperties/opFoc.json +0 -164
  356. package/validation/tests/jobProperties/targetSmall.json +0 -152
  357. package/validation/tests/jobProperties/targetTiny.json +0 -142
  358. package/validation/tests/targets/focOp/bad.html +0 -25
  359. package/validation/tests/targets/linkTitle/index.html +0 -24
  360. package/validation/tests/targets/opFoc/bad.html +0 -26
  361. package/validation/tests/targets/opFoc/good.html +0 -23
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "es2022",
5
+ "module": "commonjs",
6
+ "moduleResolution": "node10",
7
+ "lib": ["es2022", "es2023.array", "dom", "dom.iterable"],
8
+ "types": ["node"],
9
+ "esModuleInterop": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "skipLibCheck": true,
12
+ "declaration": true,
13
+ "removeComments": false
14
+ },
15
+ "include": ["**/*.ts"],
16
+ "exclude": ["node_modules", "htmlcs", "ed11y", "dist", "validation/tests/targets", "**/*.d.ts"]
17
+ }
package/types.d.ts ADDED
@@ -0,0 +1,243 @@
1
+ export type ToolID = 'alfa' | 'aslint' | 'axe' | 'ed11y' | 'htmlcs' | 'ibm' | 'nuVal' | 'nuVnu' | 'pour' | 'qualWeb' | 'surea11y' | 'testaro' | 'wave';
2
+ export type Outcome = 'failed' | 'cantTell';
3
+ export type UncertaintyCode = 'not-computable' | 'judgement-required' | 'runtime-dependent' | 'spec-only' | 'equivalence-unknown' | 'out-of-scope';
4
+ export type BrowserID = 'chromium' | 'firefox' | 'webkit';
5
+ export type SeverityTotals = [number, number, number, number];
6
+ export interface StandardInstance {
7
+ ruleID: string;
8
+ what: string;
9
+ ordinalSeverity: 0 | 1 | 2 | 3;
10
+ outcome?: Outcome;
11
+ uncertainty?: UncertaintyCode;
12
+ needed?: string;
13
+ count?: number;
14
+ catalogIndex?: string | number;
15
+ checkpoint?: number;
16
+ tagName?: string;
17
+ id?: string;
18
+ location?: {
19
+ doc: string;
20
+ type: 'selector' | 'xpath' | 'box' | '';
21
+ spec: string | string[] | BoundingBox | Record<string, never>;
22
+ };
23
+ boxID?: string;
24
+ pathID?: string;
25
+ excerpt?: string;
26
+ }
27
+ export interface BoundingBox {
28
+ x: number;
29
+ y: number;
30
+ width: number;
31
+ height: number;
32
+ }
33
+ export interface StandardResult {
34
+ prevented?: boolean;
35
+ totals?: SeverityTotals;
36
+ outcomeTotals?: Record<Outcome, number>;
37
+ instances?: StandardInstance[];
38
+ }
39
+ export interface CatalogEntry {
40
+ tagName: string;
41
+ pathID: string;
42
+ id?: string;
43
+ startTag?: string;
44
+ text?: string;
45
+ textLinkable?: boolean;
46
+ boxID?: string;
47
+ headingIndex?: string;
48
+ checkpoint?: number;
49
+ }
50
+ export type Catalog = Record<string, CatalogEntry>;
51
+ export interface Interaction {
52
+ modality: 'efficient';
53
+ }
54
+ export interface Checkpoint {
55
+ index: number;
56
+ name: string;
57
+ implicit: boolean;
58
+ actIndex: number | null;
59
+ launchActIndex: number | null;
60
+ launchURL: string;
61
+ replay: number[];
62
+ interaction: Interaction;
63
+ kind: 'navigation' | 'interaction';
64
+ url: string;
65
+ title: string;
66
+ imageIndexes: number[];
67
+ catalogRange: [number, number] | null;
68
+ elementCount: number;
69
+ ariaSnapshot: string;
70
+ domDigest?: string;
71
+ elapsedMs: number;
72
+ testActs: number[];
73
+ structure?: StructureDiff;
74
+ }
75
+ export interface Act {
76
+ type: string;
77
+ which?: string;
78
+ what?: string;
79
+ startTime?: string;
80
+ endTime?: string;
81
+ actualURL?: string;
82
+ data?: Record<string, unknown>;
83
+ result?: {
84
+ nativeResult?: unknown;
85
+ standardResult?: StandardResult;
86
+ [key: string]: unknown;
87
+ };
88
+ expectations?: unknown;
89
+ expectationFailures?: number;
90
+ checkpoint?: number | null;
91
+ scope?: TestScope;
92
+ [key: string]: unknown;
93
+ }
94
+ export type TestScope = 'page' | 'changed';
95
+ export interface ScopeData {
96
+ requested: TestScope;
97
+ applied: boolean;
98
+ reason: string;
99
+ roots: string[];
100
+ pathIDs: string[];
101
+ commonRoot?: string;
102
+ localRules?: string[];
103
+ pageRules?: string[];
104
+ }
105
+ export interface FlowIssue {
106
+ tool: ToolID;
107
+ ruleID: string;
108
+ pathID: string;
109
+ startTag: string;
110
+ what: string;
111
+ ordinalSeverity: number;
112
+ outcome?: Outcome;
113
+ count: number;
114
+ actIndexes: number[];
115
+ }
116
+ export interface StructureDiff {
117
+ added: string[];
118
+ removed: string[];
119
+ changed: string[];
120
+ textChanged: string[];
121
+ roots: string[];
122
+ counts: Record<'before' | 'after' | 'added' | 'removed' | 'changed' | 'textChanged' | 'roots', number>;
123
+ }
124
+ export interface AriaDiff {
125
+ addedLineCount: number;
126
+ removedLineCount: number;
127
+ truncated: boolean;
128
+ changes: {
129
+ type: 'added' | 'removed';
130
+ line: number;
131
+ text: string;
132
+ }[];
133
+ }
134
+ export interface FlowDelta {
135
+ from: number;
136
+ to: number;
137
+ tools: ToolID[];
138
+ notObserved: ToolID[];
139
+ added: FlowIssue[];
140
+ persisted: FlowIssue[];
141
+ removed: FlowIssue[];
142
+ notRetested: FlowIssue[];
143
+ structure: StructureDiff;
144
+ aria: AriaDiff;
145
+ }
146
+ export interface Flow {
147
+ checkpoints: {
148
+ index: number;
149
+ name: string;
150
+ kind: Checkpoint['kind'];
151
+ url: string;
152
+ actIndex: number | null;
153
+ testActs: number[];
154
+ tools: ToolID[];
155
+ issueCount: number;
156
+ }[];
157
+ deltas: FlowDelta[];
158
+ }
159
+ export interface Report {
160
+ id: string;
161
+ what?: string;
162
+ strict?: boolean;
163
+ standard?: 'also' | 'only' | 'no';
164
+ observe?: boolean;
165
+ browserID?: BrowserID;
166
+ device?: {
167
+ id: string;
168
+ windowOptions?: Record<string, unknown>;
169
+ };
170
+ target?: {
171
+ what?: string;
172
+ url: string;
173
+ };
174
+ sources?: Record<string, unknown>;
175
+ creationTimeStamp?: string;
176
+ executionTimeStamp?: string;
177
+ timeLimit?: number;
178
+ acts: Act[];
179
+ imageColor?: number;
180
+ imageScale?: number;
181
+ stealth?: boolean;
182
+ browserChannel?: 'bundled' | 'chrome' | 'msedge';
183
+ scannerId?: string;
184
+ scroll?: boolean;
185
+ navigation?: {
186
+ waitUntil?: 'networkidle' | 'load' | 'domcontentloaded';
187
+ timeout?: number;
188
+ failFast4xx?: boolean;
189
+ };
190
+ images?: string[];
191
+ catalog?: Catalog;
192
+ checkpoints?: Checkpoint[];
193
+ pathIDs?: Record<string, Record<string, string>>;
194
+ catalogNextIndex?: number;
195
+ activeCheckpoint?: number | null;
196
+ scope?: {
197
+ roots: string[];
198
+ commonRoot?: string;
199
+ } | null;
200
+ ruleScopeRoots?: string[] | null;
201
+ flow?: Flow;
202
+ jobData?: {
203
+ aborted?: boolean;
204
+ abortedAct?: number;
205
+ endTime?: string;
206
+ elapsedSeconds?: number;
207
+ visitLatency?: number;
208
+ logCount?: number;
209
+ logSize?: number;
210
+ errorLogCount?: number;
211
+ errorLogSize?: number;
212
+ prohibitedCount?: number;
213
+ visitRejectionCount?: number;
214
+ visitTimeoutCount?: number;
215
+ preventions?: Partial<Record<ToolID, unknown>>;
216
+ toolTimes?: Partial<Record<ToolID, number>>;
217
+ [key: string]: unknown;
218
+ };
219
+ [key: string]: unknown;
220
+ }
221
+ export type BadWhat = null | undefined | false | '' | string | {
222
+ description: string;
223
+ data?: unknown;
224
+ };
225
+ export type GetBadWhat = (element: Element) => BadWhat | Promise<BadWhat>;
226
+ declare global {
227
+ interface Window {
228
+ getXPath: (element: Element) => string | null | undefined;
229
+ getAccessibleName: (element: Element) => string;
230
+ computeAccessibleName: (element: Element) => string;
231
+ getProtoInstance: (element: Element) => unknown;
232
+ HTMLCS_WCAG?: unknown;
233
+ HTMLCS_RUNNER?: {
234
+ run: (standard: string) => string[];
235
+ };
236
+ HTMLCS_WCAG2AAA?: {
237
+ sniffs?: unknown;
238
+ };
239
+ define?: unknown;
240
+ exports?: unknown;
241
+ module?: unknown;
242
+ }
243
+ }
package/types.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /*
3
+ © 2026 Jonathan Robert Pool.
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
+ Object.defineProperty(exports, "__esModule", { value: true });
package/types.ts ADDED
@@ -0,0 +1,376 @@
1
+ /*
2
+ © 2026 Jonathan Robert Pool.
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
+ types
12
+ Type declarations for the shapes Testaro produces and consumes.
13
+
14
+ Since Phase 1 of the TypeScript migration (issue #73) this is a TypeScript
15
+ source file; types.d.ts and types.js are generated from it by tsc and
16
+ committed. These describe the report as it exists today; where the runtime is
17
+ looser than a type can promise, the property is optional or unknown rather
18
+ than aspirational. The Act type is deliberately permissive in this phase; the
19
+ discriminated union over act types arrives when procs/job.js is converted.
20
+ */
21
+
22
+ // The tools whose adapters ship with Testaro (the keys of the tools map in procs/job.js).
23
+ export type ToolID =
24
+ | 'alfa'
25
+ | 'aslint'
26
+ | 'axe'
27
+ | 'ed11y'
28
+ | 'htmlcs'
29
+ | 'ibm'
30
+ | 'nuVal'
31
+ | 'nuVnu'
32
+ | 'pour'
33
+ | 'qualWeb'
34
+ | 'surea11y'
35
+ | 'testaro'
36
+ | 'wave';
37
+
38
+ // Whether a rule engine asserted a violation or reported that it could not tell (ACT vocabulary).
39
+ export type Outcome = 'failed' | 'cantTell';
40
+
41
+ // Reasons a rule engine may give for a cantTell outcome.
42
+ export type UncertaintyCode =
43
+ | 'not-computable'
44
+ | 'judgement-required'
45
+ | 'runtime-dependent'
46
+ | 'spec-only'
47
+ | 'equivalence-unknown'
48
+ | 'out-of-scope';
49
+
50
+ // The browser types a job may specify.
51
+ export type BrowserID = 'chromium' | 'firefox' | 'webkit';
52
+
53
+ // Rule-violation counts at the 4 ordinal severities, least severe first.
54
+ export type SeverityTotals = [number, number, number, number];
55
+
56
+ // One violation (or group of violations of one rule) in a standard result.
57
+ export interface StandardInstance {
58
+ ruleID: string;
59
+ what: string;
60
+ ordinalSeverity: 0 | 1 | 2 | 3;
61
+ // Certainty of the violation (v78.1+ reports); the authoritative certainty signal.
62
+ outcome?: Outcome;
63
+ // Reason for a cantTell outcome, if the rule engine gave one.
64
+ uncertainty?: UncertaintyCode;
65
+ // What a reviewer must determine to resolve a cantTell outcome, if the rule engine said.
66
+ needed?: string;
67
+ count?: number;
68
+ // Key of the violating element in the report catalog (v70+ reports).
69
+ catalogIndex?: string | number;
70
+ // Index of the checkpoint (page state) the instance was found in (v78.2+ reports).
71
+ checkpoint?: number;
72
+ // Inline element identifiers of pre-catalog (v60) reports; absent on v70+.
73
+ tagName?: string;
74
+ id?: string;
75
+ location?: {
76
+ doc: string;
77
+ type: 'selector' | 'xpath' | 'box' | '';
78
+ spec: string | string[] | BoundingBox | Record<string, never>;
79
+ };
80
+ boxID?: string;
81
+ pathID?: string;
82
+ excerpt?: string;
83
+ }
84
+
85
+ export interface BoundingBox {
86
+ x: number;
87
+ y: number;
88
+ width: number;
89
+ height: number;
90
+ }
91
+
92
+ // The Testaro-standardized result of one test act.
93
+ export interface StandardResult {
94
+ prevented?: boolean;
95
+ totals?: SeverityTotals;
96
+ // Counts of violations by outcome (v78.1+ reports).
97
+ outcomeTotals?: Record<Outcome, number>;
98
+ instances?: StandardInstance[];
99
+ }
100
+
101
+ /*
102
+ One entry per element in the report catalog (v70+). getCatalog writes all 8
103
+ properties; getXPathCatalogIndex appends stub entries carrying only tagName
104
+ and pathID when a test cites an element the catalog pass did not record, so
105
+ only those 2 properties can be promised.
106
+ */
107
+ export interface CatalogEntry {
108
+ tagName: string;
109
+ pathID: string;
110
+ id?: string;
111
+ startTag?: string;
112
+ text?: string;
113
+ textLinkable?: boolean;
114
+ boxID?: string;
115
+ headingIndex?: string;
116
+ // Index of the checkpoint (page state) whose snapshot the entry belongs to.
117
+ checkpoint?: number;
118
+ }
119
+
120
+ // The element catalog: element index (stringified integer) to entry.
121
+ export type Catalog = Record<string, CatalogEntry>;
122
+
123
+ // The event modality of interaction acts; only 'efficient' exists today.
124
+ export interface Interaction {
125
+ modality: 'efficient';
126
+ }
127
+
128
+ /*
129
+ A checkpoint: a page state reached by the job's serial acts, snapshotted (catalog, page
130
+ image, ARIA snapshot) and tested by the test acts that follow it. Checkpoint 0 is the job
131
+ target as launched; a checkpoint act creates each later one. A navigation checkpoint was
132
+ reached by navigation alone and needs no replay; an interaction checkpoint was reached by
133
+ acts on a page, which a test act's browser replays after navigating to launchURL.
134
+ */
135
+ export interface Checkpoint {
136
+ index: number;
137
+ name: string;
138
+ implicit: boolean;
139
+ actIndex: number | null;
140
+ launchActIndex: number | null;
141
+ launchURL: string;
142
+ replay: number[];
143
+ interaction: Interaction;
144
+ kind: 'navigation' | 'interaction';
145
+ url: string;
146
+ title: string;
147
+ imageIndexes: number[];
148
+ catalogRange: [number, number] | null;
149
+ elementCount: number;
150
+ ariaSnapshot: string;
151
+ domDigest?: string;
152
+ elapsedMs: number;
153
+ testActs: number[];
154
+ // Job-time: the structure diff with the previous checkpoint, moved into report.flow at job end.
155
+ structure?: StructureDiff;
156
+ }
157
+
158
+ // One act of a job. Permissive in Phase 0; see the file comment.
159
+ export interface Act {
160
+ type: string;
161
+ which?: string;
162
+ what?: string;
163
+ startTime?: string;
164
+ endTime?: string;
165
+ actualURL?: string;
166
+ data?: Record<string, unknown>;
167
+ result?: {
168
+ nativeResult?: unknown;
169
+ standardResult?: StandardResult;
170
+ [key: string]: unknown;
171
+ };
172
+ expectations?: unknown;
173
+ expectationFailures?: number;
174
+ // The checkpoint a test act belongs to, assigned by doActs.
175
+ checkpoint?: number | null;
176
+ // What a test act tests: its checkpoint's whole page (default) or the subtrees changed
177
+ // since the previous checkpoint (for the rules and tools that can be so restricted).
178
+ scope?: TestScope;
179
+ [key: string]: unknown;
180
+ }
181
+
182
+ // The scope of a test act.
183
+ export type TestScope = 'page' | 'changed';
184
+
185
+ // What a changed-scope test act was given, recorded as act.data.scope.
186
+ export interface ScopeData {
187
+ requested: TestScope;
188
+ // Whether the tool restricted its tests to the roots.
189
+ applied: boolean;
190
+ // Why not, if not.
191
+ reason: string;
192
+ // CSS selectors of the changed subtree roots, and their XPaths.
193
+ roots: string[];
194
+ pathIDs: string[];
195
+ // A selector of the nearest common ancestor of the roots, for tools that take one root.
196
+ commonRoot?: string;
197
+ // For the testaro tool: which of its rules were scoped to the roots and which tested the page.
198
+ localRules?: string[];
199
+ pageRules?: string[];
200
+ }
201
+
202
+ // An issue found at a checkpoint, identified across checkpoints by tool, rule, element XPath,
203
+ // and start tag (report.flow).
204
+ export interface FlowIssue {
205
+ tool: ToolID;
206
+ ruleID: string;
207
+ pathID: string;
208
+ startTag: string;
209
+ what: string;
210
+ ordinalSeverity: number;
211
+ outcome?: Outcome;
212
+ count: number;
213
+ actIndexes: number[];
214
+ }
215
+
216
+ // The difference between the catalogs of two checkpoints, as XPaths.
217
+ export interface StructureDiff {
218
+ added: string[];
219
+ removed: string[];
220
+ changed: string[];
221
+ textChanged: string[];
222
+ // The outermost changed elements, whose subtrees contain every change.
223
+ roots: string[];
224
+ counts: Record<'before' | 'after' | 'added' | 'removed' | 'changed' | 'textChanged' | 'roots', number>;
225
+ }
226
+
227
+ // The line difference between the ARIA snapshots of two checkpoints.
228
+ export interface AriaDiff {
229
+ addedLineCount: number;
230
+ removedLineCount: number;
231
+ truncated: boolean;
232
+ changes: {type: 'added' | 'removed'; line: number; text: string}[];
233
+ }
234
+
235
+ // What changed between two consecutive checkpoints: issues and page structure.
236
+ export interface FlowDelta {
237
+ from: number;
238
+ to: number;
239
+ // Tools that observed both checkpoints, whose issues are compared.
240
+ tools: ToolID[];
241
+ // Tools that observed only one of the two.
242
+ notObserved: ToolID[];
243
+ added: FlowIssue[];
244
+ persisted: FlowIssue[];
245
+ removed: FlowIssue[];
246
+ // Earlier issues outside the changed subtrees a tool's later acts were all scoped to.
247
+ notRetested: FlowIssue[];
248
+ structure: StructureDiff;
249
+ aria: AriaDiff;
250
+ }
251
+
252
+ // The running list of issues across a job's checkpoints (reports with 2 or more checkpoints).
253
+ export interface Flow {
254
+ checkpoints: {
255
+ index: number;
256
+ name: string;
257
+ kind: Checkpoint['kind'];
258
+ url: string;
259
+ actIndex: number | null;
260
+ testActs: number[];
261
+ tools: ToolID[];
262
+ issueCount: number;
263
+ }[];
264
+ deltas: FlowDelta[];
265
+ }
266
+
267
+ // A job before execution and the report it becomes during and after execution.
268
+ export interface Report {
269
+ id: string;
270
+ what?: string;
271
+ strict?: boolean;
272
+ standard?: 'also' | 'only' | 'no';
273
+ observe?: boolean;
274
+ browserID?: BrowserID;
275
+ device?: {id: string; windowOptions?: Record<string, unknown>};
276
+ target?: {
277
+ what?: string;
278
+ url: string;
279
+ };
280
+ sources?: Record<string, unknown>;
281
+ creationTimeStamp?: string;
282
+ executionTimeStamp?: string;
283
+ timeLimit?: number;
284
+ acts: Act[];
285
+ // The severity of required page-image colorfulness; even values require an image.
286
+ imageColor?: number;
287
+ // Page-image scale factor; values greater than 1 add a device-scale image (images[1]).
288
+ imageScale?: number;
289
+ // Whether Chromium runs stealth evasions (default true; Chromium only).
290
+ stealth?: boolean;
291
+ // Branded Chromium channel to run instead of the bundled build (chrome or msedge).
292
+ browserChannel?: 'bundled' | 'chrome' | 'msedge';
293
+ // Scanner identity sent as the X-YRA-Scanner request header (default SCANNER_ID, else none).
294
+ scannerId?: string;
295
+ // Whether to scroll the full page after navigation so lazily loaded content is present
296
+ // (default PRESCAN_SCROLL, else false).
297
+ scroll?: boolean;
298
+ // Navigation options (defaults NAV_WAIT_UNTIL, NAV_TIMEOUT, NAV_FAIL_FAST_4XX, else
299
+ // networkidle, 10000, false).
300
+ navigation?: {
301
+ waitUntil?: 'networkidle' | 'load' | 'domcontentloaded';
302
+ timeout?: number;
303
+ failFast4xx?: boolean;
304
+ };
305
+ // Base64-encoded page images added by shoot with the report action.
306
+ images?: string[];
307
+ // The element catalog, added by getCatalog and pruned before the report ships.
308
+ catalog?: Catalog;
309
+ // The checkpoints (page states) of the job; checkpoint 0 is added by getCatalog.
310
+ checkpoints?: Checkpoint[];
311
+ // Job-time properties, deleted by pruneCatalog: per-checkpoint XPath-to-catalog-index maps,
312
+ // the next unused catalog index, and the checkpoint the current test act belongs to.
313
+ pathIDs?: Record<string, Record<string, string>>;
314
+ catalogNextIndex?: number;
315
+ activeCheckpoint?: number | null;
316
+ // Job-time: the changed subtree roots of the current changed-scope test act, and, within
317
+ // the testaro tool, those of the current rule (null for a page-level rule).
318
+ scope?: {roots: string[]; commonRoot?: string} | null;
319
+ ruleScopeRoots?: string[] | null;
320
+ // The running list of issues across checkpoints, added at job end when there are 2 or more.
321
+ flow?: Flow;
322
+ jobData?: {
323
+ aborted?: boolean;
324
+ abortedAct?: number;
325
+ endTime?: string;
326
+ elapsedSeconds?: number;
327
+ visitLatency?: number;
328
+ logCount?: number;
329
+ logSize?: number;
330
+ errorLogCount?: number;
331
+ errorLogSize?: number;
332
+ prohibitedCount?: number;
333
+ visitRejectionCount?: number;
334
+ visitTimeoutCount?: number;
335
+ preventions?: Partial<Record<ToolID, unknown>>;
336
+ toolTimes?: Partial<Record<ToolID, number>>;
337
+ [key: string]: unknown;
338
+ };
339
+ [key: string]: unknown;
340
+ }
341
+
342
+ /*
343
+ The classification a getBadWhat predicate returns for a candidate element:
344
+ falsy for no violation, a description string for a violation, or an object
345
+ when the violation carries data. A `0:`–`3:` prefix on a description
346
+ overrides the rule's default ordinal severity. Predicates are serialized with
347
+ toString() and rehydrated inside the page, so they must be closure-free; the
348
+ type checker cannot enforce that invariant.
349
+ */
350
+ export type BadWhat =
351
+ | null
352
+ | undefined
353
+ | false
354
+ | ''
355
+ | string
356
+ | {description: string; data?: unknown};
357
+ export type GetBadWhat = (element: Element) => BadWhat | Promise<BadWhat>;
358
+
359
+ declare global {
360
+ interface Window {
361
+ // Injected by procs/launch.js for use inside page.evaluate callbacks.
362
+ getXPath: (element: Element) => string | null | undefined;
363
+ getAccessibleName: (element: Element) => string;
364
+ computeAccessibleName: (element: Element) => string;
365
+ getProtoInstance: (element: Element) => unknown;
366
+ // Set by the HTML CodeSniffer bundle while the htmlcs tool runs.
367
+ HTMLCS_WCAG?: unknown;
368
+ HTMLCS_RUNNER?: {run: (standard: string) => string[]};
369
+ // Redefined by the htmlcs tool to limit testing to selected rules.
370
+ HTMLCS_WCAG2AAA?: {sniffs?: unknown};
371
+ // Loader globals the htmlcs tool hides while its UMD bundle executes.
372
+ define?: unknown;
373
+ exports?: unknown;
374
+ module?: unknown;
375
+ }
376
+ }
@@ -0,0 +1,46 @@
1
+ # Capture harness: ACT fixtures (Track A) + real-page URL lists (Stage 3)
2
+
3
+ Two page sources share one hardened capture loop (deadline-raced browser operations, wedge-
4
+ signature browser replacement, resume-from-output): the default ACT-fixture mode described
5
+ below, and `--urls <file>` mode (one URL per line, `#` comments) for real-world captures — used
6
+ by the engine-candidacy pipeline's Stage-3 stress/mapping run. URL mode additionally records
7
+ per-instance element paths (`instances: [{ruleID, severity, xPath}]`, capped at 2,000/row) for
8
+ element-level co-occurrence, and captures the page's CSP script nonce so script-injecting
9
+ adapters can reuse it (mirroring the production launch proc's `jobData.lastScriptNonce`).
10
+
11
+ Runs tool reporters over the [W3C ACT-Rules test cases](https://www.w3.org/WAI/standards-guidelines/act/rules/)
12
+ — ~1,200 tiny fixture pages with expected outcomes (passed / failed / inapplicable) per ACT rule —
13
+ and scores each engine's per-rule **sensitivity and specificity** against that ground truth. This
14
+ is Stage 2 ("fixture confusion matrix") of the engine-candidacy pipeline; see yra-monitor
15
+ `docs/plans/engine-candidacy-pipeline.md`.
16
+
17
+ Capture and scoring are separate so the expensive browser pass never repeats to try a different
18
+ scoring policy:
19
+
20
+ ```sh
21
+ # cwd must be the repository root (tool assets resolve relative to it).
22
+ node validation/act/capture.js --engines pour,axe --match "image|button|link" \
23
+ --out validation/act/results/run.jsonl
24
+ node validation/act/score.js --in validation/act/results/run.jsonl --band asserted
25
+ ```
26
+
27
+ - `capture.js` — downloads/caches the testcase feed, navigates each fixture, replicates the
28
+ launch-proc environment each engine needs (`window.getXPath` injection; `data-xpath` stamping
29
+ for attribute-need engines), runs each reporter with a per-act time limit, and appends one JSONL
30
+ row per testcase × engine with raw findings: engine rule-ID counts, `outcomeTotals` (standard
31
+ instances by outcome `failed` / `cantTell`), plus per-criterion `asserted` (violation band)
32
+ and `review` (incomplete band) counts. Filters: `--rules id,id`,
33
+ `--match nameRegex`, `--max N`.
34
+ - `score.js` — computes the confusion matrix. An ACT rule's positive criteria are its
35
+ `forConformance` WCAG 2.x success criteria; an engine flags a testcase when it reports ≥1
36
+ finding on any of them (`--band asserted|review|both`). `failed` testcases are the positive
37
+ class; `passed` + `inapplicable` the negative class. Prints markdown tables; `--json` writes the
38
+ full report.
39
+
40
+ Criterion-level matching is the v1 comparability layer, and its biases are stated wherever
41
+ results publish: a finding on the right criterion from an unrelated check still counts
42
+ (generous), and a real finding reported under a different criterion does not (strict). Engine
43
+ rule-IDs are captured raw, so an exact engine-rule → ACT-rule mapping can refine scoring later
44
+ without recapturing. Criterion extractors exist for `pour` and `axe` (both tag findings with WCAG
45
+ criteria natively); other engines record rule IDs and instance counts until an extractor (or a
46
+ tic-based criterion map) is added.