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 +118 -24
- package/aim.js +4 -18
- package/call.js +162 -18
- package/digest.js +14 -42
- package/multiDigest.js +51 -0
- package/multiScore.js +45 -0
- package/package.json +1 -1
- package/score.js +11 -42
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
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
fs.
|
|
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
|
|
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
|
|
112
|
-
|
|
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
|
|
180
|
+
node call multiScore sp25a
|
|
122
181
|
```
|
|
123
182
|
|
|
124
|
-
The
|
|
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
|
|
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
|
-
|
|
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 {
|
|
136
|
-
|
|
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
|
|
239
|
+
node call multiDigest dp25a
|
|
146
240
|
```
|
|
147
241
|
|
|
148
|
-
The
|
|
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
|
|
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`
|
|
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
|
|
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 =
|
|
21
|
-
//
|
|
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
|
-
//
|
|
41
|
-
script.
|
|
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
|
|
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
|
|
12
|
-
node call score sp25a 8ep9f
|
|
13
|
-
node call
|
|
14
|
-
node call digest dp25a
|
|
15
|
-
node call
|
|
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
|
|
29
|
+
// Function to score a report.
|
|
28
30
|
const {score} = require('./score');
|
|
29
|
-
// Function to
|
|
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,
|
|
47
|
-
const
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
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: ${
|
|
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
|
|
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.
|
|
7
|
-
1
|
|
8
|
-
|
|
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
|
|
32
|
-
exports.digest =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
package/score.js
CHANGED
|
@@ -1,50 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
2
|
score.js
|
|
3
|
-
Scores Testaro
|
|
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.
|
|
7
|
-
1
|
|
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
|
|
29
|
-
exports.score = async (
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
};
|