testaro 9.0.2 → 10.1.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
@@ -591,14 +591,15 @@ The arguments passed to `cycle` by a module or to `watch` by a user are:
591
591
  - whether to watch a directory (`true`) or the network (`false`)
592
592
  - whether to continue watching indefinitely after the first report (`true` or `false`)
593
593
  - how many seconds to wait after finding no job before checking again (a nonnegative number)
594
+ - optionally, where the watched jobs are located
594
595
 
595
596
  ##### Directory watch
596
597
 
597
- With directory watch, Testaro checks whether the `todo` subdirectory of the job directory (`process.env.JOBDIR`) contains a job.
598
+ With directory watch, Testaro checks whether the `todo` subdirectory of the job directory contains a job. The job directory is given by the fourth passed argument, if present, or, if not, then by `process.env.JOBDIR`.
598
599
 
599
- When Testaro finds one or more jobs to do, the `watch` module runs the first job, saves the report in the `raw` subdirectory of the `process.env.REPORTDIR` directory, and moves the job file from the `todo` subdirectory to the `done` subdirectory of the `process.env.JOBDIR` directory.
600
+ When Testaro finds one or more jobs to do, the `watch` module runs the first job, saves the report in the `raw` subdirectory of the report directory. The report directory is given by `process.env.REPORTDIR`. Testaro also moves the job file from the `todo` subdirectory to the `done` subdirectory of the job directory.
600
601
 
601
- Since Testaro runs the first job (i.e. the job whose file name is first in ASCII order), whoever populates the `todo` subdirectory of the `process.env.JOBDIR` directory with job files has control over the order in which Testaro runs them. For example, to force a new job to be run before the already waiting jobs, one can give it a filename that comes before that of the first waiting job.
602
+ Since Testaro runs the first job (i.e. the job whose file name is first in ASCII order), whoever populates the `todo` subdirectory of the job directory with job files has control over the order in which Testaro runs them. For example, to force a new job to be run before the already waiting jobs, one can give it a filename that comes before that of the first waiting job.
602
603
 
603
604
  ##### Network watch
604
605
 
@@ -610,6 +611,10 @@ With network watch, the initiator of an interaction is Testaro, not the server.
610
611
 
611
612
  If multiple workstations run Testaro and do work for the same server, the server can assign jobs to specific agents by requiring each instance of Testaro to have a distinct value of `process.env.AGENT`.
612
613
 
