@jrpool/kilotest 46.0.3 → 46.0.5
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/api/requestRetest.js +4 -8
- package/index.js +0 -3
- package/package.json +1 -1
- package/test/fixtures/buildFixtures.js +48 -47
- package/test/fixtures/db/hiddenReports/260101T0007-hid.json +69 -0
- package/util.js +22 -74
- package/util.test.js +2 -4
- package/web/expungeReportsForm/index.js +1 -3
- package/web/hideReportForm/index.js +1 -3
- package/web/pruneReportsForm/index.js +1 -3
- package/web/unhideReportForm/index.js +1 -3
package/api/requestRetest.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
8
|
const {getResponseMetadata, getToolsFacts, processTestRequest, thisHost} = require('./util');
|
|
9
|
-
const {getLatestReportExtracts,
|
|
9
|
+
const {getLatestReportExtracts, getReportExtract} = require('../util');
|
|
10
10
|
|
|
11
11
|
// FUNCTIONS
|
|
12
12
|
|
|
@@ -19,15 +19,11 @@ exports.response = async args => {
|
|
|
19
19
|
'disposition of your request': null
|
|
20
20
|
};
|
|
21
21
|
const reasonLength = reason.length;
|
|
22
|
-
// Get data on the available reports.
|
|
23
|
-
const reportsExtract = await getReportsExtract();
|
|
24
22
|
// Get data on the report.
|
|
25
|
-
const reportExtract =
|
|
26
|
-
.
|
|
27
|
-
.find(extract => extract.timeStamp === timeStamp && extract.jobID === jobID);
|
|
28
|
-
const {what, url} = reportExtract || {};
|
|
23
|
+
const reportExtract = await getReportExtract(timeStamp, jobID);
|
|
24
|
+
const {what, url} = reportExtract.error ? {} : reportExtract;
|
|
29
25
|
// If this failed:
|
|
30
|
-
if (
|
|
26
|
+
if (reportExtract.error) {
|
|
31
27
|
// Add this to the response content.
|
|
32
28
|
responseContent['details about your request'] = {
|
|
33
29
|
error: 'request invalid: the specified existing report is not an available report'
|
package/index.js
CHANGED
|
@@ -24,7 +24,6 @@ const {
|
|
|
24
24
|
isTimeStamp,
|
|
25
25
|
isJobID,
|
|
26
26
|
jobsPath,
|
|
27
|
-
makeReportsExtract,
|
|
28
27
|
recsLock,
|
|
29
28
|
reportsPath
|
|
30
29
|
} = require('./util');
|
|
@@ -747,8 +746,6 @@ const requestHandler = async (request, response) => {
|
|
|
747
746
|
report.sources = {...report.sources, worker: workerName};
|
|
748
747
|
// Save the report.
|
|
749
748
|
await fs.writeFile(getReportPath(timeStamp, jobID), getJSON(report));
|
|
750
|
-
// Update the data on the available reports.
|
|
751
|
-
await makeReportsExtract();
|
|
752
749
|
// Annotate the report.
|
|
753
750
|
await annotateReport(timeStamp, jobID);
|
|
754
751
|
console.log(`Testaro report ${id} was annotated, saved, and indexed`);
|
package/package.json
CHANGED
|
@@ -2,14 +2,9 @@
|
|
|
2
2
|
buildFixtures.js
|
|
3
3
|
Builds the fixture corpus for Kilotest tests.
|
|
4
4
|
|
|
5
|
-
Each fixture is a minimal Testaro report in the current format, crafted so
|
|
6
|
-
that the expected API response can be hand-computed and hard-coded into
|
|
7
|
-
tests. The fixtures collectively cover the `outcome` property values
|
|
8
|
-
(`failed`, `cantTell`, and missing/undefined), superseded reports, empty
|
|
9
|
-
reports, and prevented rule engines.
|
|
5
|
+
Each fixture is a minimal Testaro report in the current format, crafted so that the expected API response can be hand-computed and hard-coded into tests. The fixtures collectively cover the `outcome` property values (`failed`, `cantTell`, and missing/undefined), superseded reports, empty reports, and prevented rule engines.
|
|
10
6
|
|
|
11
7
|
Run with: node test/fixtures/buildFixtures.js [targetDir]
|
|
12
|
-
Defaults to test/fixtures/db under the project root.
|
|
13
8
|
*/
|
|
14
9
|
|
|
15
10
|
// IMPORTS
|
|
@@ -20,14 +15,17 @@ const path = require('path');
|
|
|
20
15
|
// CONSTANTS
|
|
21
16
|
|
|
22
17
|
// Issue IDs with known specs in testaro-issues, chosen for different weights.
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
18
|
+
// weight 4 (highest)
|
|
19
|
+
const issueLinkNoText = 'linkNoText';
|
|
20
|
+
// weight 4
|
|
21
|
+
const issueFocusIndication = 'focusIndicationBad';
|
|
22
|
+
// weight 1 (lowest)
|
|
23
|
+
const issueAllCaps = 'allCaps';
|
|
26
24
|
|
|
27
25
|
// Rule engine IDs that exist in util.js ruleEngines.
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
26
|
+
const engineAxe = 'axe';
|
|
27
|
+
const engineAlfa = 'alfa';
|
|
28
|
+
const engineIbm = 'ibm';
|
|
31
29
|
|
|
32
30
|
// FUNCTIONS
|
|
33
31
|
|
|
@@ -116,27 +114,23 @@ const main = async () => {
|
|
|
116
114
|
await fs.mkdir(path.join(targetDir, 'jobs', 'failed'), {recursive: true});
|
|
117
115
|
await fs.mkdir(path.join(targetDir, 'hiddenReports'), {recursive: true});
|
|
118
116
|
|
|
119
|
-
// Fixture 1: mixedOutcomes
|
|
120
|
-
// Two rule engines, three instances: one failed, one cantTell, one failed.
|
|
121
|
-
// The cantTell instance must be excluded from issue/violator counts.
|
|
122
|
-
// Two distinct issues (linkNoText weight 4, allCaps weight 1) and two
|
|
123
|
-
// distinct violator catalog indexes (0 and 1).
|
|
117
|
+
// Fixture 1: mixedOutcomes. Two rule engines, four instances: two failed (same issue, same violator, two engines), one cantTell (excluded from counts), one failed (different issue, different violator). Two distinct issues (linkNoText weight 4, allCaps weight 1) and two distinct violator catalog indexes (0 and 1).
|
|
124
118
|
const mixedCatalog = {
|
|
125
119
|
'0': catalogItem('A', 'About Us', '/html/body/a[1]', '10:20:80:30'),
|
|
126
120
|
'1': catalogItem('P', 'ALL ABOUT US', '/html/body/p[1]', '10:60:80:20')
|
|
127
121
|
};
|
|
128
122
|
const mixedActs = [
|
|
129
|
-
testAct(
|
|
123
|
+
testAct(engineAxe, [
|
|
130
124
|
instance('r11', 'The link does not have an accessible name', 'failed',
|
|
131
|
-
|
|
125
|
+
issueLinkNoText, 0, 2, 1),
|
|
132
126
|
instance('r65', 'Focus Visible', 'cantTell',
|
|
133
|
-
|
|
127
|
+
issueFocusIndication, 0, 0, 1)
|
|
134
128
|
]),
|
|
135
|
-
testAct(
|
|
129
|
+
testAct(engineAlfa, [
|
|
136
130
|
instance('r11', 'The link does not have an accessible name', 'failed',
|
|
137
|
-
|
|
131
|
+
issueLinkNoText, 0, 2, 1),
|
|
138
132
|
instance('r3', 'Text is all-capital', 'failed',
|
|
139
|
-
|
|
133
|
+
issueAllCaps, 1, 1, 1)
|
|
140
134
|
])
|
|
141
135
|
];
|
|
142
136
|
await writeJSON(
|
|
@@ -145,16 +139,14 @@ const main = async () => {
|
|
|
145
139
|
'https://example.com/mixed', mixedActs, mixedCatalog)
|
|
146
140
|
);
|
|
147
141
|
|
|
148
|
-
// Fixture 2: allCantTell
|
|
149
|
-
// All instances have outcome cantTell, so no issues or violators should be
|
|
150
|
-
// reported by listIssues, listViolators, or listDiagnoses.
|
|
142
|
+
// Fixture 2: allCantTell. All instances have outcome cantTell, so no issues or violators should be reported by listIssues, listViolators, or listDiagnoses.
|
|
151
143
|
const cantTellCatalog = {
|
|
152
144
|
'0': catalogItem('A', 'Click here', '/html/body/a[1]', '5:10:60:20')
|
|
153
145
|
};
|
|
154
146
|
const cantTellActs = [
|
|
155
|
-
testAct(
|
|
147
|
+
testAct(engineAxe, [
|
|
156
148
|
instance('r65', 'Focus Visible', 'cantTell',
|
|
157
|
-
|
|
149
|
+
issueFocusIndication, 0, 0, 1)
|
|
158
150
|
])
|
|
159
151
|
];
|
|
160
152
|
await writeJSON(
|
|
@@ -163,15 +155,12 @@ const main = async () => {
|
|
|
163
155
|
'https://example.com/canttell', cantTellActs, cantTellCatalog)
|
|
164
156
|
);
|
|
165
157
|
|
|
166
|
-
// Fixture 3: noOutcomes
|
|
167
|
-
// Instances with no outcome property. Testaro defaults to 'failed', but
|
|
168
|
-
// Kilotest code checks `outcome !== 'cantTell'`, so missing outcome (undefined)
|
|
169
|
-
// is treated as a violation. This verifies that behavior.
|
|
158
|
+
// Fixture 3: noOutcomes. Instances with no outcome property. Testaro defaults to 'failed', but Kilotest code checks `outcome !== 'cantTell'`, so missing outcome (undefined) is treated as a violation. This verifies that behavior.
|
|
170
159
|
const noOutcomeCatalog = {
|
|
171
160
|
'0': catalogItem('BUTTON', 'Submit', '/html/body/button[1]', '15:25:70:30')
|
|
172
161
|
};
|
|
173
162
|
const noOutcomeActs = [
|
|
174
|
-
testAct(
|
|
163
|
+
testAct(engineIbm, [
|
|
175
164
|
{
|
|
176
165
|
ruleID: 'r1',
|
|
177
166
|
what: 'Button has no accessible name',
|
|
@@ -179,7 +168,7 @@ const main = async () => {
|
|
|
179
168
|
count: 1,
|
|
180
169
|
catalogIndex: '0',
|
|
181
170
|
checkpoint: 0,
|
|
182
|
-
issueID:
|
|
171
|
+
issueID: issueLinkNoText
|
|
183
172
|
}
|
|
184
173
|
])
|
|
185
174
|
];
|
|
@@ -189,13 +178,11 @@ const main = async () => {
|
|
|
189
178
|
'https://example.com/nooutcomes', noOutcomeActs, noOutcomeCatalog)
|
|
190
179
|
);
|
|
191
180
|
|
|
192
|
-
// Fixture 4: superseded
|
|
193
|
-
// A report about the same page as mixedOutcomes but with a later timestamp,
|
|
194
|
-
// so mixedOutcomes is superseded by this one.
|
|
181
|
+
// Fixture 4: superseded. A report about the same page as mixedOutcomes but with a later timestamp, so mixedOutcomes is superseded by this one.
|
|
195
182
|
const newerActs = [
|
|
196
|
-
testAct(
|
|
183
|
+
testAct(engineAxe, [
|
|
197
184
|
instance('r11', 'The link does not have an accessible name', 'failed',
|
|
198
|
-
|
|
185
|
+
issueLinkNoText, 0, 2, 1)
|
|
199
186
|
])
|
|
200
187
|
];
|
|
201
188
|
await writeJSON(
|
|
@@ -204,24 +191,21 @@ const main = async () => {
|
|
|
204
191
|
'https://example.com/mixed', newerActs, mixedCatalog, '26-02-02T00:10')
|
|
205
192
|
);
|
|
206
193
|
|
|
207
|
-
// Fixture 5: empty
|
|
208
|
-
// A valid report with no test acts that have instances, so listIssues
|
|
209
|
-
// returns zero issues.
|
|
194
|
+
// Fixture 5: empty. A valid report with no test acts that have instances, so listIssues returns zero issues.
|
|
210
195
|
await writeJSON(
|
|
211
196
|
path.join(reportsDir, '260101T0005-emp.json'),
|
|
212
197
|
report('260101T0005-emp', 'Empty Results Page',
|
|
213
|
-
'https://example.com/empty', [testAct(
|
|
198
|
+
'https://example.com/empty', [testAct(engineAxe, [])], {})
|
|
214
199
|
);
|
|
215
200
|
|
|
216
|
-
// Fixture 6: prevented
|
|
217
|
-
// A report where one rule engine was prevented from testing.
|
|
201
|
+
// Fixture 6: prevented. A report where one rule engine was prevented from testing.
|
|
218
202
|
const preventedCatalog = {
|
|
219
203
|
'0': catalogItem('IMG', 'An image', '/html/body/img[1]', '0:0:200:100')
|
|
220
204
|
};
|
|
221
205
|
const preventedActs = [
|
|
222
|
-
testAct(
|
|
206
|
+
testAct(engineAxe, [
|
|
223
207
|
instance('r11', 'The image has no alt text', 'failed',
|
|
224
|
-
|
|
208
|
+
issueLinkNoText, 0, 2, 1)
|
|
225
209
|
])
|
|
226
210
|
];
|
|
227
211
|
const preventedReport = report('260101T0006-prv', 'Prevented Page',
|
|
@@ -232,6 +216,23 @@ const main = async () => {
|
|
|
232
216
|
preventedReport
|
|
233
217
|
);
|
|
234
218
|
|
|
219
|
+
// Fixture 7: hidden. A report placed in hiddenReports/ instead of reports/, so it must not appear in listReports, listIssues, listViolators, listDiagnoses, or getReport API responses.
|
|
220
|
+
const hiddenCatalog = {
|
|
221
|
+
'0': catalogItem('A', 'Secret link', '/html/body/a[1]', '5:10:60:20')
|
|
222
|
+
};
|
|
223
|
+
const hiddenActs = [
|
|
224
|
+
testAct(engineAxe, [
|
|
225
|
+
instance('r11', 'The link does not have an accessible name', 'failed',
|
|
226
|
+
issueLinkNoText, 0, 2, 1)
|
|
227
|
+
])
|
|
228
|
+
];
|
|
229
|
+
const hiddenReport = report('260101T0007-hid', 'Hidden Page',
|
|
230
|
+
'https://example.com/hidden', hiddenActs, hiddenCatalog);
|
|
231
|
+
await writeJSON(
|
|
232
|
+
path.join(targetDir, 'hiddenReports', '260101T0007-hid.json'),
|
|
233
|
+
hiddenReport
|
|
234
|
+
);
|
|
235
|
+
|
|
235
236
|
// Write an empty recs.json so getRecs does not try to create one.
|
|
236
237
|
await writeJSON(path.join(targetDir, 'jobs', 'recs.json'), {});
|
|
237
238
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "260101T0007-hid",
|
|
3
|
+
"what": "Hidden Page",
|
|
4
|
+
"strict": false,
|
|
5
|
+
"standard": "only",
|
|
6
|
+
"device": {
|
|
7
|
+
"id": "default"
|
|
8
|
+
},
|
|
9
|
+
"browserID": "chromium",
|
|
10
|
+
"creationTimeStamp": "260101T0007",
|
|
11
|
+
"executionTimeStamp": "260101T0007",
|
|
12
|
+
"target": {
|
|
13
|
+
"what": "Hidden Page",
|
|
14
|
+
"url": "https://example.com/hidden"
|
|
15
|
+
},
|
|
16
|
+
"sources": {
|
|
17
|
+
"worker": "test-worker"
|
|
18
|
+
},
|
|
19
|
+
"acts": [
|
|
20
|
+
{
|
|
21
|
+
"type": "test",
|
|
22
|
+
"which": "axe",
|
|
23
|
+
"startTime": "26-01-01T00:00",
|
|
24
|
+
"endTime": "26-01-01T00:01",
|
|
25
|
+
"result": {
|
|
26
|
+
"standardResult": {
|
|
27
|
+
"instances": [
|
|
28
|
+
{
|
|
29
|
+
"ruleID": "r11",
|
|
30
|
+
"what": "The link does not have an accessible name",
|
|
31
|
+
"ordinalSeverity": 2,
|
|
32
|
+
"outcome": "failed",
|
|
33
|
+
"count": 1,
|
|
34
|
+
"catalogIndex": "0",
|
|
35
|
+
"checkpoint": 0,
|
|
36
|
+
"issueID": "linkNoText"
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"outcomeTotals": {
|
|
40
|
+
"failed": 1,
|
|
41
|
+
"cantTell": 0
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"jobData": {
|
|
48
|
+
"startTime": "26-01-01T00:00",
|
|
49
|
+
"endTime": "26-01-01T00:10",
|
|
50
|
+
"elapsedSeconds": 600,
|
|
51
|
+
"preventions": {},
|
|
52
|
+
"issuelessRules": []
|
|
53
|
+
},
|
|
54
|
+
"catalog": {
|
|
55
|
+
"0": {
|
|
56
|
+
"tagName": "A",
|
|
57
|
+
"id": "",
|
|
58
|
+
"startTag": "<a>",
|
|
59
|
+
"text": "Secret link",
|
|
60
|
+
"textLinkable": false,
|
|
61
|
+
"boxID": "5:10:60:20",
|
|
62
|
+
"pathID": "/html/body/a[1]",
|
|
63
|
+
"headingIndex": "",
|
|
64
|
+
"checkpoint": 0
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"images": {},
|
|
68
|
+
"checkpoints": []
|
|
69
|
+
}
|
package/util.js
CHANGED
|
@@ -14,8 +14,7 @@ const wcagMap = require('./wcagMap.json');
|
|
|
14
14
|
|
|
15
15
|
// CONSTANTS
|
|
16
16
|
|
|
17
|
-
// Path of the data directory. Read afresh on every call (rather than cached at module load) so
|
|
18
|
-
// that tests can point Kilotest at a fixture directory via the DB_DIR environment variable.
|
|
17
|
+
// Path of the data directory. Read afresh on every call (rather than cached at module load) so that tests can point Kilotest at a fixture directory via the DB_DIR environment variable.
|
|
19
18
|
const dbPath = exports.dbPath = () => process.env.DB_DIR || path.join(__dirname, 'db');
|
|
20
19
|
// Path of the jobs directory.
|
|
21
20
|
const jobsPath = exports.jobsPath = () => path.join(dbPath(), 'jobs');
|
|
@@ -25,8 +24,6 @@ const recsPath = exports.recsPath = () => path.join(jobsPath(), 'recs.json');
|
|
|
25
24
|
const reportsPath = exports.reportsPath = () => path.join(dbPath(), 'reports');
|
|
26
25
|
// Path of the hidden-reports directory.
|
|
27
26
|
const hiddenReportsPath = exports.hiddenReportsPath = () => path.join(dbPath(), 'hiddenReports');
|
|
28
|
-
// Path of the reports data file.
|
|
29
|
-
const reportsExtractPath = exports.reportsExtractPath = () => path.join(dbPath(), 'reportsExtract.json');
|
|
30
27
|
// IDs, names, and sponsors of Testaro rule engines.
|
|
31
28
|
const ruleEngines = exports.ruleEngines = {
|
|
32
29
|
alfa: ['Alfa', 'Siteimprove'],
|
|
@@ -663,68 +660,43 @@ exports.isHidden = async (timeStamp, jobID) => {
|
|
|
663
660
|
// Return whether the report is among them.
|
|
664
661
|
return hiddenReportFileNames.includes(`${timeStamp}-${jobID}.json`);
|
|
665
662
|
};
|
|
666
|
-
//
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
// For each of them:
|
|
673
|
-
for (const reportFileName of reportFileNames) {
|
|
674
|
-
// Get its content.
|
|
675
|
-
const reportJSON = await fs.readFile(path.join(reportsPath(), reportFileName), 'utf8');
|
|
676
|
-
// Get the object that it represents.
|
|
663
|
+
// Returns an extract of an available report, or an error object if it cannot be read or parsed.
|
|
664
|
+
const getReportExtract = exports.getReportExtract = async (timeStamp, jobID) => {
|
|
665
|
+
try {
|
|
666
|
+
const reportJSON = await fs.readFile(
|
|
667
|
+
path.join(reportsPath(), `${timeStamp}-${jobID}.json`), 'utf8'
|
|
668
|
+
);
|
|
677
669
|
const report = JSON.parse(reportJSON);
|
|
678
670
|
const {target, jobData} = report;
|
|
679
|
-
const jobName = reportFileName.slice(0, -5);
|
|
680
|
-
const [timeStamp, jobID] = jobName.split('-');
|
|
681
671
|
const {what, url} = target;
|
|
682
|
-
|
|
683
|
-
const reportExtract = {
|
|
672
|
+
return {
|
|
684
673
|
timeStamp,
|
|
685
674
|
jobID,
|
|
686
675
|
what,
|
|
687
676
|
url,
|
|
688
677
|
reportTime: new Date(`20${jobData.endTime}Z`).toISOString()
|
|
689
678
|
};
|
|
690
|
-
// Add the report extract to the reports extract.
|
|
691
|
-
reportsExtract[jobName] = reportExtract;
|
|
692
|
-
}
|
|
693
|
-
// Ensure the data directory exists.
|
|
694
|
-
await fs.mkdir(dbPath(), {recursive: true});
|
|
695
|
-
// Save the extract of the reports.
|
|
696
|
-
await fs.writeFile(reportsExtractPath(), getJSON(reportsExtract));
|
|
697
|
-
// Return it.
|
|
698
|
-
return reportsExtract;
|
|
699
|
-
};
|
|
700
|
-
// Gets an extract of all available reports.
|
|
701
|
-
const getReportsExtract = exports.getReportsExtract = async () => {
|
|
702
|
-
let reportsExtract;
|
|
703
|
-
try {
|
|
704
|
-
// Get the reports extract file.
|
|
705
|
-
const reportsExtractJSON = await fs.readFile(reportsExtractPath(), 'utf8');
|
|
706
|
-
// Get its data.
|
|
707
|
-
reportsExtract = JSON.parse(reportsExtractJSON);
|
|
708
|
-
// If it is empty:
|
|
709
|
-
if (!Object.keys(reportsExtract).length) {
|
|
710
|
-
// Throw an error.
|
|
711
|
-
throw new Error('Reports extract file is empty');
|
|
712
|
-
}
|
|
713
679
|
}
|
|
714
|
-
// If this fails:
|
|
715
680
|
catch {
|
|
716
|
-
|
|
717
|
-
|
|
681
|
+
return {
|
|
682
|
+
error: `No report ${timeStamp}-${jobID} is available`
|
|
683
|
+
};
|
|
718
684
|
}
|
|
719
|
-
// Return the extract.
|
|
720
|
-
return reportsExtract;
|
|
721
685
|
};
|
|
722
686
|
// Returns extracts of all available reports.
|
|
723
687
|
const getReportExtracts = exports.getReportExtracts = async () => {
|
|
724
|
-
const
|
|
725
|
-
|
|
688
|
+
const reportFileNames = await fs.readdir(reportsPath());
|
|
689
|
+
const extracts = [];
|
|
690
|
+
for (const reportFileName of reportFileNames) {
|
|
691
|
+
const [timeStamp, jobID] = reportFileName.slice(0, -5).split('-');
|
|
692
|
+
const extract = await getReportExtract(timeStamp, jobID);
|
|
693
|
+
if (!extract.error) {
|
|
694
|
+
extracts.push(extract);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return extracts;
|
|
726
698
|
};
|
|
727
|
-
// Returns whether a report
|
|
699
|
+
// Returns whether a report with a description or URL is available.
|
|
728
700
|
exports.isReportAvailable = async (what, url) => {
|
|
729
701
|
const reportExtracts = await getReportExtracts();
|
|
730
702
|
const whats = reportExtracts.map(reportExtract => reportExtract.what);
|
|
@@ -733,43 +705,19 @@ exports.isReportAvailable = async (what, url) => {
|
|
|
733
705
|
};
|
|
734
706
|
// Gets extracts of the latest available reports for all page descriptions.
|
|
735
707
|
exports.getLatestReportExtracts = async () => {
|
|
736
|
-
// Get extracts of all available reports.
|
|
737
708
|
const reportExtracts = await getReportExtracts();
|
|
738
|
-
// Sort them primarily by page description and secondarily by completion time.
|
|
739
709
|
objectSort(reportExtracts, 'reportTime', 'alpha');
|
|
740
710
|
objectSort(reportExtracts, 'what', 'alpha');
|
|
741
|
-
// Get the latest ones for all page descriptions.
|
|
742
711
|
const latestReportExtracts = reportExtracts
|
|
743
712
|
.filter((extract, index) => extract.what !== reportExtracts[index + 1]?.what);
|
|
744
|
-
// Return them.
|
|
745
713
|
return latestReportExtracts;
|
|
746
714
|
};
|
|
747
|
-
// Returns an extract of an available report.
|
|
748
|
-
exports.getReportExtract = async (timeStamp, jobID) => {
|
|
749
|
-
// Get the extract of all available reports.
|
|
750
|
-
const reportsExtract = await getReportsExtract();
|
|
751
|
-
// Get the extract of the report from it.
|
|
752
|
-
const reportExtract = reportsExtract[`${timeStamp}-${jobID}`];
|
|
753
|
-
// If it exists:
|
|
754
|
-
if (reportExtract) {
|
|
755
|
-
// Return it.
|
|
756
|
-
return reportExtract;
|
|
757
|
-
}
|
|
758
|
-
// Otherwise, i.e. if it does not exist, return this.
|
|
759
|
-
return {
|
|
760
|
-
error: `No report ${timeStamp}-${jobID} is available`
|
|
761
|
-
};
|
|
762
|
-
};
|
|
763
715
|
// Gets the descriptions of multi-report pages.
|
|
764
716
|
exports.getMultiReportWhats = async () => {
|
|
765
|
-
// Get extracts of all available reports.
|
|
766
717
|
const reportExtracts = await getReportExtracts();
|
|
767
|
-
// Get their sorted page descriptions.
|
|
768
718
|
const sortedWhats = reportExtracts.map(extract => extract.what).sort();
|
|
769
|
-
// Get those that at least 2 reports have.
|
|
770
719
|
const multiReportWhats = sortedWhats.filter(
|
|
771
720
|
(what, index) => what !== sortedWhats[index - 1] && what === sortedWhats[index + 1]
|
|
772
721
|
);
|
|
773
|
-
// Return an array of descriptions of multi-report pages.
|
|
774
722
|
return multiReportWhats;
|
|
775
723
|
};
|
package/util.test.js
CHANGED
|
@@ -13,8 +13,7 @@ const {
|
|
|
13
13
|
jobsPath,
|
|
14
14
|
recsPath,
|
|
15
15
|
reportsPath,
|
|
16
|
-
hiddenReportsPath
|
|
17
|
-
reportsExtractPath
|
|
16
|
+
hiddenReportsPath
|
|
18
17
|
} = require('./util');
|
|
19
18
|
|
|
20
19
|
// TESTS
|
|
@@ -48,7 +47,7 @@ test('dbPath honors DB_DIR when it is set', () => {
|
|
|
48
47
|
}
|
|
49
48
|
});
|
|
50
49
|
|
|
51
|
-
test('jobsPath, recsPath, reportsPath,
|
|
50
|
+
test('jobsPath, recsPath, reportsPath, and hiddenReportsPath derive from DB_DIR', () => {
|
|
52
51
|
const saved = process.env.DB_DIR;
|
|
53
52
|
process.env.DB_DIR = '/tmp/kilotest-fixtures';
|
|
54
53
|
try {
|
|
@@ -56,7 +55,6 @@ test('jobsPath, recsPath, reportsPath, hiddenReportsPath, and reportsExtractPath
|
|
|
56
55
|
assert.equal(recsPath(), path.join('/tmp/kilotest-fixtures', 'jobs', 'recs.json'));
|
|
57
56
|
assert.equal(reportsPath(), path.join('/tmp/kilotest-fixtures', 'reports'));
|
|
58
57
|
assert.equal(hiddenReportsPath(), path.join('/tmp/kilotest-fixtures', 'hiddenReports'));
|
|
59
|
-
assert.equal(reportsExtractPath(), path.join('/tmp/kilotest-fixtures', 'reportsExtract.json'));
|
|
60
58
|
}
|
|
61
59
|
finally {
|
|
62
60
|
if (saved !== undefined) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
const {getReportData,
|
|
8
|
+
const {getReportData, objectSort, reportsPath} = require('../../util');
|
|
9
9
|
const fs = require('fs/promises');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -26,8 +26,6 @@ exports.answer = async (_, search) => {
|
|
|
26
26
|
// Delete it.
|
|
27
27
|
await fs.unlink(path.join(reportsPath(), `${jobName}.json`));
|
|
28
28
|
}
|
|
29
|
-
// Update the extract of the available reports.
|
|
30
|
-
await makeReportsExtract();
|
|
31
29
|
}
|
|
32
30
|
// If this failed:
|
|
33
31
|
catch (error) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
const {getReportExtracts, hiddenReportsPath,
|
|
8
|
+
const {getReportExtracts, hiddenReportsPath, reportsPath} = require('../../util');
|
|
9
9
|
const fs = require('fs/promises');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -24,8 +24,6 @@ exports.answer = async (_, search) => {
|
|
|
24
24
|
try {
|
|
25
25
|
// Move the specified report to the directory of hidden reports.
|
|
26
26
|
await fs.rename(path.join(reportsPath(), fileName), path.join(hiddenReportsPath(), fileName));
|
|
27
|
-
// Update the data on the available reports.
|
|
28
|
-
await makeReportsExtract();
|
|
29
27
|
}
|
|
30
28
|
// If this failed:
|
|
31
29
|
catch (error) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
const {getReportData,
|
|
8
|
+
const {getReportData, reportsPath} = require('../../util');
|
|
9
9
|
const fs = require('fs/promises');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -26,8 +26,6 @@ exports.answer = async (_, search) => {
|
|
|
26
26
|
// Delete it.
|
|
27
27
|
await fs.unlink(path.join(reportsPath(), `${jobName}.json`));
|
|
28
28
|
}
|
|
29
|
-
// Update the extract of the available reports.
|
|
30
|
-
await makeReportsExtract();
|
|
31
29
|
}
|
|
32
30
|
// If this failed:
|
|
33
31
|
catch (error) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
const {hiddenReportsPath,
|
|
8
|
+
const {hiddenReportsPath, reportsPath} = require('../../util');
|
|
9
9
|
const fs = require('fs/promises');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
@@ -24,8 +24,6 @@ exports.answer = async (_, search) => {
|
|
|
24
24
|
try {
|
|
25
25
|
// Move the report to the reports directory.
|
|
26
26
|
await fs.rename(path.join(hiddenReportsPath(), fileName), path.join(reportsPath(), fileName));
|
|
27
|
-
// Update the data on the available reports.
|
|
28
|
-
await makeReportsExtract();
|
|
29
27
|
}
|
|
30
28
|
catch (error) {
|
|
31
29
|
// Return why.
|