testaro 30.0.8 → 32.0.1

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.
Files changed (212) hide show
  1. package/CONTRIBUTING.md +26 -4
  2. package/LICENSE +1 -1
  3. package/README.md +24 -2
  4. package/aceconfig.js +27 -0
  5. package/actSpecs.js +28 -1
  6. package/call.js +22 -1
  7. package/data/template.js +22 -0
  8. package/dirWatch.js +22 -0
  9. package/package.json +1 -1
  10. package/procs/aslint.js +24 -0
  11. package/procs/getLocatorData.js +29 -1
  12. package/procs/getSource.js +30 -3
  13. package/procs/isInlineLink.js +23 -0
  14. package/procs/operable.js +23 -2
  15. package/procs/sample.js +22 -0
  16. package/procs/standardize.js +28 -7
  17. package/procs/tellServer.js +24 -2
  18. package/procs/testaro.js +26 -1
  19. package/procs/visChange.js +22 -0
  20. package/run.js +373 -83
  21. package/testaro/allCaps.js +22 -0
  22. package/testaro/allHidden.js +22 -0
  23. package/testaro/allSlanted.js +22 -0
  24. package/testaro/attVal.js +22 -0
  25. package/testaro/autocomplete.js +22 -0
  26. package/testaro/bulk.js +22 -0
  27. package/testaro/buttonMenu.js +22 -0
  28. package/testaro/distortion.js +22 -0
  29. package/testaro/docType.js +22 -0
  30. package/testaro/dupAtt.js +22 -0
  31. package/testaro/elements.js +22 -0
  32. package/testaro/embAc.js +22 -0
  33. package/testaro/filter.js +22 -0
  34. package/testaro/focAll.js +22 -0
  35. package/testaro/focInd.js +22 -0
  36. package/testaro/focOp.js +22 -0
  37. package/testaro/focVis.js +22 -0
  38. package/testaro/headEl.js +22 -0
  39. package/testaro/headingAmb.js +22 -0
  40. package/testaro/hovInd.js +21 -24
  41. package/testaro/hover.js +22 -0
  42. package/testaro/labClash.js +22 -0
  43. package/testaro/lineHeight.js +22 -0
  44. package/testaro/linkAmb.js +22 -0
  45. package/testaro/linkTitle.js +22 -0
  46. package/testaro/linkUl.js +22 -0
  47. package/testaro/miniText.js +22 -0
  48. package/testaro/motion.js +22 -0
  49. package/testaro/nonTable.js +22 -0
  50. package/testaro/opFoc.js +22 -0
  51. package/testaro/pseudoP.js +25 -3
  52. package/testaro/radioSet.js +22 -0
  53. package/testaro/role.js +22 -0
  54. package/testaro/styleDiff.js +80 -1
  55. package/testaro/tabNav.js +99 -2
  56. package/testaro/targetSize.js +22 -0
  57. package/testaro/textNodes.js +25 -3
  58. package/testaro/title.js +22 -0
  59. package/testaro/zIndex.js +22 -0
  60. package/tests/alfa.js +34 -12
  61. package/tests/aslint.js +55 -17
  62. package/tests/axe.js +35 -9
  63. package/tests/htmlcs.js +109 -78
  64. package/tests/ibm.js +111 -45
  65. package/tests/nuVal.js +39 -9
  66. package/tests/qualWeb.js +49 -25
  67. package/tests/testaro.js +50 -22
  68. package/tests/wave.js +36 -10
  69. package/validation/executors/run.js +26 -2
  70. package/validation/executors/test.js +27 -3
  71. package/validation/executors/tests.js +26 -2
  72. package/validation/executors/watchDir.js +26 -2
  73. package/validation/executors/watchNet.js +26 -2
  74. package/validation/jobs/todo/README.md +22 -0
  75. package/validation/tests/targets/adbID/index.html +21 -0
  76. package/validation/tests/targets/allCaps/index.html +21 -0
  77. package/validation/tests/targets/allHidden/ariaHiddenBody.html +21 -0
  78. package/validation/tests/targets/allHidden/good.html +21 -0
  79. package/validation/tests/targets/allHidden/hiddenMain.html +21 -0
  80. package/validation/tests/targets/allHidden/mixedHidden.html +22 -1
  81. package/validation/tests/targets/allHidden/noBody.html +21 -0
  82. package/validation/tests/targets/allHidden/noMain.html +21 -0
  83. package/validation/tests/targets/allHidden/noneDoc.html +21 -0
  84. package/validation/tests/targets/allHidden/visHiddenMain.html +21 -0
  85. package/validation/tests/targets/allSlanted/index.html +21 -0
  86. package/validation/tests/targets/altScheme/index.html +21 -0
  87. package/validation/tests/targets/attVal/bad.html +21 -0
  88. package/validation/tests/targets/attVal/good.html +21 -0
  89. package/validation/tests/targets/autocomplete/bad.html +21 -0
  90. package/validation/tests/targets/autocomplete/good.html +21 -0
  91. package/validation/tests/targets/bulk/bad.html +21 -0
  92. package/validation/tests/targets/bulk/good.html +21 -0
  93. package/validation/tests/targets/buttonMenu/bad.html +21 -0
  94. package/validation/tests/targets/buttonMenu/bad.js +27 -0
  95. package/validation/tests/targets/buttonMenu/good.html +21 -0
  96. package/validation/tests/targets/buttonMenu/good.js +27 -0
  97. package/validation/tests/targets/buttonMenu/style.css +27 -0
  98. package/validation/tests/targets/captionLoc/index.html +21 -0
  99. package/validation/tests/targets/datalistRef/index.html +21 -0
  100. package/validation/tests/targets/distortion/index.html +21 -0
  101. package/validation/tests/targets/docType/bad.html +21 -0
  102. package/validation/tests/targets/docType/good.html +21 -0
  103. package/validation/tests/targets/dupAtt/bad.html +21 -0
  104. package/validation/tests/targets/dupAtt/good.html +21 -0
  105. package/validation/tests/targets/elements/index.html +21 -0
  106. package/validation/tests/targets/embAc/bad.html +21 -0
  107. package/validation/tests/targets/embAc/good.html +21 -0
  108. package/validation/tests/targets/filter/bad.html +21 -0
  109. package/validation/tests/targets/filter/good.html +21 -0
  110. package/validation/tests/targets/focAll/good.html +21 -0
  111. package/validation/tests/targets/focAll/less.html +21 -0
  112. package/validation/tests/targets/focAll/more.html +21 -0
  113. package/validation/tests/targets/focInd/bad.html +21 -0
  114. package/validation/tests/targets/focInd/good.html +21 -0
  115. package/validation/tests/targets/focOp/bad.html +21 -0
  116. package/validation/tests/targets/focOp/good.html +21 -0
  117. package/validation/tests/targets/focVis/index.html +21 -0
  118. package/validation/tests/targets/headEl/index.html +21 -0
  119. package/validation/tests/targets/headingAmb/index.html +21 -0
  120. package/validation/tests/targets/hovInd/index.html +21 -0
  121. package/validation/tests/targets/hover/bad.html +21 -0
  122. package/validation/tests/targets/hover/good.html +21 -0
  123. package/validation/tests/targets/hr/index.html +21 -0
  124. package/validation/tests/targets/imageLink/index.html +21 -0
  125. package/validation/tests/targets/labClash/bad.html +21 -0
  126. package/validation/tests/targets/labClash/good.html +21 -0
  127. package/validation/tests/targets/legendLoc/index.html +21 -0
  128. package/validation/tests/targets/lineHeight/index.html +21 -0
  129. package/validation/tests/targets/linkAmb/index.html +21 -0
  130. package/validation/tests/targets/linkExt/index.html +21 -0
  131. package/validation/tests/targets/linkOldAtt/index.html +21 -0
  132. package/validation/tests/targets/linkTitle/index.html +21 -0
  133. package/validation/tests/targets/linkTo/index.html +21 -0
  134. package/validation/tests/targets/linkUl/bad.html +21 -0
  135. package/validation/tests/targets/linkUl/good.html +21 -0
  136. package/validation/tests/targets/linkUl/na.html +21 -0
  137. package/validation/tests/targets/miniText/index.html +21 -0
  138. package/validation/tests/targets/motion/bad.css +27 -0
  139. package/validation/tests/targets/motion/bad.html +21 -0
  140. package/validation/tests/targets/motion/good.html +21 -0
  141. package/validation/tests/targets/nonTable/index.html +21 -0
  142. package/validation/tests/targets/opFoc/bad.html +21 -0
  143. package/validation/tests/targets/opFoc/good.html +21 -0
  144. package/validation/tests/targets/optRoleSel/index.html +21 -0
  145. package/validation/tests/targets/phOnly/index.html +21 -0
  146. package/validation/tests/targets/pseudoP/index.html +21 -0
  147. package/validation/tests/targets/radioSet/bad.html +21 -0
  148. package/validation/tests/targets/radioSet/good.html +21 -0
  149. package/validation/tests/targets/role/bad.html +21 -0
  150. package/validation/tests/targets/role/good.html +21 -0
  151. package/validation/tests/targets/secHeading/index.html +21 -0
  152. package/validation/tests/targets/styleDiff/bad.html +21 -0
  153. package/validation/tests/targets/styleDiff/good.html +21 -0
  154. package/validation/tests/targets/tabNav/bad.html +21 -0
  155. package/validation/tests/targets/tabNav/bad.js +27 -0
  156. package/validation/tests/targets/tabNav/good.html +21 -0
  157. package/validation/tests/targets/tabNav/good.js +27 -0
  158. package/validation/tests/targets/tabNav/style.css +27 -0
  159. package/validation/tests/targets/targetSize/index.html +21 -0
  160. package/validation/tests/targets/textNodes/index.html +21 -0
  161. package/validation/tests/targets/textSem/index.html +21 -0
  162. package/validation/tests/targets/title/bad.html +21 -0
  163. package/validation/tests/targets/title/good.html +21 -0
  164. package/validation/tests/targets/titledEl/index.html +21 -0
  165. package/validation/tests/targets/zIndex/bad.html +21 -0
  166. package/validation/tests/targets/zIndex/good.html +21 -0
  167. package/validation/validateTest.js +39 -3
  168. package/validation/watch/done/README.md +23 -1
  169. package/validation/watch/todo/README.md +23 -1
  170. package/watch.js +32 -7
  171. package/call-old.js +0 -86
  172. package/procs/allText.js +0 -76
  173. package/procs/allVis.js +0 -17
  174. package/procs/getTextNodes.js +0 -39
  175. package/procs/linksByType.js +0 -54
  176. package/procs/nav.js +0 -259
  177. package/procs/textOf.txt +0 -73
  178. package/test copy.js +0 -38
  179. package/validation/tests/old/allCaps.json +0 -102
  180. package/validation/tests/old/allHidden.json +0 -314
  181. package/validation/tests/old/attVal.json +0 -60
  182. package/validation/tests/old/autocomplete.json +0 -51
  183. package/validation/tests/old/bulk.json +0 -48
  184. package/validation/tests/old/docType.json +0 -46
  185. package/validation/tests/old/dupAtt.json +0 -51
  186. package/validation/tests/old/elements.json +0 -140
  187. package/validation/tests/old/embAc.json +0 -54
  188. package/validation/tests/old/filter.json +0 -55
  189. package/validation/tests/old/focAll.json +0 -68
  190. package/validation/tests/old/focInd.json +0 -69
  191. package/validation/tests/old/focOp.json +0 -62
  192. package/validation/tests/old/focVis.json +0 -35
  193. package/validation/tests/old/hover.json +0 -118
  194. package/validation/tests/old/labClash.json +0 -52
  195. package/validation/tests/old/linkTo.json +0 -35
  196. package/validation/tests/old/linkUl.json +0 -71
  197. package/validation/tests/old/menuNav.json +0 -106
  198. package/validation/tests/old/miniText.json +0 -36
  199. package/validation/tests/old/motion.json +0 -62
  200. package/validation/tests/old/nonTable.json +0 -37
  201. package/validation/tests/old/radioSet.json +0 -52
  202. package/validation/tests/old/role.json +0 -60
  203. package/validation/tests/old/styleDiff.json +0 -71
  204. package/validation/tests/old/tabNav.json +0 -106
  205. package/validation/tests/old/temp.js +0 -28
  206. package/validation/tests/old/textNodes.json +0 -98
  207. package/validation/tests/old/title.json +0 -46
  208. package/validation/tests/old/titledEl.json +0 -37
  209. package/validation/tests/old/zIndex.json +0 -49
  210. package/validation/tests/targets/tabNav/goodMoz.js +0 -206
  211. package/watch-old.js +0 -275
  212. package/watch-temp.js +0 -45
