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
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -8,58 +9,54 @@
8
9
 
9
10
  SPDX-License-Identifier: MIT
10
11
  */
11
-
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.reporter = void 0;
14
+ const testaro_1 = require("../procs/testaro");
12
15
  /*
13
16
  miniText
14
17
  Derived from the bbc-a11y textCannotBeTooSmall test.
15
18
  Related to Tenon rule 134.
16
19
  This test reports elements with font sizes smaller than 11 pixels.
20
+ Compiled to miniText.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
- const rawText = element.textContent || '';
29
- // If the element has text content with any non-whitespace:
30
- if (/[^\s]/.test(rawText)) {
31
- const isVisible = element.checkVisibility({
32
- contentVisibilityAuto: true,
33
- opacityProperty: true,
34
- visibilityProperty: true
35
- });
36
- // If the element is visible:
37
- if (isVisible) {
38
- const styleDec = window.getComputedStyle(element);
39
- // Get its font size.
40
- const fontSizeString = styleDec.fontSize;
41
- const fontSize = Number.parseFloat(fontSizeString);
42
- // If its font size is smaller than 11 pixels:
43
- if (fontSize < 11) {
44
- const parent = element.parentElement;
45
- // If the element has a parent:
46
- if (parent) {
47
- const parentStyleDec = window.getComputedStyle(parent);
48
- const parentFontSize = Number.parseFloat(parentStyleDec.fontSize);
49
- // If the parent also has a font size smaller than 11 pixels:
50
- if (parentFontSize < 11) {
51
- // Do not report a violation, because the font size may be inherited.
52
- return null;
24
+ const reporter = async (page, report, _, withItems) => {
25
+ const getBadWhat = element => {
26
+ const rawText = element.textContent || '';
27
+ // If the element has text content with any non-whitespace:
28
+ if (/[^\s]/.test(rawText)) {
29
+ const isVisible = element.checkVisibility({
30
+ contentVisibilityAuto: true,
31
+ opacityProperty: true,
32
+ visibilityProperty: true
33
+ });
34
+ // If the element is visible:
35
+ if (isVisible) {
36
+ const styleDec = window.getComputedStyle(element);
37
+ // Get its font size.
38
+ const fontSizeString = styleDec.fontSize;
39
+ const fontSize = Number.parseFloat(fontSizeString);
40
+ // If its font size is smaller than 11 pixels:
41
+ if (fontSize < 11) {
42
+ const parent = element.parentElement;
43
+ // If the element has a parent:
44
+ if (parent) {
45
+ const parentStyleDec = window.getComputedStyle(parent);
46
+ const parentFontSize = Number.parseFloat(parentStyleDec.fontSize);
47
+ // If the parent also has a font size smaller than 11 pixels:
48
+ if (parentFontSize < 11) {
49
+ // Do not report a violation, because the font size may be inherited.
50
+ return null;
51
+ }
52
+ }
53
+ // Return a violation description.
54
+ return `Element is visible but its font size is ${fontSize}px, smaller than 11px`;
55
+ }
53
56
  }
54
- }
55
- // Return a violation description.
56
- return `Element is visible but its font size is ${fontSize}px, smaller than 11px`;
57
57
  }
58
- }
59
- }
60
- };
61
- const whats = 'Visible elements have font sizes smaller than 11 pixels';
62
- return await doTest(
63
- page, report, withItems, 'miniText', 'body, body *:not(script, style)', whats, 2, getBadWhat.toString()
64
- );
58
+ };
59
+ const whats = 'Visible elements have font sizes smaller than 11 pixels';
60
+ return await (0, testaro_1.doTest)(page, report, withItems, 'miniText', 'body, body *:not(script, style)', whats, 2, getBadWhat.toString());
65
61
  };
62
+ exports.reporter = reporter;
@@ -0,0 +1,68 @@
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
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 {GetBadWhat, Report} from '../types';
17
+
18
+ /*
19
+ miniText
20
+ Derived from the bbc-a11y textCannotBeTooSmall test.
21
+ Related to Tenon rule 134.
22
+ This test reports elements with font sizes smaller than 11 pixels.
23
+ Compiled to miniText.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
+ const rawText = element.textContent || '';
32
+ // If the element has text content with any non-whitespace:
33
+ if (/[^\s]/.test(rawText)) {
34
+ const isVisible = element.checkVisibility({
35
+ contentVisibilityAuto: true,
36
+ opacityProperty: true,
37
+ visibilityProperty: true
38
+ });
39
+ // If the element is visible:
40
+ if (isVisible) {
41
+ const styleDec = window.getComputedStyle(element);
42
+ // Get its font size.
43
+ const fontSizeString = styleDec.fontSize;
44
+ const fontSize = Number.parseFloat(fontSizeString);
45
+ // If its font size is smaller than 11 pixels:
46
+ if (fontSize < 11) {
47
+ const parent = element.parentElement;
48
+ // If the element has a parent:
49
+ if (parent) {
50
+ const parentStyleDec = window.getComputedStyle(parent);
51
+ const parentFontSize = Number.parseFloat(parentStyleDec.fontSize);
52
+ // If the parent also has a font size smaller than 11 pixels:
53
+ if (parentFontSize < 11) {
54
+ // Do not report a violation, because the font size may be inherited.
55
+ return null;
56
+ }
57
+ }
58
+ // Return a violation description.
59
+ return `Element is visible but its font size is ${fontSize}px, smaller than 11px`;
60
+ }
61
+ }
62
+ }
63
+ };
64
+ const whats = 'Visible elements have font sizes smaller than 11 pixels';
65
+ return await doTest(
66
+ page, report, withItems, 'miniText', 'body, body *:not(script, style)', whats, 2, getBadWhat.toString()
67
+ );
68
+ };
@@ -0,0 +1,10 @@
1
+ import type { Page } from 'playwright';
2
+ import type { Report, StandardInstance } from '../types';
3
+ export declare const reporter: (page: Page, report: Report) => Promise<{
4
+ data: {
5
+ prevented?: boolean;
6
+ error?: string;
7
+ };
8
+ totals: number[];
9
+ standardInstances: StandardInstance[];
10
+ }>;
package/testaro/motion.js CHANGED
@@ -1,9 +1,16 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2026 Jeff Witt.
3
4
  © 2025–2026 Jonathan Robert Pool.
4
5
  Licensed under the MIT License. See LICENSE for details.
5
6
  */
