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
package/tests/nuVal.ts ADDED
@@ -0,0 +1,145 @@
1
+ /*
2
+ © 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt.
4
+ © 2026 Jonathan Robert Pool.
5
+
6
+ Licensed under the MIT License. See LICENSE file at the project root or
7
+ https://opensource.org/license/mit/ for details.
8
+
9
+ SPDX-License-Identifier: MIT
10
+ */
11
+
12
+ // IMPORTS
13
+
14
+ import type {Page} from 'playwright';
15
+ import {curate, getContent, getExtractExcerpt} from '../procs/nu';
16
+ import type {NuResult} from '../procs/nu';
17
+ import {getAttributeXPath, getXPathCatalogIndex} from '../procs/xPath';
18
+ import {getStandardResult, addInstance} from '../procs/standard';
19
+ import type {Act, Report, StandardInstance, StandardResult} from '../types';
20
+
21
+ // TYPES
22
+
23
+ // The nuVal-act properties this reporter consumes.
24
+ interface NuValAct extends Act {
25
+ rules?: string[];
26
+ withSource?: boolean;
27
+ }
28
+
29
+ /*
30
+ nuVal
31
+ Subjects a page and its source to the Nu Html Checker, thereby testing scripted content found only in the loaded page and erroneous content before the browser corrects it. The API erratically replaces left and right double quotation marks with invalid UTF-8, which appears as 2 or 3 successive instances of the replacement character (U+fffd). Therefore, this test removes all such quotation marks and the replacement character. That causes 'Bad value “” for' to become 'Bad value for'. Since the corruption of quotation marks is erratic, no better solution is known.
32
+ This rule engine is the API version of the Nu Html Checker. It is an alternative to the nuVnu rule engine, which uses the same validator as an installed dependency. Each rule engine has advantages and disadvantages. The main advantage of nuVal is that it does not require the Testaro host to provide a Java virtual machine. The main advantage of the nuVnu tool is that it can evaluate pages reachable from the host that Testaro runs on even if not reachable from the public Internet.
33
+ This rule engine calls the W3C validation service unless a TESTARO_NU_URL environment variable is defined with the URL of another instance of the API.
34
+ Compiled to nuVal.js by tsc (issue #73); edit this file, not the emitted one.
35
+ */
36
+
37
+ // FUNCTIONS
38
+
39
+ // Conducts and reports the Nu Html Checker API tests.
40
+ export const reporter = async (page: Page, report: Report, actIndex: number) => {
41
+ const act = report.acts[actIndex] as NuValAct;
42
+ const {rules, withSource} = act;
43
+ // Initialize the act report.
44
+ const data: {prevented?: boolean; error?: string} = {};
45
+ const result: {nativeResult: NuResult; standardResult: StandardResult} = {
46
+ nativeResult: {} as NuResult,
47
+ standardResult: {}
48
+ };
49
+ const standard = report.standard !== 'no';
50
+ // If standard results are to be reported:
51
+ if (standard) {
52
+ // Initialize the standard result.
53
+ result.standardResult = getStandardResult();
54
+ }
55
+ const {standardResult} = result;
56
+ // Get the content.
57
+ const content = await getContent(page, withSource);
58
+ const {testTarget} = content;
59
+ // If it was obtained and contains a test target:
60
+ if (testTarget) {
61
+ const fetchOptions = {
62
+ method: 'post',
63
+ headers: {
64
+ 'user-agent': 'Mozilla/5.0',
65
+ 'content-type': 'text/html; charset=utf-8'
66
+ },
67
+ body: testTarget
68
+ };
69
+ // Override target for self-hosted Nu Html Checker instances (Docker
70
+ // image ghcr.io/validator/validator or equivalent). Defaults to the
71
+ // public W3C service, which 502s on bodies >~80 kB.
72
+ const nuURL = process.env.TESTARO_NU_URL
73
+ || 'https://validator.w3.org/nu/?parser=html&out=json';
74
+ let nuData: unknown = {};
75
+ let nuResponse = new Response();
76
+ try {
77
+ // Get a Nu Html Checker report from the W3C validator service.
78
+ nuResponse = await fetch(nuURL, fetchOptions);
79
+ const {ok, status, statusText} = nuResponse;
80
+ // If the acquisition succeeded:
81
+ if (ok) {
82
+ // Get the response body as an object.
83
+ nuData = await nuResponse.json();
84
+ }
85
+ // Otherwise, i.e. if the request failed:
86
+ else {
87
+ // Get the response body as text.
88
+ const nuResponseText = await nuResponse.text();
89
+ // Add a failure report to the data.
90
+ data.prevented = true;
91
+ data.error = `HTTP ${status}: ${statusText} (${nuResponseText?.slice(0, 200)})`;
92
+ }
93
+ }
94
+ // If an error occurred:
95
+ catch (error) {
96
+ // Report it.
97
+ const message = `ERROR getting results (${(error as Error).message}; status ${nuResponse.status || 'none'} (${JSON.stringify((nuData as {body?: unknown})?.body || 'no body', null, 2)})`;
98
+ console.log(message);
99
+ data.prevented = true;
100
+ data.error = message;
101
+ };
102
+ // Postprocess the response data and add the postprocessed data to the native result.
103
+ result.nativeResult = await curate(data, nuData, rules) as NuResult;
104
+ // If standard results are to be reported:
105
+ if (standard) {
106
+ // For each message in the native result:
107
+ result.nativeResult.messages.forEach(message => {
108
+ const isInfo = message.type === 'info';
109
+ // Initialize a standard instance. Informational messages are uncertainty.
110
+ const standardInstance: StandardInstance = {
111
+ ruleID: message.message,
112
+ what: message.message,
113
+ ordinalSeverity: isInfo ? 0 : 3,
114
+ outcome: isInfo ? 'cantTell' : 'failed'
115
+ };
116
+ // Get the XPath of the element from its extract.
117
+ const xPath = getAttributeXPath(message.extract);
118
+ // If the acquisition succeeded:
119
+ if (xPath) {
120
+ // Add the catalog index to the standard instance.
121
+ standardInstance.catalogIndex = getXPathCatalogIndex(report, xPath);
122
+ }
123
+ // Get an excerpt of the extract, if the extract identifies no element.
124
+ const extractExcerpt = getExtractExcerpt(message.extract);
125
+ // If one was obtained (e.g. the erroneous CSS of a CSS: Parse Error message):
126
+ if (extractExcerpt) {
127
+ // Add it to the description of the violation, so the extract is not lost.
128
+ standardInstance.what = `${message.message} Extract: ${extractExcerpt}`;
129
+ }
130
+ // Add the standard instance to the standard result.
131
+ addInstance(standardResult, standardInstance);
132
+ })
133
+ }
134
+ }
135
+ // Otherwise, i.e. if the page content was not obtained:
136
+ else {
137
+ // Report this.
138
+ data.prevented = true;
139
+ data.error = 'Content not obtained';
140
+ }
141
+ return {
142
+ data,
143
+ result
144
+ };
145
+ };
@@ -0,0 +1,15 @@
1
+ import type { Page } from 'playwright';
2
+ import type { NuResult } from '../procs/nu';
3
+ import type { Report, StandardResult } from '../types';
4
+ export declare const reporter: (page: Page, report: Report, actIndex: number) => Promise<{
5
+ data: {
6
+ prevented?: boolean;
7
+ error?: string;
8
+ skipped?: boolean;
9
+ reason?: string;
10
+ };
11
+ result: {
12
+ nativeResult: NuResult;
13
+ standardResult: StandardResult;
14
+ };
15
+ }>;
package/tests/nuVnu.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -8,128 +9,157 @@
8
9
 
