testaro 12.7.3 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -639,6 +639,14 @@ The URL from which Testaro requests jobs is given by the fourth passed argument,
639
639
 
640
640
  The URL to which Testaro sends reports is given by the `sources.sendReportTo` property of each job, if the property exists, or, if not, by `process.env.REPORT_URL`.
641
641
 
642
+ ##### Job isolation
643
+
644
+ If you execute a repeating watch and the watch process becomes corrupted, the corruption can damage an indefinite subsequent sequence of job performances. Under some conditions, for example, Playwright has been observed to throw errors with a message starting with “Navigation timeout of 30000 ms exceeded” and ending with the entire HTML content of the web page, even when page navigation has been subjected to shorter time limits. Thereafter, Playwright issues that error message again about every 15 seconds, even if the browser has been closed.
645
+
646
+ To counteract such corruption, you can perform repeating directory watches with job isolation. To do this, use the statement `node dirWatch n`, where `n` is the number of seconds Testaro should wait after finding no job before looking again. The `dirWatch` module spawns a new process for each job, and also shrinks Playwright error messages before they are logged on the console.
647
+
648
+ You can stop a repeating directory watch of this kind by entering `CTRL-c`.
649
+
642
650
  ### Environment variables
643
651
 
644
652
  In addition to their uses described above, environment variables can be used by acts of type `text`, as documented in the `actSpecs.js` file.
package/dirWatch.js ADDED
@@ -0,0 +1,47 @@
1
+ /*
2
+ dirWatch.js
3
+ Module for launching a one-time directory watch.
4
+ */
5
+
6
+ // ########## IMPORTS
7
+
8
+ // Module to spawn a child process.
9
+ const {spawn} = require('node:child_process');
10
+
11
+ // ########## CONSTANTS
12
+
13
+ const interval = process.argv[2];
14
+
15
+ // ########## FUNCTIONS
16
+
17
+ // Spawns a one-time directory watch.
18
+ const spawnWatch = (command, args) => spawn(command, args, {stdio: ['inherit', 'inherit', 'pipe']});
19
+ // Repeatedly spawns a one-time directory watch.
20
+ const reWatch = () => {
21
+ const watcher = spawnWatch('node', ['call', 'watch', 'true', 'false', interval]);
22
+ let error = '';
23
+ watcher.stderr.on('data', data => {
24
+ error += data.toString();
25
+ });
26
+ watcher.on('close', async code => {
27
+ if (error) {
28
+ if (error.startsWith('Navigation timeout of 30000 ms exceeded')) {
29
+ console.log('ERROR: Playwright claims 30-second timeout exceeded');
30
+ }
31
+ else {
32
+ console.log(`ERROR: ${error.slice(0, 200)}`);
33
+ }
34
+ }
35
+ if (code === 0) {
36
+ console.log('Watcher exited successfully');
37
+ }
38
+ else {
39
+ console.log(`Watcher exited with error code ${code}`);
40
+ }
41
+ reWatch();
42
+ });
43
+ };
44
+
45
+ // ########## OPERATION
46
+
47
+ reWatch();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "12.7.3",
3
+ "version": "13.0.0",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/run.js CHANGED
@@ -302,11 +302,15 @@ const nowString = () => (new Date()).toISOString().slice(0, 19);
302
302
  // Closes the current browser.
303
303
  const browserClose = async () => {
304
304
  if (browser) {
305
- const contexts = browser.contexts();
305
+ const browserType = browser.browserType().name();
306
+ let contexts = browser.contexts();
306
307
  for (const context of contexts) {
307
308
  await context.close();
309
+ contexts = browser.contexts();
308
310
  }
309
311
  await browser.close();
312
+ browser = null;
313
+ console.log(`${browserType} browser closed`);
310
314
  }
311
315
  };
312
316
  // Returns the first line of an error message.
@@ -319,7 +323,12 @@ const launch = async (report, typeName, lowMotion = false) => {
319
323
  // Close the current browser, if any.
320
324
  await browserClose();
321
325
  // Launch a browser of that type.
322
- const browserOptions = {};
326
+ const browserOptions = {
327
+ logger: {
328
+ isEnabled: (name, severity) => false,
329
+ log: (name, severity, message, args) => console.log(message.slice(0, 100))
330
+ }
331
+ };
323
332
  if (debug) {
324
333
  browserOptions.headless = false;
325
334
  }
@@ -375,7 +384,10 @@ const launch = async (report, typeName, lowMotion = false) => {
375
384
  report.jobData.errorLogSize += msgLength;
376
385
  }
377
386
  const msgLC = msgText.toLowerCase();
378
- if (msgText.includes('403') && (msgLC.includes('status') || msgLC.includes('prohibited'))) {
387
+ if (
388
+ msgText.includes('403') && (msgLC.includes('status')
389
+ || msgLC.includes('prohibited'))
390
+ ) {
379
391
  report.jobData.prohibitedCount++;
380
392
  }
381
393
  });
