testaro 78.0.8 → 78.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/.gitattributes +94 -0
  2. package/.github/workflows/ci.yml +58 -0
  3. package/.github/workflows/publish.yml +49 -0
  4. package/.github/workflows/typescript.yml +35 -0
  5. package/AGENTS.md +2 -2
  6. package/CLAUDE.md +24 -18
  7. package/CONTAINERS.md +9 -6
  8. package/CONTRIBUTING.md +29 -3
  9. package/Dockerfile +1 -1
  10. package/README.md +73 -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/docker-compose.yml +8 -2
  16. package/docs/checkpoint-scanning.md +199 -0
  17. package/docs/standard-result-outcome.md +165 -0
  18. package/env.example +44 -10
  19. package/eslint.config.mjs +101 -0
  20. package/netWatch.js +372 -222
  21. package/package.json +46 -31
  22. package/pour/README.md +47 -0
  23. package/pour/pour.min.js +8 -0
  24. package/procs/actDo.js +808 -0
  25. package/procs/catalog.d.ts +17 -0
  26. package/procs/catalog.js +312 -221
  27. package/procs/catalog.ts +398 -0
  28. package/procs/checkpoint.js +110 -0
  29. package/procs/config.d.ts +35 -0
  30. package/procs/config.js +62 -0
  31. package/procs/dateTime.js +2 -1
  32. package/procs/doActs.js +442 -849
  33. package/procs/doTestAct.js +27 -121
  34. package/procs/flow.js +221 -0
  35. package/procs/generateRuleRegistry.js +94 -0
  36. package/procs/getSource.d.ts +26 -0
  37. package/procs/job.js +115 -1
  38. package/procs/launch.d.ts +40 -0
  39. package/procs/launch.js +239 -108
  40. package/procs/nu.d.ts +48 -0
  41. package/procs/scope.js +196 -0
  42. package/procs/shoot.d.ts +33 -0
  43. package/procs/standard.d.ts +18 -0
  44. package/procs/standard.js +83 -0
  45. package/procs/standard.ts +126 -0
  46. package/procs/testAct.js +145 -0
  47. package/procs/testaro.d.ts +20 -0
  48. package/procs/testaro.js +236 -189
  49. package/procs/testaro.ts +321 -0
  50. package/procs/userPath.js +126 -0
  51. package/procs/xPath.d.ts +4 -0
  52. package/procs/xPath.js +81 -69
  53. package/procs/xPath.ts +106 -0
  54. package/procs/xPathScript.d.ts +6 -0
  55. package/procs/xPathScript.js +70 -0
  56. package/run.js +3 -2
  57. package/surea11y/README.md +44 -0
  58. package/surea11y/surea11y.browser.js +14 -0
  59. package/testaro/adbID.d.ts +3 -0
  60. package/testaro/adbID.js +38 -41
  61. package/testaro/adbID.ts +66 -0
  62. package/testaro/allCapStyle.d.ts +3 -0
  63. package/testaro/allCapStyle.js +31 -34
  64. package/testaro/allCapStyle.ts +55 -0
  65. package/testaro/allCaps.d.ts +16 -0
  66. package/testaro/allCaps.js +179 -151
  67. package/testaro/allCaps.ts +228 -0
  68. package/testaro/allHidden.d.ts +13 -0
  69. package/testaro/allHidden.js +29 -30
  70. package/testaro/allHidden.ts +50 -0
  71. package/testaro/allSlanted.d.ts +3 -0
  72. package/testaro/allSlanted.js +30 -33
  73. package/testaro/allSlanted.ts +54 -0
  74. package/testaro/altScheme.d.ts +3 -0
  75. package/testaro/altScheme.js +27 -30
  76. package/testaro/altScheme.ts +50 -0
  77. package/testaro/attVal.d.ts +3 -0
  78. package/testaro/attVal.js +20 -21
  79. package/testaro/attVal.ts +52 -0
  80. package/testaro/autocomplete.d.ts +3 -0
  81. package/testaro/autocomplete.js +59 -75
  82. package/testaro/autocomplete.ts +101 -0
  83. package/testaro/bulk.d.ts +13 -0
  84. package/testaro/bulk.js +32 -33
  85. package/testaro/bulk.ts +55 -0
  86. package/testaro/buttonMenu.d.ts +9 -0
  87. package/testaro/buttonMenu.js +317 -319
  88. package/testaro/buttonMenu.ts +391 -0
  89. package/testaro/captionLoc.d.ts +3 -0
  90. package/testaro/captionLoc.js +17 -20
  91. package/testaro/captionLoc.ts +40 -0
  92. package/testaro/datalistRef.d.ts +3 -0
  93. package/testaro/datalistRef.js +33 -36
  94. package/testaro/datalistRef.ts +55 -0
  95. package/testaro/distortion.d.ts +3 -0
  96. package/testaro/distortion.js +57 -26
  97. package/testaro/distortion.ts +81 -0
  98. package/testaro/docType.d.ts +15 -0
  99. package/testaro/docType.js +25 -25
  100. package/testaro/docType.ts +45 -0
  101. package/testaro/dupAtt.d.ts +16 -0
  102. package/testaro/dupAtt.js +113 -104
  103. package/testaro/dupAtt.ts +144 -0
  104. package/testaro/elements.d.ts +6 -0
  105. package/testaro/elements.js +153 -153
  106. package/testaro/elements.ts +215 -0
  107. package/testaro/embAc.d.ts +3 -0
  108. package/testaro/embAc.js +19 -20
  109. package/testaro/embAc.ts +40 -0
  110. package/testaro/focAll.d.ts +13 -0
  111. package/testaro/focAll.js +185 -191
  112. package/testaro/focAll.ts +217 -0
  113. package/testaro/focAndOp.d.ts +3 -0
  114. package/testaro/focAndOp.js +100 -104
  115. package/testaro/focAndOp.ts +128 -0
  116. package/testaro/focInd.d.ts +3 -0
  117. package/testaro/focInd.js +64 -66
  118. package/testaro/focInd.ts +96 -0
  119. package/testaro/focVis.d.ts +3 -0
  120. package/testaro/focVis.js +28 -30
  121. package/testaro/focVis.ts +52 -0
  122. package/testaro/headEl.d.ts +10 -0
  123. package/testaro/headEl.js +61 -62
  124. package/testaro/headEl.ts +82 -0
  125. package/testaro/headingAmb.d.ts +3 -0
  126. package/testaro/headingAmb.js +54 -63
  127. package/testaro/headingAmb.ts +72 -0
  128. package/testaro/hovInd.d.ts +12 -0
  129. package/testaro/hovInd.js +163 -130
  130. package/testaro/hovInd.ts +199 -0
  131. package/testaro/hover.d.ts +3 -0
  132. package/testaro/hover.js +155 -126
  133. package/testaro/hover.ts +154 -0
  134. package/testaro/hr.d.ts +3 -0
  135. package/testaro/hr.js +14 -17
  136. package/testaro/hr.ts +36 -0
  137. package/testaro/imageLink.d.ts +3 -0
  138. package/testaro/imageLink.js +17 -20
  139. package/testaro/imageLink.ts +42 -0
  140. package/testaro/labClash.d.ts +3 -0
  141. package/testaro/labClash.js +31 -33
  142. package/testaro/labClash.ts +54 -0
  143. package/testaro/legendLoc.d.ts +3 -0
  144. package/testaro/legendLoc.js +17 -20
  145. package/testaro/legendLoc.ts +42 -0
  146. package/testaro/lineHeight.d.ts +3 -0
  147. package/testaro/lineHeight.js +43 -48
  148. package/testaro/lineHeight.ts +70 -0
  149. package/testaro/linkAmb.d.ts +7 -0
  150. package/testaro/linkAmb.js +79 -80
  151. package/testaro/linkAmb.ts +105 -0
  152. package/testaro/linkExt.d.ts +3 -0
  153. package/testaro/linkExt.js +13 -16
  154. package/testaro/linkExt.ts +35 -0
  155. package/testaro/linkOldAtt.d.ts +3 -0
  156. package/testaro/linkOldAtt.js +25 -28
  157. package/testaro/linkOldAtt.ts +48 -0
  158. package/testaro/linkTo.d.ts +3 -0
  159. package/testaro/linkTo.js +22 -22
  160. package/testaro/linkTo.ts +43 -0
  161. package/testaro/linkUl.d.ts +3 -0
  162. package/testaro/linkUl.js +30 -34
  163. package/testaro/linkUl.ts +54 -0
  164. package/testaro/miniText.d.ts +3 -0
  165. package/testaro/miniText.js +41 -44
  166. package/testaro/miniText.ts +68 -0
  167. package/testaro/motion.d.ts +10 -0
  168. package/testaro/motion.js +92 -96
  169. package/testaro/motion.ts +125 -0
  170. package/testaro/nonTable.d.ts +3 -0
  171. package/testaro/nonTable.js +39 -45
  172. package/testaro/nonTable.ts +66 -0
  173. package/testaro/optRoleSel.d.ts +3 -0
  174. package/testaro/optRoleSel.js +16 -19
  175. package/testaro/optRoleSel.ts +41 -0
  176. package/testaro/phOnly.d.ts +3 -0
  177. package/testaro/phOnly.js +18 -21
  178. package/testaro/phOnly.ts +43 -0
  179. package/testaro/pseudoP.d.ts +3 -0
  180. package/testaro/pseudoP.js +36 -38
  181. package/testaro/pseudoP.ts +59 -0
  182. package/testaro/radioSet.d.ts +3 -0
  183. package/testaro/radioSet.js +57 -59
  184. package/testaro/radioSet.ts +79 -0
  185. package/testaro/registry.d.ts +63 -0
  186. package/testaro/registry.js +67 -0
  187. package/testaro/registry.ts +141 -0
  188. package/testaro/role.d.ts +3 -0
  189. package/testaro/role.js +27 -29
  190. package/testaro/role.ts +53 -0
  191. package/testaro/secHeading.d.ts +3 -0
  192. package/testaro/secHeading.js +30 -33
  193. package/testaro/secHeading.ts +53 -0
  194. package/testaro/styleDiff.d.ts +25 -0
  195. package/testaro/styleDiff.js +248 -252
  196. package/testaro/styleDiff.ts +303 -0
  197. package/testaro/tabNav.d.ts +33 -0
  198. package/testaro/tabNav.js +272 -342
  199. package/testaro/tabNav.ts +454 -0
  200. package/testaro/targetsNear.d.ts +9 -0
  201. package/testaro/targetsNear.js +130 -132
  202. package/testaro/targetsNear.ts +160 -0
  203. package/testaro/textNodes.d.ts +6 -0
  204. package/testaro/textNodes.js +139 -135
  205. package/testaro/textNodes.ts +185 -0
  206. package/testaro/textSem.d.ts +3 -0
  207. package/testaro/textSem.js +25 -28
  208. package/testaro/textSem.ts +47 -0
  209. package/testaro/title.d.ts +9 -0
  210. package/testaro/title.js +16 -13
  211. package/testaro/title.ts +31 -0
  212. package/testaro/titledEl.d.ts +3 -0
  213. package/testaro/titledEl.js +15 -18
  214. package/testaro/titledEl.ts +38 -0
  215. package/testaro/zIndex.d.ts +3 -0
  216. package/testaro/zIndex.js +19 -22
  217. package/testaro/zIndex.ts +42 -0
  218. package/tests/alfa.d.ts +45 -0
  219. package/tests/alfa.js +138 -141
  220. package/tests/alfa.ts +214 -0
  221. package/tests/aslint.d.ts +33 -0
  222. package/tests/aslint.js +272 -249
  223. package/tests/aslint.ts +301 -0
  224. package/tests/axe.d.ts +27 -0
  225. package/tests/axe.js +199 -200
  226. package/tests/axe.ts +277 -0
  227. package/tests/ed11y.d.ts +28 -0
  228. package/tests/ed11y.js +141 -99
  229. package/tests/ed11y.ts +178 -0
  230. package/tests/htmlcs.d.ts +21 -0
  231. package/tests/htmlcs.js +174 -140
  232. package/tests/htmlcs.ts +181 -0
  233. package/tests/ibm.d.ts +52 -0
  234. package/tests/ibm.js +164 -166
  235. package/tests/ibm.ts +251 -0
  236. package/tests/nuVal.d.ts +13 -0
  237. package/tests/nuVal.js +107 -112
  238. package/tests/nuVal.ts +145 -0
  239. package/tests/nuVnu.d.ts +15 -0
  240. package/tests/nuVnu.js +141 -111
  241. package/tests/nuVnu.ts +144 -0
  242. package/tests/pour.d.ts +31 -0
  243. package/tests/pour.js +242 -0
  244. package/tests/pour.ts +273 -0
  245. package/tests/qualWeb.d.ts +39 -0
  246. package/tests/qualWeb.js +302 -272
  247. package/tests/qualWeb.ts +415 -0
  248. package/tests/surea11y.d.ts +33 -0
  249. package/tests/surea11y.js +288 -0
  250. package/tests/surea11y.ts +334 -0
  251. package/tests/testaro.d.ts +25 -0
  252. package/tests/testaro.js +745 -652
  253. package/tests/testaro.ts +862 -0
  254. package/tests/wave.d.ts +46 -0
  255. package/tests/wave.js +166 -177
  256. package/tests/wave.ts +252 -0
  257. package/tsconfig.json +17 -0
  258. package/types.d.ts +243 -0
  259. package/types.js +10 -0
  260. package/types.ts +376 -0
  261. package/validation/act/README.md +46 -0
  262. package/validation/act/capture.js +424 -0
  263. package/validation/act/chromium-issue-draft.md +66 -0
  264. package/validation/act/fp-triage-2026-08-22.md +86 -0
  265. package/validation/act/isolation-notes.md +159 -0
  266. package/validation/act/playwright-issue-draft.md +73 -0
  267. package/validation/act/propose-mappings.js +0 -0
  268. package/validation/act/repro-cdp-raw.js +105 -0
  269. package/validation/act/repro-metarefresh.js +65 -0
  270. package/validation/act/score.js +184 -0
  271. package/validation/act/stage3a-stress-report.md +69 -0
  272. package/validation/act/stage3b-mapping-proposals.md +94 -0
  273. package/validation/act/stage3b-triage-draft.md +138 -0
  274. package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
  275. package/validation/executors/netWatch.js +180 -90
  276. package/validation/executors/test.js +17 -2
  277. package/validation/executors/tests.js +118 -10
  278. package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
  279. package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
  280. package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
  281. package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
  282. package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
  283. package/validation/knownFailures.json +1 -0
  284. package/validation/tests/jobProperties/adbID.json +27 -2
  285. package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
  286. package/validation/tests/jobProperties/allCaps.json +25 -0
  287. package/validation/tests/jobProperties/allHidden.json +133 -13
  288. package/validation/tests/jobProperties/allSlanted.json +3 -3
  289. package/validation/tests/jobProperties/altScheme.json +23 -8
  290. package/validation/tests/jobProperties/attVal.json +57 -57
  291. package/validation/tests/jobProperties/autocomplete.json +11 -1
  292. package/validation/tests/jobProperties/bulk.json +6 -1
  293. package/validation/tests/jobProperties/buttonMenu.json +63 -42
  294. package/validation/tests/jobProperties/captionLoc.json +1 -6
  295. package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
  296. package/validation/tests/jobProperties/checkpoint-page.json +397 -0
  297. package/validation/tests/jobProperties/checkpoint.json +395 -0
  298. package/validation/tests/jobProperties/datalistRef.json +20 -5
  299. package/validation/tests/jobProperties/distortion.json +28 -3
  300. package/validation/tests/jobProperties/docType.json +2 -2
  301. package/validation/tests/jobProperties/dupAtt.json +48 -33
  302. package/validation/tests/jobProperties/elements.json +28 -28
  303. package/validation/tests/jobProperties/embAc.json +36 -31
  304. package/validation/tests/jobProperties/focAndOp.json +284 -0
  305. package/validation/tests/jobProperties/focInd.json +34 -34
  306. package/validation/tests/jobProperties/focVis.json +1 -1
  307. package/validation/tests/jobProperties/hover.json +39 -37
  308. package/validation/tests/jobProperties/hr.json +13 -3
  309. package/validation/tests/jobProperties/imageLink.json +0 -5
  310. package/validation/tests/jobProperties/labClash.json +68 -33
  311. package/validation/tests/jobProperties/legendLoc.json +1 -6
  312. package/validation/tests/jobProperties/lineHeight.json +2 -2
  313. package/validation/tests/jobProperties/linkAmb.json +13 -18
  314. package/validation/tests/jobProperties/linkExt.json +1 -1
  315. package/validation/tests/jobProperties/linkOldAtt.json +12 -2
  316. package/validation/tests/jobProperties/linkTo.json +1 -1
  317. package/validation/tests/jobProperties/linkUl.json +65 -65
  318. package/validation/tests/jobProperties/miniText.json +7 -2
  319. package/validation/tests/jobProperties/motion.json +4 -50
  320. package/validation/tests/jobProperties/nonTable.json +48 -3
  321. package/validation/tests/jobProperties/optRoleSel.json +12 -2
  322. package/validation/tests/jobProperties/phOnly.json +6 -16
  323. package/validation/tests/jobProperties/pseudoP.json +17 -2
  324. package/validation/tests/jobProperties/radioSet.json +30 -30
  325. package/validation/tests/jobProperties/role.json +25 -5
  326. package/validation/tests/jobProperties/secHeading.json +26 -21
  327. package/validation/tests/jobProperties/styleDiff.json +35 -5
  328. package/validation/tests/jobProperties/tabNav.json +3 -1
  329. package/validation/tests/jobProperties/targetsNear.json +237 -0
  330. package/validation/tests/jobProperties/textNodes.json +29 -29
  331. package/validation/tests/jobProperties/textSem.json +26 -1
  332. package/validation/tests/jobProperties/title.json +12 -2
  333. package/validation/tests/jobProperties/titledEl.json +39 -9
  334. package/validation/tests/jobProperties/userPath.json +317 -0
  335. package/validation/tests/jobProperties/zIndex.json +40 -35
  336. package/validation/tests/targets/allCapStyle/index.html +26 -0
  337. package/validation/tests/targets/checkpoint/index.html +38 -0
  338. package/validation/tests/targets/datalistRef/index.html +1 -1
  339. package/validation/tests/targets/focAndOp/bad.html +29 -0
  340. package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
  341. package/validation/tests/targets/focInd/bad.html +2 -1
  342. package/validation/tests/targets/headEl/index.html +10 -1
  343. package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
  344. package/validation/tests/targets/userPath/index.html +42 -0
  345. package/validation/validateTest.js +61 -9
  346. package/.claude/settings.local.json +0 -11
  347. package/.eslintrc.json +0 -41
  348. package/htmlcs/.eslintrc.json +0 -67
  349. package/validation/tests/jobProperties/linkTitle.json +0 -127
  350. package/validation/tests/jobProperties/opFoc.json +0 -164
  351. package/validation/tests/jobProperties/targetSmall.json +0 -152
  352. package/validation/tests/jobProperties/targetTiny.json +0 -142
  353. package/validation/tests/targets/focOp/bad.html +0 -25
  354. package/validation/tests/targets/linkTitle/index.html +0 -24
  355. package/validation/tests/targets/opFoc/bad.html +0 -26
  356. package/validation/tests/targets/opFoc/good.html +0 -23