9
10
  SPDX-License-Identifier: MIT
10
11
  */
11
-
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.reporter = void 0;
47
+ // IMPORTS
48
+ const fs = __importStar(require("fs/promises"));
49
+ const path = __importStar(require("path"));
50
+ const nu_1 = require("../procs/nu");
51
+ const xPath_1 = require("../procs/xPath");
52
+ const standard_1 = require("../procs/standard");
53
+ // vnu-jar ships no type declarations, so its import stays a require and is untyped.
54
+ const { vnu } = require('vnu-jar');
12
55
  /*
56
+ nuVnu
13
57
  Subjects a page and its source to the Nu Html Checker, thereby testing scripted content found only in the loaded page and erroneous content before the browser corrects it. The API erratically replaces left and right double quotation marks with invalid UTF-8, which appears as 2 or 3 successive instances of the replacement character (U+fffd). Therefore, this test removes all such quotation marks and the replacement character. That causes 'Bad value “” for' to become 'Bad value for'. Since the corruption of quotation marks is erratic, no better solution is known.
14
58
  This rule engine is the installed version of the Nu Html Checker. It is an alternative to the nuVal rule engine, which uses the same validator as a web service of the World Wide Web Consortium (W3C). Each rule engine has advantages and disadvantages. The main advantage of the nuVnu rule engine is that it can evaluate pages larger than about 80,000 bytes and pages reachable from the host that Testaro runs on even if not reachable from the public Internet. The main advantages of nuVal are that it usually runs faster than nuVnu and it does not require the Testaro host to provide a Java virtual machine.
15
59
  When both nuVal and nuVnu are included in a job, nuVal should precede nuVnu. If nuVal succeeds, nuVnu aborts. So, only one of the two tools contributes instances to the job report.
60
+ Compiled to nuVnu.js by tsc (issue #73); edit this file, not the emitted one.
16
61
  */