@@ -694,41 +706,31 @@ const doActs = async (report, actIndex, page) => {
694
706
  // Identify the URL.
695
707
  const resolved = act.which.replace('__dirname', __dirname);
696
708
  requestedURL = resolved;
697
- // Visit it and wait until the network is idle.
698
709
  const {strict} = report;
699
- let response = await goTo(report, page, requestedURL, 15000, 'networkidle', strict);
710
+ // Visit it and wait until the DOM is loaded.
711
+ response = await goTo(report, page, requestedURL, 15000, 'domcontentloaded', strict);
700
712
  // If the visit fails:
701
713
  if (response.error) {
702
- // Try again until the DOM is loaded.
714
+ // Launch another browser type.
715
+ const newBrowserName = Object.keys(browserTypeNames)
716
+ .find(name => name !== browserTypeName);
717
+ console.log(`>> Launching ${newBrowserName} instead`);
718
+ await launch(newBrowserName);
719
+ // Identify its only page as current.
720
+ page = browserContext.pages()[0];
721
+ // Visit the URL and wait until the DOM is loaded.
703
722
  response = await goTo(report, page, requestedURL, 10000, 'domcontentloaded', strict);
704
723
  // If the visit fails:
705
724
  if (response.error) {
706
- // Launch another browser type.
707
- const newBrowserName = Object.keys(browserTypeNames)
708
- .find(name => name !== browserTypeName);
709
- console.log(`>> Launching ${newBrowserName} instead`);
710
- await launch(newBrowserName);
711
- // Identify its only page as current.
712
- page = browserContext.pages()[0];
713
- // Try again until the network is idle.
714
- response = await goTo(report, page, requestedURL, 10000, 'networkidle', strict);
725
+ // Try again and wait until a load.
726
+ response = await goTo(report, page, requestedURL, 5000, 'load', strict);
715
727
  // If the visit fails:
716
728
  if (response.error) {
717
- // Try again until the DOM is loaded.
718
- response = await goTo(report, page, requestedURL, 5000, 'domcontentloaded', strict);
719
- // If the visit fails:
720
- if (response.error) {
721
- // Try again or until a load.
722
- response = await goTo(report, page, requestedURL, 5000, 'load', strict);
723
- // If the visit fails:
724
- if (response.error) {
725
- // Navigate to a blank page instead.
726
- await page.goto('about:blank')
727
- .catch(error => {
728
- console.log(`ERROR: Navigation to blank page failed (${error.message})`);
729
- });
730
- }
731
- }
729
+ // Navigate to a blank page instead.
730
+ await page.goto('about:blank')
731
+ .catch(error => {
732
+ console.log(`ERROR: Navigation to blank page failed (${error.message})`);
733
+ });
732
734
  }
733
735
  }
734
736
  }
@@ -1505,7 +1507,6 @@ const doActs = async (report, actIndex, page) => {
1505
1507
  else if (! report.jobData.abortTime) {
1506
1508
  console.log('Acts completed');
1507
1509
  await browserClose();
1508
- console.log('Browser closed');
1509
1510
  }
1510
1511
  };
1511
1512
  /*
package/tests/dupAtt.js CHANGED
@@ -50,14 +50,12 @@ exports.reporter = async (page, withItems) => {
50
50
  rawPage = rawPage.replace(/<(script [^<>]+)>.*?<\/script>/g, '$1');
51
51
  // Remove any comments from it.
52
52
  rawPage = rawPage.replace(/<!--.*?-->/g, '');
53
- console.log(rawPage);
54
53
  // Extract the opening tags of its elements.
55
54
  let elements = rawPage.match(/<[a-zA-Z][^<>]+>/g);
56
55
  // Delete their enclosing angle brackets and the values of any attributes in them.
57
56
  elements = elements.map(el => el.replace(/^< *|=[^ ]*| *>$/g, ''));
58
57
  // For each element:
59
58
  elements.forEach(element => {
60
- console.log(element);
61
59
  // Identify its tag name and attributes.
62
60
  const terms = element.split(' ');
63
61
  // If it has 2 or more attributes:
package/tests/ibm.js CHANGED
@@ -26,7 +26,7 @@ const run = async (content, timeLimit) => {
26
26
  });
27
27
  // Return the result of the test, or null if it timed out.
28
28
  try {
29
- const ibmReport = await getCompliance(content, nowLabel);
29
+ const ibmReport = getCompliance(content, nowLabel);
30
30
  const result = await Promise.race([ibmReport, timeout]);
31
31
  clearTimeout(timeoutID);
32
32
  return result;
package/watch.js CHANGED
@@ -214,14 +214,13 @@ const runJob = async (job, isDirWatch) => {
214
214
  }
215
215
  };
216
216
  // Checks for a job, performs it, and submits a report, once or repeatedly.
217
- exports.cycle = async (isDirWatch, isForever, interval, watchee = null) => {
218
- const intervalMS = 1000 * Number.parseInt(interval);
217
+ exports.cycle = async (isDirWatch, isForever, interval = 300, watchee = null) => {
219
218
  let statusOK = true;
220
219
  // Prevent a wait before the first iteration.
221
220
  let empty = false;
222
- console.log(
223
- `Watching started with intervals of ${interval} seconds when idle (${nowString()})`
224
- );
221
+ const intervalMS = 1000 * Number.parseInt(interval);
222
+ const intervalSpec = isForever ? `with intervals of ${interval} seconds when idle ` : '';
223
+ console.log(`Watching started ${intervalSpec}(${nowString()})`);
225
224
  while (statusOK) {
226
225
  if (empty) {
227
226
  await wait(intervalMS);