testaro 67.0.0 → 68.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 (95) hide show
  1. package/LICENSE +4 -16
  2. package/README.md +10 -2
  3. package/UPGRADES.md +1 -1
  4. package/dirWatch.js +2 -3
  5. package/ed11y/editoria11y.min.js +109 -690
  6. package/ed11y/editoria11y210.min.js +747 -0
  7. package/netWatch.js +6 -6
  8. package/package.json +1 -1
  9. package/procs/aslint.js +2 -2
  10. package/procs/catalog.js +190 -0
  11. package/procs/{dateOf.js → dateTime.js} +6 -4
  12. package/procs/doActs.js +1227 -0
  13. package/procs/doTestAct.js +63 -29
  14. package/procs/error.js +53 -0
  15. package/procs/job.js +64 -38
  16. package/procs/launch.js +596 -0
  17. package/procs/nu.js +3 -18
  18. package/procs/shoot.js +18 -2
  19. package/procs/testaro.js +102 -125
  20. package/procs/xPath.js +62 -0
  21. package/run.js +42 -1938
  22. package/scratch/README.md +9 -0
  23. package/testaro/adbID.js +3 -3
  24. package/testaro/allCaps.js +4 -5
  25. package/testaro/allHidden.js +19 -18
  26. package/testaro/allSlanted.js +4 -5
  27. package/testaro/altScheme.js +3 -3
  28. package/testaro/attVal.js +19 -35
  29. package/testaro/autocomplete.js +65 -62
  30. package/testaro/bulk.js +21 -20
  31. package/testaro/buttonMenu.js +112 -33
  32. package/testaro/captionLoc.js +3 -3
  33. package/testaro/datalistRef.js +4 -5
  34. package/testaro/distortion.js +3 -3
  35. package/testaro/docType.js +6 -9
  36. package/testaro/dupAtt.js +12 -25
  37. package/testaro/elements.js +4 -3
  38. package/testaro/embAc.js +4 -2
  39. package/testaro/focAll.js +6 -13
  40. package/testaro/focAndOp.js +3 -3
  41. package/testaro/focInd.js +3 -3
  42. package/testaro/focVis.js +4 -3
  43. package/testaro/headEl.js +5 -12
  44. package/testaro/headingAmb.js +45 -88
  45. package/testaro/hovInd.js +5 -5
  46. package/testaro/hover.js +44 -8
  47. package/testaro/hr.js +4 -4
  48. package/testaro/imageLink.js +3 -3
  49. package/testaro/labClash.js +3 -3
  50. package/testaro/legendLoc.js +3 -3
  51. package/testaro/lineHeight.js +3 -3
  52. package/testaro/linkAmb.js +25 -17
  53. package/testaro/linkExt.js +5 -5
  54. package/testaro/linkOldAtt.js +4 -3
  55. package/testaro/linkTo.js +4 -3
  56. package/testaro/linkUl.js +4 -5
  57. package/testaro/miniText.js +4 -3
  58. package/testaro/motion.js +3 -22
  59. package/testaro/nonTable.js +4 -5
  60. package/testaro/optRoleSel.js +3 -3
  61. package/testaro/phOnly.js +3 -3
  62. package/testaro/pseudoP.js +5 -5
  63. package/testaro/radioSet.js +4 -5
  64. package/testaro/role.js +4 -5
  65. package/testaro/secHeading.js +4 -5
  66. package/testaro/shoot0.js +3 -2
  67. package/testaro/shoot1.js +3 -2
  68. package/testaro/styleDiff.js +5 -12
  69. package/testaro/tabNav.js +30 -118
  70. package/testaro/targetSmall.js +30 -15
  71. package/testaro/textNodes.js +3 -1
  72. package/testaro/textSem.js +4 -5
  73. package/testaro/title.js +4 -2
  74. package/testaro/titledEl.js +3 -3
  75. package/testaro/zIndex.js +3 -3
  76. package/tests/alfa.js +28 -54
  77. package/tests/aslint.js +20 -53
  78. package/tests/axe.js +76 -13
  79. package/tests/ed11y.js +69 -141
  80. package/tests/htmlcs.js +69 -38
  81. package/tests/ibm.js +54 -9
  82. package/tests/nuVal.js +65 -12
  83. package/tests/nuVnu.js +76 -26
  84. package/tests/qualWeb.js +89 -44
  85. package/tests/testaro.js +288 -273
  86. package/tests/wave.js +142 -117
  87. package/tests/wax.js +61 -42
  88. package/procs/getLocatorData.js +0 -192
  89. package/procs/identify.js +0 -250
  90. package/procs/isInlineLink.js +0 -42
  91. package/procs/screenShot.js +0 -32
  92. package/procs/standardize.js +0 -524
  93. package/procs/target.js +0 -90
  94. package/procs/tellServer.js +0 -43
  95. package/scripts/dumpAlts.js +0 -28
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  © 2024–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
- © 2025 Jonathan Robert Pool.
3
+ © 2025–2026 Jonathan Robert Pool.
4
4
 
