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
@@ -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,117 +9,112 @@
7
9
 
8
10
  SPDX-License-Identifier: MIT
9
11
  */
10
-
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.reporter = void 0;
14
+ const testaro_1 = require("../procs/testaro");
11
15
  /*
12
16
  focAndOp
13
17
  Related to Tenon rule 190.
14
18
 
15
19
  This test reports discrepancies between Tab-focusability and operability. The standard practice is to make focusable elements operable and operable elements focusable. If focusable elements are not operable, users are likely to be surprised that nothing happens when they try to operate such elements. Conversely, if operable elements are not focusable, users who navigate with a keyboard are prevented from operating those elements. The test considers an element Tab-focusable if its tabIndex property has the value 0. The test considers an element operable if it has a non-inherited pointer cursor and is not a 'LABEL' element, has an operable tag name, has an interactive explicit role, or has an 'onclick' attribute.
20
+ Compiled to focAndOp.js by tsc (issue #73); edit this file, not the emitted one.
16
21
  */
17
-
18
- // IMPORTS
19
-
20
- const {doTest} = require('../procs/testaro');
21
-
22
22
  // FUNCTIONS
23
-
24
23
  // Runs the test and returns the result.
25
- exports.reporter = async (page, report, _, withItems) => {
26
- const getBadWhat = element => {
27
- // Get whether the element is visible.
28
- const isVisible = element.checkVisibility({
29
- contentVisibilityAuto: true,
30
- opacityProperty: true,
31
- visibilityProperty: true
32
- });
33
- // If so:
34
- if (isVisible) {
35
- // Get whether it is focusable.
36
- const isFocusable = element.tabIndex === 0;
37
- // Get the operable tagnames.
38
- const opTags = new Set(
39
- ['A', 'BUTTON', 'IFRAME', 'INPUT','OPTION', 'SELECT', 'SUMMARY', 'TEXTAREA']
40
- );
41
- // Get the operable roles.
42
- const opRoles = new Set([
43
- 'button',
44
- 'checkbox',
45
- 'combobox',
46
- 'composite',
47
- 'grid',
48
- 'gridcell',
49
- 'input',
50
- 'link',
51
- 'listbox',
52
- 'menu',
53
- 'menubar',
54
- 'menuitem',
55
- 'menuitemcheckbox',
56
- 'option',
57
- 'radio',
58
- 'radiogroup',
59
- 'scrollbar',
60
- 'searchbox',
61
- 'select',
62
- 'slider',
63
- 'spinbutton',
64
- 'switch',
65
- 'tab',
66
- 'tablist',
67
- 'textbox',
68
- 'tree',
69
- 'treegrid',
70
- 'treeitem',
71
- 'widget',
72
- ]);
73
- // Initialize the operabilities of the element.
74
- const opHow = [];
75
- // If the element is not a label:
76
- if (element.tagName !== 'LABEL') {
77
- const liveStyleDec = window.getComputedStyle(element);
78
- // If it has a pointer cursor:
79
- if (liveStyleDec.cursor === 'pointer') {
80
- // Neutralize the cursor style of the parent element of the element.
81
- element.parentElement.style.cursor = 'default';
82
- // If, after this, the element still has a pointer cursor:
83
- if (liveStyleDec.cursor === 'pointer') {
84
- // Add this to the operabilities of the element.
85
- opHow.push('pointer cursor');
86
- }
24
+ const reporter = async (page, report, _, withItems) => {
25
+ // The candidate selector yields rendered HTML elements.
26
+ const getBadWhat = (element) => {
27
+ // Get whether the element is visible.
28
+ const isVisible = element.checkVisibility({
29
+ contentVisibilityAuto: true,
30
+ opacityProperty: true,
31
+ visibilityProperty: true
32
+ });
33
+ // If so:
34
+ if (isVisible) {
35
+ // Get whether it is focusable.
36
+ const isFocusable = element.tabIndex === 0;
37
+ // Get the operable tagnames.
38
+ const opTags = new Set(['A', 'BUTTON', 'IFRAME', 'INPUT', 'OPTION', 'SELECT', 'SUMMARY', 'TEXTAREA']);
39
+ // Get the operable roles.
40
+ const opRoles = new Set([
41
+ 'button',
42
+ 'checkbox',
43
+ 'combobox',
44
+ 'composite',
45
+ 'grid',
46
+ 'gridcell',
47
+ 'input',
48
+ 'link',
49
+ 'listbox',
50
+ 'menu',
51
+ 'menubar',
52
+ 'menuitem',
53
+ 'menuitemcheckbox',
54
+ 'option',
55
+ 'radio',
56
+ 'radiogroup',
57
+ 'scrollbar',
58
+ 'searchbox',
59
+ 'select',
60
+ 'slider',
61
+ 'spinbutton',
62
+ 'switch',
63
+ 'tab',
64
+ 'tablist',
65
+ 'textbox',
66
+ 'tree',
67
+ 'treegrid',
68
+ 'treeitem',
69
+ 'widget',
70
+ ]);
71
+ // Initialize the operabilities of the element.
72
+ const opHow = [];
73
+ // If the element is not a label:
74
+ if (element.tagName !== 'LABEL') {
75
+ const liveStyleDec = window.getComputedStyle(element);
76
+ // If it has a pointer cursor:
77
+ if (liveStyleDec.cursor === 'pointer') {
78
+ // Neutralize the cursor style of the parent element of the element.
79
+ element.parentElement.style.cursor = 'default';
80
+ // If, after this, the element still has a pointer cursor:
81
+ if (liveStyleDec.cursor === 'pointer') {
82
+ // Add this to the operabilities of the element.
83
+ opHow.push('pointer cursor');
84
+ }
85
+ }
86
+ }
87
+ // If the element has a click event listener:
88
+ if (element.onclick) {
89
+ // Add this to the operabilities.
90
+ opHow.push('click listener');
91
+ }
92
+ // If the element has an operable explicit role:
93
+ const role = element.getAttribute('role');
94
+ if (opRoles.has(role)) {
95
+ // Add this to the operabilities.
96
+ opHow.push(`role ${role}`);
97
+ }
98
+ // If the element has an operable tagname:
99
+ const tagName = element.tagName;
100
+ if (opTags.has(tagName)) {
101
+ // Add this to the operabilities.
102
+ opHow.push(`tagname ${tagName}`);
103
+ }
104
+ const isOperable = opHow.length > 0;
105
+ // If the element is focusable but not operable:
106
+ if (isFocusable && !isOperable) {
107
+ // Return a severity and violation description.
108
+ return '2:Element is Tab-focusable but not operable';
109
+ }
110
+ // Otherwise, if it is operable but not focusable:
111
+ else if (isOperable && !isFocusable) {
112
+ // Return a severity and violation description.
113
+ return `3:Element is operable (${opHow.join(', ')}) but not Tab-focusable`;
114
+ }
87
115
  }
88
- }
89
- // If the element has a click event listener:
90
- if (element.onclick) {
91
- // Add this to the operabilities.
92
- opHow.push('click listener');
93
- }
94
- // If the element has an operable explicit role:
95
- const role = element.getAttribute('role');
96
- if (opRoles.has(role)) {
97
- // Add this to the operabilities.
98
- opHow.push(`role ${role}`);
99
- }
100
- // If the element has an operable tagname:
101
- const tagName = element.tagName;
102
- if (opTags.has(tagName)) {
103
- // Add this to the operabilities.
104
- opHow.push(`tagname ${tagName}`);
105
- }
106
- const isOperable = opHow.length > 0;
107
- // If the element is focusable but not operable:
108
- if (isFocusable && ! isOperable) {
109
- // Return a severity and violation description.
110
- return '2:Element is Tab-focusable but not operable';
111
- }
112
- // Otherwise, if it is operable but not focusable:
113
- else if (isOperable && ! isFocusable) {
114
- // Return a severity and violation description.
115
- return `3:Element is operable (${opHow.join(', ')}) but not Tab-focusable`;
116
- }
117
- }
118
- };
119
- const whats = 'Elements are Tab-focusable but not operable or vice versa';
120
- return await doTest(
121
- page, report, withItems, 'focAndOp', 'body, body *', whats, 2, getBadWhat.toString()
122
- );
116
+ };
117
+ const whats = 'Elements are Tab-focusable but not operable or vice versa';
118
+ return await (0, testaro_1.doTest)(page, report, withItems, 'focAndOp', 'body, body *', whats, 2, getBadWhat.toString());
123
119
  };
120
+ exports.reporter = reporter;
@@ -0,0 +1,128 @@
1
+ /*
2
+ © 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt.
4
+ © 2025–2026 Jonathan Robert Pool.
5
+
6
+ Licensed under the MIT License. See LICENSE file at the project root or
7
+ https://opensource.org/license/mit/ for details.
8
+
9
+ SPDX-License-Identifier: MIT
10
+ */
11
+
12
+ // IMPORTS
13
+
14
+ import type {Page} from 'playwright';
15
+ import {doTest} from '../procs/testaro';
16
+ import type {BadWhat, Report} from '../types';
17
+
18
+ /*
19
+ focAndOp
20
+ Related to Tenon rule 190.
21
+
22
+ This test reports discrepancies between Tab-focusability and operability. The standard practice is to make focusable elements operable and operable elements focusable. If focusable elements are not operable, users are likely to be surprised that nothing happens when they try to operate such elements. Conversely, if operable elements are not focusable, users who navigate with a keyboard are prevented from operating those elements. The test considers an element Tab-focusable if its tabIndex property has the value 0. The test considers an element operable if it has a non-inherited pointer cursor and is not a 'LABEL' element, has an operable tag name, has an interactive explicit role, or has an 'onclick' attribute.
23
+ Compiled to focAndOp.js by tsc (issue #73); edit this file, not the emitted one.
24
+ */
25
+
26
+ // FUNCTIONS
27
+
28
+ // Runs the test and returns the result.
29
+ export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
30
+ // The candidate selector yields rendered HTML elements.
31
+ const getBadWhat = (element: HTMLElement): BadWhat => {
32
+ // Get whether the element is visible.
33
+ const isVisible = element.checkVisibility({
34
+ contentVisibilityAuto: true,
35
+ opacityProperty: true,
36
+ visibilityProperty: true
37
+ });
38
+ // If so:
39
+ if (isVisible) {
40
+ // Get whether it is focusable.
41
+ const isFocusable = element.tabIndex === 0;
42
+ // Get the operable tagnames.
43
+ const opTags = new Set(
44
+ ['A', 'BUTTON', 'IFRAME', 'INPUT','OPTION', 'SELECT', 'SUMMARY', 'TEXTAREA']
45
+ );
46
+ // Get the operable roles.
47
+ const opRoles = new Set([
48
+ 'button',
49
+ 'checkbox',
50
+ 'combobox',
51
+ 'composite',
52
+ 'grid',
53
+ 'gridcell',
54
+ 'input',
55
+ 'link',
56
+ 'listbox',
57
+ 'menu',
58
+ 'menubar',
59
+ 'menuitem',
60
+ 'menuitemcheckbox',
61
+ 'option',
62
+ 'radio',
63
+ 'radiogroup',
64
+ 'scrollbar',
65
+ 'searchbox',
66
+ 'select',
67
+ 'slider',
68
+ 'spinbutton',
69
+ 'switch',
70
+ 'tab',
71
+ 'tablist',
72
+ 'textbox',
73
+ 'tree',
74
+ 'treegrid',
75
+ 'treeitem',
76
+ 'widget',
77
+ ]);
78
+ // Initialize the operabilities of the element.
79
+ const opHow = [];
80
+ // If the element is not a label:
81
+ if (element.tagName !== 'LABEL') {
82
+ const liveStyleDec = window.getComputedStyle(element);
83
+ // If it has a pointer cursor:
84
+ if (liveStyleDec.cursor === 'pointer') {
85
+ // Neutralize the cursor style of the parent element of the element.
86
+ element.parentElement!.style.cursor = 'default';
87
+ // If, after this, the element still has a pointer cursor:
88
+ if (liveStyleDec.cursor === 'pointer') {
89
+ // Add this to the operabilities of the element.
90
+ opHow.push('pointer cursor');
91
+ }
92
+ }
93
+ }
94
+ // If the element has a click event listener:
95
+ if (element.onclick) {
96
+ // Add this to the operabilities.
97
+ opHow.push('click listener');
98
+ }
99
+ // If the element has an operable explicit role:
100
+ const role = element.getAttribute('role');
101
+ if (opRoles.has(role as string)) {
102
+ // Add this to the operabilities.
103
+ opHow.push(`role ${role}`);
104
+ }
105
+ // If the element has an operable tagname:
106
+ const tagName = element.tagName;
107
+ if (opTags.has(tagName)) {
108
+ // Add this to the operabilities.
109
+ opHow.push(`tagname ${tagName}`);
110
+ }
111
+ const isOperable = opHow.length > 0;
112
+ // If the element is focusable but not operable:
113
+ if (isFocusable && ! isOperable) {
114
+ // Return a severity and violation description.
115
+ return '2:Element is Tab-focusable but not operable';
116
+ }
117
+ // Otherwise, if it is operable but not focusable:
118
+ else if (isOperable && ! isFocusable) {
119
+ // Return a severity and violation description.
120
+ return `3:Element is operable (${opHow.join(', ')}) but not Tab-focusable`;
121
+ }
122
+ }
123
+ };
124
+ const whats = 'Elements are Tab-focusable but not operable or vice versa';
125
+ return await doTest(
126
+ page, report, withItems, 'focAndOp', 'body, body *', whats, 2, getBadWhat.toString()
127
+ );
128
+ };
@@ -0,0 +1,3 @@
1
+ import type { Page } from 'playwright';
2
+ import type { Report } from '../types';
3
+ export declare const reporter: (page: Page, report: Report, _: unknown, withItems: boolean) => Promise<import("../procs/testaro").RuleResult>;
package/testaro/focInd.js CHANGED
@@ -1,5 +1,7 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2021–2023 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,7 +9,9 @@
7
9
 
8
10
  SPDX-License-Identifier: MIT
9
11
  */
10
-
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.reporter = void 0;
14
+ const testaro_1 = require("../procs/testaro");
11
15
  /*
12
16
  focInd
13
17
 
@@ -18,74 +22,69 @@
18
22
  Solid outlines are the standard and thus most familiar focus indicator. Other focus indicators are likely to be misunderstood. For example, underlines may be mistaken for selection or link indicators.
19
23
 
20
24
  WARNING: This test fails to recognize outlines when run with firefox.
25
+ Compiled to focInd.js by tsc (issue #73); edit this file, not the emitted one.
21
26
  */
22
-
23
- // IMPORTS
24
-
25
- const {doTest} = require('../procs/testaro');
26
-
27
27
  // FUNCTIONS
28
-
29
28
  // Runs the test and returns the result.
30
- exports.reporter = async (page, report, _, withItems) => {
31
- const getBadWhat = element => {
32
- // Get whether the element is visible.
33
- const isVisible = element.checkVisibility({
34
- contentVisibilityAuto: true,
35
- opacityProperty: true,
36
- visibilityProperty: true
37
- });
38
- // If so:
39
- if (isVisible) {
40
- // Get whether it is focusable.
41
- const isFocusable = element.tabIndex === 0;
42
- // If so:
43
- if (isFocusable) {
44
- // Get its live style declaration.
45
- const styleDec = window.getComputedStyle(element);
46
- // If the element has a visible outline before being focused:
47
- if (styleDec.outlineStyle !== 'none' && styleDec.outlineWidth !== '0px') {
48
- // Return a violation description.
49
- return 'Element is focusable but has an outline when blurred';
50
- }
51
- // Otherwise, i.e. if the element has no outline, focus the element.
52
- element.focus({preventScroll: true});
53
- // If it now has no outline:
54
- if (styleDec.outlineWidth === '0px' || styleDec.outlineStyle === 'none') {
55
- // Return a violation description.
56
- return 'Element when focused has no outline';
57
- }
58
- // Otherwise, if it now has an outline thinner than 2 pixels:
59
- if (Number.parseFloat(styleDec.outlineWidth) < 2 && styleDec.outlineStyle !== 'none') {
60
- // Return a violation description.
61
- return 'Element when focused has an outline thinner than 2 pixels';
62
- }
63
- // Otherwise, if it now has a transparent outline:
64
- if (styleDec.outlineColor === 'rgba(0, 0, 0, 0)') {
65
- // Return a violation description.
66
- return 'Element when focused has a transparent outline';
67
- }
68
- // Otherwise, if it now has a non-solid outline:
69
- if (styleDec.outlineStyle !== 'solid') {
70
- // If the outline style exists:
71
- if (styleDec.outlineStyle) {
72
- // If the style is not delegated to the user agent:
73
- if (styleDec.outlineStyle !== 'auto') {
74
- // Return a violation description
75
- return `Element when focused has an outline with the ${styleDec.outlineStyle} instead of solid style`;
29
+ const reporter = async (page, report, _, withItems) => {
30
+ // The candidate selector yields rendered HTML elements.
31
+ const getBadWhat = (element) => {
32
+ // Get whether the element is visible.
33
+ const isVisible = element.checkVisibility({
34
+ contentVisibilityAuto: true,
35
+ opacityProperty: true,
36
+ visibilityProperty: true
37
+ });
38
+ // If so:
39
+ if (isVisible) {
40
+ // Get whether it is focusable.
41
+ const isFocusable = element.tabIndex === 0;
42
+ // If so:
43
+ if (isFocusable) {
44
+ // Get its live style declaration.
45
+ const styleDec = window.getComputedStyle(element);
46
+ // If the element has a visible outline before being focused:
47
+ if (styleDec.outlineStyle !== 'none' && styleDec.outlineWidth !== '0px') {
48
+ // Return a violation description.
49
+ return 'Element is focusable but has an outline when blurred';
50
+ }
51
+ // Otherwise, i.e. if the element has no outline, focus the element.
52
+ element.focus({ preventScroll: true });
53
+ // If it now has no outline:
54
+ if (styleDec.outlineWidth === '0px' || styleDec.outlineStyle === 'none') {
55
+ // Return a violation description.
56
+ return 'Element when focused has no outline';
57
+ }
58
+ // Otherwise, if it now has an outline thinner than 2 pixels:
59
+ if (Number.parseFloat(styleDec.outlineWidth) < 2 && styleDec.outlineStyle !== 'none') {
60
+ // Return a violation description.
61
+ return 'Element when focused has an outline thinner than 2 pixels';
62
+ }
63
+ // Otherwise, if it now has a transparent outline:
64
+ if (styleDec.outlineColor === 'rgba(0, 0, 0, 0)') {
65
+ // Return a violation description.
66
+ return 'Element when focused has a transparent outline';
67
+ }
68
+ // Otherwise, if it now has a non-solid outline:
69
+ if (styleDec.outlineStyle !== 'solid') {
70
+ // If the outline style exists:
71
+ if (styleDec.outlineStyle) {
72
+ // If the style is not delegated to the user agent:
73
+ if (styleDec.outlineStyle !== 'auto') {
74
+ // Return a violation description
75
+ return `Element when focused has an outline with the ${styleDec.outlineStyle} instead of solid style`;
76
+ }
77
+ }
78
+ // Otherwise, i.e. if no outline style exists:
79
+ else {
80
+ // Return a violation description.
81
+ return 'Element when focused has an outline with no instead of solid style';
82
+ }
83
+ }
76
84
  }
77
- }
78
- // Otherwise, i.e. if no outline style exists:
79
- else {
80
- // Return a violation description.
81
- return 'Element when focused has an outline with no instead of solid style';
82
- }
83
85
  }
84
- }
85
- }
86
- };
87
- const whats = 'Elements fail to have standard focus indicators';
88
- return await doTest(
89
- page, report, withItems, 'focInd', 'body, body *', whats, 1, getBadWhat.toString()
90
- );
86
+ };
87
+ const whats = 'Elements fail to have standard focus indicators';
88
+ return await (0, testaro_1.doTest)(page, report, withItems, 'focInd', 'body, body *', whats, 1, getBadWhat.toString());
91
89
  };
90
+ exports.reporter = reporter;
@@ -0,0 +1,96 @@
1
+ /*
2
+ © 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt.
4
+ © 2025–2026 Jonathan Robert Pool.
5
+
6
+ Licensed under the MIT License. See LICENSE file at the project root or
7
+ https://opensource.org/license/mit/ for details.
8
+
9
+ SPDX-License-Identifier: MIT
10
+ */
11
+
12
+ // IMPORTS
13
+
14
+ import type {Page} from 'playwright';
15
+ import {doTest} from '../procs/testaro';
16
+ import type {BadWhat, Report} from '../types';
17
+
18
+ /*
19
+ focInd
20
+
21
+ This test reports focusable elements without standard focus indicators. The standard focus indicator is deemed to be a solid outline with a line thickness of at least 2 pixels and a non-transparent color, and only if the element, when not focused, has no outline.
22
+
23
+ The focus indicator is checked immediately after the element is focused. Thus, a delayed focus indicator is ignored. Indication delayed is treated as indication denied. The bases for this treatment are that delayed indication interferes with rapid human or mechanized document consumption and also, if it must be respected, slows accessibility testing.
24
+
25
+ Solid outlines are the standard and thus most familiar focus indicator. Other focus indicators are likely to be misunderstood. For example, underlines may be mistaken for selection or link indicators.
26
+
27
+ WARNING: This test fails to recognize outlines when run with firefox.
28
+ Compiled to focInd.js by tsc (issue #73); edit this file, not the emitted one.
29
+ */
30
+
31
+ // FUNCTIONS
32
+
33
+ // Runs the test and returns the result.
34
+ export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
35
+ // The candidate selector yields rendered HTML elements.
36
+ const getBadWhat = (element: HTMLElement): BadWhat => {
37
+ // Get whether the element is visible.
38
+ const isVisible = element.checkVisibility({
39
+ contentVisibilityAuto: true,
40
+ opacityProperty: true,
41
+ visibilityProperty: true
42
+ });
43
+ // If so:
44
+ if (isVisible) {
45
+ // Get whether it is focusable.
46
+ const isFocusable = element.tabIndex === 0;
47
+ // If so:
48
+ if (isFocusable) {
49
+ // Get its live style declaration.
50
+ const styleDec = window.getComputedStyle(element);
51
+ // If the element has a visible outline before being focused:
52
+ if (styleDec.outlineStyle !== 'none' && styleDec.outlineWidth !== '0px') {
53
+ // Return a violation description.
54
+ return 'Element is focusable but has an outline when blurred';
55
+ }
56
+ // Otherwise, i.e. if the element has no outline, focus the element.
57
+ element.focus({preventScroll: true});
58
+ // If it now has no outline:
59
+ if (styleDec.outlineWidth === '0px' || styleDec.outlineStyle === 'none') {
60
+ // Return a violation description.
61
+ return 'Element when focused has no outline';
62
+ }
63
+ // Otherwise, if it now has an outline thinner than 2 pixels:
64
+ if (Number.parseFloat(styleDec.outlineWidth) < 2 && styleDec.outlineStyle !== 'none') {
65
+ // Return a violation description.
66
+ return 'Element when focused has an outline thinner than 2 pixels';
67
+ }
68
+ // Otherwise, if it now has a transparent outline:
69
+ if (styleDec.outlineColor === 'rgba(0, 0, 0, 0)') {
70
+ // Return a violation description.
71
+ return 'Element when focused has a transparent outline';
72
+ }
73
+ // Otherwise, if it now has a non-solid outline:
74
+ if (styleDec.outlineStyle !== 'solid') {
75
+ // If the outline style exists:
76
+ if (styleDec.outlineStyle) {
77
+ // If the style is not delegated to the user agent:
78
+ if (styleDec.outlineStyle !== 'auto') {
79
+ // Return a violation description
80
+ return `Element when focused has an outline with the ${styleDec.outlineStyle} instead of solid style`;
81
+ }
82
+ }
83
+ // Otherwise, i.e. if no outline style exists:
84
+ else {
85
+ // Return a violation description.
86
+ return 'Element when focused has an outline with no instead of solid style';
87
+ }
88
+ }
89
+ }
90
+ }
91
+ };
92
+ const whats = 'Elements fail to have standard focus indicators';
93
+ return await doTest(
94
+ page, report, withItems, 'focInd', 'body, body *', whats, 1, getBadWhat.toString()
95
+ );
96
+ };
@@ -0,0 +1,3 @@
1
+ import type { Page } from 'playwright';
2
+ import type { Report } from '../types';
3
+ export declare const reporter: (page: Page, report: Report, _: unknown, withItems: boolean) => Promise<import("../procs/testaro").RuleResult>;