testaro 30.0.8 → 32.0.1
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/CONTRIBUTING.md +26 -4
- package/LICENSE +1 -1
- package/README.md +24 -2
- package/aceconfig.js +27 -0
- package/actSpecs.js +28 -1
- package/call.js +22 -1
- package/data/template.js +22 -0
- package/dirWatch.js +22 -0
- package/package.json +1 -1
- package/procs/aslint.js +24 -0
- package/procs/getLocatorData.js +29 -1
- package/procs/getSource.js +30 -3
- package/procs/isInlineLink.js +23 -0
- package/procs/operable.js +23 -2
- package/procs/sample.js +22 -0
- package/procs/standardize.js +28 -7
- package/procs/tellServer.js +24 -2
- package/procs/testaro.js +26 -1
- package/procs/visChange.js +22 -0
- package/run.js +373 -83
- package/testaro/allCaps.js +22 -0
- package/testaro/allHidden.js +22 -0
- package/testaro/allSlanted.js +22 -0
- package/testaro/attVal.js +22 -0
- package/testaro/autocomplete.js +22 -0
- package/testaro/bulk.js +22 -0
- package/testaro/buttonMenu.js +22 -0
- package/testaro/distortion.js +22 -0
- package/testaro/docType.js +22 -0
- package/testaro/dupAtt.js +22 -0
- package/testaro/elements.js +22 -0
- package/testaro/embAc.js +22 -0
- package/testaro/filter.js +22 -0
- package/testaro/focAll.js +22 -0
- package/testaro/focInd.js +22 -0
- package/testaro/focOp.js +22 -0
- package/testaro/focVis.js +22 -0
- package/testaro/headEl.js +22 -0
- package/testaro/headingAmb.js +22 -0
- package/testaro/hovInd.js +21 -24
- package/testaro/hover.js +22 -0
- package/testaro/labClash.js +22 -0
- package/testaro/lineHeight.js +22 -0
- package/testaro/linkAmb.js +22 -0
- package/testaro/linkTitle.js +22 -0
- package/testaro/linkUl.js +22 -0
- package/testaro/miniText.js +22 -0
- package/testaro/motion.js +22 -0
- package/testaro/nonTable.js +22 -0
- package/testaro/opFoc.js +22 -0
- package/testaro/pseudoP.js +25 -3
- package/testaro/radioSet.js +22 -0
- package/testaro/role.js +22 -0
- package/testaro/styleDiff.js +80 -1
- package/testaro/tabNav.js +99 -2
- package/testaro/targetSize.js +22 -0
- package/testaro/textNodes.js +25 -3
- package/testaro/title.js +22 -0
- package/testaro/zIndex.js +22 -0
- package/tests/alfa.js +34 -12
- package/tests/aslint.js +55 -17
- package/tests/axe.js +35 -9
- package/tests/htmlcs.js +109 -78
- package/tests/ibm.js +111 -45
- package/tests/nuVal.js +39 -9
- package/tests/qualWeb.js +49 -25
- package/tests/testaro.js +50 -22
- package/tests/wave.js +36 -10
- package/validation/executors/run.js +26 -2
- package/validation/executors/test.js +27 -3
- package/validation/executors/tests.js +26 -2
- package/validation/executors/watchDir.js +26 -2
- package/validation/executors/watchNet.js +26 -2
- package/validation/jobs/todo/README.md +22 -0
- package/validation/tests/targets/adbID/index.html +21 -0
- package/validation/tests/targets/allCaps/index.html +21 -0
- package/validation/tests/targets/allHidden/ariaHiddenBody.html +21 -0
- package/validation/tests/targets/allHidden/good.html +21 -0
- package/validation/tests/targets/allHidden/hiddenMain.html +21 -0
- package/validation/tests/targets/allHidden/mixedHidden.html +22 -1
- package/validation/tests/targets/allHidden/noBody.html +21 -0
- package/validation/tests/targets/allHidden/noMain.html +21 -0
- package/validation/tests/targets/allHidden/noneDoc.html +21 -0
- package/validation/tests/targets/allHidden/visHiddenMain.html +21 -0
- package/validation/tests/targets/allSlanted/index.html +21 -0
- package/validation/tests/targets/altScheme/index.html +21 -0
- package/validation/tests/targets/attVal/bad.html +21 -0
- package/validation/tests/targets/attVal/good.html +21 -0
- package/validation/tests/targets/autocomplete/bad.html +21 -0
- package/validation/tests/targets/autocomplete/good.html +21 -0
- package/validation/tests/targets/bulk/bad.html +21 -0
- package/validation/tests/targets/bulk/good.html +21 -0
- package/validation/tests/targets/buttonMenu/bad.html +21 -0
- package/validation/tests/targets/buttonMenu/bad.js +27 -0
- package/validation/tests/targets/buttonMenu/good.html +21 -0
- package/validation/tests/targets/buttonMenu/good.js +27 -0
- package/validation/tests/targets/buttonMenu/style.css +27 -0
- package/validation/tests/targets/captionLoc/index.html +21 -0
- package/validation/tests/targets/datalistRef/index.html +21 -0
- package/validation/tests/targets/distortion/index.html +21 -0
- package/validation/tests/targets/docType/bad.html +21 -0
- package/validation/tests/targets/docType/good.html +21 -0
- package/validation/tests/targets/dupAtt/bad.html +21 -0
- package/validation/tests/targets/dupAtt/good.html +21 -0
- package/validation/tests/targets/elements/index.html +21 -0
- package/validation/tests/targets/embAc/bad.html +21 -0
- package/validation/tests/targets/embAc/good.html +21 -0
- package/validation/tests/targets/filter/bad.html +21 -0
- package/validation/tests/targets/filter/good.html +21 -0
- package/validation/tests/targets/focAll/good.html +21 -0
- package/validation/tests/targets/focAll/less.html +21 -0
- package/validation/tests/targets/focAll/more.html +21 -0
- package/validation/tests/targets/focInd/bad.html +21 -0
- package/validation/tests/targets/focInd/good.html +21 -0
- package/validation/tests/targets/focOp/bad.html +21 -0
- package/validation/tests/targets/focOp/good.html +21 -0
- package/validation/tests/targets/focVis/index.html +21 -0
- package/validation/tests/targets/headEl/index.html +21 -0
- package/validation/tests/targets/headingAmb/index.html +21 -0
- package/validation/tests/targets/hovInd/index.html +21 -0
- package/validation/tests/targets/hover/bad.html +21 -0
- package/validation/tests/targets/hover/good.html +21 -0
- package/validation/tests/targets/hr/index.html +21 -0
- package/validation/tests/targets/imageLink/index.html +21 -0
- package/validation/tests/targets/labClash/bad.html +21 -0
- package/validation/tests/targets/labClash/good.html +21 -0
- package/validation/tests/targets/legendLoc/index.html +21 -0
- package/validation/tests/targets/lineHeight/index.html +21 -0
- package/validation/tests/targets/linkAmb/index.html +21 -0
- package/validation/tests/targets/linkExt/index.html +21 -0
- package/validation/tests/targets/linkOldAtt/index.html +21 -0
- package/validation/tests/targets/linkTitle/index.html +21 -0
- package/validation/tests/targets/linkTo/index.html +21 -0
- package/validation/tests/targets/linkUl/bad.html +21 -0
- package/validation/tests/targets/linkUl/good.html +21 -0
- package/validation/tests/targets/linkUl/na.html +21 -0
- package/validation/tests/targets/miniText/index.html +21 -0
- package/validation/tests/targets/motion/bad.css +27 -0
- package/validation/tests/targets/motion/bad.html +21 -0
- package/validation/tests/targets/motion/good.html +21 -0
- package/validation/tests/targets/nonTable/index.html +21 -0
- package/validation/tests/targets/opFoc/bad.html +21 -0
- package/validation/tests/targets/opFoc/good.html +21 -0
- package/validation/tests/targets/optRoleSel/index.html +21 -0
- package/validation/tests/targets/phOnly/index.html +21 -0
- package/validation/tests/targets/pseudoP/index.html +21 -0
- package/validation/tests/targets/radioSet/bad.html +21 -0
- package/validation/tests/targets/radioSet/good.html +21 -0
- package/validation/tests/targets/role/bad.html +21 -0
- package/validation/tests/targets/role/good.html +21 -0
- package/validation/tests/targets/secHeading/index.html +21 -0
- package/validation/tests/targets/styleDiff/bad.html +21 -0
- package/validation/tests/targets/styleDiff/good.html +21 -0
- package/validation/tests/targets/tabNav/bad.html +21 -0
- package/validation/tests/targets/tabNav/bad.js +27 -0
- package/validation/tests/targets/tabNav/good.html +21 -0
- package/validation/tests/targets/tabNav/good.js +27 -0
- package/validation/tests/targets/tabNav/style.css +27 -0
- package/validation/tests/targets/targetSize/index.html +21 -0
- package/validation/tests/targets/textNodes/index.html +21 -0
- package/validation/tests/targets/textSem/index.html +21 -0
- package/validation/tests/targets/title/bad.html +21 -0
- package/validation/tests/targets/title/good.html +21 -0
- package/validation/tests/targets/titledEl/index.html +21 -0
- package/validation/tests/targets/zIndex/bad.html +21 -0
- package/validation/tests/targets/zIndex/good.html +21 -0
- package/validation/validateTest.js +39 -3
- package/validation/watch/done/README.md +23 -1
- package/validation/watch/todo/README.md +23 -1
- package/watch.js +32 -7
- package/call-old.js +0 -86
- package/procs/allText.js +0 -76
- package/procs/allVis.js +0 -17
- package/procs/getTextNodes.js +0 -39
- package/procs/linksByType.js +0 -54
- package/procs/nav.js +0 -259
- package/procs/textOf.txt +0 -73
- package/test copy.js +0 -38
- package/validation/tests/old/allCaps.json +0 -102
- package/validation/tests/old/allHidden.json +0 -314
- package/validation/tests/old/attVal.json +0 -60
- package/validation/tests/old/autocomplete.json +0 -51
- package/validation/tests/old/bulk.json +0 -48
- package/validation/tests/old/docType.json +0 -46
- package/validation/tests/old/dupAtt.json +0 -51
- package/validation/tests/old/elements.json +0 -140
- package/validation/tests/old/embAc.json +0 -54
- package/validation/tests/old/filter.json +0 -55
- package/validation/tests/old/focAll.json +0 -68
- package/validation/tests/old/focInd.json +0 -69
- package/validation/tests/old/focOp.json +0 -62
- package/validation/tests/old/focVis.json +0 -35
- package/validation/tests/old/hover.json +0 -118
- package/validation/tests/old/labClash.json +0 -52
- package/validation/tests/old/linkTo.json +0 -35
- package/validation/tests/old/linkUl.json +0 -71
- package/validation/tests/old/menuNav.json +0 -106
- package/validation/tests/old/miniText.json +0 -36
- package/validation/tests/old/motion.json +0 -62
- package/validation/tests/old/nonTable.json +0 -37
- package/validation/tests/old/radioSet.json +0 -52
- package/validation/tests/old/role.json +0 -60
- package/validation/tests/old/styleDiff.json +0 -71
- package/validation/tests/old/tabNav.json +0 -106
- package/validation/tests/old/temp.js +0 -28
- package/validation/tests/old/textNodes.json +0 -98
- package/validation/tests/old/title.json +0 -46
- package/validation/tests/old/titledEl.json +0 -37
- package/validation/tests/old/zIndex.json +0 -49
- package/validation/tests/targets/tabNav/goodMoz.js +0 -206
- package/watch-old.js +0 -275
- package/watch-temp.js +0 -45
package/watch.js
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2022–2023 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
|
|
1
23
|
/*
|
|
2
24
|
watch.js
|
|
3
25
|
Module for watching for a job and running it when found.
|
|
@@ -142,7 +164,9 @@ const checkNetJob = async (servers, serverIndex, isForever, interval, noJobCount
|
|
|
142
164
|
const client = server.startsWith('https://') ? httpsClient : httpClient;
|
|
143
165
|
const fullURL = `${server}?agent=${agent}`;
|
|
144
166
|
const logStart = `Requested job from server ${server} and got `;
|
|
145
|
-
|
|
167
|
+
// Tolerate unrecognized certificate authorities if the environment specifies.
|
|
168
|
+
const ruOpt = process.env.REJECT_UNAUTHORIZED === 'false' ? {rejectUnauthorized: false} : {};
|
|
169
|
+
client.request(fullURL, ruOpt, response => {
|
|
146
170
|
const chunks = [];
|
|
147
171
|
response
|
|
148
172
|
// If the response to the job request threw an error:
|
|
@@ -187,7 +211,7 @@ const checkNetJob = async (servers, serverIndex, isForever, interval, noJobCount
|
|
|
187
211
|
// Perform the job, adding result data to it.
|
|
188
212
|
const testee = sources.target.which;
|
|
189
213
|
console.log(
|
|
190
|
-
`${logStart}job ${id} (${nowString()})\n>> It will test ${testee}\n>> It will send report to ${sendReportTo}`
|
|
214
|
+
`${logStart}job ${id} (${nowString()})\n>> It will test ${testee}\n>> It will send report to ${sendReportTo}\n`
|
|
191
215
|
);
|
|
192
216
|
await doJob(contentObj);
|
|
193
217
|
let reportJSON = JSON.stringify(contentObj, null, 2);
|
|
@@ -202,7 +226,7 @@ const checkNetJob = async (servers, serverIndex, isForever, interval, noJobCount
|
|
|
202
226
|
// If the response to the report threw an error:
|
|
203
227
|
.on('error', async error => {
|
|
204
228
|
// Report this.
|
|
205
|
-
console.log(`${reportLogStart}error message ${error.message}`);
|
|
229
|
+
console.log(`${reportLogStart}error message ${error.message}\n`);
|
|
206
230
|
// Check the next server.
|
|
207
231
|
await checkNetJob(servers, serverIndex + 1, isForever, interval, noJobCount + 1);
|
|
208
232
|
})
|
|
@@ -218,7 +242,7 @@ const checkNetJob = async (servers, serverIndex, isForever, interval, noJobCount
|
|
|
218
242
|
const {message} = ackObj;
|
|
219
243
|
if (message) {
|
|
220
244
|
// Report it.
|
|
221
|
-
console.log(`${reportLogStart}${message}`);
|
|
245
|
+
console.log(`${reportLogStart}${message}\n`);
|
|
222
246
|
// Free the memory used by the report.
|
|
223
247
|
reportJSON = '';
|
|
224
248
|
contentObj = {};
|
|
@@ -229,7 +253,7 @@ const checkNetJob = async (servers, serverIndex, isForever, interval, noJobCount
|
|
|
229
253
|
else {
|
|
230
254
|
// Report it.
|
|
231
255
|
console.log(
|
|
232
|
-
`ERROR: ${reportLogStart}status ${repResponse.statusCode} and error message ${JSON.stringify(ackObj, null, 2)}`
|
|
256
|
+
`ERROR: ${reportLogStart}status ${repResponse.statusCode} and error message ${JSON.stringify(ackObj, null, 2)}\n`
|
|
233
257
|
);
|
|
234
258
|
// Check the next server, disregarding the failed job.
|
|
235
259
|
await checkNetJob(
|
|
@@ -241,7 +265,7 @@ const checkNetJob = async (servers, serverIndex, isForever, interval, noJobCount
|
|
|
241
265
|
catch(error) {
|
|
242
266
|
// Report it.
|
|
243
267
|
console.log(
|
|
244
|
-
`ERROR: ${reportLogStart}status ${repResponse.statusCode}, error message ${error.message}, and response ${content.slice(0, 1000)}`
|
|
268
|
+
`ERROR: ${reportLogStart}status ${repResponse.statusCode}, error message ${error.message}, and response ${content.slice(0, 1000)}\n`
|
|
245
269
|
);
|
|
246
270
|
// Check the next server, disregarding the failed job.
|
|
247
271
|
await checkNetJob(
|
|
@@ -253,7 +277,7 @@ const checkNetJob = async (servers, serverIndex, isForever, interval, noJobCount
|
|
|
253
277
|
// If the report submission throws an error:
|
|
254
278
|
.on('error', async error => {
|
|
255
279
|
// Report this.
|
|
256
|
-
console.log(`ERROR: ${reportLogStart}error message ${error.message}`);
|
|
280
|
+
console.log(`ERROR: ${reportLogStart}error message ${error.message}\n`);
|
|
257
281
|
// Check the next server, disregarding the failed job.
|
|
258
282
|
await checkNetJob(servers, serverIndex + 1, isForever, interval, noJobCount + 1);
|
|
259
283
|
})
|
|
@@ -336,6 +360,7 @@ exports.dirWatch = async (isForever, interval = 300) => {
|
|
|
336
360
|
};
|
|
337
361
|
// Checks for a network job, performs it, and submits a report, once or repeatedly.
|
|
338
362
|
exports.netWatch = async (isForever, interval = 300) => {
|
|
363
|
+
console.log('Starting netWatch');
|
|
339
364
|
// If the servers to be checked are valid:
|
|
340
365
|
const servers = jobURLs
|
|
341
366
|
.split('+')
|
package/call-old.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
call.js
|
|
3
|
-
Invokes Testaro modules with arguments.
|
|
4
|
-
This is the universal module for use of Testaro from a command line.
|
|
5
|
-
Arguments:
|
|
6
|
-
0. function to execute.
|
|
7
|
-
1+. arguments to pass to the function.
|
|
8
|
-
Usage examples:
|
|
9
|
-
node call run ts25
|
|
10
|
-
node call watch true true 30
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
// ########## IMPORTS
|
|
14
|
-
|
|
15
|
-
// Module to keep secrets.
|
|
16
|
-
require('dotenv').config();
|
|
17
|
-
// Module to process files.
|
|
18
|
-
const fs = require('fs/promises');
|
|
19
|
-
// Function to process a testing request.
|
|
20
|
-
const {doJob} = require('./run');
|
|
21
|
-
// Function to watch for jobs.
|
|
22
|
-
const {watch} = require('./watch');
|
|
23
|
-
|
|
24
|
-
// ########## CONSTANTS
|
|
25
|
-
|
|
26
|
-
const fn = process.argv[2];
|
|
27
|
-
const fnArgs = process.argv.slice(3);
|
|
28
|
-
const jobDir = process.env.JOBDIR;
|
|
29
|
-
const todoDir = `${jobDir}/todo`;
|
|
30
|
-
const reportDir = process.env.REPORTDIR;
|
|
31
|
-
const rawDir = `${reportDir}/raw`;
|
|
32
|
-
|
|
33
|
-
// ########## FUNCTIONS
|
|
34
|
-
|
|
35
|
-
// Fulfills a testing request.
|
|
36
|
-
const callRun = async jobIDStart => {
|
|
37
|
-
// Find the job.
|
|
38
|
-
const jobDirFileNames = await fs.readdir(todoDir);
|
|
39
|
-
const jobFileName = jobDirFileNames.find(fileName => fileName.startsWith(jobIDStart));
|
|
40
|
-
// If it exists:
|
|
41
|
-
if (jobFileName) {
|
|
42
|
-
// Get it.
|
|
43
|
-
const jobJSON = await fs.readFile(`${todoDir}/${jobFileName}`, 'utf8');
|
|
44
|
-
const report = JSON.parse(jobJSON);
|
|
45
|
-
// Run it.
|
|
46
|
-
await doJob(report);
|
|
47
|
-
// Archive it.
|
|
48
|
-
await fs.rename(`${todoDir}/${jobFileName}`, `${jobDir}/done/${jobFileName}`);
|
|
49
|
-
// Save the report.
|
|
50
|
-
await fs.writeFile(`${rawDir}/${jobFileName}`, JSON.stringify(report, null, 2));
|
|
51
|
-
console.log(`Job completed and report ${report.id}.json saved in ${rawDir}`);
|
|
52
|
-
}
|
|
53
|
-
// Otherwise, i.e. if the job does not exist.
|
|
54
|
-
else {
|
|
55
|
-
// Report the error.
|
|
56
|
-
console.log(`ERROR: No to-do job ID starts with ${jobIDStart}`);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
// Starts a watch.
|
|
60
|
-
const callWatch = async (isDirWatch, interval) => {
|
|
61
|
-
const whenType = interval > -1 ? 'repeating' : 'one-time';
|
|
62
|
-
const whereType = isDirWatch === 'true' ? 'directory' : 'network';
|
|
63
|
-
console.log(`Starting ${whenType} ${whereType} watch`);
|
|
64
|
-
await watch(isDirWatch === 'true', Number.parseInt(interval, 10));
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// ########## OPERATION
|
|
68
|
-
|
|
69
|
-
// Execute the requested function.
|
|
70
|
-
if (fn === 'run' && fnArgs.length === 1) {
|
|
71
|
-
callRun(fnArgs)
|
|
72
|
-
.then(() => {
|
|
73
|
-
console.log('Execution completed\n');
|
|
74
|
-
process.exit(0);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
else if (fn === 'watch' && fnArgs.length === 2) {
|
|
78
|
-
callWatch(... fnArgs)
|
|
79
|
-
.then(() => {
|
|
80
|
-
console.log('Execution completed\n');
|
|
81
|
-
process.exit(0);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
console.log('ERROR: Invalid statement');
|
|
86
|
-
}
|
package/procs/allText.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// Returns the text associated with an element.
|
|
2
|
-
exports.allText = async (page, elementHandle) => await page.evaluate(element => {
|
|
3
|
-
// Identify the element, if specified, or else the focused element.
|
|
4
|
-
const el = element || document.activeElement;
|
|
5
|
-
// Initialize an array of its texts.
|
|
6
|
-
const texts = [];
|
|
7
|
-
// FUNCTION DEFINITION START
|
|
8
|
-
// Removes excess spacing from a string.
|
|
9
|
-
const debloat = text => text.trim().replace(/\s+/g, ' ');
|
|
10
|
-
// FUNCTION DEFINITION END
|
|
11
|
-
// Add any attribute label to the array.
|
|
12
|
-
const ariaLabel = el.getAttribute('aria-label');
|
|
13
|
-
if (ariaLabel) {
|
|
14
|
-
const trimmedLabel = debloat(ariaLabel);
|
|
15
|
-
if (trimmedLabel) {
|
|
16
|
-
texts.push(trimmedLabel);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
// Add any explicit and implicit labels to the array.
|
|
20
|
-
const labelNodeList = el.labels;
|
|
21
|
-
if (labelNodeList && labelNodeList.length) {
|
|
22
|
-
const labels = Array.from(labelNodeList);
|
|
23
|
-
const labelTexts = labels
|
|
24
|
-
.map(label => label.textContent && debloat(label.textContent))
|
|
25
|
-
.filter(text => text);
|
|
26
|
-
if (labelTexts.length) {
|
|
27
|
-
texts.push(...labelTexts);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
// Add any referenced labels to the array.
|
|
31
|
-
if (el.hasAttribute('aria-labelledby')) {
|
|
32
|
-
const labelerIDs = el.getAttribute('aria-labelledby').split(/\s+/);
|
|
33
|
-
labelerIDs.forEach(id => {
|
|
34
|
-
const labeler = document.getElementById(id);
|
|
35
|
-
if (labeler) {
|
|
36
|
-
const labelerText = debloat(labeler.textContent);
|
|
37
|
-
if (labelerText) {
|
|
38
|
-
texts.push(labelerText);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
// Add any image text alternatives to the array.
|
|
44
|
-
const altTexts = Array
|
|
45
|
-
.from(element.querySelectorAll('img[alt]:not([alt=""])'))
|
|
46
|
-
.map(img => debloat(img.alt))
|
|
47
|
-
.join('; ');
|
|
48
|
-
if (altTexts.length) {
|
|
49
|
-
texts.push(altTexts);
|
|
50
|
-
}
|
|
51
|
-
// Add the first 100 characters of any text content of the element to the array.
|
|
52
|
-
const ownText = element.textContent;
|
|
53
|
-
if (ownText) {
|
|
54
|
-
const minText = debloat(ownText);
|
|
55
|
-
if (minText) {
|
|
56
|
-
texts.push(minText.slice(0, 100));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// Add any ID of the element to the array.
|
|
60
|
-
const id = element.id;
|
|
61
|
-
if (id) {
|
|
62
|
-
texts.push(`#${id}`);
|
|
63
|
-
}
|
|
64
|
-
// Identify a concatenation of the texts.
|
|
65
|
-
let textChain = texts.join('; ');
|
|
66
|
-
// If it is empty:
|
|
67
|
-
if (! textChain) {
|
|
68
|
-
// Substitute the HTML of the element.
|
|
69
|
-
textChain = `{${debloat(element.outerHTML)}}`;
|
|
70
|
-
if (textChain === '{}') {
|
|
71
|
-
textChain = '';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Return a concatenation of the texts in the array.
|
|
75
|
-
return textChain;
|
|
76
|
-
}, elementHandle);
|
package/procs/allVis.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// Makes all elements in a page visible.
|
|
2
|
-
exports.allVis = async page => {
|
|
3
|
-
await page.$$eval('body *', elements => {
|
|
4
|
-
elements.forEach(element => {
|
|
5
|
-
const styleDec = window.getComputedStyle(element);
|
|
6
|
-
if (styleDec.display === 'none') {
|
|
7
|
-
element.style.display = 'initial';
|
|
8
|
-
}
|
|
9
|
-
if (['hidden', 'collapse'].includes(styleDec.visibility)) {
|
|
10
|
-
element.style.visibility = 'inherit';
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
})
|
|
14
|
-
.catch(error => {
|
|
15
|
-
console.log(`ERROR making all elements visible (${error.message})`);
|
|
16
|
-
});
|
|
17
|
-
};
|
package/procs/getTextNodes.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
getTextNodes
|
|
3
|
-
Gets the text nodes of a page and their parent elements as a JSHandle.
|
|
4
|
-
*/
|
|
5
|
-
exports.getTextNodes = async page => {
|
|
6
|
-
// Identify the text nodes.
|
|
7
|
-
const data = await page.evaluateHandle(() => {
|
|
8
|
-
// Initialize the result.
|
|
9
|
-
const data = [];
|
|
10
|
-
// Collapse any adjacent text nodes.
|
|
11
|
-
document.body.normalize();
|
|
12
|
-
// Remove the irrelevant text content.
|
|
13
|
-
const extraElements = Array.from(document.body.querySelectorAll('style, script, svg'));
|
|
14
|
-
extraElements.forEach(element => {
|
|
15
|
-
element.textContent = '';
|
|
16
|
-
});
|
|
17
|
-
// FUNCTION DEFINITION START
|
|
18
|
-
// Returns a space-minimized copy of a string.
|
|
19
|
-
const compact = string => string.replace(/\s+/g, ' ').trim();
|
|
20
|
-
// FUNCTION DEFINITION END
|
|
21
|
-
// Create a collection of the text nodes.
|
|
22
|
-
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
23
|
-
let more = true;
|
|
24
|
-
while(more) {
|
|
25
|
-
if (walker.nextNode()) {
|
|
26
|
-
const nodeText = walker.currentNode.nodeValue;
|
|
27
|
-
const compactNodeText = compact(nodeText);
|
|
28
|
-
if (compactNodeText) {
|
|
29
|
-
data.push([nodeText, walker.currentNode.parentElement]);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
more = false;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return data;
|
|
37
|
-
});
|
|
38
|
-
return data;
|
|
39
|
-
};
|
package/procs/linksByType.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// Returns an object classifying the links in a page by layout.
|
|
2
|
-
exports.linksByType = async page => await page.evaluateHandle(() => {
|
|
3
|
-
// FUNCTION DEFINITIONS START
|
|
4
|
-
// Removes spacing characters from a text.
|
|
5
|
-
const despace = text => text.replace(/\s/g, '');
|
|
6
|
-
// Returns whether a list is a list entirely of links.
|
|
7
|
-
const isLinkList = list => {
|
|
8
|
-
const listItems = Array.from(list.children);
|
|
9
|
-
if (listItems.length > 1) {
|
|
10
|
-
return listItems.length > 1 && listItems.every(item => {
|
|
11
|
-
if (item.tagName === 'LI') {
|
|
12
|
-
const {children} = item;
|
|
13
|
-
if (children.length === 1) {
|
|
14
|
-
const link = children[0];
|
|
15
|
-
if (link.tagName === 'A') {
|
|
16
|
-
const itemText = despace(item.textContent);
|
|
17
|
-
const linkText = despace(link.textContent);
|
|
18
|
-
return itemText.length === linkText.length;
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
// FUNCTION DEFINITIONS END
|
|
38
|
-
// Identify the list links in the page.
|
|
39
|
-
const lists = Array.from(document.body.querySelectorAll('ul, ol'));
|
|
40
|
-
const listLinks = [];
|
|
41
|
-
lists.forEach(list => {
|
|
42
|
-
if (isLinkList(list)) {
|
|
43
|
-
listLinks.push(... Array.from(list.querySelectorAll('a')));
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
// Identify the inline links in the page.
|
|
47
|
-
const allLinks = Array.from(document.body.querySelectorAll('a'));
|
|
48
|
-
const inlineLinks = allLinks.filter(link => ! listLinks.includes(link));
|
|
49
|
-
// Return the data.
|
|
50
|
-
return {
|
|
51
|
-
adjacent: inlineLinks,
|
|
52
|
-
list: listLinks
|
|
53
|
-
};
|
|
54
|
-
});
|
package/procs/nav.js
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
// nav
|
|
2
|
-
|
|
3
|
-
// ######## IMPORTS
|
|
4
|
-
|
|
5
|
-
// Playwright package.
|
|
6
|
-
const playwright = require('playwright');
|
|
7
|
-
|
|
8
|
-
// ######## CONSTANTS
|
|
9
|
-
|
|
10
|
-
// Strings in log messages indicating errors.
|
|
11
|
-
const errorWords = [
|
|
12
|
-
'but not used',
|
|
13
|
-
'content security policy',
|
|
14
|
-
'deprecated',
|
|
15
|
-
'error',
|
|
16
|
-
'exception',
|
|
17
|
-
'expected',
|
|
18
|
-
'failed',
|
|
19
|
-
'invalid',
|
|
20
|
-
'missing',
|
|
21
|
-
'non-standard',
|
|
22
|
-
'not supported',
|
|
23
|
-
'refused',
|
|
24
|
-
'requires',
|
|
25
|
-
'sorry',
|
|
26
|
-
'suspicious',
|
|
27
|
-
'unrecognized',
|
|
28
|
-
'violates',
|
|
29
|
-
'warning'
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
// ######## VARIABLES
|
|
33
|
-
|
|
34
|
-
let browser;
|
|
35
|
-
|
|
36
|
-
// ######## FUNCTIONS
|
|
37
|
-
|
|
38
|
-
// Returns a string with any final slash removed.
|
|
39
|
-
const deSlash = string => string.endsWith('/') ? string.slice(0, -1) : string;
|
|
40
|
-
// Gets the script nonce from a response.
|
|
41
|
-
const getNonce = async response => {
|
|
42
|
-
let nonce = '';
|
|
43
|
-
// If the response includes a content security policy:
|
|
44
|
-
const headers = await response.allHeaders();
|
|
45
|
-
const cspWithQuotes = headers && headers['content-security-policy'];
|
|
46
|
-
if (cspWithQuotes) {
|
|
47
|
-
// If it requires scripts to have a nonce:
|
|
48
|
-
const csp = cspWithQuotes.replace(/'/g, '');
|
|
49
|
-
const directives = csp.split(/ *; */).map(directive => directive.split(/ +/));
|
|
50
|
-
const scriptDirective = directives.find(dir => dir[0] === 'script-src');
|
|
51
|
-
if (scriptDirective) {
|
|
52
|
-
const nonceSpec = scriptDirective.find(valPart => valPart.startsWith('nonce-'));
|
|
53
|
-
if (nonceSpec) {
|
|
54
|
-
// Return the nonce.
|
|
55
|
-
nonce = nonceSpec.replace(/^nonce-/, '');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// Return the nonce, if any.
|
|
60
|
-
return nonce;
|
|
61
|
-
};
|
|
62
|
-
// Visits a URL and returns the response of the server.
|
|
63
|
-
const goTo = async (report, page, url, timeout, waitUntil) => {
|
|
64
|
-
// If the URL is a file path:
|
|
65
|
-
if (url.startsWith('file://')) {
|
|
66
|
-
// Make it absolute.
|
|
67
|
-
url = url.replace('file://', `file://${__dirname.replace(/procs$/, '')}`);
|
|
68
|
-
}
|
|
69
|
-
// Visit the URL.
|
|
70
|
-
const startTime = Date.now();
|
|
71
|
-
try {
|
|
72
|
-
const response = await page.goto(url, {
|
|
73
|
-
timeout,
|
|
74
|
-
waitUntil
|
|
75
|
-
});
|
|
76
|
-
report.jobData.visitLatency += Math.round((Date.now() - startTime) / 1000);
|
|
77
|
-
const httpStatus = response.status();
|
|
78
|
-
// If the response status was normal:
|
|
79
|
-
if ([200, 304].includes(httpStatus) || url.startsWith('file:')) {
|
|
80
|
-
// If the browser was redirected in violation of a strictness requirement:
|
|
81
|
-
const actualURL = page.url();
|
|
82
|
-
if (report.strict && deSlash(actualURL) !== deSlash(url)) {
|
|
83
|
-
// Return an error.
|
|
84
|
-
console.log(`ERROR: Visit to ${url} redirected to ${actualURL}`);
|
|
85
|
-
return {
|
|
86
|
-
exception: 'badRedirection'
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
// Otherwise, i.e. if no prohibited redirection occurred:
|
|
90
|
-
else {
|
|
91
|
-
// Press the Escape key to dismiss any modal dialog.
|
|
92
|
-
await page.keyboard.press('Escape');
|
|
93
|
-
// Return the result of the navigation.
|
|
94
|
-
return {
|
|
95
|
-
success: true,
|
|
96
|
-
response
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// Otherwise, i.e. if the response status was abnormal:
|
|
101
|
-
else {
|
|
102
|
-
// Return an error.
|
|
103
|
-
console.log(`ERROR: Visit to ${url} got status ${httpStatus}`);
|
|
104
|
-
report.jobData.visitRejectionCount++;
|
|
105
|
-
return {
|
|
106
|
-
success: false,
|
|
107
|
-
error: 'badStatus'
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
catch(error) {
|
|
112
|
-
console.log(`ERROR visiting ${url} (${error.message.slice(0, 200)})`);
|
|
113
|
-
return {
|
|
114
|
-
success: false,
|
|
115
|
-
error: 'noVisit'
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
// Closes the current browser.
|
|
120
|
-
const browserClose = async () => {
|
|
121
|
-
if (browser) {
|
|
122
|
-
let contexts = browser.contexts();
|
|
123
|
-
for (const context of contexts) {
|
|
124
|
-
await context.close();
|
|
125
|
-
contexts = browser.contexts();
|
|
126
|
-
}
|
|
127
|
-
await browser.close();
|
|
128
|
-
browser = null;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
// Launches a browser, navigates to a URL, and returns browser data.
|
|
132
|
-
const launch = async (report, typeName, url, debug, waits, isLowMotion = false) => {
|
|
133
|
-
// If the specified browser type exists:
|
|
134
|
-
const browserType = playwright[typeName];
|
|
135
|
-
if (browserType) {
|
|
136
|
-
// Close the current browser, if any.
|
|
137
|
-
await browserClose();
|
|
138
|
-
// Launch a browser of the specified type.
|
|
139
|
-
const browserOptions = {
|
|
140
|
-
logger: {
|
|
141
|
-
isEnabled: () => false,
|
|
142
|
-
log: (name, severity, message) => console.log(message.slice(0, 100))
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
if (debug) {
|
|
146
|
-
browserOptions.headless = false;
|
|
147
|
-
}
|
|
148
|
-
if (waits) {
|
|
149
|
-
browserOptions.slowMo = waits;
|
|
150
|
-
}
|
|
151
|
-
browser = await browserType.launch(browserOptions)
|
|
152
|
-
// If the launch failed:
|
|
153
|
-
.catch(async error => {
|
|
154
|
-
console.log(`ERROR launching browser (${error.message.slice(0, 200)})`);
|
|
155
|
-
// Return this.
|
|
156
|
-
return {
|
|
157
|
-
success: false,
|
|
158
|
-
error: 'Browser launch failed'
|
|
159
|
-
};
|
|
160
|
-
});
|
|
161
|
-
// Open a context (i.e. browser tab), with reduced motion if specified.
|
|
162
|
-
const options = {reduceMotion: isLowMotion ? 'reduce' : 'no-preference'};
|
|
163
|
-
const browserContext = await browser.newContext(options);
|
|
164
|
-
// When a page (i.e. browser tab) is added to the browser context (i.e. browser window):
|
|
165
|
-
browserContext.on('page', async page => {
|
|
166
|
-
// If it emits a message:
|
|
167
|
-
page.on('console', msg => {
|
|
168
|
-
const msgText = msg.text();
|
|
169
|
-
let indentedMsg = '';
|
|
170
|
-
// If debugging is on:
|
|
171
|
-
if (debug) {
|
|
172
|
-
// Log a summary of the message on the console.
|
|
173
|
-
const parts = [msgText.slice(0, 75)];
|
|
174
|
-
if (msgText.length > 75) {
|
|
175
|
-
parts.push(msgText.slice(75, 150));
|
|
176
|
-
if (msgText.length > 150) {
|
|
177
|
-
const tail = msgText.slice(150).slice(-150);
|
|
178
|
-
if (msgText.length > 300) {
|
|
179
|
-
parts.push('...');
|
|
180
|
-
}
|
|
181
|
-
parts.push(tail.slice(0, 75));
|
|
182
|
-
if (tail.length > 75) {
|
|
183
|
-
parts.push(tail.slice(75));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
indentedMsg = parts.map(part => ` | ${part}`).join('\n');
|
|
188
|
-
console.log(`\n${indentedMsg}`);
|
|
189
|
-
}
|
|
190
|
-
// Add statistics on the message to the report.
|
|
191
|
-
const msgTextLC = msgText.toLowerCase();
|
|
192
|
-
const msgLength = msgText.length;
|
|
193
|
-
report.jobData.logCount++;
|
|
194
|
-
report.jobData.logSize += msgLength;
|
|
195
|
-
if (errorWords.some(word => msgTextLC.includes(word))) {
|
|
196
|
-
report.jobData.errorLogCount++;
|
|
197
|
-
report.jobData.errorLogSize += msgLength;
|
|
198
|
-
}
|
|
199
|
-
const msgLC = msgText.toLowerCase();
|
|
200
|
-
if (
|
|
201
|
-
msgText.includes('403') && (msgLC.includes('status')
|
|
202
|
-
|| msgLC.includes('prohibited'))
|
|
203
|
-
) {
|
|
204
|
-
report.jobData.prohibitedCount++;
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
// Open the first page of the context.
|
|
209
|
-
const page = await browserContext.newPage();
|
|
210
|
-
try {
|
|
211
|
-
// Wait until it is stable.
|
|
212
|
-
await page.waitForLoadState('domcontentloaded', {timeout: 5000});
|
|
213
|
-
// Navigate to the specified URL.
|
|
214
|
-
const navResult = await goTo(report, page, url, 15000, 'domcontentloaded');
|
|
215
|
-
// If the navigation succeeded:
|
|
216
|
-
if (navResult.success) {
|
|
217
|
-
// Update the name of the current browser type and store it in the page.
|
|
218
|
-
page.browserTypeName = typeName;
|
|
219
|
-
// Return the response of the target server, the browser context, and the page.
|
|
220
|
-
return {
|
|
221
|
-
success: true,
|
|
222
|
-
response: navResult.response,
|
|
223
|
-
browserContext,
|
|
224
|
-
page
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
// If the navigation failed:
|
|
228
|
-
if (navResult.error) {
|
|
229
|
-
// Return this.
|
|
230
|
-
return {
|
|
231
|
-
success: false,
|
|
232
|
-
error: 'Navigation failed'
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// If it fails to become stable after load:
|
|
237
|
-
catch(error) {
|
|
238
|
-
// Return this.
|
|
239
|
-
console.log(`ERROR: Blank page load in new tab timed out (${error.message})`);
|
|
240
|
-
return {
|
|
241
|
-
success: false,
|
|
242
|
-
error: 'Blank page load in new tab timed out'
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
// Otherwise, i.e. if it does not exist:
|
|
247
|
-
else {
|
|
248
|
-
// Return this.
|
|
249
|
-
console.log(`ERROR: Browser of type ${typeName} could not be launched`);
|
|
250
|
-
return {
|
|
251
|
-
success: false,
|
|
252
|
-
error: `${typeName} browser launch failed`
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
exports.browserClose = browserClose;
|
|
257
|
-
exports.getNonce = getNonce;
|
|
258
|
-
exports.goTo = goTo;
|
|
259
|
-
exports.launch = launch;
|