testilo 6.1.7 → 7.0.1

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
@@ -9,10 +9,20 @@ Testaro performs digital accessibility tests on web artifacts and creates report
9
9
 
10
10
  Because Testilo supports Testaro, this `README` file presumes that you have access to the Testaro `README` file and therefore does not repeat information provided there.
11
11
 
12
+ ## Branches
13
+
14
+ The `writer` branch contains the state of Testilo as of 2022-11-07. In that branch, Testilo must read and write files when it aims, scores, or digests. That makes Testilo unusable as a dependency in applications that do not have permission to both read and write files.
15
+
16
+ The `main` branch contains the state of Testilo starting on 2022-11-07. In that branch, Testilo does not need to read or write files when it aims, scores, or digests, so applications without write permission can still use Testilo as a dependency if they need only those functionalities.
17
+
18
+ This `README.md` file documents the `main` branch.
19
+
12
20
  ## Dependencies
13
21
 
14
22
  The `dotenv` dependency lets you set environment variables in an untracked `.env` file. This prevents secret data, such as passwords, from being shared as part of this package.
15
23
 
24
+ When Testilo is a dependency of another application, the `.env` file is not imported, because it is not tracked, so all needed environment variables must be provided by the importing application.
25
+
16
26
  ## Architecture
17
27
 
18
28
  Testilo is written in Node.js. Commands are given to Testilo in a command-line (terminal) interface or programmatically.
@@ -37,15 +47,23 @@ The `aim` function allows you to _aim_ a script at a host. That means modifying
37
47
  Execution by a module:
38
48
 
39
49
  ```javaScript
40
- const host = {
41
- which: https://w3c.org/,
42
- what: 'World Wide Web Consortium',
43
- id: w3c
44
- };
45
- const fs = require('fs/promises');
46
- const {aim} = require('testilo/aim');
47
- const aimedScript = aim('tp25', host, 'developer@w3c.org');
48
- fs.writeFile(process.env.JOBDIR, JSON.stringify(aimedScript, null, 2));
50
+ const aimScript = async () => {
51
+ const fs = require('fs/promises');
52
+ const host = {
53
+ which: https://w3c.org/,
54
+ what: 'World Wide Web Consortium',
55
+ id: w3c
56
+ };
57
+ const {aim} = require('testilo/aim');
58
+ const scriptJSON = await fs.readFile(`${process.env.SCRIPTDIR}/tp25.json`, 'utf8');
59
+ const script = JSON.parse(scriptJSON);
60
+ const aimedScript = aim(script, host, 'developer@w3c.org');
61
+ await fs.writeFile(
62
+ `${process.env.JOBDIR}/${aimedScript.id}.json`, JSON.stringify(aimedScript, null, 2)
63
+ );
64
+ console.log(`Script ${aimedScript.source.script} aimed at ${aimedScript.host.what}`);
65
+ }
66
+ aimScript();
49
67
  ```
50
68
 
51
69
  Execution by a user:
@@ -56,6 +74,8 @@ node call aim tp25 https://w3.org/ 'World Wide Web Consortium' w3c developer@w3c
56
74
 
57
75
  In these examples, a copy of the script file named `tp25.json` in the `SCRIPTDIR` directory is aimed at the World Wide Web Consortium and then saved in the `JOBDIR` directory.
58
76
 
77
+ The `aim` function neither reads nor writes files. Its arguments are a script object, a host object, and a requester-email-address string, and it returns the script object, aimed at the host.
78
+
59
79
  ### `merge`
60
80
 
61
81
  The `merge` function is similar to the `aim` function, but it aims a script at multiple hosts, not only one. The hosts are identified in a _batch_, a file in the `BATCHDIR` directory. The output of `merge` is multiple script files, one per host, saved in the `JOBDIR` directory.
@@ -99,17 +119,56 @@ Execution by a user:
99
119
  node call merge tp25 weborgs
100
120
  ```
101
121
 
122
+ In these examples, a copy of the script file named `tp25.json` in the `SCRIPTDIR` directory is aimed at all the hosts in the batch file named `weborgs.json` in the `BATCHDIR` directory, and the resulting host-specific scripts are saved in the `JOBDIR` directory.
123
+
102
124
  ### `score`
103
125
 
104
126
  Testaro performs tests and produces reports of test results. Testilo can add scores to those reports. In this way, each report can not only detail successes and failures of individual tests but also assign scores to those results and combine the partial scores into total scores.
105
127
 
106
- The `score` function performs scoring. It depends on _score procs_ to define the scoring rules. Some score procs are in the Testilo package (in the `procs/score` directory), and you can create more score procs to implement different rules.
128
+ The `score` function performs scoring. It depends on a _score proc_ to define the scoring rules. Some score procs are in the Testilo package (in the `procs/score` directory), and you can create more score procs to implement different rules.
107
129
 
108
130
  Execution by a module:
109
131
 
110
132
  ```javaScript
