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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2023–2024 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 https://opensource.org/license/mit/ for details.
|
|
7
|
+
|
|
8
|
+
SPDX-License-Identifier: MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// ########## IMPORTS
|
|
12
|
+
|
|
13
|
+
import type {Page} from 'playwright';
|
|
14
|
+
import {getSource} from '../procs/getSource';
|
|
15
|
+
import {getXPathCatalogIndex} from '../procs/xPath';
|
|
16
|
+
import type {Report, StandardInstance} from '../types';
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
dupAtt
|
|
20
|
+
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.
|
|
21
|
+
Compiled to dupAtt.js by tsc (issue #73); edit this file, not the emitted one.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ########## FUNCTIONS
|
|
25
|
+
|
|
26
|
+
// 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.
|
|
27
|
+
const getIDCatalogIndex = (report: Report, tagName: string, id: string): string => {
|
|
28
|
+
const {catalog} = report;
|
|
29
|
+
if (id && catalog) {
|
|
30
|
+
const index = Object.keys(catalog).find(
|
|
31
|
+
key => catalog[key].id === id && catalog[key].tagName === tagName
|
|
32
|
+
);
|
|
33
|
+
if (index !== undefined) {
|
|
34
|
+
return index;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return getXPathCatalogIndex(report, '/html/body');
|
|
38
|
+
};
|
|
39
|
+
// Runs the test and returns the result.
|
|
40
|
+
export const reporter = async (page: Page, report: Report, _: unknown, withItems: boolean) => {
|
|
41
|
+
// Initialize the data and standard result.
|
|
42
|
+
const data: {
|
|
43
|
+
total: number;
|
|
44
|
+
items?: {tagName: string; id: string; duplicatedAttribute: string}[];
|
|
45
|
+
prevented?: boolean;
|
|
46
|
+
error?: string;
|
|
47
|
+
} = {total: 0};
|
|
48
|
+
if (withItems) {
|
|
49
|
+
data.items = [];
|
|
50
|
+
}
|
|
51
|
+
let totals: number[] = [];
|
|
52
|
+
const standardInstances: StandardInstance[] = [];
|
|
53
|
+
// Get the source.
|
|
54
|
+
const sourceData = await getSource(page);
|
|
55
|
+
// If it was not obtained:
|
|
56
|
+
if (sourceData.prevented) {
|
|
57
|
+
// Report this.
|
|
58
|
+
data.prevented = true;
|
|
59
|
+
data.error = sourceData.error;
|
|
60
|
+
}
|
|
61
|
+
// Otherwise, i.e. if it was obtained:
|
|
62
|
+
else {
|
|
63
|
+
let rawPage = sourceData.source;
|
|
64
|
+
// Change any spacing character sequences in it to single spaces.
|
|
65
|
+
rawPage = rawPage.replace(/\s+/g, ' ');
|
|
66
|
+
// Delete any spaces adjacent to equal symbols in it.
|
|
67
|
+
rawPage = rawPage.replace(/ = | =|= /g, '=');
|
|
68
|
+
// Remove any escaped quotation marks from it.
|
|
69
|
+
rawPage = rawPage.replace(/\\"|\\'/g, '');
|
|
70
|
+
// Remove any script code from it.
|
|
71
|
+
rawPage = rawPage.replace(/<script(?: [^<>]+)?>.*?<\/script>/g, '');
|
|
72
|
+
rawPage = rawPage.replace(/<script(?: [^<>]+)?>/g, '');
|
|
73
|
+
rawPage = rawPage.replace(/<\/script>/g, '');
|
|
74
|
+
// Remove any comments from it.
|
|
75
|
+
rawPage = rawPage.replace(/<!--.*?-->/g, '');
|
|
76
|
+
// Extract the syntactically valid opening tags of its elements.
|
|
77
|
+
// A pageless source would make the match null; the original dereferenced it unguarded.
|
|
78
|
+
let elements: string[] = rawPage.match(/<[a-zA-Z]+(?: [^<>]+)?>/g)!;
|
|
79
|
+
// Delete their enclosing angle brackets and any closing slashes.
|
|
80
|
+
elements = elements.map(element => element.replace(/< ?| ?\/?>/g, ''));
|
|
81
|
+
// Delete the values of any attributes in them.
|
|
82
|
+
const nvElements = elements.map(element => element.replace(/="[^"]*"/g, ''));
|
|
83
|
+
// For each element:
|
|
84
|
+
nvElements.forEach((element, index) => {
|
|
85
|
+
// Identify its tag name and attributes.
|
|
86
|
+
const terms = element.split(' ');
|
|
87
|
+
// If it has 2 or more attributes:
|
|
88
|
+
if (terms.length > 2) {
|
|
89
|
+
// If any is duplicated:
|
|
90
|
+
const tagName = terms[0].toUpperCase();
|
|
91
|
+
const attributes = terms.slice(1);
|
|
92
|
+
attributes.sort();
|
|
93
|
+
const duplicatedAttribute = attributes.find(
|
|
94
|
+
(current, attIndex) => attributes[attIndex + 1] === current
|
|
95
|
+
);
|
|
96
|
+
if (duplicatedAttribute) {
|
|
97
|
+
// Add data on the element to the data.
|
|
98
|
+
const id = terms.includes('id')
|
|
99
|
+
? elements[index].replace(/^.+id="/, '').replace(/".+/, '')
|
|
100
|
+
: '';
|
|
101
|
+
data.total++;
|
|
102
|
+
if (withItems) {
|
|
103
|
+
data.items!.push({
|
|
104
|
+
tagName,
|
|
105
|
+
id,
|
|
106
|
+
duplicatedAttribute
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// If itemization is required and there are any instances:
|
|
113
|
+
if (data.items) {
|
|
114
|
+
// For each violator:
|
|
115
|
+
data.items.forEach(item => {
|
|
116
|
+
// Add an instance to the standard instances.
|
|
117
|
+
standardInstances.push({
|
|
118
|
+
ruleID: 'dupAtt',
|
|
119
|
+
what: `${item.tagName} element has 2 attributes named ${item.duplicatedAttribute}`,
|
|
120
|
+
ordinalSeverity: 2,
|
|
121
|
+
count: 1,
|
|
122
|
+
catalogIndex: getIDCatalogIndex(report, item.tagName, item.id)
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// Otherwise, if itemization is not required and there are any violations:
|
|
127
|
+
else if (data.total) {
|
|
128
|
+
// Add a summary instance to the standard instances.
|
|
129
|
+
standardInstances.push({
|
|
130
|
+
ruleID: 'dupAtt',
|
|
131
|
+
what: 'In some elements 2 attributes have the same name',
|
|
132
|
+
ordinalSeverity: 2,
|
|
133
|
+
count: data.total
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
totals = [0, 0, data.total, 0];
|
|
137
|
+
}
|
|
138
|
+
// Return the data.
|
|
139
|
+
return {
|
|
140
|
+
data,
|
|
141
|
+
totals,
|
|
142
|
+
standardInstances
|
|
143
|
+
};
|
|
144
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
export declare const reporter: (page: Page, _0: unknown, _1: unknown, _2: unknown, detailLevel?: number, tagName?: string | null, onlyVisible?: boolean, attribute?: string) => Promise<{
|
|
3
|
+
data: Record<string, unknown>;
|
|
4
|
+
totals: never[];
|
|
5
|
+
standardInstances: never[];
|
|
6
|
+
}>;
|
package/testaro/elements.js
CHANGED
|
@@ -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 Jonathan Robert Pool.
|
|
@@ -6,7 +7,8 @@
|
|
|
6
7
|
|
|
7
8
|
SPDX-License-Identifier: MIT
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.reporter = void 0;
|
|
10
12
|
/*
|
|
11
13
|
elements
|
|
12
14
|
This test reports data about specified elements within the document body. Meanings of detailLevel values:
|
|
@@ -14,163 +16,161 @@
|
|
|
14
16
|
1. Also data on each specified element.
|
|
15
17
|
2. Data on each specified element also include the text content of the parent element.
|
|
16
18
|
3. Data on each specified element also include data on its sibling nodes.
|
|
19
|
+
Compiled to elements.js by tsc (issue #73); edit this file, not the emitted one.
|
|
17
20
|
*/
|
|
18
|
-
|
|
19
21
|
// FUNCTIONS
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
value: attribute.value
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
else if (nodeType === 3) {
|
|
57
|
-
sibInfo.text = compact(text);
|
|
58
|
-
}
|
|
59
|
-
return sibInfo;
|
|
60
|
-
};
|
|
61
|
-
// FUNCTION DEFINITIONS END
|
|
62
|
-
// Initialize the data with the count of the specified elements.
|
|
63
|
-
const data = {
|
|
64
|
-
total: elements.length
|
|
65
|
-
};
|
|
66
|
-
// If no itemization is required:
|
|
67
|
-
if (detailLevel === 0) {
|
|
68
|
-
// Return the element count.
|
|
69
|
-
return data;
|
|
70
|
-
}
|
|
71
|
-
// Otherwise, i.e. if itemization is required:
|
|
72
|
-
else {
|
|
73
|
-
// Initialize the item data.
|
|
74
|
-
data.items = [];
|
|
75
|
-
// For each specified element:
|
|
76
|
-
elements.forEach(element => {
|
|
77
|
-
// Initialize data on it.
|
|
78
|
-
const parent = element.parentElement;
|
|
79
|
-
const datum = {
|
|
80
|
-
tagName: element.tagName,
|
|
81
|
-
parentTagName: parent ? parent.tagName : '',
|
|
82
|
-
code: compact(element.outerHTML),
|
|
83
|
-
attributes: [],
|
|
84
|
-
textContent: compact(element.textContent)
|
|
85
|
-
};
|
|
86
|
-
// For each of its attributes:
|
|
87
|
-
for (const attribute of element.attributes) {
|
|
88
|
-
// Add data on the attribute to the element data.
|
|
89
|
-
const {name, value} = attribute;
|
|
90
|
-
datum.attributes.push({
|
|
91
|
-
name,
|
|
92
|
-
value
|
|
93
|
-
});
|
|
94
|
-
// If the element has reference labels:
|
|
95
|
-
if (name === 'aria-labelledby') {
|
|
96
|
-
// Add their texts to the element data.
|
|
97
|
-
const labelerIDs = value.split(/\s+/);
|
|
98
|
-
const labelers = [];
|
|
99
|
-
labelerIDs.forEach(id => {
|
|
100
|
-
const labeler = document.getElementById(id);
|
|
101
|
-
if (labeler) {
|
|
102
|
-
labelers.push(compact(labeler.textContent));
|
|
22
|
+
const reporter = async (page, _0, _1, _2, detailLevel = 0, tagName = null, onlyVisible = false, attribute) => {
|
|
23
|
+
// Determine a selector of the specified elements, including any descendants of open shadow roots.
|
|
24
|
+
let selector = `body ${tagName ? tagName.toLowerCase() : '*'}`;
|
|
25
|
+
if (attribute) {
|
|
26
|
+
selector += `[${attribute}]`;
|
|
27
|
+
}
|
|
28
|
+
if (onlyVisible) {
|
|
29
|
+
selector += ':visible';
|
|
30
|
+
}
|
|
31
|
+
let data = {};
|
|
32
|
+
// Get the data on the elements.
|
|
33
|
+
try {
|
|
34
|
+
const locator = page.locator(selector);
|
|
35
|
+
data = await locator.evaluateAll((elements, detailLevel) => {
|
|
36
|
+
// FUNCTION DEFINITIONS START
|
|
37
|
+
// Compacts a string.
|
|
38
|
+
const compact = (string) => string.replace(/\s+/g, ' ').trim();
|
|
39
|
+
// Gets data on a sibling node of an element.
|
|
40
|
+
const getSibInfo = (node, nodeType, text) => {
|
|
41
|
+
const sibInfo = {
|
|
42
|
+
type: nodeType
|
|
43
|
+
};
|
|
44
|
+
// If the sibling node is an element:
|
|
45
|
+
if (nodeType === 1) {
|
|
46
|
+
sibInfo.tagName = node.tagName;
|
|
47
|
+
sibInfo.attributes = [];
|
|
48
|
+
Array.from(node.attributes).forEach(attribute => {
|
|
49
|
+
sibInfo.attributes.push({
|
|
50
|
+
name: attribute.name,
|
|
51
|
+
value: attribute.value
|
|
52
|
+
});
|
|
53
|
+
});
|
|
103
54
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
datum.labelers = labelers;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
// If the element has text content:
|
|
111
|
-
const {labels, textContent} = element;
|
|
112
|
-
const compactContent = compact(textContent);
|
|
113
|
-
if (compactContent) {
|
|
114
|
-
// Add it to the element data.
|
|
115
|
-
datum.textContent = compactContent;
|
|
116
|
-
}
|
|
117
|
-
// If the element has labels:
|
|
118
|
-
if (labels && labels.length) {
|
|
119
|
-
// Add their texts to the element data.
|
|
120
|
-
datum.labels = Array.from(labels).map(label => compact(label.textContent));
|
|
121
|
-
}
|
|
122
|
-
// If the parental text content is required:
|
|
123
|
-
if (detailLevel > 1) {
|
|
124
|
-
// Add it (excluding any shadow-root descendants) to the element data.
|
|
125
|
-
datum.parentTextContent = parent ? compact(parent.textContent) : '';
|
|
126
|
-
}
|
|
127
|
-
// If sibling itemization is required:
|
|
128
|
-
if (detailLevel === 3) {
|
|
129
|
-
// Add data on the siblings, excluding any descendants of shadow roots, to the data.
|
|
130
|
-
datum.siblings = {
|
|
131
|
-
before: [],
|
|
132
|
-
after: []
|
|
133
|
-
};
|
|
134
|
-
let more = element;
|
|
135
|
-
while (more) {
|
|
136
|
-
more = more.previousSibling;
|
|
137
|
-
if (more) {
|
|
138
|
-
const {nodeType, nodeValue} = more;
|
|
139
|
-
if (! (nodeType === 3 && nodeValue === '')) {
|
|
140
|
-
const sibInfo = getSibInfo(more, nodeType, nodeValue);
|
|
141
|
-
datum.siblings.before.unshift(sibInfo);
|
|
55
|
+
else if (nodeType === 3) {
|
|
56
|
+
sibInfo.text = compact(text);
|
|
142
57
|
}
|
|
143
|
-
|
|
58
|
+
return sibInfo;
|
|
59
|
+
};
|
|
60
|
+
// FUNCTION DEFINITIONS END
|
|
61
|
+
// Initialize the data with the count of the specified elements.
|
|
62
|
+
const data = {
|
|
63
|
+
total: elements.length
|
|
64
|
+
};
|
|
65
|
+
// If no itemization is required:
|
|
66
|
+
if (detailLevel === 0) {
|
|
67
|
+
// Return the element count.
|
|
68
|
+
return data;
|
|
144
69
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
70
|
+
// Otherwise, i.e. if itemization is required:
|
|
71
|
+
else {
|
|
72
|
+
// Initialize the item data.
|
|
73
|
+
data.items = [];
|
|
74
|
+
// For each specified element:
|
|
75
|
+
elements.forEach(element => {
|
|
76
|
+
// Initialize data on it.
|
|
77
|
+
const parent = element.parentElement;
|
|
78
|
+
const datum = {
|
|
79
|
+
tagName: element.tagName,
|
|
80
|
+
parentTagName: parent ? parent.tagName : '',
|
|
81
|
+
code: compact(element.outerHTML),
|
|
82
|
+
attributes: [],
|
|
83
|
+
textContent: compact(element.textContent)
|
|
84
|
+
};
|
|
85
|
+
// For each of its attributes:
|
|
86
|
+
for (const attribute of element.attributes) {
|
|
87
|
+
// Add data on the attribute to the element data.
|
|
88
|
+
const { name, value } = attribute;
|
|
89
|
+
datum.attributes.push({
|
|
90
|
+
name,
|
|
91
|
+
value
|
|
92
|
+
});
|
|
93
|
+
// If the element has reference labels:
|
|
94
|
+
if (name === 'aria-labelledby') {
|
|
95
|
+
// Add their texts to the element data.
|
|
96
|
+
const labelerIDs = value.split(/\s+/);
|
|
97
|
+
const labelers = [];
|
|
98
|
+
labelerIDs.forEach(id => {
|
|
99
|
+
const labeler = document.getElementById(id);
|
|
100
|
+
if (labeler) {
|
|
101
|
+
labelers.push(compact(labeler.textContent));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
if (labelers.length) {
|
|
105
|
+
datum.labelers = labelers;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// If the element has text content:
|
|
110
|
+
const { labels, textContent } = element;
|
|
111
|
+
const compactContent = compact(textContent);
|
|
112
|
+
if (compactContent) {
|
|
113
|
+
// Add it to the element data.
|
|
114
|
+
datum.textContent = compactContent;
|
|
115
|
+
}
|
|
116
|
+
// If the element has labels:
|
|
117
|
+
if (labels && labels.length) {
|
|
118
|
+
// Add their texts to the element data.
|
|
119
|
+
datum.labels = Array.from(labels).map(label => compact(label.textContent));
|
|
120
|
+
}
|
|
121
|
+
// If the parental text content is required:
|
|
122
|
+
if (detailLevel > 1) {
|
|
123
|
+
// Add it (excluding any shadow-root descendants) to the element data.
|
|
124
|
+
datum.parentTextContent = parent ? compact(parent.textContent) : '';
|
|
125
|
+
}
|
|
126
|
+
// If sibling itemization is required:
|
|
127
|
+
if (detailLevel === 3) {
|
|
128
|
+
// Add data on the siblings, excluding any descendants of shadow roots, to the data.
|
|
129
|
+
datum.siblings = {
|
|
130
|
+
before: [],
|
|
131
|
+
after: []
|
|
132
|
+
};
|
|
133
|
+
let more = element;
|
|
134
|
+
while (more) {
|
|
135
|
+
more = more.previousSibling;
|
|
136
|
+
if (more) {
|
|
137
|
+
const { nodeType, nodeValue } = more;
|
|
138
|
+
if (!(nodeType === 3 && nodeValue === '')) {
|
|
139
|
+
const sibInfo = getSibInfo(more, nodeType, nodeValue);
|
|
140
|
+
datum.siblings.before.unshift(sibInfo);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
more = element;
|
|
145
|
+
while (more) {
|
|
146
|
+
more = more.nextSibling;
|
|
147
|
+
if (more) {
|
|
148
|
+
const { nodeType, textContent } = more;
|
|
149
|
+
if (!(nodeType === 3 && textContent === '')) {
|
|
150
|
+
const sibInfo = getSibInfo(more, nodeType, compact(textContent));
|
|
151
|
+
datum.siblings.after.push(sibInfo);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
data.items.push(datum);
|
|
157
|
+
});
|
|
158
|
+
return data;
|
|
155
159
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
}, detailLevel);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
console.log(`ERROR performing test (${error.message})`);
|
|
164
|
+
data = {
|
|
165
|
+
prevented: true,
|
|
166
|
+
error: 'ERROR performing test'
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
// Return the result.
|
|
170
|
+
return {
|
|
171
|
+
data,
|
|
172
|
+
totals: [],
|
|
173
|
+
standardInstances: []
|
|
168
174
|
};
|
|
169
|
-
}
|
|
170
|
-
// Return the result.
|
|
171
|
-
return {
|
|
172
|
-
data,
|
|
173
|
-
totals: [],
|
|
174
|
-
standardInstances: []
|
|
175
|
-
};
|
|
176
175
|
};
|
|
176
|
+
exports.reporter = reporter;
|