@jrpool/kilotest 46.0.5 → 46.0.7
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/getReport.test.js +62 -0
- package/api/listDiagnoses.test.js +88 -0
- package/api/listIssues.test.js +98 -0
- package/api/listReports.test.js +104 -0
- package/api/listViolators.test.js +73 -0
- package/api/requestFeature.test.js +54 -0
- package/api/requestRetest.test.js +76 -0
- package/api/requestTest.test.js +81 -0
- package/index.js +25 -19
- package/package.json +2 -2
- package/routing.test.js +77 -0
- package/util.js +5 -5
- package/web/listDiagnoses/listDiagnoses.test.js +74 -0
- package/web/listIssues/index.js +3 -3
- package/web/listIssues/listIssues.test.js +80 -0
- package/web/listReports/listReports.test.js +84 -0
- package/web/listViolators/listViolators.test.js +66 -0
- package/api/testSuite.js +0 -311
package/api/testSuite.js
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
© 2026 Jonathan Robert Pool.
|
|
3
|
-
|
|
4
|
-
Licensed under the MIT License. See LICENSE file at the project root or
|
|
5
|
-
https://opensource.org/license/mit/ for details.
|
|
6
|
-
|
|
7
|
-
SPDX-License-Identifier: MIT
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/*
|
|
11
|
-
researchAgent.js
|
|
12
|
-
Simulates a research agent.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
// IMPORTS
|
|
16
|
-
|
|
17
|
-
require('dotenv').config();
|
|
18
|
-
const httpClient = require('http');
|
|
19
|
-
const httpsClient = require('https');
|
|
20
|
-
|
|
21
|
-
// CONSTANTS
|
|
22
|
-
|
|
23
|
-
const kilotestHosts = [
|
|
24
|
-
process.env.LOCAL_KILOTEST_HOST || 'http://localhost:3000',
|
|
25
|
-
process.env.DEPLOYED_KILOTEST_HOST || 'https://kilotest.com'
|
|
26
|
-
];
|
|
27
|
-
// Kilotest host specified by the argument.
|
|
28
|
-
const kilotestHost = kilotestHosts[process.argv[2] === 'pub' ? 1 : 0];
|
|
29
|
-
const hostParts = kilotestHost.split(/:\/*/);
|
|
30
|
-
const scheme = hostParts[0] === 'https' ? 'https' : 'http';
|
|
31
|
-
const client = scheme === 'https' ? httpsClient : httpClient;
|
|
32
|
-
const host = hostParts[1] || 'localhost';
|
|
33
|
-
const port = hostParts[2] || (scheme === 'https' ? 443 : 80);
|
|
34
|
-
|
|
35
|
-
// FUNCTIONS
|
|
36
|
-
|
|
37
|
-
// Gets, parses into an object, and returns the body of a response.
|
|
38
|
-
const getBody = async response => {
|
|
39
|
-
const body = await new Promise(resolve => {
|
|
40
|
-
// Initialize an array of data from the response.
|
|
41
|
-
const chunks = [];
|
|
42
|
-
response
|
|
43
|
-
// If the response throws an error:
|
|
44
|
-
.on('error', error => {
|
|
45
|
-
const {message} = error;
|
|
46
|
-
// Report and return the error message.
|
|
47
|
-
console.log(message);
|
|
48
|
-
resolve({error: message});
|
|
49
|
-
})
|
|
50
|
-
// Whenever the response delivers data:
|
|
51
|
-
.on('data', chunk => {
|
|
52
|
-
// Add them to the array.
|
|
53
|
-
chunks.push(chunk);
|
|
54
|
-
})
|
|
55
|
-
// When the response is completed:
|
|
56
|
-
.on('end', () => {
|
|
57
|
-
const bodyString = chunks.join('');
|
|
58
|
-
try {
|
|
59
|
-
const body = JSON.parse(bodyString);
|
|
60
|
-
// Return the response body as an object.
|
|
61
|
-
resolve(body);
|
|
62
|
-
}
|
|
63
|
-
// If it is not JSON:
|
|
64
|
-
catch {
|
|
65
|
-
// Return this.
|
|
66
|
-
resolve({error: `Response body not JSON (${bodyString})`});
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
return body;
|
|
71
|
-
};
|
|
72
|
-
const getRequestOptions = (path, method = 'GET') => ({
|
|
73
|
-
method,
|
|
74
|
-
host,
|
|
75
|
-
port,
|
|
76
|
-
path,
|
|
77
|
-
headers: {
|
|
78
|
-
'body-type': 'application/json; charset=utf-8'
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
// Submits a request, returns the response body, and increments the results.
|
|
82
|
-
const submitRequest = async (path, method, requestBody = null) => new Promise(resolve => {
|
|
83
|
-
console.log(`Making ${scheme} ${method} request on port ${port} of ${host} to ${path}`);
|
|
84
|
-
client.request(getRequestOptions(path, method), async response => {
|
|
85
|
-
const responseBody = await getBody(response);
|
|
86
|
-
resolve(responseBody);
|
|
87
|
-
})
|
|
88
|
-
.on('error', error => {
|
|
89
|
-
console.log(`ERROR submitting request (${JSON.stringify(error, null, 2)})`);
|
|
90
|
-
resolve({error});
|
|
91
|
-
})
|
|
92
|
-
.end(requestBody ? JSON.stringify(requestBody) : '');
|
|
93
|
-
});
|
|
94
|
-
// Submits requests to the specified Kilotest host.
|
|
95
|
-
const requestService = async () => {
|
|
96
|
-
let body;
|
|
97
|
-
let jobID;
|
|
98
|
-
let method;
|
|
99
|
-
let path;
|
|
100
|
-
let reportBasics;
|
|
101
|
-
let reportsBasics;
|
|
102
|
-
let requestDetails;
|
|
103
|
-
let requestDisposition;
|
|
104
|
-
let responseContent;
|
|
105
|
-
let timeStamp;
|
|
106
|
-
console.log('======================\nRequest: List all available reports');
|
|
107
|
-
method = 'GET';
|
|
108
|
-
path = '/api/listReports';
|
|
109
|
-
body = await submitRequest(path, method);
|
|
110
|
-
responseContent = body?.['response content'] ?? {};
|
|
111
|
-
reportsBasics = responseContent?.['basics about all available reports'] ?? [];
|
|
112
|
-
if (
|
|
113
|
-
!reportsBasics.length
|
|
114
|
-
|| !Array.isArray(reportsBasics)
|
|
115
|
-
|| !reportsBasics.length
|
|
116
|
-
|| reportsBasics.some(
|
|
117
|
-
reportBasics => reportBasics['how to get details about the report']?.method !== 'GET'
|
|
118
|
-
)
|
|
119
|
-
) {
|
|
120
|
-
console.log(`reportsBasics: ${JSON.stringify(reportsBasics, null, 2)}`);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
console.log('======================\nRequest: List issues in one nonexistent report');
|
|
124
|
-
[timeStamp, jobID] = ['111111T1111', 'abc'];
|
|
125
|
-
method = 'GET';
|
|
126
|
-
path = `/api/listIssues/${timeStamp}/${jobID}`;
|
|
127
|
-
body = await submitRequest(path, method);
|
|
128
|
-
responseContent = body?.['response content'] ?? {};
|
|
129
|
-
reportBasics = responseContent?.['basics about the report'] ?? {};
|
|
130
|
-
if (
|
|
131
|
-
!reportBasics.error || !reportBasics.error.includes('missing, unreadable, or not JSON')
|
|
132
|
-
) {
|
|
133
|
-
console.log(`reportBasics: ${JSON.stringify(reportBasics, null, 2)}`);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
console.log('======================\nRequest: List issues in one report');
|
|
137
|
-
const reportsBasicsIndex = Math.floor(reportsBasics.length * Math.random());
|
|
138
|
-
// Choose one report at random.
|
|
139
|
-
reportBasics = reportsBasics[reportsBasicsIndex];
|
|
140
|
-
// Get whether the report has been superseded by a later report about the same page.
|
|
141
|
-
const isSuperseded = reportBasics['whether a later report about the same page exists'];
|
|
142
|
-
[timeStamp, jobID] = reportBasics.identifier?.split('-') || [null, null];
|
|
143
|
-
if (!(timeStamp && jobID)) {
|
|
144
|
-
console.log(`reportBasicsIndex: ${reportsBasicsIndex}`);
|
|
145
|
-
console.log(`reportBasics: ${JSON.stringify(reportBasics, null, 2)}`);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
path = `/api/listIssues/${timeStamp}/${jobID}`;
|
|
149
|
-
body = await submitRequest(path, method);
|
|
150
|
-
responseContent = body?.['response content'] ?? {};
|
|
151
|
-
const issuesBasics = responseContent?.['basics about all issues reported in the report'] ?? [];
|
|
152
|
-
if (
|
|
153
|
-
!issuesBasics.length
|
|
154
|
-
|| !Array.isArray(issuesBasics)
|
|
155
|
-
|| !issuesBasics.length
|
|
156
|
-
|| issuesBasics.some(issueBasics => !issueBasics['impact on a user'])
|
|
157
|
-
) {
|
|
158
|
-
console.log(`issuesBasics: ${JSON.stringify(issuesBasics, null, 2)}`);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
console.log('======================\nRequest: List violators of one issue in one report');
|
|
162
|
-
// Get the issue IDs.
|
|
163
|
-
const issueIDs = issuesBasics.map(issueBasics => issueBasics.identifier);
|
|
164
|
-
// Choose one at random.
|
|
165
|
-
const issueID = issueIDs[Math.floor(Math.random() * issueIDs.length)];
|
|
166
|
-
path = `/api/listViolators/${issueID}/${timeStamp}/${jobID}`;
|
|
167
|
-
body = await submitRequest(path, method);
|
|
168
|
-
responseContent = body?.['response content'] ?? {};
|
|
169
|
-
const violatorsBasics = responseContent?.['basics about all elements exhibiting the issue'] ?? [];
|
|
170
|
-
if (
|
|
171
|
-
!violatorsBasics.length
|
|
172
|
-
|| !Array.isArray(violatorsBasics)
|
|
173
|
-
|| !violatorsBasics.length
|
|
174
|
-
|| violatorsBasics.some(violatorBasics => !violatorBasics[
|
|
175
|
-
'count of rule engines reporting that the element exhibited the issue'
|
|
176
|
-
])
|
|
177
|
-
) {
|
|
178
|
-
console.log(`violatorsBasics: ${JSON.stringify(violatorsBasics, null, 2)}`);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
console.log(
|
|
182
|
-
'======================\nRequest: List diagnoses of one violation of one issue in one report'
|
|
183
|
-
);
|
|
184
|
-
// Get the issue IDs.
|
|
185
|
-
const violatorIDs = violatorsBasics.map(violatorBasics => violatorBasics.identifier);
|
|
186
|
-
// Choose one at random.
|
|
187
|
-
const violatorID = violatorIDs[Math.floor(Math.random() * violatorIDs.length)];
|
|
188
|
-
path = `/api/listDiagnoses/${violatorID}/${issueID}/${timeStamp}/${jobID}`;
|
|
189
|
-
body = await submitRequest(path, method);
|
|
190
|
-
responseContent = body?.['response content'] ?? {};
|
|
191
|
-
const diagnoses = responseContent?.['diagnoses of how the element exhibited the issue'] ?? [];
|
|
192
|
-
if (
|
|
193
|
-
!diagnoses.length
|
|
194
|
-
|| !Array.isArray(diagnoses)
|
|
195
|
-
|| !diagnoses.length
|
|
196
|
-
|| diagnoses.some(
|
|
197
|
-
diagnosis => typeof diagnosis !== 'object' || !diagnosis['description of the violation']
|
|
198
|
-
)
|
|
199
|
-
) {
|
|
200
|
-
console.log(`diagnoses: ${JSON.stringify(diagnoses, null, 2)}`);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
console.log('======================\nRequest: Get one report');
|
|
204
|
-
path = `/api/getReport/${timeStamp}/${jobID}`;
|
|
205
|
-
body = await submitRequest(path, method);
|
|
206
|
-
responseContent = body?.['response content'] ?? {};
|
|
207
|
-
const fullReport = responseContent?.['full report'] ?? {};
|
|
208
|
-
if (responseContent.error || typeof fullReport !== 'object' || !fullReport.device) {
|
|
209
|
-
console.log('Full report request failed');
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
console.log('======================\nRequest: Make a test request with the GET method');
|
|
213
|
-
path = '/api/requestTest';
|
|
214
|
-
body = await submitRequest(path, method);
|
|
215
|
-
if (body.error?.message !== 'Invalid service request') {
|
|
216
|
-
console.log(`body: ${JSON.stringify(body, null, 2)}`);
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
console.log('======================\nRequest: Request a test with a too short URL');
|
|
220
|
-
method = 'POST';
|
|
221
|
-
body = await submitRequest(path, method, {
|
|
222
|
-
description: 'Zilch',
|
|
223
|
-
URL: 'https://a.b',
|
|
224
|
-
reason: 'Just because'
|
|
225
|
-
});
|
|
226
|
-
responseContent = body?.['response content'] ?? {};
|
|
227
|
-
requestDetails = responseContent['details about your request'] ?? {};
|
|
228
|
-
if (!requestDetails.error?.includes('specified a URL for the page')) {
|
|
229
|
-
console.log(`requestDetails: ${JSON.stringify(requestDetails, null, 2)}`);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
console.log('======================\nRequest: Request a test');
|
|
233
|
-
body = await submitRequest(path, method, {
|
|
234
|
-
description: 'Organization that does not exist',
|
|
235
|
-
URL: 'https://nonexistentorg.com',
|
|
236
|
-
reason: 'I have no good reason for wanting this page to be tested'
|
|
237
|
-
});
|
|
238
|
-
responseContent = body?.['response content'] ?? {};
|
|
239
|
-
requestDetails = responseContent['details about your request'] ?? {};
|
|
240
|
-
requestDisposition = responseContent['disposition of your request'] ?? {};
|
|
241
|
-
if (
|
|
242
|
-
!requestDetails['reason why the page should be tested']
|
|
243
|
-
|| !requestDisposition['what happens next']?.includes('1 hour to 1 day')
|
|
244
|
-
) {
|
|
245
|
-
console.log(`requestDetails: ${JSON.stringify(requestDetails, null, 2)}`);
|
|
246
|
-
console.log(`requestDisposition: ${JSON.stringify(requestDisposition, null, 2)}`);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
console.log('======================\nRequest: Request a retest of a nonexistent report');
|
|
250
|
-
path = `/api/requestRetest/${timeStamp}/xyz`;
|
|
251
|
-
body = await submitRequest(path, method, {
|
|
252
|
-
reason: 'It would be cool to retest what does not exist'
|
|
253
|
-
});
|
|
254
|
-
responseContent = body?.['response content'] ?? {};
|
|
255
|
-
requestDetails = responseContent['details about your request'] ?? {};
|
|
256
|
-
if (!requestDetails.error?.includes('not an available report')) {
|
|
257
|
-
console.log(`requestDetails: ${JSON.stringify(requestDetails, null, 2)}`);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
console.log('======================\nRequest: Request a retest');
|
|
261
|
-
path = `/api/requestRetest/${timeStamp}/${jobID}`;
|
|
262
|
-
body = await submitRequest(path, method, {
|
|
263
|
-
reason: 'I just feel like making this request'
|
|
264
|
-
});
|
|
265
|
-
responseContent = body?.['response content'] ?? {};
|
|
266
|
-
requestDetails = responseContent['details about your request'] ?? {};
|
|
267
|
-
requestDisposition = responseContent['disposition of your request'] ?? {};
|
|
268
|
-
// If the report has been superseded:
|
|
269
|
-
if (isSuperseded) {
|
|
270
|
-
// Expect the request to be rejected.
|
|
271
|
-
if (!requestDetails.error?.includes('a later report about the page exists')) {
|
|
272
|
-
console.log(`requestDetails: ${JSON.stringify(requestDetails, null, 2)}`);
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
// Otherwise, i.e. if the report has not been superseded:
|
|
277
|
-
else {
|
|
278
|
-
if (
|
|
279
|
-
!requestDetails['reason why the page should be retested']
|
|
280
|
-
|| !requestDisposition['how you can check for completion']
|
|
281
|
-
) {
|
|
282
|
-
console.log(`requestDetails: ${JSON.stringify(requestDetails, null, 2)}`);
|
|
283
|
-
console.log(`requestDisposition: ${JSON.stringify(requestDisposition, null, 2)}`);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
console.log('======================\nRequest: Make a feature request');
|
|
288
|
-
path = '/api/requestFeature';
|
|
289
|
-
const featureRequest = 'Do the impossible';
|
|
290
|
-
body = await submitRequest(path, method, {
|
|
291
|
-
feature: featureRequest
|
|
292
|
-
});
|
|
293
|
-
const feature = body?.['this request']?.body?.feature ?? {};
|
|
294
|
-
requestDetails = body?.['response content']?.['details about your request'] ?? {};
|
|
295
|
-
if (
|
|
296
|
-
feature !== featureRequest
|
|
297
|
-
|| !requestDetails.disposition?.includes('received and logged')
|
|
298
|
-
) {
|
|
299
|
-
console.log(`requestDetails: ${JSON.stringify(requestDetails, null, 2)}`);
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
console.log('======================\nRequest: Results');
|
|
303
|
-
console.log('All tests succeeded');
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
// EXECUTION
|
|
307
|
-
|
|
308
|
-
// Execute the research agent.
|
|
309
|
-
(async () => {
|
|
310
|
-
await requestService();
|
|
311
|
-
})();
|