package/run.js CHANGED
@@ -1,3 +1,25 @@
1
+ /*
2
+ © 2021–2023 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
+
1
23
  /*
2
24
  run.js
3
25
  Testaro main utility module.
@@ -9,11 +31,9 @@
9
31
  require('dotenv').config();
10
32
  // Requirements for acts.
11
33
  const {actSpecs} = require('./actSpecs');
12
- // Navigation.
13
- const {browserClose, getNonce, goTo, launch} = require('./procs/nav');
14
34
  // Module to standardize report formats.
15
35
  const {standardize} = require('./procs/standardize');
16
- // Module to send a notice to the server.
36
+ // Module to send a notice to an observer.
17
37
  const {tellServer} = require('./procs/tellServer');
18
38
 
19
39
  // ########## CONSTANTS
@@ -34,7 +54,7 @@ const moves = {
34
54
  text: 'input'
35
55
  };
36
56
  // Names and descriptions of tools.
37
- const tests = {
57
+ const tools = {
38
58
  alfa: 'alfa',
39
59
  aslint: 'ASLint',
40
60
  axe: 'Axe',
@@ -45,12 +65,34 @@ const tests = {
45
65
  testaro: 'Testaro',
46
66
  wave: 'WAVE',
47
67
  };
68
+ // Strings in log messages indicating errors.
69
+ const errorWords = [
70
+ 'but not used',
71
+ 'content security policy',
72
+ 'deprecated',
73
+ 'error',
74
+ 'exception',
75
+ 'expected',
76
+ 'failed',
77
+ 'invalid',
78
+ 'missing',
79
+ 'non-standard',
80
+ 'not supported',
81
+ 'refused',
82
+ 'requires',
83
+ 'sorry',
84
+ 'suspicious',
85
+ 'unrecognized',
86
+ 'violates',
87
+ 'warning'
88
+ ];
48
89
 
49
90
  // ########## VARIABLES
50
91
 
51
92
  // Facts about the current session.
52
93
  let actCount = 0;
53
94
  // Facts about the current browser.
95
+ let browser;
54
96
  let browserContext;
55
97
  let currentPage;
56
98
  let requestedURL = '';
@@ -108,7 +150,7 @@ const hasSubtype = (variable, subtype) => {
108
150
  return isFocusable(variable);
109
151
  }
110
152
  else if (subtype === 'isTest') {
111
- return tests[variable];
153
+ return tools[variable];
112
154
  }
113
155
  else if (subtype === 'isWaitable') {
114
156
  return ['url', 'title', 'body'].includes(variable);
@@ -145,13 +187,13 @@ const isValidAct = act => {
145
187
  // If the type is test:
146
188
  if (type === 'test') {
147
189
  // Identify the test.
148
- const testName = act.which;
190
+ const toolName = act.which;
149
191
  // If one was specified and is known:
150
- if (testName && tests[testName]) {
192
+ if (toolName && tools[toolName]) {
151
193
  // If it has special properties:
152
- if (actSpecs.tests[testName]) {
194
+ if (actSpecs.tools[toolName]) {
153
195
  // Expand the validator by adding them.
154
- Object.assign(validator, actSpecs.tests[testName][1]);
196
+ Object.assign(validator, actSpecs.tools[toolName][1]);
155
197
  }
156
198
  }
157
199
  // Otherwise, i.e. if no or an unknown test was specified:
@@ -233,7 +275,7 @@ const isValidReport = report => {
233
275
  return 'Bad report sources';
234
276
  }
235
277
  if (typeof sources.script !== 'string') {
236
- return 'Bad sources script';
278
+ return 'Bad source script';
237
279
  }
238
280
  if (
239
281
  ! creationTime
@@ -254,6 +296,224 @@ const isValidReport = report => {
254
296
 
255
297
  // ########## OTHER FUNCTIONS
256
298
 
299
+ // Returns a string with any final slash removed.
300
+ const deSlash = string => string.endsWith('/') ? string.slice(0, -1) : string;
301
+ // Gets the script nonce from a response.
302
+ const getNonce = async response => {
303
+ let nonce = '';
304
+ // If the response includes a content security policy:
305
+ const headers = await response.allHeaders();
306
+ const cspWithQuotes = headers && headers['content-security-policy'];
307
+ if (cspWithQuotes) {
308
+ // If it requires scripts to have a nonce:
309
+ const csp = cspWithQuotes.replace(/'/g, '');
310
+ const directives = csp.split(/ *; */).map(directive => directive.split(/ +/));
311
+ const scriptDirective = directives.find(dir => dir[0] === 'script-src');
312
+ if (scriptDirective) {
313
+ const nonceSpec = scriptDirective.find(valPart => valPart.startsWith('nonce-'));
314
+ if (nonceSpec) {
315
+ // Return the nonce.
316
+ nonce = nonceSpec.replace(/^nonce-/, '');
317
+ }
318
+ }
319
+ }
320
+ // Return the nonce, if any.
321
+ return nonce;
322
+ };
323
+ // Visits a URL and returns the response of the server.
324
+ const goTo = async (report, page, url, timeout, waitUntil) => {
325
+ // If the URL is a file path:
326
+ if (url.startsWith('file://')) {
327
+ // Make it absolute.
328
+ url = url.replace('file://', `file://${__dirname}/`);
329
+ }
330
+ // Visit the URL.
331
+ const startTime = Date.now();
332
+ try {
333
+ const response = await page.goto(url, {
334
+ timeout,
335
+ waitUntil
336
+ });
337
+ report.jobData.visitLatency += Math.round((Date.now() - startTime) / 1000);
338
+ const httpStatus = response.status();
339
+ // If the response status was normal:
340
+ if ([200, 304].includes(httpStatus) || url.startsWith('file:')) {
341
+ // If the browser was redirected in violation of a strictness requirement:
342
+ const actualURL = page.url();
343
+ if (report.strict && deSlash(actualURL) !== deSlash(url)) {
344
+ // Return an error.
345
+ console.log(`ERROR: Visit to ${url} redirected to ${actualURL}`);
346
+ return {
347
+ exception: 'badRedirection'
348
+ };
349
+ }
350
+ // Otherwise, i.e. if no prohibited redirection occurred:
351
+ else {
352
+ // Press the Escape key to dismiss any modal dialog.
353
+ await page.keyboard.press('Escape');
354
+ // Return the result of the navigation.
355
+ return {
356
+ success: true,
357
+ response
358
+ };
359
+ }
360
+ }
361
+ // Otherwise, i.e. if the response status was abnormal:
362
+ else {
363
+ // Return an error.
364
+ console.log(`ERROR: Visit to ${url} got status ${httpStatus}`);
365
+ report.jobData.visitRejectionCount++;
366
+ return {
367
+ success: false,
368
+ error: 'badStatus'
369
+ };
370
+ }
371
+ }
372
+ catch(error) {
373
+ console.log(`ERROR visiting ${url} (${error.message.slice(0, 200)})`);
374
+ return {
375
+ success: false,
376
+ error: 'noVisit'
377
+ };
378
+ }
379
+ };
380
+ // Closes the current browser.
381
+ const browserClose = async () => {
382
+ if (browser) {
383
+ let contexts = browser.contexts();
384
+ for (const context of contexts) {
385
+ await context.close();
386
+ contexts = browser.contexts();
387
+ }
388
+ await browser.close();
389
+ browser = null;
390
+ }
391
+ };
392
+ // Launches a browser, navigates to a URL, and returns browser data.
393
+ const launch = async (report, typeName, url, debug, waits, isLowMotion = false) => {
394
+ // If the specified browser type exists:
395
+ const browserType = require('playwright')[typeName];
396
+ if (browserType) {
397
+ // Close the current browser, if any.
398
+ await browserClose();
399
+ // Launch a browser of the specified type.
400
+ const browserOptions = {
401
+ logger: {
402
+ isEnabled: () => false,
403
+ log: (name, severity, message) => console.log(message.slice(0, 100))
404
+ }
405
+ };
406
+ if (debug) {
407
+ browserOptions.headless = false;
408
+ }
409
+ if (waits) {
410
+ browserOptions.slowMo = waits;
411
+ }
412
+ browser = await browserType.launch(browserOptions)
413
+ // If the launch failed:
414
+ .catch(async error => {
415
+ console.log(`ERROR launching browser (${error.message.slice(0, 200)})`);
416
+ // Return this.
417
+ return {
418
+ success: false,
419
+ error: 'Browser launch failed'
420
+ };
421
+ });
422
+ // Open a context (i.e. browser tab), with reduced motion if specified.
423
+ const options = {reduceMotion: isLowMotion ? 'reduce' : 'no-preference'};
424
+ const browserContext = await browser.newContext(options);
425
+ // When a page (i.e. browser tab) is added to the browser context (i.e. browser window):
426
+ browserContext.on('page', async page => {
427
+ // If it emits a message:
428
+ page.on('console', msg => {
429
+ const msgText = msg.text();
430
+ let indentedMsg = '';
431
+ // If debugging is on:
432
+ if (debug) {
433
+ // Log a summary of the message on the console.
434
+ const parts = [msgText.slice(0, 75)];
435
+ if (msgText.length > 75) {
436
+ parts.push(msgText.slice(75, 150));
437
+ if (msgText.length > 150) {
438
+ const tail = msgText.slice(150).slice(-150);
439
+ if (msgText.length > 300) {
440
+ parts.push('...');
441
+ }
442
+ parts.push(tail.slice(0, 75));
443
+ if (tail.length > 75) {
444
+ parts.push(tail.slice(75));
445
+ }
446
+ }
447
+ }
448
+ indentedMsg = parts.map(part => ` | ${part}`).join('\n');
449
+ console.log(`\n${indentedMsg}`);
450
+ }
451
+ // Add statistics on the message to the report.
452
+ const msgTextLC = msgText.toLowerCase();
453
+ const msgLength = msgText.length;
454
+ report.jobData.logCount++;
455
+ report.jobData.logSize += msgLength;
456
+ if (errorWords.some(word => msgTextLC.includes(word))) {
457
+ report.jobData.errorLogCount++;
458
+ report.jobData.errorLogSize += msgLength;
459
+ }
460
+ const msgLC = msgText.toLowerCase();
461
+ if (
462
+ msgText.includes('403') && (msgLC.includes('status')
463
+ || msgLC.includes('prohibited'))
464
+ ) {
465
+ report.jobData.prohibitedCount++;
466
+ }
467
+ });
468
+ });
469
+ // Open the first page of the context.
470
+ const page = await browserContext.newPage();
471
+ try {
472
+ // Wait until it is stable.
473
+ await page.waitForLoadState('domcontentloaded', {timeout: 5000});
474
+ // Navigate to the specified URL.
475
+ const navResult = await goTo(report, page, url, 15000, 'domcontentloaded');
476
+ // If the navigation succeeded:
477
+ if (navResult.success) {
478
+ // Update the name of the current browser type and store it in the page.
479
+ page.browserTypeName = typeName;
480
+ // Return the response of the target server, the browser context, and the page.
481
+ return {
482
+ success: true,
483
+ response: navResult.response,
484
+ browserContext,
485
+ page
486
+ };
487
+ }
488
+ // Otherwise, if the navigation failed:
489
+ else if (navResult.error) {
490
+ // Return this.
491
+ return {
492
+ success: false,
493
+ error: 'Navigation failed'
494
+ };
495
+ }
496
+ }
497
+ // If it fails to become stable after load:
498
+ catch(error) {
499
+ // Return this.
500
+ console.log(`ERROR: Blank page load in new tab timed out (${error.message})`);
501
+ return {
502
+ success: false,
503
+ error: 'Blank page load in new tab timed out'
504
+ };
505
+ }
506
+ }
507
+ // Otherwise, i.e. if it does not exist:
508
+ else {
509
+ // Return this.
510
+ console.log(`ERROR: Browser of type ${typeName} could not be launched`);
511
+ return {
512
+ success: false,
513
+ error: `${typeName} browser launch failed`
514
+ };
515
+ }
516
+ };
257
517
  // Returns a string representing the date and time.