111
- const {score} = require('testilo/score');
112
- score('sp25a', '756mr')
133
+ const scoreReport = async (scoreProcID, rawReportID) => {
134
+ const fs = require('fs/promises');
135
+ const {score} = require('testilo/score');
136
+ const {scorer} = require(`${process.env.SCOREPROCDIR}/${scoreProcID}`);
137
+ const rawReportJSON = await fs.readFile(
138
+ `${process.env.REPORTDIR_RAW}/${rawReportID}.json`, 'utf8'
139
+ );
140
+ const rawReport = JSON.parse(rawReportJSON);
141
+ const scoredReport = score(scorer, rawReport);
142
+ await fs.writeFile(
143
+ `${process.env.REPORTDIR_SCORED}/${scoredReport.id}.json`, JSON.stringify(scoredReport, null, 2)
144
+ );
145
+ console.log(`Report ${scoredReport.id} scored`);
146
+ };
147
+ scoreReport('sp25a', '756mr-tp25-w3c');
148
+ ```
149
+
150
+ Execution by a user:
151
+
152
+ ```bash
153
+ node call score sp25a
154
+ node call score sp25a 756mr
155
+ ```
156
+
157
+ In these examples, the `score` function applies a score proc named `sp25a`, of which a copy is in the file `sp25a.json` in the `SCOREPROCDIR` directory, to a raw report `756mr-tp25-w3c.json` in the `REPORTDIR_RAW` directory and returns the same report with score data added. The scored report is saved in the `REPORTDIR_SCORED` directory.
158
+
159
+ The user statement can pass only 2 arguments to `call` if the first report in the `REPORTDIR_RAW` directory is the desired raw report. If there are multiple reports in that directory and the desired one is not the first, the user must pass 3 arguments to `call`, and the third argument must be a string, such that the first report in that directory that begins with that string is the desired report.
160
+
161
+ The `score` function neither reads nor writes files. Its arguments are a score-proc string and a raw-report object, and it returns a scored-report object.
162
+
163
+ ### `multiScore`
164
+
165
+ The `multiScore` function scores all the reports in the `REPORTDIR_RAW` directory and writes the scored reports in the `REPORTDIR_SCORED` directory.
166
+
167
+ Execution by a module:
168
+
169
+ ```javaScript
170
+ const {multiScore} = require('testilo/multiScore');
171
+ multiScore('sp25a')
113
172
  .then(hostCount => {
114
173
  console.log(`Scoring complete. Count of reports scored: ${hostCount}`);
115
174
  });
@@ -118,40 +177,75 @@ score('sp25a', '756mr')
118
177
  Execution by a user:
119
178
 
120
179
  ```bash
121
- node call score sp25a 756mr
180
+ node call multiScore sp25a
122
181
  ```
123
182
 
124
- The first argument to `score` names the score proc to be used. The second argument is optional. If it is present, then only reports whose names begin with the value of that argument will be scored. Otherwise all reports in the `REPORTDIR_RAW` directory will be scored. The scored reports will be written in the `REPORTDIR_SCORED` directory.
183
+ The argument to `multiScore` names the score proc to be used. The `multiScore` function scores all the reports in the `REPORTDIR_RAW` directory and writes the scored reports in the `REPORTDIR_SCORED` directory.
125
184
 
126
185
  ### `digest`
127
186
 
128
- Testaro reports, both originally and after they are scored, are JSON files. That format is human-readable, but it is basically designed for machine tractability.
187
+ Testaro reports, both originally and after they are scored, are JavaScript objects. When represented as JSON, they are human-readable, but basically designed for machine tractability.
188
+
189
+ The `digest` function converts scored reports into HTML documents with explanatory content. Thus, it converts machine-oriented reports into human-oriented reports, called _digests_. It depends on a _digest proc_ to define the digesting rules. Some digest procs are in the Testilo package (in the `procs/digest` directory), and you can create more digest procs to implement different rules.
190
+
191
+ Execution by a module:
192
+
193
+ ```javaScript
194
+ const digestReport = async (digestProcID, scoredReportID) => {
195
+ const fs = require('fs/promises');
196
+ const {digest} = require('testilo/digest');
197
+ const {makeQuery} = require(`${process.env.DIGESTPROCDIR}/${digestProcID}/index`);
198
+ const digestTemplate = await fs.readFile(
199
+ `${process.env.DIGESTPROCDIR}/${digestProcID}/index.html`
200
+ );
201
+ const digestedReport = digest(digestTemplate, makeQuery, scoredReport);
202
+ const digestedReport = digest(makeQuery, scoredReport);
203
+ await fs.writeFile(`${process.env.REPORTDIR_DIGESTED}/${digestedReport.id}.html`, digestedReport);
204
+ console.log(`Report ${digestedReport.id} digested`);
205
+ };
206
+ digestReport('dp25a', '756mr-tp25-w3c');
207
+ ```
208
+
209
+ Execution by a user:
210
+
211
+ ```bash
212
+ node call digest sp25a
213
+ node call digest sp25a 756mr
214
+ ```
129
215
 
