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/testaro/tabNav.js CHANGED
@@ -1,5 +1,7 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2021–2024 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,391 +9,320 @@
7
9
 
8
10
  SPDX-License-Identifier: MIT
9
11
  */
10
-
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.reporter = void 0;
14
+ // Shared configuration for timeout multiplier.
15
+ const config_1 = require("../procs/config");
16
+ const xPath_1 = require("../procs/xPath");
11
17
  /*
12
18
  tabNav
13
19
  This test reports nonstandard keyboard navigation among tab elements in visible tab lists. Standards are based on https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel.
20
+ Compiled to tabNav.js by tsc (issue #73); edit this file, not the emitted one.
14
21
  */
15
-
16
- // IMPORTS
17
-
18
- // Shared configuration for timeout multiplier.
19
- const {applyMultiplier} = require('../procs/config');
20
- const {getXPathCatalogIndex} = require('../procs/xPath');
21
-
22
22
  // CONSTANTS
23
-
23
+ // The cast types the object that reporter populates before the other functions read it.
24
24
  const data = {};
25
-
26
25
  // FUNCTIONS
27
-
28
26
  // Returns the index of the focused tab in an array of tabs.
29
27
  const focusedTab = async (tabs, page) => await page.evaluate(tabs => {
30
- const focus = document.activeElement;
31
- return tabs.indexOf(focus);
28
+ const focus = document.activeElement;
29
+ return tabs.indexOf(focus);
32
30
  }, tabs)