5
5
  Licensed under the MIT License. See LICENSE file at the project root or
6
6
  https://opensource.org/license/mit/ for details.
@@ -9,18 +9,55 @@
9
9
  */
10
10
 
11
11
  /*
12
- doTestAct
12
+ doActs
13
13
  Performs the tests of an act.
14
+ This file is designed to be run as a child process.
14
15
  */
15
16
 
17
+ // ERROR LOGGING
18
+
19
+ // Log uncaught exceptions.
20
+ process.on('uncaughtException', error => {
21
+ console.error(`ERROR:\n${error.stack || 'Uncaught exception'}`);
22
+ process.exit(1);
23
+ });
24
+
25
+ // Log unhandled rejections.
26
+ process.on('unhandledRejection', reason => {
27
+ console.error(`ERROR:\n${reason?.stack || 'Unhandled rejection'}`);
28
+ process.exit(1);
29
+ });
30
+
16
31
  // IMPORTS
17
32
 
18
33
  // Module to perform file operations.
19
34
  const fs = require('fs/promises');
20
- //Modules to close and launch browsers.
21
- const {browserClose, launch} = require(`${__dirname}/../run`);
22
- // Module to set operating-system constants.
23
- const os = require('os');
35
+ // Module to close and launch browsers.
36
+ const {browserClose, launch} = require('./launch');
37
+
38
+ // CONSTANTS
39
+
40
+ /*
41
+ Tool XPath requirements.
42
+ none: Needs no script or extra load time.
43
+ own: Needs extra load time for its own XPath computations.
44
+ script: Needs the window.getXPath script.
45
+ attribute: Needs data-xpath attributes made with window.getXPath.
46
+ */
47
+ const xPathNeeds = {
48
+ alfa: 'own',
49
+ aslint: 'own',
50
+ axe: 'attribute',
51
+ ed11y: 'script',
52
+ htmlcs: 'attribute',
53
+ ibm: 'attribute',
54
+ nuVal: 'attribute',
55
+ nuVnu: 'attribute',
56
+ qualWeb: 'attribute',
57
+ wave: 'script',
58
+ wax: 'attribute'
59
+ };
60
+ const accessibleNameNeeders = ['testaro'];
24
61
 
25
62
  // FUNCTIONS
26
63
 
