testaro 78.0.8 → 78.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/.gitattributes +94 -0
  2. package/.github/workflows/ci.yml +58 -0
  3. package/.github/workflows/publish.yml +49 -0
  4. package/.github/workflows/typescript.yml +35 -0
  5. package/AGENTS.md +2 -2
  6. package/CLAUDE.md +24 -18
  7. package/CONTAINERS.md +9 -6
  8. package/CONTRIBUTING.md +29 -3
  9. package/Dockerfile +1 -1
  10. package/README.md +73 -19
  11. package/UPGRADES.md +4 -0
  12. package/actSpecs-doc.md +13 -1
  13. package/actSpecs.js +29 -5
  14. package/call.js +6 -5
  15. package/docker-compose.yml +8 -2
  16. package/docs/checkpoint-scanning.md +199 -0
  17. package/docs/standard-result-outcome.md +165 -0
  18. package/env.example +44 -10
  19. package/eslint.config.mjs +101 -0
  20. package/netWatch.js +372 -222
  21. package/package.json +46 -31
  22. package/pour/README.md +47 -0
  23. package/pour/pour.min.js +8 -0
  24. package/procs/actDo.js +808 -0
  25. package/procs/catalog.d.ts +17 -0
  26. package/procs/catalog.js +312 -221
  27. package/procs/catalog.ts +398 -0
  28. package/procs/checkpoint.js +110 -0
  29. package/procs/config.d.ts +35 -0
  30. package/procs/config.js +62 -0
  31. package/procs/dateTime.js +2 -1
  32. package/procs/doActs.js +442 -849
  33. package/procs/doTestAct.js +27 -121
  34. package/procs/flow.js +221 -0
  35. package/procs/generateRuleRegistry.js +94 -0
  36. package/procs/getSource.d.ts +26 -0
  37. package/procs/job.js +115 -1
  38. package/procs/launch.d.ts +40 -0
  39. package/procs/launch.js +239 -108
  40. package/procs/nu.d.ts +48 -0
  41. package/procs/scope.js +196 -0
  42. package/procs/shoot.d.ts +33 -0
  43. package/procs/standard.d.ts +18 -0
  44. package/procs/standard.js +83 -0
  45. package/procs/standard.ts +126 -0
  46. package/procs/testAct.js +145 -0
  47. package/procs/testaro.d.ts +20 -0
  48. package/procs/testaro.js +236 -189
  49. package/procs/testaro.ts +321 -0
  50. package/procs/userPath.js +126 -0
  51. package/procs/xPath.d.ts +4 -0
  52. package/procs/xPath.js +81 -69
  53. package/procs/xPath.ts +106 -0
  54. package/procs/xPathScript.d.ts +6 -0
  55. package/procs/xPathScript.js +70 -0
  56. package/run.js +3 -2
  57. package/surea11y/README.md +44 -0
  58. package/surea11y/surea11y.browser.js +14 -0
  59. package/testaro/adbID.d.ts +3 -0
  60. package/testaro/adbID.js +38 -41
  61. package/testaro/adbID.ts +66 -0
  62. package/testaro/allCapStyle.d.ts +3 -0
  63. package/testaro/allCapStyle.js +31 -34
  64. package/testaro/allCapStyle.ts +55 -0
  65. package/testaro/allCaps.d.ts +16 -0
  66. package/testaro/allCaps.js +179 -151
  67. package/testaro/allCaps.ts +228 -0
  68. package/testaro/allHidden.d.ts +13 -0
  69. package/testaro/allHidden.js +29 -30
  70. package/testaro/allHidden.ts +50 -0
  71. package/testaro/allSlanted.d.ts +3 -0
  72. package/testaro/allSlanted.js +30 -33
  73. package/testaro/allSlanted.ts +54 -0
  74. package/testaro/altScheme.d.ts +3 -0
  75. package/testaro/altScheme.js +27 -30
  76. package/testaro/altScheme.ts +50 -0
  77. package/testaro/attVal.d.ts +3 -0
  78. package/testaro/attVal.js +20 -21
  79. package/testaro/attVal.ts +52 -0
  80. package/testaro/autocomplete.d.ts +3 -0
  81. package/testaro/autocomplete.js +59 -75
  82. package/testaro/autocomplete.ts +101 -0
  83. package/testaro/bulk.d.ts +13 -0
  84. package/testaro/bulk.js +32 -33
  85. package/testaro/bulk.ts +55 -0
  86. package/testaro/buttonMenu.d.ts +9 -0
  87. package/testaro/buttonMenu.js +317 -319
  88. package/testaro/buttonMenu.ts +391 -0
  89. package/testaro/captionLoc.d.ts +3 -0
  90. package/testaro/captionLoc.js +17 -20
  91. package/testaro/captionLoc.ts +40 -0
  92. package/testaro/datalistRef.d.ts +3 -0
  93. package/testaro/datalistRef.js +33 -36
  94. package/testaro/datalistRef.ts +55 -0
  95. package/testaro/distortion.d.ts +3 -0
  96. package/testaro/distortion.js +57 -26
  97. package/testaro/distortion.ts +81 -0
  98. package/testaro/docType.d.ts +15 -0
  99. package/testaro/docType.js +25 -25
  100. package/testaro/docType.ts +45 -0
  101. package/testaro/dupAtt.d.ts +16 -0
  102. package/testaro/dupAtt.js +113 -104
  103. package/testaro/dupAtt.ts +144 -0
  104. package/testaro/elements.d.ts +6 -0
  105. package/testaro/elements.js +153 -153
  106. package/testaro/elements.ts +215 -0
  107. package/testaro/embAc.d.ts +3 -0
  108. package/testaro/embAc.js +19 -20
  109. package/testaro/embAc.ts +40 -0
  110. package/testaro/focAll.d.ts +13 -0
  111. package/testaro/focAll.js +185 -191
  112. package/testaro/focAll.ts +217 -0
  113. package/testaro/focAndOp.d.ts +3 -0
  114. package/testaro/focAndOp.js +100 -104
  115. package/testaro/focAndOp.ts +128 -0
  116. package/testaro/focInd.d.ts +3 -0
  117. package/testaro/focInd.js +64 -66
  118. package/testaro/focInd.ts +96 -0
  119. package/testaro/focVis.d.ts +3 -0
  120. package/testaro/focVis.js +28 -30
  121. package/testaro/focVis.ts +52 -0
  122. package/testaro/headEl.d.ts +10 -0
  123. package/testaro/headEl.js +61 -62
  124. package/testaro/headEl.ts +82 -0
  125. package/testaro/headingAmb.d.ts +3 -0
  126. package/testaro/headingAmb.js +54 -63
  127. package/testaro/headingAmb.ts +72 -0
  128. package/testaro/hovInd.d.ts +12 -0
  129. package/testaro/hovInd.js +163 -130
  130. package/testaro/hovInd.ts +199 -0
  131. package/testaro/hover.d.ts +3 -0
  132. package/testaro/hover.js +155 -126
  133. package/testaro/hover.ts +154 -0
  134. package/testaro/hr.d.ts +3 -0
  135. package/testaro/hr.js +14 -17
  136. package/testaro/hr.ts +36 -0
  137. package/testaro/imageLink.d.ts +3 -0
  138. package/testaro/imageLink.js +17 -20
  139. package/testaro/imageLink.ts +42 -0
  140. package/testaro/labClash.d.ts +3 -0
  141. package/testaro/labClash.js +31 -33
  142. package/testaro/labClash.ts +54 -0
  143. package/testaro/legendLoc.d.ts +3 -0
  144. package/testaro/legendLoc.js +17 -20
  145. package/testaro/legendLoc.ts +42 -0
  146. package/testaro/lineHeight.d.ts +3 -0
  147. package/testaro/lineHeight.js +43 -48
  148. package/testaro/lineHeight.ts +70 -0
  149. package/testaro/linkAmb.d.ts +7 -0
  150. package/testaro/linkAmb.js +79 -80
  151. package/testaro/linkAmb.ts +105 -0
  152. package/testaro/linkExt.d.ts +3 -0
  153. package/testaro/linkExt.js +13 -16
  154. package/testaro/linkExt.ts +35 -0
  155. package/testaro/linkOldAtt.d.ts +3 -0
  156. package/testaro/linkOldAtt.js +25 -28
  157. package/testaro/linkOldAtt.ts +48 -0
  158. package/testaro/linkTo.d.ts +3 -0
  159. package/testaro/linkTo.js +22 -22
  160. package/testaro/linkTo.ts +43 -0
  161. package/testaro/linkUl.d.ts +3 -0
  162. package/testaro/linkUl.js +30 -34
  163. package/testaro/linkUl.ts +54 -0
  164. package/testaro/miniText.d.ts +3 -0
  165. package/testaro/miniText.js +41 -44
  166. package/testaro/miniText.ts +68 -0
  167. package/testaro/motion.d.ts +10 -0
  168. package/testaro/motion.js +92 -96
  169. package/testaro/motion.ts +125 -0
  170. package/testaro/nonTable.d.ts +3 -0
  171. package/testaro/nonTable.js +39 -45
  172. package/testaro/nonTable.ts +66 -0
  173. package/testaro/optRoleSel.d.ts +3 -0
  174. package/testaro/optRoleSel.js +16 -19
  175. package/testaro/optRoleSel.ts +41 -0
  176. package/testaro/phOnly.d.ts +3 -0
  177. package/testaro/phOnly.js +18 -21
  178. package/testaro/phOnly.ts +43 -0
  179. package/testaro/pseudoP.d.ts +3 -0
  180. package/testaro/pseudoP.js +36 -38
  181. package/testaro/pseudoP.ts +59 -0
  182. package/testaro/radioSet.d.ts +3 -0
  183. package/testaro/radioSet.js +57 -59
  184. package/testaro/radioSet.ts +79 -0
  185. package/testaro/registry.d.ts +63 -0
  186. package/testaro/registry.js +67 -0
  187. package/testaro/registry.ts +141 -0
  188. package/testaro/role.d.ts +3 -0
  189. package/testaro/role.js +27 -29
  190. package/testaro/role.ts +53 -0
  191. package/testaro/secHeading.d.ts +3 -0
  192. package/testaro/secHeading.js +30 -33
  193. package/testaro/secHeading.ts +53 -0
  194. package/testaro/styleDiff.d.ts +25 -0
  195. package/testaro/styleDiff.js +248 -252
  196. package/testaro/styleDiff.ts +303 -0
  197. package/testaro/tabNav.d.ts +33 -0
  198. package/testaro/tabNav.js +272 -342
  199. package/testaro/tabNav.ts +454 -0
  200. package/testaro/targetsNear.d.ts +9 -0
  201. package/testaro/targetsNear.js +130 -132
  202. package/testaro/targetsNear.ts +160 -0
  203. package/testaro/textNodes.d.ts +6 -0
  204. package/testaro/textNodes.js +139 -135
  205. package/testaro/textNodes.ts +185 -0
  206. package/testaro/textSem.d.ts +3 -0
  207. package/testaro/textSem.js +25 -28
  208. package/testaro/textSem.ts +47 -0
  209. package/testaro/title.d.ts +9 -0
  210. package/testaro/title.js +16 -13
  211. package/testaro/title.ts +31 -0
  212. package/testaro/titledEl.d.ts +3 -0
  213. package/testaro/titledEl.js +15 -18
  214. package/testaro/titledEl.ts +38 -0
  215. package/testaro/zIndex.d.ts +3 -0
  216. package/testaro/zIndex.js +19 -22
  217. package/testaro/zIndex.ts +42 -0
  218. package/tests/alfa.d.ts +45 -0
  219. package/tests/alfa.js +138 -141
  220. package/tests/alfa.ts +214 -0
  221. package/tests/aslint.d.ts +33 -0
  222. package/tests/aslint.js +272 -249
  223. package/tests/aslint.ts +301 -0
  224. package/tests/axe.d.ts +27 -0
  225. package/tests/axe.js +199 -200
  226. package/tests/axe.ts +277 -0
  227. package/tests/ed11y.d.ts +28 -0
  228. package/tests/ed11y.js +141 -99
  229. package/tests/ed11y.ts +178 -0
  230. package/tests/htmlcs.d.ts +21 -0
  231. package/tests/htmlcs.js +174 -140
  232. package/tests/htmlcs.ts +181 -0
  233. package/tests/ibm.d.ts +52 -0
  234. package/tests/ibm.js +164 -166
  235. package/tests/ibm.ts +251 -0
  236. package/tests/nuVal.d.ts +13 -0
  237. package/tests/nuVal.js +107 -112
  238. package/tests/nuVal.ts +145 -0
  239. package/tests/nuVnu.d.ts +15 -0
  240. package/tests/nuVnu.js +141 -111
  241. package/tests/nuVnu.ts +144 -0
  242. package/tests/pour.d.ts +31 -0
  243. package/tests/pour.js +242 -0
  244. package/tests/pour.ts +273 -0
  245. package/tests/qualWeb.d.ts +39 -0
  246. package/tests/qualWeb.js +302 -272
  247. package/tests/qualWeb.ts +415 -0
  248. package/tests/surea11y.d.ts +33 -0
  249. package/tests/surea11y.js +288 -0
  250. package/tests/surea11y.ts +334 -0
  251. package/tests/testaro.d.ts +25 -0
  252. package/tests/testaro.js +745 -652
  253. package/tests/testaro.ts +862 -0
  254. package/tests/wave.d.ts +46 -0
  255. package/tests/wave.js +166 -177
  256. package/tests/wave.ts +252 -0
  257. package/tsconfig.json +17 -0
  258. package/types.d.ts +243 -0
  259. package/types.js +10 -0
  260. package/types.ts +376 -0
  261. package/validation/act/README.md +46 -0
  262. package/validation/act/capture.js +424 -0
  263. package/validation/act/chromium-issue-draft.md +66 -0
  264. package/validation/act/fp-triage-2026-08-22.md +86 -0
  265. package/validation/act/isolation-notes.md +159 -0
  266. package/validation/act/playwright-issue-draft.md +73 -0
  267. package/validation/act/propose-mappings.js +0 -0
  268. package/validation/act/repro-cdp-raw.js +105 -0
  269. package/validation/act/repro-metarefresh.js +65 -0
  270. package/validation/act/score.js +184 -0
  271. package/validation/act/stage3a-stress-report.md +69 -0
  272. package/validation/act/stage3b-mapping-proposals.md +94 -0
  273. package/validation/act/stage3b-triage-draft.md +138 -0
  274. package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
  275. package/validation/executors/netWatch.js +180 -90
  276. package/validation/executors/test.js +17 -2
  277. package/validation/executors/tests.js +118 -10
  278. package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
  279. package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
  280. package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
  281. package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
  282. package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
  283. package/validation/knownFailures.json +1 -0
  284. package/validation/tests/jobProperties/adbID.json +27 -2
  285. package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
  286. package/validation/tests/jobProperties/allCaps.json +25 -0
  287. package/validation/tests/jobProperties/allHidden.json +133 -13
  288. package/validation/tests/jobProperties/allSlanted.json +3 -3
  289. package/validation/tests/jobProperties/altScheme.json +23 -8
  290. package/validation/tests/jobProperties/attVal.json +57 -57
  291. package/validation/tests/jobProperties/autocomplete.json +11 -1
  292. package/validation/tests/jobProperties/bulk.json +6 -1
  293. package/validation/tests/jobProperties/buttonMenu.json +63 -42
  294. package/validation/tests/jobProperties/captionLoc.json +1 -6
  295. package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
  296. package/validation/tests/jobProperties/checkpoint-page.json +397 -0
  297. package/validation/tests/jobProperties/checkpoint.json +395 -0
  298. package/validation/tests/jobProperties/datalistRef.json +20 -5
  299. package/validation/tests/jobProperties/distortion.json +28 -3
  300. package/validation/tests/jobProperties/docType.json +2 -2
  301. package/validation/tests/jobProperties/dupAtt.json +48 -33
  302. package/validation/tests/jobProperties/elements.json +28 -28
  303. package/validation/tests/jobProperties/embAc.json +36 -31
  304. package/validation/tests/jobProperties/focAndOp.json +284 -0
  305. package/validation/tests/jobProperties/focInd.json +34 -34
  306. package/validation/tests/jobProperties/focVis.json +1 -1
  307. package/validation/tests/jobProperties/hover.json +39 -37
  308. package/validation/tests/jobProperties/hr.json +13 -3
  309. package/validation/tests/jobProperties/imageLink.json +0 -5
  310. package/validation/tests/jobProperties/labClash.json +68 -33
  311. package/validation/tests/jobProperties/legendLoc.json +1 -6
  312. package/validation/tests/jobProperties/lineHeight.json +2 -2
  313. package/validation/tests/jobProperties/linkAmb.json +13 -18
  314. package/validation/tests/jobProperties/linkExt.json +1 -1
  315. package/validation/tests/jobProperties/linkOldAtt.json +12 -2
  316. package/validation/tests/jobProperties/linkTo.json +1 -1
  317. package/validation/tests/jobProperties/linkUl.json +65 -65
  318. package/validation/tests/jobProperties/miniText.json +7 -2
  319. package/validation/tests/jobProperties/motion.json +4 -50
  320. package/validation/tests/jobProperties/nonTable.json +48 -3
  321. package/validation/tests/jobProperties/optRoleSel.json +12 -2
  322. package/validation/tests/jobProperties/phOnly.json +6 -16
  323. package/validation/tests/jobProperties/pseudoP.json +17 -2
  324. package/validation/tests/jobProperties/radioSet.json +30 -30
  325. package/validation/tests/jobProperties/role.json +25 -5
  326. package/validation/tests/jobProperties/secHeading.json +26 -21
  327. package/validation/tests/jobProperties/styleDiff.json +35 -5
  328. package/validation/tests/jobProperties/tabNav.json +3 -1
  329. package/validation/tests/jobProperties/targetsNear.json +237 -0
  330. package/validation/tests/jobProperties/textNodes.json +29 -29
  331. package/validation/tests/jobProperties/textSem.json +26 -1
  332. package/validation/tests/jobProperties/title.json +12 -2
  333. package/validation/tests/jobProperties/titledEl.json +39 -9
  334. package/validation/tests/jobProperties/userPath.json +317 -0
  335. package/validation/tests/jobProperties/zIndex.json +40 -35
  336. package/validation/tests/targets/allCapStyle/index.html +26 -0
  337. package/validation/tests/targets/checkpoint/index.html +38 -0
  338. package/validation/tests/targets/datalistRef/index.html +1 -1
  339. package/validation/tests/targets/focAndOp/bad.html +29 -0
  340. package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
  341. package/validation/tests/targets/focInd/bad.html +2 -1
  342. package/validation/tests/targets/headEl/index.html +10 -1
  343. package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
  344. package/validation/tests/targets/userPath/index.html +42 -0
  345. package/validation/validateTest.js +61 -9
  346. package/.claude/settings.local.json +0 -11
  347. package/.eslintrc.json +0 -41
  348. package/htmlcs/.eslintrc.json +0 -67
  349. package/validation/tests/jobProperties/linkTitle.json +0 -127
  350. package/validation/tests/jobProperties/opFoc.json +0 -164
  351. package/validation/tests/jobProperties/targetSmall.json +0 -152
  352. package/validation/tests/jobProperties/targetTiny.json +0 -142
  353. package/validation/tests/targets/focOp/bad.html +0 -25
  354. package/validation/tests/targets/linkTitle/index.html +0 -24
  355. package/validation/tests/targets/opFoc/bad.html +0 -26
  356. package/validation/tests/targets/opFoc/good.html +0 -23
