testaro 22.0.0 → 23.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/README.md +27 -15
- package/actSpecs.js +4 -3
- package/dirWatch.js +1 -1
- package/package.json +1 -1
- package/procs/nav.js +220 -0
- package/procs/testaro.js +2 -2
- package/procs/visChange.js +1 -0
- package/run.js +494 -684
- package/testaro/bulk.js +7 -4
- package/tests/testaro.js +10 -8
- /package/{standardize.js → procs/standardize.js} +0 -0
package/README.md
CHANGED
|
@@ -144,14 +144,9 @@ Here is an example of a job:
|
|
|
144
144
|
{
|
|
145
145
|
type: 'launch',
|
|
146
146
|
which: 'chromium',
|
|
147
|
+
url: 'https://www.w3c.org',
|
|
147
148
|
what: 'Chromium browser'
|
|
148
149
|
},
|
|
149
|
-
{
|
|
150
|
-
type: 'url',
|
|
151
|
-
which: 'https://www.w3c.org',
|
|
152
|
-
what: 'World Wide Web Consortium',
|
|
153
|
-
id: 'w3c'
|
|
154
|
-
},
|
|
155
150
|
{
|
|
156
151
|
type: 'test',
|
|
157
152
|
which: 'alfa',
|
|
@@ -174,9 +169,8 @@ Here is an example of a job:
|
|
|
174
169
|
}
|
|
175
170
|
```
|
|
176
171
|
|
|
177
|
-
This job contains
|
|
178
|
-
1. open a page in the Chromium browser
|
|
179
|
-
1. navigate to a specified URL
|
|
172
|
+
This job contains two _acts_, telling Testaro to:
|
|
173
|
+
1. open a page in the Chromium browser and navigate to a specified URL
|
|
180
174
|
1. perform two of the tests of the `alfa` tool (the tests for rules `r25` and `r71`) on that URL
|
|
181
175
|
|
|
182
176
|
Job properties:
|
|
@@ -306,7 +300,7 @@ Each act object has a `type` property and optionally has a `name` property (used
|
|
|
306
300
|
|
|
307
301
|
#### Act sequence
|
|
308
302
|
|
|
309
|
-
The first
|
|
303
|
+
The first act in any job has the type `launch`, as shown in the example above. It launches a browser and then uses it to visit a URL.
|
|
310
304
|
|
|
311
305
|
#### Act types
|
|
312
306
|
|
|
@@ -338,11 +332,9 @@ When the texts of multiple elements of the same type will contain the same `whic
|
|
|
338
332
|
|
|
339
333
|
##### Navigations
|
|
340
334
|
|
|
341
|
-
An example of a **navigation** is the act of type `
|
|
342
|
-
|
|
343
|
-
Once you have included a `url` act in a job, you do not need to add more `url` acts unless you want the browser to visit a different URL or revisit the same URL.
|
|
335
|
+
An example of a **navigation** is the act of type `launch` above.
|
|
344
336
|
|
|
345
|
-
If any act alters the page, you can restore the page to its original state for the next act by inserting new `launch`
|
|
337
|
+
If any act alters the page, you can restore the page to its original state for the next act by inserting a new `launch` act (and, if necessary, additional page-specific acts) between them.
|
|
346
338
|
|
|
347
339
|
Another navigation example is:
|
|
348
340
|
|
|
@@ -585,7 +577,7 @@ The `rules` property for `testaro` is an array whose first item is either `'y'`
|
|
|
585
577
|
|
|
586
578
|
The `testaro` tool (like the `ibm` tool) has a `withItems` property. If you set it to `false`, the `standardResult` object of `testaro` will contain an `instances` property with summaries that identify issues and instance counts. If you set it to `true`, some of the instances will be itemized.
|
|
587
579
|
|
|
588
|
-
Unlike any other tool, the `testaro`
|
|
580
|
+
Unlike any other tool, the `testaro` tool requires a `stopOnFail` property, which specifies whether a failure to conform to any rule (i.e. any value of `totals` other than `[0, 0, 0, 0]`) should terminate the execution of tests for the remaining rules.
|
|
589
581
|
|
|
590
582
|
Warnings in the `testaro/hover.js`, `testaro/motion.js`, and `procs/visChange.js` files advise you to avoid launching particular browser types for the performance of particular Testaro tests.
|
|
591
583
|
|
|
@@ -622,6 +614,26 @@ This act checks the result of the previous act to determine whether its `result.
|
|
|
622
614
|
|
|
623
615
|
A `next` act can use a `next` property instead of a `jump` property. The value of the `next` property is an act name. It tells Testaro to continue performing acts starting with the act having that value as the value of its `name` property.
|
|
624
616
|
|
|
617
|
+
#### Browser types
|
|
618
|
+
|
|
619
|
+
After any act in a job, you can change the browser type by inserting a `launch` act. One reason for specifying a particular browser type is that particular tests have different results with different browser types. Another is that you may wish to perform tests with more than a single browser type.
|
|
620
|
+
|
|
621
|
+
The warning comments in the `testaro/hover.js` and `testaro/motion.js` files state that those tests operate correctly only with the `webkit` browser type.
|
|
622
|
+
|
|
623
|
+
When you want to run some tests of a tool with one browser type and other tests of the same tool with another browser type, you can do so by splitting the rules into two test acts. For example, one test act can specify the rules as
|
|
624
|
+
|
|
625
|
+
```javascript
|
|
626
|
+
['y', 'r15', 'r54']
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
and the other test act can specify the rules as
|
|
630
|
+
|
|
631
|
+
```javascript
|
|
632
|
+
['n', 'r15', 'r54']
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
Before each test act, you can ensure that the latest `launch` act has specified the browser type to be used in that test act.
|
|
636
|
+
|
|
625
637
|
#### `actSpecs` file
|
|
626
638
|
|
|
627
639
|
##### Introduction
|
package/actSpecs.js
CHANGED
|
@@ -27,9 +27,10 @@ exports.actSpecs = {
|
|
|
27
27
|
launch: [
|
|
28
28
|
'Launch a Playwright browser',
|
|
29
29
|
{
|
|
30
|
-
which: [true, 'string', 'isBrowserType', '
|
|
31
|
-
|
|
32
|
-
lowMotion: [false, 'boolean', '', 'set reduced-motion option if true']
|
|
30
|
+
which: [true, 'string', 'isBrowserType', 'chromium, firefox, or webkit'],
|
|
31
|
+
url: [true, 'string', 'isURL', 'initial URL to navigate to'],
|
|
32
|
+
lowMotion: [false, 'boolean', '', 'set reduced-motion option if true'],
|
|
33
|
+
what: [false, 'string', 'hasLength', 'comment']
|
|
33
34
|
}
|
|
34
35
|
],
|
|
35
36
|
link: [
|
package/dirWatch.js
CHANGED
package/package.json
CHANGED
package/procs/nav.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// nav
|
|
2
|
+
|
|
3
|
+
// ######## IMPORTS
|
|
4
|
+
|
|
5
|
+
// Playwright package.
|
|
6
|
+
const playwright = require('playwright');
|
|
7
|
+
|
|
8
|
+
// ######## CONSTANTS
|
|
9
|
+
|
|
10
|
+
// Strings in log messages indicating errors.
|
|
11
|
+
const errorWords = [
|
|
12
|
+
'but not used',
|
|
13
|
+
'content security policy',
|
|
14
|
+
'deprecated',
|
|
15
|
+
'error',
|
|
16
|
+
'exception',
|
|
17
|
+
'expected',
|
|
18
|
+
'failed',
|
|
19
|
+
'invalid',
|
|
20
|
+
'missing',
|
|
21
|
+
'non-standard',
|
|
22
|
+
'not supported',
|
|
23
|
+
'refused',
|
|
24
|
+
'requires',
|
|
25
|
+
'sorry',
|
|
26
|
+
'suspicious',
|
|
27
|
+
'unrecognized',
|
|
28
|
+
'violates',
|
|
29
|
+
'warning'
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
// ######## VARIABLES
|
|
33
|
+
|
|
34
|
+
let browser;
|
|
35
|
+
|
|
36
|
+
// ######## FUNCTIONS
|
|
37
|
+
|
|
38
|
+
// Returns a string with any final slash removed.
|
|
39
|
+
const deSlash = string => string.endsWith('/') ? string.slice(0, -1) : string;
|
|
40
|
+
// Visits a URL and returns the response of the server.
|
|
41
|
+
const goTo = async (report, page, url, timeout, waitUntil) => {
|
|
42
|
+
if (url.startsWith('file://')) {
|
|
43
|
+
url = url.replace('file://', `file://${__dirname}/`);
|
|
44
|
+
}
|
|
45
|
+
// Visit the URL.
|
|
46
|
+
const startTime = Date.now();
|
|
47
|
+
try {
|
|
48
|
+
const response = await page.goto(url, {
|
|
49
|
+
timeout,
|
|
50
|
+
waitUntil
|
|
51
|
+
});
|
|
52
|
+
report.jobData.visitLatency += Math.round((Date.now() - startTime) / 1000);
|
|
53
|
+
const httpStatus = response.status();
|
|
54
|
+
// If the response status was normal:
|
|
55
|
+
if ([200, 304].includes(httpStatus) || url.startsWith('file:')) {
|
|
56
|
+
// If the browser was redirected in violation of a strictness requirement:
|
|
57
|
+
const actualURL = page.url();
|
|
58
|
+
if (report.strict && deSlash(actualURL) !== deSlash(url)) {
|
|
59
|
+
// Return an error.
|
|
60
|
+
console.log(`ERROR: Visit to ${url} redirected to ${actualURL}`);
|
|
61
|
+
return {
|
|
62
|
+
exception: 'badRedirection'
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Otherwise, i.e. if no prohibited redirection occurred:
|
|
66
|
+
else {
|
|
67
|
+
// Press the Escape key to dismiss any modal dialog.
|
|
68
|
+
await page.keyboard.press('Escape');
|
|
69
|
+
// Return the response.
|
|
70
|
+
return response;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Otherwise, i.e. if the response status was abnormal:
|
|
74
|
+
else {
|
|
75
|
+
// Return an error.
|
|
76
|
+
console.log(`ERROR: Visit to ${url} got status ${httpStatus}`);
|
|
77
|
+
report.jobData.visitRejectionCount++;
|
|
78
|
+
return {
|
|
79
|
+
error: 'badStatus'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch(error) {
|
|
84
|
+
console.log(`ERROR visiting ${url} (${error.message.slice(0, 200)})`);
|
|
85
|
+
return {
|
|
86
|
+
error: 'noVisit'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// Closes the current browser.
|
|
91
|
+
const browserClose = async () => {
|
|
92
|
+
if (browser) {
|
|
93
|
+
const browserType = browser.browserType().name();
|
|
94
|
+
let contexts = browser.contexts();
|
|
95
|
+
for (const context of contexts) {
|
|
96
|
+
await context.close();
|
|
97
|
+
contexts = browser.contexts();
|
|
98
|
+
}
|
|
99
|
+
await browser.close();
|
|
100
|
+
browser = null;
|
|
101
|
+
console.log(`${browserType} browser closed`);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
// Launches a browser, navigates to a URL, and returns the status.
|
|
105
|
+
const launch = async (report, typeName, url, debug, waits, isLowMotion = false) => {
|
|
106
|
+
// If the specified browser type exists:
|
|
107
|
+
const browserType = playwright[typeName];
|
|
108
|
+
if (browserType) {
|
|
109
|
+
// Close the current browser, if any.
|
|
110
|
+
await browserClose();
|
|
111
|
+
// Launch a browser of the specified type.
|
|
112
|
+
const browserOptions = {
|
|
113
|
+
logger: {
|
|
114
|
+
isEnabled: () => false,
|
|
115
|
+
log: (name, severity, message) => console.log(message.slice(0, 100))
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
if (debug) {
|
|
119
|
+
browserOptions.headless = false;
|
|
120
|
+
}
|
|
121
|
+
if (waits) {
|
|
122
|
+
browserOptions.slowMo = waits;
|
|
123
|
+
}
|
|
124
|
+
browser = await browserType.launch(browserOptions)
|
|
125
|
+
// If the launch failed:
|
|
126
|
+
.catch(async error => {
|
|
127
|
+
healthy = false;
|
|
128
|
+
console.log(`ERROR launching browser (${errorStart(error)})`);
|
|
129
|
+
// Return this.
|
|
130
|
+
return false;
|
|
131
|
+
});
|
|
132
|
+
// Open a context (i.e. browser tab), with reduced motion if specified.
|
|
133
|
+
const options = {reduceMotion: isLowMotion ? 'reduce' : 'no-preference'};
|
|
134
|
+
browserContext = await browser.newContext(options);
|
|
135
|
+
// When a page (i.e. browser tab) is added to the browser context (i.e. browser window):
|
|
136
|
+
browserContext.on('page', async page => {
|
|
137
|
+
// Make the page current.
|
|
138
|
+
currentPage = page;
|
|
139
|
+
// If it emits a message:
|
|
140
|
+
page.on('console', msg => {
|
|
141
|
+
const msgText = msg.text();
|
|
142
|
+
let indentedMsg = '';
|
|
143
|
+
// If debugging is on:
|
|
144
|
+
if (debug) {
|
|
145
|
+
// Log a summary of the message on the console.
|
|
146
|
+
const parts = [msgText.slice(0, 75)];
|
|
147
|
+
if (msgText.length > 75) {
|
|
148
|
+
parts.push(msgText.slice(75, 150));
|
|
149
|
+
if (msgText.length > 150) {
|
|
150
|
+
const tail = msgText.slice(150).slice(-150);
|
|
151
|
+
if (msgText.length > 300) {
|
|
152
|
+
parts.push('...');
|
|
153
|
+
}
|
|
154
|
+
parts.push(tail.slice(0, 75));
|
|
155
|
+
if (tail.length > 75) {
|
|
156
|
+
parts.push(tail.slice(75));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
indentedMsg = parts.map(part => ` | ${part}`).join('\n');
|
|
161
|
+
console.log(`\n${indentedMsg}`);
|
|
162
|
+
}
|
|
163
|
+
// Add statistics on the message to the report.
|
|
164
|
+
const msgTextLC = msgText.toLowerCase();
|
|
165
|
+
const msgLength = msgText.length;
|
|
166
|
+
report.jobData.logCount++;
|
|
167
|
+
report.jobData.logSize += msgLength;
|
|
168
|
+
if (errorWords.some(word => msgTextLC.includes(word))) {
|
|
169
|
+
report.jobData.errorLogCount++;
|
|
170
|
+
report.jobData.errorLogSize += msgLength;
|
|
171
|
+
}
|
|
172
|
+
const msgLC = msgText.toLowerCase();
|
|
173
|
+
if (
|
|
174
|
+
msgText.includes('403') && (msgLC.includes('status')
|
|
175
|
+
|| msgLC.includes('prohibited'))
|
|
176
|
+
) {
|
|
177
|
+
report.jobData.prohibitedCount++;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
// Open the first page of the context.
|
|
182
|
+
currentPage = await browserContext.newPage();
|
|
183
|
+
try {
|
|
184
|
+
// Wait until it is stable.
|
|
185
|
+
await currentPage.waitForLoadState('domcontentloaded', {timeout: 5000});
|
|
186
|
+
// Navigate to the specified URL.
|
|
187
|
+
const navResult = await goTo(report, currentPage, url, 15000, 'domcontentloaded');
|
|
188
|
+
// If the navigation failed:
|
|
189
|
+
if (navResult.error) {
|
|
190
|
+
// Return this.
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
// Otherwise, i.e. if it succeeded:
|
|
194
|
+
else if (! navResult.error) {
|
|
195
|
+
// Update the name of the current browser type and store it in the page.
|
|
196
|
+
currentPage.browserTypeName = typeName;
|
|
197
|
+
// Return the browser context and the page.
|
|
198
|
+
return {
|
|
199
|
+
browserContext,
|
|
200
|
+
currentPage
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// If it fails to become stable by the deadline:
|
|
205
|
+
catch(error) {
|
|
206
|
+
// Return this.
|
|
207
|
+
console.log(`ERROR: Blank page load in new tab timed out (${error.message})`);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Otherwise, i.e. if it does not exist:
|
|
212
|
+
else {
|
|
213
|
+
// Return this.
|
|
214
|
+
console.log(`ERROR: Browser of type ${typeName} could not be launched`);
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
exports.browserClose = browserClose;
|
|
219
|
+
exports.goTo = goTo;
|
|
220
|
+
exports.launch = launch;
|
package/procs/testaro.js
CHANGED
|
@@ -9,7 +9,7 @@ const {getLocatorData} = require('../procs/getLocatorData');
|
|
|
9
9
|
|
|
10
10
|
// ########## CONSTANTS
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const sampleMax = 100;
|
|
13
13
|
|
|
14
14
|
// ########## FUNCTIONS
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ exports.init = async (page, locAllSelector, options = {}) => {
|
|
|
19
19
|
const locPop = page.locator(locAllSelector, options);
|
|
20
20
|
const locPops = await locPop.all();
|
|
21
21
|
const populationSize = locPops.length;
|
|
22
|
-
sampleSize = Math.min(
|
|
22
|
+
const sampleSize = Math.min(sampleMax, populationSize);
|
|
23
23
|
const locIndexes = getSample(locPops, sampleSize);
|
|
24
24
|
const allLocs = locIndexes.map(index => locPops[index]);
|
|
25
25
|
const result = {
|
package/procs/visChange.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
visChange
|
|
3
3
|
This procedure reports a change in the visible content of a page between two times, optionally
|
|
4
4
|
hovering over a locator-defined element immediately after the first time.
|
|
5
|
+
|
|
5
6
|
WARNING: This test uses the Playwright page.screenshot method, which produces incorrect results
|
|
6
7
|
when the browser type is chromium and is not implemented for the firefox browser type. The only
|
|
7
8
|
browser type usable with this test is webkit.
|