6
-
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.reporter = void 0;
9
+ const xPath_1 = require("../procs/xPath");
10
+ const shoot_1 = require("../procs/shoot");
11
+ // pixelmatch and pngjs ship no bundled declarations, so their imports stay requires, untyped.
12
+ const pixelmatch = require('pixelmatch').default;
13
+ const { PNG } = require('pngjs');
7
14
  /*
8
15
  motion
9
16
  This test reports motion in a page by making a page image and comparing it with the initial one, i.e. the one made by the catalog proc.
@@ -11,107 +18,96 @@
11
18
  For minimal accessibility, standards require motion to be brief, or else stoppable by the user. But stopping motion can be difficult or impossible, and, by the time a user manages to stop motion, the motion may have caused annoyance or harm. For superior accessibility, a page contains no motion until and unless the user authorizes it. The test reports a rule violation if any pixels differ between the screenshots. The larger the change fraction, the greater the ordinal severity.
12
19
 
13
20
  WARNING: The shoot test uses the procs/screenShot module. See the warning in that module about browser types.
14
- */
15
-
16
- // IMPORTS
17
-
18
- const {getXPathCatalogIndex} = require('../procs/xPath');
19
- const {shoot} = require('../procs/shoot');
20
- const pixelmatch = require('pixelmatch').default;
21
- const {PNG} = require('pngjs');
22
21
 