package/tests/testaro.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -8,670 +9,762 @@
8
9
 
9
10
  SPDX-License-Identifier: MIT
10
11
  */
11
-
12
- /*
13
- testaro
14
- Implements the Testaro evaluative rules.
15
- */
16
-
17
- // IMPORTS
18
-
19
- // Shared configuration for timeout multiplier.
20
- const {applyMultiplier} = require('../procs/config');
21
- const {launch} = require('../procs/launch');
22
-
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.reporter = void 0;
14
+ const config_1 = require("../procs/config");
15
+ const launch_1 = require("../procs/launch");
16
+ // Function to get an empty standard result.
17
+ const standard_1 = require("../procs/standard");
18
+ const registry_1 = require("../testaro/registry");
23
19
  // CONSTANTS
24
-
25
20
  // Metadata of all rules in default execution order.
26
21
  const allRules = [
27
- {
28
- id: 'adbID',
29
- what: 'elements with ambiguous or missing referenced descriptions',
30
- contaminates: false,
31
- needsAccessibleName: false,
32
- timeOut: 5,
33
- defaultOn: true
34
- },
35
- {
36
- id: 'allCaps',
37
- what: 'elements with unnecessarily all-capital text substrings',
38
- contaminates: false,
39
- needsAccessibleName: false,
40
- timeOut: 30,
41
- defaultOn: true
42
- },
43
- {
44
- id: 'allHidden',
45
- what: 'page that is entirely or mostly hidden',
46
- contaminates: false,
47
- needsAccessibleName: false,
48
- timeOut: 5,
49
- defaultOn: true
50
- },
51
- {
52
- id: 'allSlanted',
53
- what: 'leaf elements with entirely italic or oblique text longer than 39 characters',
54
- contaminates: false,
55
- needsAccessibleName: false,
56
- timeOut: 5,
57
- defaultOn: true
58
- },
59
- {
60
- id: 'altScheme',
61
- what: 'img elements with alt attributes having URLs as their entire values',
62
- contaminates: false,
63
- needsAccessibleName: false,
64
- timeOut: 5,
65
- defaultOn: true
66
- },
67
- {
68
- id: 'attVal',
69
- what: 'elements with attributes having illicit values',
70
- contaminates: false,
71
- needsAccessibleName: false,
72
- timeOut: 5,
73
- defaultOn: false
74
- },
75
- {
76
- id: 'bulk',
77
- what: 'large count of visible elements',
78
- contaminates: false,
79
- needsAccessibleName: false,
80
- timeOut: 5,
81
- defaultOn: true
82
- },
83
- {
84
- id: 'captionLoc',
85
- what: 'caption elements that are not first children of table elements',
86
- contaminates: false,
87
- needsAccessibleName: false,
88
- timeOut: 5,
89
- defaultOn: true
90
- },
91
- {
92
- id: 'datalistRef',
93
- what: 'elements with ambiguous or missing referenced datalist elements',
94
- contaminates: false,
95
- needsAccessibleName: false,
96
- timeOut: 5,
97
- defaultOn: true
98
- },
99
- {
100
- id: 'distortion',
101
- what: 'distorted text',
102
- contaminates: false,
103
- needsAccessibleName: false,
104
- timeOut: 5,
105
- defaultOn: true
106
- },
107
- {
108
- id: 'docType',
109
- what: 'document without a doctype property',
110
- contaminates: false,
111
- needsAccessibleName: false,
112
- timeOut: 5,
113
- defaultOn: true
114
- },
115
- {
116
- id: 'embAc',
117
- what: 'active elements embedded in links or buttons',
118
- contaminates: false,
119
- needsAccessibleName: false,
120
- timeOut: 5,
121
- defaultOn: true
122
- },
123
- {
124
- id: 'headEl',
125
- what: 'invalid elements within the head',
126
- contaminates: false,
127
- needsAccessibleName: false,
128
- timeOut: 5,
129
- defaultOn: true
130
- },
131
- {
132
- id: 'headingAmb',
133
- what: 'same-level sibling headings with identical texts',
134
- contaminates: false,
135
- needsAccessibleName: false,
136
- timeOut: 5,
137
- defaultOn: true
138
- },
139
- {
140
- id: 'hr',
141
- what: 'hr element instead of styles used for vertical segmentation',
142
- contaminates: false,
143
- needsAccessibleName: false,
144
- timeOut: 5,
145
- defaultOn: true
146
- },
147
- {
148
- id: 'imageLink',
149
- what: 'links with image files as their destinations',
150
- contaminates: false,
151
- needsAccessibleName: false,
152
- timeOut: 5,
153
- defaultOn: true
154
- },
155
- {
156
- id: 'labClash',
157
- what: 'labeling inconsistencies',
158
- contaminates: false,
159
- needsAccessibleName: false,
160
- timeOut: 5,
161
- defaultOn: true
162
- },
163
- {
164
- id: 'legendLoc',
165
- what: 'legend elements that are not first children of fieldset elements',
166
- contaminates: false,
167
- needsAccessibleName: false,
168
- timeOut: 5,
169
- defaultOn: true
170
- },
171
- {
172
- id: 'lineHeight',
173
- what: 'text with a line height less than 1.5 times its font size',
174
- contaminates: false,
175
- needsAccessibleName: false,
176
- timeOut: 5,
177
- defaultOn: true
178
- },
179
- {
180
- id: 'linkAmb',
181
- what: 'links with identical texts but different destinations',
182
- contaminates: false,
183
- needsAccessibleName: false,
184
- timeOut: 20,
185
- defaultOn: true
186
- },
187
- {
188
- id: 'linkExt',
189
- what: 'links that automatically open new windows',
190
- contaminates: false,
191
- needsAccessibleName: false,
192
- timeOut: 5,
193
- defaultOn: true
194
- },
195
- {
196
- id: 'linkOldAtt',
197
- what: 'links with deprecated attributes',
198
- contaminates: false,
199
- needsAccessibleName: false,
200
- timeOut: 5,
201
- defaultOn: true
202
- },
203
- {
204
- id: 'linkTo',
205
- what: 'links without destinations',
206
- contaminates: false,
207
- needsAccessibleName: false,
208
- timeOut: 5,
209
- defaultOn: true
210
- },
211
- {
212
- id: 'linkUl',
213
- what: 'missing underlines on inline links',
214
- contaminates: false,
215
- needsAccessibleName: false,
216
- timeOut: 5,
217
- defaultOn: true
218
- },
219
- {
220
- id: 'miniText',
221
- what: 'text smaller than 11 pixels',
222
- contaminates: false,
223
- needsAccessibleName: false,
224
- timeOut: 5,
225
- defaultOn: true
226
- },
227
- {
228
- id: 'nonTable',
229
- what: 'table elements used for layout',
230
- contaminates: false,
231
- needsAccessibleName: false,
232
- timeOut: 5,
233
- defaultOn: true
234
- },
235
- {
236
- id: 'optRoleSel',
237
- what: 'Non-option elements with option roles that have no aria-selected attributes',
238
- contaminates: false,
239
- needsAccessibleName: false,
240
- timeOut: 5,
241
- defaultOn: true
242
- },
243
- {
244
- id: 'pseudoP',
245
- what: 'adjacent br elements suspected of nonsemantically simulating p elements',
246
- contaminates: false,
247
- needsAccessibleName: false,
248
- timeOut: 5,
249
- defaultOn: true
250
- },
251
- {
252
- id: 'radioSet',
253
- what: 'radio buttons not grouped into standard field sets',
254
- contaminates: false,
255
- needsAccessibleName: false,
256
- timeOut: 5,
257
- defaultOn: true
258
- },
259
- {
260
- id: 'role',
261
- what: 'native-replacing explicit roles',
262
- contaminates: false,
263
- needsAccessibleName: false,
264
- timeOut: 20,
265
- defaultOn: true
266
- },
267
- {
268
- id: 'secHeading',
269
- what: 'headings that violate the logical level order in their sectioning containers',
270
- contaminates: false,
271
- needsAccessibleName: false,
272
- timeOut: 5,
273
- defaultOn: true
274
- },
275
- {
276
- id: 'styleDiff',
277
- what: 'style inconsistencies',
278
- contaminates: false,
279
- needsAccessibleName: false,
280
- timeOut: 5,
281
- defaultOn: true
282
- },
283
- {
284
- id: 'targetsNear',
285
- what: 'labels, buttons, inputs, and links too near each other',
286
- contaminates: false,
287
- needsAccessibleName: false,
288
- timeOut: 5,
289
- defaultOn: true
290
- },
291
- {
292
- id: 'textSem',
293
- what: 'semantically vague elements i, b, and/or small',
294
- contaminates: false,
295
- needsAccessibleName: false,
296
- timeOut: 5,
297
- defaultOn: true
298
- },
299
- {
300
- id: 'title',
301
- what: 'page title',
302
- contaminates: false,
303
- needsAccessibleName: false,
304
- timeOut: 5,
305
- defaultOn: false
306
- },
307
- {
308
- id: 'titledEl',
309
- what: 'title attributes on inappropriate elements',
310
- contaminates: false,
311
- needsAccessibleName: false,
312
- timeOut: 5,
313
- defaultOn: true
314
- },
315
- {
316
- id: 'zIndex',
317
- what: 'non-default Z indexes',
318
- contaminates: false,
319
- needsAccessibleName: false,
320
- timeOut: 5,
321
- defaultOn: true
322
- },
323
- {
324
- id: 'motion',
325
- what: 'motion without user request',
326
- contaminates: false,
327
- needsAccessibleName: false,
328
- // The budget must cover a full-page screenshot (itself allowed 4 seconds in
329
- // procs/shoot.js), decoding two full-page PNGs, and a pixel comparison; 5
330
- // seconds made the rule time out whenever an initial image existed.
331
- timeOut: 30,
332
- defaultOn: true
333
- },
334
- {
335
- id: 'dupAtt',
336
- what: 'duplicate attribute values',
337
- contaminates: false,
338
- needsAccessibleName: false,
339
- timeOut: 5,
340
- defaultOn: true
341
- },
342
- {
343
- id: 'autocomplete',
344
- what: 'name and email inputs without autocomplete attributes',
345
- contaminates: false,
346
- needsAccessibleName: true,
347
- timeOut: 5,
348
- defaultOn: true
349
- },
350
- {
351
- id: 'phOnly',
352
- what: 'input elements with placeholders but no accessible names',
353
- contaminates: false,
354
- needsAccessibleName: true,
355
- timeOut: 5,
356
- defaultOn: true
357
- },
358
- {
359
- id: 'buttonMenu',
360
- what: 'nonstandard keyboard navigation between items of button-controlled menus',
361
- contaminates: true,
362
- needsAccessibleName: false,
363
- timeOut: 15,
364
- defaultOn: true
365
- },
366
- {
367
- id: 'elements',
368
- what: 'data on specified elements',
369
- contaminates: true,
370
- needsAccessibleName: false,
371
- timeOut: 10,
372
- defaultOn: false
373
- },
374
- {
375
- id: 'focAll',
376
- what: 'discrepancies between focusable and Tab-focused elements',
377
- contaminates: true,
378
- needsAccessibleName: false,
379
- timeOut: 10,
380
- defaultOn: true
381
- },
382
- {
383
- id: 'focAndOp',
384
- what: 'Tab-focusable elements that are not operable or vice versa',
385
- contaminates: true,
386
- needsAccessibleName: false,
387
- timeOut: 5,
388
- defaultOn: true
389
- },
390
- {
391
- id: 'focInd',
392
- what: 'missing and nonstandard focus indicators',
393
- contaminates: true,
394
- needsAccessibleName: false,
395
- timeOut: 10,
396
- defaultOn: true
397
- },
398
- {
399
- id: 'focVis',
400
- what: 'links that are not entirely visible when focused',
401
- contaminates: true,
402
- needsAccessibleName: false,
403
- timeOut: 10,
404
- defaultOn: true
405
- },
406
- {
407
- id: 'hover',
408
- what: 'hover-caused content changes',
409
- contaminates: true,
410
- needsAccessibleName: false,
411
- timeOut: 20,
412
- defaultOn: true
413
- },
414
- {
415
- id: 'hovInd',
416
- what: 'hover indication nonstandard',
417
- contaminates: true,
418
- needsAccessibleName: false,
419
- timeOut: 10,
420
- defaultOn: true
421
- },
422
- {
423
- id: 'tabNav',
424
- what: 'nonstandard keyboard navigation between elements with the tab role',
425
- contaminates: true,
426
- needsAccessibleName: false,
427
- timeOut: 10,
428
- defaultOn: true
429
- },
430
- {
431
- id: 'textNodes',
432
- what: 'data on specified text nodes',
433
- contaminates: true,
434
- needsAccessibleName: false,
435
- timeOut: 10,
436
- defaultOn: false
437
- }
22
+ {
23
+ id: 'adbID',
24
+ what: 'elements with ambiguous or missing referenced descriptions',
25
+ contaminates: false,
26
+ needsAccessibleName: false,
27
+ local: true,
28
+ timeOut: 5,
29
+ defaultOn: true
30
+ },
31
+ {
32
+ id: 'allCapStyle',
33
+ what: 'elements with all-capital text transformation styles',
34
+ contaminates: false,
35
+ needsAccessibleName: false,
36
+ local: true,
37
+ timeOut: 5,
38
+ defaultOn: false
39
+ },
40
+ {
41
+ id: 'allCaps',
42
+ what: 'elements with unnecessarily all-capital text substrings',
43
+ contaminates: false,
44
+ needsAccessibleName: false,
45
+ local: true,
46
+ outcome: 'cantTell',
47
+ uncertainty: 'judgement-required',
48
+ timeOut: 30,
49
+ defaultOn: true
50
+ },
51
+ {
52
+ id: 'allHidden',
53
+ what: 'page that is entirely or mostly hidden',
54
+ contaminates: false,
55
+ needsAccessibleName: false,
56
+ local: false,
57
+ timeOut: 5,
58
+ defaultOn: true
59
+ },
60
+ {
61
+ id: 'allSlanted',
62
+ what: 'leaf elements with entirely italic or oblique text longer than 39 characters',
63
+ contaminates: false,
64
+ needsAccessibleName: false,
65
+ local: true,
66
+ timeOut: 5,
67
+ defaultOn: true
68
+ },
69
+ {
70
+ id: 'altScheme',
71
+ what: 'img elements with alt attributes having URLs as their entire values',
72
+ contaminates: false,
73
+ needsAccessibleName: false,
74
+ local: true,
75
+ timeOut: 5,
76
+ defaultOn: true
77
+ },
78
+ {
79
+ id: 'attVal',
80
+ what: 'elements with attributes having illicit values',
81
+ contaminates: false,
82
+ needsAccessibleName: false,
83
+ local: true,
84
+ timeOut: 5,
85
+ defaultOn: false
86
+ },
87
+ {
88
+ id: 'bulk',
89
+ what: 'large count of visible elements',
90
+ contaminates: false,
91
+ needsAccessibleName: false,
92
+ local: false,
93
+ timeOut: 5,
94
+ defaultOn: true
95
+ },
96
+ {
97
+ id: 'captionLoc',
98
+ what: 'caption elements that are not first children of table elements',
99
+ contaminates: false,
100
+ needsAccessibleName: false,
101
+ local: true,
102
+ timeOut: 5,
103
+ defaultOn: true
104
+ },
105
+ {
106
+ id: 'datalistRef',
107
+ what: 'elements with ambiguous or missing referenced datalist elements',
108
+ contaminates: false,
109
+ needsAccessibleName: false,
110
+ local: true,
111
+ timeOut: 5,
112
+ defaultOn: true
113
+ },
114
+ {
115
+ id: 'distortion',
116
+ what: 'distorted text',
117
+ contaminates: false,
118
+ needsAccessibleName: false,
119
+ local: true,
120
+ timeOut: 5,
121
+ defaultOn: true
122
+ },
123
+ {
124
+ id: 'docType',
125
+ what: 'document without a doctype property',
126
+ contaminates: false,
127
+ needsAccessibleName: false,
128
+ local: false,
129
+ timeOut: 5,
130
+ defaultOn: true
131
+ },
132
+ {
133
+ id: 'embAc',
134
+ what: 'active elements embedded in links or buttons',
135
+ contaminates: false,
136
+ needsAccessibleName: false,
137
+ local: true,
138
+ timeOut: 5,
139
+ defaultOn: true
140
+ },
141
+ {
142
+ id: 'headEl',
143
+ what: 'invalid elements within the head',
144
+ contaminates: false,
145
+ needsAccessibleName: false,
146
+ local: false,
147
+ timeOut: 5,
148
+ defaultOn: true
149
+ },
150
+ {
151
+ id: 'headingAmb',
152
+ what: 'same-level sibling headings with identical texts',
153
+ contaminates: false,
154
+ needsAccessibleName: false,
155
+ local: false,
156
+ timeOut: 5,
157
+ defaultOn: true
158
+ },
159
+ {
160
+ id: 'hr',
161
+ what: 'hr element instead of styles used for vertical segmentation',
162
+ contaminates: false,
163
+ needsAccessibleName: false,
164
+ local: true,
165
+ timeOut: 5,
166
+ defaultOn: true
167
+ },
168
+ {
169
+ id: 'imageLink',
170
+ what: 'links with image files as their destinations',
171
+ contaminates: false,
172
+ needsAccessibleName: false,
173
+ local: true,
174
+ timeOut: 5,
175
+ defaultOn: true
176
+ },
177
+ {
178
+ id: 'labClash',
179
+ what: 'labeling inconsistencies',
180
+ contaminates: false,
181
+ needsAccessibleName: false,
182
+ local: false,
183
+ timeOut: 5,
184
+ defaultOn: true
185
+ },
186
+ {
187
+ id: 'legendLoc',
188
+ what: 'legend elements that are not first children of fieldset elements',
189
+ contaminates: false,
190
+ needsAccessibleName: false,
191
+ local: true,
192
+ timeOut: 5,
193
+ defaultOn: true
194
+ },
195
+ {
196
+ id: 'lineHeight',
197
+ what: 'text with a line height less than 1.5 times its font size',
198
+ contaminates: false,
199
+ needsAccessibleName: false,
200
+ local: true,
201
+ timeOut: 5,
202
+ defaultOn: true
203
+ },
204
+ {
205
+ id: 'linkAmb',
206
+ what: 'links with identical texts but different destinations',
207
+ contaminates: false,
208
+ needsAccessibleName: false,
209
+ local: false,
210
+ timeOut: 20,
211
+ defaultOn: true
212
+ },
213
+ {
214
+ id: 'linkExt',
215
+ what: 'links that automatically open new windows',
216
+ contaminates: false,
217
+ needsAccessibleName: false,
218
+ local: true,
219
+ timeOut: 5,
220
+ defaultOn: true
221
+ },
222
+ {
223
+ id: 'linkOldAtt',
224
+ what: 'links with deprecated attributes',
225
+ contaminates: false,
226
+ needsAccessibleName: false,
227
+ local: true,
228
+ timeOut: 5,
229
+ defaultOn: true
230
+ },
231
+ {
232
+ id: 'linkTo',
233
+ what: 'links without destinations',
234
+ contaminates: false,
235
+ needsAccessibleName: false,
236
+ local: true,
237
+ timeOut: 5,
238
+ defaultOn: true
239
+ },
240
+ {
241
+ id: 'linkUl',
242
+ what: 'missing underlines on inline links',
243
+ contaminates: false,
244
+ needsAccessibleName: false,
245
+ local: true,
246
+ timeOut: 5,
247
+ defaultOn: true
248
+ },
249
+ {
250
+ id: 'miniText',
251
+ what: 'text smaller than 11 pixels',
252
+ contaminates: false,
253
+ needsAccessibleName: false,
254
+ local: true,
255
+ timeOut: 5,
256
+ defaultOn: true
257
+ },
258
+ {
259
+ id: 'nonTable',
260
+ what: 'table elements used for layout',
261
+ contaminates: false,
262
+ needsAccessibleName: false,
263
+ local: true,
264
+ timeOut: 5,
265
+ defaultOn: true
266
+ },
267
+ {
268
+ id: 'optRoleSel',
269
+ what: 'Non-option elements with option roles that have no aria-selected attributes',
270
+ contaminates: false,
271
+ needsAccessibleName: false,
272
+ local: true,
273
+ timeOut: 5,
274
+ defaultOn: true
275
+ },
276
+ {
277
+ id: 'pseudoP',
278
+ what: 'adjacent br elements suspected of nonsemantically simulating p elements',
279
+ contaminates: false,
280
+ needsAccessibleName: false,
281
+ local: true,
282
+ timeOut: 5,
283
+ defaultOn: true
284
+ },
285
+ {
286
+ id: 'radioSet',
287
+ what: 'radio buttons not grouped into standard field sets',
288
+ contaminates: false,
289
+ needsAccessibleName: false,
290
+ local: false,
291
+ timeOut: 5,
292
+ defaultOn: true
293
+ },
294
+ {
295
+ id: 'role',
296
+ what: 'native-replacing explicit roles',
297
+ contaminates: false,
298
+ needsAccessibleName: false,
299
+ local: false,
300
+ timeOut: 20,
301
+ defaultOn: true
302
+ },
303
+ {
304
+ id: 'secHeading',
305
+ what: 'headings that violate the logical level order in their sectioning containers',
306
+ contaminates: false,
307
+ needsAccessibleName: false,
308
+ local: false,
309
+ timeOut: 5,
310
+ defaultOn: true
311
+ },
312
+ {
313
+ id: 'styleDiff',
314
+ what: 'style inconsistencies',
315
+ contaminates: false,
316
+ needsAccessibleName: false,
317
+ local: false,
318
+ timeOut: 5,
319
+ defaultOn: true
320
+ },
321
+ {
322
+ id: 'targetsNear',
323
+ what: 'labels, buttons, inputs, and links too near each other',
324
+ contaminates: false,
325
+ needsAccessibleName: false,
326
+ local: false,
327
+ timeOut: 5,
328
+ defaultOn: true
329
+ },
330
+ {
331
+ id: 'textSem',
332
+ what: 'semantically vague elements i, b, and/or small',
333
+ contaminates: false,
334
+ needsAccessibleName: false,
335
+ local: true,
336
+ timeOut: 5,
337
+ defaultOn: true
338
+ },
339
+ {
340
+ id: 'title',
341
+ what: 'page title',
342
+ contaminates: false,
343
+ needsAccessibleName: false,
344
+ local: false,
345
+ timeOut: 5,
346
+ defaultOn: false
347
+ },
348
+ {
349
+ id: 'titledEl',
350
+ what: 'title attributes on inappropriate elements',
351
+ contaminates: false,
352
+ needsAccessibleName: false,
353
+ local: true,
354
+ timeOut: 5,
355
+ defaultOn: true
356
+ },
357
+ {
358
+ id: 'zIndex',
359
+ what: 'non-default Z indexes',
360
+ contaminates: false,
361
+ needsAccessibleName: false,
362
+ local: true,
363
+ timeOut: 5,
364
+ defaultOn: true
365
+ },
366
+ {
367
+ id: 'motion',
368
+ what: 'motion without user request',
369
+ contaminates: false,
370
+ needsAccessibleName: false,
371
+ local: false,
372
+ // The budget must cover a full-page screenshot (itself allowed 4 seconds in
373
+ // procs/shoot.js), decoding two full-page PNGs, and a pixel comparison; 5
374
+ // seconds made the rule time out whenever an initial image existed.
375
+ timeOut: 30,
376
+ defaultOn: true
377
+ },
378
+ {
379
+ id: 'dupAtt',
380
+ what: 'duplicate attribute values',
381
+ contaminates: false,
382
+ needsAccessibleName: false,
383
+ local: false,
384
+ timeOut: 5,
385
+ defaultOn: true
386
+ },
387
+ {
388
+ id: 'autocomplete',
389
+ what: 'name and email inputs without autocomplete attributes',
390
+ contaminates: false,
391
+ needsAccessibleName: true,
392
+ local: true,
393
+ timeOut: 5,
394
+ defaultOn: true
395
+ },
396
+ {
397
+ id: 'phOnly',
398
+ what: 'input elements with placeholders but no accessible names',
399
+ contaminates: false,
400
+ needsAccessibleName: true,
401
+ local: true,
402
+ timeOut: 5,
403
+ defaultOn: true
404
+ },
405
+ {
406
+ id: 'buttonMenu',
407
+ what: 'nonstandard keyboard navigation between items of button-controlled menus',
408
+ contaminates: true,
409
+ needsAccessibleName: false,
410
+ local: false,
411
+ timeOut: 15,
412
+ defaultOn: true
413
+ },
414
+ {
415
+ id: 'elements',
416
+ what: 'data on specified elements',
417
+ contaminates: true,
418
+ needsAccessibleName: false,
419
+ local: false,
420
+ timeOut: 10,
421
+ defaultOn: false
422
+ },
423
+ {
424
+ id: 'focAll',
425
+ what: 'discrepancies between focusable and Tab-focused elements',
426
+ contaminates: true,
427
+ needsAccessibleName: false,
428
+ local: false,
429
+ timeOut: 10,
430
+ defaultOn: true
431
+ },
432
+ {
433
+ id: 'focAndOp',
434
+ what: 'Tab-focusable elements that are not operable or vice versa',
435
+ contaminates: true,
436
+ needsAccessibleName: false,
437
+ local: false,
438
+ timeOut: 5,
439
+ defaultOn: true
440
+ },
441
+ {
442
+ id: 'focInd',
443
+ what: 'missing and nonstandard focus indicators',
444
+ contaminates: true,
445
+ needsAccessibleName: false,
446
+ local: false,
447
+ timeOut: 10,
448
+ defaultOn: true
449
+ },
450
+ {
451
+ id: 'focVis',
452
+ what: 'links that are not entirely visible when focused',
453
+ contaminates: true,
454
+ needsAccessibleName: false,
455
+ local: false,
456
+ timeOut: 10,
457
+ defaultOn: true
458
+ },
459
+ {
460
+ id: 'hover',
461
+ what: 'hover-caused content changes',
462
+ contaminates: true,
463
+ needsAccessibleName: false,
464
+ local: false,
465
+ timeOut: 20,
466
+ defaultOn: true
467
+ },
468
+ {
469
+ id: 'hovInd',
470
+ what: 'hover indication nonstandard',
471
+ contaminates: true,
472
+ needsAccessibleName: false,
473
+ local: false,
474
+ timeOut: 10,
475
+ defaultOn: true
476
+ },
477
+ {
478
+ id: 'tabNav',
479
+ what: 'nonstandard keyboard navigation between elements with the tab role',
480
+ contaminates: true,
481
+ needsAccessibleName: false,
482
+ local: false,
483
+ timeOut: 10,
484
+ defaultOn: true
485
+ },
486
+ {
487
+ id: 'textNodes',
488
+ what: 'data on specified text nodes',
489
+ contaminates: true,
490
+ needsAccessibleName: false,
491
+ local: false,
492
+ timeOut: 10,
493
+ defaultOn: false
494
+ }
438
495
  ];
