testaro 40.0.2 → 41.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 +213 -217
- package/actSpecs.js +3 -3
- package/package.json +1 -1
- package/procs/device.js +59 -0
- package/procs/standardize.js +16 -8
- package/run.js +192 -178
- package/tests/ed11y.js +2 -2
package/actSpecs.js
CHANGED
|
@@ -54,9 +54,9 @@ exports.actSpecs = {
|
|
|
54
54
|
launch: [
|
|
55
55
|
'Launch a Playwright browser',
|
|
56
56
|
{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
url: [false, 'string', 'isURL', 'initial URL to navigate to'],
|
|
58
|
+
deviceID: [false, 'string', 'isDeviceID', 'Playwright device ID if not default, e.g. iPhone 6 landscape'],
|
|
59
|
+
browserID: [false, 'string', 'isBrowserID', 'chromium, firefox, or webkit if not job default'],
|
|
60
60
|
lowMotion: [false, 'boolean', '', 'set reduced-motion option if true'],
|
|
61
61
|
what: [false, 'string', 'hasLength', 'comment']
|
|
62
62
|
}
|
package/package.json
CHANGED
package/procs/device.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
© 2024 CVS Health and/or one of its affiliates. All rights reserved.
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// device
|
|
24
|
+
|
|
25
|
+
// IMPORTS
|
|
26
|
+
|
|
27
|
+
const {devices} = require('playwright');
|
|
28
|
+
|
|
29
|
+
// FUNCTIONS
|
|
30
|
+
|
|
31
|
+
// Returns whether a device ID is valid.
|
|
32
|
+
exports.isDeviceID = deviceID => deviceID === 'default' || !! devices[deviceID];
|
|
33
|
+
|
|
34
|
+
// Returns options for the browser.newContext() function.
|
|
35
|
+
exports.getDeviceOptions = (deviceID, motion) => {
|
|
36
|
+
const options = {
|
|
37
|
+
reduceMotion: motion
|
|
38
|
+
};
|
|
39
|
+
// If a non-default device was specified:
|
|
40
|
+
if (deviceID && deviceID !== 'default') {
|
|
41
|
+
// Get its properties if it exists.
|
|
42
|
+
const deviceProperties = devices[deviceID];
|
|
43
|
+
// Return options or report the device as invalid.
|
|
44
|
+
if (deviceProperties) {
|
|
45
|
+
return {
|
|
46
|
+
... options,
|
|
47
|
+
... deviceProperties
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Otherwise, i.e. if no non-default device was specified:
|
|
55
|
+
else {
|
|
56
|
+
// Return options.
|
|
57
|
+
return options;
|
|
58
|
+
}
|
|
59
|
+
};
|
package/procs/standardize.js
CHANGED
|
@@ -40,6 +40,8 @@ const cap = rawString => {
|
|
|
40
40
|
return '';
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
+
// Returns whether an id attribute value is valid without character escaping.
|
|
44
|
+
const isBadID = id => /[^-\w]|^\d|^--|^-\d/.test(id);
|
|
43
45
|
// Returns the tag name and the value of an id attribute from a substring of HTML code.
|
|
44
46
|
const getIdentifiers = code => {
|
|
45
47
|
let tagName = '';
|
|
@@ -47,7 +49,7 @@ const getIdentifiers = code => {
|
|
|
47
49
|
// If the substring includes the start tag of an element:
|
|
48
50
|
if (code && typeof code === 'string' && code.length && /<\s*[a-zA-Z]/.test(code)) {
|
|
49
51
|
// Get the first start tag in the substring.
|
|
50
|
-
const startTag = code.replace(/^.*?<(?=[a-zA-Z])/s, '').replace(
|
|
52
|
+
const startTag = code.replace(/^.*?<(?=[a-zA-Z])/s, '').replace(/[^a-zA-Z].*$/gs, '').trim();
|
|
51
53
|
// If it exists:
|
|
52
54
|
if (startTag && startTag.length) {
|
|
53
55
|
// Get its tag name, upper-cased.
|
|
@@ -57,6 +59,11 @@ const getIdentifiers = code => {
|
|
|
57
59
|
if (idArray && idArray.length === 2) {
|
|
58
60
|
id = idArray[1];
|
|
59
61
|
}
|
|
62
|
+
// If the id value is invalid without character escaping:
|
|
63
|
+
if (isBadID(id)) {
|
|
64
|
+
// Remove it.
|
|
65
|
+
id = '';
|
|
66
|
+
}
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
69
|
return [tagName, id];
|
|
@@ -207,7 +214,7 @@ const doHTMLCS = (result, standardResult, severity) => {
|
|
|
207
214
|
what,
|
|
208
215
|
ordinalSeverity: ['Warning', '', '', 'Error'].indexOf(severity),
|
|
209
216
|
tagName: tagName.toUpperCase(),
|
|
210
|
-
id: id.slice(1),
|
|
217
|
+
id: isBadID(id.slice(1)) ? '' : id.slice(1),
|
|
211
218
|
location: {
|
|
212
219
|
doc: 'dom',
|
|
213
220
|
type: '',
|
|
@@ -317,7 +324,7 @@ const doWAVE = (result, standardResult, categoryName) => {
|
|
|
317
324
|
if (finalTerm.includes('#')) {
|
|
318
325
|
const finalArray = finalTerm.split('#');
|
|
319
326
|
tagName = finalArray[0].replace(/:.*/, '');
|
|
320
|
-
id = finalArray[1];
|
|
327
|
+
id = isBadID(finalArray[1]) ? '' : finalArray[1];
|
|
321
328
|
}
|
|
322
329
|
else {
|
|
323
330
|
tagName = finalTerm.replace(/:.*/, '');
|
|
@@ -450,14 +457,16 @@ const convert = (toolName, data, result, standardResult) => {
|
|
|
450
457
|
if (! tagName && finalRuleID.endsWith('_svg')) {
|
|
451
458
|
tagName = 'SVG';
|
|
452
459
|
}
|
|
453
|
-
const excerpt = ruleResult.element && ruleResult.element.html
|
|
460
|
+
const excerpt = ruleResult.element && ruleResult.element.html.replace(/\s+/g, ' ')
|
|
461
|
+
|| '';
|
|
454
462
|
if (! tagName && /^<[a-z]+[ >]/.test(excerpt)) {
|
|
455
|
-
tagName = excerpt.slice(1).replace(/[ >]
|
|
463
|
+
tagName = excerpt.slice(1).replace(/[ >].+/, '').toUpperCase();
|
|
456
464
|
}
|
|
457
465
|
const idDraft = excerpt && excerpt.replace(/^[^[>]+id="/, 'id=').replace(/".*$/, '');
|
|
458
|
-
const
|
|
466
|
+
const idFinal = idDraft && idDraft.length > 3 && idDraft.startsWith('id=')
|
|
459
467
|
? idDraft.slice(3)
|
|
460
468
|
: '';
|
|
469
|
+
const id = idFinal === '' || isBadID(idFinal) ? '' : idFinal;
|
|
461
470
|
const instance = {
|
|
462
471
|
ruleID: finalRuleID,
|
|
463
472
|
what,
|
|
@@ -513,7 +522,7 @@ const convert = (toolName, data, result, standardResult) => {
|
|
|
513
522
|
what,
|
|
514
523
|
ordinalSeverity: 0,
|
|
515
524
|
tagName,
|
|
516
|
-
id,
|
|
525
|
+
id: isBadID(id) ? '' : id,
|
|
517
526
|
location: {
|
|
518
527
|
doc: 'dom',
|
|
519
528
|
type: 'box',
|
|
@@ -567,7 +576,6 @@ const convert = (toolName, data, result, standardResult) => {
|
|
|
567
576
|
if (result.rawPage) {
|
|
568
577
|
doNuVal(result, standardResult, 'rawPage');
|
|
569
578
|
}
|
|
570
|
-
const {instances} = standardResult;
|
|
571
579
|
}
|
|
572
580
|
// qualWeb
|
|
573
581
|
else if (
|
package/run.js
CHANGED
|
@@ -37,6 +37,8 @@ const {standardize} = require('./procs/standardize');
|
|
|
37
37
|
const {identify} = require('./procs/identify');
|
|
38
38
|
// Module to send a notice to an observer.
|
|
39
39
|
const {tellServer} = require('./procs/tellServer');
|
|
40
|
+
// Module to get device options.
|
|
41
|
+
const {getDeviceOptions, isDeviceID} = require('./procs/device');
|
|
40
42
|
|
|
41
43
|
// ########## CONSTANTS
|
|
42
44
|
|
|
@@ -103,7 +105,7 @@ let requestedURL = '';
|
|
|
103
105
|
// ########## VALIDATORS
|
|
104
106
|
|
|
105
107
|
// Validates a browser type.
|
|
106
|
-
const
|
|
108
|
+
const isBrowserID = type => ['chromium', 'firefox', 'webkit'].includes(type);
|
|
107
109
|
// Validates a load state.
|
|
108
110
|
const isState = string => ['loaded', 'idle'].includes(string);
|
|
109
111
|
// Validates a URL.
|
|
@@ -146,8 +148,11 @@ const hasSubtype = (variable, subtype) => {
|
|
|
146
148
|
else if (subtype === 'isURL') {
|
|
147
149
|
return isURL(variable);
|
|
148
150
|
}
|
|
149
|
-
else if (subtype === '
|
|
150
|
-
return
|
|
151
|
+
else if (subtype === 'isDeviceID') {
|
|
152
|
+
return isDeviceID(variable);
|
|
153
|
+
}
|
|
154
|
+
else if (subtype === 'isBrowserID') {
|
|
155
|
+
return isBrowserID(variable);
|
|
151
156
|
}
|
|
152
157
|
else if (subtype === 'isFocusable') {
|
|
153
158
|
return isFocusable(variable);
|
|
@@ -179,7 +184,7 @@ const hasSubtype = (variable, subtype) => {
|
|
|
179
184
|
return true;
|
|
180
185
|
}
|
|
181
186
|
};
|
|
182
|
-
// Validates an act.
|
|
187
|
+
// Validates an act by reference to actSpecs.js.
|
|
183
188
|
const isValidAct = act => {
|
|
184
189
|
// Identify the type of the act.
|
|
185
190
|
const type = act.type;
|
|
@@ -250,60 +255,78 @@ const dateOf = timeStamp => {
|
|
|
250
255
|
const isValidReport = report => {
|
|
251
256
|
if (report) {
|
|
252
257
|
// Return whether the report is valid.
|
|
253
|
-
const {
|
|
258
|
+
const {
|
|
259
|
+
id,
|
|
260
|
+
strict,
|
|
261
|
+
isolate,
|
|
262
|
+
standard,
|
|
263
|
+
observe,
|
|
264
|
+
deviceID,
|
|
265
|
+
browserID,
|
|
266
|
+
lowMotion,
|
|
267
|
+
timeLimit,
|
|
268
|
+
creationTimeStamp,
|
|
269
|
+
executionTimeStamp,
|
|
270
|
+
sources,
|
|
271
|
+
acts
|
|
272
|
+
} = report;
|
|
254
273
|
if (! id || typeof id !== 'string') {
|
|
255
274
|
return 'Bad report ID';
|
|
256
275
|
}
|
|
257
|
-
if (! what || typeof what !== 'string') {
|
|
258
|
-
return 'Bad report what';
|
|
259
|
-
}
|
|
260
276
|
if (typeof strict !== 'boolean') {
|
|
261
277
|
return 'Bad report strict';
|
|
262
278
|
}
|
|
263
|
-
if (typeof
|
|
264
|
-
return 'Bad report
|
|
279
|
+
if (typeof isolate !== 'boolean') {
|
|
280
|
+
return 'Bad report isolate';
|
|
265
281
|
}
|
|
266
|
-
if (!
|
|
267
|
-
return 'Bad report
|
|
282
|
+
if (! ['also', 'only', 'no'].includes(standard)) {
|
|
283
|
+
return 'Bad report standard';
|
|
268
284
|
}
|
|
269
|
-
if (
|
|
270
|
-
return '
|
|
285
|
+
if (typeof observe !== 'boolean') {
|
|
286
|
+
return 'Bad report observe';
|
|
271
287
|
}
|
|
272
|
-
if (
|
|
273
|
-
return '
|
|
288
|
+
if (! isDeviceID(deviceID)) {
|
|
289
|
+
return 'Bad report deviceID';
|
|
274
290
|
}
|
|
275
|
-
if (! ['chromium', '
|
|
276
|
-
return 'Bad
|
|
291
|
+
if (! ['chromium', 'firefox', 'webkit'].includes(browserID)) {
|
|
292
|
+
return 'Bad report browserID';
|
|
277
293
|
}
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
! acts[0].url
|
|
281
|
-
|| typeof acts[0].url !== 'string'
|
|
282
|
-
|| ! isURL(acts[0].url)
|
|
283
|
-
)
|
|
284
|
-
&& (
|
|
285
|
-
acts[1].type !== 'url'
|
|
286
|
-
|| ! acts[1].which
|
|
287
|
-
|| typeof acts[1].which !== 'string'
|
|
288
|
-
|| ! isURL(acts[1].which)
|
|
289
|
-
)
|
|
290
|
-
)) {
|
|
291
|
-
return 'First or second act has no valid URL';
|
|
294
|
+
if (typeof lowMotion !== 'boolean') {
|
|
295
|
+
return 'Bad report lowMotion';
|
|
292
296
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
297
|
+
if (typeof timeLimit !== 'number' || timeLimit < 1) {
|
|
298
|
+
return 'Bad report timeLimit';
|
|
299
|
+
}
|
|
300
|
+
if (
|
|
301
|
+
! (creationTimeStamp && typeof creationTimeStamp === 'string' && dateOf(creationTimeStamp))
|
|
302
|
+
) {
|
|
303
|
+
return 'bad job creationTimeStamp';
|
|
304
|
+
}
|
|
305
|
+
if (
|
|
306
|
+
! (executionTimeStamp && typeof executionTimeStamp === 'string') && dateOf(executionTimeStamp)
|
|
307
|
+
) {
|
|
308
|
+
return 'bad report executionTimeStamp';
|
|
296
309
|
}
|
|
297
|
-
if (
|
|
310
|
+
if (
|
|
311
|
+
! sources
|
|
312
|
+
|| typeof sources !== 'object'
|
|
313
|
+
|| ! ['script', 'batch', 'target'].every(key => sources[key])
|
|
314
|
+
|| ! ['what', 'url'].every(key => sources.target[key])
|
|
315
|
+
) {
|
|
298
316
|
return 'Bad report sources';
|
|
299
317
|
}
|
|
300
318
|
if (
|
|
301
|
-
!
|
|
319
|
+
! acts
|
|
320
|
+
|| ! Array.isArray(acts)
|
|
321
|
+
|| acts.length < 2
|
|
322
|
+
|| ! acts.every(act => act.type && typeof act.type === 'string')
|
|
323
|
+
|| acts[0].type !== 'launch'
|
|
302
324
|
) {
|
|
303
|
-
return '
|
|
325
|
+
return 'Bad report acts';
|
|
304
326
|
}
|
|
305
|
-
|
|
306
|
-
|
|
327
|
+
const invalidAct = acts.find(act => ! isValidAct(act));
|
|
328
|
+
if (invalidAct) {
|
|
329
|
+
return `Invalid act:\n${JSON.stringify(invalidAct, null, 2)}`;
|
|
307
330
|
}
|
|
308
331
|
return '';
|
|
309
332
|
}
|
|
@@ -409,27 +432,28 @@ const browserClose = async () => {
|
|
|
409
432
|
}
|
|
410
433
|
};
|
|
411
434
|
// Launches a browser, navigates to a URL, and returns browser data.
|
|
412
|
-
const launch = async (
|
|
413
|
-
|
|
414
|
-
|
|
435
|
+
const launch = async (report, url, debug, waits, deviceID, browserID, lowMotion) => {
|
|
436
|
+
// Get the default arguments.
|
|
437
|
+
url ??= report.url;
|
|
438
|
+
deviceID ??= report.deviceID;
|
|
439
|
+
browserID ??= report.browserID;
|
|
440
|
+
lowMotion ??= report.lowMotion;
|
|
415
441
|
// If the specified browser type exists:
|
|
416
|
-
|
|
417
|
-
|
|
442
|
+
if (! browserID || ['chromium', 'firefox', 'webkit'].includes(browserID)) {
|
|
443
|
+
// Create a browser of the specified or default type.
|
|
444
|
+
const browserType = require('playwright')[browserID || report.browserID];
|
|
418
445
|
// Close the current browser, if any.
|
|
419
446
|
await browserClose();
|
|
420
|
-
//
|
|
447
|
+
// Define browser options.
|
|
421
448
|
const browserOptions = {
|
|
422
449
|
logger: {
|
|
423
450
|
isEnabled: () => false,
|
|
424
451
|
log: (name, severity, message) => console.log(message.slice(0, 100))
|
|
425
452
|
}
|
|
426
453
|
};
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (waits) {
|
|
431
|
-
browserOptions.slowMo = waits;
|
|
432
|
-
}
|
|
454
|
+
browserOptions.headless = ! debug;
|
|
455
|
+
browserOptions.slowMo = waits || 0;
|
|
456
|
+
// Launch the browser.
|
|
433
457
|
browser = await browserType.launch(browserOptions)
|
|
434
458
|
// If the launch failed:
|
|
435
459
|
.catch(async error => {
|
|
@@ -440,135 +464,132 @@ const launch = async (
|
|
|
440
464
|
error: 'Browser launch failed'
|
|
441
465
|
};
|
|
442
466
|
});
|
|
443
|
-
//
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
// Log a summary of the message on the console.
|
|
492
|
-
const parts = [msgText.slice(0, 75)];
|
|
493
|
-
if (msgText.length > 75) {
|
|
494
|
-
parts.push(msgText.slice(75, 150));
|
|
495
|
-
if (msgText.length > 150) {
|
|
496
|
-
const tail = msgText.slice(150).slice(-150);
|
|
497
|
-
if (msgText.length > 300) {
|
|
498
|
-
parts.push('...');
|
|
499
|
-
}
|
|
500
|
-
parts.push(tail.slice(0, 75));
|
|
501
|
-
if (tail.length > 75) {
|
|
502
|
-
parts.push(tail.slice(75));
|
|
467
|
+
// Get the device options for a new context.
|
|
468
|
+
const deviceOptions = getDeviceOptions(
|
|
469
|
+
deviceID || 'default', lowMotion ? 'reduce-motion' : 'no-preference'
|
|
470
|
+
);
|
|
471
|
+
// If the device is valid:
|
|
472
|
+
if (deviceOptions) {
|
|
473
|
+
// Open a context (i.e. browser tab), with reduced motion if specified.
|
|
474
|
+
const browserContext = await browser.newContext(deviceOptions);
|
|
475
|
+
// Prevent default timeouts.
|
|
476
|
+
browserContext.setDefaultTimeout(0);
|
|
477
|
+
// When a page (i.e. browser tab) is added to the browser context (i.e. browser window):
|
|
478
|
+
browserContext.on('page', async page => {
|
|
479
|
+
// Ensure the report has a jobData property.
|
|
480
|
+
report.jobData ??= {};
|
|
481
|
+
report.jobData.logCount ??= 0;
|
|
482
|
+
report.jobData.logSize ??= 0;
|
|
483
|
+
report.jobData.errorLogCount ??= 0;
|
|
484
|
+
report.jobData.browserTabOptions ??= deviceOptions;
|
|
485
|
+
// Add any error events to the count of logging errors.
|
|
486
|
+
page.on('crash', () => {
|
|
487
|
+
report.jobData.errorLogCount++;
|
|
488
|
+
console.log('Page crashed');
|
|
489
|
+
});
|
|
490
|
+
page.on('pageerror', () => {
|
|
491
|
+
report.jobData.errorLogCount++;
|
|
492
|
+
});
|
|
493
|
+
page.on('requestfailed', () => {
|
|
494
|
+
report.jobData.errorLogCount++;
|
|
495
|
+
});
|
|
496
|
+
// If the page emits a message:
|
|
497
|
+
page.on('console', msg => {
|
|
498
|
+
const msgText = msg.text();
|
|
499
|
+
let indentedMsg = '';
|
|
500
|
+
// If debugging is on:
|
|
501
|
+
if (debug) {
|
|
502
|
+
// Log a summary of the message on the console.
|
|
503
|
+
const parts = [msgText.slice(0, 75)];
|
|
504
|
+
if (msgText.length > 75) {
|
|
505
|
+
parts.push(msgText.slice(75, 150));
|
|
506
|
+
if (msgText.length > 150) {
|
|
507
|
+
const tail = msgText.slice(150).slice(-150);
|
|
508
|
+
if (msgText.length > 300) {
|
|
509
|
+
parts.push('...');
|
|
510
|
+
}
|
|
511
|
+
parts.push(tail.slice(0, 75));
|
|
512
|
+
if (tail.length > 75) {
|
|
513
|
+
parts.push(tail.slice(75));
|
|
514
|
+
}
|
|
503
515
|
}
|
|
504
516
|
}
|
|
517
|
+
indentedMsg = parts.map(part => ` | ${part}`).join('\n');
|
|
518
|
+
console.log(`\n${indentedMsg}`);
|
|
505
519
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
520
|
+
// Add statistics on the message to the report.
|
|
521
|
+
const msgTextLC = msgText.toLowerCase();
|
|
522
|
+
const msgLength = msgText.length;
|
|
523
|
+
report.jobData.logCount++;
|
|
524
|
+
report.jobData.logSize += msgLength;
|
|
525
|
+
if (errorWords.some(word => msgTextLC.includes(word))) {
|
|
526
|
+
report.jobData.errorLogCount++;
|
|
527
|
+
report.jobData.errorLogSize += msgLength;
|
|
528
|
+
}
|
|
529
|
+
const msgLC = msgText.toLowerCase();
|
|
530
|
+
if (
|
|
531
|
+
msgText.includes('403') && (msgLC.includes('status')
|
|
532
|
+
|| msgLC.includes('prohibited'))
|
|
533
|
+
) {
|
|
534
|
+
report.jobData.prohibitedCount++;
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
// Open the first page of the context.
|
|
539
|
+
const page = await browserContext.newPage();
|
|
540
|
+
try {
|
|
541
|
+
// Wait until it is stable.
|
|
542
|
+
await page.waitForLoadState('domcontentloaded', {timeout: 5000});
|
|
543
|
+
// Navigate to the specified URL.
|
|
544
|
+
const navResult = await goTo(report, page, url, 15000, 'domcontentloaded');
|
|
545
|
+
// If the navigation succeeded:
|
|
546
|
+
if (navResult.success) {
|
|
547
|
+
// Update the name of the current browser type and store it in the page.
|
|
548
|
+
page.browserTypeName = browserID;
|
|
549
|
+
// Return the response of the target server, the browser context, and the page.
|
|
550
|
+
return {
|
|
551
|
+
success: true,
|
|
552
|
+
response: navResult.response,
|
|
553
|
+
browserContext,
|
|
554
|
+
page
|
|
555
|
+
};
|
|
517
556
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
557
|
+
// Otherwise, if the navigation failed:
|
|
558
|
+
else {
|
|
559
|
+
// Return the error.
|
|
560
|
+
return {
|
|
561
|
+
success: false,
|
|
562
|
+
error: navResult.error
|
|
563
|
+
};
|
|
524
564
|
}
|
|
525
|
-
});
|
|
526
|
-
});
|
|
527
|
-
// Open the first page of the context.
|
|
528
|
-
const page = await browserContext.newPage();
|
|
529
|
-
try {
|
|
530
|
-
// Wait until it is stable.
|
|
531
|
-
await page.waitForLoadState('domcontentloaded', {timeout: 5000});
|
|
532
|
-
// Navigate to the specified URL.
|
|
533
|
-
const navResult = await goTo(report, page, url, 15000, 'domcontentloaded');
|
|
534
|
-
// If the navigation succeeded:
|
|
535
|
-
if (navResult.success) {
|
|
536
|
-
// Update the name of the current browser type and store it in the page.
|
|
537
|
-
page.browserTypeName = typeName;
|
|
538
|
-
// Return the response of the target server, the browser context, and the page.
|
|
539
|
-
return {
|
|
540
|
-
success: true,
|
|
541
|
-
response: navResult.response,
|
|
542
|
-
browserContext,
|
|
543
|
-
page
|
|
544
|
-
};
|
|
545
565
|
}
|
|
546
|
-
//
|
|
547
|
-
|
|
548
|
-
// Return
|
|
566
|
+
// If it fails to become stable after load:
|
|
567
|
+
catch(error) {
|
|
568
|
+
// Return this.
|
|
569
|
+
console.log(`ERROR: Blank page load in new tab timed out (${error.message})`);
|
|
549
570
|
return {
|
|
550
571
|
success: false,
|
|
551
|
-
error:
|
|
572
|
+
error: 'Blank page load in new tab timed out'
|
|
552
573
|
};
|
|
553
574
|
}
|
|
554
575
|
}
|
|
555
|
-
//
|
|
556
|
-
|
|
576
|
+
// Otherwise, i.e. if the device is invalid:
|
|
577
|
+
else {
|
|
557
578
|
// Return this.
|
|
558
|
-
console.log(`ERROR:
|
|
579
|
+
console.log(`ERROR: Device ${deviceID} invalid`);
|
|
559
580
|
return {
|
|
560
581
|
success: false,
|
|
561
|
-
error:
|
|
582
|
+
error: `${deviceID} device invalid`
|
|
562
583
|
};
|
|
563
584
|
}
|
|
564
585
|
}
|
|
565
586
|
// Otherwise, i.e. if it does not exist:
|
|
566
587
|
else {
|
|
567
588
|
// Return this.
|
|
568
|
-
console.log(`ERROR: Browser of type ${
|
|
589
|
+
console.log(`ERROR: Browser of type ${browserID} could not be launched`);
|
|
569
590
|
return {
|
|
570
591
|
success: false,
|
|
571
|
-
error: `${
|
|
592
|
+
error: `${browserID} browser launch failed`
|
|
572
593
|
};
|
|
573
594
|
}
|
|
574
595
|
};
|
|
@@ -801,23 +822,16 @@ const doActs = async (report, actIndex, page) => {
|
|
|
801
822
|
if (actIndex > -1 && actIndex < acts.length) {
|
|
802
823
|
// Identify the act to be performed.
|
|
803
824
|
const act = acts[actIndex];
|
|
825
|
+
const {type, which} = act;
|
|
804
826
|
// If it is valid:
|
|
805
827
|
if (isValidAct(act)) {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
if (act.type === 'launch' && act.url) {
|
|
809
|
-
actInfo = `${act.which} to ${act.url}`;
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
actInfo = act.which;
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
const message = `>>>> ${act.type}: ${actInfo}`;
|
|
828
|
+
const actSuffix = type === 'test' ? ` ${which}` : '';
|
|
829
|
+
const message = `>>>> ${type}${actSuffix}`;
|
|
816
830
|
// If granular reporting has been specified:
|
|
817
831
|
if (report.observe) {
|
|
818
832
|
// Notify the observer of the act and log it.
|
|
819
|
-
const whichParam =
|
|
820
|
-
const messageParams = `act=${
|
|
833
|
+
const whichParam = which ? `&which=${which}` : '';
|
|
834
|
+
const messageParams = `act=${type}${whichParam}`;
|
|
821
835
|
tellServer(report, messageParams, message);
|
|
822
836
|
}
|
|
823
837
|
// Otherwise, i.e. if granular reporting has not been specified:
|
|
@@ -829,7 +843,7 @@ const doActs = async (report, actIndex, page) => {
|
|
|
829
843
|
actCount++;
|
|
830
844
|
act.startTime = Date.now();
|
|
831
845
|
// If the act is an index changer:
|
|
832
|
-
if (
|
|
846
|
+
if (type === 'next') {
|
|
833
847
|
const condition = act.if;
|
|
834
848
|
const logSuffix = condition.length === 3 ? ` ${condition[1]} ${condition[2]}` : '';
|
|
835
849
|
console.log(`>> ${condition[0]}${logSuffix}`);
|
|
@@ -865,16 +879,16 @@ const doActs = async (report, actIndex, page) => {
|
|
|
865
879
|
}
|
|
866
880
|
}
|
|
867
881
|
// Otherwise, if the act is a launch:
|
|
868
|
-
else if (
|
|
869
|
-
// Launch the specified browser and navigate to the specified URL.
|
|
882
|
+
else if (type === 'launch') {
|
|
883
|
+
// Launch the specified browser on the specified device and navigate to the specified URL.
|
|
870
884
|
const launchResult = await launch(
|
|
871
885
|
report,
|
|
872
|
-
act.
|
|
873
|
-
act.url,
|
|
886
|
+
act.url || report.sources.target.url,
|
|
874
887
|
debug,
|
|
875
888
|
waits,
|
|
876
|
-
act.deviceID ||
|
|
877
|
-
act.
|
|
889
|
+
act.deviceID || report.deviceID,
|
|
890
|
+
act.browserID || report.browserID,
|
|
891
|
+
act.lowMotion || report.lowMotion
|
|
878
892
|
);
|
|
879
893
|
// If the launch and navigation succeeded:
|
|
880
894
|
if (launchResult && launchResult.success) {
|