@@ -0,0 +1,66 @@
1
+ /*
2
+ © 2022–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 https://opensource.org/license/mit/ for details.
7
+
8
+ SPDX-License-Identifier: MIT
9
+ */
10
+
11
+ // IMPORTS
12
+
13
+ import type {Page} from 'playwright';
14
+ import {doTest} from '../procs/testaro';
15
+ import type {BadWhat, Report} from '../types';
16
+
17
+ /*
18
+ nonTable
19
+ Derived from the bbc-a11y useTablesForData test. Crude heuristics omitted.
20
+ This test reports tables used for layout.
21
+ Compiled to nonTable.js by tsc (issue #73); edit this file, not the emitted one.
22
+ */
23
+
24
+ // FUNCTIONS
25
+
26
+ // Runs the test and returns the result.
27
+ export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
28
+ // The candidate selector yields table elements.
29
+ const getBadWhat = (element: HTMLTableElement): BadWhat => {
30
+ // If the element contains another table:
31
+ if (element.querySelector('table')) {
32
+ // Return a violation description.
33
+ return 'Element contains another table';
34
+ }
35
+ const rowCount = element.querySelectorAll('tr').length;
36
+ const columnCount = Math.max(
37
+ ... Array
38
+ .from(element.querySelectorAll('tr'))
39
+ .map(row => Array.from(row.querySelectorAll('th, td')).length)
40
+ );
41
+ // Otherwise, if it has only 1 column or 1 row:
42
+ if (rowCount === 1 || columnCount === 1) {
43
+ // Return a violation description.
44
+ return 'Element has only one row or one column';
45
+ }
46
+ // Otherwise, if it contains an object or player:
47
+ if (element.querySelector('object, embed, applet, audio, video')) {
48
+ // Return a violation description.
49
+ return 'Element contains an object or player';
50
+ }
51
+ const role = element.getAttribute('role');
52
+ // Otherwise, if it has no table-compatible explicit role or descendant element:
53
+ if (! (
54
+ ['grid', 'treegrid'].includes(role as string)
55
+ || element.caption
56
+ || element.querySelector('col, colgroup, tfoot, th, thead')
57
+ )) {
58
+ // Return a violation description.
59
+ return 'Element has no table-compatible explicit role or descendant element';
60
+ }
61
+ };
62
+ const whats = 'table elements are misused for non-table content';
63
+ return await doTest(
64
+ page, report, withItems, 'nonTable', 'body table', whats, 2, getBadWhat.toString()
65
+ );
66
+ };
@@ -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>;
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2025 Juan S. Casado.
@@ -9,30 +10,26 @@
9
10
 