130
- The `digest` function converts scored reports into HTML documents with explanatory content. Thus, it converts machine-oriented reports into human-oriented reports, called _digests_. It depends on _digest procs_ to define the digesting rules. Some digest procs are in the Testilo package (in the `procs/digest` directory), and you can create more digest procs to implement different rules.
216
+ In these examples, the `digest` function applies a digest proc named `dp25a`, of which a copy is in the file `dp25a.json` in the `DIGESTPROCDIR` directory, to a scored report `756mr-tp25-w3c.json` in the `REPORTDIR_SCORED` directory and returns an HTML digest for that same report. The digest is saved in the `REPORTDIR_DIGESTED` directory.
217
+
218
+ The user statement can pass only 2 arguments to `call` if the first report in the `REPORTDIR_SCORED` directory is the desired scored report. If there are multiple reports in that directory and the desired one is not the first, the user must pass 3 arguments to `call`, and the third argument must be a string, such that the first report in that directory that begins with that string is the desired report.
219
+
220
+ The `digest` function neither reads nor writes files. Its arguments are a digest-proc string and a scored-report object, and it returns a digest HTML string.
221
+
222
+ ### `multiDigest`
223
+
224
+ The `multiDigest` function digests all the reports in the `REPORTDIR_SCORED` directory and writes the digests in the `REPORTDIR_DIGESTED` directory.
131
225
 
132
226
  Execution by a module:
133
227
 
134
228
  ```javaScript
135
- const {digest} = require('testilo/digest');
136
- digest('dp25a', '756mr')
229
+ const {multiDigest} = require('testilo/multiDigest');
230
+ multiDigest('dp25a')
137
231
  .then(hostCount => {
138
- console.log(`Digesting complete. Count of reports digested: ${hostCount}.`);
232
+ console.log(`Digesting complete. Count of reports digested: ${hostCount}`);
139
233
  });
140
234
  ```
141
235
 
142
236
  Execution by a user:
143
237
 
144
238
  ```bash
145
- node call digest sp25a 756mr
239
+ node call multiDigest dp25a
146
240
  ```
147
241
 
148
- The first argument to `digest` names the digest proc to be used. The second argument is optional. If it is present, then only reports whose names begin with the value of that argument will be digested. Otherwise all reports in the `REPORTDIR_SCORED` directory will be digested. The digested reports will be written in the `REPORTDIR_DIGESTED` directory.
242
+ The argument to `multiDigest` or the second argument to `call` names the digest proc to be used. The `multiDigest` function digests all the reports in the `REPORTDIR_SCORED` directory and writes the digests in the `REPORTDIR_DIGESTED` directory.
149
243
 
150
244
  The digests created by `digest` are HTML files, and they expect a `style.css` file to exist in their directory. The `reports/digested/style.css` file in Testilo is an appropriate stylesheet to be copied into the `REPORTDIR_DIGESTED` directory.
151
245
 
152
246
  ### `compare`
153
247
 
154
- You can summarize the results of a multi-host job by producing a document comparing the scores received by the hosts. The `compare` function does this. It gathers scores from a set of scored reports and produces an HTML document comparing the scores. It depends on _comparison procs_ to define the rules for making and showing the comparative scores. Some compare procs are in the Testilo package (in the `procs/compare` directory), and you can create more compare procs to implement different rules.
248
+ You can summarize multiple scored reports by producing a document comparing the scores. The `compare` function does this. It gathers scores from a set of scored reports and produces an HTML document comparing the scores. It depends on a _comparison proc_ to define the rules for making and showing the comparative scores. Some compare procs are in the Testilo package (in the `procs/compare` directory), and you can create more compare procs to implement different rules.
155
249
 
156
250
  Execution by a module:
157
251
 
@@ -169,6 +263,6 @@ Execution by a user:
169
263
  node call compare cp25a legislators
