testaro 60.5.1 → 60.6.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/README.md +36 -6
- package/package.json +1 -1
- package/procs/doTestAct.js +72 -68
- package/run.js +38 -13
- package/tests/testaro.js +89 -85
- package/validation/jobs/todo/240101T1200-simple-example.json +1 -2
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ All of the tests that Testaro can perform are free of cost, except those perform
|
|
|
140
140
|
|
|
141
141
|
## Jobs
|
|
142
142
|
|
|
143
|
-
A _job_ is an object that specifies what Testaro is to do. As Testaro performs a job, Testaro reports results by adding data to the job and making that enhanced object available as a _report_.
|
|
143
|
+
A _job_ is an object that specifies what Testaro is to do, and how. As Testaro performs a job, Testaro reports results by adding data to the job and making that enhanced object available as a _report_.
|
|
144
144
|
|
|
145
145
|
### Example of a job
|
|
146
146
|
|
|
@@ -192,7 +192,13 @@ Here is an example of a job:
|
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
194
|
type: 'test',
|
|
195
|
-
launch: {
|
|
195
|
+
launch: {
|
|
196
|
+
browserID: 'chromium',
|
|
197
|
+
target: {
|
|
198
|
+
what: 'Real Estate Management',
|
|
199
|
+
url: 'https://abccorp.com/mgmt/realproperty.html'
|
|
200
|
+
}
|
|
201
|
+
},
|
|
196
202
|
which: 'qualWeb',
|
|
197
203
|
withNewContent: false,
|
|
198
204
|
rules: ['QW-BP25', 'QW-BP26']
|
|
@@ -204,7 +210,7 @@ Here is an example of a job:
|
|
|
204
210
|
|
|
205
211
|
This job tells Testaro to perform two _acts_. One performs one test of the Axe tool wih reporting at detail level 2, and the other performs two tests of the QualWeb tool.
|
|
206
212
|
|
|
207
|
-
Each act includes a `launch` property
|
|
213
|
+
Each act includes a `launch` object property. In the first act it is an empty object, the browser ID and target URL specified by the job are used. In the second act it overrides the job values with per-act values.
|
|
208
214
|
|
|
209
215
|
Job properties:
|
|
210
216
|
|
|
@@ -518,7 +524,18 @@ The target can be provided to QualWeb either as an existing page or as a URL. Ex
|
|
|
518
524
|
|
|
519
525
|
The rules that Testaro can test for are implemented in files within the `testaro` directory.
|
|
520
526
|
|
|
521
|
-
|
|
527
|
+
The Testaro rules are classified by an `allRules` array defined in the `tests/testaro.js` file. Each item in that array is an object with these properties:
|
|
528
|
+
|
|
529
|
+
- `id`: the rule ID.
|
|
530
|
+
- `what`: a description of the rule.
|
|
531
|
+
- `launchRole`: what a test for the rule does with respect to a browser launch:
|
|
532
|
+
- `sharer`: requires a browser and leaves it unchanged so the next test can safely reuse it
|
|
533
|
+
- `waster`: requires a browser and modifies it so the next test cannot safely reuse it
|
|
534
|
+
- `owner`: launches a custom browser itself and closes it at the end of the test
|
|
535
|
+
- `defaultOn`: whether the rule is to be tested for by default.
|
|
536
|
+
- `timeOut`: the maximum time in seconds allowed for a test for the rule.
|
|
537
|
+
|
|
538
|
+
If you do not specify rules when using the `testaro` tool, Testaro will test for its default rules. It will test for these rules in the order in which they appear in the array.
|
|
522
539
|
|
|
523
540
|
The optional `rules` argument for a `testaro` test act is an array whose first item is either `'y'` or `'n'` and whose remaining items are rule IDs. If `'y'`, then only the specified rules’ tests are performed. If `'n'`, then all the default rules are tested for, **except** for the specified rules.
|
|
524
541
|
|
|
@@ -882,12 +899,17 @@ The arguments and behaviors described above for execution by a module apply here
|
|
|
882
899
|
|
|
883
900
|
In addition to their uses described above, environment variables can be used by acts of type `test`, as documented in the `actSpecs.js` file.
|
|
884
901
|
|
|
885
|
-
Before making Testaro run a job, you can optionally also set `
|
|
902
|
+
Before making Testaro run a job, you can optionally also set `HEADED_BROWSER`, `DEBUG`, and/or `WAITS`. The effects of these variables are:
|
|
903
|
+
|
|
904
|
+
- `HEADED_BROWSER`: whether to run the browser in headed mode instead of the default headless mode
|
|
905
|
+
- `DEBUG`: whether to make logging verbose
|
|
906
|
+
- `WAITS`: the number of milliseconds to wait between actions
|
|
886
907
|
|
|
887
908
|
You may store environment variables in an untracked `.env` file if you wish, and Testaro will recognize them. Here is a template for a `.env` file:
|
|
888
909
|
|
|
889
910
|
```conf
|
|
890
911
|
AGENT=agentabc
|
|
912
|
+
HEADED_BROWSER=false
|
|
891
913
|
DEBUG=false
|
|
892
914
|
JOBDIR=../testing/jobs
|
|
893
915
|
NETWATCH_URL_0_JOB=http://localhost:3000/api/assignJob/agentabc
|
|
@@ -974,9 +996,17 @@ Tools can become faulty. For example, Alfa stopped reporting any rule violations
|
|
|
974
996
|
|
|
975
997
|
Testaro would become more reliable if the behavior of its tools were monitored for suspect changes.
|
|
976
998
|
|
|
999
|
+
### Dependency deployment
|
|
1000
|
+
|
|
1001
|
+
The behavior of Testaro as a dependency of an application deployed on a virtual private server has been observed to be vulnerable to slower performance and more frequent test failures than when Testaro is deployed as a stand-alone application on a workstation. The configuration of Testaro has been tuned for mitigation of such behaviors.
|
|
1002
|
+
|
|
977
1003
|
### Containerized deployment
|
|
978
1004
|
|
|
979
|
-
The experimental deployment of Testaro as a dependency in a containerized application has been unsuccessful.
|
|
1005
|
+
The experimental deployment of Testaro as a dependency in a containerized application has been unsuccessful. Playwright errors have been thrown that are not thrown when the same application is deployed without containerization.
|
|
1006
|
+
|
|
1007
|
+
### Headless browser fidelity
|
|
1008
|
+
|
|
1009
|
+
Testaro normally performs tests with headless browsers. Some experiments appear to have shown that some test results are inaccurate with headless browsers, but this has not been replicated. The `launch` function in the `run` module accepts a `headEmulation` argument with `'high'` and `'low'` values. Its purpose is to permit optimizations of headless browsers to be turned off (`high`), at some performance cost, when making the browsers behave and appear more similar to headed browsers improves test accuracy. Observation has, however, failed to show any performance cost. Therefore, `'high'` is currently the default value.
|
|
980
1010
|
|
|
981
1011
|
## Repository exclusions
|
|
982
1012
|
|
package/package.json
CHANGED
package/procs/doTestAct.js
CHANGED
|
@@ -39,9 +39,8 @@ const os = require('os');
|
|
|
39
39
|
|
|
40
40
|
// CONSTANTS
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const headedBrowser = process.env.HEADED_BROWSER === 'true';
|
|
43
43
|
const debug = process.env.DEBUG === 'true';
|
|
44
|
-
// Set WAITS environment variable to a positive number to insert delays (in ms).
|
|
45
44
|
const waits = Number.parseInt(process.env.WAITS) || 0;
|
|
46
45
|
const tmpDir = os.tmpdir();
|
|
47
46
|
|
|
@@ -61,80 +60,85 @@ const doTestAct = async () => {
|
|
|
61
60
|
const act = report.acts[actIndex];
|
|
62
61
|
// Get the tool name.
|
|
63
62
|
const {which} = act;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const {page} = require('../run');
|
|
86
|
-
// If it exists:
|
|
87
|
-
if (page) {
|
|
88
|
-
try {
|
|
89
|
-
// Make the act reporter perform the specified tests of the tool.
|
|
90
|
-
const actReport = await require(`../tests/${which}`).reporter(page, report, actIndex, 65);
|
|
91
|
-
// Add the data and result to the act.
|
|
92
|
-
act.data = actReport.data;
|
|
93
|
-
act.result = actReport.result;
|
|
94
|
-
// If the tool reported that the page prevented testing:
|
|
95
|
-
if (act.data && act.data.prevented) {
|
|
96
|
-
// Add prevention data to the job data.
|
|
97
|
-
report.jobData.preventions[which] = act.data.error;
|
|
98
|
-
}
|
|
99
|
-
// Close any existing browser.
|
|
100
|
-
await browserClose();
|
|
101
|
-
const reportJSON = JSON.stringify(report);
|
|
102
|
-
// Save the revised report.
|
|
103
|
-
await fs.writeFile(reportPath, reportJSON);
|
|
104
|
-
// Send a completion message.
|
|
105
|
-
process.send('Act completed');
|
|
106
|
-
}
|
|
107
|
-
// If the tool invocation failed:
|
|
108
|
-
catch(error) {
|
|
109
|
-
// Close any existing browser.
|
|
110
|
-
await browserClose();
|
|
111
|
-
// Save the revised report.
|
|
112
|
-
const reportJSON = JSON.stringify(report);
|
|
113
|
-
await fs.writeFile(reportPath, reportJSON);
|
|
114
|
-
// Report the failure.
|
|
115
|
-
const message = error.message.slice(0, 400);
|
|
116
|
-
console.log(`ERROR: Test act ${act.which} failed (${message})`);
|
|
117
|
-
process.send('ERROR performing the act');
|
|
118
|
-
};
|
|
63
|
+
let page;
|
|
64
|
+
// If the tool is not Testaro:
|
|
65
|
+
if (which !== 'testaro') {
|
|
66
|
+
const browserID = act.launch && act.launch.browserID || report.browserID;
|
|
67
|
+
const targetURL = act.launch && act.launch.target && act.launch.target.url || report.target.url;
|
|
68
|
+
// Launch a browser, navigate to the URL, and update the run-module page export.
|
|
69
|
+
await launch(
|
|
70
|
+
report,
|
|
71
|
+
'high',
|
|
72
|
+
browserID,
|
|
73
|
+
targetURL
|
|
74
|
+
);
|
|
75
|
+
// If the launch aborted the job:
|
|
76
|
+
if (report.jobData && report.jobData.aborted) {
|
|
77
|
+
// Close any existing browser.
|
|
78
|
+
await browserClose();
|
|
79
|
+
// Save the revised report.
|
|
80
|
+
const reportJSON = JSON.stringify(report);
|
|
81
|
+
await fs.writeFile(reportPath, reportJSON);
|
|
82
|
+
// Report this.
|
|
83
|
+
process.send('ERROR: Job aborted');
|
|
119
84
|
}
|
|
120
|
-
// Otherwise, i.e. if the
|
|
85
|
+
// Otherwise, i.e. if the launch did not abort the job:
|
|
121
86
|
else {
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
87
|
+
// Get the updated page.
|
|
88
|
+
page = require('../run').page;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// If the page exists:
|
|
92
|
+
if (page || which === 'testaro') {
|
|
93
|
+
try {
|
|
94
|
+
// Make the act reporter perform the specified tests of the tool.
|
|
95
|
+
const actReport = await require(`../tests/${which}`).reporter(page, report, actIndex, 65);
|
|
96
|
+
// Add the data and result to the act.
|
|
97
|
+
act.data = actReport.data;
|
|
98
|
+
act.result = actReport.result;
|
|
99
|
+
// If the tool reported that the page prevented testing:
|
|
100
|
+
if (act.data && act.data.prevented) {
|
|
101
|
+
// Add prevention data to the job data.
|
|
102
|
+
report.jobData.preventions[which] = act.data.error;
|
|
103
|
+
}
|
|
128
104
|
// Close any existing browser.
|
|
129
105
|
await browserClose();
|
|
130
106
|
const reportJSON = JSON.stringify(report);
|
|
131
107
|
// Save the revised report.
|
|
132
108
|
await fs.writeFile(reportPath, reportJSON);
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
console.log(message);
|
|
136
|
-
process.send(message);
|
|
109
|
+
// Send a completion message.
|
|
110
|
+
process.send('Act completed');
|
|
137
111
|
}
|
|
112
|
+
// If the tool invocation failed:
|
|
113
|
+
catch(error) {
|
|
114
|
+
// Close any existing browser.
|
|
115
|
+
await browserClose();
|
|
116
|
+
// Save the revised report.
|
|
117
|
+
const reportJSON = JSON.stringify(report);
|
|
118
|
+
await fs.writeFile(reportPath, reportJSON);
|
|
119
|
+
// Report the failure.
|
|
120
|
+
const message = error.message.slice(0, 400);
|
|
121
|
+
console.log(`ERROR: Test act ${act.which} failed (${message})`);
|
|
122
|
+
process.send('ERROR performing the act');
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Otherwise, i.e. if the page does not exist:
|
|
126
|
+
else {
|
|
127
|
+
// Add data to the act.
|
|
128
|
+
act.data ??= {};
|
|
129
|
+
act.data.prevented = true;
|
|
130
|
+
act.data.error = 'No page';
|
|
131
|
+
// Add prevention data to the job data.
|
|
132
|
+
report.jobData.preventions[which] = act.data.error;
|
|
133
|
+
// Close any existing browser.
|
|
134
|
+
await browserClose();
|
|
135
|
+
const reportJSON = JSON.stringify(report);
|
|
136
|
+
// Save the revised report.
|
|
137
|
+
await fs.writeFile(reportPath, reportJSON);
|
|
138
|
+
// Report this.
|
|
139
|
+
const message = 'ERROR: No page';
|
|
140
|
+
console.log(message);
|
|
141
|
+
process.send(message);
|
|
138
142
|
}
|
|
139
143
|
};
|
|
140
144
|
|
package/run.js
CHANGED
|
@@ -58,9 +58,8 @@ const os = require('os');
|
|
|
58
58
|
|
|
59
59
|
// CONSTANTS
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
const headedBrowser = process.env.HEADED_BROWSER === 'true';
|
|
62
62
|
const debug = process.env.DEBUG === 'true';
|
|
63
|
-
// Set WAITS environment variable to a positive number to insert delays (in ms).
|
|
64
63
|
const waits = Number.parseInt(process.env.WAITS) || 0;
|
|
65
64
|
// CSS selectors for targets of moves.
|
|
66
65
|
const moves = {
|
|
@@ -291,7 +290,7 @@ const browserClose = exports.browserClose = async () => {
|
|
|
291
290
|
};
|
|
292
291
|
// Launches a browser and navigates to a URL.
|
|
293
292
|
const launch = exports.launch = async (
|
|
294
|
-
report,
|
|
293
|
+
report, headEmulation, tempBrowserID, tempURL, retries = 2
|
|
295
294
|
) => {
|
|
296
295
|
const act = report.acts[actIndex];
|
|
297
296
|
const {device} = report;
|
|
@@ -306,7 +305,37 @@ const launch = exports.launch = async (
|
|
|
306
305
|
const browserType = playwrightBrowsers[browserID];
|
|
307
306
|
// Close any current browser.
|
|
308
307
|
await browserClose();
|
|
309
|
-
// Define browser
|
|
308
|
+
// Define the browser-option args, depending on the browser type and head-emulation level.
|
|
309
|
+
const browserOptionArgs = [];
|
|
310
|
+
if (browserID === 'chromium') {
|
|
311
|
+
browserOptionArgs.push(
|
|
312
|
+
'--disable-dev-shm-usage', '--disable-blink-features=AutomationControlled'
|
|
313
|
+
);
|
|
314
|
+
if (headEmulation === 'high') {
|
|
315
|
+
browserOptionArgs.push(
|
|
316
|
+
'--no-sandbox',
|
|
317
|
+
'--disable-setuid-sandbox',
|
|
318
|
+
'--disable-gpu',
|
|
319
|
+
'--disable-software-rasterizer',
|
|
320
|
+
'--force-device-scale-factor=1',
|
|
321
|
+
'--disable-default-apps',
|
|
322
|
+
'--disable-extensions',
|
|
323
|
+
'--disable-sync',
|
|
324
|
+
'--disable-background-timer-throttling',
|
|
325
|
+
'--disable-backgrounding-occluded-windows',
|
|
326
|
+
'--disable-renderer-backgrounding',
|
|
327
|
+
'--disable-background-networking',
|
|
328
|
+
'--force-color-profile=srgb',
|
|
329
|
+
'--disable-features=TranslateUI,VizDisplayCompositor',
|
|
330
|
+
'--disable-ipc-flooding-protection',
|
|
331
|
+
'--disable-logging',
|
|
332
|
+
'--disable-permissions-api',
|
|
333
|
+
'--disable-notifications',
|
|
334
|
+
'--disable-popup-blocking'
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Define the browser options.
|
|
310
339
|
const browserOptions = {
|
|
311
340
|
logger: {
|
|
312
341
|
isEnabled: () => false,
|
|
@@ -316,11 +345,9 @@ const launch = exports.launch = async (
|
|
|
316
345
|
}
|
|
317
346
|
}
|
|
318
347
|
},
|
|
319
|
-
headless: !
|
|
348
|
+
headless: ! headedBrowser,
|
|
320
349
|
slowMo: waits || 0,
|
|
321
|
-
|
|
322
|
-
args: ['--disable-dev-shm-usage', '--disable-blink-features=AutomationControlled']
|
|
323
|
-
})
|
|
350
|
+
args: browserOptionArgs
|
|
324
351
|
};
|
|
325
352
|
try {
|
|
326
353
|
// Replace the browser with a new one.
|
|
@@ -460,7 +487,7 @@ const launch = exports.launch = async (
|
|
|
460
487
|
);
|
|
461
488
|
await wait(1000 * waitSeconds + 100);
|
|
462
489
|
// Then retry the launch and navigation.
|
|
463
|
-
return launch(report,
|
|
490
|
+
return launch(report, headEmulation, tempBrowserID, tempURL, retries - 1);
|
|
464
491
|
}
|
|
465
492
|
// Otherwise, i.e. if no retries remain:
|
|
466
493
|
else {
|
|
@@ -765,8 +792,7 @@ const doActs = async (report, opts = {}) => {
|
|
|
765
792
|
// Launch a browser, navigate to a page, and add the result to the act.
|
|
766
793
|
await launch(
|
|
767
794
|
report,
|
|
768
|
-
|
|
769
|
-
waits,
|
|
795
|
+
'high',
|
|
770
796
|
actLaunchSpecs[0],
|
|
771
797
|
actLaunchSpecs[1]
|
|
772
798
|
);
|
|
@@ -1516,8 +1542,7 @@ const doActs = async (report, opts = {}) => {
|
|
|
1516
1542
|
// Replace the browser and navigate to the URL.
|
|
1517
1543
|
await launch(
|
|
1518
1544
|
report,
|
|
1519
|
-
|
|
1520
|
-
waits,
|
|
1545
|
+
'high',
|
|
1521
1546
|
specs[0],
|
|
1522
1547
|
specs[1]
|
|
1523
1548
|
);
|
package/tests/testaro.js
CHANGED
|
@@ -44,410 +44,413 @@ const allRules = [
|
|
|
44
44
|
{
|
|
45
45
|
id: 'shoot0',
|
|
46
46
|
what: 'first page screenshot',
|
|
47
|
-
|
|
47
|
+
launchRole: 'owner',
|
|
48
48
|
timeOut: 5,
|
|
49
49
|
defaultOn: true
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
id: 'adbID',
|
|
53
53
|
what: 'elements with ambiguous or missing referenced descriptions',
|
|
54
|
-
|
|
54
|
+
launchRole: 'sharer',
|
|
55
55
|
timeOut: 5,
|
|
56
56
|
defaultOn: true
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
id: 'allCaps',
|
|
60
60
|
what: 'leaf elements with entirely upper-case text longer than 7 characters',
|
|
61
|
-
|
|
61
|
+
launchRole: 'sharer',
|
|
62
62
|
timeOut: 10,
|
|
63
63
|
defaultOn: true
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
id: 'allHidden',
|
|
67
67
|
what: 'page that is entirely or mostly hidden',
|
|
68
|
-
|
|
68
|
+
launchRole: 'sharer',
|
|
69
69
|
timeOut: 5,
|
|
70
70
|
defaultOn: true
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
73
|
id: 'allSlanted',
|
|
74
74
|
what: 'leaf elements with entirely italic or oblique text longer than 39 characters',
|
|
75
|
-
|
|
75
|
+
launchRole: 'sharer',
|
|
76
76
|
timeOut: 5,
|
|
77
77
|
defaultOn: true
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
id: 'altScheme',
|
|
81
81
|
what: 'img elements with alt attributes having URLs as their entire values',
|
|
82
|
-
|
|
82
|
+
launchRole: 'sharer',
|
|
83
83
|
timeOut: 5,
|
|
84
84
|
defaultOn: true
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
id: 'attVal',
|
|
88
88
|
what: 'elements with attributes having illicit values',
|
|
89
|
-
|
|
89
|
+
launchRole: 'sharer',
|
|
90
90
|
timeOut: 5,
|
|
91
91
|
defaultOn: false
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
id: 'dupAtt',
|
|
95
95
|
what: 'duplicate attribute values',
|
|
96
|
-
|
|
96
|
+
launchRole: 'sharer',
|
|
97
97
|
timeOut: 5,
|
|
98
98
|
defaultOn: true
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
101
|
id: 'autocomplete',
|
|
102
102
|
what: 'name and email inputs without autocomplete attributes',
|
|
103
|
-
|
|
103
|
+
launchRole: 'sharer',
|
|
104
104
|
timeOut: 5,
|
|
105
105
|
defaultOn: true
|
|
106
106
|
},
|
|
107
107
|
{
|
|
108
108
|
id: 'bulk',
|
|
109
109
|
what: 'large count of visible elements',
|
|
110
|
-
|
|
110
|
+
launchRole: 'sharer',
|
|
111
111
|
timeOut: 5,
|
|
112
112
|
defaultOn: true
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
id: 'captionLoc',
|
|
116
116
|
what: 'caption elements that are not first children of table elements',
|
|
117
|
-
|
|
117
|
+
launchRole: 'sharer',
|
|
118
118
|
timeOut: 5,
|
|
119
119
|
defaultOn: true
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
id: 'datalistRef',
|
|
123
123
|
what: 'elements with ambiguous or missing referenced datalist elements',
|
|
124
|
-
|
|
124
|
+
launchRole: 'sharer',
|
|
125
125
|
timeOut: 5,
|
|
126
126
|
defaultOn: true
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
id: 'distortion',
|
|
130
130
|
what: 'distorted text',
|
|
131
|
-
|
|
131
|
+
launchRole: 'sharer',
|
|
132
132
|
timeOut: 10,
|
|
133
133
|
defaultOn: true
|
|
134
134
|
},
|
|
135
135
|
{
|
|
136
136
|
id: 'docType',
|
|
137
137
|
what: 'document without a doctype property',
|
|
138
|
-
|
|
138
|
+
launchRole: 'sharer',
|
|
139
139
|
timeOut: 10,
|
|
140
140
|
defaultOn: true
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
id: 'dupAtt',
|
|
144
144
|
what: 'elements with duplicate attributes',
|
|
145
|
-
|
|
145
|
+
launchRole: 'sharer',
|
|
146
146
|
timeOut: 5,
|
|
147
147
|
defaultOn: true
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
id: 'embAc',
|
|
151
151
|
what: 'active elements embedded in links or buttons',
|
|
152
|
-
|
|
152
|
+
launchRole: 'sharer',
|
|
153
153
|
timeOut: 5,
|
|
154
154
|
defaultOn: true
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
157
|
id: 'headEl',
|
|
158
158
|
what: 'invalid elements within the head',
|
|
159
|
-
|
|
159
|
+
launchRole: 'sharer',
|
|
160
160
|
timeOut: 5,
|
|
161
161
|
defaultOn: true
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
id: 'headingAmb',
|
|
165
165
|
what: 'same-level sibling headings with identical texts',
|
|
166
|
-
|
|
166
|
+
launchRole: 'sharer',
|
|
167
167
|
timeOut: 5,
|
|
168
168
|
defaultOn: true
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
171
|
id: 'hr',
|
|
172
172
|
what: 'hr element instead of styles used for vertical segmentation',
|
|
173
|
-
|
|
173
|
+
launchRole: 'sharer',
|
|
174
174
|
timeOut: 5,
|
|
175
175
|
defaultOn: true
|
|
176
176
|
},
|
|
177
177
|
{
|
|
178
178
|
id: 'imageLink',
|
|
179
179
|
what: 'links with image files as their destinations',
|
|
180
|
-
|
|
180
|
+
launchRole: 'sharer',
|
|
181
181
|
timeOut: 5,
|
|
182
182
|
defaultOn: true
|
|
183
183
|
},
|
|
184
184
|
{
|
|
185
185
|
id: 'labClash',
|
|
186
186
|
what: 'labeling inconsistencies',
|
|
187
|
-
|
|
187
|
+
launchRole: 'sharer',
|
|
188
188
|
timeOut: 10,
|
|
189
189
|
defaultOn: true
|
|
190
190
|
},
|
|
191
191
|
{
|
|
192
192
|
id: 'legendLoc',
|
|
193
193
|
what: 'legend elements that are not first children of fieldset elements',
|
|
194
|
-
|
|
194
|
+
launchRole: 'sharer',
|
|
195
195
|
timeOut: 5,
|
|
196
196
|
defaultOn: true
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
199
|
id: 'lineHeight',
|
|
200
200
|
what: 'text with a line height less than 1.5 times its font size',
|
|
201
|
-
|
|
201
|
+
launchRole: 'sharer',
|
|
202
202
|
timeOut: 10,
|
|
203
203
|
defaultOn: true
|
|
204
204
|
},
|
|
205
205
|
{
|
|
206
206
|
id: 'linkAmb',
|
|
207
207
|
what: 'links with identical texts but different destinations',
|
|
208
|
-
|
|
208
|
+
launchRole: 'sharer',
|
|
209
209
|
timeOut: 5,
|
|
210
210
|
defaultOn: true
|
|
211
211
|
},
|
|
212
212
|
{
|
|
213
213
|
id: 'linkExt',
|
|
214
214
|
what: 'links that automatically open new windows',
|
|
215
|
-
|
|
215
|
+
launchRole: 'sharer',
|
|
216
216
|
timeOut: 5,
|
|
217
217
|
defaultOn: true
|
|
218
218
|
},
|
|
219
219
|
{
|
|
220
220
|
id: 'linkOldAtt',
|
|
221
221
|
what: 'links with deprecated attributes',
|
|
222
|
-
|
|
222
|
+
launchRole: 'sharer',
|
|
223
223
|
timeOut: 5,
|
|
224
224
|
defaultOn: true
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
id: 'linkTitle',
|
|
228
228
|
what: 'links with title attributes repeating text content',
|
|
229
|
-
|
|
229
|
+
launchRole: 'sharer',
|
|
230
230
|
timeOut: 5,
|
|
231
231
|
defaultOn: true
|
|
232
232
|
},
|
|
233
233
|
{
|
|
234
234
|
id: 'linkTo',
|
|
235
235
|
what: 'links without destinations',
|
|
236
|
-
|
|
236
|
+
launchRole: 'sharer',
|
|
237
237
|
timeOut: 5,
|
|
238
238
|
defaultOn: true
|
|
239
239
|
},
|
|
240
240
|
{
|
|
241
241
|
id: 'linkUl',
|
|
242
242
|
what: 'missing underlines on inline links',
|
|
243
|
-
|
|
243
|
+
launchRole: 'sharer',
|
|
244
244
|
timeOut: 10,
|
|
245
245
|
defaultOn: true
|
|
246
246
|
},
|
|
247
247
|
{
|
|
248
248
|
id: 'miniText',
|
|
249
249
|
what: 'text smaller than 11 pixels',
|
|
250
|
-
|
|
250
|
+
launchRole: 'sharer',
|
|
251
251
|
timeOut: 5,
|
|
252
252
|
defaultOn: true
|
|
253
253
|
},
|
|
254
254
|
{
|
|
255
255
|
id: 'nonTable',
|
|
256
256
|
what: 'table elements used for layout',
|
|
257
|
-
|
|
257
|
+
launchRole: 'sharer',
|
|
258
258
|
timeOut: 5,
|
|
259
259
|
defaultOn: true
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
262
|
id: 'optRoleSel',
|
|
263
263
|
what: 'Non-option elements with option roles that have no aria-selected attributes',
|
|
264
|
-
|
|
264
|
+
launchRole: 'sharer',
|
|
265
265
|
timeOut: 5,
|
|
266
266
|
defaultOn: true
|
|
267
267
|
},
|
|
268
268
|
{
|
|
269
269
|
id: 'phOnly',
|
|
270
270
|
what: 'input elements with placeholders but no accessible names',
|
|
271
|
-
|
|
271
|
+
launchRole: 'sharer',
|
|
272
272
|
timeOut: 5,
|
|
273
273
|
defaultOn: true
|
|
274
274
|
},
|
|
275
275
|
{
|
|
276
276
|
id: 'pseudoP',
|
|
277
277
|
what: 'adjacent br elements suspected of nonsemantically simulating p elements',
|
|
278
|
-
|
|
278
|
+
launchRole: 'sharer',
|
|
279
279
|
timeOut: 5,
|
|
280
280
|
defaultOn: true
|
|
281
281
|
},
|
|
282
282
|
{
|
|
283
283
|
id: 'radioSet',
|
|
284
284
|
what: 'radio buttons not grouped into standard field sets',
|
|
285
|
-
|
|
285
|
+
launchRole: 'sharer',
|
|
286
286
|
timeOut: 5,
|
|
287
287
|
defaultOn: true
|
|
288
288
|
},
|
|
289
289
|
{
|
|
290
290
|
id: 'role',
|
|
291
291
|
what: 'native-replacing explicit roles',
|
|
292
|
-
|
|
292
|
+
launchRole: 'sharer',
|
|
293
293
|
timeOut: 5,
|
|
294
294
|
defaultOn: true
|
|
295
295
|
},
|
|
296
296
|
{
|
|
297
297
|
id: 'secHeading',
|
|
298
298
|
what: 'headings that violate the logical level order in their sectioning containers',
|
|
299
|
-
|
|
299
|
+
launchRole: 'sharer',
|
|
300
300
|
timeOut: 5,
|
|
301
301
|
defaultOn: true
|
|
302
302
|
},
|
|
303
303
|
{
|
|
304
304
|
id: 'styleDiff',
|
|
305
305
|
what: 'style inconsistencies',
|
|
306
|
-
|
|
306
|
+
launchRole: 'sharer',
|
|
307
307
|
timeOut: 5,
|
|
308
308
|
defaultOn: true
|
|
309
309
|
},
|
|
310
310
|
{
|
|
311
311
|
id: 'targetSmall',
|
|
312
312
|
what: 'buttons, inputs, and non-inline links smaller than 44 pixels wide and high',
|
|
313
|
-
|
|
313
|
+
launchRole: 'sharer',
|
|
314
314
|
timeOut: 5,
|
|
315
315
|
defaultOn: true
|
|
316
316
|
},
|
|
317
317
|
{
|
|
318
318
|
id: 'targetTiny',
|
|
319
319
|
what: 'buttons, inputs, and non-inline links smaller than 24 pixels wide and high',
|
|
320
|
-
|
|
320
|
+
launchRole: 'sharer',
|
|
321
321
|
timeOut: 5,
|
|
322
322
|
defaultOn: true
|
|
323
323
|
},
|
|
324
324
|
{
|
|
325
325
|
id: 'textSem',
|
|
326
326
|
what: 'semantically vague elements i, b, and/or small',
|
|
327
|
-
|
|
327
|
+
launchRole: 'sharer',
|
|
328
328
|
timeOut: 10,
|
|
329
329
|
defaultOn: true
|
|
330
330
|
},
|
|
331
331
|
{
|
|
332
332
|
id: 'title',
|
|
333
333
|
what: 'page title',
|
|
334
|
-
|
|
334
|
+
launchRole: 'sharer',
|
|
335
335
|
timeOut: 5,
|
|
336
336
|
defaultOn: false
|
|
337
337
|
},
|
|
338
338
|
{
|
|
339
339
|
id: 'titledEl',
|
|
340
340
|
what: 'title attributes on inappropriate elements',
|
|
341
|
-
|
|
341
|
+
launchRole: 'sharer',
|
|
342
342
|
timeOut: 5,
|
|
343
343
|
defaultOn: true
|
|
344
344
|
},
|
|
345
345
|
{
|
|
346
346
|
id: 'zIndex',
|
|
347
347
|
what: 'non-default Z indexes',
|
|
348
|
-
|
|
348
|
+
launchRole: 'sharer',
|
|
349
349
|
timeOut: 5,
|
|
350
350
|
defaultOn: true
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
id: 'shoot1',
|
|
354
354
|
what: 'second page screenshot',
|
|
355
|
-
|
|
355
|
+
launchRole: 'owner',
|
|
356
356
|
timeOut: 5,
|
|
357
357
|
defaultOn: true
|
|
358
358
|
},
|
|
359
359
|
{
|
|
360
360
|
id: 'motion',
|
|
361
361
|
what: 'motion without user request, measured across tests',
|
|
362
|
-
|
|
362
|
+
launchRole: 'sharer',
|
|
363
363
|
timeOut: 5,
|
|
364
364
|
defaultOn: true
|
|
365
365
|
},
|
|
366
366
|
{
|
|
367
367
|
id: 'buttonMenu',
|
|
368
368
|
what: 'nonstandard keyboard navigation between items of button-controlled menus',
|
|
369
|
-
|
|
369
|
+
launchRole: 'waster',
|
|
370
370
|
timeOut: 15,
|
|
371
371
|
defaultOn: true
|
|
372
372
|
},
|
|
373
373
|
{
|
|
374
374
|
id: 'elements',
|
|
375
375
|
what: 'data on specified elements',
|
|
376
|
-
|
|
376
|
+
launchRole: 'waster',
|
|
377
377
|
timeOut: 10,
|
|
378
378
|
defaultOn: false
|
|
379
379
|
},
|
|
380
380
|
{
|
|
381
381
|
id: 'focAll',
|
|
382
382
|
what: 'discrepancies between focusable and Tab-focused elements',
|
|
383
|
-
|
|
383
|
+
launchRole: 'waster',
|
|
384
384
|
timeOut: 10,
|
|
385
385
|
defaultOn: true
|
|
386
386
|
},
|
|
387
387
|
{
|
|
388
388
|
id: 'focInd',
|
|
389
389
|
what: 'missing and nonstandard focus indicators',
|
|
390
|
-
|
|
390
|
+
launchRole: 'waster',
|
|
391
391
|
timeOut: 10,
|
|
392
392
|
defaultOn: true
|
|
393
393
|
},
|
|
394
394
|
{
|
|
395
395
|
id: 'focOp',
|
|
396
396
|
what: 'Tab-focusable elements that are not operable',
|
|
397
|
-
|
|
397
|
+
launchRole: 'waster',
|
|
398
398
|
timeOut: 5,
|
|
399
399
|
defaultOn: true
|
|
400
400
|
},
|
|
401
401
|
{
|
|
402
402
|
id: 'focVis',
|
|
403
403
|
what: 'links that are not entirely visible when focused',
|
|
404
|
-
|
|
404
|
+
launchRole: 'waster',
|
|
405
405
|
timeOut: 10,
|
|
406
406
|
defaultOn: true
|
|
407
407
|
},
|
|
408
408
|
{
|
|
409
409
|
id: 'hover',
|
|
410
410
|
what: 'hover-caused content changes',
|
|
411
|
-
|
|
411
|
+
launchRole: 'waster',
|
|
412
412
|
timeOut: 10,
|
|
413
413
|
defaultOn: true
|
|
414
414
|
},
|
|
415
415
|
{
|
|
416
416
|
id: 'hovInd',
|
|
417
417
|
what: 'hover indication nonstandard',
|
|
418
|
-
|
|
418
|
+
launchRole: 'waster',
|
|
419
419
|
timeOut: 10,
|
|
420
420
|
defaultOn: true
|
|
421
421
|
},
|
|
422
|
-
{
|
|
423
|
-
id: 'motionSolo',
|
|
424
|
-
what: 'motion without user request, measured within this test',
|
|
425
|
-
contaminator: true,
|
|
426
|
-
timeOut: 15,
|
|
427
|
-
defaultOn: false
|
|
428
|
-
},
|
|
429
422
|
{
|
|
430
423
|
id: 'opFoc',
|
|
431
424
|
what: 'operable elements that are not Tab-focusable',
|
|
432
|
-
|
|
425
|
+
launchRole: 'waster',
|
|
433
426
|
timeOut: 10,
|
|
434
427
|
defaultOn: true
|
|
435
428
|
},
|
|
436
429
|
{
|
|
437
430
|
id: 'tabNav',
|
|
438
431
|
what: 'nonstandard keyboard navigation between elements with the tab role',
|
|
439
|
-
|
|
432
|
+
launchRole: 'waster',
|
|
440
433
|
timeOut: 10,
|
|
441
434
|
defaultOn: true
|
|
442
435
|
},
|
|
436
|
+
{
|
|
437
|
+
id: 'motionSolo',
|
|
438
|
+
what: 'motion without user request, measured within this test',
|
|
439
|
+
launchRole: 'waster',
|
|
440
|
+
timeOut: 15,
|
|
441
|
+
defaultOn: false
|
|
442
|
+
},
|
|
443
443
|
{
|
|
444
444
|
id: 'textNodes',
|
|
445
445
|
what: 'data on specified text nodes',
|
|
446
|
-
|
|
446
|
+
launchRole: 'waster',
|
|
447
447
|
timeOut: 10,
|
|
448
448
|
defaultOn: false
|
|
449
449
|
}
|
|
450
450
|
];
|
|
451
|
+
const headedBrowser = process.env.HEADED_BROWSER === 'true';
|
|
452
|
+
const debug = process.env.DEBUG === 'true';
|
|
453
|
+
const waits = Number.parseInt(process.env.WAITS) || 0;
|
|
451
454
|
const timeoutMultiplier = Number.parseFloat(process.env.TIMEOUT_MULTIPLIER) || 1;
|
|
452
455
|
|
|
453
456
|
// ERROR HANDLER
|
|
@@ -485,11 +488,11 @@ const wait = ms => {
|
|
|
485
488
|
};
|
|
486
489
|
// Conducts and reports Testaro tests.
|
|
487
490
|
exports.reporter = async (page, report, actIndex) => {
|
|
488
|
-
const url = await page.url();
|
|
489
491
|
const act = report.acts[actIndex];
|
|
490
492
|
const {args, stopOnFail, withItems} = act;
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
+
const target = act.target || report.target;
|
|
494
|
+
const url = target.url;
|
|
495
|
+
const browserID = act.launch ? act.launch.browserID || report.browserID : report.browserID;
|
|
493
496
|
const argRules = args ? Object.keys(args) : null;
|
|
494
497
|
// Get the specification of rules to be tested for.
|
|
495
498
|
const ruleSpec = act.rules
|
|
@@ -528,34 +531,36 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
528
531
|
: allRules.filter(rule => rule.defaultOn && ! allRuleIDs.includes(rule.id));
|
|
529
532
|
const jobRules = allRules.filter(rule => jobRuleIDs.includes(rule.id));
|
|
530
533
|
const testTimes = [];
|
|
531
|
-
let contaminatorsStarted = false;
|
|
532
534
|
// For each rule to be tested for:
|
|
533
|
-
for (const
|
|
535
|
+
for (const ruleIndexString in jobRules) {
|
|
536
|
+
const ruleIndex = Number.parseInt(ruleIndexString);
|
|
537
|
+
const rule = jobRules[ruleIndex];
|
|
534
538
|
const ruleID = rule.id;
|
|
535
539
|
console.log(`Starting rule ${ruleID}`);
|
|
536
|
-
|
|
537
|
-
const
|
|
538
|
-
//
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
540
|
+
// Make the browser emulate headedness in all cases, because performance does not suffer.
|
|
541
|
+
const headEmulation = ruleID.startsWith('shoot') ? 'high' : 'high';
|
|
542
|
+
// Get whether it needs a new browser launched.
|
|
543
|
+
const needsLaunch = ruleIndex
|
|
544
|
+
&& jobRules[ruleIndex - 1].launchRole !== 'sharer'
|
|
545
|
+
&& rule.launchRole !== 'owner'
|
|
546
|
+
|| ! ruleIndex;
|
|
547
|
+
const pageClosed = page && page.isClosed();
|
|
548
|
+
// If it does, or if the page has closed:
|
|
549
|
+
if (needsLaunch || pageClosed) {
|
|
550
|
+
// If the page has closed when it is expected to be open:
|
|
551
|
+
if (pageClosed && ! needsLaunch) {
|
|
542
552
|
// Report this.
|
|
543
553
|
console.log(`WARNING: Relaunching browser for test ${rule} after abnormal closure`);
|
|
544
554
|
}
|
|
545
555
|
// Replace the browser and the page and navigate to the target.
|
|
546
556
|
await launch(
|
|
547
557
|
report,
|
|
548
|
-
|
|
549
|
-
Number.parseInt(process.env.WAITS) || 0,
|
|
558
|
+
headEmulation,
|
|
550
559
|
browserID,
|
|
551
560
|
url
|
|
552
561
|
);
|
|
553
562
|
page = require('../run').page;
|
|
554
563
|
}
|
|
555
|
-
// If the rule is a contaminator, ensure that future tests use new browsers.
|
|
556
|
-
if (isContaminator) {
|
|
557
|
-
contaminatorsStarted = true;
|
|
558
|
-
}
|
|
559
564
|
// Report crashes and disconnections during this test.
|
|
560
565
|
let crashHandler;
|
|
561
566
|
let disconnectHandler;
|
|
@@ -573,7 +578,7 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
573
578
|
};
|
|
574
579
|
browser.on('disconnected', disconnectHandler);
|
|
575
580
|
}
|
|
576
|
-
// Initialize an argument array.
|
|
581
|
+
// Initialize an argument array for reporter or jsonTest.
|
|
577
582
|
const ruleArgs = [page, withItems];
|
|
578
583
|
const ruleFileNames = await fs.readdir(`${__dirname}/../testaro`);
|
|
579
584
|
const isJS = ruleFileNames.includes(`${ruleID}.js`);
|
|
@@ -658,11 +663,10 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
658
663
|
`WARNING: Retry ${3 - testRetries--} of test ${ruleID} starting after page closed`
|
|
659
664
|
);
|
|
660
665
|
await wait(2000);
|
|
661
|
-
// Replace the browser and the page and navigate to the target.
|
|
666
|
+
// Replace the browser and the page in the run module and navigate to the target.
|
|
662
667
|
await launch(
|
|
663
668
|
report,
|
|
664
|
-
|
|
665
|
-
Number.parseInt(process.env.WAITS) || 0,
|
|
669
|
+
headEmulation,
|
|
666
670
|
report.browserID,
|
|
667
671
|
url
|
|
668
672
|
);
|