testaro 5.5.3 → 5.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/create.js CHANGED
@@ -15,13 +15,23 @@ const {handleRequest} = require('./run');
15
15
  const {batchify} = require('./batchify');
16
16
 
17
17
  // ########## CONSTANTS
18
+
18
19
  const scriptDir = process.env.SCRIPTDIR;
19
20
  const batchDir = process.env.BATCHDIR;
20
21
  const reportDir = process.env.REPORTDIR;
22
+ const successHosts = [];
23
+ const crashHosts = [];
24
+ const timeoutHosts = [];
25
+
26
+ // ########## VARIABLES
27
+
28
+ let healthy = true;
29
+ let timeLimit = 300;
30
+ let reportCount = 0;
21
31
 
22
32
  // ########## FUNCTIONS
23
33
 
24
- // Runs one script and writes a report file.
34
+ // Runs one script with no batch and writes a report file.
25
35
  const runHost = async (id, script) => {
26
36
  const report = {
27
37
  id,
@@ -34,10 +44,69 @@ const runHost = async (id, script) => {
34
44
  const reportJSON = JSON.stringify(report, null, 2);
35
45
  await fs.writeFile(`${reportDir}/${id}.json`, reportJSON);
36
46
  };
47
+ // Recursively runs host scripts.
48
+ const runHosts = async (timeStamp, specs) => {
49
+ // If any scripts remain to be run and the process has not been interrupted:
50
+ if (specs.length && healthy) {
51
+ // Run the first one and save the report with a host-suffixed ID.
52
+ const spec = specs.shift();
53
+ const {id, host, script} = spec;
54
+ // Fork a child process to run the script for that host.
55
+ const subprocess = fork(
56
+ 'runHost', [id, JSON.stringify(script), JSON.stringify(host)],
57
+ {
58
+ detached: true,
59
+ stdio: [0, 1, 'ignore', 'ipc']
60
+ }
61
+ );
62
+ // If the child process times out:
63
+ const timer = setTimeout(async () => {
64
+ clearTimeout(timer);
65
+ // Record the host as timed out.
66
+ timeoutHosts.push(id);
67
+ // Kill the child process.
68
+ subprocess.kill();
69
+ console.log(`Script for host ${id} exceeded ${timeLimit}-second time limit, so was killed`);
70
+ // Run the remaining host scripts.
71
+ await runHosts(timeStamp, specs);
72
+ }, 1000 * (script.timeLimit || timeLimit));
73
+ // If the child process succeeds:
74
+ subprocess.on('message', async message => {
75
+ clearTimeout(timer);
76
+ // Save its report as a file.
77
+ await fs.writeFile(`${reportDir}/${id}.json`, message);
78
+ console.log(`Report ${id}.json saved in ${reportDir}`);
79
+ reportCount++;
80
+ successHosts.push(id);
81
+ // Run the remaining host scripts.
82
+ await runHosts(timeStamp, specs);
83
+ });
84
+ // If the child process ends:
85
+ subprocess.on('exit', async () => {
86
+ // If it ended in a crash:
87
+ if (! (successHosts.includes(id) || timeoutHosts.includes(id))) {
88
+ clearTimeout(timer);
89
+ crashHosts.push(id);
90
+ console.log(`Script for host ${id} crashed`);
91
+ // Run the remaining host scripts.
92
+ await runHosts(timeStamp, specs);
93
+ }
94
+ });
95
+ }
96
+ // Otherwise, i.e. if no more host scripts are to be run:
97
+ else {
98
+ // Report the metadata.
99
+ console.log(`Count of ${timeStamp}- reports saved in ${reportDir}: ${reportCount}`);
100
+ if (timeoutHosts.length) {
101
+ console.log(`Hosts timed out:\n${JSON.stringify(timeoutHosts, null, 2)}`);
102
+ }
103
+ if (crashHosts.length) {
104
+ console.log(`Hosts crashed:\n${JSON.stringify(crashHosts, null, 2)}`);
105
+ }
106
+ }
107
+ };
37
108
  // Runs a file-based job and writes a report file for the script or each host.
38
109
  exports.runJob = async (scriptID, batchID) => {
39
- let healthy = true;
40
- let childAlive = true;
41
110
  process.on('SIGINT', () => {
42
111
  console.log('ERROR: Terminal interrupted runJob');
43
112
  healthy = false;
@@ -47,8 +116,7 @@ exports.runJob = async (scriptID, batchID) => {
47
116
  const scriptJSON = await fs.readFile(`${scriptDir}/${scriptID}.json`, 'utf8');
48
117
  const script = JSON.parse(scriptJSON);
49
118
  // Get the time limit of the script or, if none, set it to 5 minutes.
50
- let {timeLimit} = script;
51
- timeLimit = timeLimit || 300;
119
+ timeLimit = script.timeLimit || timeLimit;
52
120
  // Identify the start time and a timestamp.
53
121
  const timeStamp = Math.floor((Date.now() - Date.UTC(2022, 1)) / 2000).toString(36);
54
122
  // If there is a batch:
@@ -58,79 +126,8 @@ exports.runJob = async (scriptID, batchID) => {
58
126
  const batchJSON = await fs.readFile(`${batchDir}/${batchID}.json`, 'utf8');
59
127
  batch = JSON.parse(batchJSON);
60
128
  const specs = batchify(script, batch, timeStamp);
61
- const batchSize = specs.length;
62
- const sizedRep = `${batchSize} report${batchSize > 1 ? 's' : ''}`;
63
- const timeoutHosts = [];
64
- const crashHosts = [];
65
- // FUNCTION DEFINITION START
66
- // Recursively runs host scripts.
67
- const runHosts = specs => {
68
- // If any scripts remain to be run and the process has not been interrupted:
69
- if (specs.length && healthy) {
70
- childAlive = true;
71
- // Run the first one and save the report with a host-suffixed ID.
72
- const spec = specs.shift();
73
- const {id, host, script} = spec;
74
- const subprocess = fork(
75
- 'runHost', [id, JSON.stringify(script), JSON.stringify(host)],
76
- {
77
- detached: true,
78
- stdio: [0, 1, 'ipc']
79
- }
80
- );
81
- subprocess.on('exit', () => {
82
- childAlive = false;
83
- });
84
- const startTime = Date.now();
85
- // At 5-second intervals:
86
- const reCheck = setInterval(async () => {
87
- // If the user has not interrupted the process:
88
- if (healthy) {
89
- // If there is no need to keep checking:
90
- const reportNames = await fs.readdir(reportDir);
91
- const timedOut = Date.now() - startTime > 1000 * timeLimit;
92
- const reportWritten = reportNames.includes(`${id}.json`);
93
- if (timedOut || reportWritten || ! childAlive) {
94
- // Stop checking.
95
- clearInterval(reCheck);
96
- // If the cause is a timeout:
97
- if (timedOut) {
98
- // Add the host to the array of timed-out hosts.
99
- timeoutHosts.push(id);
100
- }
101
- // Otherwise, if the cause is a child crash:
102
- else if (! (childAlive || reportWritten)) {
103
- // Add the host to the array of crashed hosts.
104
- crashHosts.push(id);
105
- }
106
- // Run the script of the next host.
107
- runHosts(specs);
108
- }
109
- }
110
- // Otherwise, i.e. if the user has interrupted the process:
111
- else {
112
- // Tell the script run to quit.
113
- subprocess.send('interrupt');
114
- // Stop checking.
115
- clearInterval(reCheck);
116
- }
117
- }, 5000);
118
- }
119
- else {
120
- console.log(`${sizedRep} ${timeStamp}-....json in ${process.env.REPORTDIR}`);
121
- if (timeoutHosts.length) {
122
- console.log(`Reports not created:\n${JSON.stringify(timeoutHosts), null, 2}`);
123
- }
124
- if (crashHosts.length) {
125
- console.log(
126
- `Hosts crashed with or without report:\n${JSON.stringify(crashHosts, null, 2)}`
127
- );
128
- }
129
- }
130
- };
131
- // FUNCTION DEFINITION END
132
129
  // Recursively run each host script and save the reports.
133
- runHosts(specs);
130
+ runHosts(timeStamp, specs);
134
131
  }
135
132
  // Otherwise, i.e. if there is no batch:
136
133
  else {
@@ -140,7 +137,7 @@ exports.runJob = async (scriptID, batchID) => {
140
137
  }
141
138
  }
142
139
  catch(error) {
143
- console.log(`ERROR: ${error.message}\n${error.stack}`);
140
+ console.log(`ERROR running job (${error.message})\n${error.stack}`);
144
141
  }
145
142
  }
146
143
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "5.5.3",
3
+ "version": "5.5.6",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/run.js CHANGED
@@ -98,8 +98,6 @@ let actCount = 0;
98
98
  let browserContext;
99
99
  let browserTypeName;
100
100
  let requestedURL = '';
101
- // All browsers launched.
102
- let browsers = [];
103
101
 
104
102
  // ########## VALIDATORS
105
103
 
@@ -248,24 +246,11 @@ const isValidReport = async report => {
248
246
 
249
247
  // ########## OTHER FUNCTIONS
250
248
 
251
- // Closes all existing browsers.
252
- const closeBrowsers = async () => {
253
- for (const browserObj of browsers) {
254
- const {browser, typeName} = browserObj;
255
- if (browser) {
256
- console.log(`Closing ${typeName} browser, version ${browser.version()}`);
257
- await browser.close();
258
- }
259
- }
260
- browsers = [];
261
- };
262
249
  // Launches a browser.
263
250
  const launch = async typeName => {
264
251
  const browserType = require('playwright')[typeName];
265
252
  // If the specified browser type exists:
266
253
  if (browserType) {
267
- // Close any existing browser.
268
- await closeBrowsers();
269
254
  // Launch a browser of the specified type.
270
255
  const browserOptions = {};
271
256
  if (debug) {
@@ -274,55 +259,70 @@ const launch = async typeName => {
274
259
  if (waits) {
275
260
  browserOptions.slowMo = waits;
276
261
  }
277
- const browser = await browserType.launch(browserOptions);
278
- // Register it.
279
- browsers.push({
280
- browser,
281
- typeName
262
+ let healthy = true;
263
+ const browser = await browserType.launch(browserOptions)
264
+ .catch(error => {
265
+ healthy = false;
266
+ console.log(`ERROR launching browser: ${error.message.replace(/\n.+/s, '')}`);
282
267
  });
283
- // Create a new context (window) in it, taller if debugging is on.
284
- const viewport = debug ? {
285
- viewPort: {
286
- width: 1280,
287
- height: 1120
288
- }
289
- } : {};
290
- browserContext = await browser.newContext(viewport);
291
- // When a page is added to the browser context:
292
- browserContext.on('page', page => {
293
- // Make abbreviations of its console messages get reported in the Playwright console.
294
- page.on('console', msg => {
295
- let msgText = msg.text();
296
- if (msgText.length > 300) {
297
- msgText = `${msgText.slice(0, 150)} ... ${msgText.slice(-150)}`;
268
+ // If the launch succeeded:
269
+ if (healthy) {
270
+ // Create a new context (window) in it, taller if debugging is on.
271
+ const viewport = debug ? {
272
+ viewPort: {
273
+ width: 1280,
274
+ height: 1120
298
275
  }
299
- console.log(`[${msgText}]`);
300
- const msgTextLC = msgText.toLowerCase();
301
- const msgLength = msgText.length;
302
- logCount++;
303
- logSize += msgLength;
304
- if (errorWords.some(word => msgTextLC.includes(word))) {
305
- errorLogCount++;
306
- errorLogSize += msgLength;
307
- }
308
- const msgLC = msgText.toLowerCase();
309
- if (msgText.includes('403') && (msgLC.includes('status') || msgLC.includes('prohibited'))) {
310
- prohibitedCount++;
311
- }
312
- });
313
- });
314
- // Open the first page of the context.
315
- const page = await browserContext.newPage();
316
- if (debug) {
317
- page.setViewportSize({
318
- width: 1280,
319
- height: 1120
276
+ } : {};
277
+ browserContext = await browser.newContext(viewport);
278
+ // When a page is added to the browser context:
279
+ browserContext.on('page', page => {
280
+ // Make abbreviations of its console messages get reported in the Playwright console.
281
+ page.on('console', msg => {
282
+ const msgText = msg.text();
283
+ const parts = [msgText.slice(0, 75)];
284
+ if (msgText.length > 75) {
285
+ parts.push(msgText.slice(75, 150));
286
+ if (msgText.length > 150) {
287
+ const tail = msgText.slice(150).slice(-150);
288
+ if (msgText.length > 300) {
289
+ parts.push('...');
290
+ }
291
+ parts.push(tail.slice(0, 75));
292
+ if (tail.length > 75) {
293
+ parts.push(tail.slice(75));
294
+ }
295
+ }
296
+ }
297
+ const indentedMsg = parts.map(part => ` | ${part}`).join('\n');
298
+ console.log(`\n${indentedMsg}`);
299
+ const msgTextLC = msgText.toLowerCase();
300
+ const msgLength = msgText.length;
301
+ logCount++;
302
+ logSize += msgLength;
303
+ if (errorWords.some(word => msgTextLC.includes(word))) {
304
+ errorLogCount++;
305
+ errorLogSize += msgLength;
306
+ }
307
+ const msgLC = msgText.toLowerCase();
308
+ if (msgText.includes('403') && (msgLC.includes('status') || msgLC.includes('prohibited'))) {
309
+ prohibitedCount++;
310
+ }
311
+ });
320
312
  });
313
+ // Open the first page of the context.
314
+ const page = await browserContext.newPage();
315
+ if (debug) {
316
+ page.setViewportSize({
317
+ width: 1280,
318
+ height: 1120
319
+ });
320
+ }
321
+ // Wait until it is stable.
322
+ await page.waitForLoadState('domcontentloaded');
323
+ // Update the name of the current browser type and store it in the page.
324
+ page.browserTypeName = browserTypeName = typeName;
321
325
  }
322
- // Wait until it is stable.
323
- await page.waitForLoadState('domcontentloaded');
324
- // Update the name of the current browser type and store it in the page.
325
- page.browserTypeName = browserTypeName = typeName;
326
326
  }
327
327
  };
328
328
  // Normalizes spacing characters and cases in a string.
@@ -1295,8 +1295,6 @@ const doScript = async (report) => {
1295
1295
  report.testTimes = [];
1296
1296
  // Perform the specified acts.
1297
1297
  await doActs(report, 0, null);
1298
- // Close all browsers.
1299
- await closeBrowsers();
1300
1298
  // Add the log statistics to the report.
1301
1299
  report.logCount = logCount;
1302
1300
  report.logSize = logSize;
package/runHost.js CHANGED
@@ -5,18 +5,11 @@
5
5
 
6
6
  // ########## IMPORTS
7
7
 
8
- // Module to keep secrets.
9
- require('dotenv').config();
10
- // Module to read and write files.
11
- const fs = require('fs/promises');
12
8
  const {handleRequest} = require('./run');
13
9
 
14
- // ########## CONSTANTS
15
- const reportDir = process.env.REPORTDIR;
16
-
17
10
  // ########## FUNCTIONS
18
11
 
19
- // Runs one script and writes a report file.
12
+ // Runs one script and sends the report to the parent.
20
13
  const runHost = async (id, scriptJSON, hostJSON) => {
21
14
  const report = {
22
15
  id,
@@ -27,7 +20,7 @@ const runHost = async (id, scriptJSON, hostJSON) => {
27
20
  };
28
21
  await handleRequest(report);
29
22
  const reportJSON = JSON.stringify(report, null, 2);
30
- await fs.writeFile(`${reportDir}/${id}.json`, reportJSON);
23
+ process.send(reportJSON);
31
24
  process.disconnect();
32
25
  process.exit();
33
26
  };
@@ -2,6 +2,7 @@
2
2
  "id": "tp12",
3
3
  "what": "Alfa, Axe, HTML CodeSniffer, IBM, Tenon, WAVE, and 16 custom tests",
4
4
  "strict": true,
5
+ "timeLimit": 300,
5
6
  "commands": [
6
7
  {
7
8
  "type": "launch",
@@ -146,17 +147,17 @@
146
147
  "withItems": true,
147
148
  "what": "IBM Accessibility Checker, with page content and again with URL"
148
149
  },
149
- {
150
- "type": "test",
151
- "which": "tenon",
152
- "id": "a",
153
- "what": "Tenon API version 2 result retrieval"
154
- },
155
150
  {
156
151
  "type": "test",
157
152
  "which": "wave",
158
153
  "reportType": 4,
159
154
  "what": "WAVE, report-type 4"
155
+ },
156
+ {
157
+ "type": "test",
158
+ "which": "tenon",
159
+ "id": "a",
160
+ "what": "Tenon API version 2 result retrieval"
160
161
  }
161
162
  ]
162
163
  }
package/tests/ibm.js CHANGED
@@ -19,23 +19,37 @@
19
19
  */
20
20
  // Import required modules.
21
21
  const fs = require('fs').promises;
22
- const {getCompliance, close} = require('accessibility-checker');
22
+ // Scanner. Importing and executing 'close' crashed the Node process.
23
+ const {getCompliance} = require('accessibility-checker');
23
24
  // Runs the IBM test.
24
- const run = async content => {
25
+ const run = async (content, timeLimit) => {
25
26
  const nowLabel = (new Date()).toISOString().slice(0, 19);
26
- // Return the result of a test.
27
- const ibmReport = await getCompliance(content, nowLabel);
28
- return ibmReport;
27
+ // Start the timeout clock.
28
+ let timeoutID;
29
+ const timeout = new Promise(resolve => {
30
+ timeoutID = setTimeout(() => {
31
+ resolve(null);
32
+ }, 1000 * timeLimit);
33
+ });
34
+ // Return the result of the test, or null if it timed out.
35
+ const ibmReport = getCompliance(content, nowLabel)
36
+ .catch(error => {
37
+ console.log(`ERROR: getCompliance failed (${error.message.replace(/\n+/s, '')}).`);
38
+ return null;
39
+ });
40
+ const result = await Promise.race([ibmReport, timeout]);
41
+ clearTimeout(timeoutID);
42
+ return result;
29
43
  };
30
44
  // Trims an IBM report.
31
- const report = (ibmReport, withItems) => {
45
+ const trimReport = (report, withItems) => {
32
46
  const data = {};
33
- if (ibmReport && ibmReport.report && ibmReport.report.summary) {
34
- const totals = ibmReport.report.summary.counts;
47
+ if (report && report.report && report.report.summary) {
48
+ const totals = report.report.summary.counts;
35
49
  if (totals) {
36
50
  data.totals = totals;
37
51
  if (withItems) {
38
- data.items = ibmReport.report.results;
52
+ data.items = report.report.results;
39
53
  data.items.forEach(item => {
40
54
  delete item.apiArgs;
41
55
  delete item.category;
@@ -58,34 +72,25 @@ const report = (ibmReport, withItems) => {
58
72
  }
59
73
  return data;
60
74
  };
61
- // Performs an IBM test.
75
+ // Performs an IBM test and return the result.
62
76
  const doTest = async (content, withItems, timeLimit) => {
63
- // Start a timeout clock.
64
- let timeoutID;
65
- const wait = new Promise(resolve => {
66
- timeoutID = setTimeout(() => {
67
- resolve({});
68
- }, 1000 * timeLimit);
69
- });
70
- // Conduct the test and get a Promise of the report.
71
- const ibmReport = run(content);
72
- // Wait for completion or until the time limit expires.
73
- const ibmReportIfFast = await Promise.race([ibmReport, wait]);
74
- // Delete existing report files.
75
- try {
76
- const reportNames = await fs.readdir('results');
77
- for (const reportName of reportNames) {
78
- await fs.rm(`results/${reportName}`);
77
+ // Conduct the test and get the result.
78
+ const report = await run(content, timeLimit);
79
+ // If the test did not time out:
80
+ if (report) {
81
+ // Delete any report files.
82
+ try {
83
+ const reportNames = await fs.readdir('results');
84
+ for (const reportName of reportNames) {
85
+ await fs.rm(`results/${reportName}`);
86
+ }
79
87
  }
80
- }
81
- catch(error) {
82
- console.log('ibm test created no result files.');
83
- }
84
- // Return the result.
85
- if (ibmReportIfFast.report) {
86
- clearTimeout(timeoutID);
87
- const ibmTypeReport = report(ibmReportIfFast, withItems);
88
- return ibmTypeReport;
88
+ catch(error) {
89
+ console.log('ibm test created no result files.');
90
+ }
91
+ // Return the result.
92
+ const typeReport = trimReport(report, withItems);
93
+ return typeReport;
89
94
  }
90
95
  else {
91
96
  return {
@@ -117,6 +122,6 @@ exports.reporter = async (page, withItems, withNewContent) => {
117
122
  console.log(`ERROR: Getting ibm test report from URL timed out at ${timeLimit} seconds`);
118
123
  }
119
124
  }
120
- await close();
125
+ // Return the result. Execution of close() crashed the Node process.
121
126
  return {result};
122
127
  };