33
- .catch(error => {
34
- console.log(`ERROR: could not find focused tab (${error.message})`);
35
- return -1;
31
+ .catch(error => {
32
+ console.log(`ERROR: could not find focused tab (${error.message})`);
33
+ return -1;
36
34
  });
37
35
  // Tests a navigation on a focused tab element.
38
- const testKey = async (
39
- tabs, tabElement, keyName, keyProp, goodIndex, elementIsCorrect, itemData, withItems, page
40
- ) => {
41
- let pressed = true;
42
- // Click the tab element, to make the focus on it effective.
43
- await tabElement.click({
44
- timeout: applyMultiplier(500)
45
- })
46
- .catch(async error => {
47
- console.log(
48
- `ERROR clicking tab element ${itemData.text} (${error.message.replace(/\n.+/s, '')})`
49
- );
36
+ const testKey = async (tabs, tabElement, keyName, keyProp, goodIndex, elementIsCorrect, itemData, withItems, page) => {
37
+ let pressed = true;
38
+ // Click the tab element, to make the focus on it effective.
50
39
  await tabElement.click({
51
- force: true
52
- });
53
- })
54
- .catch(error => {
55
- console.log(
56
- `ERROR force-clicking tab element ${itemData.text} (${error.message.replace(/\n.+/s, '')})`
57
- );
58
- pressed = false;
59
- });
60
- // Increment the counts of navigations and key navigations.
61
- data.totals.navigations.all.total++;
62
- data.totals.navigations.specific[keyProp].total++;
63
- const {navigationErrors} = itemData;
64
- // If the click succeeded:
65
- if (pressed) {
66
- // Refocus the tab element and press the specified key (page.keyboard.press may fail).
67
- await tabElement.press(keyName, {
68
- timeout: applyMultiplier(1000)
40
+ timeout: (0, config_1.applyMultiplier)(500)
69
41
  })
70
- .catch(error => {
71
- console.log(`ERROR: could not press ${keyName} (${error.message})`);
72
- pressed = false;
42
+ .catch(async (error) => {
43
+ console.log(`ERROR clicking tab element ${itemData.text} (${error.message.replace(/\n.+/s, '')})`);
44
+ await tabElement.click({
45
+ force: true
46
+ });
47
+ })
48
+ .catch(error => {
49
+ console.log(`ERROR force-clicking tab element ${itemData.text} (${error.message.replace(/\n.+/s, '')})`);
50
+ pressed = false;
73
51
  });
74
- // If the refocus and keypress succeeded:
52
+ // Increment the counts of navigations and key navigations.
53
+ data.totals.navigations.all.total++;
54
+ data.totals.navigations.specific[keyProp].total++;
55
+ const { navigationErrors } = itemData;
56
+ // If the click succeeded:
75
57
  if (pressed) {
76
- // Identify which tab element is now focused, if any.
77
- const focusIndex = await focusedTab(tabs, page);
78
- // If the focus is correct:
79
- if (focusIndex === goodIndex) {
80
- // Increment the counts of correct navigations and correct key navigations.
81
- data.totals.navigations.all.correct++;
82
- data.totals.navigations.specific[keyProp].correct++;
83
- }
84
- // Otherwise, i.e. if the focus is incorrect:
85
- else {
58
+ // Refocus the tab element and press the specified key (page.keyboard.press may fail).
59
+ await tabElement.press(keyName, {
60
+ timeout: (0, config_1.applyMultiplier)(1000)
61
+ })
62
+ .catch(error => {
63
+ console.log(`ERROR: could not press ${keyName} (${error.message})`);
64
+ pressed = false;
65
+ });
66
+ // If the refocus and keypress succeeded:
67
+ if (pressed) {
68
+ // Identify which tab element is now focused, if any.
69
+ const focusIndex = await focusedTab(tabs, page);
70
+ // If the focus is correct:
71
+ if (focusIndex === goodIndex) {
72
+ // Increment the counts of correct navigations and correct key navigations.
73
+ data.totals.navigations.all.correct++;
74
+ data.totals.navigations.specific[keyProp].correct++;
75
+ }
76
+ // Otherwise, i.e. if the focus is incorrect:
77
+ else {
78
+ // Increment the counts of incorrect navigations and incorrect key navigations.
79
+ data.totals.navigations.all.incorrect++;
80
+ data.totals.navigations.specific[keyProp].incorrect++;
81
+ // Update the element status to incorrect.
82
+ elementIsCorrect = false;
83
+ // If itemization is required:
84
+ if (withItems) {
85
+ // Update the element report.
86
+ navigationErrors.push(keyName);
87
+ }
88
+ }
89
+ return elementIsCorrect;
90
+ }
91
+ // Otherwise, i.e. if the refocus or keypress failed:
92
+ else {
93
+ // Increment the counts of incorrect navigations and incorrect key navigations.
94
+ data.totals.navigations.all.incorrect++;
95
+ data.totals.navigations.specific[keyProp].incorrect++;
96
+ // If itemization is required and a focus failure has not yet been reported:
97
+ if (withItems && !navigationErrors.includes('focus')) {
98
+ // Update the element report.
99
+ navigationErrors.push('focus');
100
+ }
101
+ return false;
102
+ }
103
+ }
104
+ // Otherwise, i.e. if the click failed:
105
+ else {
86
106
  // Increment the counts of incorrect navigations and incorrect key navigations.
87
107
  data.totals.navigations.all.incorrect++;
88
108
  data.totals.navigations.specific[keyProp].incorrect++;
89
- // Update the element status to incorrect.
90
- elementIsCorrect = false;
91
- // If itemization is required:
92
- if (withItems) {
93
- // Update the element report.
94
- navigationErrors.push(keyName);
109
+ // If itemization is required and a click failure has not yet been reported:
110
+ if (withItems && !navigationErrors.includes('click')) {
111
+ // Update the element report.
112
+ navigationErrors.push('click');
95
113
  }
96
- }
97
- return elementIsCorrect;
98
- }
99
- // Otherwise, i.e. if the refocus or keypress failed:
100
- else {
101
- // Increment the counts of incorrect navigations and incorrect key navigations.
102
- data.totals.navigations.all.incorrect++;
103
- data.totals.navigations.specific[keyProp].incorrect++;
104
- // If itemization is required and a focus failure has not yet been reported:
105
- if (withItems && ! navigationErrors.includes('focus')) {
106
- // Update the element report.
107
- navigationErrors.push('focus');
108
- }
109
- return false;
110
- }
111
- }
112
- // Otherwise, i.e. if the click failed:
113
- else {
114
- // Increment the counts of incorrect navigations and incorrect key navigations.
115
- data.totals.navigations.all.incorrect++;
116
- data.totals.navigations.specific[keyProp].incorrect++;
117
- // If itemization is required and a click failure has not yet been reported:
118
- if (withItems && ! navigationErrors.includes('click')) {
119
- // Update the element report.
120
- navigationErrors.push('click');
114
+ return false;
121
115
  }
122
- return false;
123
- }
124
116
  };
125
117
  // Returns the index to which an arrow key should move the focus.
126
118
  const arrowTarget = (startIndex, tabCount, orientation, direction) => {
127
- if (orientation === 'horizontal') {
128
- if (['up', 'down'].includes(direction)) {
129
- return startIndex;
130
- }
131
- else if (direction === 'left') {
132
- return startIndex ? startIndex - 1 : tabCount - 1;
133
- }
134
- else if (direction === 'right') {
135
- return startIndex === tabCount - 1 ? 0 : startIndex + 1;
136
- }
137
- }
138
- else if (orientation === 'vertical') {
139
- if (['left', 'right'].includes(direction)) {
140
- return startIndex;
141
- }
142
- else if (direction === 'up') {
143
- return startIndex ? startIndex - 1 : tabCount - 1;
119
+ if (orientation === 'horizontal') {
120
+ if (['up', 'down'].includes(direction)) {
121
+ return startIndex;
122
+ }
123
+ else if (direction === 'left') {
124
+ return startIndex ? startIndex - 1 : tabCount - 1;
125
+ }
126
+ else if (direction === 'right') {
127
+ return startIndex === tabCount - 1 ? 0 : startIndex + 1;
128
+ }
144
129
  }
145
- else if (direction === 'down') {
146
- return startIndex === tabCount - 1 ? 0 : startIndex + 1;
130
+ else if (orientation === 'vertical') {
131
+ if (['left', 'right'].includes(direction)) {
132
+ return startIndex;
133
+ }
134
+ else if (direction === 'up') {
135
+ return startIndex ? startIndex - 1 : tabCount - 1;
136
+ }
137
+ else if (direction === 'down') {
138
+ return startIndex === tabCount - 1 ? 0 : startIndex + 1;
139
+ }
147
140
  }
148
- }
149
141
  };
150
142
  /*
151
143
  Recursively tests tablist tab elements (per
152
144
  https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel)
153
145
  */
154
146
  const testTabs = async (tabs, index, listOrientation, listIsCorrect, withItems, page) => {
155
- const tabCount = tabs.length;
156
- // If any tab elements remain to be tested:
157
- if (index < tabCount) {
158
- // Increment the reported count of tab elements.
159
- data.totals.tabElements.total++;
160
- // Identify the tab element to be tested.
161
- const currentTab = tabs[index];
162
- // Initialize it as correct.
163
- let isCorrect = true;
164
- const itemData = {};
165
- // If itemization is required:
166
- if (withItems) {
167
- // Initialize data on the element.
168
- itemData.xPath = await page.evaluate(element => window.getXPath(element), currentTab);
169
- itemData.navigationErrors = [];
147
+ const tabCount = tabs.length;
148
+ // If any tab elements remain to be tested:
149
+ if (index < tabCount) {
150
+ // Increment the reported count of tab elements.
151
+ data.totals.tabElements.total++;
152
+ // Identify the tab element to be tested.
153
+ const currentTab = tabs[index];
154
+ // Initialize it as correct.
155
+ let isCorrect = true;
156
+ const itemData = {};
157
+ // If itemization is required:
158
+ if (withItems) {
159
+ // Initialize data on the element.
160
+ itemData.xPath = await page.evaluate(element => window.getXPath(element), currentTab);
161
+ itemData.navigationErrors = [];
162
+ }
163
+ // Test the element with each navigation key.
164
+ isCorrect = await testKey(tabs, currentTab, 'Tab', 'tab', -1, isCorrect, itemData, withItems, page);
165
+ isCorrect = await testKey(tabs, currentTab, 'ArrowLeft', 'left', arrowTarget(index, tabCount, listOrientation, 'left'), isCorrect, itemData, withItems, page);
166
+ isCorrect = await testKey(tabs, currentTab, 'ArrowRight', 'right', arrowTarget(index, tabCount, listOrientation, 'right'), isCorrect, itemData, withItems, page);
167
+ isCorrect = await testKey(tabs, currentTab, 'ArrowUp', 'up', arrowTarget(index, tabCount, listOrientation, 'up'), isCorrect, itemData, withItems, page);
168
+ isCorrect = await testKey(tabs, currentTab, 'ArrowDown', 'down', arrowTarget(index, tabCount, listOrientation, 'down'), isCorrect, itemData, withItems, page);
169
+ isCorrect = await testKey(tabs, currentTab, 'Home', 'home', 0, isCorrect, itemData, withItems, page);
170
+ isCorrect = await testKey(tabs, currentTab, 'End', 'end', tabCount - 1, isCorrect, itemData, withItems, page);
171
+ // Update the tablist status (Node 14 does not support the ES 2021 &&= operator).
172
+ listIsCorrect = listIsCorrect && isCorrect;
173
+ // Increment the data.
174
+ data.totals.tabElements[isCorrect ? 'correct' : 'incorrect']++;
175
+ if (withItems) {
176
+ data.tabElements[isCorrect ? 'correct' : 'incorrect'].push(itemData);
177
+ }
178
+ // Process the next tab element.
179
+ return await testTabs(tabs, index + 1, listOrientation, listIsCorrect, withItems, page);
170
180
  }
171
- // Test the element with each navigation key.
172
- isCorrect = await testKey(
173
- tabs,
174
- currentTab,
175
- 'Tab',
176
- 'tab',
177
- -1,
178
- isCorrect,
179
- itemData,
180
- withItems,
181
- page
182
- );
183
- isCorrect = await testKey(
184
- tabs,
185
- currentTab,
186
- 'ArrowLeft',
187
- 'left',
188
- arrowTarget(index, tabCount, listOrientation, 'left'),
189
- isCorrect,
190
- itemData,
191
- withItems,
192
- page
193
- );
194
- isCorrect = await testKey(
195
- tabs,
196
- currentTab,
197
- 'ArrowRight',
198
- 'right',
199
- arrowTarget(index, tabCount, listOrientation, 'right'),
200
- isCorrect,
201
- itemData,
202
- withItems,
203
- page
204
- );
205
- isCorrect = await testKey(
206
- tabs,
207
- currentTab,
208
- 'ArrowUp',
209
- 'up',
210
- arrowTarget(index, tabCount, listOrientation, 'up'),
211
- isCorrect,
212
- itemData,
213
- withItems,
214
- page
215
- );
216
- isCorrect = await testKey(
217
- tabs,
218
- currentTab,
219
- 'ArrowDown',
220
- 'down',
221
- arrowTarget(index, tabCount, listOrientation, 'down'),
222
- isCorrect,
223
- itemData,
224
- withItems,
225
- page
226
- );
227
- isCorrect = await testKey(
228
- tabs,
229
- currentTab,
230
- 'Home',
231
- 'home',
232
- 0,
233
- isCorrect,
234
- itemData,
235
- withItems,
236
- page
237
- );
238
- isCorrect = await testKey(
239
- tabs, currentTab, 'End', 'end', tabCount - 1, isCorrect, itemData, withItems, page
240
- );
241
- // Update the tablist status (Node 14 does not support the ES 2021 &&= operator).
242
- listIsCorrect = listIsCorrect && isCorrect;
243
- // Increment the data.
244
- data.totals.tabElements[isCorrect ? 'correct' : 'incorrect']++;
245
- if (withItems) {
246
- data.tabElements[isCorrect ? 'correct' : 'incorrect'].push(itemData);
181
+ // Otherwise, i.e. if all tab elements have been tested:
182
+ else {
183
+ // Return whether the tablist is correct.
184
+ return listIsCorrect;
247
185
  }
248
- // Process the next tab element.
249
- return await testTabs(tabs, index + 1, listOrientation, listIsCorrect, withItems, page);
250
- }
251
- // Otherwise, i.e. if all tab elements have been tested:
252
- else {
253
- // Return whether the tablist is correct.
254
- return listIsCorrect;
255
- }
256
186
  };
257
187
  // Recursively tests tablists.
258
188
  const testTabLists = async (tabLists, withItems, page) => {
259
- // If any tablists remain to be tested:
260
- if (tabLists.length) {
261
- const firstTabList = tabLists[0];
262
- let orientation = await firstTabList.getAttribute('aria-orientation')
263
- .catch(error=> {
264
- console.log(`ERROR: could not get tab-list orientation (${error.message})`);
265
- return 'ERROR';
266
- });
267
- if (! orientation) {
268
- orientation = 'horizontal';
269
- }
270
- if (orientation === 'ERROR') {
271
- data.prevented = true;
272
- }
273
- else {
274
- const tabs = await firstTabList.$$('[role=tab]');
275
- // If the tablist contains at least 2 tab elements:
276
- if (tabs.length > 1) {
277
- // Test them.
278
- const isCorrect = await testTabs(tabs, 0, orientation, true, withItems, page);
279
- // Increment the data.
280
- data.totals.tabLists.total++;
281
- data.totals.tabLists[isCorrect ? 'correct' : 'incorrect']++;
282
- // Process the remaining tablists.
283
- await testTabLists(tabLists.slice(1), withItems, page);
284
- }
189
+ // If any tablists remain to be tested:
190
+ if (tabLists.length) {
191
+ const firstTabList = tabLists[0];
192
+ let orientation = await firstTabList.getAttribute('aria-orientation')
193
+ .catch(error => {
194
+ console.log(`ERROR: could not get tab-list orientation (${error.message})`);
195
+ return 'ERROR';
196
+ });
197
+ if (!orientation) {
198
+ orientation = 'horizontal';
199
+ }
200
+ if (orientation === 'ERROR') {
201
+ data.prevented = true;
202
+ }
203
+ else {
204
+ const tabs = await firstTabList.$$('[role=tab]');
205
+ // If the tablist contains at least 2 tab elements:
206
+ if (tabs.length > 1) {
207
+ // Test them.
208
+ const isCorrect = await testTabs(tabs, 0, orientation, true, withItems, page);
209
+ // Increment the data.
210
+ data.totals.tabLists.total++;
211
+ data.totals.tabLists[isCorrect ? 'correct' : 'incorrect']++;
212
+ // Process the remaining tablists.
213
+ await testTabLists(tabLists.slice(1), withItems, page);
214
+ }
215
+ }
285
216
  }
286
- }
287
217
  };
288
218
  // Runs the test and returns the result.
289
- exports.reporter = async (page, report, _, withItems) => {
290
- // Initialize the results.
291
- data.totals = {
292
- navigations: {
293
- all: {
294
- total: 0,
295
- correct: 0,
296
- incorrect: 0
297
- },
298
- specific: {
299
- tab: {
300
- total: 0,
301
- correct: 0,
302
- incorrect: 0
303
- },
304
- left: {
305
- total: 0,
306
- correct: 0,
307
- incorrect: 0
308
- },
309
- right: {
310
- total: 0,
311
- correct: 0,
312
- incorrect: 0
219
+ const reporter = async (page, report, _, withItems) => {
220
+ // Initialize the results.
221
+ data.totals = {
222
+ navigations: {
223
+ all: {
224
+ total: 0,
225
+ correct: 0,
226
+ incorrect: 0
227
+ },
228
+ specific: {
229
+ tab: {
230
+ total: 0,
231
+ correct: 0,
232
+ incorrect: 0
233
+ },
234
+ left: {
235
+ total: 0,
236
+ correct: 0,
237
+ incorrect: 0
238
+ },
239
+ right: {
240
+ total: 0,
241
+ correct: 0,
242
+ incorrect: 0
243
+ },
244
+ up: {
245
+ total: 0,
246
+ correct: 0,
247
+ incorrect: 0
248
+ },
249
+ down: {
250
+ total: 0,
251
+ correct: 0,
252
+ incorrect: 0
253
+ },
254
+ home: {
255
+ total: 0,
256
+ correct: 0,
257
+ incorrect: 0
258
+ },
259
+ end: {
260
+ total: 0,
261
+ correct: 0,
262
+ incorrect: 0
263
+ }
264
+ }
313
265
  },
314
- up: {
315
- total: 0,
316
- correct: 0,
317
- incorrect: 0
266
+ tabElements: {
267
+ total: 0,
268
+ correct: 0,
269
+ incorrect: 0
318
270
  },
319
- down: {
320
- total: 0,
321
- correct: 0,
322
- incorrect: 0
323
- },
324
- home: {
325
- total: 0,
326
- correct: 0,
327
- incorrect: 0
328
- },
329
- end: {
330
- total: 0,
331
- correct: 0,
332
- incorrect: 0
271
+ tabLists: {
272
+ total: 0,
273
+ correct: 0,
274
+ incorrect: 0
333
275
  }
334
- }
335
- },
336
- tabElements: {
337
- total: 0,
338
- correct: 0,
339
- incorrect: 0
340
- },
341
- tabLists: {
342
- total: 0,
343
- correct: 0,
344
- incorrect: 0
276
+ };
277
+ if (withItems) {
278
+ data.tabElements = {
279
+ incorrect: [],
280
+ correct: []
281
+ };
345
282
  }
346
- };
347
- if (withItems) {
348
- data.tabElements = {
349
- incorrect: [],
350
- correct: []
283
+ // Get an array of element handles for the visible tablists.
284
+ const tabLists = await page.$$('[role=tablist]:visible');
285
+ if (tabLists.length) {
286
+ await testTabLists(tabLists, withItems, page);
287
+ }
288
+ // Get the totals of navigation errors, bad tabs, and bad tab lists.
289
+ const totals = data.totals ? [
290
+ data.totals.navigations.all.incorrect,
291
+ data.totals.tabElements.incorrect,
292
+ data.totals.tabLists.incorrect,
293
+ 0
294
+ ] : [];
295
+ // Initialize the standard instances.
296
+ const standardInstances = [];
297
+ // If itemization is required:
298
+ if (withItems) {
299
+ // For each bad tab:
300
+ data.tabElements.incorrect.forEach(item => {
301
+ // Add an instance to the standard instances.
302
+ standardInstances.push({
303
+ ruleID: 'tabNav',
304
+ what: `Tab responds nonstandardly to ${item.navigationErrors.join(', ')}`,
305
+ ordinalSeverity: 1,
306
+ count: 1,
307
+ catalogIndex: (0, xPath_1.getXPathCatalogIndex)(report, item.xPath)
308
+ });
309
+ });
310
+ }
311
+ // Otherwise, if navigation is not required and any navigations were bad:
312
+ else if (data.totals.navigations.all.incorrect) {
313
+ // Add a summary instance to the standard instances.
314
+ standardInstances.push({
315
+ ruleID: 'tabNav',
316
+ what: 'Tab lists have nonstandard navigation',
317
+ ordinalSeverity: 1,
318
+ count: data.totals.navigations.all.incorrect
319
+ });
320
+ }
321
+ // Return the result.
322
+ return {
323
+ data,
324
+ totals,
325
+ standardInstances
351
326
  };
352
- }
353
- // Get an array of element handles for the visible tablists.
354
- const tabLists = await page.$$('[role=tablist]:visible');
355
- if (tabLists.length) {
356
- await testTabLists(tabLists, withItems, page);
357
- }
358
- // Get the totals of navigation errors, bad tabs, and bad tab lists.
359
- const totals = data.totals ? [
360
- data.totals.navigations.all.incorrect,
361
- data.totals.tabElements.incorrect,
362
- data.totals.tabLists.incorrect,
363
- 0
364
- ] : [];
365
- // Initialize the standard instances.
366
- const standardInstances = [];
367
- // If itemization is required:
368
- if (withItems) {
369
- // For each bad tab:
370
- data.tabElements.incorrect.forEach(item => {
371
- // Add an instance to the standard instances.
372
- standardInstances.push({
373
- ruleID: 'tabNav',
374
- what: `Tab responds nonstandardly to ${item.navigationErrors.join(', ')}`,
375
- ordinalSeverity: 1,
376
- count: 1,
377
- catalogIndex: getXPathCatalogIndex(report, item.xPath)
378
- });
379
- });
380
- }
381
- // Otherwise, if navigation is not required and any navigations were bad:
382
- else if (data.totals.navigations.all.incorrect) {
383
- // Add a summary instance to the standard instances.
384
- standardInstances.push({
385
- ruleID: 'tabNav',
386
- what: 'Tab lists have nonstandard navigation',
387
- ordinalSeverity: 1,
388
- count: data.totals.navigations.all.incorrect
389
- });
390
- }
391
- // Return the result.
392
- return {
393
- data,
394
- totals,
395
- standardInstances
396
- };
397
327
  };
328
+ exports.reporter = reporter;