258
518
  const nowString = () => (new Date()).toISOString().slice(0, 19);
259
519
  // Returns the first line of an error message.
@@ -421,23 +681,51 @@ const wait = ms => {
421
681
  }, ms);
422
682
  });
423
683
  };
684
+ // Reports a job being aborted and returns an abortive act index.
685
+ const abortActs = async (report, actIndex) => {
686
+ // Add data on the aborted act to the report.
687
+ report.jobData.abortTime = nowString();
688
+ report.jobData.abortedAct = actIndex;
689
+ report.jobData.aborted = true;
690
+ // Report the job being aborted.
691
+ console.log('ERROR: Job aborted');
692
+ // Return an abortive act index.
693
+ return -2;
694
+ };
424
695
  // Adds an error result to an act.
425
- const addError = (alsoLog, act, error, message) => {
696
+ const addError = async(alsoLog, alsoAbort, report, actIndex, message) => {
697
+ // If the error is to be logged:
426
698
  if (alsoLog) {
427
- console.log(`${message} (${error})`);
428
- }
429
- if (! act.result) {
430
- act.result = {};
699
+ // Log it.
700
+ console.log(message);
431
701
  }
432
- act.result.success = false;
433
- act.result.error = error;
434
- act.result.message = message;
702
+ // Add error data to the result.
703
+ const act = report.acts[actIndex];
704
+ act.result ??= {};
705
+ act.result.success ??= false;
706
+ act.result.error ??= message;
435
707
  if (act.type === 'test') {
436
- act.result.prevented = true;
708
+ act.data.success = false;
709
+ act.data.prevented = true;
710
+ act.data.error = message;
711
+ // Add prevention data to the job data.
712
+ report.jobData.preventions[act.which] = message;
713
+ }
714
+ // If the job is to be aborted:
715
+ if (alsoAbort) {
716
+ console.log(`report:\n${JSON.stringify(report, null, 2)}`);
717
+ // Return an abortive act index.
718
+ return await abortActs(report, actIndex);
719
+ }
720
+ // Otherwise, i.e. if the job is not to be aborted:
721
+ else {
722
+ // Return the current act index.
723
+ return actIndex;
437
724
  }
438
725
  };
439
726
  // Recursively performs the acts in a report.
440
727
  const doActs = async (report, actIndex, page) => {
728
+ // FUNCTION DEFINITION START
441
729
  // Quits and reports the job being aborted.
442
730
  const abortActs = async () => {
443
731
  // Add data on the aborted act to the report.
@@ -449,6 +737,7 @@ const doActs = async (report, actIndex, page) => {
449
737
  // Report this.
450
738
  console.log('ERROR: Job aborted');
451
739
  };
740
+ // FUNCTION DEFINITION END
452
741
  const {acts} = report;
453
742
  // If any more acts are to be performed:
454
743
  if (actIndex > -1 && actIndex < acts.length) {
@@ -533,10 +822,10 @@ const doActs = async (report, actIndex, page) => {
533
822
  }
534
823
  // Otherwise, i.e. if the launch or navigation failed:
535
824
  else {
536
- // Add an error result to the act.
537
- addError(true, act, 'badLaunch', `ERROR: Launch failed (${launchResult.error})`);
538
- // Abort the job.
539
- await abortActs();
825
+ // Add an error result to the act and abort the job.
826
+ actIndex = await addError(
827
+ true, true, report, actIndex, `ERROR: Launch failed (${launchResult.error})`
828
+ );
540
829
  }
541
830
  }
542
831
  // Otherwise, if a current page exists:
@@ -563,15 +852,15 @@ const doActs = async (report, actIndex, page) => {
563
852
  act.result.url = page.url();
564
853
  // If a prohibited redirection occurred:
565
854
  if (response.exception === 'badRedirection') {
566
- // Report this and quit.
567
- addError(true, act, 'badRedirection', 'ERROR: Navigation illicitly redirected');
568
- await abortActs();
855
+ // Report this and abort the job.
856
+ actIndex = await addError(
857
+ true, true, report, actIndex, 'ERROR: Navigation illicitly redirected'
858
+ );
569
859
  }
570
860
  // Otherwise, i.e. if the visit failed:
571
861
  else {
572
- // Report this and quit.
573
- addError(true, act, 'failure', 'ERROR: Visit failed');
574
- await abortActs();
862
+ // Report this and abort the job.
863
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: Visit failed');
575
864
  }
576
865
  }
577
866
  }
@@ -652,10 +941,11 @@ const doActs = async (report, actIndex, page) => {
652
941
  )
653
942
  // If the wait times out:
654
943
  .catch(async error => {
655
- // Quit.
944
+ // Report this and abort the job.
656
945
  console.log(`ERROR waiting for page to be ${act.which} (${error.message})`);
657
- addError(true, act, 'timeout', `ERROR waiting for page to be ${act.which}`);
658
- await abortActs();
946
+ actIndex = await addError(
947
+ true, true, report, actIndex, `ERROR waiting for page to be ${act.which}`
948
+ );
659
949
  });
660
950
  // If the wait succeeded:
661
951
  if (actIndex > -2) {
@@ -686,21 +976,35 @@ const doActs = async (report, actIndex, page) => {
686
976
  // If the act is a revelation:
687
977
  if (act.type === 'reveal') {
688
978
  // Make all elements in the page visible.
689
- await require('./procs/allVis').allVis(page);
690
- act.result = {
691
- success: true
692
- };
979
+ await page.$$eval('body *', elements => {
980
+ elements.forEach(element => {
981
+ const styleDec = window.getComputedStyle(element);
982
+ if (styleDec.display === 'none') {
983
+ element.style.display = 'initial';
984
+ }
985
+ if (['hidden', 'collapse'].includes(styleDec.visibility)) {
986
+ element.style.visibility = 'inherit';
987
+ }
988
+ });
989
+ act.result = {
990
+ success: true
991
+ };
992
+ })
993
+ .catch(error => {
994
+ console.log(`ERROR making all elements visible (${error.message})`);
995
+ act.result = {
996
+ success: false
997
+ };
998
+ });
693
999
  }
694
1000
  // Otherwise, if the act performs tests of a tool:
695
1001
  else if (act.type === 'test') {
696
- // Add a description of the test to the act.
697
- act.what = tests[act.which];
1002
+ // Add a description of the tool to the act.
1003
+ act.what = tools[act.which];
698
1004
  // Initialize the options argument.
699
1005
  const options = {
700
1006
  report,
701
- act,
702
- granular: report.observe,
703
- scriptNonce: report.jobData.lastScriptNonce || ''
1007
+ act
704
1008
  };
705
1009
  // Add any specified arguments to it.
706
1010
  Object.keys(act).forEach(key => {
@@ -708,33 +1012,34 @@ const doActs = async (report, actIndex, page) => {
708
1012
  options[key] = act[key];
709
1013
  }
710
1014
  });
711
- // Initialize the test report.
1015
+ // Get the start time of the act.
712
1016
  const startTime = Date.now();
713
- let toolReport = {
714
- result: {
715
- success: false
716
- }
717
- };
718
1017
  // Perform the specified tests of the tool and get a report.
719
1018
  try {
720
- toolReport = await require(`./tests/${act.which}`).reporter(page, options);
721
- toolReport.result.success = true;
1019
+ const actReport = await require(`./tests/${act.which}`).reporter(page, options);
1020
+ // Import its test results and process data into the act.
1021
+ act.result = actReport && actReport.result || {};
1022
+ act.data = actReport && actReport.data || {};
1023
+ // If the page prevented the tool from operating:
1024
+ if (act.data.prevented) {
1025
+ // Add prevention data to the job data.
1026
+ report.jobData.preventions[act.which] = act.data.error;
1027
+ }
722
1028
  }
723
1029
  // If the testing failed:
724
1030
  catch(error) {
725
- // Report this but do not abort the job.
726
- console.log(`ERROR: Test act ${act.which} failed (${error.message.slice(0, 400)})`);
1031
+ // Report this.
1032
+ const message = error.message.slice(0, 400);
1033
+ console.log(`ERROR: Test act ${act.which} failed (${message})`);
1034
+ act.data.error = act.data.error ? `${act.data.error}; ${message}` : message;
727
1035
  }
728
- // Add the elapsed time to the report.
1036
+ // Add the elapsed time of the tool to the report.
729
1037
  const time = Math.round((Date.now() - startTime) / 1000);
730
1038
  const {toolTimes} = report.jobData;
731
1039
  if (! toolTimes[act.which]) {
732
1040
  toolTimes[act.which] = 0;
733
1041
  }
734
1042
  toolTimes[act.which] += time;
735
- // Add the result object (possibly an array) to the act.
736
- const resultCount = Object.keys(toolReport.result).length;
737
- act.result = resultCount ? toolReport.result : {success: false};
738
1043
  // If a standard-format result is to be included in the report:
739
1044
  const standard = report.standard || 'only';
740
1045
  if (['also', 'only'].includes(standard)) {
@@ -747,14 +1052,10 @@ const doActs = async (report, actIndex, page) => {
747
1052
  standardize(act);
748
1053
  // If the original-format result is not to be included in the report:
749
1054
  if (standard === 'only') {
750
- // Remove it, except any important property.
751
- if (act.result.important) {
752
- act.data = act.result.important;
753
- console.log(`>>>>>> Important ${act.which} data included in report`);
754
- }
1055
+ // Remove it.
755
1056
  delete act.result;
756
1057
  }
757
- // If the test has expectations:
1058
+ // If the act has expectations:
758
1059
  const expectations = act.expect;
759
1060
  if (expectations) {
760
1061
  // Initialize whether they were fulfilled.
@@ -871,10 +1172,8 @@ const doActs = async (report, actIndex, page) => {
871
1172
  }
872
1173
  // If the move fails:
873
1174
  catch(error) {
874
- // Add the error result to the act.
875
- addError(true, act, 'moveFailure', `ERROR: ${move} failed`);
876
- // Abort.
877
- await abortActs();
1175
+ // Add the error result to the act and abort the job.
1176
+ actIndex = await addError(true, true, report, actIndex, `ERROR: ${move} failed`);
878
1177
  }
879
1178
  if (act.result.success) {
880
1179
  try {
@@ -1210,37 +1509,27 @@ const doActs = async (report, actIndex, page) => {
1210
1509
  }
1211
1510
  // Otherwise, i.e. if the act type is unknown:
1212
1511
  else {
1213
- // Add the error result to the act.
1214
- addError(true, act, 'badType', 'ERROR: Invalid act type');
1215
- // Abort.
1216
- await abortActs();
1512
+ // Add the error result to the act and abort the job.
1513
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: Invalid act type');
1217
1514
  }
1218
1515
  }
1219
1516
  // Otherwise, a page URL is required but does not exist, so:
1220
1517
  else {
1221
- // Add an error result to the act.
1222
- addError(true, act, 'noURL', 'ERROR: Page has no URL');
1223
- // Abort.
1224
- await abortActs();
1518
+ // Add an error result to the act and abort the job.
1519
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: Page has no URL');
1225
1520
  }
1226
1521
  }
1227
1522
  // Otherwise, i.e. if no page exists:
1228
1523
  else {
1229
- // Add an error result to the act.
1230
- addError(true, act, 'noPage', 'ERROR: No page identified');
1231
- // Abort.
1232
- await abortActs();
1524
+ // Add an error result to the act and abort the job.
1525
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: No page identified');
1233
1526
  }
1234
1527
  act.endTime = Date.now();
1235
1528
  }
1236
1529
  // Otherwise, i.e. if the act is invalid:
1237
1530
  else {
1238
- // Quit and add error data to the report.
1239
- const errorMsg = `ERROR: Invalid act of type ${act.type}`;
1240
- console.log(errorMsg);
1241
- addError(true, act, 'badAct', errorMsg);
1242
- // Abort.
1243
- await abortActs();
1531
+ // Add error data to the act and abort the job.
1532
+ addError(true, true, report, actIndex, `ERROR: Invalid act of type ${act.type}`);
1244
1533
  }
1245
1534
  // Perform any remaining acts if not aborted.
1246
1535
  await doActs(report, actIndex + 1, page);
@@ -1280,6 +1569,7 @@ exports.doJob = async report => {
1280
1569
  report.jobData.presses = 0;
1281
1570
  report.jobData.amountRead = 0;
1282
1571
  report.jobData.toolTimes = {};
1572
+ report.jobData.preventions = {};
1283
1573
  process.on('message', message => {
1284
1574
  if (message === 'interrupt') {
1285
1575
  console.log('ERROR: Terminal interrupted the job');
@@ -1,3 +1,25 @@
1
+ /*
2
+ © 2023 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
+
1
23
  /*
2
24
  allCaps
3
25
  Related to Tenon rule 153.
@@ -1,3 +1,25 @@
1
+ /*
2
+ © 2023 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
+
1
23
  /*
2
24
  allHidden
3
25
  This test reports a page that is entirely or mainly hidden.