@jrpool/kilotest 37.0.0 → 38.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/alerts.js +1 -1
- package/diagnoses/index.js +39 -5
- package/eslint.config.mjs +4 -1
- package/index.js +27 -26
- package/issues/index.js +31 -10
- package/llms-full.txt +15 -15
- package/llms.txt +2 -2
- package/mcp.js +24 -18
- package/openapi.yaml +125 -70
- package/package.json +1 -1
- package/reannotate/index.js +10 -2
- package/reannotateForm/index.js +19 -4
- package/reportHideForm/index.js +20 -1
- package/reportIssue/api.js +28 -9
- package/reportIssue/index.js +39 -7
- package/reportIssues/index.js +26 -14
- package/reportIssues/util.js +27 -8
- package/reportList/api.js +36 -28
- package/reportSummary/api.js +181 -0
- package/reportUnhideForm/index.js +20 -2
- package/reportsExpungeForm/index.js +9 -1
- package/reportsPruneForm/index.js +11 -1
- package/reportsRewindForm/index.js +9 -1
- package/researchAgent.js +38 -29
- package/retestRec/index.js +11 -2
- package/retestRecForm/index.js +5 -0
- package/target/api.js +46 -35
- package/targets/index.js +20 -1
- package/testRecForm/api.js +2 -2
- package/tutorial/index.js +15 -5
- package/util.js +63 -48
- package/reportIssues/api.js +0 -129
package/target/api.js
CHANGED
|
@@ -21,9 +21,10 @@ const thisHost = process.env.THIS_KILOTEST_HOST;
|
|
|
21
21
|
|
|
22
22
|
// Returns a response to a target request.
|
|
23
23
|
exports.response = async args => {
|
|
24
|
-
const [
|
|
24
|
+
const [description = '', hostname = ''] = args;
|
|
25
25
|
const hostLC = hostname.toLowerCase();
|
|
26
|
-
const whatLC =
|
|
26
|
+
const whatLC = description.toLowerCase();
|
|
27
|
+
// Initialize an array of summaries of matching reports.
|
|
27
28
|
const matchingReports = [];
|
|
28
29
|
// Get the non-hidden logs.
|
|
29
30
|
const targetLogs = await getLogs();
|
|
@@ -42,6 +43,7 @@ exports.response = async args => {
|
|
|
42
43
|
// Get data about its report.
|
|
43
44
|
const data = await getReportData(timeStamp, jobID);
|
|
44
45
|
const {
|
|
46
|
+
error,
|
|
45
47
|
creationDate,
|
|
46
48
|
daysAgo,
|
|
47
49
|
issueCount,
|
|
@@ -53,35 +55,44 @@ exports.response = async args => {
|
|
|
53
55
|
preventedToolNames,
|
|
54
56
|
preventedToolCount
|
|
55
57
|
} = data;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
'
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
58
|
+
// If this failed:
|
|
59
|
+
if (error) {
|
|
60
|
+
// Report why.
|
|
61
|
+
console.error(error);
|
|
62
|
+
}
|
|
63
|
+
// Otherwise, i.e. if it succeeded:
|
|
64
|
+
else {
|
|
65
|
+
// Add a report summary to the array.
|
|
66
|
+
matchingReports.push({
|
|
67
|
+
identifier: jobName,
|
|
68
|
+
'creation date': creationDate,
|
|
69
|
+
'days since the creation date': daysAgo,
|
|
70
|
+
'tested web page': {
|
|
71
|
+
description: reportWhat,
|
|
72
|
+
URL: reportURL
|
|
73
|
+
},
|
|
74
|
+
'whether a later report about the same page exists': !! superseded,
|
|
75
|
+
'rule engines that tried to test the page': {
|
|
76
|
+
number: toolCount,
|
|
77
|
+
names: toolNames
|
|
78
|
+
},
|
|
79
|
+
'rule engines that were unable to test the page': {
|
|
80
|
+
number: preventedToolCount,
|
|
81
|
+
names: preventedToolNames
|
|
82
|
+
},
|
|
83
|
+
'rule engines that reported issues': {
|
|
84
|
+
number: reporterCount,
|
|
85
|
+
names: reporterNames
|
|
86
|
+
},
|
|
87
|
+
'number of issues reported': issueCount,
|
|
88
|
+
'number of HTML elements reported as exhibiting issues': violatorCount,
|
|
89
|
+
'URLs for getting data about the reported issues': {
|
|
90
|
+
'for you': `${thisHost}/api/reportSummary/${timeStamp}/${jobID}`,
|
|
91
|
+
'for humans': `${thisHost}/reportIssues.html/${timeStamp}/${jobID}`
|
|
92
|
+
},
|
|
93
|
+
'URL for getting the full technical report as JSON': `${thisHost}/fullReport.json/${timeStamp}/${jobID}`
|
|
94
|
+
});
|
|
95
|
+
}
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
// Get a response.
|
|
@@ -96,12 +107,12 @@ exports.response = async args => {
|
|
|
96
107
|
},
|
|
97
108
|
method: 'POST',
|
|
98
109
|
payload: {
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
description,
|
|
111
|
+
hostname
|
|
101
112
|
},
|
|
102
113
|
URLs: {
|
|
103
|
-
'
|
|
104
|
-
'
|
|
114
|
+
'for you': `${thisHost}/api/target`,
|
|
115
|
+
'for humans': 'none'
|
|
105
116
|
},
|
|
106
117
|
'closest ancestor request': null
|
|
107
118
|
},
|
package/targets/index.js
CHANGED
|
@@ -80,8 +80,10 @@ const populateQuery = async query => {
|
|
|
80
80
|
for (const targetLog of targetLogs) {
|
|
81
81
|
const {jobName, url, what} = targetLog;
|
|
82
82
|
const [timeStamp, jobID] = jobName.split('-');
|
|
83
|
+
// Get data about its report.
|
|
83
84
|
const reportData = await getReportData(timeStamp, jobID);
|
|
84
85
|
const {
|
|
86
|
+
error,
|
|
85
87
|
issueCount,
|
|
86
88
|
preventedToolCount,
|
|
87
89
|
preventedToolNames,
|
|
@@ -89,6 +91,15 @@ const populateQuery = async query => {
|
|
|
89
91
|
reporterCount,
|
|
90
92
|
violatorCount
|
|
91
93
|
} = reportData;
|
|
94
|
+
// If this failed:
|
|
95
|
+
if (error) {
|
|
96
|
+
console.error(error);
|
|
97
|
+
// Populate the query with the reason.
|
|
98
|
+
query.error = error;
|
|
99
|
+
// Stop populating the query.
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Otherwise, i.e. if it succeeded, add lines about the report.
|
|
92
103
|
lines.tested.push(`${margin}<details>`);
|
|
93
104
|
const daysAgo = getAgoDays(timeStamp);
|
|
94
105
|
const pageDataStrings = await getPageDataStrings(timeStamp, jobID, {what, url, daysAgo});
|
|
@@ -155,7 +166,15 @@ exports.answer = async () => {
|
|
|
155
166
|
const query = {};
|
|
156
167
|
// Create a query to replace placeholders.
|
|
157
168
|
await populateQuery(query);
|
|
158
|
-
//
|
|
169
|
+
// If the query reports an error:
|
|
170
|
+
if (query.error) {
|
|
171
|
+
// Return it.
|
|
172
|
+
return {
|
|
173
|
+
status: 'error',
|
|
174
|
+
message: query.error
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// Otherwise, i.e. if it does not report an error, get the template.
|
|
159
178
|
let answerPage = await fs.readFile(path.join(__dirname, 'index.html'), 'utf8');
|
|
160
179
|
// Replace its placeholders.
|
|
161
180
|
Object.keys(query).forEach(param => {
|
package/testRecForm/api.js
CHANGED
|
@@ -41,8 +41,8 @@ exports.response = async (what, url, why) => {
|
|
|
41
41
|
'reason for testing the web page': why
|
|
42
42
|
},
|
|
43
43
|
URLs: {
|
|
44
|
-
'
|
|
45
|
-
'
|
|
44
|
+
'for you': `${thisHost}/api/testRecForm`,
|
|
45
|
+
'for humans': `${thisHost}/testRecForm.html`
|
|
46
46
|
},
|
|
47
47
|
'closest ancestor request': null
|
|
48
48
|
},
|
package/tutorial/index.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
-
const {
|
|
8
|
+
const {sendAlert} = require('../alerts');
|
|
9
|
+
const {getJSON, getNowStamp} = require('../util');
|
|
9
10
|
const fs = require('fs/promises');
|
|
10
11
|
const path = require('path');
|
|
11
12
|
|
|
@@ -31,7 +32,7 @@ exports.answer = async () => {
|
|
|
31
32
|
};
|
|
32
33
|
};
|
|
33
34
|
// Sanitizes and saves a tutorial comment to comments.json.
|
|
34
|
-
exports.
|
|
35
|
+
exports.handleComment = async content => {
|
|
35
36
|
if (!content || typeof content !== 'string') {
|
|
36
37
|
return {status: 'error', message: 'No content provided'};
|
|
37
38
|
}
|
|
@@ -41,13 +42,22 @@ exports.saveComment = async content => {
|
|
|
41
42
|
}
|
|
42
43
|
let comments = [];
|
|
43
44
|
try {
|
|
45
|
+
// Get the existing comments.
|
|
44
46
|
const existing = await fs.readFile(commentsPath, 'utf8');
|
|
45
47
|
comments = JSON.parse(existing);
|
|
46
48
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
// If there are none:
|
|
50
|
+
catch {
|
|
51
|
+
// Initialize a comments array.
|
|
49
52
|
}
|
|
50
|
-
|
|
53
|
+
// Add the comment to the existing ones.
|
|
54
|
+
comments.push({
|
|
55
|
+
timeStamp: getNowStamp(),
|
|
56
|
+
content: sanitized
|
|
57
|
+
});
|
|
58
|
+
// Save the revised comments.
|
|
51
59
|
await fs.writeFile(commentsPath, getJSON(comments));
|
|
60
|
+
// Send an alert to the manager.
|
|
61
|
+
await sendAlert('New tutorial comment received', 'A new tutorial comment has been received.');
|
|
52
62
|
return {status: 'ok'};
|
|
53
63
|
};
|
package/util.js
CHANGED
|
@@ -37,6 +37,7 @@ const tools = exports.tools = {
|
|
|
37
37
|
wave: ['WAVE', 'Utah State University'],
|
|
38
38
|
wax: ['WallyAX', 'Wally']
|
|
39
39
|
};
|
|
40
|
+
exports.ruleEngines = tools;
|
|
40
41
|
exports.researchAgents = {
|
|
41
42
|
'research-agent': 'Internal Research Agent'
|
|
42
43
|
}
|
|
@@ -83,7 +84,7 @@ const getRecordPath = exports.getRecordPath = (recordType, timeStamp, jobID) =>
|
|
|
83
84
|
return null;
|
|
84
85
|
}
|
|
85
86
|
};
|
|
86
|
-
// Returns a log or report
|
|
87
|
+
// Returns a log or report.
|
|
87
88
|
const getRecord = exports.getRecord = async (recordType, timeStamp, jobID) => {
|
|
88
89
|
const recordPath = getRecordPath(recordType, timeStamp, jobID);
|
|
89
90
|
let recordJSON, record;
|
|
@@ -92,18 +93,18 @@ const getRecord = exports.getRecord = async (recordType, timeStamp, jobID) => {
|
|
|
92
93
|
}
|
|
93
94
|
catch(error) {
|
|
94
95
|
console.log(error.message);
|
|
95
|
-
|
|
96
|
+
recordJSON = JSON.stringify({error: `Requested ${recordType} ${timeStamp}-${jobID} not found`});
|
|
96
97
|
}
|
|
97
98
|
try {
|
|
98
99
|
record = JSON.parse(recordJSON);
|
|
99
100
|
}
|
|
100
101
|
catch (error) {
|
|
101
102
|
console.log(error.message);
|
|
102
|
-
|
|
103
|
+
record = {error: `Requested ${recordType} ${timeStamp}-${jobID} not JSON`};
|
|
103
104
|
}
|
|
104
105
|
return record;
|
|
105
106
|
};
|
|
106
|
-
// Returns a report
|
|
107
|
+
// Returns a report.
|
|
107
108
|
const getReport = exports.getReport = async (timeStamp, jobID) => await getRecord(
|
|
108
109
|
'report', timeStamp, jobID
|
|
109
110
|
);
|
|
@@ -246,13 +247,13 @@ const getIssue = exports.getIssue = (ruleIDs, toolID, ruleID) => {
|
|
|
246
247
|
// Adds issue IDs to the standard instances of a report.
|
|
247
248
|
const annotateReport = exports.annotateReport = async (ruleIDs, timeStamp, jobID) => {
|
|
248
249
|
// Get a copy of the report.
|
|
249
|
-
const
|
|
250
|
+
const report = await getReport(timeStamp, jobID);
|
|
250
251
|
// If this failed:
|
|
251
|
-
if (
|
|
252
|
-
// Return
|
|
253
|
-
return
|
|
252
|
+
if (report.error) {
|
|
253
|
+
// Return why.
|
|
254
|
+
return report.error;
|
|
254
255
|
}
|
|
255
|
-
|
|
256
|
+
// Otherwise, i.e. if it succeeded:
|
|
256
257
|
const unclassifiableRules = new Set();
|
|
257
258
|
// For each of its acts:
|
|
258
259
|
for (const act of report.acts) {
|
|
@@ -293,36 +294,35 @@ const annotateReport = exports.annotateReport = async (ruleIDs, timeStamp, jobID
|
|
|
293
294
|
// Save the annotated report.
|
|
294
295
|
await fs.writeFile(getReportPath(timeStamp, jobID), getJSON(report));
|
|
295
296
|
// Get a copy of the log of the report.
|
|
296
|
-
const
|
|
297
|
+
const log = await getLog(timeStamp, jobID, false);
|
|
297
298
|
// If this failed:
|
|
298
|
-
if (
|
|
299
|
-
// Return
|
|
300
|
-
return
|
|
299
|
+
if (log.error) {
|
|
300
|
+
// Return why.
|
|
301
|
+
return log.error;
|
|
301
302
|
}
|
|
302
303
|
// Otherwise, i.e. if it succeeded:
|
|
303
304
|
else {
|
|
304
|
-
const log = logOrError;
|
|
305
305
|
// Mark the report as annotated in the log.
|
|
306
306
|
log.annotated = true;
|
|
307
307
|
// Save the revised log.
|
|
308
308
|
await fs.writeFile(getLogPath(timeStamp, jobID), getJSON(log));
|
|
309
|
-
// Return
|
|
309
|
+
// Return success.
|
|
310
|
+
return '';
|
|
310
311
|
}
|
|
311
312
|
};
|
|
312
|
-
// Returns a report log after conditionally annotating it
|
|
313
|
+
// Returns a report log after conditionally annotating it.
|
|
313
314
|
const getLog = exports.getLog = async (timeStamp, jobID, annotate = false) => {
|
|
314
315
|
const log = await getRecord('log', timeStamp, jobID);
|
|
315
|
-
if (
|
|
316
|
-
annotateReport(ruleIDs, timeStamp, jobID);
|
|
316
|
+
if (annotate && ! (log.error || log.annotated)) {
|
|
317
|
+
await annotateReport(ruleIDs, timeStamp, jobID);
|
|
317
318
|
}
|
|
318
319
|
return log;
|
|
319
320
|
};
|
|
320
|
-
// Returns whether a report is hidden
|
|
321
|
+
// Returns whether a report is hidden.
|
|
321
322
|
exports.isHidden = async (timeStamp, jobID) => {
|
|
323
|
+
// Get the log of the report.
|
|
322
324
|
const log = await getLog(timeStamp, jobID, false);
|
|
323
|
-
|
|
324
|
-
return log;
|
|
325
|
-
}
|
|
325
|
+
// Return whether the log exists and its report is hidden.
|
|
326
326
|
return !! log.hidden;
|
|
327
327
|
};
|
|
328
328
|
// Returns summary data on a report.
|
|
@@ -330,9 +330,9 @@ exports.getReportData = async (timeStamp, jobID) => {
|
|
|
330
330
|
// Validate the report and annotate it if necessary.
|
|
331
331
|
const log = await getLog(timeStamp, jobID, true);
|
|
332
332
|
// If this failed:
|
|
333
|
-
if (
|
|
334
|
-
// Return
|
|
335
|
-
return log;
|
|
333
|
+
if (log.error) {
|
|
334
|
+
// Return why.
|
|
335
|
+
return {error: log.error};
|
|
336
336
|
}
|
|
337
337
|
// Initialize the data.
|
|
338
338
|
const data = {
|
|
@@ -357,9 +357,9 @@ exports.getReportData = async (timeStamp, jobID) => {
|
|
|
357
357
|
// Get the report.
|
|
358
358
|
const report = await getReport(timeStamp, jobID);
|
|
359
359
|
// If this failed:
|
|
360
|
-
if (
|
|
361
|
-
// Return
|
|
362
|
-
return report;
|
|
360
|
+
if (report.error) {
|
|
361
|
+
// Return why.
|
|
362
|
+
return {error: report.error};
|
|
363
363
|
}
|
|
364
364
|
// For each act of the report:
|
|
365
365
|
report.acts.forEach(act => {
|
|
@@ -526,30 +526,31 @@ const getLogs = exports.getLogs = async () => {
|
|
|
526
526
|
try {
|
|
527
527
|
logFileNames = await fs.readdir(logsPath);
|
|
528
528
|
}
|
|
529
|
-
catch
|
|
530
|
-
|
|
529
|
+
catch {
|
|
530
|
+
console.error('Logs directory not readable');
|
|
531
|
+
logFileNames = [];
|
|
531
532
|
}
|
|
532
533
|
// For each log:
|
|
533
534
|
for (const fileName of logFileNames) {
|
|
534
535
|
const logName = fileName.slice(0, -5);
|
|
535
536
|
const [timeStamp, jobID] = logName.split('-');
|
|
536
537
|
// Get it.
|
|
537
|
-
const
|
|
538
|
+
const log = await getLog(timeStamp, jobID);
|
|
538
539
|
// If this failed:
|
|
539
|
-
if (
|
|
540
|
-
//
|
|
541
|
-
|
|
540
|
+
if (log.error) {
|
|
541
|
+
// Report why.
|
|
542
|
+
console.error(log.error);
|
|
542
543
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
544
|
+
// Otherwise, i.e. if it succeeded:
|
|
545
|
+
else {
|
|
546
|
+
// If the report is not hidden:
|
|
547
|
+
if (! log.hidden) {
|
|
548
|
+
// Add the job name to the log.
|
|
549
|
+
log.jobName = logName;
|
|
550
|
+
// Add the log to the logs.
|
|
551
|
+
logs.push(log);
|
|
552
|
+
}
|
|
548
553
|
}
|
|
549
|
-
// Add the job name to the log.
|
|
550
|
-
log.jobName = logName;
|
|
551
|
-
// Add the log to the logs.
|
|
552
|
-
logs.push(log);
|
|
553
554
|
}
|
|
554
555
|
// Sort the logs by target name and secondarily by test time.
|
|
555
556
|
logs.sort((a, b) => {
|
|
@@ -692,12 +693,12 @@ const getPageData = exports.getPageData = async (timeStamp, jobID) => {
|
|
|
692
693
|
// Get the log of the report.
|
|
693
694
|
const log = await getLog(timeStamp, jobID, false);
|
|
694
695
|
// If this failed:
|
|
695
|
-
if (
|
|
696
|
-
// Return
|
|
696
|
+
if (log.error) {
|
|
697
|
+
// Return why.
|
|
697
698
|
return log;
|
|
698
699
|
}
|
|
699
700
|
const {url, what} = log;
|
|
700
|
-
//
|
|
701
|
+
// Otherwise, i.e. if it succeeded, get the elapsed time in days since the test.
|
|
701
702
|
const daysAgo = getAgoDays(timeStamp);
|
|
702
703
|
// Return the data.
|
|
703
704
|
return {
|
|
@@ -713,7 +714,15 @@ exports.getPageDataStrings = async (timeStamp, jobID, pageData) => {
|
|
|
713
714
|
// Get them.
|
|
714
715
|
pageData = await getPageData(timeStamp, jobID);
|
|
715
716
|
}
|
|
716
|
-
const {
|
|
717
|
+
const {daysAgo, error, url, what} = pageData;
|
|
718
|
+
// If the page data are invalid:
|
|
719
|
+
if (error) {
|
|
720
|
+
// Return why.
|
|
721
|
+
return {
|
|
722
|
+
error
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
// Otherwise, i.e. if they are valid, get a description of the timestamp.
|
|
717
726
|
const when = getDateTimeString(timeStamp);
|
|
718
727
|
// Return the HTML strings.
|
|
719
728
|
return {
|
|
@@ -766,7 +775,13 @@ exports.isReportAvailable = async (what, url) => {
|
|
|
766
775
|
};
|
|
767
776
|
// Returns the size of a report.
|
|
768
777
|
exports.getReportSize = async (timeStamp, jobID) => {
|
|
769
|
-
const reportStat = await fs.stat(
|
|
778
|
+
const reportStat = await fs.stat(
|
|
779
|
+
path.join(reportsPath, `${timeStamp}-${jobID}.json`),
|
|
780
|
+
{throwIfNoEntry: false}
|
|
781
|
+
);
|
|
782
|
+
if (! reportStat) {
|
|
783
|
+
return 0;
|
|
784
|
+
}
|
|
770
785
|
const reportSize = reportStat.size;
|
|
771
786
|
return reportSize;
|
|
772
787
|
};
|
package/reportIssues/api.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
api.js
|
|
3
|
-
Responds to the report-issues request.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// IMPORTS
|
|
7
|
-
|
|
8
|
-
const {getData} = require('./util');
|
|
9
|
-
const {
|
|
10
|
-
getDateTime,
|
|
11
|
-
getNowStamp,
|
|
12
|
-
getRandomString,
|
|
13
|
-
getToolsFacts,
|
|
14
|
-
isHidden,
|
|
15
|
-
tools
|
|
16
|
-
} = require('../util');
|
|
17
|
-
|
|
18
|
-
// FUNCTIONS
|
|
19
|
-
|
|
20
|
-
// Gets facts about an issue.
|
|
21
|
-
const getIssueFacts = (thisHost, timeStamp, jobID, issue) => {
|
|
22
|
-
const {issueID, reporterCount, reporters, summary, violatorCount, wcag, why} = issue;
|
|
23
|
-
const wcagType = wcag.length === 3 ? 'guideline' : 'success criterion';
|
|
24
|
-
return {
|
|
25
|
-
identifier: issueID,
|
|
26
|
-
summary,
|
|
27
|
-
'related WCAG 2.2 standard': {
|
|
28
|
-
layer: wcagType,
|
|
29
|
-
'numeric identifier': wcag
|
|
30
|
-
},
|
|
31
|
-
'impact on a user': why,
|
|
32
|
-
'rule engines reporting the issue': {
|
|
33
|
-
'number': reporterCount,
|
|
34
|
-
'names': reporters.map(tool => tool.toolName)
|
|
35
|
-
},
|
|
36
|
-
'number of HTML elements reported as exhibiting the issue': violatorCount,
|
|
37
|
-
'URLs for details about the issue on the page': {
|
|
38
|
-
'for you': `${thisHost}/api/reportIssue/${issueID}/${timeStamp}/${jobID}`,
|
|
39
|
-
'for humans': `${thisHost}/reportIssue.html/${issueID}/${timeStamp}/${jobID}`
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
// Returns a response to a report-issues request.
|
|
44
|
-
exports.response = async args => {
|
|
45
|
-
const [timeStamp, jobID] = args;
|
|
46
|
-
const reportIsHidden = await isHidden(timeStamp, jobID);
|
|
47
|
-
// If the report is not available:
|
|
48
|
-
if (reportIsHidden) {
|
|
49
|
-
return {
|
|
50
|
-
status: 'error',
|
|
51
|
-
message: 'Report not available'
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
// Otherwise, i.e. if the report is available, get data on the target and issues.
|
|
55
|
-
const data = await getData(timeStamp, jobID);
|
|
56
|
-
const {pageData, issuesData} = data;
|
|
57
|
-
const {what, url, daysAgo} = pageData;
|
|
58
|
-
const {issueCount, issues, preventions, reporterCount, reporters, violatorCount} = issuesData;
|
|
59
|
-
const preventedTools = Object.entries(preventions).map(prevention => ({
|
|
60
|
-
name: tools[prevention[0]][0],
|
|
61
|
-
'reason for failure': prevention[1]
|
|
62
|
-
}));
|
|
63
|
-
const thisHost = process.env.THIS_KILOTEST_HOST;
|
|
64
|
-
// Get a response.
|
|
65
|
-
const content = {
|
|
66
|
-
summary: `This document fulfills a request made by a language model to a Kilotest tool. The model requested data from a Kilotest report about the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. Kilotest, with the help of Testaro, Testilo, and an ensemble of ten rule engines, performs tests on web pages, using a combination of rule- and machine-learning-based methods, and produces reports. Kilotest exposes several API endpoints to recommend web pages for testing and to obtain information from Kilotest reports. To learn more about Kilotest and the advantages of testing with an ensemble of rule engines, visit the deployed instance of Kilotest (${process.env.DEPLOYED_KILOTEST_HOST}), which contains an introduction on its home page and a tutorial.`,
|
|
67
|
-
'tool collection name': 'Kilotest',
|
|
68
|
-
'tool name': 'describeQualityOfOneWebPage',
|
|
69
|
-
request: {
|
|
70
|
-
'type of request': {
|
|
71
|
-
identifier: 'reportIssues',
|
|
72
|
-
description: 'Describe the quality of one web page.'
|
|
73
|
-
},
|
|
74
|
-
method: 'GET',
|
|
75
|
-
URLs: {
|
|
76
|
-
'URL of your request': `${thisHost}/api/reportIssues/${timeStamp}/${jobID}`,
|
|
77
|
-
'equivalent URL for humans': `${thisHost}/reportIssues.html/${timeStamp}/${jobID}`
|
|
78
|
-
},
|
|
79
|
-
'closest ancestor request': {
|
|
80
|
-
identifier: 'getListOfAllAvailableReports',
|
|
81
|
-
description: 'Get a list of all available reports.',
|
|
82
|
-
URLs: {
|
|
83
|
-
'for you': `${thisHost}/api/reportList`,
|
|
84
|
-
'for humans': `${thisHost}/targets.html`
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
'response metadata': {
|
|
89
|
-
identifier: `${getNowStamp()}-${getRandomString(3)}`,
|
|
90
|
-
'date and time': new Date().toISOString()
|
|
91
|
-
},
|
|
92
|
-
report: {
|
|
93
|
-
identifier: `${timeStamp}-${jobID}`,
|
|
94
|
-
'creation date': getDateTime(timeStamp),
|
|
95
|
-
'days since the creation date': daysAgo
|
|
96
|
-
},
|
|
97
|
-
'tested web page': {
|
|
98
|
-
description: what,
|
|
99
|
-
URL: url
|
|
100
|
-
},
|
|
101
|
-
'rule engines that tried to test the page': getToolsFacts(Object.keys(tools)),
|
|
102
|
-
'rule engines that were unable to test the page': preventedTools,
|
|
103
|
-
'rule engines that reported issues': {
|
|
104
|
-
number: reporterCount,
|
|
105
|
-
names: reporters.map(tool => tool.toolName)
|
|
106
|
-
},
|
|
107
|
-
'number of issues reported': {
|
|
108
|
-
total: issueCount,
|
|
109
|
-
'by priority': {
|
|
110
|
-
'highest priority': issues[4].length,
|
|
111
|
-
'high priority': issues[3].length,
|
|
112
|
-
'low priority': issues[2].length,
|
|
113
|
-
'lowest priority': issues[1].length
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
'number of HTML elements reported as exhibiting issues': violatorCount,
|
|
117
|
-
'issues reported': {
|
|
118
|
-
'highest priority': issues[4]
|
|
119
|
-
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue)),
|
|
120
|
-
'high priority': issues[3]
|
|
121
|
-
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue)),
|
|
122
|
-
'low priority': issues[2]
|
|
123
|
-
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue)),
|
|
124
|
-
'lowest priority': issues[1]
|
|
125
|
-
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue))
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
return content;
|
|
129
|
-
};
|