170
264
  ```
171
265
 
172
- The first argument to `compare` names the comparison proc to be used. The second argument specifies a base of the name of the comparative report that will be produced. The scores in all reports in the `REPORTDIR_SCORED` directory will be compared. The comparative report will be written in the `COMPARISONDIR` directory.
266
+ The first argument to `compare`, or the second argument to `call`, names the comparison proc to be used. The subsequent argument specifies a base of the name of the comparative report that will be produced. The scores in all reports in the `REPORTDIR_SCORED` directory will be compared. The comparative report will be written in the `COMPARISONDIR` directory.
173
267
 
174
- The digests created by `compare` are HTML files, and they expect a `style.css` file to exist in their directory. The `reports/comparative/style.css` file in Testilo is an appropriate stylesheet to be copied into the `COMPARISONDIR` directory.
268
+ The comparison created by `compare` is an HTML file, and it expects a `style.css` file to exist in its directory. The `reports/comparative/style.css` file in Testilo is an appropriate stylesheet to be copied into the `COMPARISONDIR` directory.
package/aim.js CHANGED
@@ -3,25 +3,11 @@
3
3
  Module for aiming a script at a host.
4
4
  */
5
5
 
6
- // ########## IMPORTS
7
-
8
- // Module to keep secrets.
9
- require('dotenv').config();
10
- // Module to read and write files.
11
- const fs = require('fs/promises');
12
-
13
- // ########## CONSTANTS
14
-
15
- const scriptDir = process.env.SCRIPTDIR || 'scripts';
16
-
17
6
  // ########## FUNCTIONS
18
7
 
19
8
  // Returns a script, aimed at a host.
20
- exports.aim = async (scriptName, host, requester) => {
21
- // Copy the script.
22
- const scriptFile = await fs.readFile(`${scriptDir}/${scriptName}.json`, 'utf8');
23
- const script = JSON.parse(scriptFile);
24
- // In the copy, make all url commands visit the host.
9
+ exports.aim = (script, host, requester) => {
10
+ // Make all url commands in the script visit the host.
25
11
  script.commands.forEach(command => {
26
12
  if (command.type === 'url') {
27
13
  command.id = host.id;
@@ -37,8 +23,8 @@ exports.aim = async (scriptName, host, requester) => {
37
23
  }
38
24
  // Create a job-creation time stamp.
39
25
  const timeStamp = Math.floor((Date.now() - Date.UTC(2022, 1)) / 2000).toString(36);
40
- // Change the script id property to include the time stamp and the host ID.
41
- script.id = `${timeStamp}-${script.id}-${host.id}`;
26
+ // Add a job-ID property to the script, including the time stamp, the script ID, and the host ID.
27
+ script.jobID = `${timeStamp}-${script.id}-${host.id}`;
42
28
  // Return the host-specific script.
43
29
  return script;
44
30
  };
package/call.js CHANGED
@@ -6,13 +6,15 @@
6
6
  0. function to execute.
7
7
  1+. arguments to pass to the function.
8
8
  Usage examples:
9
- node call aim script454 https://www.w3c.org/ 'World Wide Web Consortium' w3c
9
+ node call aim tp25 https://www.w3c.org/ 'World Wide Web Consortium' w3c developer@w3.org
10
10
  node call merge script454 webOrgs
11
- node call score sp25a (to score all reports in REPORTDIR_RAW)
12
- node call score sp25a 8ep9f- (same, but only if names start with 8ep9f-)
13
- node call digest dp25a (to digest all reports in REPORTDIR_SCORED)
14
- node call digest dp25a 8ep9f- (same, but only if names start with 8ep9f-)
15
- node call compare cp25a weborgs (to write weborgs.html, comparing all reports in REPORTDIR_SCORED)
11
+ node call score sp25a (to score the first raw report)
12
+ node call score sp25a 8ep9f (to score the first raw report whose name starts with 8ep9f)
13
+ node call multiScore sp25a
14
+ node call digest dp25a (to digest the first scored report)
15
+ node call digest dp25a 8ep9f (to digest the first scored report whose name starts with 8ep9f)
16
+ node call multiDigest dp25a
17
+ node call compare cp25a weborgs (to write weborgs.html, comparing all scored reports)
16
18
  */
17
19
 
18
20
  // ########## IMPORTS
@@ -24,16 +26,24 @@ const fs = require('fs/promises');
24
26
  const {aim} = require('./aim');
25
27
  // Function to process a script-batch merger.
26
28
  const {merge} = require('./merge');
27
- // Function to score reports.
29
+ // Function to score a report.
28
30
  const {score} = require('./score');
29
- // Function to digest reports.
31
+ // Function to score multiple reports.
32
+ const {multiScore} = require('./multiScore');
33
+ // Function to digest a report.
30
34
  const {digest} = require('./digest');
35
+ // Function to digest multiple reports.
36
+ const {multiDigest} = require('./multiDigest');
31
37
  // Function to compare scores.
32
38
  const {compare} = require('./compare');
33
39
 
34
40
  // ########## CONSTANTS
35
41
 
42
+ const scriptDir = process.env.SCRIPTDIR;
43
+ const scoreProcDir = process.env.SCOREPROCDIR;
44
+ const digestProcDir = process.env.DIGESTPROCDIR;
36
45
  const jobDir = process.env.JOBDIR;
46
+ const rawDir = process.env.REPORTDIR_RAW;
37
47
  const scoredDir = process.env.REPORTDIR_SCORED;
38
48
  const digestedDir = process.env.REPORTDIR_DIGESTED;
39
49
  const comparisonDir = process.env.COMPARISONDIR;
@@ -43,15 +53,17 @@ const fnArgs = process.argv.slice(3);
43
53
  // ########## FUNCTIONS
44
54
 
45
55
  // Fulfills an aiming request.
46
- const callAim = async (scriptName, hostURL, hostName, hostID, notifyee) => {
47
- const aimedScript = await aim(
48
- scriptName,
56
+ const callAim = async (scriptName, hostURL, hostName, hostID, requester) => {
57
+ const scriptJSON = await fs.readFile(`${scriptDir}/${scriptName}.json`, 'utf8');
58
+ const script = JSON.parse(scriptJSON);
59
+ const aimedScript = aim(
60
+ script,
49
61
  {
50
62
  id: hostID,
51
63
  which: hostURL,
52
64
  what: hostName
53
65
  },
54
- notifyee
66
+ requester
55
67
  );
56
68
  const aimedScriptID = aimedScript.id;
57
69
  await fs.writeFile(`${jobDir}/${aimedScriptID}.json`, JSON.stringify(aimedScript, null, 2));
@@ -64,16 +76,148 @@ const callMerge = async (scriptName, batchName) => {
64
76
  };
65
77
  // Fulfills a scoring request.
66
78
  const callScore = async (scoreProcID, reportIDStart) => {
67
- const reportCount = await score(scoreProcID, reportIDStart);
68
- console.log(
69
- `Scoring completed. Score proc: ${scoreProcID}. Report count: ${reportCount}. Directory: ${scoredDir}`
70
- );
79
+ // Identify the raw reports.
80
+ const rawFileNames = await fs.readdir(rawDir);
81
+ const rawReportNames = rawFileNames.filter(fileName => fileName.endsWith('.json'));
82
+ // If any exist:
83
+ if (rawReportNames.length) {
84
+ // Identify the one to be scored.
85
+ let rawReportName = '';
86
+ if (reportIDStart) {
87
+ rawReportName = rawReportNames.find(reportName => reportName.startsWith(reportIDStart));
88
+ }
89
+ else {
90
+ rawReportName = rawReportNames[0];
91
+ }
92
+ // If it exists:
93
+ if (rawReportName) {
94
+ // Score it.
95
+ const rawReportJSON = await fs.readFile(`${rawDir}/${rawReportName}.json`);
96
+ const rawReport = JSON.parse(rawReportJSON);
97
+ const {scorer} = require(`${scoreProcDir}/${scoreProcID}.js`);
98
+ const scoredReport = score(scorer, rawReport);
99
+ // Save it, scored.
100
+ await fs.writeFile(
101
+ `${scoredDir}/${scoredReport.id}.json`, JSON.stringify(scoredReport, null, 2)
102
+ );
103
+ console.log(`Report ${rawReport.id} scored and saved in ${scoredDir}`);
104
+ }
105
+ // Otherwise, i.e. if it does not exist:
106
+ else {
107
+ // Report this.
108
+ console.log('ERROR: Specified raw report not found');
109
+ }
110
+ }
111
+ // Otherwise, i.e. if no raw report found:
112
+ else {
113
+ // Report this.
114
+ console.log('ERROR: No raw report found');
115
+ }
116
+ };
117
+ // Fulfills a multiple-report scoring request.
118
+ const callMultiScore = async scoreProcID => {
119
+ // Identify all the raw reports.
120
+ const rawFileNames = await fs.readdir(rawDir);
121
+ const rawReportNames = rawFileNames.filter(fileName => fileName.endsWith('.json'));
122
+ // If any exist:
123
+ if (rawReportNames.length) {
124
+ // For each of them:
125
+ const {scorer} = require(`${scoreProcDir}/${scoreProcID}.js`);
126
+ for (const rawReportName of rawReportNames) {
127
+ // Score it.
128
+ const rawReportJSON = await fs.readFile(`${rawDir}/${rawReportName}.json`);
129
+ const rawReport = JSON.parse(rawReportJSON);
130
+ const scoredReport = score(scorer, rawReport);
131
+ // Save it, scored.
132
+ await fs.writeFile(
133
+ `${scoredDir}/${scoredReport.id}.json`, JSON.stringify(scoredReport, null, 2)
134
+ );
135
+ console.log(`Report ${rawReport.id} scored and saved in ${scoredDir}`);
136
+ }
137
+ }
138
+ // Otherwise, i.e. if no raw report exists:
139
+ else {
140
+ // Report this.
141
+ console.log('ERROR: No raw report found');
142
+ }
71
143
  };
144
+ // Prepares to fulfill a digesting request.
145
+ const digestPrep = async digestProcID => {
146
+ const {digest} = require('testilo/digest');
147
+ const {makeQuery} = require(`${digestProcDir}/${digestProcID}/index`);
148
+ const digestTemplate = await fs.readFile(`${digestProcDir}/${digestProcID}/index.html`, 'utf8');
149
+ // Identify the scored reports.
150
+ const scoredFileNames = await fs.readdir(scoredDir);
151
+ const scoredReportNames = scoredFileNames.filter(fileName => fileName.endsWith('.json'));
152
+ // Return the data required for the fulfillment of the request.
153
+ return {
154
+ anyScoredReports: scoredReportNames.length > 0,
155
+ digest,
156
+ makeQuery,
157
+ digestTemplate,
158
+ scoredReportNames
159
+ };
160
+ }
72
161
  // Fulfills a digesting request.
73
162
  const callDigest = async (digestProcID, reportIDStart) => {
74
- const reportCount = await digest(digestProcID, reportIDStart);
163
+ const prepData = await digestPrep(digestProcID);
164
+ // If any scored reports exist:
165
+ if (prepData.anyScoredReports) {
166
+ // Identify the one to be digested.
167
+ let scoredReportName = '';
168
+ if (reportIDStart) {
169
+ scoredReportName = prepData.scoredReportNames.find(
170
+ reportName => reportName.startsWith(reportIDStart)
171
+ );
172
+ }
173
+ else {
174
+ scoredReportName = prepData.scoredReportNames[0];
175
+ }
176
+ // If it exists:
177
+ if (scoredReportName) {
178
+ // Digest it.
179
+ const scoredReportJSON = await fs.readFile(`${scoredDir}/${scoredReportName}.json`, 'utf8');
180
+ const scoredReport = JSON.parse(scoredReportJSON);
181
+ const digestedReport = digest(prepData.digestTemplate, makeQuery, scoredReport);
182
+ // Save it, digested.
183
+ await fs.writeFile(`${digestedDir}/${digestedReport.id}.html`, digestedReport);
184
+ console.log(`Report ${scoredReport.id} digested and saved in ${digestedDir}`);
185
+ }
186
+ // Otherwise, i.e. if it does not exist:
187
+ else {
188
+ // Report this.
189
+ console.log('ERROR: Specified scored report not found');
190
+ }
191
+ }
192
+ // Otherwise, i.e. if no scored report exists:
193
+ else {
194
+ // Report this.
195
+ console.log('ERROR: No scored report found');
196
+ }
197
+ };
198
+ // Fulfills a multiple-report digesting request.
199
+ const callMultiDigest = async digestProcID => {
200
+ const prepData = await digestPrep(digestProcID);
201
+ // If any scored reports exist:
202
+ if (prepData.anyScoredReports) {
203
+ // For each of them:
204
+ for (const scoredReportName of prepData.scoredReportNames) {
205
+ // Digest it.
206
+ const scoredReportJSON = await fs.readFile(`${scoredDir}/${scoredReportName}.json`, 'utf8');
207
+ const scoredReport = JSON.parse(scoredReportJSON);
208
+ const digestedReport = digest(prepData.digestTemplate, makeQuery, scoredReport);
209
+ // Save it, digested.
210
+ await fs.writeFile(`${digestedDir}/${digestedReport.id}.html`, digestedReport);
211
+ console.log(`Report ${scoredReport.id} digested and saved in ${digestedDir}`);
212
+ }
213
+ }
214
+ // Otherwise, i.e. if no raw report exists:
215
+ else {
216
+ // Report this.
217
+ console.log('ERROR: No raw report found');
218
+ }
75
219
  console.log(
76
- `Digesting completed. Digest proc: ${digestProcID}. Report count: ${reportCount}. Directory: ${digestedDir}`
220
+ `Digesting completed. Digest proc: ${digestProcID}. Report count: ${prepData.scoredReportNames.length}. Directory: ${digestedDir}`
77
221
  );
78
222
  };
79
223
  // Fulfills a comparison request.
package/digest.js CHANGED
@@ -1,53 +1,25 @@
1
1
  /*
2
2
  digest.js
3
- Creates digests from scored reports.
4
- Reads reports in process.env.REPORTDIR_SCORED and outputs into process.env.REPORTDIR_DIGESTED.
3
+ Creates a digest from a scored report.
5
4
  Arguments:
6
- 0. Base of name of digest proc located in procs/digest.
7
- 1?. starting substring of names of reports in process.env.REPORTDIR_SCORED.
8
- Usage examples:
9
- node digest dp18a 35k1r (to digest all scored reports with names starting with 35k1r)
10
- node digest dp18a (to digest all scored reports)
5
+ 0. Digest template.
6
+ 1. Digest proc.
7
+ 2. Scored report.
11
8
  */
12
9
 
13
- // ########## IMPORTS
14
-
15
- // Module to keep secrets.
16
- require('dotenv').config();
17
- // Module to read and write files.
18
- const fs = require('fs/promises');
19
-
20
- // ########## CONSTANTS
21
-
22
- const reportDirScored = process.env.REPORTDIR_SCORED || 'reports/scored';
23
- const reportDirDigested = process.env.REPORTDIR_DIGESTED || 'reports/digested';
24
- const digestProcDir = process.env.DIGESTPROCDIR || `${__dirname}/procs/digest`;
25
-
26
10
  // ########## FUNCTIONS
27
11
 
28
12
  // Replaces the placeholders in content with eponymous query parameters.
29
13
  const replaceHolders = (content, query) => content
30
14
  .replace(/__([a-zA-Z]+)__/g, (ph, qp) => query[qp]);
31
- // Creates digests.
32
- exports.digest = async (digesterID, reportIDStart) => {
33
- let reportFileNames = await fs.readdir(reportDirScored);
34
- reportFileNames = reportFileNames.filter(fileName => fileName.endsWith('.json'));
35
- if (reportIDStart) {
36
- reportFileNames = reportFileNames
37
- .filter(fileName => fileName.startsWith(reportIDStart));
38
- }
39
- const {makeQuery} = require(`${digestProcDir}/${digesterID}/index.js`);
40
- for (const fileName of reportFileNames) {
41
- const reportJSON = await fs.readFile(`${reportDirScored}/${fileName}`, 'utf8');
42
- const report = JSON.parse(reportJSON);
43
- const query = {};
44
- makeQuery(report, query);
45
- const template = await fs
46
- .readFile(`${digestProcDir}/${digesterID}/index.html`, 'utf8');
47
- const digest = replaceHolders(template, query);
48
- const fileNameBase = fileName.slice(0, -5);
49
- await fs.writeFile(`${reportDirDigested}/${fileNameBase}.html`, digest);
50
- console.log(`Report ${fileNameBase} digested and saved`);
51
- };
52
- return reportFileNames.length;
15
+ // Creates a digest.
16
+ exports.digest = (digestTemplate, digestProc, scoredReport) => {
17
+ // Create a query.
18
+ const query = {};
19
+ digestProc(scoredReport, query);
20
+ // Use it to replace the placeholders in the template.
21
+ const digest = replaceHolders(digestTemplate, query);
22
+ // Return the digest.
23
+ console.log(`Report ${scoredReport.id} digested`);
24
+ return digest;
53
25
  };
package/multiDigest.js ADDED
@@ -0,0 +1,51 @@
1
+ /*
2
+ multiDigest.js
3
+ Creates digests from scored reports.
4
+ Reads all reports in process.env.REPORTDIR_SCORED and outputs them, digested, into
5
+ process.env.REPORTDIR_DIGESTED.
6
+ Argument:
7
+ 0. Base of name of digest proc located in process.env.DIGESTPROCDIR.
8
+ Usage example: node digest dp25a
9
+ */
10
+
11
+ // ########## IMPORTS
12
+
13
+ // Module to keep secrets.
14
+ require('dotenv').config();
15
+ // Module to read and write files.
16
+ const fs = require('fs/promises');
17
+
18
+ // ########## CONSTANTS
19
+
20
+ const reportDirScored = process.env.REPORTDIR_SCORED || 'reports/scored';
21
+ const reportDirDigested = process.env.REPORTDIR_DIGESTED || 'reports/digested';
22
+ const digestProcDir = process.env.DIGESTPROCDIR || `${__dirname}/procs/digest`;
23
+
24
+ // ########## FUNCTIONS
25
+
26
+ // Replaces the placeholders in content with eponymous query parameters.
27
+ const replaceHolders = (content, query) => content
28
+ .replace(/__([a-zA-Z]+)__/g, (ph, qp) => query[qp]);
29
+ // Creates digests.
30
+ exports.multiDigest = async (digesterID, reportIDStart) => {
31
+ // Identify the reports to be digested.
32
+ let reportFileNames = await fs.readdir(reportDirScored);
33
+ reportFileNames = reportFileNames.filter(fileName => fileName.endsWith('.json'));
34
+ // For each report:
35
+ const {makeQuery} = require(`${digestProcDir}/${digesterID}/index.js`);
36
+ for (const fileName of reportFileNames) {
37
+ const reportJSON = await fs.readFile(`${reportDirScored}/${fileName}`, 'utf8');
38
+ const report = JSON.parse(reportJSON);
39
+ // Define its replacements for the placeholders in the digest template.
40
+ const query = {};
41
+ makeQuery(report, query);
42
+ // Replace the placeholders.
43
+ const template = await fs.readFile(`${digestProcDir}/${digesterID}/index.html`, 'utf8');
44
+ const digest = replaceHolders(template, query);
45
+ // Write its digest.
46
+ const fileNameBase = fileName.slice(0, -5);
47
+ await fs.writeFile(`${reportDirDigested}/${fileNameBase}.html`, digest);
48
+ console.log(`Report ${fileNameBase} digested and saved`);
49
+ };
50
+ return reportFileNames.length;
51
+ };
package/multiScore.js ADDED
@@ -0,0 +1,45 @@
1
+ /*
2
+ multiScore.js
3
+ Scores multiple Testaro reports.
4
+ Reads all reports in process.env.REPORTDIR_RAW and outputs them, scored, into
5
+ process.env.REPORTDIR_SCORED.
6
+ Arguments:
7
+ 0. Base of name of score proc located in process.env.SCOREPROCDIR.
8
+ Usage example: node score sp18a
9
+ */
10
+
11
+ // ########## IMPORTS
12
+
13
+ // Module to keep secrets.
14
+ require('dotenv').config();
15
+ // Module to read and write files.
16
+ const fs = require('fs/promises');
17
+
18
+ // ########## CONSTANTS
19
+
20
+ const reportDirRaw = process.env.REPORTDIR_RAW || 'reports/raw';
21
+ const reportDirScored = process.env.REPORTDIR_SCORED || 'reports/scored';
22
+ const scoreProcDir = process.env.SCOREPROCDIR || `${__dirname}/procs/score`;
23
+
24
+ // ########## FUNCTIONS
25
+
26
+ // Score the specified reports and return their count.
27
+ exports.multiScore = async scoreProcID => {
28
+ // Identify the reports to be scored.
29
+ let reportFileNames = await fs.readdir(reportDirRaw);
30
+ reportFileNames = reportFileNames.filter(fileName => fileName.endsWith('.json'));
31
+ // For each of them:
32
+ const {scorer} = require(`${scoreProcDir}/${scoreProcID}`);
33
+ for (const fileName of reportFileNames) {
34
+ // Score it.
35
+ const reportJSON = await fs.readFile(`${reportDirRaw}/${fileName}`, 'utf8');
36
+ const report = JSON.parse(reportJSON);
37
+ await scorer(report);
38
+ report.scoreProcID = scoreProcID;
39
+ // Write it to a file.
40
+ const scoredReportJSON = JSON.stringify(report, null, 2);
41
+ await fs.writeFile(`${reportDirScored}/${fileName}`, `${scoredReportJSON}\n`);
42
+ console.log(`Report ${fileName.slice(0, -5)} scored and saved`);
43
+ };
44
+ return reportFileNames.length;
45
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testilo",
3
- "version": "6.1.7",
3
+ "version": "7.0.1",
4
4
  "description": "Client that scores and digests Testaro reports",
5
5
  "main": "aim.js",
6
6
  "scripts": {
package/score.js CHANGED
@@ -1,50 +1,19 @@
1
1
  /*
2
2
  score.js
3
- Scores Testaro reports.
4
- Reads reports in process.env.REPORTDIR_RAW and outputs into process.env.REPORTDIR_SCORED.
3
+ Scores a Testaro report.
5
4
  Arguments:
6
- 0. Base of name of score proc located in procs/score.
7
- 1?. Starting substring of names of reports in process.env.REPORTDIR_RAW.
8
- Usage examples:
9
- node score sp18a 35k1r (to score all reports with names starting with 35k1r)
10
- node score sp18a (to score all reports)
5
+ 0. Score proc.
6
+ 1. Raw report.
11
7
  */
12
8
 
13
- // ########## IMPORTS
14
-
15
- // Module to keep secrets.
16
- require('dotenv').config();
17
- // Module to read and write files.
18
- const fs = require('fs/promises');
19
-
20
- // ########## CONSTANTS
21
-
22
- const reportDirRaw = process.env.REPORTDIR_RAW || 'reports/raw';
23
- const reportDirScored = process.env.REPORTDIR_SCORED || 'reports/scored';
24
- const scoreProcDir = process.env.SCOREPROCDIR || `${__dirname}/procs/score`;
25
-
26
9
  // ########## FUNCTIONS
27
10
 
28
- // Score the specified reports and return their count.
29
- exports.score = async (scoreProcID, reportIDStart) => {
30
- // Identify the reports to be scored.
31
- let reportFileNames = await fs.readdir(reportDirRaw);
32
- reportFileNames = reportFileNames.filter(fileName => fileName.endsWith('.json'));
33
- if (reportIDStart) {
34
- reportFileNames = reportFileNames.filter(fileName => fileName.startsWith(reportIDStart));
35
- }
36
- // For each of them:
37
- const {scorer} = require(`${scoreProcDir}/${scoreProcID}`);
38
- for (const fileName of reportFileNames) {
39
- // Score it.
40
- const reportJSON = await fs.readFile(`${reportDirRaw}/${fileName}`, 'utf8');
41
- const report = JSON.parse(reportJSON);
42
- await scorer(report);
43
- report.scoreProcID = scoreProcID;
44
- // Write it to a file.
45
- const scoredReportJSON = JSON.stringify(report, null, 2);
46
- await fs.writeFile(`${reportDirScored}/${fileName}`, `${scoredReportJSON}\n`);
47
- console.log(`Report ${fileName.slice(0, -5)} scored and saved`);
48
- };
49
- return reportFileNames.length;
11
+ // Score the specified raw report and return it, scored.
12
+ exports.score = async (scoreProc, rawReport) => {
13
+ // Initialize a scored report.
14
+ const scoredReport = JSON.parse(JSON.stringify(rawReport));
15
+ // Score it.
16
+ await scoreProc(scoredReport);
17
+ console.log(`Report ${rawReport.id} scored`);
18
+ return scoredReport;
50
19
  };