439
-
440
496
  // ERROR HANDLER
441
497
  process.on('unhandledRejection', reason => {
442
- console.error(`ERROR: Unhandled Promise Rejection (${reason})`);
498
+ console.error(`ERROR: Unhandled Promise Rejection (${reason})`);
443
499
  });
444
-
445
500
  // FUNCTIONS
446
-
447
501
  // Conducts and reports Testaro tests.
448
- exports.reporter = async (page, report, actIndex) => {
449
- const act = report.acts[actIndex];
450
- const {args, stopOnFail, withItems} = act;
451
- const target = act.target || report.target;
452
- const url = target.url;
453
- const browserID = act.launch ? act.launch.browserID || report.browserID : report.browserID;
454
- const argRules = args ? Object.keys(args) : null;
455
- // Get the specification of rules to be tested for or, by default, all rules with defaultOn true.
456
- const ruleSpec = act.rules
457
- || ['y', ... allRules.filter(rule => rule.defaultOn).map(rule => rule.id)];
458
- // Initialize the act data.
459
- const data = {
460
- prevented: false,
461
- error: '',
462
- rulePreventions: {},
463
- rulesInvalid: [],
464
- ruleTestTimes: [],
465
- ruleData: {}
466
- };
467
- // Initialize the act result.
468
- const result = {
469
- nativeResult: {},
470
- standardResult: {
471
- prevented: false,
472
- totals: [0, 0, 0, 0],
473
- instances: []
502
+ const reporter = async (page, report, actIndex) => {
503
+ const act = report.acts[actIndex];
504
+ const givenPage = page;
505
+ const { args, stopOnFail, withItems } = act;
506
+ // A testaro act always has a target on itself or the report; verbatim from the original.
507
+ const target = (act.target || report.target);
508
+ const url = target.url;
509
+ const browserID = act.launch ? act.launch.browserID || report.browserID : report.browserID;
510
+ const argRules = args ? Object.keys(args) : null;
511
+ // Get the specification of rules to be tested for or, by default, all rules with defaultOn true.
512
+ const ruleSpec = act.rules
513
+ || ['y', ...allRules.filter(rule => rule.defaultOn).map(rule => rule.id)];
514
+ // Initialize the act data.
515
+ const data = {
516
+ prevented: false,
517
+ error: '',
518
+ rulePreventions: {},
519
+ rulesInvalid: [],
520
+ ruleTestTimes: [],
521
+ ruleData: {}
522
+ };
523
+ // The changed subtree roots the act is scoped to, if any (report.scope, set by the acts loop).
524
+ const scopeRoots = report.scope?.roots ?? null;
525
+ if (scopeRoots) {
526
+ data.scope = { localRules: [], pageRules: [] };
474
527
  }
475
- };
476
- const {standardResult} = result;
477
- const allRuleIDs = allRules.map(rule => rule.id);
478
- if (
528
+ // Initialize the act result.
529
+ const result = {
530
+ nativeResult: {},
531
+ standardResult: (0, standard_1.getStandardResult)()
532
+ };
533
+ const { standardResult } = result;
534
+ const allRuleIDs = allRules.map(rule => rule.id);
535
+ if (
479
536
  // If the rule specification has at least 2 items
480
537
  ruleSpec.length > 1
481
- // and the first item is y or n
482
- && ['y', 'n'].includes(ruleSpec[0])
483
- // and all subsequent items are rule IDs to be included (if y) or excluded (if n):
484
- && ruleSpec.slice(1).every(ruleID => allRuleIDs.includes(ruleID))
485
- ) {
486
- // Get the rules to be tested for and their execution order.
487
- const excludeIDs = ruleSpec.slice(1);
488
- const jobRuleIDs = ruleSpec[0] === 'y'
489
- ? excludeIDs
490
- : allRules
491
- .filter(rule => rule.defaultOn && ! excludeIDs.includes(rule.id))
492
- .map(rule => rule.id);
493
- const jobRules = allRules.filter(rule => jobRuleIDs.includes(rule.id));
494
- let justPrevented = false;
495
- // For each rule to be tested for:
496
- for (let ruleIndex = 0; ruleIndex < jobRules.length; ruleIndex++) {
497
- const rule = jobRules[ruleIndex];
498
- // Initialize the rule result.
499
- const ruleResult = {
500
- id: rule.id,
501
- prevented: false,
502
- error: '',
503
- data: {},
504
- totals: [0, 0, 0, 0],
505
- instances: [],
506
- elapsedTime: 0
507
- };
508
- console.log(`Starting rule ${ruleResult.id}`);
509
- // Make the browser emulate headedness in all cases, because performance does not suffer.
510
- const headEmulation = ruleResult.id.startsWith('shoot') ? 'high' : 'high';
511
- // Get whether the rule needs a new browser launched.
512
- const needsLaunch = ruleIndex === 0
513
- || justPrevented
514
- || jobRules[ruleIndex - 1].contaminates
515
- || jobRules[ruleIndex].needsAccessibleName && ! jobRules[ruleIndex - 1].needsAccessibleName;
516
- const pageClosed = page && page.isClosed();
517
- // If it does, or if the page has closed:
518
- if (needsLaunch || pageClosed) {
519
- // If the page has closed when it is expected to be open:
520
- if (pageClosed && ! needsLaunch) {
521
- // Report this.
522
- console.log(`WARNING: Relaunching browser for test ${rule} after abnormal closure`);
523
- }
524
- // Create a browser, replace the page, and visit the target, retrying twice if necessary.
525
- page = await launch({
526
- report,
527
- actIndex,
528
- tempBrowserID: browserID,
529
- tempURL: url,
530
- headEmulation,
531
- xPathNeed: 'script',
532
- needsAccessibleName: jobRules[ruleIndex].needsAccessibleName,
533
- retries: 2
534
- });
535
- }
536
- // Report crashes and disconnections during this test.
537
- let crashHandler;
538
- let disconnectHandler;
539
- if (page && ! page.isClosed()) {
540
- crashHandler = () => {
541
- console.log(`ERROR: Page crashed during ${rule} test`);
542
- };
543
- page.on('crash', crashHandler);
544
- }
545
- const browser = page.context().browser();
546
- if (browser) {
547
- disconnectHandler = () => {
548
- console.log(`ERROR: Browser disconnected during ${rule} test`);
549
- };
550
- browser.on('disconnected', disconnectHandler);
551
- }
552
- // Initialize an argument array for the reporter.
553
- const ruleArgs = [page, report, actIndex, withItems];
554
- // If the rule needs a temporary directory:
555
- if (rule.needsTmpDir) {
556
- // Add its path to the argument array.
557
- ruleArgs.push(report.jobData.tmpDir);
558
- }
559
- // If the testaro test act specifies extra arguments for this rule:
560
- if (argRules?.includes(ruleResult.id)) {
561
- // Add them to the argument array.
562
- ruleArgs.push(... args[ruleResult.id]);
563
- }
564
- const startTime = Date.now();
565
- let timer;
566
- try {
567
- // Apply a time limit to the test.
568
- const timeLimit = applyMultiplier(1000 * rule.timeOut);
569
- let timeout;
570
- // If the time limit expires during the test:
571
- timer = new Promise(resolve => {
572
- timeout = setTimeout(() => {
573
- // Add data about the timeout to the rule result.
574
- justPrevented = true;
575
- ruleResult.prevented = true;
576
- ruleResult.error = 'Timeout';
577
- console.log(`ERROR: Test of testaro rule ${ruleResult.id} timed out`);
578
- resolve({timedOut: true});
579
- }, timeLimit);
580
- });
581
- // Try to perform the test and get a test report.
582
- const testReport = require(`../testaro/${ruleResult.id}`).reporter(... ruleArgs);
583
- // Get a test or timeout report.
584
- const ruleReport = await Promise.race([timer, testReport]);
585
- clearTimeout(timeout);
586
- // If it was a test report:
587
- if (! ruleReport.timedOut) {
588
- // Add the rule-report properties to the rule result.
589
- ruleResult.data = ruleReport.data;
590
- ruleResult.totals = ruleReport.totals;
591
- ruleResult.instances = ruleReport.standardInstances;
592
- // Add the rule-result properties to the result.
593
- if (Object.keys(ruleReport.data).length) {
594
- data.ruleData[ruleResult.id] = ruleResult.data;
595
- }
596
- if (ruleResult.totals) {
597
- ruleResult.totals.forEach((total, index) => {
598
- standardResult.totals[index] += Math.round(total);
599
- });
600
- }
601
- if (ruleResult.instances?.length) {
602
- standardResult.instances.push(... ruleResult.instances);
603
- }
604
- justPrevented = false;
605
- // If testing is to stop after a failure and the page failed the test:
606
- if (stopOnFail && ruleReport.totals?.some(total => total)) {
607
- // Test for no more rules.
608
- break;
609
- }
610
- }
611
- }
612
- // If an error is thrown by the test:
613
- catch(error) {
614
- ruleResult.prevented = true;
615
- justPrevented = true;
616
- const isPageClosed = ['closed', 'Protocol error', 'Target page'].some(phrase =>
617
- error.message.includes(phrase)
618
- );
619
- // If the page has closed:
620
- if (isPageClosed) {
621
- // Report this.
622
- console.log(`ERROR: Test ${ruleResult.id} failed because page closed`);
538
+ // and the first item is y or n
539
+ && ['y', 'n'].includes(ruleSpec[0])
540
+ // and all subsequent items are rule IDs to be included (if y) or excluded (if n):
541
+ && ruleSpec.slice(1).every(ruleID => allRuleIDs.includes(ruleID))) {
542
+ // Get the rules to be tested for and their execution order.
543
+ const excludeIDs = ruleSpec.slice(1);
544
+ const jobRuleIDs = ruleSpec[0] === 'y'
545
+ ? excludeIDs
546
+ : allRules
547
+ .filter(rule => rule.defaultOn && !excludeIDs.includes(rule.id))
548
+ .map(rule => rule.id);
549
+ const jobRules = allRules.filter(rule => jobRuleIDs.includes(rule.id));
550
+ let justPrevented = false;
551
+ // For each rule to be tested for:
552
+ for (let ruleIndex = 0; ruleIndex < jobRules.length; ruleIndex++) {
553
+ const rule = jobRules[ruleIndex];
554
+ // Initialize the rule result.
555
+ const ruleResult = {
556
+ id: rule.id,
557
+ prevented: false,
558
+ error: '',
559
+ data: {},
560
+ totals: [0, 0, 0, 0],
561
+ instances: [],
562
+ elapsedTime: 0
563
+ };
564
+ console.log(`Starting rule ${ruleResult.id}`);
565
+ // Make the browser emulate headedness in all cases, because performance does not suffer.
566
+ const headEmulation = ruleResult.id.startsWith('shoot') ? 'high' : 'high';
567
+ // Get whether the rule needs a new browser launched. Under page isolation the first rules
568
+ // run on the live page provided; a contaminating rule still gets a fresh page.
569
+ const previousRule = ruleIndex > 0 ? jobRules[ruleIndex - 1] : null;
570
+ const needsLaunch = (ruleIndex === 0 && !(page && report.jobData?.isolation === 'page'))
571
+ || justPrevented
572
+ || Boolean(previousRule?.contaminates)
573
+ || jobRules[ruleIndex].needsAccessibleName && !previousRule?.needsAccessibleName
574
+ && !(ruleIndex === 0 && page);
575
+ const pageClosed = page && page.isClosed();
576
+ // If it does, or if the page has closed:
577
+ if (needsLaunch || pageClosed) {
578
+ // If the page has closed when it is expected to be open:
579
+ if (pageClosed && !needsLaunch) {
580
+ // Report this.
581
+ console.log(`WARNING: Relaunching browser for test ${rule} after abnormal closure`);
582
+ }
583
+ // Create a browser, replace the page, and visit the target, retrying twice if necessary.
584
+ page = await (0, launch_1.launch)({
585
+ report,
586
+ actIndex,
587
+ tempBrowserID: browserID,
588
+ tempURL: url,
589
+ headEmulation,
590
+ xPathNeed: 'script',
591
+ needsAccessibleName: jobRules[ruleIndex].needsAccessibleName,
592
+ retries: Number.isInteger(act.retries) && act.retries >= 0
593
+ ? act.retries
594
+ : config_1.ruleLaunchRetries
595
+ });
596
+ }
597
+ // If no page exists, the launch (or the replay of a checkpoint's acts) failed: the
598
+ // target is unreachable, so prevent the act and stop testing rules.
599
+ if (!page) {
600
+ const message = String(report.jobData?.abortMessage
601
+ || `Launch or checkpoint replay failed before rule ${ruleResult.id}`);
602
+ ruleResult.prevented = true;
603
+ ruleResult.error = message;
604
+ data.rulePreventions[ruleResult.id] = message;
605
+ data.prevented = true;
606
+ data.error = message;
607
+ standardResult.prevented = true;
608
+ console.log(`ERROR: ${message}`);
609
+ break;
610
+ }
611
+ // Report crashes and disconnections during this test.
612
+ let crashHandler;
613
+ let disconnectHandler;
614
+ if (page && !page.isClosed()) {
615
+ crashHandler = () => {
616
+ console.log(`ERROR: Page crashed during ${rule} test`);
617
+ };
618
+ page.on('crash', crashHandler);
619
+ }
620
+ // The page exists whenever this runs; verbatim from the original.
621
+ const browser = page.context().browser();
622
+ if (browser) {
623
+ disconnectHandler = () => {
624
+ console.log(`ERROR: Browser disconnected during ${rule} test`);
625
+ };
626
+ browser.on('disconnected', disconnectHandler);
627
+ }
628
+ // Restrict an element-local rule to the scope roots; a page-level rule tests the page.
629
+ if (scopeRoots) {
630
+ report.ruleScopeRoots = rule.local ? scopeRoots : null;
631
+ (rule.local ? data.scope.localRules : data.scope.pageRules).push(rule.id);
632
+ }
633
+ // Initialize an argument array for the reporter.
634
+ const ruleArgs = [page, report, actIndex, withItems];
635
+ // If the rule needs a temporary directory:
636
+ if (rule.needsTmpDir) {
637
+ // Add its path to the argument array.
638
+ ruleArgs.push(report.jobData.tmpDir);
639
+ }
640
+ // If the testaro test act specifies extra arguments for this rule:
641
+ if (argRules?.includes(ruleResult.id)) {
642
+ // Add them to the argument array.
643
+ ruleArgs.push(...args[ruleResult.id]);
644
+ }
645
+ const startTime = Date.now();
646
+ let timer;
647
+ try {
648
+ // Apply a time limit to the test.
649
+ const timeLimit = (0, config_1.applyMultiplier)(1000 * rule.timeOut);
650
+ let timeout;
651
+ // If the time limit expires during the test:
652
+ timer = new Promise(resolve => {
653
+ timeout = setTimeout(() => {
654
+ // Add data about the timeout to the rule result.
655
+ justPrevented = true;
656
+ ruleResult.prevented = true;
657
+ ruleResult.error = 'Timeout';
658
+ console.log(`ERROR: Test of testaro rule ${ruleResult.id} timed out`);
659
+ resolve({ timedOut: true });
660
+ }, timeLimit);
661
+ });
662
+ // Try to perform the test and get a test report, loading the rule via the registry.
663
+ // The RuleModule cast widens the per-rule signature union to the registry contract.
664
+ const testReport = registry_1.ruleModules[ruleResult.id]().reporter(...ruleArgs);
665
+ // Get a test or timeout report.
666
+ const ruleReport = await Promise.race([timer, testReport]);
667
+ clearTimeout(timeout);
668
+ // If it was a test report:
669
+ if (!ruleReport.timedOut) {
670
+ // Add the rule-report properties to the rule result.
671
+ ruleResult.data = ruleReport.data;
672
+ ruleResult.totals = ruleReport.totals;
673
+ ruleResult.instances = ruleReport.standardInstances;
674
+ // Add the rule-result properties to the result.
675
+ if (Object.keys(ruleReport.data).length) {
676
+ data.ruleData[ruleResult.id] = ruleResult.data;
677
+ }
678
+ if (ruleResult.totals) {
679
+ ruleResult.totals.forEach((total, index) => {
680
+ standardResult.totals[index] += Math.round(total);
681
+ });
682
+ }
683
+ if (ruleResult.instances?.length) {
684
+ // Apply the rule's default outcome to instances without their own.
685
+ ruleResult.instances.forEach(instance => {
686
+ instance.outcome ??= rule.outcome ?? 'failed';
687
+ if (instance.outcome === 'cantTell' && rule.uncertainty) {
688
+ instance.uncertainty ??= rule.uncertainty;
689
+ }
690
+ standardResult.outcomeTotals[instance.outcome] += instance.count || 1;
691
+ });
692
+ standardResult.instances.push(...ruleResult.instances);
693
+ }
694
+ justPrevented = false;
695
+ // If testing is to stop after a failure and the page failed the test:
696
+ if (stopOnFail && ruleReport.totals?.some(total => total)) {
697
+ // Test for no more rules.
698
+ break;
699
+ }
700
+ }
701
+ }
702
+ // If an error is thrown by the test:
703
+ catch (error) {
704
+ ruleResult.prevented = true;
705
+ justPrevented = true;
706
+ const isPageClosed = ['closed', 'Protocol error', 'Target page'].some(phrase => error.message.includes(phrase));
707
+ // If the page has closed:
708
+ if (isPageClosed) {
709
+ // Report this.
710
+ console.log(`ERROR: Test ${ruleResult.id} failed because page closed`);
711
+ }
712
+ // Otherwise, i.e. if the page is open:
713
+ else {
714
+ // Add this to the rule result.
715
+ ruleResult.error = error.message;
716
+ console.log(`ERROR: Test of testaro rule ${ruleResult.id} prevented (${error.message})`);
717
+ }
718
+ }
719
+ finally {
720
+ // Add the elapsed time to the rule result.
721
+ ruleResult.elapsedTime = Math.round((Date.now() - startTime) / 1000);
722
+ // Add the elapsed time to the data.
723
+ data.ruleTestTimes.push([ruleResult.id, ruleResult.elapsedTime]);
724
+ // If the test timed out or otherwise failed:
725
+ if (ruleResult.prevented) {
726
+ // Add this and the error to the data.
727
+ data.rulePreventions[ruleResult.id] = ruleResult.error;
728
+ }
729
+ }
730
+ // Sort the rule test times.
731
+ data.ruleTestTimes.sort((a, b) => b[1] - a[1]);
732
+ // Clear the error listeners.
733
+ if (page && !page.isClosed() && crashHandler) {
734
+ page.off('crash', crashHandler);
735
+ crashHandler = null;
736
+ }
737
+ if (browser && disconnectHandler) {
738
+ browser.off('disconnected', disconnectHandler);
739
+ disconnectHandler = null;
740
+ }
741
+ // Force a garbage collection.
742
+ try {
743
+ if (global.gc) {
744
+ global.gc();
745
+ }
746
+ }
747
+ catch (error) { }
623
748
  }
624
- // Otherwise, i.e. if the page is open:
625
- else {
626
- // Add this to the rule result.
627
- ruleResult.error = error.message;
628
- console.log(
629
- `ERROR: Test of testaro rule ${ruleResult.id} prevented (${error.message})`
630
- );
631
- }
632
- }
633
- finally {
634
- // Add the elapsed time to the rule result.
635
- ruleResult.elapsedTime = Math.round((Date.now() - startTime) / 1000);
636
- // Add the elapsed time to the data.
637
- data.ruleTestTimes.push([ruleResult.id, ruleResult.elapsedTime]);
638
- // If the test timed out or otherwise failed:
639
- if (ruleResult.prevented) {
640
- // Add this and the error to the data.
641
- data.rulePreventions[ruleResult.id] = ruleResult.error;
642
- }
643
- }
644
- // Sort the rule test times.
645
- data.ruleTestTimes.sort((a, b) => b[1] - a[1]);
646
- // Clear the error listeners.
647
- if (page && ! page.isClosed() && crashHandler) {
648
- page.off('crash', crashHandler);
649
- crashHandler = null;
650
- }
651
- if (browser && disconnectHandler) {
652
- browser.off('disconnected', disconnectHandler);
653
- disconnectHandler = null;
654
- }
655
- // Force a garbage collection.
656
- try {
657
- if (global.gc) {
658
- global.gc();
659
- }
660
- }
661
- catch(error) {}
749
+ ;
750
+ }
751
+ // Otherwise, i.e. if the rule specification is invalid:
752
+ else {
753
+ // Report this and stop testing.
754
+ standardResult.prevented = true;
755
+ data.prevented = true;
756
+ const message = 'ERROR: Testaro rule specification invalid';
757
+ data.error = message;
758
+ console.log(message);
759
+ }
760
+ // Close the last page the tool launched, unless it is the live page it was given.
761
+ if (page && page !== givenPage) {
762
+ await (0, launch_1.browserClose)(page);
763
+ }
764
+ delete report.ruleScopeRoots;
765
+ return {
766
+ data,
767
+ result
662
768
  };
663
- }
664
- // Otherwise, i.e. if the rule specification is invalid:
665
- else {
666
- // Report this and stop testing.
667
- standardResult.prevented = true;
668
- data.prevented = true;
669
- const message = 'ERROR: Testaro rule specification invalid';
670
- data.error = message;
671
- console.log(message);
672
- }
673
- return {
674
- data,
675
- result
676
- };
677
769
  };
770
+ exports.reporter = reporter;