testaro 30.0.7 → 32.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.
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 +371 -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 +26 -2
  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.replace(/procs$/, '')}`);
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,19 +681,46 @@ 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.
@@ -533,10 +820,10 @@ const doActs = async (report, actIndex, page) => {
533
820
  }
534
821
  // Otherwise, i.e. if the launch or navigation failed:
535
822
  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();
823
+ // Add an error result to the act and abort the job.
824
+ actIndex = await addError(
825
+ true, true, report, actIndex, `ERROR: Launch failed (${launchResult.error})`
826
+ );
540
827
  }
541
828
  }
542
829
  // Otherwise, if a current page exists:
@@ -563,15 +850,15 @@ const doActs = async (report, actIndex, page) => {
563
850
  act.result.url = page.url();
564
851
  // If a prohibited redirection occurred:
565
852
  if (response.exception === 'badRedirection') {
566
- // Report this and quit.
567
- addError(true, act, 'badRedirection', 'ERROR: Navigation illicitly redirected');
568
- await abortActs();
853
+ // Report this and abort the job.
854
+ actIndex = await addError(
855
+ true, true, report, actIndex, 'ERROR: Navigation illicitly redirected'
856
+ );
569
857
  }
570
858
  // Otherwise, i.e. if the visit failed:
571
859
  else {
572
- // Report this and quit.
573
- addError(true, act, 'failure', 'ERROR: Visit failed');
574
- await abortActs();
860
+ // Report this and abort the job.
861
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: Visit failed');
575
862
  }
576
863
  }
577
864
  }
@@ -652,10 +939,11 @@ const doActs = async (report, actIndex, page) => {
652
939
  )
653
940
  // If the wait times out:
654
941
  .catch(async error => {
655
- // Quit.
942
+ // Report this and abort the job.
656
943
  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();
944
+ actIndex = await addError(
945
+ true, true, report, actIndex, `ERROR waiting for page to be ${act.which}`
946
+ );
659
947
  });
660
948
  // If the wait succeeded:
661
949
  if (actIndex > -2) {
@@ -686,21 +974,35 @@ const doActs = async (report, actIndex, page) => {
686
974
  // If the act is a revelation:
687
975
  if (act.type === 'reveal') {
688
976
  // Make all elements in the page visible.
689
- await require('./procs/allVis').allVis(page);
690
- act.result = {
691
- success: true
692
- };
977
+ await page.$$eval('body *', elements => {
978
+ elements.forEach(element => {
979
+ const styleDec = window.getComputedStyle(element);
980
+ if (styleDec.display === 'none') {
981
+ element.style.display = 'initial';
982
+ }
983
+ if (['hidden', 'collapse'].includes(styleDec.visibility)) {
984
+ element.style.visibility = 'inherit';
985
+ }
986
+ });
987
+ act.result = {
988
+ success: true
989
+ };
990
+ })
991
+ .catch(error => {
992
+ console.log(`ERROR making all elements visible (${error.message})`);
993
+ act.result = {
994
+ success: false
995
+ };
996
+ });
693
997
  }
694
998
  // Otherwise, if the act performs tests of a tool:
695
999
  else if (act.type === 'test') {
696
- // Add a description of the test to the act.
697
- act.what = tests[act.which];
1000
+ // Add a description of the tool to the act.
1001
+ act.what = tools[act.which];
698
1002
  // Initialize the options argument.
699
1003
  const options = {
700
1004
  report,
701
- act,
702
- granular: report.observe,
703
- scriptNonce: report.jobData.lastScriptNonce || ''
1005
+ act
704
1006
  };
705
1007
  // Add any specified arguments to it.
706
1008
  Object.keys(act).forEach(key => {
@@ -708,33 +1010,34 @@ const doActs = async (report, actIndex, page) => {
708
1010
  options[key] = act[key];
709
1011
  }
710
1012
  });
711
- // Initialize the test report.
1013
+ // Get the start time of the act.
712
1014
  const startTime = Date.now();
713
- let toolReport = {
714
- result: {
715
- success: false
716
- }
717
- };
718
1015
  // Perform the specified tests of the tool and get a report.
719
1016
  try {
720
- toolReport = await require(`./tests/${act.which}`).reporter(page, options);
721
- toolReport.result.success = true;
1017
+ const actReport = await require(`./tests/${act.which}`).reporter(page, options);
1018
+ // Import its test results and process data into the act.
1019
+ act.result = actReport && actReport.result || {};
1020
+ act.data = actReport && actReport.data || {};
1021
+ // If the page prevented the tool from operating:
1022
+ if (act.data.prevented) {
1023
+ // Add prevention data to the job data.
1024
+ report.jobData.preventions[act.which] = act.data.error;
1025
+ }
722
1026
  }
723
1027
  // If the testing failed:
724
1028
  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)})`);
