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
  © 2023 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -8,37 +9,67 @@
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
  distortion
14
17
  Related to Tenon rule 271.
15
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.
16
20
  */
17
-
18
- // IMPORTS
19
-
20
- const {doTest} = require('../procs/testaro');
21
-
22
21
  // FUNCTIONS
23
-
24
22
  // Runs the test and returns the result.
25
- exports.reporter = async (page, report, _, withItems) => {
26
- const getBadWhat = element => {
27
- const styleDec = window.getComputedStyle(element);
28
- const {transform} = styleDec;
29
- const badTransformTypes = ['matrix', 'perspective', 'rotate', 'scale', 'skew'];
30
- // If the element style transforms the text:
31
- if (transform) {
32
- const transformType = badTransformTypes.find(key => transform.includes(key));
33
- // If the transformation is distortive:
34
- if (transformType) {
35
- // Return a violation description.
36
- return `Element distorts its text with ${transformType} transformation`;
37
- }
38
- }
39
- };
40
- const whats = 'Elements distort their texts';
41
- return await doTest(
42
- page, report, withItems, 'distortion', 'body, body *', whats, 0, getBadWhat.toString()
43
- );
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());
44
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,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,35 +9,34 @@
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 xPath_1 = require("../procs/xPath");
12
15
  /*
13
16
  docType
14
17
  Derived from the bbc-a11y allDocumentsMustHaveAW3cRecommendedDoctype test.
15
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.
16
20
  */
17
-
18
- // IMPORTS
19
-
20
- const {getXPathCatalogIndex} = require('../procs/xPath');
21
-
22
21
  // Runs the test and returns the result.
23
- exports.reporter = async (page, report) => {
24
- // Returns whether the page declares a document type.
25
- const docHasType = await page.evaluate(() => {
26
- const docType = document.doctype;
27
- const docHasType = !! docType && docType.name && docType.name.toLowerCase() === 'html';
28
- return docHasType;
29
- });
30
- // Return data, totals, and, if no document type is declared, a standard instance.
31
- return {
32
- data: {docHasType},
33
- totals: [0, 0, 0, docHasType ? 0 : 1],
34
- standardInstances: docHasType ? [] : [{
35
- ruleID: 'docType',
36
- what: 'Document has no standard HTML doctype preamble',
37
- ordinalSeverity: 3,
38
- count: 1,
39
- catalogIndex: getXPathCatalogIndex(report, '/html')
40
- }]
41
- };
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
+ };
42
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,3 +1,4 @@
1
+ "use strict";
1
2
  /*
2
3
  © 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
3
4
  © 2026 Jeff Witt.
@@ -7,116 +8,124 @@
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 getSource_1 = require("../procs/getSource");
14
+ const xPath_1 = require("../procs/xPath");
11
15
  /*
12
- dupAtt.js
16
+ dupAtt
13
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.
14
19
  */
15
-
16
- // ########## IMPORTS
17
-
18
- const {getSource} = require('../procs/getSource');
19
- const {getXPathCatalogIndex} = require('../procs/xPath');
20
-
21
20
  // ########## FUNCTIONS
22
-
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
+ };
23
32
  // Runs the test and returns the result.
24
- exports.reporter = async (page, report, _, withItems) => {
25
- // Initialize the data and standard result.
26
- const data = {total: 0};
27
- if (withItems) {
28
- data.items = [];
29
- }
30
- let totals = [];
31
- const standardInstances = [];
32
- // Get the source.
33
- const sourceData = await getSource(page);
34
- // If it was not obtained:
35
- if (sourceData.prevented) {
36
- // Report this.
37
- data.prevented = true;
38
- data.error = sourceData.error;
39
- }
40
- // Otherwise, i.e. if it was obtained:
41
- else {
42
- let rawPage = sourceData.source;
43
- // Change any spacing character sequences in it to single spaces.
44
- rawPage = rawPage.replace(/\s+/g, ' ');
45
- // Delete any spaces adjacent to equal symbols in it.
46
- rawPage = rawPage.replace(/ = | =|= /g, '=');
47
- // Remove any escaped quotation marks from it.
48
- rawPage = rawPage.replace(/\\"|\\'/g, '');
49
- // Remove any script code from it.
50
- rawPage = rawPage.replace(/<script(?: [^<>]+)?>.*?<\/script>/g, '');
51
- rawPage = rawPage.replace(/<script(?: [^<>]+)?>/g, '');
52
- rawPage = rawPage.replace(/<\/script>/g, '');
53
- // Remove any comments from it.
54
- rawPage = rawPage.replace(/<!--.*?-->/g, '');
55
- // Extract the syntactically valid opening tags of its elements.
56
- let elements = rawPage.match(/<[a-zA-Z]+(?: [^<>]+)?>/g);
57
- // Delete their enclosing angle brackets and any closing slashes.
58
- elements = elements.map(element => element.replace(/< ?| ?\/?>/g, ''));
59
- // Delete the values of any attributes in them.
60
- const nvElements = elements.map(element => element.replace(/="[^"]*"/g, ''));
61
- // For each element:
62
- nvElements.forEach((element, index) => {
63
- // Identify its tag name and attributes.
64
- const terms = element.split(' ');
65
- // If it has 2 or more attributes:
66
- if (terms.length > 2) {
67
- // If any is duplicated:
68
- const tagName = terms[0].toUpperCase();
69
- const attributes = terms.slice(1);
70
- attributes.sort();
71
- const duplicatedAttribute = attributes.find(
72
- (current, attIndex) => attributes[attIndex + 1] === current
73
- );
74
- if (duplicatedAttribute) {
75
- // Add data on the element to the data.
76
- const id = terms.includes('id')
77
- ? elements[index].replace(/^.+id="/, '').replace(/".+/, '')
78
- : '';
79
- data.total++;
80
- if (withItems) {
81
- data.items.push({
82
- tagName,
83
- id,
84
- 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
+ });
85
110
  });
86
- }
87
111
  }
88
- }
89
- });
90
- // If itemization is required and there are any instances:
91
- if (data.items) {
92
- // For each violator:
93
- data.items.forEach(item => {
94
- // Add an instance to the standard instances.
95
- standardInstances.push({
96
- ruleID: 'dupAtt',
97
- what: `${item.tagName} element has 2 attributes named ${item.duplicatedAttribute}`,
98
- ordinalSeverity: 2,
99
- count: 1,
100
- catalogIndex: getXPathCatalogIndex(report, '/html/body')
101
- });
102
- });
103
- }
104
- // Otherwise, if itemization is not required and there are any violations:
105
- else if (data.total) {
106
- // Add a summary instance to the standard instances.
107
- standardInstances.push({
108
- ruleID: 'dupAtt',
109
- what: 'In some elements 2 attributes have the same name',
110
- ordinalSeverity: 2,
111
- count: data.total
112
- });
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];
113
123
  }
114
- totals = [0, 0, data.total, 0];
115
- }
116
- // Return the data.
117
- return {
118
- data,
119
- totals,
120
- standardInstances
121
- };
124
+ // Return the data.
125
+ return {
126
+ data,
127
+ totals,
128
+ standardInstances
129
+ };
122
130
  };
131
+ exports.reporter = reporter;