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
@@ -1,5 +1,7 @@
1
1
  /*
2
2
  © 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jonathan Robert Pool.
4
+ © 2026 Jeff Witt.
3
5
 
4
6
  Licensed under the MIT License. See LICENSE file at the project root or
5
7
  https://opensource.org/license/mit/ for details.
@@ -8,119 +10,207 @@
8
10
  */
9
11
 
10
12
  /*
11
- watchNet.js
12
- Validator for network watching.
13
+ netWatch.js
14
+ Validator for network watching, covering all three auth types.
13
15
  */
14
16
 
15
17
  // IMPORTS
16
18
 
17
19
  const fs = require('fs/promises');
20
+ const client = require('http');
18
21
 
19
22
  // CONSTANTS
20
23
 
21
- // Override netWatch environment variables with validation-specific ones.
22
24
  const jobDir = `${__dirname}/../jobs/todo`;
23
- process.env.JOB_URLS = 'http://localhost:3007/api/job';
24
- process.env.AGENT = 'testarauth';
25
- const {netWatch} = require('../../netWatch');
26
- const client = require('http');
27
25
  const jobID = '240101T1200-simple-example';
26
+ const workerID = 'testaro1';
27
+ const workerSecret = 'testarosecret';
28
+ // Override netWatch environment variables with validation-specific ones.
29
+ process.env.NETWATCH_URL_JOB = 'http://localhost:3007/api/job';
30
+ process.env.NETWATCH_URL_REPORT = 'http://localhost:3007/api/report';
31
+ process.env.NETWATCH_WORKER_ID = workerID;
32
+ process.env.NETWATCH_WORKER_SECRET = workerSecret;
33
+ const {netWatch} = require('../../netWatch');
34
+ /*
35
+ The authorization-header value netWatch is expected to send in header mode: per RFC 7617, Basic
36
+ followed by base64 of testaro1:testarosecret. Intentionally a literal, not derived from the
37
+ netWatch code, so an encoding regression there fails here.
38
+ */
39
+ const workerAuth = 'Basic dGVzdGFybzE6dGVzdGFyb3NlY3JldA==';
40
+ // Auth types to be validated.
41
+ const authTypes = ['header', 'pathBody', 'none'];
28
42
 
29
- // OPERATION
43
+ // FUNCTIONS
30
44
 