10
11
  SPDX-License-Identifier: MIT
11
12
  */
12
-
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.reporter = void 0;
15
+ const testaro_1 = require("../procs/testaro");
13
16
  /*
14
17
  optRoleSel
15
18
  Clean-room rule.
16
19
  This test reports elements with role=option that are missing aria-selected attributes.
20
+ Compiled to optRoleSel.js by tsc (issue #73); edit this file, not the emitted one.
17
21
  */
18
-
19
- // IMPORTS
20
-
21
- const {doTest} = require('../procs/testaro');
22
-
23
22
  // FUNCTIONS
24
-
25
23
  // Runs the test and returns the result.
26
- exports.reporter = async (page, report, _, withItems) => {
27
- const getBadWhat = element => {
28
- // If the element has no aria-selected attribute:
29
- if (! element.hasAttribute('aria-selected')) {
30
- // Return a violation description.
31
- return 'Element has role=option but no aria-selected attribute';
32
- }
33
- };
34
- const whats = 'Elements with role=option have no aria-selected attributes';
35
- return await doTest(
36
- page, report, withItems, 'optRoleSel', 'body [role="option"]', whats, 1, getBadWhat.toString()
37
- );
24
+ const reporter = async (page, report, _, withItems) => {
25
+ const getBadWhat = element => {
26
+ // If the element has no aria-selected attribute:
27
+ if (!element.hasAttribute('aria-selected')) {
28
+ // Return a violation description.
29
+ return 'Element has role=option but no aria-selected attribute';
30
+ }
31
+ };
32
+ const whats = 'Elements with role=option have no aria-selected attributes';
33
+ return await (0, testaro_1.doTest)(page, report, withItems, 'optRoleSel', 'body [role="option"]', whats, 1, getBadWhat.toString());
38
34
  };
35
+ exports.reporter = reporter;
@@ -0,0 +1,41 @@
1
+ /*
2
+ © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Juan S. Casado.
4
+ © 2026 Jeff Witt.
5
+ © 2025–2026 Jonathan Robert Pool.
6
+
7
+ Licensed under the MIT License. See LICENSE file at the project root or
8
+ https://opensource.org/license/mit/ for details.
9
+
10
+ SPDX-License-Identifier: MIT
11
+ */
12
+
13
+ // IMPORTS
14
+
15
+ import type {Page} from 'playwright';
16
+ import {doTest} from '../procs/testaro';
17
+ import type {GetBadWhat, Report} from '../types';
18
+
19
+ /*
20
+ optRoleSel
21
+ Clean-room rule.
22
+ This test reports elements with role=option that are missing aria-selected attributes.
23
+ Compiled to optRoleSel.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
+ const getBadWhat: GetBadWhat = element => {
31
+ // If the element has no aria-selected attribute:
32
+ if (! element.hasAttribute('aria-selected')) {
33
+ // Return a violation description.
34
+ return 'Element has role=option but no aria-selected attribute';
35
+ }
36
+ };
37
+ const whats = 'Elements with role=option have no aria-selected attributes';
38
+ return await doTest(
39
+ page, report, withItems, 'optRoleSel', 'body [role="option"]', whats, 1, getBadWhat.toString()
40
+ );
41
+ };
@@ -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/phOnly.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2025 Juan S. Casado.
@@ -9,32 +10,28 @@
9
10
 
10
11
  SPDX-License-Identifier: MIT
11
12
  */
12
-
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.reporter = void 0;
15
+ const testaro_1 = require("../procs/testaro");
13
16
  /*
14
17
  phOnly
15
18
  Clean-room rule.
16
19
  This test reports input elements that have placeholders but no accessible names. The standard for accessible name computation is employed; it accepts title attributes as sources for accessible names. Thus, this test does not report reliance on title attributes for accessible names, although such reliance is generally considered a poor practice.
20
+ Compiled to phOnly.js by tsc (issue #73); edit this file, not the emitted one.
17
21
  */
18
-
19
- // IMPORTS
20
-
21
- const {doTest} = require('../procs/testaro');
22
-
23
22
  // FUNCTIONS
24
-
25
23
  // Runs the test and returns the result.
26
- exports.reporter = async (page, report, _, withItems) => {
27
- const getBadWhat = element => {
28
- // Get the accessible name of the element.
29
- const accessibleName = window.getAccessibleName(element);
30
- // If there is none:
31
- if (! accessibleName) {
32
- // Return a violation description.
33
- return 'Element has a placeholder but no accessible name';
34
- }
35
- };
36
- const whats = 'input elements have placeholders but no accessible names';
37
- return await doTest(
38
- page, report, withItems, 'phOnly', 'body input[placeholder]', whats, 2, getBadWhat.toString()
39
- );
24
+ const reporter = async (page, report, _, withItems) => {
25
+ const getBadWhat = element => {
26
+ // Get the accessible name of the element.
27
+ const accessibleName = window.getAccessibleName(element);
28
+ // If there is none:
29
+ if (!accessibleName) {
30
+ // Return a violation description.
31
+ return 'Element has a placeholder but no accessible name';
32
+ }
33
+ };
34
+ const whats = 'input elements have placeholders but no accessible names';
35
+ return await (0, testaro_1.doTest)(page, report, withItems, 'phOnly', 'body input[placeholder]', whats, 2, getBadWhat.toString());
40
36
  };
37
+ exports.reporter = reporter;
@@ -0,0 +1,43 @@
1
+ /*
2
+ © 2025 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025 Juan S. Casado.
4
+ © 2026 Jeff Witt.
5
+ © 2025–2026 Jonathan Robert Pool.
6
+
7
+ Licensed under the MIT License. See LICENSE file at the project root or
8
+ https://opensource.org/license/mit/ for details.
9
+
10
+ SPDX-License-Identifier: MIT
11
+ */
12
+
13
+ // IMPORTS
14
+
15
+ import type {Page} from 'playwright';
16
+ import {doTest} from '../procs/testaro';
17
+ import type {GetBadWhat, Report} from '../types';
18
+
19
+ /*
20
+ phOnly
21
+ Clean-room rule.
22
+ This test reports input elements that have placeholders but no accessible names. The standard for accessible name computation is employed; it accepts title attributes as sources for accessible names. Thus, this test does not report reliance on title attributes for accessible names, although such reliance is generally considered a poor practice.
23
+ Compiled to phOnly.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
+ const getBadWhat: GetBadWhat = element => {
31
+ // Get the accessible name of the element.
32
+ const accessibleName = window.getAccessibleName(element);
33
+ // If there is none:
34
+ if (! accessibleName) {
35
+ // Return a violation description.
36
+ return 'Element has a placeholder but no accessible name';
37
+ }
38
+ };
39
+ const whats = 'input elements have placeholders but no accessible names';
40
+ return await doTest(
41
+ page, report, withItems, 'phOnly', 'body input[placeholder]', whats, 2, getBadWhat.toString()
42
+ );
43
+ };
@@ -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>;
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -7,49 +8,46 @@
7
8
 
8
9
  SPDX-License-Identifier: MIT
9
10
  */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.reporter = void 0;
13
+ const testaro_1 = require("../procs/testaro");
10
14
  /*
11
15
  pseudoP
12
16
  This test reports 2 or more sequential br elements without intervening text content. They may be inferior substitutes for p elements.
17
+ Compiled to pseudoP.js by tsc (issue #73); edit this file, not the emitted one.
13
18
  */
14
-
15
- // IMPORTS
16
-
17
- const {doTest} = require('../procs/testaro');
18
-
19
19
  // FUNCTIONS
20
-
21
20
  // Runs the test and returns the result.
22
- exports.reporter = async (page, report, _, withItems) => {
23
- const getBadWhat = element => {
24
- // Get the node before the element node.
25
- const previousNode = element.previousSibling;
26
- let isBad = false;
27
- // If it is a br element:
28
- if (previousNode && previousNode.nodeType === Node.ELEMENT_NODE && previousNode.tagName === 'BR') {
29
- // Classify the element as a violator.
30
- isBad = true;
31
- }
32
- // Otherwise, if it is a text node:
33
- else if (previousNode && previousNode.nodeType === Node.TEXT_NODE) {
34
- // If the text node contains only whitespace:
35
- if (previousNode.textContent.trim() === '') {
36
- // Get the node before the text node.
37
- const beforeText = previousNode.previousSibling;
38
- // If that node is a br element:
39
- if (beforeText && beforeText.nodeType === Node.ELEMENT_NODE && beforeText.tagName === 'BR') {
40
- // Classify the element as a violator.
41
- isBad = true;
21
+ const reporter = async (page, report, _, withItems) => {
22
+ const getBadWhat = element => {
23
+ // Get the node before the element node.
24
+ const previousNode = element.previousSibling;
25
+ let isBad = false;
26
+ // If it is a br element:
27
+ if (previousNode && previousNode.nodeType === Node.ELEMENT_NODE && previousNode.tagName === 'BR') {
28
+ // Classify the element as a violator.
29
+ isBad = true;
30
+ }
31
+ // Otherwise, if it is a text node:
32
+ else if (previousNode && previousNode.nodeType === Node.TEXT_NODE) {
33
+ // If the text node contains only whitespace:
34
+ if (previousNode.textContent.trim() === '') {
35
+ // Get the node before the text node.
36
+ const beforeText = previousNode.previousSibling;
37
+ // If that node is a br element:
38
+ if (beforeText && beforeText.nodeType === Node.ELEMENT_NODE && beforeText.tagName === 'BR') {
39
+ // Classify the element as a violator.
40
+ isBad = true;
41
+ }
42
+ }
43
+ }
44
+ // If the element is a violator:
45
+ if (isBad) {
46
+ // Return a violation description.
47
+ return `Element follows another br element, possibly constituting a pseudo-paragraph`;
42
48
  }
43
- }
44
- }
45
- // If the element is a violator:
46
- if (isBad) {
47
- // Return a violation description.
48
- return `Element follows another br element, possibly constituting a pseudo-paragraph`;
49
- }
50
- };
51
- const whats = 'br elements follow other br elements, possibly constituting pseudo-paragraphs';
52
- return await doTest(
53
- page, report, withItems, 'pseudoP', 'body br', whats, 0, getBadWhat.toString()
54
- );
49
+ };
50
+ const whats = 'br elements follow other br elements, possibly constituting pseudo-paragraphs';
51
+ return await (0, testaro_1.doTest)(page, report, withItems, 'pseudoP', 'body br', whats, 0, getBadWhat.toString());
55
52
  };
53
+ exports.reporter = reporter;
@@ -0,0 +1,59 @@
1
+ /*
2
+ © 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 https://opensource.org/license/mit/ for details.
7
+
8
+ SPDX-License-Identifier: MIT
9
+ */
10
+
11
+ // IMPORTS
12
+
13
+ import type {Page} from 'playwright';
14
+ import {doTest} from '../procs/testaro';
15
+ import type {GetBadWhat, Report} from '../types';
16
+
17
+ /*
18
+ pseudoP
19
+ This test reports 2 or more sequential br elements without intervening text content. They may be inferior substitutes for p elements.
20
+ Compiled to pseudoP.js by tsc (issue #73); edit this file, not the emitted one.
21
+ */
22
+
23
+ // FUNCTIONS
24
+
25
+ // Runs the test and returns the result.
26
+ export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
27
+ const getBadWhat: GetBadWhat = element => {
28
+ // Get the node before the element node.
29
+ const previousNode = element.previousSibling;
30
+ let isBad = false;
31
+ // If it is a br element:
32
+ if (previousNode && previousNode.nodeType === Node.ELEMENT_NODE && (previousNode as Element).tagName === 'BR') {
33
+ // Classify the element as a violator.
34
+ isBad = true;
35
+ }
36
+ // Otherwise, if it is a text node:
37
+ else if (previousNode && previousNode.nodeType === Node.TEXT_NODE) {
38
+ // If the text node contains only whitespace:
39
+ if (previousNode.textContent!.trim() === '') {
40
+ // Get the node before the text node.
41
+ const beforeText = previousNode.previousSibling;
42
+ // If that node is a br element:
43
+ if (beforeText && beforeText.nodeType === Node.ELEMENT_NODE && (beforeText as Element).tagName === 'BR') {
44
+ // Classify the element as a violator.
45
+ isBad = true;
46
+ }
47
+ }
48
+ }
49
+ // If the element is a violator:
50
+ if (isBad) {
51
+ // Return a violation description.
52
+ return `Element follows another br element, possibly constituting a pseudo-paragraph`;
53
+ }
54
+ };
55
+ const whats = 'br elements follow other br elements, possibly constituting pseudo-paragraphs';
56
+ return await doTest(
57
+ page, report, withItems, 'pseudoP', 'body br', whats, 0, getBadWhat.toString()
58
+ );
59
+ };
@@ -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>;
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2021–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -7,69 +8,66 @@
7
8
 
8
9
  SPDX-License-Identifier: MIT
9
10
  */
10
-
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.reporter = void 0;
13
+ const testaro_1 = require("../procs/testaro");
11
14
  /*
12
15
  radioSet
13
16
  This test reports nonstandard groupings of radio buttons. It defines a standard grouping to require that two or more radio buttons with the same name, and no other radio buttons, be grouped in a fieldset element with a valid legend element.
17
+ Compiled to radioSet.js by tsc (issue #73); edit this file, not the emitted one.
14
18
  */
15
-
16
- // IMPORTS
17
-
18
- const {doTest} = require('../procs/testaro');
19
-
20
19
  // FUNCTIONS
21
-
22
20
  // Runs the test and returns the result.
23
- exports.reporter = async (page, report, _, withItems) => {
24
- const getBadWhat = element => {
25
- // Get the name of the element.
26
- const elName = element.name;
27
- // If it has none:
28
- if (! elName) {
29
- // Return a violation description.
30
- return 'radio button has no name attribute';
31
- }
32
- // Identify the field set of the element.
33
- const elFS = element.closest('fieldset');
34
- // If it has none:
35
- if (! elFS) {
36
- // Return a violation description.
37
- return 'radio button is not in a field set';
38
- }
39
- // Get the first child element of the field set.
40
- const fsChild1 = elFS.firstElementChild;
41
- // Get whether the child is a legend with text content.
42
- const legendOK = fsChild1.tagName === 'LEGEND'
43
- && fsChild1.textContent.replace(/\s/g, '').length;
44
- // If it is not:
45
- if (! legendOK) {
46
- // Return a violation description.
47
- return 'radio button is in a field set without a valid legend';
48
- }
49
- // Get the count of radio buttons with the same name in the field set.
50
- const nameGroupSize = elFS.querySelectorAll(`input[type=radio][name=${elName}]`).length;
51
- // If the count is only 1:
52
- if (nameGroupSize === 1) {
53
- // Return a violation description.
54
- return 'radio button is the only one with its name in its field set';
55
- }
56
- // Get the count of radio buttons in the field set.
57
- const groupSize = elFS.querySelectorAll('input[type=radio]').length;
58
- // If it is greater than the count of radio buttons with the same name in the field set:
59
- if (groupSize > nameGroupSize) {
60
- // Return a violation description.
61
- return 'radio button shares a field set with differently named others';
62
- }
63
- // Get the count of radio buttons with the same name in the document.
64
- const nameDocSize = document.querySelectorAll(`input[type=radio][name=${elName}]`).length;
65
- // If it is greater, and thus some such radio button is outside the field set:
66
- if (nameDocSize > nameGroupSize) {
67
- // Return a violation description.
68
- return 'radio button shares a name with others outside its field set';
69
- }
70
- };
71
- const whats = 'Radio buttons are not validly grouped in fieldsets with legends';
72
- return await doTest(
73
- page, report, withItems, 'radioSet', 'body input[type=radio]', whats, 2, getBadWhat.toString()
74
- );
21
+ const reporter = async (page, report, _, withItems) => {
22
+ // The candidate selector guarantees input elements, so the parameter is typed as one.
23
+ const getBadWhat = (element) => {
24
+ // Get the name of the element.
25
+ const elName = element.name;
26
+ // If it has none:
27
+ if (!elName) {
28
+ // Return a violation description.
29
+ return 'radio button has no name attribute';
30
+ }
31
+ // Identify the field set of the element.
32
+ const elFS = element.closest('fieldset');
33
+ // If it has none:
34
+ if (!elFS) {
35
+ // Return a violation description.
36
+ return 'radio button is not in a field set';
37
+ }
38
+ // Get the first child element of the field set.
39
+ const fsChild1 = elFS.firstElementChild;
40
+ // Get whether the child is a legend with text content.
41
+ const legendOK = fsChild1.tagName === 'LEGEND'
42
+ && fsChild1.textContent.replace(/\s/g, '').length;
43
+ // If it is not:
44
+ if (!legendOK) {
45
+ // Return a violation description.
46
+ return 'radio button is in a field set without a valid legend';
47
+ }
48
+ // Get the count of radio buttons with the same name in the field set.
49
+ const nameGroupSize = elFS.querySelectorAll(`input[type=radio][name=${elName}]`).length;
50
+ // If the count is only 1:
51
+ if (nameGroupSize === 1) {
52
+ // Return a violation description.
53
+ return 'radio button is the only one with its name in its field set';
54
+ }
55
+ // Get the count of radio buttons in the field set.
56
+ const groupSize = elFS.querySelectorAll('input[type=radio]').length;
57
+ // If it is greater than the count of radio buttons with the same name in the field set:
58
+ if (groupSize > nameGroupSize) {
59
+ // Return a violation description.
60
+ return 'radio button shares a field set with differently named others';
61
+ }
62
+ // Get the count of radio buttons with the same name in the document.
63
+ const nameDocSize = document.querySelectorAll(`input[type=radio][name=${elName}]`).length;
64
+ // If it is greater, and thus some such radio button is outside the field set:
65
+ if (nameDocSize > nameGroupSize) {
66
+ // Return a violation description.
67
+ return 'radio button shares a name with others outside its field set';
68
+ }
69
+ };
70
+ const whats = 'Radio buttons are not validly grouped in fieldsets with legends';
71
+ return await (0, testaro_1.doTest)(page, report, withItems, 'radioSet', 'body input[type=radio]', whats, 2, getBadWhat.toString());
75
72
  };
73
+ exports.reporter = reporter;
@@ -0,0 +1,79 @@
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 https://opensource.org/license/mit/ for details.
7
+
8
+ SPDX-License-Identifier: MIT
9
+ */
10
+
11
+ // IMPORTS
12
+
13
+ import type {Page} from 'playwright';
14
+ import {doTest} from '../procs/testaro';
15
+ import type {BadWhat, Report} from '../types';
16
+
17
+ /*
18
+ radioSet
19
+ This test reports nonstandard groupings of radio buttons. It defines a standard grouping to require that two or more radio buttons with the same name, and no other radio buttons, be grouped in a fieldset element with a valid legend element.
20
+ Compiled to radioSet.js by tsc (issue #73); edit this file, not the emitted one.
21
+ */
22
+
23
+ // FUNCTIONS
24
+
25
+ // Runs the test and returns the result.
26
+ export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
27
+ // The candidate selector guarantees input elements, so the parameter is typed as one.
28
+ const getBadWhat = (element: HTMLInputElement): BadWhat => {
29
+ // Get the name of the element.
30
+ const elName = element.name;
31
+ // If it has none:
32
+ if (! elName) {
33
+ // Return a violation description.
34
+ return 'radio button has no name attribute';
35
+ }
36
+ // Identify the field set of the element.
37
+ const elFS = element.closest('fieldset');
38
+ // If it has none:
39
+ if (! elFS) {
40
+ // Return a violation description.
41
+ return 'radio button is not in a field set';
42
+ }
43
+ // Get the first child element of the field set.
44
+ const fsChild1 = elFS.firstElementChild;
45
+ // Get whether the child is a legend with text content.
46
+ const legendOK = fsChild1!.tagName === 'LEGEND'
47
+ && fsChild1!.textContent!.replace(/\s/g, '').length;
48
+ // If it is not:
49
+ if (! legendOK) {
50
+ // Return a violation description.
51
+ return 'radio button is in a field set without a valid legend';
52
+ }
53
+ // Get the count of radio buttons with the same name in the field set.
54
+ const nameGroupSize = elFS.querySelectorAll(`input[type=radio][name=${elName}]`).length;
55
+ // If the count is only 1:
56
+ if (nameGroupSize === 1) {
57
+ // Return a violation description.
58
+ return 'radio button is the only one with its name in its field set';
59
+ }
60
+ // Get the count of radio buttons in the field set.
61
+ const groupSize = elFS.querySelectorAll('input[type=radio]').length;
62
+ // If it is greater than the count of radio buttons with the same name in the field set:
63
+ if (groupSize > nameGroupSize) {
64
+ // Return a violation description.
65
+ return 'radio button shares a field set with differently named others';
66
+ }
67
+ // Get the count of radio buttons with the same name in the document.
68
+ const nameDocSize = document.querySelectorAll(`input[type=radio][name=${elName}]`).length;
69
+ // If it is greater, and thus some such radio button is outside the field set:
70
+ if (nameDocSize > nameGroupSize) {
71
+ // Return a violation description.
72
+ return 'radio button shares a name with others outside its field set';
73
+ }
74
+ };
75
+ const whats = 'Radio buttons are not validly grouped in fieldsets with legends';
76
+ return await doTest(
77
+ page, report, withItems, 'radioSet', 'body input[type=radio]', whats, 2, getBadWhat.toString()
78
+ );
79
+ };