614
+ The URL from which Testaro requests jobs is given by the fourth passed argument, if present, or, if not, then by `process.env.JOB_URL`.
615
+
616
+ 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`.
617
+
613
618
  ### Environment variables
614
619
 
615
620
  In addition to their uses described above, environment variables can be used by commands of type `text`, as documented in the `commands.js` file.
package/actSpecs.js CHANGED
@@ -153,6 +153,12 @@ exports.actSpecs = {
153
153
  ]
154
154
  },
155
155
  tests: {
156
+ alfa: [
157
+ 'Perform an alfa test',
158
+ {
159
+ rules: [false, 'array', 'areStrings', 'rule names (e.g., r25), if not all']
160
+ }
161
+ ],
156
162
  attVal: [
157
163
  'Perform an attVal test',
158
164
  {
package/call.js CHANGED
@@ -57,11 +57,11 @@ const callRun = async jobIDStart => {
57
57
  }
58
58
  };
59
59
  // Starts a watch.
60
- const callWatch = async (isDirWatch, isForever, interval) => {
60
+ const callWatch = async (isDirWatch, isForever, interval, watchee = null) => {
61
61
  const whenType = isForever === 'true' ? 'repeating' : 'one-time';
62
62
  const whereType = isDirWatch === 'true' ? 'directory' : 'network';
63
63
  console.log(`Starting ${whenType} ${whereType} watch`);
64
- await cycle(isDirWatch === 'true', isForever === 'true', Number.parseInt(interval, 10));
64
+ await cycle(isDirWatch === 'true', isForever === 'true', Number.parseInt(interval, 10), watchee);
65
65
  };
66
66
 
67
67
  // ########## OPERATION
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testaro",
3
- "version": "9.0.2",
3
+ "version": "10.1.0",
4
4
  "description": "Automation of accessibility testing",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/tests/alfa.js CHANGED
@@ -7,12 +7,17 @@
7
7
 
8
8
  const {Audit} = require('@siteimprove/alfa-act');
9
9
  const {Scraper} = require('@siteimprove/alfa-scraper');
10
- const alfaRules = require('@siteimprove/alfa-rules');
10
+ let alfaRules = require('@siteimprove/alfa-rules').default;
11
11
 
12
12
  // FUNCTIONS
13
13
 
14
14
  // Conducts and reports an alfa test.
15
- exports.reporter = async page => {
15
+ exports.reporter = async (page, rules) => {
16
+ // If only some rules are to be employed:
17
+ if (rules && rules.length) {
18
+ // Remove the other rules.
19
+ alfaRules = alfaRules.filter(rule => rules.includes(rule.uri.replace(/^.+-/, '')));
20
+ }
16
21
  // Get the document containing the summaries of the alfa rules.
17
22
  const context = page.context();
18
23
  const rulePage = await context.newPage();
@@ -53,7 +58,7 @@ exports.reporter = async page => {
53
58
  };
54
59
  await Scraper.with(async scraper => {
55
60
  for (const input of await scraper.scrape(page.url())) {
56
- const audit = Audit.of(input, alfaRules.default);
61
+ const audit = Audit.of(input, alfaRules);
57
62
  const outcomes = Array.from(await audit.evaluate());
58
63
  outcomes.forEach((outcome, index) => {
59
64
  const {target} = outcome;
package/watch.js CHANGED
@@ -25,13 +25,13 @@ const reportDir = process.env.REPORTDIR;
25
25
  // ########## FUNCTIONS
26
26
 
27
27
  // Checks for a directory job.
28
- const checkDirJob = async () => {
28
+ const checkDirJob = async watchee => {
29
29
  try {
30
- const toDoDirFileNames = await fs.readdir(`${jobDir}/todo`);
30
+ const watchJobDir = watchee || jobDir;
31
+ const toDoDirFileNames = await fs.readdir(`${watchJobDir}/todo`);
31
32
  const jobFileNames = toDoDirFileNames.filter(fileName => fileName.endsWith('.json'));
32
33
  if (jobFileNames.length) {
33
- console.log('Directory job found');
34
- const jobJSON = await fs.readFile(`${jobDir}/todo/${jobFileNames[0]}`, 'utf8');
34
+ const jobJSON = await fs.readFile(`${watchJobDir}/todo/${jobFileNames[0]}`, 'utf8');
35
35
  try {
36
36
  const job = JSON.parse(jobJSON, null, 2);
37
37
  return job;
@@ -44,7 +44,7 @@ const checkDirJob = async () => {
44
44
  }
45
45
  }
46
46
  else {
47
- console.log('Directory job not found');
47
+ console.log('No job to do');
48
48
  return {};
49
49
  }
50
50
  }
@@ -52,10 +52,11 @@ const checkDirJob = async () => {
52
52
  return {};
53
53
  }
54
54
  };
55
- // Checks for a network job.
56
- const checkNetJob = async () => {
55
+ // Checks for and returns a network job.
56
+ const checkNetJob = async watchee => {
57
+ const watchJobURL = watchee || jobURL;
57
58
  const job = await new Promise(resolve => {
58
- const wholeURL = `${protocol}://${jobURL}?agent=${agent}`;
59
+ const wholeURL = `${protocol}://${watchJobURL}?agent=${agent}`;
59
60
  const request = client.request(wholeURL, response => {
60
61
  const chunks = [];
61
62
  response.on('data', chunk => {
@@ -83,7 +84,9 @@ const checkNetJob = async () => {
83
84
  });
84
85
  request.end();
85
86
  });
86
- console.log(`Network job ${job.id || 'not'} received`);
87
+ if (job.id) {
88
+ console.log(`Network job ${job.id} received`);
89
+ }
87
90
  return job;
88
91
  };
89
92
  // Writes a directory report.