17
-
18
- // IMPORTS
19
-
20
- const {curate, getContent, getExtractExcerpt} = require('../procs/nu');
21
- const {getAttributeXPath, getXPathCatalogIndex} = require('../procs/xPath');
22
- const fs = require('fs/promises');
23
- const path = require('path');
24
- const {vnu} = require('vnu-jar');
25
-
26
62
  // FUNCTIONS
27
-
28
63
  // Conducts and reports the Nu Html Checker tests.
29
- exports.reporter = async (page, report, actIndex) => {
30
- // Initialize the act report.
31
- const data = {};
32
- const result = {
33
- nativeResult: {},
34
- standardResult: {}
35
- };
36
- const standard = report.standard !== 'no';
37
- // If standard results are to be reported:
38
- if (standard) {
39
- // Initialize the standard result.
40
- result.standardResult = {
41
- prevented: false,
42
- totals: [0, 0, 0, 0],
43
- instances: []
64
+ const reporter = async (page, report, actIndex) => {
65
+ // Initialize the act report.
66
+ const data = {};
67
+ const result = {
68
+ nativeResult: {},
69
+ standardResult: {}
44
70
  };
45
- }
46
- const {standardResult} = result;
47
- // Get the nuVal act, if it exists.
48
- const nuValAct = report.acts.find(act => act.type === 'test' && act.which === 'nuVal');
49
- // If it does not exist or it exists but was prevented:
50
- if (! nuValAct || nuValAct.data?.prevented) {
51
- const act = report.acts[actIndex];
52
- const {rules, withSource} = act;
53
- // Get the content.
54
- const content = await getContent(page, withSource);
55
- const {testTarget} = content;
56
- // If it was obtained and contains a test target:
57
- if (testTarget) {
58
- const pagePath = path.join(report.jobData.tmpDir, 'nuVnu-page.html');
59
- // Save the test target in a temporary file.
60
- await fs.writeFile(pagePath, testTarget);
61
- let nuData;
62
- try {
63
- // Get Nu Html Checker output on it.
64
- nuData = await vnu.check(['--format', 'json', '--stdout', pagePath]);
65
- }
66
- // If any error was thrown:
67
- catch (error) {
68
- const errorMessage = error.message;
69
- try {
70
- // Parse it as JSON, i.e. a benign nuVnu result with at least 1 violation.
71
- nuData = JSON.parse(error.message);
71
+ const standard = report.standard !== 'no';
72
+ // If standard results are to be reported:
73
+ if (standard) {
74
+ // Initialize the standard result.
75
+ result.standardResult = (0, standard_1.getStandardResult)();
76
+ }
77
+ const { standardResult } = result;
78
+ // Get the nuVal act, if it exists.
79
+ const nuValAct = report.acts.find(act => act.type === 'test' && act.which === 'nuVal');
80
+ // If it does not exist or it exists but was prevented:
81
+ if (!nuValAct || nuValAct.data?.prevented) {
82
+ const act = report.acts[actIndex];
83
+ const { rules, withSource } = act;
84
+ // Get the content.
85
+ const content = await (0, nu_1.getContent)(page, withSource);
86
+ const { testTarget } = content;
87
+ // If it was obtained and contains a test target:
88
+ if (testTarget) {
89
+ const pagePath = path.join(report.jobData.tmpDir, 'nuVnu-page.html');
90
+ // Save the test target in a temporary file.
91
+ await fs.writeFile(pagePath, testTarget);
92
+ let nuData;
93
+ try {
94
+ // Get Nu Html Checker output on it.
95
+ nuData = await vnu.check(['--format', 'json', '--stdout', pagePath]);
96
+ }
97
+ // If any error was thrown:
98
+ catch (error) {
99
+ const errorMessage = error.message;
100
+ try {
101
+ // Parse it as JSON, i.e. a benign nuVnu result with at least 1 violation.
102
+ nuData = JSON.parse(error.message);
103
+ }
104
+ // If parsing it as JSON fails:
105
+ catch (error) {
106
+ // Report a genuine error.
107
+ data.prevented = true;
108
+ data.error = errorMessage;
109
+ }
110
+ }
111
+ // Delete the temporary file.
112
+ await fs.unlink(pagePath);
113
+ // Postprocess the output and add the postprocessed output to the native result.
114
+ result.nativeResult = await (0, nu_1.curate)(data, nuData, rules);
115
+ // If standard results are to be reported:
116
+ if (standard) {
117
+ // For each message in the native result:
118
+ result.nativeResult.messages.forEach(message => {
119
+ const isInfo = message.type === 'info';
120
+ // Initialize a standard instance. Informational messages are uncertainty.
121
+ const standardInstance = {
122
+ ruleID: message.message,
123
+ what: message.message,
124
+ ordinalSeverity: isInfo ? 0 : 3,
125
+ outcome: isInfo ? 'cantTell' : 'failed'
126
+ };
127
+ // Get the XPath of the element from its extract.
128
+ const xPath = (0, xPath_1.getAttributeXPath)(message.extract);
129
+ // If the acquisition succeeded:
130
+ if (xPath) {
131
+ // Add the catalog index to the standard instance.
132
+ standardInstance.catalogIndex = (0, xPath_1.getXPathCatalogIndex)(report, xPath);
133
+ }
134
+ // Get an excerpt of the extract, if the extract identifies no element.
135
+ const extractExcerpt = (0, nu_1.getExtractExcerpt)(message.extract);
136
+ // If one was obtained (e.g. the erroneous CSS of a CSS: Parse Error message):
137
+ if (extractExcerpt) {
138
+ // Add it to the description of the violation, so the extract is not lost.
139
+ standardInstance.what = `${message.message} Extract: ${extractExcerpt}`;
140
+ }
141
+ // Add the standard instance to the standard result.
142
+ (0, standard_1.addInstance)(standardResult, standardInstance);
143
+ });
144
+ }
72
145
  }
73
- // If parsing it as JSON fails:
74
- catch (error) {
75
- // Report a genuine error.
76
- data.prevented = true;
77
- data.error = errorMessage;
146
+ // Otherwise, i.e. if the content was not obtained or contains no test target:
147
+ else {
148
+ // Report this.
149
+ data.prevented = true;
150
+ data.error = 'Content not obtained';
78
151
  }
79
- }
80
- // Delete the temporary file.
81
- await fs.unlink(pagePath);
82
- // Postprocess the output and add the postprocessed output to the native result.
83
- result.nativeResult = await curate(data, nuData, rules);
84
- // If standard results are to be reported:
85
- if (standard) {
86
- // For each message in the native result:
87
- result.nativeResult.messages.forEach(message => {
88
- const ordinalSeverity = message.type === 'info' ? 0 : 3;
89
- // Increment the applicable standard-result total.
90
- standardResult.totals[ordinalSeverity]++;
91
- // Initialize a standard instance.
92
- const standardInstance = {
93
- ruleID: message.message,
94
- what: message.message,
95
- ordinalSeverity,
96
- count: 1,
97
- };
98
- // Get the XPath of the element from its extract.
99
- const xPath = getAttributeXPath(message.extract);
100
- // If the acquisition succeeded:
101
- if (xPath) {
102
- // Add the catalog index to the standard instance.
103
- standardInstance.catalogIndex = getXPathCatalogIndex(report, xPath);
104
- }
105
- // Get an excerpt of the extract, if the extract identifies no element.
106
- const extractExcerpt = getExtractExcerpt(message.extract);
107
- // If one was obtained (e.g. the erroneous CSS of a CSS: Parse Error message):
108
- if (extractExcerpt) {
109
- // Add it to the description of the violation, so the extract is not lost.
110
- standardInstance.what = `${message.message} Extract: ${extractExcerpt}`;
111
- }
112
- // Add the standard instance to the standard result.
113
- standardResult.instances.push(standardInstance);
114
- });
115
- }
116
152
  }
117
- // Otherwise, i.e. if the content was not obtained or contains no test target:
153
+ // Otherwise, i.e. if the nuVal act exists and succeeded:
118
154
  else {
119
- // Report this.
120
- data.prevented = true;
121
- data.error = 'Content not obtained';
155
+ // Abort this act and report this.
156
+ data.skipped = true;
157
+ data.reason = 'nuVal succeeded';
122
158
  }
123
- }
124
- // Otherwise, i.e. if the nuVal act exists and succeeded:
125
- else {
126
- // Abort this act and report this.
127
- data.skipped = true;
128
- data.reason = 'nuVal succeeded';
129
- }
130
- // Return the data and result.
131
- return {
132
- data,
133
- result
134
- };
159
+ // Return the data and result.
160
+ return {
161
+ data,
162
+ result
163
+ };
135
164
  };
165
+ exports.reporter = reporter;
package/tests/nuVnu.ts ADDED
@@ -0,0 +1,144 @@
1
+ /*
2
+ © 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt.
4
+ © 2025–2026 Jonathan Robert Pool.
5
+
6
+ Licensed under the MIT License. See LICENSE file at the project root or
7
+ https://opensource.org/license/mit/ for details.
8
+
9
+ SPDX-License-Identifier: MIT
10
+ */
11
+
12
+ // IMPORTS
13
+
14
+ import * as fs from 'fs/promises';
15
+ import * as path from 'path';
16
+ import type {Page} from 'playwright';
17
+ import {curate, getContent, getExtractExcerpt} from '../procs/nu';
18
+ import type {NuResult} from '../procs/nu';
19
+ import {getAttributeXPath, getXPathCatalogIndex} from '../procs/xPath';
20
+ import {getStandardResult, addInstance} from '../procs/standard';
21
+ import type {Act, Report, StandardInstance, StandardResult} from '../types';
22
+ // vnu-jar ships no type declarations, so its import stays a require and is untyped.
23
+ const {vnu} = require('vnu-jar');
24
+
25
+ // TYPES
26
+
27
+ // The nuVnu-act properties this reporter consumes.
28
+ interface NuVnuAct extends Act {
29
+ rules?: string[];
30
+ withSource?: boolean;
31
+ }
32
+
33
+ /*
34
+ nuVnu
35
+ Subjects a page and its source to the Nu Html Checker, thereby testing scripted content found only in the loaded page and erroneous content before the browser corrects it. The API erratically replaces left and right double quotation marks with invalid UTF-8, which appears as 2 or 3 successive instances of the replacement character (U+fffd). Therefore, this test removes all such quotation marks and the replacement character. That causes 'Bad value “” for' to become 'Bad value for'. Since the corruption of quotation marks is erratic, no better solution is known.
36
+ This rule engine is the installed version of the Nu Html Checker. It is an alternative to the nuVal rule engine, which uses the same validator as a web service of the World Wide Web Consortium (W3C). Each rule engine has advantages and disadvantages. The main advantage of the nuVnu rule engine is that it can evaluate pages larger than about 80,000 bytes and pages reachable from the host that Testaro runs on even if not reachable from the public Internet. The main advantages of nuVal are that it usually runs faster than nuVnu and it does not require the Testaro host to provide a Java virtual machine.
37
+ When both nuVal and nuVnu are included in a job, nuVal should precede nuVnu. If nuVal succeeds, nuVnu aborts. So, only one of the two tools contributes instances to the job report.
38
+ Compiled to nuVnu.js by tsc (issue #73); edit this file, not the emitted one.
39
+ */
40
+
41
+ // FUNCTIONS
42
+
43
+ // Conducts and reports the Nu Html Checker tests.
44
+ export const reporter = async (page: Page, report: Report, actIndex: number) => {
45
+ // Initialize the act report.
46
+ const data: {prevented?: boolean; error?: string; skipped?: boolean; reason?: string} = {};
47
+ const result: {nativeResult: NuResult; standardResult: StandardResult} = {
48
+ nativeResult: {} as NuResult,
49
+ standardResult: {}
50
+ };
51
+ const standard = report.standard !== 'no';
52
+ // If standard results are to be reported:
53
+ if (standard) {
54
+ // Initialize the standard result.
55
+ result.standardResult = getStandardResult();
56
+ }
57
+ const {standardResult} = result;
58
+ // Get the nuVal act, if it exists.
59
+ const nuValAct = report.acts.find(act => act.type === 'test' && act.which === 'nuVal');
60
+ // If it does not exist or it exists but was prevented:
61
+ if (! nuValAct || nuValAct.data?.prevented) {
62
+ const act = report.acts[actIndex] as NuVnuAct;
63
+ const {rules, withSource} = act;
64
+ // Get the content.
65
+ const content = await getContent(page, withSource);
66
+ const {testTarget} = content;
67
+ // If it was obtained and contains a test target:
68
+ if (testTarget) {
69
+ const pagePath = path.join(report.jobData!.tmpDir as string, 'nuVnu-page.html');
70
+ // Save the test target in a temporary file.
71
+ await fs.writeFile(pagePath, testTarget);
72
+ let nuData: unknown;
73
+ try {
74
+ // Get Nu Html Checker output on it.
75
+ nuData = await vnu.check(['--format', 'json', '--stdout', pagePath]);
76
+ }
77
+ // If any error was thrown:
78
+ catch (error) {
79
+ const errorMessage = (error as Error).message;
80
+ try {
81
+ // Parse it as JSON, i.e. a benign nuVnu result with at least 1 violation.
82
+ nuData = JSON.parse((error as Error).message);
83
+ }
84
+ // If parsing it as JSON fails:
85
+ catch (error) {
86
+ // Report a genuine error.
87
+ data.prevented = true;
88
+ data.error = errorMessage;
89
+ }
90
+ }
91
+ // Delete the temporary file.
92
+ await fs.unlink(pagePath);
93
+ // Postprocess the output and add the postprocessed output to the native result.
94
+ result.nativeResult = await curate(data, nuData, rules) as NuResult;
95
+ // If standard results are to be reported:
96
+ if (standard) {
97
+ // For each message in the native result:
98
+ result.nativeResult.messages.forEach(message => {
99
+ const isInfo = message.type === 'info';
100
+ // Initialize a standard instance. Informational messages are uncertainty.
101
+ const standardInstance: StandardInstance = {
102
+ ruleID: message.message,
103
+ what: message.message,
104
+ ordinalSeverity: isInfo ? 0 : 3,
105
+ outcome: isInfo ? 'cantTell' : 'failed'
106
+ };
107
+ // Get the XPath of the element from its extract.
108
+ const xPath = getAttributeXPath(message.extract);
109
+ // If the acquisition succeeded:
110
+ if (xPath) {
111
+ // Add the catalog index to the standard instance.
112
+ standardInstance.catalogIndex = getXPathCatalogIndex(report, xPath);
113
+ }
114
+ // Get an excerpt of the extract, if the extract identifies no element.
115
+ const extractExcerpt = getExtractExcerpt(message.extract);
116
+ // If one was obtained (e.g. the erroneous CSS of a CSS: Parse Error message):
117
+ if (extractExcerpt) {
118
+ // Add it to the description of the violation, so the extract is not lost.
119
+ standardInstance.what = `${message.message} Extract: ${extractExcerpt}`;
120
+ }
121
+ // Add the standard instance to the standard result.
122
+ addInstance(standardResult, standardInstance);
123
+ });
124
+ }
125
+ }
126
+ // Otherwise, i.e. if the content was not obtained or contains no test target:
127
+ else {
128
+ // Report this.
129
+ data.prevented = true;
130
+ data.error = 'Content not obtained';
131
+ }
132
+ }
133
+ // Otherwise, i.e. if the nuVal act exists and succeeded:
134
+ else {
135
+ // Abort this act and report this.
136
+ data.skipped = true;
137
+ data.reason = 'nuVal succeeded';
138
+ }
139
+ // Return the data and result.
140
+ return {
141
+ data,
142
+ result
143
+ };
144
+ };
@@ -0,0 +1,31 @@
1
+ import type { Page } from 'playwright';
2
+ import type { Report, StandardResult } from '../types';
3
+ type PourSeverity = 'minor' | 'moderate' | 'serious' | 'critical';
4
+ interface PourFinding {
5
+ ruleID: string;
6
+ what: string;
7
+ wcag: string;
8
+ severity: PourSeverity;
9
+ html: string;
10
+ cssPath: string;
11
+ xPath: string;
12
+ }
13
+ interface PourNativeResult {
14
+ engineVersion?: string;
15
+ violations?: PourFinding[];
16
+ incomplete?: PourFinding[];
17
+ passRuleCount?: number;
18
+ passElementCount?: number;
19
+ inapplicableRuleCount?: number;
20
+ manualReviewCriterionCount?: number;
21
+ prevented?: boolean;
22
+ error?: string;
23
+ }
24
+ export declare const reporter: (page: Page, report: Report, actIndex: number) => Promise<{
25
+ data: Record<string, unknown>;
26
+ result: {
27
+ nativeResult: PourNativeResult;
28
+ standardResult: StandardResult;
29
+ };
30
+ }>;
31
+ export {};