testaro 78.0.6 → 78.4.0

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