1029
+ // Report this.
1030
+ const message = error.message.slice(0, 400);
1031
+ console.log(`ERROR: Test act ${act.which} failed (${message})`);
1032
+ act.data.error = act.data.error ? `${act.data.error}; ${message}` : message;
727
1033
  }
728
- // Add the elapsed time to the report.
1034
+ // Add the elapsed time of the tool to the report.
729
1035
  const time = Math.round((Date.now() - startTime) / 1000);
730
1036
  const {toolTimes} = report.jobData;
731
1037
  if (! toolTimes[act.which]) {
732
1038
  toolTimes[act.which] = 0;
733
1039
  }
734
1040
  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
1041
  // If a standard-format result is to be included in the report:
739
1042
  const standard = report.standard || 'only';
740
1043
  if (['also', 'only'].includes(standard)) {
@@ -747,14 +1050,10 @@ const doActs = async (report, actIndex, page) => {
747
1050
  standardize(act);
748
1051
  // If the original-format result is not to be included in the report:
749
1052
  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
- }
1053
+ // Remove it.
755
1054
  delete act.result;
756
1055
  }
757
- // If the test has expectations:
1056
+ // If the act has expectations:
758
1057
  const expectations = act.expect;
759
1058
  if (expectations) {
760
1059
  // Initialize whether they were fulfilled.
@@ -871,10 +1170,8 @@ const doActs = async (report, actIndex, page) => {
871
1170
  }
872
1171
  // If the move fails:
873
1172
  catch(error) {
874
- // Add the error result to the act.
875
- addError(true, act, 'moveFailure', `ERROR: ${move} failed`);
876
- // Abort.
877
- await abortActs();
1173
+ // Add the error result to the act and abort the job.
1174
+ actIndex = await addError(true, true, report, actIndex, `ERROR: ${move} failed`);
878
1175
  }
879
1176
  if (act.result.success) {
880
1177
  try {
@@ -1210,37 +1507,27 @@ const doActs = async (report, actIndex, page) => {
1210
1507
  }
1211
1508
  // Otherwise, i.e. if the act type is unknown:
1212
1509
  else {
1213
- // Add the error result to the act.
1214
- addError(true, act, 'badType', 'ERROR: Invalid act type');
1215
- // Abort.
1216
- await abortActs();
1510
+ // Add the error result to the act and abort the job.
1511
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: Invalid act type');
1217
1512
  }
1218
1513
  }
1219
1514
  // Otherwise, a page URL is required but does not exist, so:
1220
1515
  else {
1221
- // Add an error result to the act.
1222
- addError(true, act, 'noURL', 'ERROR: Page has no URL');
1223
- // Abort.
1224
- await abortActs();
1516
+ // Add an error result to the act and abort the job.
1517
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: Page has no URL');
1225
1518
  }
1226
1519
  }
1227
1520
  // Otherwise, i.e. if no page exists:
1228
1521
  else {
1229
- // Add an error result to the act.
1230
- addError(true, act, 'noPage', 'ERROR: No page identified');
1231
- // Abort.
1232
- await abortActs();
1522
+ // Add an error result to the act and abort the job.
1523
+ actIndex = await addError(true, true, report, actIndex, 'ERROR: No page identified');
1233
1524
  }
1234
1525
  act.endTime = Date.now();
1235
1526
  }
1236
1527
  // Otherwise, i.e. if the act is invalid:
1237
1528
  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();
1529
+ // Add error data to the act and abort the job.
1530
+ addError(true, true, report, actIndex, `ERROR: Invalid act of type ${act.type}`);
1244
1531
  }
1245
1532
  // Perform any remaining acts if not aborted.
1246
1533
  await doActs(report, actIndex + 1, page);
@@ -1280,6 +1567,7 @@ exports.doJob = async report => {
1280
1567
  report.jobData.presses = 0;
1281
1568
  report.jobData.amountRead = 0;
1282
1569
  report.jobData.toolTimes = {};
1570
+ report.jobData.preventions = {};
1283
1571
  process.on('message', message => {
1284
1572
  if (message === 'interrupt') {
1285
1573
  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.