testaro 74.2.3 → 75.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 +2 -0
- package/README.md +52 -59
- package/actSpecs-doc.md +25 -1
- package/actSpecs.js +6 -4
- package/package.json +1 -1
- package/procs/catalog.js +0 -1
- package/procs/dateTime.js +4 -2
- package/procs/doActs.js +15 -8
- package/procs/launch.js +0 -1
- package/procs/shoot.js +48 -43
- package/testaro/adbID.js +2 -2
- package/testaro/allCapStyle.js +2 -2
- package/testaro/allCaps.js +3 -3
- package/testaro/allHidden.js +2 -2
- package/testaro/allSlanted.js +2 -2
- package/testaro/altScheme.js +2 -2
- package/testaro/attVal.js +2 -2
- package/testaro/autocomplete.js +3 -2
- package/testaro/bulk.js +2 -2
- package/testaro/buttonMenu.js +3 -3
- package/testaro/captionLoc.js +2 -2
- package/testaro/datalistRef.js +2 -2
- package/testaro/distortion.js +2 -2
- package/testaro/docType.js +1 -1
- package/testaro/dupAtt.js +2 -2
- package/testaro/embAc.js +2 -2
- package/testaro/focAll.js +2 -2
- package/testaro/focAndOp.js +2 -2
- package/testaro/focInd.js +2 -2
- package/testaro/focVis.js +2 -2
- package/testaro/headEl.js +2 -2
- package/testaro/headingAmb.js +2 -2
- package/testaro/hovInd.js +2 -2
- package/testaro/hover.js +2 -2
- package/testaro/hr.js +2 -2
- package/testaro/imageLink.js +2 -2
- package/testaro/labClash.js +2 -2
- package/testaro/legendLoc.js +2 -2
- package/testaro/lineHeight.js +2 -2
- package/testaro/linkAmb.js +2 -2
- package/testaro/linkExt.js +2 -2
- package/testaro/linkOldAtt.js +2 -2
- package/testaro/linkTo.js +2 -2
- package/testaro/linkUl.js +2 -2
- package/testaro/miniText.js +2 -2
- package/testaro/motion.js +6 -6
- package/testaro/nonTable.js +2 -2
- package/testaro/optRoleSel.js +2 -2
- package/testaro/phOnly.js +2 -2
- package/testaro/pseudoP.js +2 -2
- package/testaro/radioSet.js +2 -2
- package/testaro/role.js +2 -2
- package/testaro/secHeading.js +2 -2
- package/testaro/shoot0.js +11 -2
- package/testaro/shoot1.js +12 -4
- package/testaro/styleDiff.js +2 -2
- package/testaro/tabNav.js +2 -2
- package/testaro/targetsNear.js +2 -2
- package/testaro/textNodes.js +1 -1
- package/testaro/textSem.js +2 -2
- package/testaro/titledEl.js +2 -2
- package/testaro/zIndex.js +2 -2
- package/tests/testaro.js +2 -10
package/LICENSE
CHANGED
|
@@ -2,6 +2,8 @@ MIT License
|
|
|
2
2
|
|
|
3
3
|
© 2021–2025 CVS Health and/or one of its affiliates. All rights reserved.
|
|
4
4
|
© 2025–2026 Jonathan Robert Pool.
|
|
5
|
+
© 2025 Juan S. Casado.
|
|
6
|
+
© 2026 Jeff Witt.
|
|
5
7
|
|
|
6
8
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
7
9
|
|
package/README.md
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Ensemble testing for web accessibility
|
|
4
4
|
|
|
5
|
-
## Breaking change
|
|
5
|
+
## Breaking change notices
|
|
6
6
|
|
|
7
|
-
Version
|
|
7
|
+
Version 75.0.0 introduced a breaking change in the methods for making screenshots of web pages.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Revision of this `README` document to reflect the latest version is in progress but is incomplete.
|
|
9
|
+
Version 68.0.0 introduced a breaking change in the format of reports.
|
|
12
10
|
|
|
13
11
|
## Purposes
|
|
14
12
|
|
|
15
|
-
Testaro is an application that performs ensemble testing of web pages,
|
|
13
|
+
Testaro is an application that performs ensemble testing of web pages for accessibility, usability, and conformity to HTML and CSS specifications.
|
|
16
14
|
|
|
17
15
|
The purposes of Testaro are to:
|
|
18
16
|
|
|
@@ -28,20 +26,20 @@ Testaro is described in two papers:
|
|
|
28
26
|
|
|
29
27
|
## Functionality
|
|
30
28
|
|
|
31
|
-
Testaro performs tasks defined by a _job_. Typically, a job identifies the URL of a web page and asks Testaro to call an ensemble of tools to test the page. Testaro adds the results of the testing to the job, thereby converting the job
|
|
29
|
+
Testaro performs tasks defined by a _job_. Typically, a job identifies the URL of a web page and asks Testaro to call an ensemble of tools to test the page. Testaro adds the results of the testing to the job, thereby converting the job to a _report_.
|
|
32
30
|
|
|
33
31
|
Testaro can be given a job to perform, in which case it performs the job, delivers the report, and quits.
|
|
34
32
|
|
|
35
33
|
Alternatively, testaro can run as a daemon, polling a server or a directory for new jobs and performing them when they are provided or when they appear in the directory.
|
|
36
34
|
|
|
37
|
-
A practical application that leverages Testaro will use other software to prepare jobs, schedule them, post-process the reports as needed, and manage the report files. Some utilities for such purposes can be found in the [Testilo project](https://www.npmjs.com/package/testilo). One application that leverages Testaro for a web service is [Kilotest](https://
|
|
35
|
+
A practical application that leverages Testaro will use other software to prepare jobs, schedule them, post-process the reports as needed, and manage the report files. Some utilities for such purposes can be found in the [Testilo project](https://www.npmjs.com/package/testilo). One application that leverages Testaro for a web service is [Kilotest](https://www.npmjs.com/package/@jrpool/kilotest).
|
|
38
36
|
|
|
39
37
|
## Dependencies
|
|
40
38
|
|
|
41
39
|
Testaro uses:
|
|
42
40
|
|
|
43
41
|
- [Playwright](https://playwright.dev/) to launch browsers, perform user actions in them, and perform tests
|
|
44
|
-
- [playwright-extra](https://www.npmjs.com/package/playwright-extra) and [puppeteer-extra-plugin-stealth](https://www.npmjs.com/package/puppeteer-extra-plugin-stealth) to make a Playwright-controlled browser more indistinguishable from a human-operated browser and thus make
|
|
42
|
+
- [playwright-extra](https://www.npmjs.com/package/playwright-extra) and [puppeteer-extra-plugin-stealth](https://www.npmjs.com/package/puppeteer-extra-plugin-stealth) to make a Playwright-controlled browser more indistinguishable from a human-operated browser and thus make its requests more likely to succeed
|
|
45
43
|
- [playwright-dompath](https://www.npmjs.com/package/playwright-dompath) to retrieve XPaths of elements
|
|
46
44
|
- [BlazeDiff](https://blazediff.dev/) to measure motion
|
|
47
45
|
- [dotenv](https://www.npmjs.com/package/dotenv) to load environment variables
|
|
@@ -68,16 +66,16 @@ As shown, Testaro is not only an integrator but also one of the integrated tools
|
|
|
68
66
|
The main concepts of Testaro are:
|
|
69
67
|
|
|
70
68
|
- `job`: a document that tells Testaro what to do.
|
|
71
|
-
- `act`: one step in a job
|
|
69
|
+
- `act`: one step in a job.
|
|
72
70
|
- `report`: a job that Testaro has added results to.
|
|
73
|
-
- `tool`: one of the testing applications in the ensemble
|
|
71
|
+
- `tool`: one of the testing applications in the ensemble assembled by Testaro.
|
|
74
72
|
- `rule`: a success or failure criterion defined by a tool (currently about 1300 across all tools).
|
|
75
73
|
- `test`: the software that a tool uses to apply a rule.
|
|
76
74
|
- `target`: a web page that a job tells Testaro to test.
|
|
77
|
-
- `result`: the information that Testaro adds to a job to describe the
|
|
78
|
-
- `native result`: the
|
|
79
|
-
- `standard result`: the
|
|
80
|
-
- `catalog`: a collection of data on the HTML elements relevant to one or more tests.
|
|
75
|
+
- `result`: the information that Testaro adds to a job to describe the outcomes of the tests of a tool.
|
|
76
|
+
- `native result`: the outcomes of the tests of a tool in exactly or approximately the original form.
|
|
77
|
+
- `standard result`: the outcomes of the tests of a tool in a uniform Testaro-defined form.
|
|
78
|
+
- `catalog`: a collection of data on the HTML elements of a target relevant to one or more tests.
|
|
81
79
|
|
|
82
80
|
## System requirements
|
|
83
81
|
|
|
@@ -99,7 +97,7 @@ One way to cope with this prohibition is to configure Playwright and Puppeteer t
|
|
|
99
97
|
- For the `qualWeb` tool, this is done in the Testaro `tests/qualweb.js` file, where the `qualWeb.start` method is called with an options argument. Its `args` array property is modified to include `'--no-sandbox'` and `'--disable-setuid-sandbox'`.
|
|
100
98
|
- The `ibm` tool, too, can launch a Puppeteer `chromium` browser, if page content instead of a Playwright page is passed to the `accessibilityChecker.getCompliance` method, or if the implementation of the tool is changed in the future. For anticipation of such a case, the Testaro `aceconfig.js` file is modified. That file defines a `module.exports` object with a `puppeteerArgs` property, and, `--no-sandbox` and `--disable-setuid-sandbox` are added to its array value.
|
|
101
99
|
|
|
102
|
-
Non-sandboxed browsers are less secure than sandboxed ones, particularly when there is no restriction on who can use Testaro and what web pages they can test with it.
|
|
100
|
+
Non-sandboxed browsers are less secure than sandboxed ones, particularly when there is no restriction on who can use Testaro and what targets (web pages) they can test with it.
|
|
103
101
|
|
|
104
102
|
### Option B
|
|
105
103
|
|
|
@@ -115,13 +113,11 @@ EOF
|
|
|
115
113
|
sudo sysctl --system
|
|
116
114
|
```
|
|
117
115
|
|
|
118
|
-
This
|
|
119
|
-
|
|
120
|
-
## Installation
|
|
116
|
+
This application implements option B.
|
|
121
117
|
|
|
122
|
-
|
|
118
|
+
## Installation an independent application
|
|
123
119
|
|
|
124
|
-
To install Testaro as an independent application, clone the [Testaro repository](https://github.com/jrpool/testaro). To ensure that the binary browsers of its Playwright dependency get installed, execute `(p)npx playwright install` after executing `(p)npm install`.
|
|
120
|
+
To install Testaro as an independent application, rather than a dependency, clone the [Testaro repository](https://github.com/jrpool/testaro). To ensure that the binary browsers of its Playwright dependency get installed, execute `(p)npx playwright install` after executing `(p)npm install`.
|
|
125
121
|
|
|
126
122
|
To update Testaro when it is an independent application, execute:
|
|
127
123
|
|
|
@@ -131,19 +127,13 @@ git pull
|
|
|
131
127
|
(p)npm run deps
|
|
132
128
|
```
|
|
133
129
|
|
|
134
|
-
### As a dependency
|
|
135
|
-
|
|
136
|
-
You can make `testaro` a dependency in another application. As noted at the beginning of this file, the entry in `package.json` should be `"testaro": "67.1.0"` if your application has not been designed to work with version 68.0.0 or later.
|
|
137
|
-
|
|
138
130
|
## Environment configuration
|
|
139
131
|
|
|
140
132
|
The `.env` file stores your decisions about the environment in which Testaro runs. The variables that can be defined there are documented in the `env.example` file.
|
|
141
133
|
|
|
142
134
|
## Jobs
|
|
143
135
|
|
|
144
|
-
Jobs tell Testaro what
|
|
145
|
-
|
|
146
|
-
### Job example
|
|
136
|
+
Jobs tell Testaro what to do.
|
|
147
137
|
|
|
148
138
|
Here is a sample job, showing properties that you can set:
|
|
149
139
|
|
|
@@ -204,8 +194,8 @@ Here is a sample job, showing properties that you can set:
|
|
|
204
194
|
}
|
|
205
195
|
},
|
|
206
196
|
which: 'qualWeb',
|
|
207
|
-
withNewContent: false,
|
|
208
|
-
rules: ['QW-BP25', 'QW-BP26']
|
|
197
|
+
withNewContent: false, // An argument required by this tool
|
|
198
|
+
rules: ['QW-BP25', 'QW-BP26'] // Which rules of the tool to test for
|
|
209
199
|
}
|
|
210
200
|
]
|
|
211
201
|
}
|
|
@@ -213,11 +203,11 @@ Here is a sample job, showing properties that you can set:
|
|
|
213
203
|
|
|
214
204
|
The `device` property lets you choose among [about 125 devices recognized by Playwright](https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/server/deviceDescriptorsSource.json).
|
|
215
205
|
|
|
216
|
-
|
|
206
|
+
The act types and their options are documented in the `etc` property of the [actSpecs.js](actSpecs.js) object and in the [actSpecs-doc.md](actSpecs-doc.md) file.
|
|
217
207
|
|
|
218
|
-
|
|
208
|
+
## Running jobs
|
|
219
209
|
|
|
220
|
-
|
|
210
|
+
### Job as an object
|
|
221
211
|
|
|
222
212
|
An application can execute a job with:
|
|
223
213
|
|
|
@@ -231,35 +221,39 @@ doJob(job)
|
|
|
231
221
|
});
|
|
232
222
|
```
|
|
233
223
|
|
|
234
|
-
|
|
224
|
+
### Job as a file
|
|
235
225
|
|
|
236
|
-
|
|
226
|
+
Jobs can be stored as JSON files in the `todo` subdirectory of a directory identified by the `JOBDIR` environment variable. After Testaro performs such a job, Testaro moves the job file to the `done` subdirectory of the same directory and saves the report in the `raw` subdirectory of the directory identified by the `REPORTDIR` environment variable.
|
|
237
227
|
|
|
238
|
-
|
|
228
|
+
### Immediate performance
|
|
229
|
+
|
|
230
|
+
A user can make Testaro perform a job from a file with a command like either of:
|
|
239
231
|
|
|
240
232
|
```bash
|
|
241
233
|
node call run
|
|
242
234
|
node call run 250725T
|
|
243
235
|
```
|
|
244
236
|
|
|
245
|
-
|
|
237
|
+
Testaro will find the first file in the `todo` subdirectory, or, if the second form of the command is used, the first file there whose name begins with the specified string.
|
|
238
|
+
|
|
239
|
+
### Directory polling
|
|
240
|
+
|
|
241
|
+
An application can poll the `todo` subdirectory for jobs with:
|
|
246
242
|
|
|
247
243
|
```javaScript
|
|
248
244
|
const {dirWatch} = require('testaro/dirWatch');
|
|
249
245
|
dirWatch(true, 300);
|
|
250
246
|
```
|
|
251
247
|
|
|
252
|
-
A user can make Testaro
|
|
248
|
+
A user can make Testaro start to poll that subdirectory with:
|
|
253
249
|
|
|
254
250
|
```javaScript
|
|
255
251
|
node call dirWatch true 300
|
|
256
252
|
```
|
|
257
253
|
|
|
258
|
-
In both cases, the first argument of `dirWatch` tells Testaro whether to continue
|
|
254
|
+
In both cases, the first argument of `dirWatch` tells Testaro whether to continue polling after performing one job, and the second argument tells Testaro how many seconds to wait after not finding a job to perform, before polling again.
|
|
259
255
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
#### Job from a server
|
|
256
|
+
### Server polling
|
|
263
257
|
|
|
264
258
|
Testaro can poll a server for jobs to be performed. The server can act as the “controller” described in [How to run a thousand accessibility tests](https://medium.com/cvs-health-tech-blog/how-to-run-a-thousand-accessibility-tests-63692ad120c3). The server is responsible for preparing Testaro jobs, assigning them to Testaro agents, receiving reports back from those agents, and performing any further processing of the reports, including enhancement, storage, and disclosure to audiences. It can be any server reachable with a URL. That includes a server running on the same host as Testaro, with a URL such as `localhost:3000`.
|
|
265
259
|
|
|
@@ -273,7 +267,7 @@ The job request sent to the server can be a `POST` request, in which the `agentP
|
|
|
273
267
|
|
|
274
268
|
Testaro will send the report as a `POST` request whose payload is a JSON object with two properties: `agentPW` (the password) and `report` (the report). However, if the environment does not contain a password, the payload is a JSON object containing only the report.
|
|
275
269
|
|
|
276
|
-
An application can poll a server for jobs with:
|
|
270
|
+
An application can make Testaro poll a server for jobs with:
|
|
277
271
|
|
|
278
272
|
```javaScript
|
|
279
273
|
const {netWatch} = require('testaro/netWatch');
|
|
@@ -286,24 +280,22 @@ A user can make Testaro poll a server for jobs with:
|
|
|
286
280
|
node call netWatch true 300 true
|
|
287
281
|
```
|
|
288
282
|
|
|
289
|
-
The first argument of `netWatch` tells Testaro whether to continue polling after performing the first job. The second argument tells Testaro how many seconds to wait after receiving a no-jobs response. The third argument tells Testaro whether to be certificate-tolerant, i.e. to accept SSL certificates that fail verification against a list of certificate authorities (the default is `true`).
|
|
283
|
+
The first argument of `netWatch` tells Testaro whether to continue polling after performing the first job. The second argument tells Testaro how many seconds to wait after receiving a no-jobs response before polling again. The third argument tells Testaro whether to be certificate-tolerant, i.e. to accept SSL certificates that fail verification against a list of certificate authorities (the default is `true`).
|
|
290
284
|
|
|
291
285
|
## Reports
|
|
292
286
|
|
|
293
287
|
A report is a job with information about the results of the performance of the job inserted by Testaro into the job.
|
|
294
288
|
|
|
295
|
-
### Whole-job
|
|
289
|
+
### Whole-job data
|
|
296
290
|
|
|
297
291
|
As Testaro performs a job, information about the job as a whole is inserted into the job. That information is organized into one or two properties:
|
|
298
292
|
|
|
299
293
|
- `jobData`: Facts about the performance of the job
|
|
300
|
-
- `catalog`: A collection of data about the HTML elements
|
|
301
|
-
|
|
302
|
-
Testaro inserts the `jobData` property into every job, but inserts the `catalog` property only into jobs that instruct Testaro to produce standard results.
|
|
294
|
+
- `catalog`: A collection of data about the HTML elements of the target that are relevant to any test failures
|
|
303
295
|
|
|
304
|
-
|
|
296
|
+
Testaro inserts the `jobData` property into every job.
|
|
305
297
|
|
|
306
|
-
|
|
298
|
+
Testaro inserts the `catalog` property only into jobs that instruct Testaro to produce standard results. The catalog is an inventory of HTML elements in the DOM of the target.
|
|
307
299
|
|
|
308
300
|
The `catalog` property has an object value. Here is an example:
|
|
309
301
|
|
|
@@ -330,9 +322,10 @@ The catalog is a mechanism for the integration of the tools. Most rule violation
|
|
|
330
322
|
Testaro uses the following techniques to make the tools calculate XPaths:
|
|
331
323
|
|
|
332
324
|
- `alfa` and `aslint`: They report XPaths, so Testaro needs only to normalize them.
|
|
333
|
-
- `ed11y`: Testaro adds it and a `window.getXPath` method to the page
|
|
325
|
+
- `ed11y`: Testaro adds it and a `window.getXPath` method to the page. When the tool reports an element, Testaro computes its XPath.
|
|
334
326
|
- `wave`: It reports a selector for each element; Testaro finds each element in the page via its selector and executes `window.getXPath` on the element.
|
|
335
|
-
- `
|
|
327
|
+
- `htmlcs`, `ibm`, `nuVal`, `nuVnu`, `qualWeb`: Testaro adds `data-xpath` attributes to all elements. The tools include code excerpts, with the `data-xpath` attributes, in the reported violations.
|
|
328
|
+
- `axe`: It reports a selector for each element, and Testaro adds `data-xpath` attributes to all elements. Testaro finds each element in the page via its selector and uses the `data-xpath` attribute. When this fails, Testaro uses the `data-xpath` attribute if its complete value is included in the reported `node.html` value.
|
|
336
329
|
- `testaro`: Testaro designs each of its own tests to report element XPaths.
|
|
337
330
|
|
|
338
331
|
By attaching a catalog entry to each reported element, Testaro allows an application that uses Testaro to tell users, for any particular HTML element, which tools ascribed violations of which rules to that element. An application could, for example, use a screenshot or a text-fragment link or could ask the user to paste the XPath into a browser developer tool.
|
|
@@ -343,14 +336,14 @@ In some cases no catalog entry can be found. The reasons may include:
|
|
|
343
336
|
- The element is inside a `noscript` element and therefore not considered an element in the DOM.
|
|
344
337
|
- The violation is not ascribed to a single element.
|
|
345
338
|
|
|
346
|
-
### Act
|
|
339
|
+
### Act data
|
|
347
340
|
|
|
348
|
-
As Testaro performs the acts of a job, information about the
|
|
341
|
+
As Testaro performs the acts of a job, information about the result of each act is inserted into that act. For acts of type `test`, the added properties are:
|
|
349
342
|
|
|
350
343
|
- `startTime`: When Testaro began to perform the act
|
|
351
344
|
- `actualURL`: The tested URL (different from the target URL if the request was redirected)
|
|
352
345
|
- `data`: Data generated by the tool
|
|
353
|
-
- `result`:
|
|
346
|
+
- `result`: Result of the testing by the tool
|
|
354
347
|
|
|
355
348
|
The `result` property is an object with one or two (depending on the value of `standard`, as described above) subproperties:
|
|
356
349
|
|
|
@@ -453,7 +446,7 @@ In a previous version of the package, the tool operated on the page content when
|
|
|
453
446
|
|
|
454
447
|
### Nu Html Checker
|
|
455
448
|
|
|
456
|
-
The `nuVal` and `nuVnu` tools perform the tests of the Nu Html Checker.
|
|
449
|
+
The `nuVal` and `nuVnu` tools perform the tests of the Nu Html Checker. The `nuVal` tool is a remote service with an API. The `nuVnu` tool is installed as a dependency. A job can choose either one, or can try `nuVal` and if it fails then invoke `nuVnu`.
|
|
457
450
|
|
|
458
451
|
Its `rules` argument is **not** an array of rule IDs, but instead is an array of rule _specifications_. A rule specification for `nuVal` or `nuVnu` is a string with the format `=ruleID` or `~ruleID`. The `=` prefix indicates that the rule ID is invariable. The `~` prefix indicates that the rule ID is variable, in which case the `ruleID` part of the specification is a matching regular expression, rather than the exact text of a message. This `rules` format arises from the fact that `nuVal` and `nuVnu` generate customized messages and do not accompany them with rule identifiers.
|
|
459
452
|
|
|
@@ -482,7 +475,7 @@ Thus, when the `rules` argument is omitted, QualWeb will test for all of the rul
|
|
|
482
475
|
|
|
483
476
|
The target can be provided to QualWeb either as HTML or as a URL. Experience indicates that the results can differ between these methods, with each method reporting some rule violations or some instances that the other method does not report. For at least some cases, more rules are reported violated when HTML is provided (`withNewItems: false`).
|
|
484
477
|
|
|
485
|
-
QualWeb creates sandboxed Puppeteer pages to perform its tests on. Therefore, the host must permit sandboxed browsers to be launched. See the discussion above about browser security.
|
|
478
|
+
QualWeb creates sandboxed Puppeteer pages to perform its tests on. Therefore, the host must permit sandboxed browsers to be launched. See the discussion above about browser security.
|
|
486
479
|
|
|
487
480
|
### Testaro
|
|
488
481
|
|
|
@@ -545,7 +538,7 @@ The Playwright “Receives Events” actionability check does **not** check whet
|
|
|
545
538
|
|
|
546
539
|
### Test prevention
|
|
547
540
|
|
|
548
|
-
Test targets employ mechanisms to prevent scraping, multiple requests within a short time, automated form submission, and other automated actions. These mechanisms may interfere with testing. When a test act is prevented
|
|
541
|
+
Test targets employ mechanisms to prevent scraping, multiple requests within a short time, automated form submission, and other automated actions. These mechanisms may interfere with testing. When a test act is prevented, Testaro reports this prevention.
|
|
549
542
|
|
|
550
543
|
Some targets prohibit the execution of alien scripts unless the client can demonstrate that it is the requester of the page. Failure to provide that evidence results in the script being blocked and an error message being logged, saying “Refused to execute a script because its hash, its nonce, or unsafe-inline does not appear in the script-src directive of the Content Security Policy”. This mechanism affects tools that insert scripts into a target in order to test it. To comply with this requirement, Testaro obtains a _nonce_ from the response that serves the target. Then the file that runs the tool adds that nonce to the script as the value of a `nonce` attribute when it inserts its script into the target.
|
|
551
544
|
|
|
@@ -615,7 +608,7 @@ From 12 February 2024 through 30 September 2025, contributors of code to Testaro
|
|
|
615
608
|
|
|
616
609
|
## Future work
|
|
617
610
|
|
|
618
|
-
Future work
|
|
611
|
+
Future work contemplated for this project is described in its [issues](https://github.com/jrpool/testaro/issues) and also discussed in the [UPGRADES.md](UPGRADES.md) file.
|
|
619
612
|
|
|
620
613
|
## Etymology
|
|
621
614
|
|
package/actSpecs-doc.md
CHANGED
|
@@ -30,7 +30,10 @@ link: [
|
|
|
30
30
|
]
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The rule is an array with two elements:
|
|
33
|
+
The rule is an array with two elements:
|
|
34
|
+
|
|
35
|
+
- a string ('Click a link' in this case) describing the act
|
|
36
|
+
- an object containing requirements for any act of the type identified by the key (`link` in this case).
|
|
34
37
|
|
|
35
38
|
The requirement `which: [true, 'string', 'hasLength', 'substring of the link text']` specifies what is required for the `which` property of a `link`-type act. The requirement is an array.
|
|
36
39
|
|
|
@@ -52,6 +55,27 @@ The validity criterion named in item 2 may be any of these:
|
|
|
52
55
|
- `'isWaitable'`: is `'url'`, `'title'`, or `'body'`
|
|
53
56
|
- `'areStrings'`: is an array of strings
|
|
54
57
|
|
|
58
|
+
## testaro tool
|
|
59
|
+
|
|
60
|
+
The `tools.testaro` object has an `args` property specifying that a `testaro` test act may include an `args` property with an object value.
|
|
61
|
+
|
|
62
|
+
If it does, the property names of the object value must be `testaro` rule IDs. Any property value must be an array of the positional arguments to be concatenated to the four default arguments (`page`, `report`, `actIndex`, and `withItems`) in the signature of the `reporter` function of each `testaro` rule.
|
|
63
|
+
|
|
64
|
+
The rules of `testaro` that accept additional arguments are `autocomplete`, `buttonMenu`, `focInd`, and `hover`.
|
|
65
|
+
|
|
66
|
+
## Screenshots
|
|
67
|
+
|
|
68
|
+
An act of type `shoot` creates a full-page screenshot and converts it to a base64 encoding of a PNG image. With its arguments, you can decide:
|
|
69
|
+
|
|
70
|
+
- Whether to mask certain elements (using a CSS selector)
|
|
71
|
+
- The color type (grayscale, RGB, grayscale alpha, or RGBA)
|
|
72
|
+
- What action to take to dispose of the base64 string:
|
|
73
|
+
- return it to the `shoot` act and save it in the result of the act (`return`)
|
|
74
|
+
- concatenate it to an `images` array in the report and return its index in the array (`report`)
|
|
75
|
+
- save it as a file in the temporary directory and return the file path (`file`)
|
|
76
|
+
|
|
77
|
+
Both the `return` and the `report` actions save the base64 string in the report, but in different places. The `file` action would permit you to create a custom act that retrieves and uses the image without the image being added to the report.
|
|
78
|
+
|
|
55
79
|
## License
|
|
56
80
|
|
|
57
81
|
© 2026 Jonathan Robert Pool.
|
package/actSpecs.js
CHANGED
|
@@ -45,6 +45,7 @@ exports.actSpecs = {
|
|
|
45
45
|
{
|
|
46
46
|
target: [false, 'object', '', 'target different from target of the job'],
|
|
47
47
|
browserID: [false, 'string', 'isBrowserID', 'browser type different from browserID of the job'],
|
|
48
|
+
shoot: [false, 'object', '', 'if screenshot to be made, exclusionSelector, colorType, and action'],
|
|
48
49
|
what: [false, 'string', 'hasLength', 'comment']
|
|
49
50
|
}
|
|
50
51
|
],
|
|
@@ -123,10 +124,11 @@ exports.actSpecs = {
|
|
|
123
124
|
}
|
|
124
125
|
],
|
|
125
126
|
shoot: [
|
|
126
|
-
'
|
|
127
|
+
'Create and dispose of a full-page screenshot as a base-64-encoded PNG',
|
|
127
128
|
{
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
exclusionSelector: [false, 'string', 'hasLength', 'CSS selector for an element to mask'],
|
|
130
|
+
colorType: [false, 'number', '', '0=grayscale, 2=RGB, 4=grayscale alpha, 6=RGBA'],
|
|
131
|
+
action: [true, 'string', 'hasLength', 'disposition: return, report, file'],
|
|
130
132
|
what: [false, 'string', 'hasLength', 'comment']
|
|
131
133
|
}
|
|
132
134
|
],
|
|
@@ -208,7 +210,7 @@ exports.actSpecs = {
|
|
|
208
210
|
{
|
|
209
211
|
withItems: [true, 'boolean', '', 'itemize'],
|
|
210
212
|
stopOnFail: [true, 'boolean', '', 'whether testing is to stop after first failure'],
|
|
211
|
-
args: [false, 'object', 'areArrays', 'extra
|
|
213
|
+
args: [false, 'object', 'areArrays', 'extra arguments of rules taking any']
|
|
212
214
|
}
|
|
213
215
|
],
|
|
214
216
|
wave: [
|
package/package.json
CHANGED
package/procs/catalog.js
CHANGED
package/procs/dateTime.js
CHANGED
|
@@ -32,5 +32,7 @@ exports.dateOf = timeStamp => {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
|
-
// Returns a
|
|
36
|
-
exports.nowString = () => (new Date()).toISOString().slice(2, 16);
|
|
35
|
+
// Returns a human-friendly representation of the current date and time.
|
|
36
|
+
const nowString = exports.nowString = () => (new Date()).toISOString().slice(2, 16);
|
|
37
|
+
// Returns a compact representation of the current date and time.
|
|
38
|
+
exports.nowStamp = () => nowString().replace(/[:-]/g, '');
|
package/procs/doActs.js
CHANGED
|
@@ -289,13 +289,14 @@ exports.doActs = async report => {
|
|
|
289
289
|
}
|
|
290
290
|
// Otherwise, if the act is a launch:
|
|
291
291
|
else if (type === 'launch') {
|
|
292
|
-
// Launch a browser, navigate
|
|
292
|
+
// Launch a browser, navigate, optionally make a screenshot, and add the result to the act.
|
|
293
293
|
page = await launch({
|
|
294
|
-
tempReport,
|
|
294
|
+
report: tempReport,
|
|
295
295
|
actIndex,
|
|
296
296
|
tempBrowserID: getActBrowserID(tempReport, actIndex),
|
|
297
297
|
tempURL: getActTargetURL(tempReport, actIndex),
|
|
298
|
-
xPathNeed: 'none'
|
|
298
|
+
xPathNeed: 'none',
|
|
299
|
+
shoot: act.shoot
|
|
299
300
|
});
|
|
300
301
|
// If this failed:
|
|
301
302
|
if (! page) {
|
|
@@ -641,11 +642,17 @@ exports.doActs = async report => {
|
|
|
641
642
|
}
|
|
642
643
|
// Otherwise, if the act is a screenshot:
|
|
643
644
|
else if (type === 'shoot') {
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
?
|
|
648
|
-
:
|
|
645
|
+
const {exclusionSelector, colorType, action} = act;
|
|
646
|
+
// Make and dispose of a full-page screenshot.
|
|
647
|
+
const shotInfo = await shoot(page, tempReport, {
|
|
648
|
+
exclusion: exclusionSelector ? page.locator(exclusionSelector) : null,
|
|
649
|
+
colorType: [0, 2, 4, 6].includes(colorType) ? colorType : null,
|
|
650
|
+
action: ['return', 'report', 'file'].includes(action) ? action : 'report'
|
|
651
|
+
});
|
|
652
|
+
// Add the PNG base-64 encoding, image index, or file path to the act result.
|
|
653
|
+
act.result = shotInfo !== ''
|
|
654
|
+
? {success: true, shotInfo}
|
|
655
|
+
: {success: false, prevented: true};
|
|
649
656
|
}
|
|
650
657
|
// Otherwise, if the act is a move:
|
|
651
658
|
else if (moves[type]) {
|
package/procs/launch.js
CHANGED
package/procs/shoot.js
CHANGED
|
@@ -6,57 +6,35 @@
|
|
|
6
6
|
|
|
7
7
|
/*
|
|
8
8
|
shoot
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Call shape:
|
|
12
|
-
shoot(page, label, options?)
|
|
13
|
-
label: string|number used in the saved filename. Sanitized to
|
|
14
|
-
testaro-shoot-<safe>.png — characters outside [A-Za-z0-9._-]
|
|
15
|
-
collapse to '_', leading/trailing dots and underscores are
|
|
16
|
-
stripped, length is capped at 100, and an empty result becomes
|
|
17
|
-
'unnamed'.
|
|
18
|
-
options: optional object:
|
|
19
|
-
exclusion: a Playwright Locator to mask in the screenshot.
|
|
20
|
-
dir: output directory (defaults to the OS temp dir).
|
|
9
|
+
Manages the production and disposition of screenshots of a page.
|
|
21
10
|
*/
|
|
22
11
|
|
|
23
12
|
// IMPORTS
|
|
24
13
|
|
|
25
14
|
// Shared configuration for timeout multiplier.
|
|
26
15
|
const {applyMultiplier} = require('./config');
|
|
16
|
+
const {nowStamp} = require('./dateTime');
|
|
27
17
|
const fs = require('fs/promises');
|
|
28
18
|
const path = require('path');
|
|
29
19
|
const {PNG} = require('pngjs');
|
|
30
20
|
|
|
31
21
|
// FUNCTIONS
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
characters; falls back to 'unnamed' if nothing usable remains.
|
|
38
|
-
*/
|
|
39
|
-
const sanitizeLabel = (label) => {
|
|
40
|
-
const raw = String(label);
|
|
41
|
-
const cleaned = raw
|
|
42
|
-
.replace(/[^A-Za-z0-9._-]+/g, '_')
|
|
43
|
-
.replace(/^[._]+|[._]+$/g, '')
|
|
44
|
-
.slice(0, 100) || 'unnamed';
|
|
45
|
-
if (cleaned !== raw) {
|
|
46
|
-
console.log(`>> shoot: label sanitized from "${raw}" to "${cleaned}"`);
|
|
47
|
-
}
|
|
48
|
-
return cleaned;
|
|
23
|
+
// Returns a probably unique file name.
|
|
24
|
+
const randomFileName = (suffixLength = 3) => {
|
|
25
|
+
const fileName = `${nowStamp()}-${Math.random().toString(36).slice(2, 2 + suffixLength)}`;
|
|
26
|
+
return fileName;
|
|
49
27
|
};
|
|
50
|
-
|
|
51
28
|
// Creates and returns a screenshot.
|
|
52
|
-
const screenShot = async (page,
|
|
29
|
+
const screenShot = async (page, exclusionLocator = null) => {
|
|
53
30
|
const options = {
|
|
54
31
|
fullPage: true,
|
|
55
32
|
omitBackground: true,
|
|
33
|
+
scale: 'css',
|
|
56
34
|
timeout: applyMultiplier(4000)
|
|
57
35
|
};
|
|
58
|
-
if (
|
|
59
|
-
options.mask = [
|
|
36
|
+
if (exclusionLocator) {
|
|
37
|
+
options.mask = [exclusionLocator];
|
|
60
38
|
}
|
|
61
39
|
// Make and return a screenshot as a buffer.
|
|
62
40
|
return await page.screenshot(options)
|
|
@@ -65,28 +43,55 @@ const screenShot = async (page, exclusion = null) => {
|
|
|
65
43
|
return '';
|
|
66
44
|
});
|
|
67
45
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
46
|
+
// Creates and disposes of the PNG of a screenshot.
|
|
47
|
+
exports.shoot = async (page, report, {
|
|
48
|
+
// Playwright locator of a mask.
|
|
49
|
+
exclusionSelector = null,
|
|
50
|
+
// Color fidelity: 0 (grayscale), 2 (RGB), 4 (grayscale alpha), 6 (RGBA).
|
|
51
|
+
colorType = 0,
|
|
52
|
+
// Disposition: return, report, file.
|
|
53
|
+
action = 'return'
|
|
54
|
+
} = {}) => {
|
|
71
55
|
// Make and get a screenshot as a buffer.
|
|
72
|
-
let shot = await screenShot(page,
|
|
56
|
+
let shot = await screenShot(page, exclusionSelector ? page.locator(exclusionSelector) : null);
|
|
73
57
|
// If it succeeded:
|
|
74
58
|
if (shot.length) {
|
|
75
59
|
// Get the screenshot as an object representation of a PNG image.
|
|
76
60
|
let png = PNG.sync.read(shot);
|
|
77
61
|
shot = null;
|
|
78
|
-
|
|
62
|
+
// Convert the PNG object to a buffer, applying the specified color type.
|
|
63
|
+
const pngBuffer = PNG.sync.write(png, {colorType});
|
|
79
64
|
png = null;
|
|
80
65
|
// Force garbage collection if available and if --expose-gc was a node option.
|
|
81
66
|
if (global.gc) {
|
|
82
67
|
global.gc();
|
|
83
68
|
}
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
69
|
+
// Convert the buffer to a base64 string.
|
|
70
|
+
const base64 = pngBuffer.toString('base64');
|
|
71
|
+
// If the string is to be returned:
|
|
72
|
+
if (action === 'return') {
|
|
73
|
+
// Return it.
|
|
74
|
+
return base64;
|
|
75
|
+
}
|
|
76
|
+
// Otherwise, if it is to be appended to the report:
|
|
77
|
+
if (action === 'report') {
|
|
78
|
+
report.images ??= [];
|
|
79
|
+
// Append it to the images array in the report.
|
|
80
|
+
report.images.push(base64);
|
|
81
|
+
// Return the index of the added image in the array.
|
|
82
|
+
return report.images.length - 1;
|
|
83
|
+
}
|
|
84
|
+
// Otherwise, if it is to be saved in a file:
|
|
85
|
+
if (action === 'file') {
|
|
86
|
+
const fileName = randomFileName(4);
|
|
87
|
+
const filePath = path.join(report.jobData.tmpDir, fileName);
|
|
88
|
+
// Save it in a file.
|
|
89
|
+
await fs.writeFile(filePath, base64);
|
|
90
|
+
// Return the file name.
|
|
91
|
+
return fileName;
|
|
92
|
+
}
|
|
93
|
+
// Otherwise, i.e. if the action is invalid, return this.
|
|
94
|
+
return '';
|
|
90
95
|
}
|
|
91
96
|
// Otherwise, i.e. if it failed:
|
|
92
97
|
else {
|
package/testaro/adbID.js
CHANGED
|
@@ -22,7 +22,7 @@ const {doTest} = require('../procs/testaro');
|
|
|
22
22
|
// FUNCTIONS
|
|
23
23
|
|
|
24
24
|
// Runs the test and returns the result.
|
|
25
|
-
exports.reporter = async (page,
|
|
25
|
+
exports.reporter = async (page, report, _, withItems) => {
|
|
26
26
|
const getBadWhat = element => {
|
|
27
27
|
// Get the IDs in the aria-describedby attribute of the element.
|
|
28
28
|
const IDs = element.getAttribute('aria-describedby').trim().split(/\s+/).filter(Boolean);
|
|
@@ -57,6 +57,6 @@ exports.reporter = async (page, catalog, withItems) => {
|
|
|
57
57
|
};
|
|
58
58
|
const whats = 'Elements have aria-describedby attributes with missing or invalid id values';
|
|
59
59
|
return await doTest(
|
|
60
|
-
page, catalog, withItems, 'adbID', 'body [aria-describedby]', whats, 3, getBadWhat.toString()
|
|
60
|
+
page, report.catalog, withItems, 'adbID', 'body [aria-describedby]', whats, 3, getBadWhat.toString()
|
|
61
61
|
);
|
|
62
62
|
};
|
package/testaro/allCapStyle.js
CHANGED
|
@@ -20,7 +20,7 @@ const {doTest} = require('../procs/testaro');
|
|
|
20
20
|
// FUNCTIONS
|
|
21
21
|
|
|
22
22
|
// Runs the test and returns the result.
|
|
23
|
-
exports.reporter = async (page,
|
|
23
|
+
exports.reporter = async (page, report, _, withItems) => {
|
|
24
24
|
const getBadWhat = element => {
|
|
25
25
|
// Get the style declaration of the element.
|
|
26
26
|
const styleDec = window.getComputedStyle(element);
|
|
@@ -46,6 +46,6 @@ exports.reporter = async (page, catalog, withItems) => {
|
|
|
46
46
|
const selector = 'body, body *:not(style, script, svg)';
|
|
47
47
|
const whats = 'Elements have an all-capital text transformation style';
|
|
48
48
|
return await doTest(
|
|
49
|
-
page, catalog, withItems, 'allCapStyle', selector, whats, 0, getBadWhat.toString()
|
|
49
|
+
page, report.catalog, withItems, 'allCapStyle', selector, whats, 0, getBadWhat.toString()
|
|
50
50
|
);
|
|
51
51
|
};
|