testaro 67.0.0 → 68.0.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/LICENSE +4 -16
- package/README.md +10 -2
- package/UPGRADES.md +1 -1
- package/dirWatch.js +2 -3
- package/ed11y/editoria11y.min.js +109 -690
- package/ed11y/editoria11y210.min.js +747 -0
- package/netWatch.js +6 -6
- package/package.json +1 -1
- package/procs/aslint.js +2 -2
- package/procs/catalog.js +190 -0
- package/procs/{dateOf.js → dateTime.js} +6 -4
- package/procs/doActs.js +1227 -0
- package/procs/doTestAct.js +63 -29
- package/procs/error.js +53 -0
- package/procs/job.js +64 -38
- package/procs/launch.js +596 -0
- package/procs/nu.js +3 -18
- package/procs/shoot.js +18 -2
- package/procs/testaro.js +102 -125
- package/procs/xPath.js +62 -0
- package/run.js +42 -1938
- package/scratch/README.md +9 -0
- package/testaro/adbID.js +3 -3
- package/testaro/allCaps.js +4 -5
- package/testaro/allHidden.js +19 -18
- package/testaro/allSlanted.js +4 -5
- package/testaro/altScheme.js +3 -3
- package/testaro/attVal.js +19 -35
- package/testaro/autocomplete.js +65 -62
- package/testaro/bulk.js +21 -20
- package/testaro/buttonMenu.js +112 -33
- package/testaro/captionLoc.js +3 -3
- package/testaro/datalistRef.js +4 -5
- package/testaro/distortion.js +3 -3
- package/testaro/docType.js +6 -9
- package/testaro/dupAtt.js +12 -25
- package/testaro/elements.js +4 -3
- package/testaro/embAc.js +4 -2
- package/testaro/focAll.js +6 -13
- package/testaro/focAndOp.js +3 -3
- package/testaro/focInd.js +3 -3
- package/testaro/focVis.js +4 -3
- package/testaro/headEl.js +5 -12
- package/testaro/headingAmb.js +45 -88
- package/testaro/hovInd.js +5 -5
- package/testaro/hover.js +44 -8
- package/testaro/hr.js +4 -4
- package/testaro/imageLink.js +3 -3
- package/testaro/labClash.js +3 -3
- package/testaro/legendLoc.js +3 -3
- package/testaro/lineHeight.js +3 -3
- package/testaro/linkAmb.js +25 -17
- package/testaro/linkExt.js +5 -5
- package/testaro/linkOldAtt.js +4 -3
- package/testaro/linkTo.js +4 -3
- package/testaro/linkUl.js +4 -5
- package/testaro/miniText.js +4 -3
- package/testaro/motion.js +3 -22
- package/testaro/nonTable.js +4 -5
- package/testaro/optRoleSel.js +3 -3
- package/testaro/phOnly.js +3 -3
- package/testaro/pseudoP.js +5 -5
- package/testaro/radioSet.js +4 -5
- package/testaro/role.js +4 -5
- package/testaro/secHeading.js +4 -5
- package/testaro/shoot0.js +3 -2
- package/testaro/shoot1.js +3 -2
- package/testaro/styleDiff.js +5 -12
- package/testaro/tabNav.js +30 -118
- package/testaro/targetSmall.js +30 -15
- package/testaro/textNodes.js +3 -1
- package/testaro/textSem.js +4 -5
- package/testaro/title.js +4 -2
- package/testaro/titledEl.js +3 -3
- package/testaro/zIndex.js +3 -3
- package/tests/alfa.js +28 -54
- package/tests/aslint.js +20 -53
- package/tests/axe.js +76 -13
- package/tests/ed11y.js +69 -141
- package/tests/htmlcs.js +69 -38
- package/tests/ibm.js +54 -9
- package/tests/nuVal.js +65 -12
- package/tests/nuVnu.js +76 -26
- package/tests/qualWeb.js +89 -44
- package/tests/testaro.js +288 -273
- package/tests/wave.js +142 -117
- package/tests/wax.js +61 -42
- package/procs/getLocatorData.js +0 -192
- package/procs/identify.js +0 -250
- package/procs/isInlineLink.js +0 -42
- package/procs/screenShot.js +0 -32
- package/procs/standardize.js +0 -524
- package/procs/target.js +0 -90
- package/procs/tellServer.js +0 -43
- package/scripts/dumpAlts.js +0 -28
package/procs/identify.js
DELETED
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
|
|
4
|
-
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
|
-
https://opensource.org/license/mit/ for details.
|
|
6
|
-
|
|
7
|
-
SPDX-License-Identifier: MIT
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/*
|
|
11
|
-
identify.js
|
|
12
|
-
Identifies the element of a standard instance.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
// IMPORTS
|
|
16
|
-
|
|
17
|
-
// Module to get the XPath of an element.
|
|
18
|
-
const {xPath} = require('playwright-dompath');
|
|
19
|
-
|
|
20
|
-
// FUNCTIONS
|
|
21
|
-
|
|
22
|
-
// Returns the bounding box of a locator.
|
|
23
|
-
const boxOf = exports.boxOf = async locator => {
|
|
24
|
-
try {
|
|
25
|
-
const isVisible = await locator.isVisible();
|
|
26
|
-
if (isVisible) {
|
|
27
|
-
const box = await locator.boundingBox({
|
|
28
|
-
timeout: 100
|
|
29
|
-
});
|
|
30
|
-
if (box) {
|
|
31
|
-
Object.keys(box).forEach(dim => {
|
|
32
|
-
box[dim] = Math.round(box[dim], 0);
|
|
33
|
-
});
|
|
34
|
-
return box;
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch(error) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Returns a string representation of a bounding box.
|
|
49
|
-
const boxToString = exports.boxToString = box => {
|
|
50
|
-
if (box) {
|
|
51
|
-
return ['x', 'y', 'width', 'height'].map(dim => box[dim]).join(':');
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
return '';
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
// Normalizes an XPath.
|
|
58
|
-
const getNormalizedXPath = exports.getNormalizedXPath = xPath => {
|
|
59
|
-
if (xPath) {
|
|
60
|
-
xPath = xPath.replace(/^\.\/\//, '/');
|
|
61
|
-
const segments = xPath.split('/');
|
|
62
|
-
// Initialize an array of normalized segments.
|
|
63
|
-
const normalizedSegments = [];
|
|
64
|
-
// For each segment of the XPath:
|
|
65
|
-
segments.forEach(segment => {
|
|
66
|
-
// If the segment is html[1] or body[1]:
|
|
67
|
-
if (/html\[1\]|body\[1\]/.test(segment)) {
|
|
68
|
-
// Add it without its subscript to the array.
|
|
69
|
-
normalizedSegments.push(segment.replace(/\[1\]/, ''));
|
|
70
|
-
}
|
|
71
|
-
// Otherwise, if the segment is empty or html or body or ends with a subscript:
|
|
72
|
-
else if (segment === '' || ['html', 'body'].includes(segment) || segment.endsWith(']')) {
|
|
73
|
-
// Add it to the array.
|
|
74
|
-
normalizedSegments.push(segment);
|
|
75
|
-
}
|
|
76
|
-
// Otherwise, i.e. if the segment is a tag name with no subscript:
|
|
77
|
-
else {
|
|
78
|
-
// Add it with a subscript 1 to the array.
|
|
79
|
-
normalizedSegments.push(`${segment}[1]`);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
// Return the concatenated segments as the normalized XPath.
|
|
83
|
-
return normalizedSegments.join('/');
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
return '';
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
// Adds a box ID and a path ID to an object.
|
|
90
|
-
const addIDs = async (locator, recipient) => {
|
|
91
|
-
const locatorCount = await locator.count();
|
|
92
|
-
// If there is exactly 1 of them:
|
|
93
|
-
if (locatorCount === 1) {
|
|
94
|
-
// Add the box ID of the element to the result if none exists yet.
|
|
95
|
-
if (! recipient.boxID) {
|
|
96
|
-
const box = await boxOf(locator);
|
|
97
|
-
recipient.boxID = boxToString(box);
|
|
98
|
-
}
|
|
99
|
-
// If the element has no path ID yet in the result:
|
|
100
|
-
if (! recipient.pathID) {
|
|
101
|
-
let timeout;
|
|
102
|
-
const timer = new Promise(resolve => {
|
|
103
|
-
timeout = setTimeout(() => {
|
|
104
|
-
resolve({timedOut: true})
|
|
105
|
-
}, 500);
|
|
106
|
-
});
|
|
107
|
-
// Use Playwright to get the XPath.
|
|
108
|
-
const pathIDPromise = xPath(locator);
|
|
109
|
-
const pathID = await Promise.race([pathIDPromise, timer]);
|
|
110
|
-
// If the XPath was computed before being timed out:
|
|
111
|
-
if (typeof pathID === 'string') {
|
|
112
|
-
// Prevent the timeout from resolving.
|
|
113
|
-
clearTimeout(timeout);
|
|
114
|
-
// Add the XPath to the result.
|
|
115
|
-
recipient.pathID = pathID;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// Normalize the path ID.
|
|
119
|
-
recipient.pathID = getNormalizedXPath(recipient.pathID);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
// Sanitizes a tag name.
|
|
123
|
-
const tagify = tagName => {
|
|
124
|
-
if (tagName) {
|
|
125
|
-
const lcTagName = tagName.toLowerCase();
|
|
126
|
-
const safeTagName = lcTagName.replace(/[^a-z0-9]/g, '');
|
|
127
|
-
if (safeTagName !== lcTagName) {
|
|
128
|
-
console.log(`ERROR on page: Tag name ${tagName} invalid; treating it as ${safeTagName}`);
|
|
129
|
-
}
|
|
130
|
-
return safeTagName;
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
return '';
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
// Returns the XPath and box ID of the element of a standard instance.
|
|
137
|
-
exports.identify = async (instance, page) => {
|
|
138
|
-
// Initialize a result.
|
|
139
|
-
const elementID = {
|
|
140
|
-
boxID: '',
|
|
141
|
-
pathID: ''
|
|
142
|
-
};
|
|
143
|
-
const {excerpt, id, location} = instance;
|
|
144
|
-
const tagName = tagify(instance.tagName);
|
|
145
|
-
const {type, spec} = location;
|
|
146
|
-
// If the instance specifies a CSS selector or XPath location:
|
|
147
|
-
if (['selector', 'xpath'].includes(type)) {
|
|
148
|
-
let specifier = spec;
|
|
149
|
-
// If the specified location is an XPath:
|
|
150
|
-
if (type === 'xpath') {
|
|
151
|
-
// Remove any final text-node segment.
|
|
152
|
-
specifier = spec.replace(/\/text\(\)\[\d+\]$/, '');
|
|
153
|
-
}
|
|
154
|
-
// If any specifier remains:
|
|
155
|
-
if (specifier) {
|
|
156
|
-
if (type === 'xpath') {
|
|
157
|
-
// Prefix it for playwright-dompath.
|
|
158
|
-
specifier = `xpath=${specifier}`;
|
|
159
|
-
}
|
|
160
|
-
try {
|
|
161
|
-
// Get a Playwright locator for it.
|
|
162
|
-
const locators = page.locator(specifier);
|
|
163
|
-
// Get the count of its referents.
|
|
164
|
-
const locatorCount = await locators.count();
|
|
165
|
-
// If the specifier is valid and the count is 1:
|
|
166
|
-
if (locatorCount === 1) {
|
|
167
|
-
// Add a box ID and a path ID to the result.
|
|
168
|
-
await addIDs(locators, elementID);
|
|
169
|
-
}
|
|
170
|
-
/*
|
|
171
|
-
Otherwise, if the specifier is invalid or the count is not 1, and the instance specifies
|
|
172
|
-
an XPath location:
|
|
173
|
-
*/
|
|
174
|
-
else if (type === 'xpath') {
|
|
175
|
-
// Use the normalized XPath location as the path ID.
|
|
176
|
-
elementID.pathID = getNormalizedXPath(specifier.replace(/^xpath=/, ''));
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// If the specifier is invalid:
|
|
180
|
-
catch(error) {
|
|
181
|
-
// Add this to the instance.
|
|
182
|
-
instance.invalidity = {
|
|
183
|
-
badProperty: 'location',
|
|
184
|
-
validityError: error.message
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
// If either ID remains undefined and the instance specifies an element ID:
|
|
190
|
-
if (id && ! (elementID.boxID && elementID.pathID)) {
|
|
191
|
-
// Get the first locator for an element with the ID.
|
|
192
|
-
try {
|
|
193
|
-
let locator = page.locator(`#${id.replace(/([-&;/]|^\d)/g, '\\$1')}`).first();
|
|
194
|
-
// Add a box ID and a path ID to the result.
|
|
195
|
-
await addIDs(locator, elementID);
|
|
196
|
-
}
|
|
197
|
-
// If the identifier is invalid:
|
|
198
|
-
catch(error) {
|
|
199
|
-
// Add this to the instance.
|
|
200
|
-
instance.invalidity = {
|
|
201
|
-
badProperty: 'id',
|
|
202
|
-
validityError: error.message
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
// If either ID remains undefined and the instance specifies a tag name:
|
|
207
|
-
if (tagName && ! (elementID.boxID && elementID.pathID)) {
|
|
208
|
-
try {
|
|
209
|
-
// Get locators for elements with the tag name.
|
|
210
|
-
let locators = page.locator(tagName);
|
|
211
|
-
// If there is exactly 1 of them:
|
|
212
|
-
let locatorCount = await locators.count();
|
|
213
|
-
if (locatorCount === 1) {
|
|
214
|
-
// Add a box ID and a path ID to the result.
|
|
215
|
-
await addIDs(locators, elementID);
|
|
216
|
-
}
|
|
217
|
-
// If either ID remains undefined and the instance also specifies an excerpt:
|
|
218
|
-
if (excerpt && ! (elementID.boxID && elementID.pathID)) {
|
|
219
|
-
// Get the plain text parts of the excerpt, converting ... to an empty string.
|
|
220
|
-
const minTagExcerpt = excerpt.replace(/<[^>]+>/g, '<>');
|
|
221
|
-
const plainParts = (minTagExcerpt.match(/[^<>]+/g) || [])
|
|
222
|
-
.map(part => part === '...' ? '' : part);
|
|
223
|
-
// Get the longest of them.
|
|
224
|
-
const sortedPlainParts = plainParts.sort((a, b) => b.length - a.length);
|
|
225
|
-
const mainPart = sortedPlainParts.length ? sortedPlainParts[0] : '';
|
|
226
|
-
// If there is one:
|
|
227
|
-
if (mainPart.trim().replace(/\s+/g, '').length) {
|
|
228
|
-
// Get locators for elements with the tag name and the text.
|
|
229
|
-
const locators = page.locator(tagName.toLowerCase(), {hasText: mainPart});
|
|
230
|
-
// If there is exactly 1 of them:
|
|
231
|
-
const locatorCount = await locators.count();
|
|
232
|
-
if (locatorCount === 1) {
|
|
233
|
-
// Add a box ID and a path ID to the result.
|
|
234
|
-
await addIDs(locators, elementID);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
// If the tag name is invalid:
|
|
240
|
-
catch(error) {
|
|
241
|
-
// Add this to the instance.
|
|
242
|
-
instance.invalidity = {
|
|
243
|
-
badProperty: 'tagName',
|
|
244
|
-
validityError: error.message
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// Return the result.
|
|
249
|
-
return elementID;
|
|
250
|
-
};
|
package/procs/isInlineLink.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
4
|
-
|
|
5
|
-
Licensed under the MIT License. See LICENSE file at the project root or
|
|
6
|
-
https://opensource.org/license/mit/ for details.
|
|
7
|
-
|
|
8
|
-
SPDX-License-Identifier: MIT
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/*
|
|
12
|
-
isInlineLink
|
|
13
|
-
Returns whether the link of a locator is inline.
|
|
14
|
-
A link is classified as inline unless its declared or effective display is block.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
// Returns whether a locator references an inline link.
|
|
18
|
-
exports.isInlineLink = async loc => await loc.evaluate(element => {
|
|
19
|
-
// Returns the normalized text content of an element.
|
|
20
|
-
const realTextOf = element => element ? element.textContent.replace(/\s/g, '') : '';
|
|
21
|
-
const blockElementTypes = 'p, div, li, h1, h2, h3, h4, h5, h6';
|
|
22
|
-
// If the element is not a link:
|
|
23
|
-
if (element.tagName !== 'A' && element.getAttribute('role') !== 'link') {
|
|
24
|
-
// Classify it as not an inline link.
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
// Otherwise, i.e. if it is a link:
|
|
28
|
-
else {
|
|
29
|
-
// Initialize the link as inline.
|
|
30
|
-
let result = true;
|
|
31
|
-
// If its display style property is block or is tantamount to block:
|
|
32
|
-
if (
|
|
33
|
-
window.getComputedStyle(element).display === 'block'
|
|
34
|
-
|| realTextOf(element.closest(blockElementTypes)) === realTextOf(element)
|
|
35
|
-
) {
|
|
36
|
-
// Reclassify the link as non-inline.
|
|
37
|
-
result = false;
|
|
38
|
-
}
|
|
39
|
-
// Return the result.
|
|
40
|
-
return result;
|
|
41
|
-
}
|
|
42
|
-
});
|
package/procs/screenShot.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2023–2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
-
© 2025 Jonathan Robert Pool.
|
|
4
|
-
Licensed under the MIT License. See LICENSE file for details.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/*
|
|
8
|
-
screenShot
|
|
9
|
-
This procedure creates and returns a full-page screenshot, optionally with an exclused element.
|
|
10
|
-
This procedure uses the Playwright page.screenshot method, which is not implemented for the
|
|
11
|
-
firefox browser type.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
// FUNCTIONS
|
|
15
|
-
|
|
16
|
-
// Creates and returns a screenshot.
|
|
17
|
-
exports.screenShot = async (page, exclusion = null) => {
|
|
18
|
-
const options = {
|
|
19
|
-
fullPage: true,
|
|
20
|
-
omitBackground: true,
|
|
21
|
-
timeout: 4000
|
|
22
|
-
};
|
|
23
|
-
if (exclusion) {
|
|
24
|
-
options.mask = [exclusion];
|
|
25
|
-
}
|
|
26
|
-
// Make and return a screenshot as a buffer.
|
|
27
|
-
return await page.screenshot(options)
|
|
28
|
-
.catch(error => {
|
|
29
|
-
console.log(`ERROR: Screenshot failed (${error.message})`);
|
|
30
|
-
return '';
|
|
31
|
-
});
|
|
32
|
-
};
|