@@ -52,19 +89,20 @@ const doTestAct = async (reportPath, actIndex) => {
52
89
  const browserID = act.launch && act.launch.browserID || report.browserID;
53
90
  const targetURL = act.launch && act.launch.target && act.launch.target.url || report.target.url;
54
91
  // Launch a browser, navigate to the URL, and update the run-module page export.
55
- await launch(
92
+ page = await launch({
56
93
  report,
57
94
  actIndex,
58
- 'high',
59
- browserID,
60
- targetURL
61
- );
95
+ tempBrowserID: browserID,
96
+ tempURL: targetURL,
97
+ xPathNeed: xPathNeeds[which] ?? 'none',
98
+ needsAccessibleName: accessibleNameNeeders.includes(which)
99
+ });
62
100
  // If the launch aborted the job:
63
- if (report.jobData && report.jobData.aborted) {
64
- // Close any existing browser.
65
- await browserClose();
66
- // Save the revised report.
101
+ if (report.jobData?.aborted) {
102
+ // Close the browser and its context, if they exist.
103
+ await browserClose(page);
67
104
  const reportJSON = JSON.stringify(report);
105
+ // Save the revised report.
68
106
  await fs.writeFile(reportPath, reportJSON);
69
107
  // Report this.
70
108
  sendMessage({
@@ -73,11 +111,6 @@ const doTestAct = async (reportPath, actIndex) => {
73
111
  });
74
112
  process.exit(1);
75
113
  }
76
- // Otherwise, i.e. if the launch did not abort the job:
77
- else {
78
- // Get the updated page.
79
- page = require('../run').page;
80
- }
81
114
  }
82
115
  // If the page exists or the tool is Testaro:
83
116
  if (page || which === 'testaro') {
@@ -89,11 +122,16 @@ const doTestAct = async (reportPath, actIndex) => {
89
122
  act.result = actReport.result;
90
123
  // If the tool reported that the page prevented testing:
91
124
  if (act.data && act.data.prevented) {
125
+ const {standardResult} = act.result;
126
+ // Add this to any standard result.
127
+ if (standardResult) {
128
+ standardResult.prevented = true;
129
+ }
92
130
  // Add prevention data to the job data.
93
131
  report.jobData.preventions[which] = act.data.error;
94
132
  }
95
- // Close any existing browser.
96
- await browserClose();
133
+ // Close the browser and its context, if they exist.
134
+ await browserClose(page);
97
135
  const reportJSON = JSON.stringify(report);
98
136
  // Save the revised report.
99
137
  await fs.writeFile(reportPath, reportJSON);
@@ -105,8 +143,8 @@ const doTestAct = async (reportPath, actIndex) => {
105
143
  }
106
144
  // If the tool invocation failed:
107
145
  catch(error) {
108
- // Close any existing browser.
109
- await browserClose();
146
+ // Close the browser and its context, if they exist.
147
+ await browserClose(page);
110
148
  // Save the revised report.
111
149
  const reportJSON = JSON.stringify(report);
112
150
  await fs.writeFile(reportPath, reportJSON);
@@ -128,8 +166,6 @@ const doTestAct = async (reportPath, actIndex) => {
128
166
  act.data.error = 'No page';
129
167
  // Add prevention data to the job data.
130
168
  report.jobData.preventions[which] = act.data.error;
131
- // Close any existing browser.
132
- await browserClose();
133
169
  const reportJSON = JSON.stringify(report);
134
170
  // Save the revised report.
135
171
  await fs.writeFile(reportPath, reportJSON);
@@ -143,7 +179,6 @@ const doTestAct = async (reportPath, actIndex) => {
143
179
  process.exit(1);
144
180
  }
145
181
  };
146
-
147
182
  process.on('uncaughtException', error => {
148
183
  console.log(`ERROR: uncaughtException (${error.message})`);
149
184
  sendMessage({
@@ -152,7 +187,6 @@ process.on('uncaughtException', error => {
152
187
  });
153
188
  process.exit(1);
154
189
  });
155
-
156
190
  process.on('unhandledRejection', error => {
157
191
  const message = error && error.message ? error.message : String(error);
158
192
  console.log(`ERROR: unhandledRejection (${message})`);
@@ -162,6 +196,6 @@ process.on('unhandledRejection', error => {
162
196
  });
163
197
  process.exit(1);
164
198
  });
165
-
166
199
  const args = process.argv;
200
+ // Perform the specified test act.
167
201
  doTestAct(args[2], Number.parseInt(args[3]));
package/procs/error.js ADDED
@@ -0,0 +1,53 @@
1
+ /*
2
+ © 2021–2025 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2025–2026 Jonathan Robert Pool.
4
+
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
6
+
7
+ SPDX-License-Identifier: MIT
8
+ */
9
+
10
+ /*
11
+ error.js
12
+ Handles errors.
13
+ */
14
+
15
+ // Reports a job being aborted.
16
+ const abortActs = exports.abortActs = (report, actIndex) => {
17
+ // Add data on the aborted act to the report.
18
+ report.jobData.abortTime = nowString();
19
+ report.jobData.abortedAct = actIndex;
20
+ report.jobData.aborted = true;
21
+ // Report that the job is aborted.
22
+ console.log(`ERROR: Job aborted on act ${actIndex}`);
23
+ };
24
+ // Adds an error result to an act.
25
+ exports.addError = (alsoLog, alsoAbort, report, actIndex, message) => {
26
+ // If the error is to be logged:
27
+ if (alsoLog) {
28
+ // Log it.
29
+ console.log(message);
30
+ }
31
+ const act = report.acts[actIndex ?? -1];
32
+ // If an act was specified:
33
+ if (act) {
34
+ // Add error data to the result.
35
+ act.result ??= {};
36
+ act.result.success ??= false;
37
+ act.result.error ??= message;
38
+ // If the act is a test act:
39
+ if (act.type === 'test') {
40
+ act.data ??= {};
41
+ // Add prevention data to the act data.
42
+ act.data.prevented = true;
43
+ act.data.error = message;
44
+ // Add prevention data to the job data.
45
+ report.jobData.preventions[act.which] = message;
46
+ }
47
+ };
48
+ // If the job is to be aborted:
49
+ if (alsoAbort) {
50
+ // Add this to the report.
51
+ abortActs(report, actIndex);
52
+ }
53
+ };
package/procs/job.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /*
2
2
  © 2024 CVS Health and/or one of its affiliates. All rights reserved.
3
+ © 2026 Jonathan Robert Pool.
3
4
 
4
- Licensed under the MIT License. See LICENSE file at the project root or
5
- https://opensource.org/license/mit/ for details.
5
+ Licensed under the MIT License. See LICENSE file at the project root or https://opensource.org/license/mit/ for details.
6
6
 
7
7
  SPDX-License-Identifier: MIT
8
8
  */
@@ -19,20 +19,20 @@ const {actSpecs} = require('../actSpecs');
19
19
  // Data on devices.
20
20
  const {devices} = require('playwright');
21
21
  // Module to get dates from time stamps.
22
- const {dateOf} = require('./dateOf');
22
+ const {dateOf} = require('./dateTime');
23
23
 
24
24
  // CONSTANTS
25
25
 
26
26
  // Names and descriptions of tools.
27
- const tools = exports.tools ={
27
+ const tools = exports.tools = {
28
28
  alfa: 'Alfa',
29
29
  aslint: 'ASLint',
30
30
  axe: 'Axe',
31
31
  ed11y: 'Editoria11y',
32
- htmlcs: 'HTML CodeSniffer WCAG 2.1 AA ruleset',
33
- ibm: 'IBM Accessibility Checker',
34
- nuVal: 'Nu Html Checker API',
35
- nuVnu: 'Nu Html Checker',
32
+ htmlcs: 'HTML CodeSniffer',
33
+ ibm: 'Accessibility Checker',
34
+ nuVal: 'Html Checker API',
35
+ nuVnu: 'Html Checker',
36
36
  qualWeb: 'QualWeb',
37
37
  testaro: 'Testaro',
38
38
  wax: 'WallyAX',
@@ -167,7 +167,7 @@ const isValidAct = exports.isValidAct = act => {
167
167
  return false;
168
168
  }
169
169
  };
170
- // Returns blank if a job is valid, or an error message.
170
+ // Returns whether a job is valid and, if not, why not.
171
171
  exports.isValidJob = job => {
172
172
  // If any job was provided:
173
173
  if (job) {
@@ -187,38 +187,68 @@ exports.isValidJob = job => {
187
187
  } = job;
188
188
  // Return an error for the first missing or invalid property.
189
189
  if (! id || typeof id !== 'string') {
190
- return 'Bad job ID';
190
+ return {
191
+ isValid: false,
192
+ error: 'Bad job ID'
193
+ };
191
194
  }
192
195
  if (typeof strict !== 'boolean') {
193
- return 'Bad job strict';
196
+ return {
197
+ isValid: false,
198
+ error: 'Bad job strict'
199
+ };
194
200
  }
195
201
  if (! ['also', 'only', 'no'].includes(standard)) {
196
- return 'Bad job standard';
202
+ return {
203
+ isValid: false,
204
+ error: 'Bad job standard'
205
+ };
197
206
  }
198
207
  if (typeof observe !== 'boolean') {
199
- return 'Bad job observe';
208
+ return {
209
+ isValid: false,
210
+ error: 'Bad job observe'
211
+ }
200
212
  }
201
213
  if (! isDeviceID(device.id)) {
202
- return 'Bad job deviceID';
214
+ return {
215
+ isValid: false,
216
+ error: 'Bad job deviceID'
217
+ };
203
218
  }
204
219
  if (! isBrowserID(browserID)) {
205
- return 'Bad job browserID';
220
+ return {
221
+ isValid: false,
222
+ error: 'Bad job browserID'
223
+ };
206
224
  }
207
225
  if (
208
226
  ! (creationTimeStamp && typeof creationTimeStamp === 'string' && dateOf(creationTimeStamp))
209
227
  ) {
210
- return 'bad job creationTimeStamp';
228
+ return {
229
+ isValid: false,
230
+ error: 'Bad job creationTimeStamp'
231
+ };
211
232
  }
212
233
  if (
213
234
  ! (executionTimeStamp && typeof executionTimeStamp === 'string') && dateOf(executionTimeStamp)
214
235
  ) {
215
- return 'bad job executionTimeStamp';
236
+ return {
237
+ isValid: false,
238
+ error: 'Bad job executionTimeStamp'
239
+ };
216
240
  }
217
241
  if (typeof target !== 'object' || target.url && ! isURL(target.url) || target.what === '') {
218
- return 'bad job target';
242
+ return {
243
+ isValid: false,
244
+ error: 'Bad job target'
245
+ };
219
246
  }
220
247
  if (sources && typeof sources !== 'object') {
221
- return 'Bad job sources';
248
+ return {
249
+ isValid: false,
250
+ error: 'Bad job sources'
251
+ };
222
252
  }
223
253
  if (
224
254
  ! acts
@@ -226,32 +256,28 @@ exports.isValidJob = job => {
226
256
  || ! acts.length
227
257
  || ! acts.every(act => act.type && typeof act.type === 'string')
228
258
  ) {
229
- return 'Bad job acts';
259
+ return {
260
+ isValid: false,
261
+ error: 'Bad job acts'
262
+ };
230
263
  }
231
264
  const invalidAct = acts.find(act => ! isValidAct(act));
232
265
  if (invalidAct) {
233
- return `Invalid act:\n${JSON.stringify(invalidAct, null, 2)}`;
266
+ return {
267
+ isValid: false,
268
+ error: `Invalid act:\n${JSON.stringify(invalidAct, null, 2)}`
269
+ };
234
270
  }
235
- return '';
271
+ return {
272
+ isValid: true
273
+ };
236
274
  }
237
275
  // Otherwise, i.e. if no job was provided:
238
276
  else {
239
277
  // Return this.
240
- return 'no job';
241
- }
242
- };
243
- // Limits the length of and unilinearizes a string.
244
- exports.cap = rawString => {
245
- const string = (rawString.trim() || '').replace(/[\s\u2028\u2029]+/g, ' ');
246
- if (string && string.length > 1000) {
247
- return `${string.slice(0, 500)} … ${string.slice(-500)}`;
248
- }
249
- else if (string) {
250
- return string;
251
- }
252
- else {
253
- return '';
278
+ return {
279
+ isValid: false,
280
+ error: 'No job'
281
+ };
254
282
  }
255
283
  };
256
- // Simplifies the spacing of a string.
257
- exports.tidy = string => string.replace(/\s+/g, ' ');