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
  © 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,37 +9,67 @@
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
  distortion
13
17
  Related to Tenon rule 271.
14
18
  This test reports elements whose transform style properties distort the content. Distortion makes text difficult to read.
19
+ Compiled to distortion.js by tsc (issue #73); edit this file, not the emitted one.
15
20
  */
16
-
17
- // IMPORTS
18
-
19
- const {doTest} = require('../procs/testaro');
20
-
21
21
  // FUNCTIONS
22
-
23
22
  // Runs the test and returns the result.
24
- exports.reporter = async (page, report, _, withItems) => {
25
- const getBadWhat = element => {
26
- const styleDec = window.getComputedStyle(element);
27
- const {transform} = styleDec;
28
- const badTransformTypes = ['matrix', 'perspective', 'rotate', 'scale', 'skew'];
29
- // If the element style transforms the text:
30
- if (transform) {
31
- const transformType = badTransformTypes.find(key => transform.includes(key));
32
- // If the transformation is distortive:
33
- if (transformType) {
34
- // Return a violation description.
35
- return `Element distorts its text with ${transformType} transformation`;
36
- }
37
- }
38
- };
39
- const whats = 'Elements distort their texts';
40
- return await doTest(
41
- page, report, withItems, 'distortion', 'body, body *', whats, 0, getBadWhat.toString()
42
- );
23
+ const reporter = async (page, report, _, withItems) => {
24
+ const getBadWhat = element => {
25
+ const { transform } = window.getComputedStyle(element);
26
+ // If the element has a transform (a computed value is always a matrix or matrix3d function):
27
+ if (transform && transform !== 'none') {
28
+ // Get the numeric arguments of the matrix function (its name is excluded, since it may contain a digit).
29
+ const argumentString = transform.slice(transform.indexOf('(') + 1);
30
+ const values = (argumentString.match(/-?\d*\.?\d+(?:e[+-]?\d+)?/g) || []).map(Number);
31
+ const isNear = (value0, value1) => Math.abs(value0 - value1) < 1e-6;
32
+ // Returns the type of a 2-dimensional transformation, or null if it only translates.
33
+ const get2DType = (a, b, c, d) => {
34
+ if (isNear(a, 1) && isNear(b, 0) && isNear(c, 0) && isNear(d, 1)) {
35
+ return null;
36
+ }
37
+ if (isNear(b, 0) && isNear(c, 0)) {
38
+ return 'scale';
39
+ }
40
+ if (isNear(a, d) && isNear(b, -c)) {
41
+ return 'rotation';
42
+ }
43
+ return 'skew';
44
+ };
45
+ let type = 'nonstandard';
46
+ // If the transform is 2-dimensional:
47
+ if (values.length === 6) {
48
+ type = get2DType(values[0], values[1], values[2], values[3]);
49
+ }
50
+ // Otherwise, if it is 3-dimensional:
51
+ else if (values.length === 16) {
52
+ const [m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, , , m43, m44] = values;
53
+ const is2D = [m13, m14, m23, m24, m31, m32, m34, m43].every(value => isNear(value, 0))
54
+ && isNear(m33, 1)
55
+ && isNear(m44, 1);
56
+ // If it is equivalent to a 2-dimensional transform, classify it as one.
57
+ if (is2D) {
58
+ type = get2DType(m11, m12, m21, m22);
59
+ }
60
+ // Otherwise, classify it as a perspective or other 3-dimensional transformation.
61
+ else {
62
+ type = isNear(m34, 0) ? '3-dimensional' : 'perspective';
63
+ }
64
+ }
65
+ // If the transformation is distortive, i.e. not a pure translation:
66
+ if (type) {
67
+ // Return a violation description.
68
+ return `Element distorts its text with a ${type} transformation`;
69
+ }
70
+ }
71
+ };
72
+ const whats = 'Elements distort their texts';
73
+ return await (0, testaro_1.doTest)(page, report, withItems, 'distortion', 'body, body *', whats, 0, getBadWhat.toString());
43
74
  };
75
+ exports.reporter = reporter;
@@ -0,0 +1,81 @@
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
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
+ distortion
20
+ Related to Tenon rule 271.
21
+ This test reports elements whose transform style properties distort the content. Distortion makes text difficult to read.
22
+ Compiled to distortion.js by tsc (issue #73); edit this file, not the emitted one.
23
+ */
24
+
25
+ // FUNCTIONS
26
+
27
+ // Runs the test and returns the result.
28
+ export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
29
+ const getBadWhat: GetBadWhat = element => {
30
+ const {transform} = window.getComputedStyle(element);
31
+ // If the element has a transform (a computed value is always a matrix or matrix3d function):
32
+ if (transform && transform !== 'none') {
33
+ // Get the numeric arguments of the matrix function (its name is excluded, since it may contain a digit).
34
+ const argumentString = transform.slice(transform.indexOf('(') + 1);
35
+ const values = (argumentString.match(/-?\d*\.?\d+(?:e[+-]?\d+)?/g) || []).map(Number);
36
+ const isNear = (value0: number, value1: number) => Math.abs(value0 - value1) < 1e-6;
37
+ // Returns the type of a 2-dimensional transformation, or null if it only translates.
38
+ const get2DType = (a: number, b: number, c: number, d: number) => {
39
+ if (isNear(a, 1) && isNear(b, 0) && isNear(c, 0) && isNear(d, 1)) {
40
+ return null;
41
+ }
42
+ if (isNear(b, 0) && isNear(c, 0)) {
43
+ return 'scale';
44
+ }
45
+ if (isNear(a, d) && isNear(b, -c)) {
46
+ return 'rotation';
47
+ }
48
+ return 'skew';
49
+ };
50
+ let type: string | null = 'nonstandard';
51
+ // If the transform is 2-dimensional:
52
+ if (values.length === 6) {
53
+ type = get2DType(values[0], values[1], values[2], values[3]);
54
+ }
55
+ // Otherwise, if it is 3-dimensional:
56
+ else if (values.length === 16) {
57
+ const [m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, , , m43, m44] = values;
58
+ const is2D = [m13, m14, m23, m24, m31, m32, m34, m43].every(value => isNear(value, 0))
59
+ && isNear(m33, 1)
60
+ && isNear(m44, 1);
61
+ // If it is equivalent to a 2-dimensional transform, classify it as one.
62
+ if (is2D) {
63
+ type = get2DType(m11, m12, m21, m22);
64
+ }
65
+ // Otherwise, classify it as a perspective or other 3-dimensional transformation.
66
+ else {
67
+ type = isNear(m34, 0) ? '3-dimensional' : 'perspective';
68
+ }
69
+ }
70
+ // If the transformation is distortive, i.e. not a pure translation:
71
+ if (type) {
72
+ // Return a violation description.
73
+ return `Element distorts its text with a ${type} transformation`;
74
+ }
75
+ }
76
+ };
77
+ const whats = 'Elements distort their texts';
78
+ return await doTest(
79
+ page, report, withItems, 'distortion', 'body, body *', whats, 0, getBadWhat.toString()
80
+ );
81
+ };
@@ -0,0 +1,15 @@
1
+ import type { Page } from 'playwright';
2
+ import type { Report } from '../types';
3
+ export declare const reporter: (page: Page, report: Report) => Promise<{
4
+ data: {
5
+ docHasType: boolean | "";
6
+ };
7
+ totals: number[];
8
+ standardInstances: {
9
+ ruleID: string;
10
+ what: string;
11
+ ordinalSeverity: number;
12
+ count: number;
13
+ catalogIndex: string;
14
+ }[];
15
+ }>;
@@ -1,5 +1,7 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
4
+ © 2026 Jeff Witt.
3
5
  © 2026 Jonathan Robert Pool.
4
6
 
5
7
  Licensed under the MIT License. See LICENSE file at the project root or
@@ -7,35 +9,34 @@
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 xPath_1 = require("../procs/xPath");
11
15
  /*
12
16
  docType
13
17
  Derived from the bbc-a11y allDocumentsMustHaveAW3cRecommendedDoctype test.
14
18
  This test reports a failure to equip the page document with a W3C-recommended HTML doctype.
19
+ Compiled to docType.js by tsc (issue #73); edit this file, not the emitted one.
15
20
  */
16
-
17
- // IMPORTS
18
-
19
- const {getXPathCatalogIndex} = require('../procs/xPath');
20
-
21
21
  // Runs the test and returns the result.
22
- exports.reporter = async (page, report) => {
23
- // Returns whether the page declares a document type.
24
- const docHasType = await page.evaluate(() => {
25
- const docType = document.doctype;
26
- const docHasType = !! docType && docType.name && docType.name.toLowerCase() === 'html';
27
- return docHasType;
28
- });
29
- // Return data, totals, and, if no document type is declared, a standard instance.
30
- return {
31
- data: {docHasType},
32
- totals: [0, 0, 0, docHasType ? 0 : 1],
33
- standardInstances: docHasType ? [] : [{
34
- ruleID: 'docType',
35
- what: 'Document has no standard HTML doctype preamble',
36
- ordinalSeverity: 3,
37
- count: 1,
38
- catalogIndex: getXPathCatalogIndex(report, '/html')
39
- }]
40
- };
22
+ const reporter = async (page, report) => {
23
+ // Returns whether the page declares a document type.
24
+ const docHasType = await page.evaluate(() => {
25
+ const docType = document.doctype;
26
+ const docHasType = !!docType && docType.name && docType.name.toLowerCase() === 'html';
27
+ return docHasType;
28
+ });
29
+ // Return data, totals, and, if no document type is declared, a standard instance.
30
+ return {
31
+ data: { docHasType },
32
+ totals: [0, 0, 0, docHasType ? 0 : 1],
33
+ standardInstances: docHasType ? [] : [{
34
+ ruleID: 'docType',
35
+ what: 'Document has no standard HTML doctype preamble',
36
+ ordinalSeverity: 3,
37
+ count: 1,
38
+ catalogIndex: (0, xPath_1.getXPathCatalogIndex)(report, '/html')
39
+ }]
40
+ };
41
41
  };
42
+ exports.reporter = reporter;
@@ -0,0 +1,45 @@
1
+ /*
2
+ © 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jeff Witt.
4
+ © 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 {getXPathCatalogIndex} from '../procs/xPath';
16
+ import type {Report} from '../types';
17
+
18
+ /*
19
+ docType
20
+ Derived from the bbc-a11y allDocumentsMustHaveAW3cRecommendedDoctype test.
21
+ This test reports a failure to equip the page document with a W3C-recommended HTML doctype.
22
+ Compiled to docType.js by tsc (issue #73); edit this file, not the emitted one.
23
+ */
24
+
25
+ // Runs the test and returns the result.
26
+ export const reporter = async (page: Page, report: Report) => {
27
+ // Returns whether the page declares a document type.
28
+ const docHasType = await page.evaluate(() => {
29
+ const docType = document.doctype;
30
+ const docHasType = !! docType && docType.name && docType.name.toLowerCase() === 'html';
31
+ return docHasType;
32
+ });
33
+ // Return data, totals, and, if no document type is declared, a standard instance.
34
+ return {
35
+ data: {docHasType},
36
+ totals: [0, 0, 0, docHasType ? 0 : 1],
37
+ standardInstances: docHasType ? [] : [{
38
+ ruleID: 'docType',
39
+ what: 'Document has no standard HTML doctype preamble',
40
+ ordinalSeverity: 3,
41
+ count: 1,
42
+ catalogIndex: getXPathCatalogIndex(report, '/html')
43
+ }]
44
+ };
45
+ };
@@ -0,0 +1,16 @@
1
+ import type { Page } from 'playwright';
2
+ import type { Report, StandardInstance } from '../types';
3
+ export declare const reporter: (page: Page, report: Report, _: unknown, withItems: boolean) => Promise<{
4
+ data: {
5
+ total: number;
6
+ items?: {
7
+ tagName: string;
8
+ id: string;
9
+ duplicatedAttribute: string;
10
+ }[];
11
+ prevented?: boolean;
12
+ error?: string;
13
+ };
14
+ totals: number[];
15
+ standardInstances: StandardInstance[];
16
+ }>;
package/testaro/dupAtt.js CHANGED
@@ -1,121 +1,131 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
4
+ © 2026 Jeff Witt.
3
5
  © 2026 Jonathan Robert Pool.
4
6
 
5
7
  Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
6
8
 
7
9
  SPDX-License-Identifier: MIT
8
10
  */
9
-
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.reporter = void 0;
13
+ const getSource_1 = require("../procs/getSource");
14
+ const xPath_1 = require("../procs/xPath");
10
15
  /*
11
- dupAtt.js
16
+ dupAtt
12
17
  This test reports duplicate attributes. The test is performed on the source of the document, because browsers delete additional same-name attributes in the DOM.
18
+ Compiled to dupAtt.js by tsc (issue #73); edit this file, not the emitted one.
13
19
  */
14
-
15
- // ########## IMPORTS
16
-
17
- const {getSource} = require('../procs/getSource');
18
- const {getXPathCatalogIndex} = require('../procs/xPath');
19
-
20
20
  // ########## FUNCTIONS
21
-
21
+ // Returns the catalog index of the element with an ID and tag name, or of the body if none does. Duplicate attributes are found in the source, not the DOM, so the ID is the only handle on the element.
22
+ const getIDCatalogIndex = (report, tagName, id) => {
23
+ const { catalog } = report;
24
+ if (id && catalog) {
25
+ const index = Object.keys(catalog).find(key => catalog[key].id === id && catalog[key].tagName === tagName);
26
+ if (index !== undefined) {
27
+ return index;
28
+ }
29
+ }
30
+ return (0, xPath_1.getXPathCatalogIndex)(report, '/html/body');
31
+ };
22
32
  // Runs the test and returns the result.
23
- exports.reporter = async (page, report, _, withItems) => {
24
- // Initialize the data and standard result.
25
- const data = {total: 0};
26
- if (withItems) {
27
- data.items = [];
28
- }
29
- let totals = [];
30
- const standardInstances = [];
31
- // Get the source.
32
- const sourceData = await getSource(page);
33
- // If it was not obtained:
34
- if (sourceData.prevented) {
35
- // Report this.
36
- data.prevented = true;
37
- data.error = sourceData.error;
38
- }
39
- // Otherwise, i.e. if it was obtained:
40
- else {
41
- let rawPage = sourceData.source;
42
- // Change any spacing character sequences in it to single spaces.
43
- rawPage = rawPage.replace(/\s+/g, ' ');
44
- // Delete any spaces adjacent to equal symbols in it.
45
- rawPage = rawPage.replace(/ = | =|= /g, '=');
46
- // Remove any escaped quotation marks from it.
47
- rawPage = rawPage.replace(/\\"|\\'/g, '');
48
- // Remove any script code from it.
49
- rawPage = rawPage.replace(/<script(?: [^<>]+)?>.*?<\/script>/g, '');
50
- rawPage = rawPage.replace(/<script(?: [^<>]+)?>/g, '');
51
- rawPage = rawPage.replace(/<\/script>/g, '');
52
- // Remove any comments from it.
53
- rawPage = rawPage.replace(/<!--.*?-->/g, '');
54
- // Extract the syntactically valid opening tags of its elements.
55
- let elements = rawPage.match(/<[a-zA-Z]+(?: [^<>]+)?>/g);
56
- // Delete their enclosing angle brackets and any closing slashes.
57
- elements = elements.map(element => element.replace(/< ?| ?\/?>/g, ''));
58
- // Delete the values of any attributes in them.
59
- const nvElements = elements.map(element => element.replace(/="[^"]*"/g, ''));
60
- // For each element:
61
- nvElements.forEach((element, index) => {
62
- // Identify its tag name and attributes.
63
- const terms = element.split(' ');
64
- // If it has 2 or more attributes:
65
- if (terms.length > 2) {
66
- // If any is duplicated:
67
- const tagName = terms[0].toUpperCase();
68
- const attributes = terms.slice(1);
69
- attributes.sort();
70
- const duplicatedAttribute = attributes.find(
71
- (current, attIndex) => attributes[attIndex + 1] === current
72
- );
73
- if (duplicatedAttribute) {
74
- // Add data on the element to the data.
75
- const id = terms.includes('id')
76
- ? elements[index].replace(/^.+id="/, '').replace(/".+/, '')
77
- : '';
78
- data.total++;
79
- if (withItems) {
80
- data.items.push({
81
- tagName,
82
- id,
83
- duplicatedAttribute
33
+ const reporter = async (page, report, _, withItems) => {
34
+ // Initialize the data and standard result.
35
+ const data = { total: 0 };
36
+ if (withItems) {
37
+ data.items = [];
38
+ }
39
+ let totals = [];
40
+ const standardInstances = [];
41
+ // Get the source.
42
+ const sourceData = await (0, getSource_1.getSource)(page);
43
+ // If it was not obtained:
44
+ if (sourceData.prevented) {
45
+ // Report this.
46
+ data.prevented = true;
47
+ data.error = sourceData.error;
48
+ }
49
+ // Otherwise, i.e. if it was obtained:
50
+ else {
51
+ let rawPage = sourceData.source;
52
+ // Change any spacing character sequences in it to single spaces.
53
+ rawPage = rawPage.replace(/\s+/g, ' ');
54
+ // Delete any spaces adjacent to equal symbols in it.
55
+ rawPage = rawPage.replace(/ = | =|= /g, '=');
56
+ // Remove any escaped quotation marks from it.
57
+ rawPage = rawPage.replace(/\\"|\\'/g, '');
58
+ // Remove any script code from it.
59
+ rawPage = rawPage.replace(/<script(?: [^<>]+)?>.*?<\/script>/g, '');
60
+ rawPage = rawPage.replace(/<script(?: [^<>]+)?>/g, '');
61
+ rawPage = rawPage.replace(/<\/script>/g, '');
62
+ // Remove any comments from it.
63
+ rawPage = rawPage.replace(/<!--.*?-->/g, '');
64
+ // Extract the syntactically valid opening tags of its elements.
65
+ // A pageless source would make the match null; the original dereferenced it unguarded.
66
+ let elements = rawPage.match(/<[a-zA-Z]+(?: [^<>]+)?>/g);
67
+ // Delete their enclosing angle brackets and any closing slashes.
68
+ elements = elements.map(element => element.replace(/< ?| ?\/?>/g, ''));
69
+ // Delete the values of any attributes in them.
70
+ const nvElements = elements.map(element => element.replace(/="[^"]*"/g, ''));
71
+ // For each element:
72
+ nvElements.forEach((element, index) => {
73
+ // Identify its tag name and attributes.
74
+ const terms = element.split(' ');
75
+ // If it has 2 or more attributes:
76
+ if (terms.length > 2) {
77
+ // If any is duplicated:
78
+ const tagName = terms[0].toUpperCase();
79
+ const attributes = terms.slice(1);
80
+ attributes.sort();
81
+ const duplicatedAttribute = attributes.find((current, attIndex) => attributes[attIndex + 1] === current);
82
+ if (duplicatedAttribute) {
83
+ // Add data on the element to the data.
84
+ const id = terms.includes('id')
85
+ ? elements[index].replace(/^.+id="/, '').replace(/".+/, '')
86
+ : '';
87
+ data.total++;
88
+ if (withItems) {
89
+ data.items.push({
90
+ tagName,
91
+ id,
92
+ duplicatedAttribute
93
+ });
94
+ }
95
+ }
96
+ }
97
+ });
98
+ // If itemization is required and there are any instances:
99
+ if (data.items) {
100
+ // For each violator:
101
+ data.items.forEach(item => {
102
+ // Add an instance to the standard instances.
103
+ standardInstances.push({
104
+ ruleID: 'dupAtt',
105
+ what: `${item.tagName} element has 2 attributes named ${item.duplicatedAttribute}`,
106
+ ordinalSeverity: 2,
107
+ count: 1,
108
+ catalogIndex: getIDCatalogIndex(report, item.tagName, item.id)
109
+ });
84
110
  });
85
- }
86
111
  }
87
- }
88
- });
89
- // If itemization is required and there are any instances:
90
- if (data.items) {
91
- // For each violator:
92
- data.items.forEach(item => {
93
- // Add an instance to the standard instances.
94
- standardInstances.push({
95
- ruleID: 'dupAtt',
96
- what: `${item.tagName} element has 2 attributes named ${item.duplicatedAttribute}`,
97
- ordinalSeverity: 2,
98
- count: 1,
99
- catalogIndex: getXPathCatalogIndex(report, '/html/body')
100
- });
101
- });
102
- }
103
- // Otherwise, if itemization is not required and there are any violations:
104
- else if (data.total) {
105
- // Add a summary instance to the standard instances.
106
- standardInstances.push({
107
- ruleID: 'dupAtt',
108
- what: 'In some elements 2 attributes have the same name',
109
- ordinalSeverity: 2,
110
- count: data.total
111
- });
112
+ // Otherwise, if itemization is not required and there are any violations:
113
+ else if (data.total) {
114
+ // Add a summary instance to the standard instances.
115
+ standardInstances.push({
116
+ ruleID: 'dupAtt',
117
+ what: 'In some elements 2 attributes have the same name',
118
+ ordinalSeverity: 2,
119
+ count: data.total
120
+ });
121
+ }
122
+ totals = [0, 0, data.total, 0];
112
123
  }
113
- totals = [0, 0, data.total, 0];
114
- }
115
- // Return the data.
116
- return {
117
- data,
118
- totals,
119
- standardInstances
120
- };
124
+ // Return the data.
125
+ return {
126
+ data,
127
+ totals,
128
+ standardInstances
129
+ };
121
130
  };
131
+ exports.reporter = reporter;