31
- // Start a timer.
32
- const startTime = Date.now();
33
- // Initialize the state.
34
- let jobGiven = false;
35
- // Start checking for jobs every 5 seconds in 5 seconds.
36
- setTimeout(() => {
37
- netWatch(false, 5, false);
38
- }, 5000);
39
- let server;
40
- // Handles Testaro requests to the server.
41
- const requestHandler = (request, response) => {
42
- const {method} = request;
43
- const bodyParts = [];
44
- request.on('error', err => {
45
- console.error(err);
46
- })
47
- .on('data', chunk => {
48
- bodyParts.push(chunk);
49
- })
50
- .on('end', async () => {
51
- // Remove any trailing slash from the URL.
52
- const requestURL = request.url.replace(/\/$/, '');
53
- // If the request method is GET:
54
- if (method === 'GET') {
55
- // If a job is validly requested:
56
- console.log('Server got a job request from Testaro');
57
- if (requestURL === `/api/job?agent=${process.env.AGENT}`) {
58
- // If at least 7 seconds has elapsed since timing started:
59
- if (Date.now() > startTime + 7000) {
60
- // Respond with a job.
61
- const jobJSON = await fs.readFile(`${jobDir}/${jobID}.json`);
62
- await response.end(jobJSON);
63
- console.log('Server sent job to Testaro');
64
- jobGiven = true;
45
+ // Returns an error description if a request carries wrong credentials for an auth type, else ''.
46
+ const credentialError = (authType, request, bodyObj) => {
47
+ const authHeader = request.headers.authorization;
48
+ if (authType === 'header') {
49
+ if (authHeader !== workerAuth) {
50
+ return 'authorization header missing or wrong';
51
+ }
52
+ if (bodyObj.agentPW !== undefined) {
53
+ return 'agentPW unexpectedly in the request body';
54
+ }
55
+ }
56
+ else if (authType === 'pathBody') {
57
+ if (authHeader) {
58
+ return 'authorization header unexpectedly present';
59
+ }
60
+ if (bodyObj.agentPW !== workerSecret) {
61
+ return 'agentPW missing from or wrong in the request body';
62
+ }
63
+ }
64
+ else {
65
+ if (authHeader) {
66
+ return 'authorization header unexpectedly present';
67
+ }
68
+ if (bodyObj.agentPW !== undefined) {
69
+ return 'agentPW unexpectedly in the request body';
70
+ }
71
+ }
72
+ return '';
73
+ };
74
+ // Validates netWatch with one auth type; returns whether the scenario succeeded.
75
+ const runScenario = authType => {
76
+ return new Promise(resolveScenario => {
77
+ process.env.NETWATCH_AUTH_TYPE = authType;
78
+ let jobGiven = false;
79
+ let noJobSent = false;
80
+ let reportOK = false;
81
+ let server;
82
+ // Responds to a request with a JSON object and a status code.
83
+ const respondWithObject = (object, response, statusCode = 200) => {
84
+ response.statusCode = statusCode;
85
+ response.setHeader('content-type', 'application/json; charset=utf-8');
86
+ response.end(JSON.stringify(object));
87
+ };
88
+ // Handles Testaro requests to the server.
89
+ const requestHandler = (request, response) => {
90
+ const {method} = request;
91
+ // Ignore any trailing slash on the request URL.
92
+ const url = request.url.replace(/\/$/, '');
93
+ const bodyParts = [];
94
+ request.on('error', err => {
95
+ console.error(err);
96
+ })
97
+ .on('data', chunk => {
98
+ bodyParts.push(chunk);
99
+ })
100
+ .on('end', async () => {
101
+ let bodyObj = {};
102
+ try {
103
+ bodyObj = JSON.parse(bodyParts.join('') || '{}');
65
104
  }
66
- // Otherwise, i.e. if timing started less than 7 seconds ago:
67
- else {
68
- // Send an empty-object response.
69
- await response.end('{}');
105
+ catch(error) {
106
+ console.log(`Scenario ${authType} failure: non-JSON request body (${error.message})`);
70
107
  }
71
- }
72
- else {
73
- const error = {
74
- error: 'ERROR: Job request invalid'
75
- };
76
- const errorJSON = JSON.stringify(error);
77
- await response.end(errorJSON);
78
- }
79
- }
80
- // Otherwise, if the request method is POST:
81
- else if (method === 'POST') {
82
- console.log('Server got report from Testaro');
83
- const ack = {};
84
- // If a report is validly submitted:
85
- if (requestURL === '/api') {
86
- // If a job was earlier given to Testaro:
87
- if (jobGiven) {
88
- // Respond, reporting success or failure.
89
- try {
90
- const bodyJSON = bodyParts.join('');
91
- const body = JSON.parse(bodyJSON);
108
+ // If the request carries wrong credentials for this auth type:
109
+ const credError = credentialError(authType, request, bodyObj);
110
+ if (credError) {
111
+ // Report this and reject the request; netWatch is expected to abort on this.
112
+ console.log(`Scenario ${authType} failure: ${credError}`);
113
+ respondWithObject({error: 'ERROR: Authorization invalid'}, response, 401);
114
+ server.close();
115
+ return;
116
+ }
117
+ // Otherwise, if the request is a job request:
118
+ if (method === 'POST' && url === '/api/job') {
119
+ console.log(`Server got a job request from Testaro (${authType})`);
120
+ // If this is the first job request of the scenario:
121
+ if (! noJobSent) {
122
+ // Send a no-job response, to validate the no-job branch.
123
+ noJobSent = true;
124
+ respondWithObject({}, response);
125
+ }
126
+ // Otherwise, i.e. if the no-job branch was already validated:
127
+ else {
128
+ // Respond with a job.
129
+ const jobJSON = await fs.readFile(`${jobDir}/${jobID}.json`);
130
+ response.setHeader('content-type', 'application/json; charset=utf-8');
131
+ response.end(jobJSON);
132
+ console.log(`Server sent job to Testaro (${authType})`);
133
+ jobGiven = true;
134
+ }
135
+ }
136
+ // Otherwise, if the request is a report submission:
137
+ else if (method === 'POST' && url === '/api/report') {
138
+ console.log(`Server got report from Testaro (${authType})`);
139
+ const ack = {};
140
+ // If a job was earlier given to Testaro:
141
+ if (jobGiven) {
142
+ // Check the report, including the worker identification.
143
+ const {report} = bodyObj;
92
144
  if (
93
- body.acts
94
- && body.sources
95
- && body.sources.agent
96
- && body.sources.agent === process.env.AGENT
145
+ report
146
+ && report.acts
147
+ && report.jobData
148
+ && report.sources
149
+ && report.sources.agent === workerID
97
150
  ) {
98
151
  ack.message = 'Success: Valid report submitted';
152
+ reportOK = true;
99
153
  }
100
154
  else {
101
- ack.message = 'Failure: Report invalid';
155
+ ack.message = 'Failure: Report invalid or worker not identified';
102
156
  }
103
157
  }
104
- catch(error) {
105
- ack.message = `ERROR: ${error.message}`;
158
+ else {
159
+ ack.message = 'ERROR: Report submitted before a job was given';
106
160
  }
161
+ respondWithObject(ack, response);
162
+ console.log(`Server responded: ${ack.message}`);
163
+ // This ends the scenario, so stop the server.
164
+ server.close();
165
+ console.log('Server closed');
107
166
  }
108
- }
109
- else {
110
- ack.message = 'ERROR: Report submission invalid';
111
- }
112
- const ackJSON = JSON.stringify(ack);
113
- response.end(ackJSON);
114
- console.log(`Server responded: ${ack.message}`);
115
- // This ends the validation, so stop the server.
167
+ // Otherwise, i.e. if the request is neither:
168
+ else {
169
+ // Report this and reject the request.
170
+ console.log(`Scenario ${authType} failure: unexpected request ${method} ${request.url}`);
171
+ respondWithObject({error: 'ERROR: Request invalid'}, response, 404);
172
+ server.close();
173
+ }
174
+ });
175
+ };
176
+ // Create a server.
177
+ server = client.createServer({}, requestHandler);
178
+ // Start the server listening for Testaro requests, then start a one-job watch.
179
+ server.listen(3007, async () => {
180
+ console.log(`Job and report server listening on port 3007 (auth type ${authType})`);
181
+ const watchOK = await netWatch(false, 5, false);
116
182
  server.close();
117
- console.log('Server closed');
118
- }
183
+ resolveScenario(reportOK && watchOK);
184
+ });
119
185
  });
120
186
  };
121
- // Create a server.
122
- server = client.createServer({}, requestHandler);
123
- // Start a server listening for Testaro requests.
124
- server.listen(3007, () => {
125
- console.log('Job and report server listening on port 3007');
126
- });
187
+
188
+ // OPERATION
189
+
190
+ // Impose a deadline, so a hang becomes a failure instead of a silent stall.
191
+ const watchdog = setTimeout(() => {
192
+ console.log('Failure: netWatch validation timed out');
193
+ process.exit(1);
194
+ }, 600000);
195
+ // Run the scenarios in sequence and exit with a code reporting the outcome.
196
+ (async () => {
197
+ const failures = [];
198
+ for (const authType of authTypes) {
199
+ const isOK = await runScenario(authType);
200
+ // Drop any kept-alive connections to the closed server before the next scenario.
201
+ client.globalAgent.destroy();
202
+ console.log(`Scenario ${authType}: ${isOK ? 'valid' : 'INVALID'}\n`);
203
+ if (! isOK) {
204
+ failures.push(authType);
205
+ }
206
+ }
207
+ clearTimeout(watchdog);
208
+ if (failures.length) {
209
+ console.log(`Failure: Invalid scenarios: ${failures.join(', ')}`);
210
+ process.exit(1);
211
+ }
212
+ else {
213
+ console.log('Success: Valid reports submitted for all auth types');
214
+ process.exit(0);
215
+ }
216
+ })();
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt.
3
4
 
4
5
  Licensed under the MIT License. See LICENSE file at the project root or
5
6
  https://opensource.org/license/mit/ for details.
@@ -13,6 +14,20 @@
13
14
  Execution example: npm test focOp
14
15
  */
15
16
 
16
- const testID = process.argv[2];
17
17
  const {validateTest} = require('../validateTest');
18
- validateTest(testID);
18
+
19
+ // Flushes the standard output and then exits, because lingering browser
20
+ // processes otherwise keep the event loop alive indefinitely.
21
+ const exitWith = code => {
22
+ process.stdout.write('', () => process.exit(code));
23
+ };
24
+
25
+ const testID = process.argv[2];
26
+ validateTest(testID)
27
+ .then(result => {
28
+ exitWith(result.success ? 0 : 1);
29
+ })
30
+ .catch(error => {
31
+ console.log(`ERROR validating test ${testID} (${error.message})`);
32
+ exitWith(1);
33
+ });
@@ -1,5 +1,6 @@
1
1
  /*
2
2
  © 2022–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt.
3
4
 
4
5
  Licensed under the MIT License. See LICENSE file at the project root or
5
6
  https://opensource.org/license/mit/ for details.
@@ -14,21 +15,128 @@
14
15
 
15
16
  // IMPORTS
16
17
 
18
+ require('dotenv').config({quiet: true});
17
19
  const fs = require('fs').promises;
18
- const {validateTest} = require('../validateTest');
20
+ const {spawn} = require('child_process');
21
+
22
+ // CONSTANTS
23
+
24
+ const ruleDir = `${__dirname}/../../testaro`;
25
+ const validatorDir = `${__dirname}/../tests/jobProperties`;
26
+ // Rules that ask an AI model to classify candidate instances.
27
+ const aiRuleIDs = ['allCaps'];
28
+ // Validators of features other than rules, run after the rule validators.
29
+ const featureValidatorIDs = ['checkpoint', 'checkpoint-browser', 'checkpoint-page', 'userPath'];
30
+ // Maximum number of seconds allowed for the validation of one rule.
31
+ const ruleTimeLimit = 180;
32
+ /*
33
+ Rules whose validations currently fail, with the reasons. They do not make
34
+ the validation as a whole fail, so a failure of a healthy rule is blocking
35
+ while these known failures are being repaired. When a repair makes a rule
36
+ pass, it is to be removed from knownFailures.json.
37
+ */
38
+ const knownFailures = require('../knownFailures.json');
39
+
40
+ // FUNCTIONS
41
+
42
+ /*
43
+ Validates one rule in a child process, so a defective rule cannot crash or
44
+ hang the whole validation and so each job's browser processes are reaped.
45
+ Returns whether the validation succeeded.
46
+ */
47
+ const validateInChild = testID => new Promise(resolve => {
48
+ const child = spawn(
49
+ process.execPath, [`${__dirname}/test.js`, testID], {stdio: 'inherit'}
50
+ );
51
+ const timer = setTimeout(() => {
52
+ console.log(`######## Failure: Validation of ${testID} timed out (${ruleTimeLimit}s)\n`);
53
+ child.kill('SIGKILL');
54
+ }, 1000 * ruleTimeLimit);
55
+ child.on('close', code => {
56
+ clearTimeout(timer);
57
+ resolve(code === 0);
58
+ });
59
+ child.on('error', error => {
60
+ clearTimeout(timer);
61
+ console.log(`######## Failure: Validation of ${testID} could not start (${error.message})\n`);
62
+ resolve(false);
63
+ });
64
+ });
19
65
 
20
66
  // OPERATION
21
67
 
22
- // Get the names of the Testaro tes files.
23
- fs.readdir(`${__dirname}/../../testaro/`)
68
+ // Get the names of the Testaro rule files and the validator files.
69
+ Promise.all([fs.readdir(ruleDir), fs.readdir(validatorDir)])
24
70
  // When they arrive:
25
- .then(async fileNames => {
26
- // For each file name:
27
- for (const fileName of fileNames) {
28
- // Get the test ID from it by disregarding its extension.
29
- const testID = fileName.replace(/\..+$/, '');
30
- // Validate the test.
71
+ .then(async ([ruleFileNames, validatorFileNames]) => {
72
+ // Get the rule IDs and the IDs of the rules with validators.
73
+ // Rule modules are the JavaScript files in the rule directory, except the generated registry.
74
+ const ruleIDs = ruleFileNames
75
+ .filter(name => name.endsWith('.js') && name !== 'registry.js')
76
+ .map(name => name.slice(0, -3));
77
+ const validatorIDs = validatorFileNames
78
+ .filter(name => name.endsWith('.json'))
79
+ .map(name => name.slice(0, -5));
80
+ // Identify the rules without validators and the validators without rules.
81
+ const unvalidatedIDs = ruleIDs.filter(id => ! validatorIDs.includes(id));
82
+ const orphanIDs = validatorIDs.filter(
83
+ id => ! ruleIDs.includes(id) && ! featureValidatorIDs.includes(id)
84
+ );
85
+ // Identify the AI-dependent rules to be skipped for lack of an API key.
86
+ const skippedIDs = process.env.ANTHROPIC_API_KEY
87
+ ? []
88
+ : ruleIDs.filter(id => aiRuleIDs.includes(id));
89
+ // Initialize the results.
90
+ const failedIDs = [];
91
+ const knownFailedIDs = [];
92
+ const recoveredIDs = [];
93
+ let validatedCount = 0;
94
+ // For each rule with a validator, and then each feature validator:
95
+ const featureIDs = featureValidatorIDs.filter(id => validatorIDs.includes(id));
96
+ for (
97
+ const testID of ruleIDs
98
+ .filter(id => validatorIDs.includes(id) && ! skippedIDs.includes(id))
99
+ .concat(featureIDs)
100
+ ) {
101
+ // Validate the rule.
31
102
  console.log(`### Validating Testaro test ${testID}`);
32
- await validateTest(testID);
103
+ const succeeded = await validateInChild(testID);
104
+ validatedCount++;
105
+ if (succeeded && knownFailures[testID]) {
106
+ recoveredIDs.push(testID);
107
+ }
108
+ else if (! succeeded) {
109
+ (knownFailures[testID] ? knownFailedIDs : failedIDs).push(testID);
110
+ }
111
+ }
112
+ // Report a summary.
113
+ console.log('\n#### Validation summary');
114
+ console.log(`Rules validated: ${validatedCount}`);
115
+ console.log(`Rules failing validation: ${failedIDs.length + knownFailedIDs.length}`);
116
+ if (failedIDs.length) {
117
+ console.log(` Failing rules: ${failedIDs.join(', ')}`);
118
+ }
119
+ if (knownFailedIDs.length) {
120
+ console.log(` Known-failing rules, not blocking: ${knownFailedIDs.join(', ')}`);
121
+ }
122
+ if (recoveredIDs.length) {
123
+ console.log(
124
+ `Warning - rules now passing; remove them from knownFailures.json: ${recoveredIDs.join(', ')}`
125
+ );
126
+ }
127
+ if (skippedIDs.length) {
128
+ console.log(
129
+ `Warning - AI-dependent rules skipped because ANTHROPIC_API_KEY is not set: ${
130
+ skippedIDs.join(', ')
131
+ }`
132
+ );
133
+ }
134
+ if (unvalidatedIDs.length) {
135
+ console.log(`Warning - rules without validators: ${unvalidatedIDs.join(', ')}`);
136
+ }
137
+ if (orphanIDs.length) {
138
+ console.log(`Warning - validators without rules: ${orphanIDs.join(', ')}`);
33
139
  }
140
+ // Make the validation failable by CI and other callers.
141
+ process.exitCode = failedIDs.length ? 1 : 0;
34
142
  });