testaro 1.0.1 → 2.0.2
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/README.md +43 -46
- package/index.js +105 -139
- package/package.json +1 -1
- package/tests/wave.js +1 -1
package/README.md
CHANGED
|
@@ -8,36 +8,30 @@ Testaro is a collection of web accessibility tests.
|
|
|
8
8
|
|
|
9
9
|
The purpose of Testaro is to provide programmatic access to over 600 accessibility tests defined in several test packages and in Testaro itself.
|
|
10
10
|
|
|
11
|
-
Calling Testaro requires telling it which operations (including tests) to perform
|
|
11
|
+
Calling Testaro requires telling it which operations (including tests) to perform, which URLs to perform them on, and where to write its reports.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Testaro is derived from [Autotest](https://github.com/jrpool/autotest).
|
|
16
|
-
|
|
17
|
-
Testaro omits some functionalities of Autotest, such as:
|
|
18
|
-
- tests producing results intended to be human-inspected
|
|
19
|
-
- previous versions of scoring algorithms
|
|
20
|
-
- file operations for score aggregation, report revision, and HTML reports
|
|
21
|
-
- a web user interface
|
|
13
|
+
Testaro outputs progress messages and a list of reports to the standard output. It writes the reports in JSON format.
|
|
22
14
|
|
|
23
15
|
## System requirements
|
|
24
16
|
|
|
25
17
|
Version 14 or later of [Node.js](https://nodejs.org/en/).
|
|
26
18
|
|
|
27
|
-
A file system with a directory that Testaro has permission to read and write in.
|
|
19
|
+
A file system with a directory that the Testaro user has permission to read and write in.
|
|
28
20
|
|
|
29
|
-
##
|
|
21
|
+
## Dependencies
|
|
30
22
|
|
|
31
|
-
Testaro uses
|
|
23
|
+
Testaro uses:
|
|
24
|
+
- [Playwright](https://playwright.dev/) to launch browsers, perform user actions in them, and perform tests
|
|
25
|
+
- [pixelmatch](https://www.npmjs.com/package/pixelmatch) to measure motion
|
|
32
26
|
|
|
33
|
-
Testaro
|
|
27
|
+
Testaro includes some of its own accessibility tests. In addition, it performs the tests in:
|
|
34
28
|
- [accessibility-checker](https://www.npmjs.com/package/accessibility-checker) (the IBM Equal Access Accessibility Checker)
|
|
35
29
|
- [alfa](https://alfa.siteimprove.com/) (Siteimprove alfa)
|
|
36
30
|
- [Automated Accessibility Testing Tool](https://www.npmjs.com/package/aatt) (Paypal AATT, running HTML CodeSniffer)
|
|
37
31
|
- [axe-playwright](https://www.npmjs.com/package/axe-playwright) (Deque Axe-core)
|
|
38
32
|
- [WAVE API](https://wave.webaim.org/api/) (WebAIM WAVE)
|
|
39
33
|
|
|
40
|
-
As of
|
|
34
|
+
As of this version, the counts of tests in the packages referenced above were:
|
|
41
35
|
- AATT: 98
|
|
42
36
|
- Alfa: 103
|
|
43
37
|
- Axe-core: 138
|
|
@@ -57,11 +51,7 @@ The main directories containing code files are:
|
|
|
57
51
|
|
|
58
52
|
## Installation
|
|
59
53
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Then make its directory your working directory (`cd testaro`).
|
|
63
|
-
|
|
64
|
-
Before installing its dependencies, authorize yourself to install them. You presumably are already authorized to install dependencies from `https://npmjs.org`. But some of the Testaro dependencies are not there, but rather at `https://npm.pkg.github.com`. If you are not already authorized to install packages from that registry, authorize yourself as follows:
|
|
54
|
+
Some of the dependencies of Testaro are published as Github packages. Installing Testaro therefore requires you to be authorized to read Github packages. If you do not yet have that authorization, you can give it to yourself as follows:
|
|
65
55
|
- Log in at [Github](https://github.com).
|
|
66
56
|
- From your avatar in the upper-right corner, choose “Settings”.
|
|
67
57
|
- In the left sidebar, choose “Developer settings”.
|
|
@@ -72,7 +62,7 @@ Before installing its dependencies, authorize yourself to install them. You pres
|
|
|
72
62
|
- Check the checkbox `read:packages`.
|
|
73
63
|
- Activate the button “Generate token”.
|
|
74
64
|
- Copy the generated token (you can use the copy icon next to it).
|
|
75
|
-
- In
|
|
65
|
+
- In the local directory of the project into which you will install Testaro, create a file named `.npmrc`, unless it already exists.
|
|
76
66
|
- Populate the `.npmrc` file with the following statements, replacing `abc` with your Github username and `xyz` with the token that you copied:
|
|
77
67
|
|
|
78
68
|
```bash
|
|
@@ -80,18 +70,15 @@ Before installing its dependencies, authorize yourself to install them. You pres
|
|
|
80
70
|
//npm.pkg.github.com/:username=abc
|
|
81
71
|
//npm.pkg.github.com/:_authToken=xyz
|
|
82
72
|
```
|
|
73
|
+
Once you have done that, you can install Testaro as you would install any `npm` package.
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## Configuration
|
|
75
|
+
## Payment
|
|
87
76
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
Use of WAVE requires you to have a WAVE API key (see the link above under “Technologies”).
|
|
77
|
+
All of the tests that Testaro can perform are free of cost, except those in the WAVE package. WebAIM requires an API key for those tests. If you wish to have Testaro perform the WAVE tests, you will need to have a WAVE API key. Visit the URL above in order to obtain your key. It costs 1 to 3 credits to perform the WAVE tests on one URL. WebAIM gives you 100 credits without cost, before you need to begin paying.
|
|
91
78
|
|
|
92
79
|
## Specification
|
|
93
80
|
|
|
94
|
-
To use Testaro, you must specify what it should do. You do this
|
|
81
|
+
To use Testaro, you must specify what it should do. You do this with a script and optionally a batch.
|
|
95
82
|
|
|
96
83
|
## Scripts
|
|
97
84
|
|
|
@@ -145,9 +132,9 @@ If the `strict` property is `true`, Testaro will accept redirections that add or
|
|
|
145
132
|
|
|
146
133
|
### Commands
|
|
147
134
|
|
|
148
|
-
####
|
|
135
|
+
#### Introduction
|
|
149
136
|
|
|
150
|
-
The `commands` property’s value is an array of
|
|
137
|
+
The `commands` property’s value is an array of command objects.
|
|
151
138
|
|
|
152
139
|
Each command has a `type` property and optionally has a `name` property (used in branching, described below). It must or may have other properties, depending on the value of `type`.
|
|
153
140
|
|
|
@@ -399,8 +386,8 @@ The second item in each array, if there are 3 items in the array, is an operator
|
|
|
399
386
|
## Batches
|
|
400
387
|
|
|
401
388
|
There are two ways to use a script to give instructions to Testaro:
|
|
402
|
-
- The script can be the complete specification of the
|
|
403
|
-
- The script can specify the operations to perform, and a _batch_ can specify which
|
|
389
|
+
- The script can be the complete specification of the operations to perform and the URLs to perform them on.
|
|
390
|
+
- The script can specify the operations to perform, and a _batch_ can specify which URLs to perform them on.
|
|
404
391
|
|
|
405
392
|
A batch is a JSON file with this format:
|
|
406
393
|
|
|
@@ -420,9 +407,9 @@ A batch is a JSON file with this format:
|
|
|
420
407
|
}
|
|
421
408
|
```
|
|
422
409
|
|
|
423
|
-
When you combine a script with a batch, Testaro performs the script, replacing the `which` and `what` properties of all `url` commands with the values in the first object in the `hosts` array, then again with the values in the second object, and so on. Those replacements also occur in the inserted extra `url` commands mentioned above.
|
|
410
|
+
When you combine a script with a batch, Testaro performs the script, replacing the `which` and `what` properties of all `url` commands with the values in the first object in the `hosts` array of the batch, then again with the values in the second `host` object, and so on. Those replacements also occur in the inserted extra `url` commands mentioned above.
|
|
424
411
|
|
|
425
|
-
A batch offers an efficient way to perform a uniform set of
|
|
412
|
+
A batch offers an efficient way to perform a uniform set of operations on every host in a set of hosts. In this way you can run the same set of tests on multiple web pages.
|
|
426
413
|
|
|
427
414
|
A no-batch script offers a way to carry out a complex operation, which can include navigating from one host to another, which is not possible with a batch. Any `url` commands are performed as-is, without changes to their URLs.
|
|
428
415
|
|
|
@@ -436,29 +423,29 @@ Testaro also writes to standard output a list of the names of the files containi
|
|
|
436
423
|
|
|
437
424
|
### Invocation
|
|
438
425
|
|
|
439
|
-
To run Testaro, create
|
|
426
|
+
To run Testaro, create an _invocation_ file like this:
|
|
440
427
|
|
|
441
428
|
```javascript
|
|
442
429
|
const options = {
|
|
443
|
-
reports:
|
|
444
|
-
|
|
445
|
-
|
|
430
|
+
reports: `${__readdir}/path/to/reports/directory`,
|
|
431
|
+
batch: `${__readdir}/path/to/batch/file.json`,
|
|
432
|
+
script: `${__readdir}/path/to/script/file.json`
|
|
446
433
|
};
|
|
447
|
-
const {handleRequest} = require('
|
|
434
|
+
const {handleRequest} = require('testaro');
|
|
448
435
|
handleRequest(options);
|
|
449
436
|
```
|
|
450
437
|
|
|
451
|
-
The `batch` option is optional. If there is no batch, it
|
|
438
|
+
The `batch` option is optional. If there is no batch, omit it.
|
|
452
439
|
|
|
453
|
-
The paths
|
|
440
|
+
The paths are relative to the directory containing the file. So, if the script file is `script.json` and is in the same directory as the invocation file, the path would be `${__readdir}/script.json`.
|
|
454
441
|
|
|
455
|
-
If the file
|
|
442
|
+
If the invocation file is named `runTestaro.js`, execute it with the statement `node runTestaro`.
|
|
456
443
|
|
|
457
444
|
### Environment variables
|
|
458
445
|
|
|
459
|
-
If a `wave` test is included in the script, an environment variable named `
|
|
446
|
+
If a `wave` test is included in the script, an environment variable named `TESTARO_WAVE_KEY` must exist, with your WAVE API key as its value.
|
|
460
447
|
|
|
461
|
-
Before executing a Testaro script, you can also set the environment variables `
|
|
448
|
+
Before executing a Testaro script, you can optionally also set the environment variables `TESTARO_DEBUG` (to `'true'` or anything else) and/or `TESTARO_WAITS` (to a non-negative integer). The effects of these variables are described in the `index.js` file.
|
|
462
449
|
|
|
463
450
|
## Contribution
|
|
464
451
|
|
|
@@ -466,7 +453,7 @@ You can define additional Testaro commands and functionality. Contributions are
|
|
|
466
453
|
|
|
467
454
|
## Accessibility principles
|
|
468
455
|
|
|
469
|
-
The rationales motivating the Testaro-defined tests and scoring procs can be found in comments within the files of those tests and procs, in the `tests` and `procs/score` directories. Unavoidably, each test is opinionated. Testaro itself, however, can accommodate other tests representing different opinions. Testaro is intended to be neutral with respect to questions such as the criteria for accessibility, the severities of accessibility issues, whether accessibility
|
|
456
|
+
The rationales motivating the Testaro-defined tests and scoring procs can be found in comments within the files of those tests and procs, in the `tests` and `procs/score` directories. Unavoidably, each test is opinionated. Testaro itself, however, can accommodate other tests representing different opinions. Testaro is intended to be neutral with respect to questions such as the criteria for accessibility, the severities of accessibility issues, whether accessibility is binary or graded, and the distinction between usability and accessibility.
|
|
470
457
|
|
|
471
458
|
### Future work
|
|
472
459
|
|
|
@@ -491,12 +478,22 @@ The Playwright “Receives Events” actionability check does **not** check whet
|
|
|
491
478
|
|
|
492
479
|
### Test-package duplication
|
|
493
480
|
|
|
494
|
-
Test packages sometimes do redundant testing, in that two or more packages test for the same issues. But such duplications are not necessarily perfect. Therefore, the scoring procs currently
|
|
481
|
+
Test packages sometimes do redundant testing, in that two or more packages test for the same issues. But such duplications are not necessarily perfect. Therefore, the scoring procs currently defined by Testaro do not select a single package to test for a single issue. Instead, they allow all packages to test for all the issues they can test for, but decrease the weights placed on issues that multiple packages test for. The more packages test for an issue, the smaller the weight placed on each package’s finding of that issue.
|
|
495
482
|
|
|
496
483
|
## Repository exclusions
|
|
497
484
|
|
|
498
485
|
The files in the `temp` directory are presumed ephemeral and are not tracked by `git`. When tests require temporary files to be written, Testaro writes them there.
|
|
499
486
|
|
|
487
|
+
## Origin
|
|
488
|
+
|
|
489
|
+
Testaro is derived from [Autotest](https://github.com/jrpool/autotest), which in turn is derived from accessibility test investigations beginning in 2018.
|
|
490
|
+
|
|
491
|
+
Testaro omits some functionalities of Autotest, such as:
|
|
492
|
+
- tests producing results intended to be human-inspected
|
|
493
|
+
- previous versions of scoring algorithms
|
|
494
|
+
- file operations for score aggregation, report revision, and HTML reports
|
|
495
|
+
- a web user interface
|
|
496
|
+
|
|
500
497
|
## Etymology
|
|
501
498
|
|
|
502
499
|
“Testaro” means “collection of tests” in Esperanto.
|
package/index.js
CHANGED
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
testaro main script.
|
|
4
4
|
*/
|
|
5
5
|
// ########## IMPORTS
|
|
6
|
-
// Module to access files.
|
|
7
|
-
const fs = require('fs/promises');
|
|
8
6
|
// Requirements for commands.
|
|
9
7
|
const {commands} = require('./commands');
|
|
10
8
|
// ########## CONSTANTS
|
|
11
9
|
// Set DEBUG environment variable to 'true' to add debugging features.
|
|
12
|
-
const debug = process.env.
|
|
10
|
+
const debug = process.env.TESTARO_DEBUG === 'true';
|
|
13
11
|
// Set WAITS environment variable to a positive number to insert delays (in ms).
|
|
14
|
-
const waits = Number.parseInt(process.env.
|
|
12
|
+
const waits = Number.parseInt(process.env.TESTARO_WAITS) || 0;
|
|
15
13
|
// CSS selectors for targets of moves.
|
|
16
14
|
const moves = {
|
|
17
15
|
button: 'button',
|
|
@@ -198,52 +196,35 @@ const isValidScript = script => {
|
|
|
198
196
|
};
|
|
199
197
|
// Validates a batch.
|
|
200
198
|
const isValidBatch = batch => {
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
199
|
+
// If the batch exists:
|
|
200
|
+
if (batch) {
|
|
201
|
+
// Get its data.
|
|
202
|
+
const {what, hosts} = batch;
|
|
203
|
+
// Return whether the batch is valid:
|
|
204
|
+
return what
|
|
205
|
+
&& hosts
|
|
206
|
+
&& typeof what === 'string'
|
|
207
|
+
&& Array.isArray(hosts)
|
|
208
|
+
&& hosts.every(host => host.which && host.what && isURL(host.which));
|
|
209
|
+
}
|
|
210
|
+
// Otherwise, i.e. if the batch does not exist:
|
|
211
|
+
else {
|
|
212
|
+
// Return that it is valid, because it is optional.
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
210
215
|
};
|
|
211
|
-
// Validates
|
|
212
|
-
const isValidReports = reports =>
|
|
216
|
+
// Validates an initialized reports array.
|
|
217
|
+
const isValidReports = reports => Array.isArray(reports) && ! reports.length;
|
|
218
|
+
// Validates an initialized log array.
|
|
219
|
+
const isValidLog = log => Array.isArray(log) && ! log.length;
|
|
213
220
|
// Validates an options object.
|
|
214
221
|
const isValidOptions = async options => {
|
|
215
222
|
if (options) {
|
|
216
|
-
const {script, batch, reports} = options;
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (reports && isValidReports(reports)) {
|
|
222
|
-
if (batch) {
|
|
223
|
-
const batchJSON = await fs.readFile(batch, 'utf8');
|
|
224
|
-
const batchObj = JSON.parse(batchJSON);
|
|
225
|
-
if (isValidBatch(batchObj)) {
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
return true;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
223
|
+
const {script, batch, log, reports} = options;
|
|
224
|
+
return isValidScript(script)
|
|
225
|
+
&& isValidBatch(batch)
|
|
226
|
+
&& isValidLog(log)
|
|
227
|
+
&& isValidReports(reports);
|
|
247
228
|
}
|
|
248
229
|
else {
|
|
249
230
|
return false;
|
|
@@ -580,6 +561,13 @@ const isTrue = (object, specs) => {
|
|
|
580
561
|
}
|
|
581
562
|
return [actual, satisfied];
|
|
582
563
|
};
|
|
564
|
+
// Adds an error result to an act.
|
|
565
|
+
const waitError = (page, act, error, what) => {
|
|
566
|
+
console.log(`ERROR waiting for ${what} (${error.message})`);
|
|
567
|
+
act.result = {url: page.url()};
|
|
568
|
+
act.result.error = `ERROR waiting for ${what}`;
|
|
569
|
+
return false;
|
|
570
|
+
};
|
|
583
571
|
// Recursively performs the commands in a report.
|
|
584
572
|
const doActs = async (report, actIndex, page) => {
|
|
585
573
|
const {acts} = report;
|
|
@@ -658,25 +646,19 @@ const doActs = async (report, actIndex, page) => {
|
|
|
658
646
|
else if (act.type === 'wait') {
|
|
659
647
|
const {what, which} = act;
|
|
660
648
|
console.log(`>> for ${what} to include “${which}”`);
|
|
661
|
-
const waitError = (error, what) => {
|
|
662
|
-
console.log(`ERROR waiting for ${what} (${error.message})`);
|
|
663
|
-
act.result = {url: page.url()};
|
|
664
|
-
act.result.error = `ERROR waiting for ${what}`;
|
|
665
|
-
return false;
|
|
666
|
-
};
|
|
667
649
|
// Wait 5 seconds for the specified text to appear in the specified place.
|
|
668
650
|
let successJSHandle;
|
|
669
651
|
if (act.what === 'url') {
|
|
670
652
|
successJSHandle = await page.waitForFunction(
|
|
671
653
|
text => document.URL.includes(text), act.which, {timeout: 5000}
|
|
672
654
|
)
|
|
673
|
-
.catch(error => waitError(error, 'URL'));
|
|
655
|
+
.catch(error => waitError(page, act, error, 'URL'));
|
|
674
656
|
}
|
|
675
657
|
else if (act.what === 'title') {
|
|
676
658
|
successJSHandle = await page.waitForFunction(
|
|
677
659
|
text => document.title.includes(text), act.which, {timeout: 5000}
|
|
678
660
|
)
|
|
679
|
-
.catch(error => waitError(error, 'title'));
|
|
661
|
+
.catch(error => waitError(page, act, error, 'title'));
|
|
680
662
|
}
|
|
681
663
|
else if (act.what === 'body') {
|
|
682
664
|
successJSHandle = await page.waitForFunction(
|
|
@@ -685,7 +667,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
685
667
|
return innerText.includes(matchText);
|
|
686
668
|
}, which, {timeout: 5000}
|
|
687
669
|
)
|
|
688
|
-
.catch(error => waitError(error, 'body'));
|
|
670
|
+
.catch(error => waitError(page, act, error, 'body'));
|
|
689
671
|
}
|
|
690
672
|
if (successJSHandle) {
|
|
691
673
|
act.result = {url: page.url()};
|
|
@@ -1031,7 +1013,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
1031
1013
|
}
|
|
1032
1014
|
// Otherwise, i.e. if the move is unknown, add the failure to the act.
|
|
1033
1015
|
else {
|
|
1034
|
-
//
|
|
1016
|
+
// Report the error.
|
|
1035
1017
|
act.result = 'ERROR: move unknown';
|
|
1036
1018
|
}
|
|
1037
1019
|
}
|
|
@@ -1085,12 +1067,6 @@ const doActs = async (report, actIndex, page) => {
|
|
|
1085
1067
|
// Perform the remaining acts.
|
|
1086
1068
|
await doActs(report, actIndex + 1, page);
|
|
1087
1069
|
}
|
|
1088
|
-
// Otherwise, i.e. if no more acts are to be performed:
|
|
1089
|
-
else {
|
|
1090
|
-
// Return a Promise.
|
|
1091
|
-
console.log('All commands performed');
|
|
1092
|
-
return Promise.resolve('');
|
|
1093
|
-
}
|
|
1094
1070
|
};
|
|
1095
1071
|
// Performs the commands in a script and returns a report.
|
|
1096
1072
|
const doScript = async report => {
|
|
@@ -1100,7 +1076,7 @@ const doScript = async report => {
|
|
|
1100
1076
|
report.presses = 0;
|
|
1101
1077
|
report.amountRead = 0;
|
|
1102
1078
|
report.testTimes = [];
|
|
1103
|
-
// Perform the specified acts
|
|
1079
|
+
// Perform the specified acts.
|
|
1104
1080
|
await doActs(report, 0, null);
|
|
1105
1081
|
// Close the browser.
|
|
1106
1082
|
await closeBrowser();
|
|
@@ -1129,9 +1105,47 @@ const doScript = async report => {
|
|
|
1129
1105
|
}
|
|
1130
1106
|
}
|
|
1131
1107
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1108
|
+
};
|
|
1109
|
+
// Recursively performs commands on the hosts of a batch.
|
|
1110
|
+
const doBatch = async (options, reportTemplate, hostIndex = 0) => {
|
|
1111
|
+
const {hosts} = options.batch;
|
|
1112
|
+
const host = hosts[hostIndex];
|
|
1113
|
+
// If the specified host exists:
|
|
1114
|
+
if (host) {
|
|
1115
|
+
// Copy the report for it.
|
|
1116
|
+
const hostReport = JSON.parse(JSON.stringify(reportTemplate));
|
|
1117
|
+
// Copy the properties of the specified host to all url acts.
|
|
1118
|
+
hostReport.acts.forEach(act => {
|
|
1119
|
+
if (act.type === 'url') {
|
|
1120
|
+
act.which = host.which;
|
|
1121
|
+
act.what = host.what;
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
// Perform the commands on the host.
|
|
1125
|
+
await doScript(hostReport);
|
|
1126
|
+
// Process the remaining hosts.
|
|
1127
|
+
await doBatch(options, reportTemplate, hostIndex + 1);
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
// Performs a script, with or without a batch.
|
|
1131
|
+
const doScriptOrBatch = async (options, reportTemplate) => {
|
|
1132
|
+
// If there is a batch:
|
|
1133
|
+
if (options.batch) {
|
|
1134
|
+
// Perform the script on all the hosts in the batch.
|
|
1135
|
+
console.log('Starting batch');
|
|
1136
|
+
await doBatch(options, reportTemplate);
|
|
1137
|
+
}
|
|
1138
|
+
// Otherwise, i.e. if there is no batch:
|
|
1139
|
+
else {
|
|
1140
|
+
// Perform the script.
|
|
1141
|
+
console.log('Starting no-batch script');
|
|
1142
|
+
await doScript(reportTemplate);
|
|
1143
|
+
}
|
|
1144
|
+
// Add an end time to the log.
|
|
1145
|
+
options.log.push({
|
|
1146
|
+
event: 'endTime',
|
|
1147
|
+
value: ((new Date()).toISOString().slice(0, 19))
|
|
1148
|
+
});
|
|
1135
1149
|
};
|
|
1136
1150
|
// Injects url commands into a report where necessary to undo DOM changes.
|
|
1137
1151
|
const injectURLCommands = commands => {
|
|
@@ -1163,84 +1177,36 @@ const injectURLCommands = commands => {
|
|
|
1163
1177
|
}
|
|
1164
1178
|
}
|
|
1165
1179
|
};
|
|
1166
|
-
// Recursively performs commands on the hosts of a batch.
|
|
1167
|
-
const doBatch = async (report, batch, hostIndex, reportList) => {
|
|
1168
|
-
const {hosts} = batch;
|
|
1169
|
-
const host = hosts[hostIndex];
|
|
1170
|
-
// If the specified host exists:
|
|
1171
|
-
if (host) {
|
|
1172
|
-
// Copy the report for it.
|
|
1173
|
-
const hostReport = JSON.parse(JSON.stringify(report));
|
|
1174
|
-
// Copy the properties of the specified host to all url acts.
|
|
1175
|
-
hostReport.acts.forEach(act => {
|
|
1176
|
-
if (act.type === 'url') {
|
|
1177
|
-
act.which = host.which;
|
|
1178
|
-
act.what = host.what;
|
|
1179
|
-
}
|
|
1180
|
-
});
|
|
1181
|
-
// Record the batch size in the report.
|
|
1182
|
-
batch.size = hosts.length;
|
|
1183
|
-
delete batch.hosts;
|
|
1184
|
-
// Perform the commands on the host and produce a report.
|
|
1185
|
-
const finalReport = await doScript(hostReport);
|
|
1186
|
-
const hostSuffix = hostIndex > -1 ? `-${hostIndex.toString().padStart(3, '0')}` : '';
|
|
1187
|
-
const reportName = `report-${finalReport.timeStamp}${hostSuffix}.json`;
|
|
1188
|
-
finalReport.reportName = reportName;
|
|
1189
|
-
const reportPath = `${report.options.reports}/${reportName}`;
|
|
1190
|
-
// Save the report.
|
|
1191
|
-
await fs.writeFile(reportPath, JSON.stringify(finalReport, null, 2));
|
|
1192
|
-
// Send the report name to the console.
|
|
1193
|
-
reportList.push(reportName);
|
|
1194
|
-
// Process the remaining hosts.
|
|
1195
|
-
return await doBatch(hostReport, hostIndex + 1, reportList);
|
|
1196
|
-
}
|
|
1197
|
-
// Otherwise, i.e. if the hosts have been exhausted:
|
|
1198
|
-
else {
|
|
1199
|
-
// Return the list of reports.
|
|
1200
|
-
return reportList;
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1203
|
-
// Performs a script.
|
|
1204
|
-
const doScriptOrBatch = async report => {
|
|
1205
|
-
// If the report has an options property:
|
|
1206
|
-
const {options} = report;
|
|
1207
|
-
// If there is a batch:
|
|
1208
|
-
if (options.batch) {
|
|
1209
|
-
// Perform the script on all the hosts in the batch and return a list of the reports.
|
|
1210
|
-
const batchJSON = await fs.readFile(options.batch, 'utf8');
|
|
1211
|
-
const batch = JSON.parse(batchJSON);
|
|
1212
|
-
const reportList = await doBatch(report, batch, 0, []);
|
|
1213
|
-
console.log(reportList);
|
|
1214
|
-
}
|
|
1215
|
-
// Otherwise, i.e. if there is no batch:
|
|
1216
|
-
else {
|
|
1217
|
-
// Perform the script and save the report.
|
|
1218
|
-
const finalReport = await doScript(report);
|
|
1219
|
-
const reportName = `report-${finalReport.timeStamp}.json`;
|
|
1220
|
-
finalReport.reportName = reportName;
|
|
1221
|
-
const reportPath = `${report.options.reports}/${reportName}`;
|
|
1222
|
-
// Save the report.
|
|
1223
|
-
await fs.writeFile(reportPath, JSON.stringify(finalReport, null, 2));
|
|
1224
|
-
// Send the report name to the console.
|
|
1225
|
-
console.log(`Report ${reportName} saved`);
|
|
1226
|
-
}
|
|
1227
|
-
};
|
|
1228
1180
|
// Handles a request.
|
|
1229
1181
|
exports.handleRequest = async options => {
|
|
1230
1182
|
// If the options object is valid:
|
|
1231
1183
|
if(isValidOptions(options)) {
|
|
1232
|
-
//
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1184
|
+
// Add a start time and a timeStamp to the log.
|
|
1185
|
+
options.log.push(
|
|
1186
|
+
{
|
|
1187
|
+
event: 'startTime',
|
|
1188
|
+
value: ((new Date()).toISOString().slice(0, 19))
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
event: 'timeStamp',
|
|
1192
|
+
value: Math.floor((Date.now() - Date.UTC(2022, 3)) / 10000).toString(36)
|
|
1193
|
+
}
|
|
1194
|
+
);
|
|
1195
|
+
// Add the batch size to the log if there is a batch.
|
|
1196
|
+
if (options.batch) {
|
|
1197
|
+
options.log.push({
|
|
1198
|
+
event: 'batchSize',
|
|
1199
|
+
value: options.batch.hosts.length
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
// Create a report template, containing a copy of the commands as its acts.
|
|
1203
|
+
const reportTemplate = {
|
|
1204
|
+
acts: JSON.parse(JSON.stringify(options.script.commands))
|
|
1205
|
+
};
|
|
1239
1206
|
// Inject url acts where necessary to undo DOM changes.
|
|
1240
|
-
injectURLCommands(
|
|
1241
|
-
// Perform the script, with or without a batch,
|
|
1242
|
-
await doScriptOrBatch(
|
|
1243
|
-
console.log('Request handled');
|
|
1207
|
+
injectURLCommands(reportTemplate.acts);
|
|
1208
|
+
// Perform the script, with or without a batch, asynchronously adding to the log and reports.
|
|
1209
|
+
await doScriptOrBatch(options, reportTemplate);
|
|
1244
1210
|
}
|
|
1245
1211
|
else {
|
|
1246
1212
|
console.log('ERROR: options missing or invalid');
|
package/package.json
CHANGED
package/tests/wave.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
const https = require('https');
|
|
8
8
|
exports.reporter = async (page, reportType) => {
|
|
9
|
-
const waveKey = process.env.
|
|
9
|
+
const waveKey = process.env.TESTARO_WAVE_KEY;
|
|
10
10
|
// Get the data from a WAVE test.
|
|
11
11
|
const data = await new Promise(resolve => {
|
|
12
12
|
https.get(
|