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