@@ -108,48 +111,57 @@ const writeDirReport = async report => {
108
111
  // Submits a network report to a server and returns the server response.
109
112
  const writeNetReport = async report => {
110
113
  const ack = await new Promise(resolve => {
111
- const wholeURL = `${process.env.PROTOCOL}://${reportURL}`;
112
- const request = client.request(wholeURL, {method: 'POST'}, response => {
113
- const chunks = [];
114
- response.on('data', chunk => {
115
- chunks.push(chunk);
116
- });
117
- response.on('end', () => {
118
- const content = chunks.join('');
119
- try {
120
- resolve(JSON.parse(content));
121
- }
122
- catch(error) {
123
- resolve({
124
- error: 'ERROR: Response was not JSON',
125
- message: error.message,
126
- status: response.statusCode,
127
- content: content.slice(0, 200)
128
- });
129
- }
114
+ if (report.sources) {
115
+ // Get the report destination from the report or the environment.
116
+ const destination = report.sources.sendReportTo;
117
+ const wholeURL = destination || `${process.env.PROTOCOL}://${reportURL}`;
118
+ // Contact the server.
119
+ const request = client.request(wholeURL, {method: 'POST'}, response => {
120
+ const chunks = [];
121
+ response.on('data', chunk => {
122
+ chunks.push(chunk);
123
+ });
124
+ response.on('end', () => {
125
+ const content = chunks.join('');
126
+ try {
127
+ resolve(JSON.parse(content));
128
+ }
129
+ catch(error) {
130
+ resolve({
131
+ error: 'ERROR: Response was not JSON',
132
+ message: error.message,
133
+ status: response.statusCode,
134
+ content: content.slice(0, 200)
135
+ });
136
+ }
137
+ });
130
138
  });
131
- });
132
- report.jobData.agent = agent;
133
- request.on('error', error => {
134
- console.log(`ERROR submitting job report (${error.message})`);
135
- resolve({
136
- error: 'ERROR: Job report submission failed',
137
- message: error.message
139
+ report.jobData.agent = agent;
140
+ request.on('error', error => {
141
+ console.log(`ERROR submitting job report (${error.message})`);
142
+ resolve({
143
+ error: 'ERROR: Job report submission failed',
144
+ message: error.message
145
+ });
138
146
  });
139
- });
140
- // Send the report to the server.
141
- request.write(JSON.stringify(report, null, 2));
142
- request.end();
143
- console.log(`Report ${report.id} submitted`);
147
+ // Send the report to the server.
148
+ request.write(JSON.stringify(report, null, 2));
149
+ request.end();
150
+ console.log(`Report ${report.id} submitted`);
151
+ }
152
+ else {
153
+ console.log('ERROR: Report has no sources property');
154
+ }
144
155
  });
145
156
  // Return the server response.
146
157
  return ack;
147
158
  };
148
159
  // Archives a job.
149
- const archiveJob = async job => {
160
+ const archiveJob = async (job, watchee) => {
150
161
  const jobJSON = JSON.stringify(job, null, 2);
151
- await fs.writeFile(`${jobDir}/done/${job.id}.json`, jobJSON);
152
- await fs.rm(`${jobDir}/todo/${job.id}.json`);
162
+ const watchJobDir = watchee || jobDir;
163
+ await fs.writeFile(`${watchJobDir}/done/${job.id}.json`, jobJSON);
164
+ await fs.rm(`${watchJobDir}/todo/${job.id}.json`);
153
165
  };
154
166
  // Waits.
155
167
  const wait = ms => {
@@ -194,7 +206,7 @@ const runJob = async (job, isDirWatch) => {
194
206
  }
195
207
  };
196
208
  // Checks for a job, performs it, and submits a report, once or repeatedly.
197
- exports.cycle = async (isDirWatch, isForever, interval) => {
209
+ exports.cycle = async (isDirWatch, isForever, interval, watchee = null) => {
198
210
  const intervalMS = 1000 * Number.parseInt(interval);
199
211
  let statusOK = true;
200
212
  // Prevent a wait before the first iteration.
@@ -207,21 +219,21 @@ exports.cycle = async (isDirWatch, isForever, interval) => {
207
219
  // Check for a job.
208
220
  let job;
209
221
  if (isDirWatch) {
210
- job = await checkDirJob();
222
+ job = await checkDirJob(watchee);
211
223
  }
212
224
  else {
213
- job = await checkNetJob();
225
+ job = await checkNetJob(watchee);
214
226
  }
215
227
  // If there was one:
216
228
  if (job.id) {
217
- // Run it and save a report.
229
+ // Run it, save a report, and if applicable send the report to the job source.
218
230
  console.log(`Running job ${job.id}`);
219
231
  await runJob(JSON.parse(JSON.stringify(job)), isDirWatch);
220
232
  console.log(`Job ${job.id} finished`);
221
233
  // If a directory was watched:
222
234
  if (isDirWatch) {
223
235
  // Archive the job.
224
- await archiveJob(job);
236
+ await archiveJob(job, watchee);
225
237
  console.log(`Job ${job.id} archived`);
226
238
  }
227
239
  // If watching was specified for only 1 job, quit.
@@ -231,6 +243,7 @@ exports.cycle = async (isDirWatch, isForever, interval) => {
231
243
  }
232
244
  // Otherwise, i.e. if no job was found:
233
245
  else {
246
+ console.log('No job to do');
234
247
  // Cause a wait before the next check.
235
248
  empty = true;
236
249
  }