22
+ Compiled to motion.js by tsc (issue #73); edit this file, not the emitted one.
23
+ */
23
24
  // FUNCTIONS
24
-
25
25
  // Runs the test and returns the result.
26
- exports.reporter = async (page, report) => {
27
- // Initialize the totals and standard instances.
28
- const data = {};
29
- const totals = [0, 0, 0, 0];
30
- const standardInstances = [];
31
- // If the initial image exists:
32
- if (report.images?.length) {
33
- let violationWhat = '';
34
- let ordinalSeverity = 0;
35
- // Make an image with the same color type as the initial one and get its base64 encoding.
36
- const png = await shoot(page, report, {
37
- exclusionSelector: null,
38
- colorType: report.imageColor,
39
- action: 'return'
40
- });
41
- // If this succeeded:
42
- if (png) {
43
- // Parse both base64 encodings into PNG objects.
44
- const initialPNG = PNG.sync.read(Buffer.from(report.images[0], 'base64'));
45
- const finalPNG = PNG.sync.read(Buffer.from(png, 'base64'));
46
- // If their dimensions differ:
47
- if (finalPNG.width !== initialPNG.width || finalPNG.height !== initialPNG.height) {
48
- const fromSize = `${initialPNG.width}×${initialPNG.height}`;
49
- const toSize = `${finalPNG.width}×${finalPNG.height}`;
50
- // Describe the violation.
51
- violationWhat = `Page size changes spontaneously (from ${fromSize} to ${toSize})`;
52
- }
53
- // Otherwise, i.e. if their dimensions are identical:
54
- else {
55
- // Get the count of differing pixels between the images, using the default sensitivity.
56
- try {
57
- const pixelChanges = pixelmatch(
58
- initialPNG.data,
59
- finalPNG.data,
60
- null,
61
- initialPNG.width,
62
- initialPNG.height,
63
- {
64
- threshold: 0.1
26
+ const reporter = async (page, report) => {
27
+ // Initialize the totals and standard instances.
28
+ const data = {};
29
+ const totals = [0, 0, 0, 0];
30
+ const standardInstances = [];
31
+ // Identify the image of the checkpoint being tested (images[0] for checkpoint 0).
32
+ const checkpointIndex = report.activeCheckpoint ?? 0;
33
+ const imageIndex = report.checkpoints?.[checkpointIndex]?.imageIndexes?.[0] ?? 0;
34
+ // If the initial image exists:
35
+ if (report.images?.[imageIndex] !== undefined) {
36
+ let violationWhat = '';
37
+ let ordinalSeverity = 0;
38
+ // Make an image with the same color type as the initial one and get its base64 encoding.
39
+ const png = await (0, shoot_1.shoot)(page, report, {
40
+ exclusionSelector: null,
41
+ colorType: report.imageColor,
42
+ action: 'return'
43
+ });
44
+ // If this succeeded:
45
+ if (png) {
46
+ // Parse both base64 encodings into PNG objects.
47
+ const initialPNG = PNG.sync.read(Buffer.from(report.images[imageIndex], 'base64'));
48
+ const finalPNG = PNG.sync.read(Buffer.from(png, 'base64'));
49
+ // If their dimensions differ:
50
+ if (finalPNG.width !== initialPNG.width || finalPNG.height !== initialPNG.height) {
51
+ const fromSize = `${initialPNG.width}×${initialPNG.height}`;
52
+ const toSize = `${finalPNG.width}×${finalPNG.height}`;
53
+ // Describe the violation.
54
+ violationWhat = `Page size changes spontaneously (from ${fromSize} to ${toSize})`;
55
+ }
56
+ // Otherwise, i.e. if their dimensions are identical:
57
+ else {
58
+ // Get the count of differing pixels between the images, using the default sensitivity.
59
+ try {
60
+ const pixelChanges = pixelmatch(initialPNG.data, finalPNG.data, null, initialPNG.width, initialPNG.height, {
61
+ threshold: 0.1
62
+ });
63
+ // Get the ratio of differing to all pixels as a percentage.
64
+ const changePercent = Math.round(100 * pixelChanges / (initialPNG.width * initialPNG.height));
65
+ // If any pixels were changed:
66
+ if (pixelChanges) {
67
+ // Describe the violation.
68
+ violationWhat = `Content changes spontaneously (${changePercent}% of pixels changed)`;
69
+ // Get the ordinal severity from the fractional pixel change.
70
+ ordinalSeverity = Math.floor(Math.min(3, 0.4 * Math.sqrt(changePercent)));
71
+ }
72
+ }
73
+ catch (err) {
74
+ console.log(`pixelmatch error: ${err.message}, ${err.stack}`);
75
+ data.prevented = true;
76
+ data.error = `Pixel comparison failed: ${err.message}`;
77
+ }
78
+ }
79
+ // If there was a violation:
80
+ if (violationWhat) {
81
+ // Add to the totals.
82
+ totals[ordinalSeverity] = 1;
83
+ // Get a summary standard instance.
84
+ standardInstances.push({
85
+ ruleID: 'motion',
86
+ what: violationWhat,
87
+ ordinalSeverity: ordinalSeverity,
88
+ count: 1,
89
+ catalogIndex: (0, xPath_1.getXPathCatalogIndex)(report, '/html/body')
90
+ });
65
91
  }
66
- );
67
- // Get the ratio of differing to all pixels as a percentage.
68
- const changePercent = Math.round(
69
- 100 * pixelChanges / (initialPNG.width * initialPNG.height)
70
- );
71
- // If any pixels were changed:
72
- if (pixelChanges) {
73
- // Describe the violation.
74
- violationWhat = `Content changes spontaneously (${changePercent}% of pixels changed)`;
75
- // Get the ordinal severity from the fractional pixel change.
76
- ordinalSeverity = Math.floor(Math.min(3, 0.4 * Math.sqrt(changePercent)));
77
- }
78
- } catch (err) {
79
- console.log(`pixelmatch error: ${err.message}, ${err.stack}`);
80
- data.prevented = true;
81
- data.error = `Pixel comparison failed: ${err.message}`;
82
92
  }
83
- }
84
- // If there was a violation:
85
- if (violationWhat) {
86
- // Add to the totals.
87
- totals[ordinalSeverity] = 1;
88
- // Get a summary standard instance.
89
- standardInstances.push({
90
- ruleID: 'motion',
91
- what: violationWhat,
92
- ordinalSeverity,
93
- count: 1,
94
- catalogIndex: getXPathCatalogIndex(report, '/html/body')
95
- });
96
- }
93
+ // Otherwise, i.e. if it failed:
94
+ else {
95
+ // Report this.
96
+ data.prevented = true;
97
+ data.error = 'Image creation failed';
98
+ }
97
99
  }
98
- // Otherwise, i.e. if it failed:
100
+ // Otherwise, i.e. if the initial image does not exist:
99
101
  else {
100
- // Report this.
101
- data.prevented = true;
102
- data.error = 'Image creation failed';
102
+ // Report this.
103
+ data.prevented = true;
104
+ data.error = 'Initial image missing';
103
105
  }
104
- }
105
- // Otherwise, i.e. if the initial image does not exist:
106
- else {
107
- // Report this.
108
- data.prevented = true;
109
- data.error = 'Initial image missing';
110
- }
111
- // Return the result.
112
- return {
113
- data,
114
- totals,
115
- standardInstances
116
- };
106
+ // Return the result.
107
+ return {
108
+ data,
109
+ totals,
110
+ standardInstances
111
+ };
117
112
  };
113
+ exports.reporter = reporter;
@@ -0,0 +1,125 @@
1
+ /*
2
+ © 2026 Jeff Witt.
3
+ © 2025–2026 Jonathan Robert Pool.
4
+ Licensed under the MIT License. See LICENSE for details.
5
+ */
6
+
7
+ // IMPORTS
8
+
9
+ import type {Page} from 'playwright';
10
+ import {getXPathCatalogIndex} from '../procs/xPath';
11
+ import {shoot} from '../procs/shoot';
12
+ import type {Report, StandardInstance} from '../types';
13
+ // pixelmatch and pngjs ship no bundled declarations, so their imports stay requires, untyped.
14
+ const pixelmatch = require('pixelmatch').default;
15
+ const {PNG} = require('pngjs');
16
+
17
+ /*
18
+ motion
19
+ This test reports motion in a page by making a page image and comparing it with the initial one, i.e. the one made by the catalog proc.
20
+
21
+ For minimal accessibility, standards require motion to be brief, or else stoppable by the user. But stopping motion can be difficult or impossible, and, by the time a user manages to stop motion, the motion may have caused annoyance or harm. For superior accessibility, a page contains no motion until and unless the user authorizes it. The test reports a rule violation if any pixels differ between the screenshots. The larger the change fraction, the greater the ordinal severity.
22
+
23
+ WARNING: The shoot test uses the procs/screenShot module. See the warning in that module about browser types.
24
+
25
+ Compiled to motion.js by tsc (issue #73); edit this file, not the emitted one.
26
+ */
27
+
28
+ // FUNCTIONS
29
+
30
+ // Runs the test and returns the result.
31
+ export const reporter = async (page: Page, report: Report) => {
32
+ // Initialize the totals and standard instances.
33
+ const data: {prevented?: boolean; error?: string} = {};
34
+ const totals = [0, 0, 0, 0];
35
+ const standardInstances: StandardInstance[] = [];
36
+ // Identify the image of the checkpoint being tested (images[0] for checkpoint 0).
37
+ const checkpointIndex = report.activeCheckpoint ?? 0;
38
+ const imageIndex = report.checkpoints?.[checkpointIndex]?.imageIndexes?.[0] ?? 0;
39
+ // If the initial image exists:
40
+ if (report.images?.[imageIndex] !== undefined) {
41
+ let violationWhat = '';
42
+ let ordinalSeverity = 0;
43
+ // Make an image with the same color type as the initial one and get its base64 encoding.
44
+ const png = await shoot(page, report, {
45
+ exclusionSelector: null,
46
+ colorType: report.imageColor,
47
+ action: 'return'
48
+ });
49
+ // If this succeeded:
50
+ if (png) {
51
+ // Parse both base64 encodings into PNG objects.
52
+ const initialPNG = PNG.sync.read(Buffer.from(report.images[imageIndex], 'base64'));
53
+ const finalPNG = PNG.sync.read(Buffer.from(png, 'base64'));
54
+ // If their dimensions differ:
55
+ if (finalPNG.width !== initialPNG.width || finalPNG.height !== initialPNG.height) {
56
+ const fromSize = `${initialPNG.width}×${initialPNG.height}`;
57
+ const toSize = `${finalPNG.width}×${finalPNG.height}`;
58
+ // Describe the violation.
59
+ violationWhat = `Page size changes spontaneously (from ${fromSize} to ${toSize})`;
60
+ }
61
+ // Otherwise, i.e. if their dimensions are identical:
62
+ else {
63
+ // Get the count of differing pixels between the images, using the default sensitivity.
64
+ try {
65
+ const pixelChanges = pixelmatch(
66
+ initialPNG.data,
67
+ finalPNG.data,
68
+ null,
69
+ initialPNG.width,
70
+ initialPNG.height,
71
+ {
72
+ threshold: 0.1
73
+ }
74
+ );
75
+ // Get the ratio of differing to all pixels as a percentage.
76
+ const changePercent = Math.round(
77
+ 100 * pixelChanges / (initialPNG.width * initialPNG.height)
78
+ );
79
+ // If any pixels were changed:
80
+ if (pixelChanges) {
81
+ // Describe the violation.
82
+ violationWhat = `Content changes spontaneously (${changePercent}% of pixels changed)`;
83
+ // Get the ordinal severity from the fractional pixel change.
84
+ ordinalSeverity = Math.floor(Math.min(3, 0.4 * Math.sqrt(changePercent)));
85
+ }
86
+ } catch (err) {
87
+ console.log(`pixelmatch error: ${(err as Error).message}, ${(err as Error).stack}`);
88
+ data.prevented = true;
89
+ data.error = `Pixel comparison failed: ${(err as Error).message}`;
90
+ }
91
+ }
92
+ // If there was a violation:
93
+ if (violationWhat) {
94
+ // Add to the totals.
95
+ totals[ordinalSeverity] = 1;
96
+ // Get a summary standard instance.
97
+ standardInstances.push({
98
+ ruleID: 'motion',
99
+ what: violationWhat,
100
+ ordinalSeverity: ordinalSeverity as StandardInstance['ordinalSeverity'],
101
+ count: 1,
102
+ catalogIndex: getXPathCatalogIndex(report, '/html/body')
103
+ });
104
+ }
105
+ }
106
+ // Otherwise, i.e. if it failed:
107
+ else {
108
+ // Report this.
109
+ data.prevented = true;
110
+ data.error = 'Image creation failed';
111
+ }
112
+ }
113
+ // Otherwise, i.e. if the initial image does not exist:
114
+ else {
115
+ // Report this.
116
+ data.prevented = true;
117
+ data.error = 'Initial image missing';
118
+ }
119
+ // Return the result.
120
+ return {
121
+ data,
122
+ totals,
123
+ standardInstances
124
+ };
125
+ };
@@ -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
  © 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -7,56 +8,49 @@
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
  nonTable
13
16
  Derived from the bbc-a11y useTablesForData test. Crude heuristics omitted.
14
17
  This test reports tables used for layout.
18
+ Compiled to nonTable.js by tsc (issue #73); edit this file, not the emitted one.
15
19
  */
16
-
17
- // IMPORTS
18
-
19
- const {doTest} = require('../procs/testaro');
20
-
21
20
  // FUNCTIONS
22
-
23
21
  // Runs the test and returns the result.
24
- exports.reporter = async (page, report, _, withItems) => {
25
- const getBadWhat = element => {
26
- // If the element contains another table:
27
- if (element.querySelector('table')) {
28
- // Return a violation description.
29
- return 'Element contains another table';
30
- }
31
- const rowCount = element.querySelectorAll('tr').length;
32
- const columnCount = Math.max(
33
- ... Array
34
- .from(element.querySelectorAll('tr'))
35
- .map(row => Array.from(row.querySelectorAll('th, td')).length)
36
- );
37
- // Otherwise, if it has only 1 column or 1 row:
38
- if (rowCount === 1 || columnCount === 1) {
39
- // Return a violation description.
40
- return 'Element has only one row or one column';
41
- }
42
- // Otherwise, if it contains an object or player:
43
- if (element.querySelector('object, embed, applet, audio, video')) {
44
- // Return a violation description.
45
- return 'Element contains an object or player';
46
- }
47
- const role = element.getAttribute('role');
48
- // Otherwise, if it has no table-compatible explicit role or descendant element:
49
- if (! (
50
- ['grid', 'treegrid'].includes(role)
51
- || element.caption
52
- || element.querySelector('col, colgroup, tfoot, th, thead')
53
- )) {
54
- // Return a violation description.
55
- return 'Element has no table-compatible explicit role or descendant element';
56
- }
57
- };
58
- const whats = 'table elements are misused for non-table content';
59
- return await doTest(
60
- page, report, withItems, 'nonTable', 'body table', whats, 2, getBadWhat.toString()
61
- );
22
+ const reporter = async (page, report, _, withItems) => {
23
+ // The candidate selector yields table elements.
24
+ const getBadWhat = (element) => {
25
+ // If the element contains another table:
26
+ if (element.querySelector('table')) {
27
+ // Return a violation description.
28
+ return 'Element contains another table';
29
+ }
30
+ const rowCount = element.querySelectorAll('tr').length;
31
+ const columnCount = Math.max(...Array
32
+ .from(element.querySelectorAll('tr'))
33
+ .map(row => Array.from(row.querySelectorAll('th, td')).length));
34
+ // Otherwise, if it has only 1 column or 1 row:
35
+ if (rowCount === 1 || columnCount === 1) {
36
+ // Return a violation description.
37
+ return 'Element has only one row or one column';
38
+ }
39
+ // Otherwise, if it contains an object or player:
40
+ if (element.querySelector('object, embed, applet, audio, video')) {
41
+ // Return a violation description.
42
+ return 'Element contains an object or player';
43
+ }
44
+ const role = element.getAttribute('role');
45
+ // Otherwise, if it has no table-compatible explicit role or descendant element:
46
+ if (!(['grid', 'treegrid'].includes(role)
47
+ || element.caption
48
+ || element.querySelector('col, colgroup, tfoot, th, thead'))) {
49
+ // Return a violation description.
50
+ return 'Element has no table-compatible explicit role or descendant element';
51
+ }
52
+ };
53
+ const whats = 'table elements are misused for non-table content';
54
+ return await (0, testaro_1.doTest)(page, report, withItems, 'nonTable', 'body table', whats, 2, getBadWhat.toString());
62
55
  };
56
+ exports.reporter = reporter;