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.
- package/.gitattributes +94 -0
- package/.github/workflows/ci.yml +58 -0
- package/.github/workflows/publish.yml +49 -0
- package/.github/workflows/typescript.yml +35 -0
- package/AGENTS.md +2 -2
- package/CLAUDE.md +24 -18
- package/CONTAINERS.md +9 -6
- package/CONTRIBUTING.md +29 -3
- package/Dockerfile +1 -1
- package/README.md +73 -19
- package/UPGRADES.md +4 -0
- package/actSpecs-doc.md +13 -1
- package/actSpecs.js +29 -5
- package/call.js +6 -5
- package/docker-compose.yml +8 -2
- package/docs/checkpoint-scanning.md +199 -0
- package/docs/standard-result-outcome.md +165 -0
- package/env.example +44 -10
- package/eslint.config.mjs +101 -0
- package/netWatch.js +372 -222
- package/package.json +46 -31
- package/pour/README.md +47 -0
- package/pour/pour.min.js +8 -0
- package/procs/actDo.js +808 -0
- package/procs/catalog.d.ts +17 -0
- package/procs/catalog.js +312 -221
- package/procs/catalog.ts +398 -0
- package/procs/checkpoint.js +110 -0
- package/procs/config.d.ts +35 -0
- package/procs/config.js +62 -0
- package/procs/dateTime.js +2 -1
- package/procs/doActs.js +442 -849
- package/procs/doTestAct.js +27 -121
- package/procs/flow.js +221 -0
- package/procs/generateRuleRegistry.js +94 -0
- package/procs/getSource.d.ts +26 -0
- package/procs/job.js +115 -1
- package/procs/launch.d.ts +40 -0
- package/procs/launch.js +239 -108
- package/procs/nu.d.ts +48 -0
- package/procs/scope.js +196 -0
- package/procs/shoot.d.ts +33 -0
- package/procs/standard.d.ts +18 -0
- package/procs/standard.js +83 -0
- package/procs/standard.ts +126 -0
- package/procs/testAct.js +145 -0
- package/procs/testaro.d.ts +20 -0
- package/procs/testaro.js +236 -189
- package/procs/testaro.ts +321 -0
- package/procs/userPath.js +126 -0
- package/procs/xPath.d.ts +4 -0
- package/procs/xPath.js +81 -69
- package/procs/xPath.ts +106 -0
- package/procs/xPathScript.d.ts +6 -0
- package/procs/xPathScript.js +70 -0
- package/run.js +3 -2
- package/surea11y/README.md +44 -0
- package/surea11y/surea11y.browser.js +14 -0
- package/testaro/adbID.d.ts +3 -0
- package/testaro/adbID.js +38 -41
- package/testaro/adbID.ts +66 -0
- package/testaro/allCapStyle.d.ts +3 -0
- package/testaro/allCapStyle.js +31 -34
- package/testaro/allCapStyle.ts +55 -0
- package/testaro/allCaps.d.ts +16 -0
- package/testaro/allCaps.js +179 -151
- package/testaro/allCaps.ts +228 -0
- package/testaro/allHidden.d.ts +13 -0
- package/testaro/allHidden.js +29 -30
- package/testaro/allHidden.ts +50 -0
- package/testaro/allSlanted.d.ts +3 -0
- package/testaro/allSlanted.js +30 -33
- package/testaro/allSlanted.ts +54 -0
- package/testaro/altScheme.d.ts +3 -0
- package/testaro/altScheme.js +27 -30
- package/testaro/altScheme.ts +50 -0
- package/testaro/attVal.d.ts +3 -0
- package/testaro/attVal.js +20 -21
- package/testaro/attVal.ts +52 -0
- package/testaro/autocomplete.d.ts +3 -0
- package/testaro/autocomplete.js +59 -75
- package/testaro/autocomplete.ts +101 -0
- package/testaro/bulk.d.ts +13 -0
- package/testaro/bulk.js +32 -33
- package/testaro/bulk.ts +55 -0
- package/testaro/buttonMenu.d.ts +9 -0
- package/testaro/buttonMenu.js +317 -319
- package/testaro/buttonMenu.ts +391 -0
- package/testaro/captionLoc.d.ts +3 -0
- package/testaro/captionLoc.js +17 -20
- package/testaro/captionLoc.ts +40 -0
- package/testaro/datalistRef.d.ts +3 -0
- package/testaro/datalistRef.js +33 -36
- package/testaro/datalistRef.ts +55 -0
- package/testaro/distortion.d.ts +3 -0
- package/testaro/distortion.js +57 -26
- package/testaro/distortion.ts +81 -0
- package/testaro/docType.d.ts +15 -0
- package/testaro/docType.js +25 -25
- package/testaro/docType.ts +45 -0
- package/testaro/dupAtt.d.ts +16 -0
- package/testaro/dupAtt.js +113 -104
- package/testaro/dupAtt.ts +144 -0
- package/testaro/elements.d.ts +6 -0
- package/testaro/elements.js +153 -153
- package/testaro/elements.ts +215 -0
- package/testaro/embAc.d.ts +3 -0
- package/testaro/embAc.js +19 -20
- package/testaro/embAc.ts +40 -0
- package/testaro/focAll.d.ts +13 -0
- package/testaro/focAll.js +185 -191
- package/testaro/focAll.ts +217 -0
- package/testaro/focAndOp.d.ts +3 -0
- package/testaro/focAndOp.js +100 -104
- package/testaro/focAndOp.ts +128 -0
- package/testaro/focInd.d.ts +3 -0
- package/testaro/focInd.js +64 -66
- package/testaro/focInd.ts +96 -0
- package/testaro/focVis.d.ts +3 -0
- package/testaro/focVis.js +28 -30
- package/testaro/focVis.ts +52 -0
- package/testaro/headEl.d.ts +10 -0
- package/testaro/headEl.js +61 -62
- package/testaro/headEl.ts +82 -0
- package/testaro/headingAmb.d.ts +3 -0
- package/testaro/headingAmb.js +54 -63
- package/testaro/headingAmb.ts +72 -0
- package/testaro/hovInd.d.ts +12 -0
- package/testaro/hovInd.js +163 -130
- package/testaro/hovInd.ts +199 -0
- package/testaro/hover.d.ts +3 -0
- package/testaro/hover.js +155 -126
- package/testaro/hover.ts +154 -0
- package/testaro/hr.d.ts +3 -0
- package/testaro/hr.js +14 -17
- package/testaro/hr.ts +36 -0
- package/testaro/imageLink.d.ts +3 -0
- package/testaro/imageLink.js +17 -20
- package/testaro/imageLink.ts +42 -0
- package/testaro/labClash.d.ts +3 -0
- package/testaro/labClash.js +31 -33
- package/testaro/labClash.ts +54 -0
- package/testaro/legendLoc.d.ts +3 -0
- package/testaro/legendLoc.js +17 -20
- package/testaro/legendLoc.ts +42 -0
- package/testaro/lineHeight.d.ts +3 -0
- package/testaro/lineHeight.js +43 -48
- package/testaro/lineHeight.ts +70 -0
- package/testaro/linkAmb.d.ts +7 -0
- package/testaro/linkAmb.js +79 -80
- package/testaro/linkAmb.ts +105 -0
- package/testaro/linkExt.d.ts +3 -0
- package/testaro/linkExt.js +13 -16
- package/testaro/linkExt.ts +35 -0
- package/testaro/linkOldAtt.d.ts +3 -0
- package/testaro/linkOldAtt.js +25 -28
- package/testaro/linkOldAtt.ts +48 -0
- package/testaro/linkTo.d.ts +3 -0
- package/testaro/linkTo.js +22 -22
- package/testaro/linkTo.ts +43 -0
- package/testaro/linkUl.d.ts +3 -0
- package/testaro/linkUl.js +30 -34
- package/testaro/linkUl.ts +54 -0
- package/testaro/miniText.d.ts +3 -0
- package/testaro/miniText.js +41 -44
- package/testaro/miniText.ts +68 -0
- package/testaro/motion.d.ts +10 -0
- package/testaro/motion.js +92 -96
- package/testaro/motion.ts +125 -0
- package/testaro/nonTable.d.ts +3 -0
- package/testaro/nonTable.js +39 -45
- package/testaro/nonTable.ts +66 -0
- package/testaro/optRoleSel.d.ts +3 -0
- package/testaro/optRoleSel.js +16 -19
- package/testaro/optRoleSel.ts +41 -0
- package/testaro/phOnly.d.ts +3 -0
- package/testaro/phOnly.js +18 -21
- package/testaro/phOnly.ts +43 -0
- package/testaro/pseudoP.d.ts +3 -0
- package/testaro/pseudoP.js +36 -38
- package/testaro/pseudoP.ts +59 -0
- package/testaro/radioSet.d.ts +3 -0
- package/testaro/radioSet.js +57 -59
- package/testaro/radioSet.ts +79 -0
- package/testaro/registry.d.ts +63 -0
- package/testaro/registry.js +67 -0
- package/testaro/registry.ts +141 -0
- package/testaro/role.d.ts +3 -0
- package/testaro/role.js +27 -29
- package/testaro/role.ts +53 -0
- package/testaro/secHeading.d.ts +3 -0
- package/testaro/secHeading.js +30 -33
- package/testaro/secHeading.ts +53 -0
- package/testaro/styleDiff.d.ts +25 -0
- package/testaro/styleDiff.js +248 -252
- package/testaro/styleDiff.ts +303 -0
- package/testaro/tabNav.d.ts +33 -0
- package/testaro/tabNav.js +272 -342
- package/testaro/tabNav.ts +454 -0
- package/testaro/targetsNear.d.ts +9 -0
- package/testaro/targetsNear.js +130 -132
- package/testaro/targetsNear.ts +160 -0
- package/testaro/textNodes.d.ts +6 -0
- package/testaro/textNodes.js +139 -135
- package/testaro/textNodes.ts +185 -0
- package/testaro/textSem.d.ts +3 -0
- package/testaro/textSem.js +25 -28
- package/testaro/textSem.ts +47 -0
- package/testaro/title.d.ts +9 -0
- package/testaro/title.js +16 -13
- package/testaro/title.ts +31 -0
- package/testaro/titledEl.d.ts +3 -0
- package/testaro/titledEl.js +15 -18
- package/testaro/titledEl.ts +38 -0
- package/testaro/zIndex.d.ts +3 -0
- package/testaro/zIndex.js +19 -22
- package/testaro/zIndex.ts +42 -0
- package/tests/alfa.d.ts +45 -0
- package/tests/alfa.js +138 -141
- package/tests/alfa.ts +214 -0
- package/tests/aslint.d.ts +33 -0
- package/tests/aslint.js +272 -249
- package/tests/aslint.ts +301 -0
- package/tests/axe.d.ts +27 -0
- package/tests/axe.js +199 -200
- package/tests/axe.ts +277 -0
- package/tests/ed11y.d.ts +28 -0
- package/tests/ed11y.js +141 -99
- package/tests/ed11y.ts +178 -0
- package/tests/htmlcs.d.ts +21 -0
- package/tests/htmlcs.js +174 -140
- package/tests/htmlcs.ts +181 -0
- package/tests/ibm.d.ts +52 -0
- package/tests/ibm.js +164 -166
- package/tests/ibm.ts +251 -0
- package/tests/nuVal.d.ts +13 -0
- package/tests/nuVal.js +107 -112
- package/tests/nuVal.ts +145 -0
- package/tests/nuVnu.d.ts +15 -0
- package/tests/nuVnu.js +141 -111
- package/tests/nuVnu.ts +144 -0
- package/tests/pour.d.ts +31 -0
- package/tests/pour.js +242 -0
- package/tests/pour.ts +273 -0
- package/tests/qualWeb.d.ts +39 -0
- package/tests/qualWeb.js +302 -272
- package/tests/qualWeb.ts +415 -0
- package/tests/surea11y.d.ts +33 -0
- package/tests/surea11y.js +288 -0
- package/tests/surea11y.ts +334 -0
- package/tests/testaro.d.ts +25 -0
- package/tests/testaro.js +745 -652
- package/tests/testaro.ts +862 -0
- package/tests/wave.d.ts +46 -0
- package/tests/wave.js +166 -177
- package/tests/wave.ts +252 -0
- package/tsconfig.json +17 -0
- package/types.d.ts +243 -0
- package/types.js +10 -0
- package/types.ts +376 -0
- package/validation/act/README.md +46 -0
- package/validation/act/capture.js +424 -0
- package/validation/act/chromium-issue-draft.md +66 -0
- package/validation/act/fp-triage-2026-08-22.md +86 -0
- package/validation/act/isolation-notes.md +159 -0
- package/validation/act/playwright-issue-draft.md +73 -0
- package/validation/act/propose-mappings.js +0 -0
- package/validation/act/repro-cdp-raw.js +105 -0
- package/validation/act/repro-metarefresh.js +65 -0
- package/validation/act/score.js +184 -0
- package/validation/act/stage3a-stress-report.md +69 -0
- package/validation/act/stage3b-mapping-proposals.md +94 -0
- package/validation/act/stage3b-triage-draft.md +138 -0
- package/validation/act/surea11y-track-a-2026-09-01.md +50 -0
- package/validation/executors/netWatch.js +180 -90
- package/validation/executors/test.js +17 -2
- package/validation/executors/tests.js +118 -10
- package/validation/jobs/reports/raw/260901T1000-surea11y-validation.json +964 -0
- package/validation/jobs/todo/240101T1200-simple-example.json +14 -6
- package/validation/jobs/todo/240101T1300-shoot-example.json +2 -1
- package/validation/jobs/todo/260821T1900-pour-validation.json +45 -0
- package/validation/jobs/todo/260901T1000-surea11y-validation.json +45 -0
- package/validation/knownFailures.json +1 -0
- package/validation/tests/jobProperties/adbID.json +27 -2
- package/validation/tests/jobProperties/{focOp.json → allCapStyle.json} +56 -53
- package/validation/tests/jobProperties/allCaps.json +25 -0
- package/validation/tests/jobProperties/allHidden.json +133 -13
- package/validation/tests/jobProperties/allSlanted.json +3 -3
- package/validation/tests/jobProperties/altScheme.json +23 -8
- package/validation/tests/jobProperties/attVal.json +57 -57
- package/validation/tests/jobProperties/autocomplete.json +11 -1
- package/validation/tests/jobProperties/bulk.json +6 -1
- package/validation/tests/jobProperties/buttonMenu.json +63 -42
- package/validation/tests/jobProperties/captionLoc.json +1 -6
- package/validation/tests/jobProperties/checkpoint-browser.json +401 -0
- package/validation/tests/jobProperties/checkpoint-page.json +397 -0
- package/validation/tests/jobProperties/checkpoint.json +395 -0
- package/validation/tests/jobProperties/datalistRef.json +20 -5
- package/validation/tests/jobProperties/distortion.json +28 -3
- package/validation/tests/jobProperties/docType.json +2 -2
- package/validation/tests/jobProperties/dupAtt.json +48 -33
- package/validation/tests/jobProperties/elements.json +28 -28
- package/validation/tests/jobProperties/embAc.json +36 -31
- package/validation/tests/jobProperties/focAndOp.json +284 -0
- package/validation/tests/jobProperties/focInd.json +34 -34
- package/validation/tests/jobProperties/focVis.json +1 -1
- package/validation/tests/jobProperties/hover.json +39 -37
- package/validation/tests/jobProperties/hr.json +13 -3
- package/validation/tests/jobProperties/imageLink.json +0 -5
- package/validation/tests/jobProperties/labClash.json +68 -33
- package/validation/tests/jobProperties/legendLoc.json +1 -6
- package/validation/tests/jobProperties/lineHeight.json +2 -2
- package/validation/tests/jobProperties/linkAmb.json +13 -18
- package/validation/tests/jobProperties/linkExt.json +1 -1
- package/validation/tests/jobProperties/linkOldAtt.json +12 -2
- package/validation/tests/jobProperties/linkTo.json +1 -1
- package/validation/tests/jobProperties/linkUl.json +65 -65
- package/validation/tests/jobProperties/miniText.json +7 -2
- package/validation/tests/jobProperties/motion.json +4 -50
- package/validation/tests/jobProperties/nonTable.json +48 -3
- package/validation/tests/jobProperties/optRoleSel.json +12 -2
- package/validation/tests/jobProperties/phOnly.json +6 -16
- package/validation/tests/jobProperties/pseudoP.json +17 -2
- package/validation/tests/jobProperties/radioSet.json +30 -30
- package/validation/tests/jobProperties/role.json +25 -5
- package/validation/tests/jobProperties/secHeading.json +26 -21
- package/validation/tests/jobProperties/styleDiff.json +35 -5
- package/validation/tests/jobProperties/tabNav.json +3 -1
- package/validation/tests/jobProperties/targetsNear.json +237 -0
- package/validation/tests/jobProperties/textNodes.json +29 -29
- package/validation/tests/jobProperties/textSem.json +26 -1
- package/validation/tests/jobProperties/title.json +12 -2
- package/validation/tests/jobProperties/titledEl.json +39 -9
- package/validation/tests/jobProperties/userPath.json +317 -0
- package/validation/tests/jobProperties/zIndex.json +40 -35
- package/validation/tests/targets/allCapStyle/index.html +26 -0
- package/validation/tests/targets/checkpoint/index.html +38 -0
- package/validation/tests/targets/datalistRef/index.html +1 -1
- package/validation/tests/targets/focAndOp/bad.html +29 -0
- package/validation/tests/targets/{focOp → focAndOp}/good.html +3 -1
- package/validation/tests/targets/focInd/bad.html +2 -1
- package/validation/tests/targets/headEl/index.html +10 -1
- package/validation/tests/targets/{targetSmall → targetsNear}/index.html +15 -1
- package/validation/tests/targets/userPath/index.html +42 -0
- package/validation/validateTest.js +61 -9
- package/.claude/settings.local.json +0 -11
- package/.eslintrc.json +0 -41
- package/htmlcs/.eslintrc.json +0 -67
- package/validation/tests/jobProperties/linkTitle.json +0 -127
- package/validation/tests/jobProperties/opFoc.json +0 -164
- package/validation/tests/jobProperties/targetSmall.json +0 -152
- package/validation/tests/jobProperties/targetTiny.json +0 -142
- package/validation/tests/targets/focOp/bad.html +0 -25
- package/validation/tests/targets/linkTitle/index.html +0 -24
- package/validation/tests/targets/opFoc/bad.html +0 -26
- package/validation/tests/targets/opFoc/good.html +0 -23
package/testaro/hover.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
4
|
© 2026 Jeff Witt.
|
|
@@ -7,140 +8,168 @@
|
|
|
7
8
|
|
|
8
9
|
SPDX-License-Identifier: MIT
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.reporter = void 0;
|
|
46
|
+
const testaro_1 = require("../procs/testaro");
|
|
47
|
+
const playwright = __importStar(require("playwright"));
|
|
48
|
+
// Shared configuration for timeout multiplier.
|
|
49
|
+
const config_1 = require("../procs/config");
|
|
11
50
|
/*
|
|
12
51
|
hover
|
|
13
52
|
This test reports unexpected impacts of hovering. The elements that are subjected to hovering (called “triggers”) include all the elements that have attributes associated with control over the visibility of other elements. If hovering over an element results in an increase or decrease in the total count of visible elements in the tree rooted in the grandparent of the trigger, the rule is considered violated. This test uses the getBasicResult function in order to use Playwright for the most realistic hover simulation.
|
|
53
|
+
Compiled to hover.js by tsc (issue #73); edit this file, not the emitted one.
|
|
14
54
|
*/
|
|
15
|
-
|
|
16
|
-
// IMPORTS
|
|
17
|
-
|
|
18
|
-
const {getBasicResult} = require('../procs/testaro');
|
|
19
|
-
const playwright = require('playwright');
|
|
20
|
-
// Shared configuration for timeout multiplier.
|
|
21
|
-
const {applyMultiplier} = require('../procs/config');
|
|
22
|
-
|
|
23
55
|
// FUNCTIONS
|
|
24
|
-
|
|
25
56
|
// Returns an awaited change in a visible element count.
|
|
26
|
-
const getVisibleCountChange = async (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
elapsedTime
|
|
62
|
-
};
|
|
57
|
+
const getVisibleCountChange = async (rootLoc, elementCount0, timeLimit = 400, settleInterval = 75) => {
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
let timeout;
|
|
60
|
+
let settleChecker;
|
|
61
|
+
let elementCount1 = elementCount0;
|
|
62
|
+
// Set a time limit on the change.
|
|
63
|
+
const timeoutPromise = new Promise(resolve => {
|
|
64
|
+
timeout = setTimeout(() => {
|
|
65
|
+
clearInterval(settleChecker);
|
|
66
|
+
resolve();
|
|
67
|
+
}, timeLimit);
|
|
68
|
+
});
|
|
69
|
+
// Until the time limit expires, periodically:
|
|
70
|
+
const settlePromise = new Promise(resolve => {
|
|
71
|
+
settleChecker = setInterval(async () => {
|
|
72
|
+
const visiblesLoc = await rootLoc.locator('*:visible');
|
|
73
|
+
// Get the count.
|
|
74
|
+
elementCount1 = await visiblesLoc.count();
|
|
75
|
+
// If the count has changed:
|
|
76
|
+
if (elementCount1 !== elementCount0) {
|
|
77
|
+
// Stop.
|
|
78
|
+
clearTimeout(timeout);
|
|
79
|
+
clearInterval(settleChecker);
|
|
80
|
+
resolve();
|
|
81
|
+
}
|
|
82
|
+
}, settleInterval);
|
|
83
|
+
});
|
|
84
|
+
// When a change occurs or the time limit expires:
|
|
85
|
+
await Promise.race([timeoutPromise, settlePromise]);
|
|
86
|
+
const elapsedTime = Math.round(Date.now() - startTime);
|
|
87
|
+
// Return the change.
|
|
88
|
+
return {
|
|
89
|
+
change: elementCount1 - elementCount0,
|
|
90
|
+
elapsedTime
|
|
91
|
+
};
|
|
63
92
|
};
|
|
64
93
|
// Gets a violation description.
|
|
65
|
-
const getViolationDescription = (change, elapsedTime) =>
|
|
66
|
-
`Hovering over the element changes the related visible element count by ${change} in ${elapsedTime}ms`;
|
|
94
|
+
const getViolationDescription = (change, elapsedTime) => `Hovering over the element changes the related visible element count by ${change} in ${elapsedTime}ms`;
|
|
67
95
|
// Runs the test and returns the result.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
96
|
+
const reporter = async (page, report, _, withItems) => {
|
|
97
|
+
// Initialize the locators and result.
|
|
98
|
+
const candidateLocs = await page.locator([
|
|
99
|
+
'[aria-controls]:visible',
|
|
100
|
+
'[aria-expanded]:visible',
|
|
101
|
+
'[aria-haspopup]:visible',
|
|
102
|
+
'[onmouseenter]:visible',
|
|
103
|
+
'[onmouseover]:visible',
|
|
104
|
+
'[onpointerenter]:visible',
|
|
105
|
+
'[onpointerover]:visible',
|
|
106
|
+
'[role="menu"]:visible',
|
|
107
|
+
'[role="menubar"]:visible',
|
|
108
|
+
'[role="menuitem"]:visible',
|
|
109
|
+
'[data-tooltip]:visible',
|
|
110
|
+
'[data-popover]:visible',
|
|
111
|
+
'[data-hover]:visible',
|
|
112
|
+
'[data-menu]:visible',
|
|
113
|
+
'[data-dropdown]:visible',
|
|
114
|
+
'[role=tab]:visible',
|
|
115
|
+
'[role=combobox]:visible'
|
|
116
|
+
].join(', '));
|
|
117
|
+
const allLocs = await candidateLocs.all();
|
|
118
|
+
const violations = [];
|
|
119
|
+
const data = {
|
|
120
|
+
hoverableCount: allLocs.length
|
|
121
|
+
};
|
|
122
|
+
// For each locator:
|
|
123
|
+
for (const loc of allLocs) {
|
|
124
|
+
// Get the XPath of the element referenced by the locator.
|
|
125
|
+
let xPath = (await loc.evaluate(element => getXPath(element)));
|
|
126
|
+
const pathSegments = xPath.split('/');
|
|
127
|
+
const { length } = pathSegments;
|
|
128
|
+
// Change it to the XPath of the desired observation root.
|
|
129
|
+
pathSegments.pop();
|
|
130
|
+
if (!['main', 'body'].includes(pathSegments[length - 2])) {
|
|
131
|
+
pathSegments.pop();
|
|
132
|
+
}
|
|
133
|
+
xPath = pathSegments.join('/');
|
|
134
|
+
// Get a locator for the observation root.
|
|
135
|
+
const rootLoc = page.locator(`xpath=${xPath}`);
|
|
136
|
+
const loc0 = await rootLoc.locator('*:visible');
|
|
137
|
+
// Get a pre-hover count of the visible elements in the observation tree.
|
|
138
|
+
const elementCount0 = await loc0.count();
|
|
139
|
+
try {
|
|
140
|
+
// Hover over the element.
|
|
141
|
+
await loc.hover({ timeout: (0, config_1.applyMultiplier)(400) });
|
|
142
|
+
// Get the change in the count of the visible elements in the observation tree.
|
|
143
|
+
const changeData = await getVisibleCountChange(rootLoc, elementCount0, 400, 75);
|
|
144
|
+
const { change, elapsedTime } = changeData;
|
|
145
|
+
// If a change occurred:
|
|
146
|
+
if (change) {
|
|
147
|
+
// Add the locator and a violation description to the array of violations.
|
|
148
|
+
violations.push({
|
|
149
|
+
loc,
|
|
150
|
+
what: getViolationDescription(change, elapsedTime)
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Stop hovering over the element.
|
|
154
|
+
await page.mouse.move(0, 0);
|
|
155
|
+
// Await a reverse change in the count of the visible elements in the observation tree.
|
|
156
|
+
await getVisibleCountChange(rootLoc, elementCount0 + change);
|
|
157
|
+
}
|
|
158
|
+
// If hovering throws an error:
|
|
159
|
+
catch (error) {
|
|
160
|
+
// If the error is a timeout:
|
|
161
|
+
if (error instanceof playwright.errors.TimeoutError) {
|
|
162
|
+
// Skip the locator.
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
// Otherwise, i.e. if the error is not a timeout, report this and quit.
|
|
166
|
+
data.prevented = true;
|
|
167
|
+
data.error = `ERROR hovering over an element (${error.message.slice(0, 200)})`;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
141
170
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return await getBasicResult(report, withItems, 'hover', 0, whats, data, violations);
|
|
171
|
+
// Get and return a result.
|
|
172
|
+
const whats = 'Hovering over elements changes the number of related visible elements';
|
|
173
|
+
return await (0, testaro_1.getBasicResult)(report, withItems, 'hover', 0, whats, data, violations);
|
|
146
174
|
};
|
|
175
|
+
exports.reporter = reporter;
|
package/testaro/hover.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2021–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jeff Witt.
|
|
4
|
+
© 2025–2026 Jonathan Robert Pool.
|
|
5
|
+
|
|
6
|
+
Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
|
|
7
|
+
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// IMPORTS
|
|
12
|
+
|
|
13
|
+
import type {Locator, Page} from 'playwright';
|
|
14
|
+
import {getBasicResult} from '../procs/testaro';
|
|
15
|
+
import type {Report} from '../types';
|
|
16
|
+
import * as playwright from 'playwright';
|
|
17
|
+
// Shared configuration for timeout multiplier.
|
|
18
|
+
import {applyMultiplier} from '../procs/config';
|
|
19
|
+
|
|
20
|
+
// TYPES
|
|
21
|
+
|
|
22
|
+
// The injected getXPath is reached as a bare page global inside locator evaluate callbacks.
|
|
23
|
+
declare const getXPath: Window['getXPath'];
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
hover
|
|
27
|
+
This test reports unexpected impacts of hovering. The elements that are subjected to hovering (called “triggers”) include all the elements that have attributes associated with control over the visibility of other elements. If hovering over an element results in an increase or decrease in the total count of visible elements in the tree rooted in the grandparent of the trigger, the rule is considered violated. This test uses the getBasicResult function in order to use Playwright for the most realistic hover simulation.
|
|
28
|
+
Compiled to hover.js by tsc (issue #73); edit this file, not the emitted one.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
// FUNCTIONS
|
|
32
|
+
|
|
33
|
+
// Returns an awaited change in a visible element count.
|
|
34
|
+
const getVisibleCountChange = async (
|
|
35
|
+
rootLoc: Locator, elementCount0: number, timeLimit = 400, settleInterval = 75
|
|
36
|
+
) => {
|
|
37
|
+
const startTime = Date.now();
|
|
38
|
+
let timeout: NodeJS.Timeout;
|
|
39
|
+
let settleChecker: NodeJS.Timeout;
|
|
40
|
+
let elementCount1 = elementCount0;
|
|
41
|
+
// Set a time limit on the change.
|
|
42
|
+
const timeoutPromise = new Promise<void>(resolve => {
|
|
43
|
+
timeout = setTimeout(() => {
|
|
44
|
+
clearInterval(settleChecker);
|
|
45
|
+
resolve();
|
|
46
|
+
}, timeLimit);
|
|
47
|
+
});
|
|
48
|
+
// Until the time limit expires, periodically:
|
|
49
|
+
const settlePromise = new Promise<void>(resolve => {
|
|
50
|
+
settleChecker = setInterval(async () => {
|
|
51
|
+
const visiblesLoc = await rootLoc.locator('*:visible');
|
|
52
|
+
// Get the count.
|
|
53
|
+
elementCount1 = await visiblesLoc.count();
|
|
54
|
+
// If the count has changed:
|
|
55
|
+
if (elementCount1 !== elementCount0) {
|
|
56
|
+
// Stop.
|
|
57
|
+
clearTimeout(timeout);
|
|
58
|
+
clearInterval(settleChecker);
|
|
59
|
+
resolve();
|
|
60
|
+
}
|
|
61
|
+
}, settleInterval);
|
|
62
|
+
});
|
|
63
|
+
// When a change occurs or the time limit expires:
|
|
64
|
+
await Promise.race([timeoutPromise, settlePromise]);
|
|
65
|
+
const elapsedTime = Math.round(Date.now() - startTime);
|
|
66
|
+
// Return the change.
|
|
67
|
+
return {
|
|
68
|
+
change: elementCount1 - elementCount0,
|
|
69
|
+
elapsedTime
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
// Gets a violation description.
|
|
73
|
+
const getViolationDescription = (change: number, elapsedTime: number) =>
|
|
74
|
+
`Hovering over the element changes the related visible element count by ${change} in ${elapsedTime}ms`;
|
|
75
|
+
// Runs the test and returns the result.
|
|
76
|
+
export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
|
|
77
|
+
// Initialize the locators and result.
|
|
78
|
+
const candidateLocs = await page.locator([
|
|
79
|
+
'[aria-controls]:visible',
|
|
80
|
+
'[aria-expanded]:visible',
|
|
81
|
+
'[aria-haspopup]:visible',
|
|
82
|
+
'[onmouseenter]:visible',
|
|
83
|
+
'[onmouseover]:visible',
|
|
84
|
+
'[onpointerenter]:visible',
|
|
85
|
+
'[onpointerover]:visible',
|
|
86
|
+
'[role="menu"]:visible',
|
|
87
|
+
'[role="menubar"]:visible',
|
|
88
|
+
'[role="menuitem"]:visible',
|
|
89
|
+
'[data-tooltip]:visible',
|
|
90
|
+
'[data-popover]:visible',
|
|
91
|
+
'[data-hover]:visible',
|
|
92
|
+
'[data-menu]:visible',
|
|
93
|
+
'[data-dropdown]:visible',
|
|
94
|
+
'[role=tab]:visible',
|
|
95
|
+
'[role=combobox]:visible'
|
|
96
|
+
].join(', '));
|
|
97
|
+
const allLocs = await candidateLocs.all();
|
|
98
|
+
const violations: {loc: Locator; what: string}[] = [];
|
|
99
|
+
const data: {hoverableCount: number; prevented?: boolean; error?: string} = {
|
|
100
|
+
hoverableCount: allLocs.length
|
|
101
|
+
};
|
|
102
|
+
// For each locator:
|
|
103
|
+
for (const loc of allLocs) {
|
|
104
|
+
// Get the XPath of the element referenced by the locator.
|
|
105
|
+
let xPath = (await loc.evaluate(element => getXPath(element)))!;
|
|
106
|
+
const pathSegments = xPath.split('/');
|
|
107
|
+
const {length} = pathSegments;
|
|
108
|
+
// Change it to the XPath of the desired observation root.
|
|
109
|
+
pathSegments.pop();
|
|
110
|
+
if (! ['main', 'body'].includes(pathSegments[length - 2])) {
|
|
111
|
+
pathSegments.pop();
|
|
112
|
+
}
|
|
113
|
+
xPath = pathSegments.join('/');
|
|
114
|
+
// Get a locator for the observation root.
|
|
115
|
+
const rootLoc = page.locator(`xpath=${xPath}`);
|
|
116
|
+
const loc0 = await rootLoc.locator('*:visible');
|
|
117
|
+
// Get a pre-hover count of the visible elements in the observation tree.
|
|
118
|
+
const elementCount0 = await loc0.count();
|
|
119
|
+
try {
|
|
120
|
+
// Hover over the element.
|
|
121
|
+
await loc.hover({timeout: applyMultiplier(400)});
|
|
122
|
+
// Get the change in the count of the visible elements in the observation tree.
|
|
123
|
+
const changeData = await getVisibleCountChange(rootLoc, elementCount0, 400, 75);
|
|
124
|
+
const {change, elapsedTime} = changeData;
|
|
125
|
+
// If a change occurred:
|
|
126
|
+
if (change) {
|
|
127
|
+
// Add the locator and a violation description to the array of violations.
|
|
128
|
+
violations.push({
|
|
129
|
+
loc,
|
|
130
|
+
what: getViolationDescription(change, elapsedTime)
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
// Stop hovering over the element.
|
|
134
|
+
await page.mouse.move(0, 0);
|
|
135
|
+
// Await a reverse change in the count of the visible elements in the observation tree.
|
|
136
|
+
await getVisibleCountChange(rootLoc, elementCount0 + change);
|
|
137
|
+
}
|
|
138
|
+
// If hovering throws an error:
|
|
139
|
+
catch(error) {
|
|
140
|
+
// If the error is a timeout:
|
|
141
|
+
if (error instanceof playwright.errors.TimeoutError) {
|
|
142
|
+
// Skip the locator.
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
// Otherwise, i.e. if the error is not a timeout, report this and quit.
|
|
146
|
+
data.prevented = true;
|
|
147
|
+
data.error = `ERROR hovering over an element (${(error as Error).message.slice(0, 200)})`;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Get and return a result.
|
|
152
|
+
const whats = 'Hovering over elements changes the number of related visible elements';
|
|
153
|
+
return await getBasicResult(report, withItems, 'hover', 0, whats, data, violations);
|
|
154
|
+
};
|
package/testaro/hr.d.ts
ADDED
package/testaro/hr.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.
|
|
@@ -8,26 +9,22 @@
|
|
|
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
|
hr
|
|
14
|
-
This test reports the use of hr elements.
|
|
17
|
+
This test reports the use of hr elements. Compiled to hr.js by tsc (issue #73); edit this
|
|
18
|
+
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
page, report, withItems, 'hr', 'body hr', whats, 0, getBadWhat.toString()
|
|
32
|
-
);
|
|
22
|
+
const reporter = async (page, report, _, withItems) => {
|
|
23
|
+
const getBadWhat = element => {
|
|
24
|
+
// Return a violation description.
|
|
25
|
+
return `hr element is used for vertical segmentation`;
|
|
26
|
+
};
|
|
27
|
+
const whats = 'HR elements are used for vertical segmentation';
|
|
28
|
+
return await (0, testaro_1.doTest)(page, report, withItems, 'hr', 'body hr', whats, 0, getBadWhat.toString());
|
|
33
29
|
};
|
|
30
|
+
exports.reporter = reporter;
|
package/testaro/hr.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2023–2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2026 Jeff Witt.
|
|
4
|
+
© 2025–2026 Jonathan Robert Pool.
|
|
5
|
+
|
|
6
|
+
Licensed under the MIT License. See LICENSE file at the project root or
|
|
7
|
+
https://opensource.org/license/mit/ for details.
|
|
8
|
+
|
|
9
|
+
SPDX-License-Identifier: MIT
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// IMPORTS
|
|
13
|
+
|
|
14
|
+
import type {Page} from 'playwright';
|
|
15
|
+
import {doTest} from '../procs/testaro';
|
|
16
|
+
import type {GetBadWhat, Report} from '../types';
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
hr
|
|
20
|
+
This test reports the use of hr elements. Compiled to hr.js by tsc (issue #73); edit this
|
|
21
|
+
file, not the emitted one.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// FUNCTIONS
|
|
25
|
+
|
|
26
|
+
// Runs the test and returns the result.
|
|
27
|
+
export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
|
|
28
|
+
const getBadWhat: GetBadWhat = element => {
|
|
29
|
+
// Return a violation description.
|
|
30
|
+
return `hr element is used for vertical segmentation`;
|
|
31
|
+
}
|
|
32
|
+
const whats = 'HR elements are used for vertical segmentation';
|
|
33
|
+
return await doTest(
|
|
34
|
+
page, report, withItems, 'hr', 'body hr', whats, 0, getBadWhat.toString()
|
|
35
|
+
);
|
|
36
|
+
};
|
package/testaro/imageLink.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
© 2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
4
|
© 2025 Juan S. Casado.
|
|
@@ -9,31 +10,27 @@
|
|
|
9
10
|
|
|
10
11
|
SPDX-License-Identifier: MIT
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.reporter = void 0;
|
|
15
|
+
const testaro_1 = require("../procs/testaro");
|
|
13
16
|
/*
|
|
14
17
|
imageLink
|
|
15
18
|
Clean-room rule.
|
|
16
19
|
This test reports links whose destinations are image files.
|
|
20
|
+
Compiled to imageLink.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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
page, report, withItems, 'imageLink', 'body a[href]', whats, 0, getBadWhat.toString()
|
|
38
|
-
);
|
|
24
|
+
const reporter = async (page, report, _, withItems) => {
|
|
25
|
+
const getBadWhat = element => {
|
|
26
|
+
const href = element.getAttribute('href') || '';
|
|
27
|
+
// If the destination of the element is an image file:
|
|
28
|
+
if (/\.(?:png|jpe?g|gif|svg|webp|ico)(?:$|[?#])/i.test(href)) {
|
|
29
|
+
// Return a violation description.
|
|
30
|
+
return 'Link destination is an image file';
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const whats = 'Links have image files as their destinations';
|
|
34
|
+
return await (0, testaro_1.doTest)(page, report, withItems, 'imageLink', 'body a[href]', whats, 0, getBadWhat.toString());
|
|
39
35
|
};
|
|
36
|
+
exports.reporter = reporter;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
© 2025 Juan S. Casado.
|
|
4
|
+
© 2026 Jeff Witt.
|
|
5
|
+
© 2025–2026 Jonathan Robert Pool
|
|
6
|
+
|
|
7
|
+
Licensed under the MIT License. See LICENSE file at the project root or
|
|
8
|
+
https://opensource.org/license/mit/ for details.
|
|
9
|
+
|
|
10
|
+
SPDX-License-Identifier: MIT
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// IMPORTS
|
|
14
|
+
|
|
15
|
+
import type {Page} from 'playwright';
|
|
16
|
+
import {doTest} from '../procs/testaro';
|
|
17
|
+
import type {GetBadWhat, Report} from '../types';
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
imageLink
|
|
21
|
+
Clean-room rule.
|
|
22
|
+
This test reports links whose destinations are image files.
|
|
23
|
+
Compiled to imageLink.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 href = element.getAttribute('href') || '';
|
|
32
|
+
// If the destination of the element is an image file:
|
|
33
|
+
if (/\.(?:png|jpe?g|gif|svg|webp|ico)(?:$|[?#])/i.test(href)) {
|
|
34
|
+
// Return a violation description.
|
|
35
|
+
return 'Link destination is an image file';
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const whats = 'Links have image files as their destinations';
|
|
39
|
+
return await doTest(
|
|
40
|
+
page, report, withItems, 'imageLink', 'body a[href]', whats, 0, getBadWhat.toString()
|
|
41
|
+
);
|
|
42
|
+
};
|