@jrpool/kilotest 26.0.1 → 28.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/index.js +118 -90
- package/llms.txt +16 -0
- package/openapi.yaml +346 -0
- package/package.json +1 -1
- package/reportIssues/api.js +22 -19
- package/reportIssues/index.html +1 -1
- package/researchAgent.js +5 -11
- package/robots.txt +4 -0
- package/sitemap.xml +9 -0
- package/targets/api.js +10 -14
- package/util.js +1 -1
package/index.js
CHANGED
|
@@ -87,6 +87,9 @@ const serveError = async (error, response, isHumanUser = true) => {
|
|
|
87
87
|
if (isHumanUser) {
|
|
88
88
|
// Serve an HTML page containing the message property of the error.
|
|
89
89
|
response.setHeader('content-type', 'text/html; charset=utf-8');
|
|
90
|
+
response.setHeader('content-location', '/error.html');
|
|
91
|
+
response.setHeader('Access-Control-Allow-Origin', '*');
|
|
92
|
+
response.setHeader('Cache-Control', 'public, max-age=300, stale-while-revalidate=3000');
|
|
90
93
|
const errorTemplate = await fs.readFile('error.html', 'utf8');
|
|
91
94
|
const errorPage = errorTemplate.replace(/__error__/, error.message || 'ERROR');
|
|
92
95
|
response.end(errorPage);
|
|
@@ -168,11 +171,28 @@ const checkBalancesForAlerts = async report => {
|
|
|
168
171
|
};
|
|
169
172
|
// Handles a request.
|
|
170
173
|
const requestHandler = async (request, response) => {
|
|
174
|
+
// Sets response headers.
|
|
175
|
+
const setHeaders = (contentType, location, volatility = 'high') => {
|
|
176
|
+
response.setHeader('content-type', `${contentType}; charset=utf-8`);
|
|
177
|
+
if (location) {
|
|
178
|
+
response.setHeader('content-location', location);
|
|
179
|
+
}
|
|
180
|
+
response.setHeader('Access-Control-Allow-Origin', '*');
|
|
181
|
+
const lives = {
|
|
182
|
+
high: [300, 3000],
|
|
183
|
+
medium: [1000, 10000],
|
|
184
|
+
low: [5000, 50000]
|
|
185
|
+
};
|
|
186
|
+
response.setHeader(
|
|
187
|
+
'Cache-Control',
|
|
188
|
+
`public, max-age=${lives[volatility][0]}, stale-while-revalidate=${lives[volatility][1]}`
|
|
189
|
+
);
|
|
190
|
+
};
|
|
171
191
|
const {method, url} = request;
|
|
172
192
|
const requestURL = new URL(url, 'https://localhost:3000');
|
|
173
193
|
const {pathname, search} = requestURL;
|
|
174
194
|
const pageName = pathname.split('/')[1];
|
|
175
|
-
const
|
|
195
|
+
const pathTail = pathname.split('/').slice(2).join('/');
|
|
176
196
|
// If the request is an OPTIONS request:
|
|
177
197
|
if (method === 'OPTIONS') {
|
|
178
198
|
// Serve response headers, including one allowing requests from other applications.
|
|
@@ -189,14 +209,30 @@ const requestHandler = async (request, response) => {
|
|
|
189
209
|
// Get the home page.
|
|
190
210
|
const homePage = await fs.readFile('index.html', 'utf8');
|
|
191
211
|
// Serve it.
|
|
192
|
-
|
|
193
|
-
response.setHeader('content-location', '/index.html');
|
|
194
|
-
response.setHeader('Access-Control-Allow-Origin', '*');
|
|
212
|
+
setHeaders('text/html', '/index.html', 'medium');
|
|
195
213
|
response.end(homePage);
|
|
196
214
|
}
|
|
215
|
+
// Otherwise, if it is for the the crawler specification:
|
|
216
|
+
else if (pageName === 'robots.txt') {
|
|
217
|
+
const robots = await fs.readFile('robots.txt', 'utf8');
|
|
218
|
+
setHeaders('text/plain', '/robots.txt', 'low');
|
|
219
|
+
response.end(robots);
|
|
220
|
+
}
|
|
221
|
+
// Otherwise, if it is for the the OpenAPI specification:
|
|
222
|
+
else if (pageName === 'openapi.yaml') {
|
|
223
|
+
const openapi = await fs.readFile('openapi.yaml', 'utf8');
|
|
224
|
+
setHeaders('text/yaml', '/openapi.yaml', 'high');
|
|
225
|
+
response.end(openapi);
|
|
226
|
+
}
|
|
227
|
+
// Otherwise, if it is for the the large-language-model specification:
|
|
228
|
+
else if (pageName === 'llms.txt') {
|
|
229
|
+
const llms = await fs.readFile('llms.txt', 'utf8');
|
|
230
|
+
setHeaders('text/plain', '/llms.txt', 'high');
|
|
231
|
+
response.end(llms);
|
|
232
|
+
}
|
|
197
233
|
// Otherwise, if it is for a full report download:
|
|
198
|
-
else if (pageName === 'fullReport.
|
|
199
|
-
const [timeStamp, jobID] =
|
|
234
|
+
else if (pageName === 'fullReport.json') {
|
|
235
|
+
const [timeStamp, jobID] = pathTail.split('/');
|
|
200
236
|
// If the request is syntactically valid:
|
|
201
237
|
if (isTimeStamp(timeStamp) && isJobID(jobID)) {
|
|
202
238
|
const reportHidden = await isHidden(timeStamp, jobID);
|
|
@@ -222,11 +258,10 @@ const requestHandler = async (request, response) => {
|
|
|
222
258
|
// If it exists and is valid:
|
|
223
259
|
if (typeof report === 'object') {
|
|
224
260
|
// Serve response headers for a JSON download.
|
|
225
|
-
|
|
261
|
+
setHeaders('application/json', null, 'low');
|
|
226
262
|
response.setHeader(
|
|
227
263
|
'content-disposition', `attachment; filename="${timeStamp}-${jobID}.json"`,
|
|
228
264
|
);
|
|
229
|
-
response.setHeader('Access-Control-Allow-Origin', '*');
|
|
230
265
|
// Download the report.
|
|
231
266
|
response.end(getJSON(report));
|
|
232
267
|
}
|
|
@@ -248,11 +283,9 @@ const requestHandler = async (request, response) => {
|
|
|
248
283
|
const topic = pageName.slice(0, -5);
|
|
249
284
|
// If the page can be generated:
|
|
250
285
|
if (answer[topic]) {
|
|
251
|
-
|
|
252
|
-
response.setHeader('content-type', 'text/html; charset=utf-8');
|
|
253
|
-
response.setHeader('content-location', `${pathname}${search}`);
|
|
286
|
+
setHeaders('text/html', `${pathname}${search}`, 'medium');
|
|
254
287
|
// Get the answer data.
|
|
255
|
-
const answerData = await answer[topic](
|
|
288
|
+
const answerData = await answer[topic](pathTail, search);
|
|
256
289
|
// If they are valid:
|
|
257
290
|
if (answerData.status === 'ok') {
|
|
258
291
|
// Serve the answer page.
|
|
@@ -270,6 +303,47 @@ const requestHandler = async (request, response) => {
|
|
|
270
303
|
await serveError({message: 'ERROR: Invalid request'}, response, true);
|
|
271
304
|
}
|
|
272
305
|
}
|
|
306
|
+
// Otherwise, if it is for an API service:
|
|
307
|
+
else if (pageName === 'api') {
|
|
308
|
+
const [service, ... specs] = pathTail.split('/');
|
|
309
|
+
// If the service is provision of facts about the available reports:
|
|
310
|
+
if (service === 'targets') {
|
|
311
|
+
// Get the response (potentially error) data.
|
|
312
|
+
const responseData = await require(path.join(__dirname, 'targets', 'api')).response(specs);
|
|
313
|
+
// Send them.
|
|
314
|
+
setHeaders('application/json', null, 'high');
|
|
315
|
+
response.end(JSON.stringify(responseData));
|
|
316
|
+
}
|
|
317
|
+
// Otherwise, if the service is provision of issue statistics for a report:
|
|
318
|
+
else if (service === 'reportIssues') {
|
|
319
|
+
// Get the report identifiers from the path.
|
|
320
|
+
const [timeStamp, jobID] = specs;
|
|
321
|
+
const reportSpecsBad = await isHidden(timeStamp, jobID);
|
|
322
|
+
// If the report is nonexistent or hidden:
|
|
323
|
+
if (reportSpecsBad) {
|
|
324
|
+
// Report this.
|
|
325
|
+
await serveError(
|
|
326
|
+
{message: reportSpecsBad === true ? 'Report nonexistent or hidden' : reportSpecsBad},
|
|
327
|
+
response,
|
|
328
|
+
false
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
// Otherwise, i.e. if the report is available:
|
|
332
|
+
else {
|
|
333
|
+
// Get the response (potentially error) data.
|
|
334
|
+
const responseData = await require(path.join(__dirname, 'reportIssues', 'api'))
|
|
335
|
+
.response(specs);
|
|
336
|
+
// Send them.
|
|
337
|
+
setHeaders('application/json', null, 'high');
|
|
338
|
+
response.end(JSON.stringify(responseData));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Otherwise, i.e. if the service is invalid:
|
|
342
|
+
else {
|
|
343
|
+
// Report this.
|
|
344
|
+
await serveError({message: 'ERROR: Invalid service request'}, response, false);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
273
347
|
// Otherwise, if it is for a tutorial image:
|
|
274
348
|
else if (pathname.startsWith('/tutorial/images/')) {
|
|
275
349
|
const imgFile = pathname.slice('/tutorial/images/'.length);
|
|
@@ -277,9 +351,15 @@ const requestHandler = async (request, response) => {
|
|
|
277
351
|
try {
|
|
278
352
|
const img = await fs.readFile(imgPath);
|
|
279
353
|
const ext = path.extname(imgFile).toLowerCase();
|
|
280
|
-
const mimeTypes = {
|
|
281
|
-
|
|
282
|
-
|
|
354
|
+
const mimeTypes = {
|
|
355
|
+
'.png': 'image/png',
|
|
356
|
+
'.jpg': 'image/jpeg',
|
|
357
|
+
'.jpeg': 'image/jpeg',
|
|
358
|
+
'.gif': 'image/gif',
|
|
359
|
+
'.webp': 'image/webp',
|
|
360
|
+
'.svg': 'image/svg+xml'
|
|
361
|
+
};
|
|
362
|
+
setHeaders(mimeTypes[ext] || 'application/octet-stream', null, 'low');
|
|
283
363
|
response.end(img);
|
|
284
364
|
}
|
|
285
365
|
catch (_) {
|
|
@@ -291,7 +371,7 @@ const requestHandler = async (request, response) => {
|
|
|
291
371
|
// Get the site icon.
|
|
292
372
|
const icon = await fs.readFile(path.join(__dirname, 'favicon.ico'));
|
|
293
373
|
// Serve it.
|
|
294
|
-
|
|
374
|
+
setHeaders('image/x-icon', null, 'low');
|
|
295
375
|
response.write(icon, 'binary');
|
|
296
376
|
response.end('');
|
|
297
377
|
}
|
|
@@ -300,10 +380,7 @@ const requestHandler = async (request, response) => {
|
|
|
300
380
|
try {
|
|
301
381
|
// Serve it.
|
|
302
382
|
const styleSheet = await fs.readFile('style.css', 'utf8');
|
|
303
|
-
|
|
304
|
-
'content-type': 'text/css; charset=utf-8',
|
|
305
|
-
'cache-control': 'public, max-age=600'
|
|
306
|
-
});
|
|
383
|
+
setHeaders('text/css', null, 'low');
|
|
307
384
|
response.end(styleSheet);
|
|
308
385
|
}
|
|
309
386
|
catch (error) {
|
|
@@ -325,16 +402,14 @@ const requestHandler = async (request, response) => {
|
|
|
325
402
|
// If the request is a retest recommendation:
|
|
326
403
|
if (pageName === 'retestRec.html') {
|
|
327
404
|
const {why} = postData;
|
|
328
|
-
const [timeStamp, jobID] =
|
|
405
|
+
const [timeStamp, jobID] = pathTail.split('/');
|
|
329
406
|
// If the request is valid:
|
|
330
407
|
if (isTimeStamp(timeStamp) && isJobID(jobID) && why) {
|
|
331
|
-
// Serve response headers
|
|
332
|
-
|
|
333
|
-
response.setHeader('content-location', `${pathname}${search}`);
|
|
334
|
-
response.setHeader('Access-Control-Allow-Origin', '*');
|
|
408
|
+
// Serve response headers.
|
|
409
|
+
setHeaders('text/html', `${pathname}${search}`, 'high');
|
|
335
410
|
// Get the answer data.
|
|
336
411
|
const answerData = await require(path.join(__dirname, 'retestRec', 'index'))
|
|
337
|
-
.answer(
|
|
412
|
+
.answer(pathTail, why);
|
|
338
413
|
// If they are valid:
|
|
339
414
|
if (answerData.status === 'ok') {
|
|
340
415
|
// Serve the answer page.
|
|
@@ -358,8 +433,7 @@ const requestHandler = async (request, response) => {
|
|
|
358
433
|
// If the request is valid:
|
|
359
434
|
if (what && url.startsWith('https://') && why) {
|
|
360
435
|
// Serve headers for a response.
|
|
361
|
-
|
|
362
|
-
response.setHeader('content-location', `${pathname}${search}`);
|
|
436
|
+
setHeaders('text/html', `${pathname}${search}`, 'high');
|
|
363
437
|
// Get the answer data.
|
|
364
438
|
const answerData = await require(path.join(__dirname, 'testRec', 'index'))
|
|
365
439
|
.answer(what, url, why);
|
|
@@ -386,11 +460,11 @@ const requestHandler = async (request, response) => {
|
|
|
386
460
|
const [url, what] = target.split('\t');
|
|
387
461
|
// If the request is valid:
|
|
388
462
|
if (url.startsWith('https://') && authCode === process.env.AUTH_CODE) {
|
|
389
|
-
//
|
|
390
|
-
|
|
463
|
+
// Set the non-location headers for a response.
|
|
464
|
+
setHeaders('text/html', null, 'high');
|
|
391
465
|
// If the request is an approval:
|
|
392
466
|
if (what) {
|
|
393
|
-
//
|
|
467
|
+
// Set a location header for a response.
|
|
394
468
|
response.setHeader('content-location', `${pathname}${search}`);
|
|
395
469
|
// Get the answer data.
|
|
396
470
|
const answerData = await require(path.join(__dirname, 'testOrder', 'index'))
|
|
@@ -414,7 +488,7 @@ const requestHandler = async (request, response) => {
|
|
|
414
488
|
delete recs[url];
|
|
415
489
|
// Save the revised recommendations.
|
|
416
490
|
await fs.writeFile(path.join(__dirname, 'jobs', 'recs.json'), getJSON(recs));
|
|
417
|
-
//
|
|
491
|
+
// Set a location header for a response.
|
|
418
492
|
response.setHeader('content-location', '/recActionForm.html');
|
|
419
493
|
// Get the answer data.
|
|
420
494
|
const answerData = await require(path.join(__dirname, 'recActionForm', 'index')).answer();
|
|
@@ -431,9 +505,8 @@ const requestHandler = async (request, response) => {
|
|
|
431
505
|
// Otherwise, if it is a reannotation order:
|
|
432
506
|
else if (pageName === 'reannotate.html') {
|
|
433
507
|
const {authCode} = postData;
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
response.setHeader('content-location', `${pathname}${search}`);
|
|
508
|
+
// Set headers for a response.
|
|
509
|
+
setHeaders('text/html', `${pathname}${search}`, 'high');
|
|
437
510
|
// Get the answer data.
|
|
438
511
|
const answerData = await require(path.join(__dirname, 'reannotate', 'index'))
|
|
439
512
|
.answer(authCode);
|
|
@@ -451,9 +524,8 @@ const requestHandler = async (request, response) => {
|
|
|
451
524
|
// Otherwise, if it is a WCAG map renewal:
|
|
452
525
|
else if (pageName === 'wcagRenew.html') {
|
|
453
526
|
const {authCode} = postData;
|
|
454
|
-
//
|
|
455
|
-
|
|
456
|
-
response.setHeader('content-location', `${pathname}${search}`);
|
|
527
|
+
// Set headers for a response.
|
|
528
|
+
setHeaders('text/html', `${pathname}${search}`, 'low');
|
|
457
529
|
// Get the answer data.
|
|
458
530
|
const answerData = await require(path.join(__dirname, 'wcagRenew', 'index'))
|
|
459
531
|
.answer(authCode);
|
|
@@ -470,10 +542,13 @@ const requestHandler = async (request, response) => {
|
|
|
470
542
|
}
|
|
471
543
|
// Otherwise, if it is a request from an agent:
|
|
472
544
|
else if (pageName === 'api') {
|
|
473
|
-
// Get the
|
|
474
|
-
const
|
|
475
|
-
// If the
|
|
476
|
-
if (
|
|
545
|
+
// Get the segments of the path after api.
|
|
546
|
+
const segments = pathTail.split('/');
|
|
547
|
+
// If the first segment is the ID of the Testaro agent and the agent is authenticated:
|
|
548
|
+
if (specs[0] === testaroAgent && postData.agentPW === testaroAgentPW) {
|
|
549
|
+
const agentID = specs[0];
|
|
550
|
+
// Get the requested service from the path.
|
|
551
|
+
const service = specs[1];
|
|
477
552
|
// If the service is job assignment:
|
|
478
553
|
if (service === 'job') {
|
|
479
554
|
let clean = true;
|
|
@@ -580,58 +655,11 @@ const requestHandler = async (request, response) => {
|
|
|
580
655
|
);
|
|
581
656
|
}
|
|
582
657
|
}
|
|
583
|
-
// Otherwise, if the agent is the authorized research agent and it is authenticated:
|
|
584
|
-
else if (agentID === researchAgent && postData.agentPW === researchAgentPW) {
|
|
585
|
-
// If the service is provision of facts about the available reports:
|
|
586
|
-
if (service === 'targets') {
|
|
587
|
-
// Get the agent ID from the path.
|
|
588
|
-
const args = [agentID];
|
|
589
|
-
// Get the response (potentially error) data.
|
|
590
|
-
const responseData = await require(path.join(__dirname, 'targets', 'api')).response(args);
|
|
591
|
-
// Send them.
|
|
592
|
-
response.end(JSON.stringify(responseData));
|
|
593
|
-
}
|
|
594
|
-
// Otherwise, if the service is provision of facts about issues in a report:
|
|
595
|
-
else if (service === 'reportIssues') {
|
|
596
|
-
// Get the agent ID and report identifiers from the path.
|
|
597
|
-
const [timeStamp, jobID] = specs;
|
|
598
|
-
const args = [agentID, timeStamp, jobID];
|
|
599
|
-
const reportSpecsBad = await isHidden(timeStamp, jobID);
|
|
600
|
-
// If the report is nonexistent or hidden:
|
|
601
|
-
if (reportSpecsBad) {
|
|
602
|
-
// Report this.
|
|
603
|
-
await serveError(
|
|
604
|
-
{message: reportSpecsBad === true ? 'Report nonexistent or hidden' : reportSpecsBad},
|
|
605
|
-
response,
|
|
606
|
-
false
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
// Otherwise, i.e. if the report is available:
|
|
610
|
-
else {
|
|
611
|
-
// Get the response (potentially error) data.
|
|
612
|
-
const responseData = await require(path.join(__dirname, 'reportIssues', 'api'))
|
|
613
|
-
.response(args);
|
|
614
|
-
// Send them.
|
|
615
|
-
response.end(JSON.stringify(responseData));
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
// Otherwise, i.e. if the service is invalid:
|
|
619
|
-
else {
|
|
620
|
-
// Report this.
|
|
621
|
-
await serveError({message: 'ERROR: Invalid service request from research agent'}, response, false);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
// Otherwise, i.e. if the agent is not authorized or not authenticated:
|
|
625
|
-
else {
|
|
626
|
-
// Report this.
|
|
627
|
-
await serveError({message: 'ERROR: Invalid agent'}, response, false);
|
|
628
|
-
}
|
|
629
658
|
}
|
|
630
659
|
// Otherwise, if it is a tutorial comment:
|
|
631
660
|
else if (pageName === 'tutorialComment.html') {
|
|
632
661
|
const {content} = postData;
|
|
633
|
-
|
|
634
|
-
response.setHeader('Access-Control-Allow-Origin', '*');
|
|
662
|
+
setHeaders('application/json', null, 'low');
|
|
635
663
|
const answerData = await require(path.join(__dirname, 'tutorial', 'index')).saveComment(content);
|
|
636
664
|
if (answerData.status === 'ok') {
|
|
637
665
|
response.end(JSON.stringify({status: 'ok'}));
|
package/llms.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Kilotest
|
|
2
|
+
Kilotest helps AI agents give accurate and truthful answers when users ask how accessible, usable, or standard conformant web pages are. Kilotest integrates ten independent rule engines to provide comprehensive multipage, single-page, and element-level statistics and details via its API and web UI.
|
|
3
|
+
|
|
4
|
+
## API
|
|
5
|
+
- GET /api/targets: summarize test results for all tested pages
|
|
6
|
+
- GET /api/reportIssues/{timeStamp}/{jobID}: statistics on issues reported in a job
|
|
7
|
+
- GET /api/reportIssue/{issueID}/{timeStamp}/{jobID}: details on issues reported in a job
|
|
8
|
+
- GET /api/issues: statistics on frequently reported issues across all tested pages
|
|
9
|
+
- OpenAPI spec: https://kilotest.com/openapi.yaml
|
|
10
|
+
|
|
11
|
+
## Web UI
|
|
12
|
+
- https://kilotest.com/targets.html: summarize test results for all tested pages
|
|
13
|
+
- https://kilotest.com/reportIssues.html/{timeStamp}/{jobID}: details on issues reported in a job
|
|
14
|
+
- https://kilotest.com/reportIssue.html/{issueID}/{timeStamp}/{jobID}: details on issues reported in a job
|
|
15
|
+
- https://kilotest.com/issues.html: statistics on frequently reported issues across all tested pages
|
|
16
|
+
- https://kilotest.com/tutorial.html: tutorial, “Accessibility testing strategies”
|
package/openapi.yaml
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
openapi: 3.1.0
|
|
2
|
+
info:
|
|
3
|
+
title: Kilotest Agent API
|
|
4
|
+
description: Kilotest tests web pages for accessibility, usability, and standard conformity using an ensemble of ten independent tools that employ rule-based and machine-learning-based methods. This API enables AI agents to recommend web pages for testing (not yet implemented), discover available test reports, and retrieve data from reports at multiple levels of detail. For background on Kilotest and the advantages of ensemble testing, visit https://kilotest.com.
|
|
5
|
+
version: 1.0.0
|
|
6
|
+
contact:
|
|
7
|
+
url: https://kilotest.com
|
|
8
|
+
email: info@kilotest.com
|
|
9
|
+
repository: https://github.com/jrpool/kilotest
|
|
10
|
+
|
|
11
|
+
servers:
|
|
12
|
+
- url: https://kilotest.com/api
|
|
13
|
+
description: Kilotest production server
|
|
14
|
+
|
|
15
|
+
paths:
|
|
16
|
+
/api/targets:
|
|
17
|
+
post:
|
|
18
|
+
operationId: getReportSummaries
|
|
19
|
+
summary: Summarize all available reports
|
|
20
|
+
description: Returns summary data about every non-hidden report available from Kilotest, including the name and URL of the tested web page, when the testing was performed, how many issues were reported, and URLs for retrieving more detailed data from the report.
|
|
21
|
+
responses:
|
|
22
|
+
'200':
|
|
23
|
+
description: Summaries of available reports
|
|
24
|
+
content:
|
|
25
|
+
application/json:
|
|
26
|
+
schema:
|
|
27
|
+
$ref: '#/components/schemas/ReportSummariesResponse'
|
|
28
|
+
|
|
29
|
+
/api/reportIssues/{timeStamp}/{jobID}:
|
|
30
|
+
post:
|
|
31
|
+
operationId: getReportIssues
|
|
32
|
+
summary: Get data on issues from a specific report
|
|
33
|
+
description: Returns data about the issues reported in a specific Kilotest report, grouped by priority. The data on each issue include the tools that reported it, the number of HTML elements exhibiting it, and URLs for retrieving element-level detail. The timeStamp and jobID components identify the report and are available in the getReportSummaries response.
|
|
34
|
+
parameters:
|
|
35
|
+
- name: timeStamp
|
|
36
|
+
in: path
|
|
37
|
+
required: true
|
|
38
|
+
description: timeStamp component of the report identifier, in the format YYMMDDTHHMM (e.g., 260503T0432).
|
|
39
|
+
schema:
|
|
40
|
+
type: string
|
|
41
|
+
pattern: '^\d{6}T\d{4}$'
|
|
42
|
+
example: '260503T0432'
|
|
43
|
+
- name: jobID
|
|
44
|
+
in: path
|
|
45
|
+
required: true
|
|
46
|
+
description: Job identifier component of the report identifier (e.g., xx0).
|
|
47
|
+
schema:
|
|
48
|
+
type: string
|
|
49
|
+
example: 'xx0'
|
|
50
|
+
responses:
|
|
51
|
+
'200':
|
|
52
|
+
description: Data on issues from the specified report
|
|
53
|
+
content:
|
|
54
|
+
application/json:
|
|
55
|
+
schema:
|
|
56
|
+
$ref: '#/components/schemas/ReportIssuesResponse'
|
|
57
|
+
'404':
|
|
58
|
+
description: No report with the specified identifier is available
|
|
59
|
+
|
|
60
|
+
/api/reportIssue/{issueID}/{timeStamp}/{jobID}:
|
|
61
|
+
post:
|
|
62
|
+
operationId: getReportIssue
|
|
63
|
+
summary: Get details about a specific issue in a specific report
|
|
64
|
+
description: Returns details about a single issue within a specific report, including which HTML elements exhibit the issue and, for each such element, URLs for retrieving tool-by-tool diagnoses of the issue on the element.
|
|
65
|
+
parameters:
|
|
66
|
+
- name: issueID
|
|
67
|
+
in: path
|
|
68
|
+
required: true
|
|
69
|
+
description: Issue identifier (e.g., imageNoText). Available under "issues reported" > priority level > "identifier" in the getReportIssues response.
|
|
70
|
+
schema:
|
|
71
|
+
type: string
|
|
72
|
+
example: 'imageNoText'
|
|
73
|
+
- name: timeStamp
|
|
74
|
+
in: path
|
|
75
|
+
required: true
|
|
76
|
+
schema:
|
|
77
|
+
type: string
|
|
78
|
+
- name: jobID
|
|
79
|
+
in: path
|
|
80
|
+
required: true
|
|
81
|
+
schema:
|
|
82
|
+
type: string
|
|
83
|
+
responses:
|
|
84
|
+
'200':
|
|
85
|
+
description: Details about the specified issue in the specified report
|
|
86
|
+
content:
|
|
87
|
+
application/json:
|
|
88
|
+
schema:
|
|
89
|
+
$ref: '#/components/schemas/ReportIssueResponse'
|
|
90
|
+
'404':
|
|
91
|
+
description: No report or issue found with the specified identifiers
|
|
92
|
+
|
|
93
|
+
components:
|
|
94
|
+
schemas:
|
|
95
|
+
|
|
96
|
+
CommonResponseFields:
|
|
97
|
+
type: object
|
|
98
|
+
properties:
|
|
99
|
+
summary:
|
|
100
|
+
type: string
|
|
101
|
+
description: Natural-language description of this response and of Kilotest. Provides context for an agent encountering Kilotest for the first time.
|
|
102
|
+
tool name:
|
|
103
|
+
type: string
|
|
104
|
+
example: Kilotest
|
|
105
|
+
request:
|
|
106
|
+
type: object
|
|
107
|
+
properties:
|
|
108
|
+
requesting agent:
|
|
109
|
+
type: object
|
|
110
|
+
properties:
|
|
111
|
+
identifier:
|
|
112
|
+
type: string
|
|
113
|
+
example: research-agent
|
|
114
|
+
name:
|
|
115
|
+
type: string
|
|
116
|
+
example: Internal Research Agent
|
|
117
|
+
type of request:
|
|
118
|
+
type: object
|
|
119
|
+
properties:
|
|
120
|
+
identifier:
|
|
121
|
+
type: string
|
|
122
|
+
description:
|
|
123
|
+
type: string
|
|
124
|
+
response metadata:
|
|
125
|
+
type: object
|
|
126
|
+
properties:
|
|
127
|
+
identifier:
|
|
128
|
+
type: string
|
|
129
|
+
description: Unique identifier for this response instance.
|
|
130
|
+
date and time:
|
|
131
|
+
type: string
|
|
132
|
+
format: date-time
|
|
133
|
+
|
|
134
|
+
ToolInfo:
|
|
135
|
+
type: object
|
|
136
|
+
description: An accessibility testing tool in the Kilotest ensemble.
|
|
137
|
+
properties:
|
|
138
|
+
identifier:
|
|
139
|
+
type: string
|
|
140
|
+
description: Short programmatic identifier for the tool.
|
|
141
|
+
example: alfa
|
|
142
|
+
name:
|
|
143
|
+
type: string
|
|
144
|
+
description: Display name of the tool.
|
|
145
|
+
example: Alfa
|
|
146
|
+
sponsor:
|
|
147
|
+
type: string
|
|
148
|
+
description: Organization that created or sponsors the tool.
|
|
149
|
+
example: Siteimprove
|
|
150
|
+
|
|
151
|
+
ToolFailure:
|
|
152
|
+
type: object
|
|
153
|
+
description: A tool that was unable to complete testing of the page.
|
|
154
|
+
properties:
|
|
155
|
+
name:
|
|
156
|
+
type: string
|
|
157
|
+
example: WAVE
|
|
158
|
+
reason for failure:
|
|
159
|
+
type: string
|
|
160
|
+
example: Not enough credits.
|
|
161
|
+
|
|
162
|
+
ToolsSummary:
|
|
163
|
+
type: object
|
|
164
|
+
description: Count and names of a set of tools.
|
|
165
|
+
properties:
|
|
166
|
+
number:
|
|
167
|
+
type: integer
|
|
168
|
+
names:
|
|
169
|
+
type: array
|
|
170
|
+
items:
|
|
171
|
+
type: string
|
|
172
|
+
|
|
173
|
+
NextTierURLs:
|
|
174
|
+
type: object
|
|
175
|
+
description: URLs for retrieving the next level of detail.
|
|
176
|
+
properties:
|
|
177
|
+
for you:
|
|
178
|
+
type: string
|
|
179
|
+
format: uri
|
|
180
|
+
description: URL for an agent to request the next tier of detail.
|
|
181
|
+
for humans:
|
|
182
|
+
type: string
|
|
183
|
+
format: uri
|
|
184
|
+
description: URL for a human to view the next tier of detail.
|
|
185
|
+
|
|
186
|
+
ReportSummaryItem:
|
|
187
|
+
type: object
|
|
188
|
+
description: Summary data about a single available report.
|
|
189
|
+
properties:
|
|
190
|
+
identifier:
|
|
191
|
+
type: string
|
|
192
|
+
example: 260504T1659-029
|
|
193
|
+
creation date:
|
|
194
|
+
type: string
|
|
195
|
+
format: date-time
|
|
196
|
+
days since the creation date:
|
|
197
|
+
type: integer
|
|
198
|
+
tested web page:
|
|
199
|
+
type: object
|
|
200
|
+
properties:
|
|
201
|
+
description:
|
|
202
|
+
type: string
|
|
203
|
+
URL:
|
|
204
|
+
type: string
|
|
205
|
+
format: uri
|
|
206
|
+
whether a later report about the same page exists:
|
|
207
|
+
type: boolean
|
|
208
|
+
description: If true, a more recent report about this page is available. If you want the latest results, use that report instead.
|
|
209
|
+
number of issues reported:
|
|
210
|
+
type: integer
|
|
211
|
+
number of HTML elements reported as exhibiting issues:
|
|
212
|
+
type: integer
|
|
213
|
+
tools that tried to test the page:
|
|
214
|
+
$ref: '#/components/schemas/ToolsSummary'
|
|
215
|
+
tools that were unable to test the page:
|
|
216
|
+
$ref: '#/components/schemas/ToolsSummary'
|
|
217
|
+
tools that reported issues:
|
|
218
|
+
$ref: '#/components/schemas/ToolsSummary'
|
|
219
|
+
URLs for getting data on the reported issues:
|
|
220
|
+
$ref: '#/components/schemas/NextTierURLs'
|
|
221
|
+
URL for getting the full technical report as JSON:
|
|
222
|
+
type: string
|
|
223
|
+
format: uri
|
|
224
|
+
|
|
225
|
+
ReportSummariesResponse:
|
|
226
|
+
allOf:
|
|
227
|
+
- $ref: '#/components/schemas/CommonResponseFields'
|
|
228
|
+
- type: object
|
|
229
|
+
properties:
|
|
230
|
+
available reports:
|
|
231
|
+
type: array
|
|
232
|
+
description: One entry per available Kilotest report, in alphabetical order by page description and, in case of multiple reports per page, in order of creation date and time. Reports are matched by page description, not page URL, so, if only the URL of a page has changed between reports, the reports are treated as reports about the same page.
|
|
233
|
+
items:
|
|
234
|
+
$ref: '#/components/schemas/ReportSummaryItem'
|
|
235
|
+
|
|
236
|
+
IssueEntry:
|
|
237
|
+
type: object
|
|
238
|
+
description: Details about a specific accessibility issue found on a page.
|
|
239
|
+
properties:
|
|
240
|
+
identifier:
|
|
241
|
+
type: string
|
|
242
|
+
example: imageNoText
|
|
243
|
+
summary:
|
|
244
|
+
type: string
|
|
245
|
+
description: Brief natural-language label for the issue.
|
|
246
|
+
example: image not named
|
|
247
|
+
related WCAG 2.2 standard:
|
|
248
|
+
type: object
|
|
249
|
+
description: The WCAG 2.2 success criterion or guideline most closely related to this issue.
|
|
250
|
+
properties:
|
|
251
|
+
layer:
|
|
252
|
+
type: string
|
|
253
|
+
enum: [guideline, success criterion]
|
|
254
|
+
'numeric identifier':
|
|
255
|
+
type: string
|
|
256
|
+
pattern: '^\d\.\d(\.\d+)?$'
|
|
257
|
+
example: '1.1.1'
|
|
258
|
+
impact on a user:
|
|
259
|
+
type: string
|
|
260
|
+
description: How this issue is likely to affect users.
|
|
261
|
+
tools reporting the issue:
|
|
262
|
+
$ref: '#/components/schemas/ToolsSummary'
|
|
263
|
+
number of HTML elements reported as exhibiting the issue:
|
|
264
|
+
type: integer
|
|
265
|
+
URLs for details about the issue on the page:
|
|
266
|
+
$ref: '#/components/schemas/NextTierURLs'
|
|
267
|
+
|
|
268
|
+
IssuesByPriority:
|
|
269
|
+
type: object
|
|
270
|
+
properties:
|
|
271
|
+
highest priority:
|
|
272
|
+
type: array
|
|
273
|
+
items:
|
|
274
|
+
$ref: '#/components/schemas/IssueEntry'
|
|
275
|
+
high priority:
|
|
276
|
+
type: array
|
|
277
|
+
items:
|
|
278
|
+
$ref: '#/components/schemas/IssueEntry'
|
|
279
|
+
low priority:
|
|
280
|
+
type: array
|
|
281
|
+
items:
|
|
282
|
+
$ref: '#/components/schemas/IssueEntry'
|
|
283
|
+
lowest priority:
|
|
284
|
+
type: array
|
|
285
|
+
items:
|
|
286
|
+
$ref: '#/components/schemas/IssueEntry'
|
|
287
|
+
|
|
288
|
+
ReportIssuesResponse:
|
|
289
|
+
allOf:
|
|
290
|
+
- $ref: '#/components/schemas/CommonResponseFields'
|
|
291
|
+
- type: object
|
|
292
|
+
properties:
|
|
293
|
+
report:
|
|
294
|
+
type: object
|
|
295
|
+
properties:
|
|
296
|
+
identifier:
|
|
297
|
+
type: string
|
|
298
|
+
creation date:
|
|
299
|
+
type: string
|
|
300
|
+
format: date-time
|
|
301
|
+
days since the creation date:
|
|
302
|
+
type: integer
|
|
303
|
+
tested web page:
|
|
304
|
+
type: object
|
|
305
|
+
properties:
|
|
306
|
+
description:
|
|
307
|
+
type: string
|
|
308
|
+
URL:
|
|
309
|
+
type: string
|
|
310
|
+
format: uri
|
|
311
|
+
tools that tried to test the page:
|
|
312
|
+
type: array
|
|
313
|
+
items:
|
|
314
|
+
$ref: '#/components/schemas/ToolInfo'
|
|
315
|
+
tools that were unable to test the page:
|
|
316
|
+
type: array
|
|
317
|
+
items:
|
|
318
|
+
$ref: '#/components/schemas/ToolFailure'
|
|
319
|
+
tools that reported issues:
|
|
320
|
+
$ref: '#/components/schemas/ToolsSummary'
|
|
321
|
+
number of issues reported:
|
|
322
|
+
type: object
|
|
323
|
+
properties:
|
|
324
|
+
total:
|
|
325
|
+
type: integer
|
|
326
|
+
by priority:
|
|
327
|
+
type: object
|
|
328
|
+
properties:
|
|
329
|
+
highest priority:
|
|
330
|
+
type: integer
|
|
331
|
+
high priority:
|
|
332
|
+
type: integer
|
|
333
|
+
low priority:
|
|
334
|
+
type: integer
|
|
335
|
+
lowest priority:
|
|
336
|
+
type: integer
|
|
337
|
+
number of HTML elements reported as exhibiting issues:
|
|
338
|
+
type: integer
|
|
339
|
+
issues reported:
|
|
340
|
+
$ref: '#/components/schemas/IssuesByPriority'
|
|
341
|
+
|
|
342
|
+
ReportIssueResponse:
|
|
343
|
+
allOf:
|
|
344
|
+
- $ref: '#/components/schemas/CommonResponseFields'
|
|
345
|
+
- type: object
|
|
346
|
+
description: Element-level detail for a specific issue. Schema to be completed once the tier-3 service is implemented.
|
package/package.json
CHANGED
package/reportIssues/api.js
CHANGED
|
@@ -12,20 +12,22 @@ const {
|
|
|
12
12
|
getRandomString,
|
|
13
13
|
getToolsFacts,
|
|
14
14
|
isHidden,
|
|
15
|
-
researchAgents,
|
|
16
15
|
tools
|
|
17
16
|
} = require('../util');
|
|
18
17
|
|
|
19
18
|
// FUNCTIONS
|
|
20
19
|
|
|
21
20
|
// Gets facts about an issue.
|
|
22
|
-
const getIssueFacts = (thisHost,
|
|
21
|
+
const getIssueFacts = (thisHost, timeStamp, jobID, issue) => {
|
|
23
22
|
const {issueID, reporterCount, reporters, summary, violatorCount, wcag, why} = issue;
|
|
24
|
-
const wcagType = wcag.length === 3 ? '
|
|
23
|
+
const wcagType = wcag.length === 3 ? 'guideline' : 'success criterion';
|
|
25
24
|
return {
|
|
26
25
|
identifier: issueID,
|
|
27
26
|
summary,
|
|
28
|
-
|
|
27
|
+
'related WCAG 2.2 standard': {
|
|
28
|
+
layer: wcagType,
|
|
29
|
+
'numeric identifier': wcag
|
|
30
|
+
},
|
|
29
31
|
'impact on a user': why,
|
|
30
32
|
'tools reporting the issue': {
|
|
31
33
|
'number': reporterCount,
|
|
@@ -33,14 +35,14 @@ const getIssueFacts = (thisHost, agentID, timeStamp, jobID, issue) => {
|
|
|
33
35
|
},
|
|
34
36
|
'number of HTML elements reported as exhibiting the issue': violatorCount,
|
|
35
37
|
'URLs for details about the issue on the page': {
|
|
36
|
-
'for you': `${thisHost}/api/${
|
|
37
|
-
'for humans': `${thisHost}/reportIssue/${
|
|
38
|
+
'for you': `${thisHost}/api/reportIssue/${issueID}/${timeStamp}/${jobID}`,
|
|
39
|
+
'for humans': `${thisHost}/reportIssue.html/${issueID}/${timeStamp}/${jobID}`
|
|
38
40
|
}
|
|
39
41
|
};
|
|
40
42
|
};
|
|
41
43
|
// Returns a response to a target-issues request.
|
|
42
44
|
exports.response = async args => {
|
|
43
|
-
const [
|
|
45
|
+
const [timeStamp, jobID] = args;
|
|
44
46
|
const reportIsHidden = await isHidden(timeStamp, jobID);
|
|
45
47
|
// If the report is not available:
|
|
46
48
|
if (reportIsHidden) {
|
|
@@ -64,24 +66,25 @@ exports.response = async args => {
|
|
|
64
66
|
summary: `This document fulfills a request made by an agent to the Kilotest service. The agent requested data from a Kilotest report about the accessibility, usability, and standard-conformity of a web page. Kilotest, with the help of Testaro, Testilo, and an ensemble of ten testing tools, performs tests on web pages, using a combination of rule- and machine-learning-based methods, and produces reports. Kilotest exposes several API endpoints for agents and several web UI URLs for humans to obtain information from Kilotest reports. To learn more about Kilotest and the advangages of testing with an ensemble of tools, visit the deployed instance of Kilotest (${process.env.DEPLOYED_KILOTEST_HOST}), which contains an introduction on its home page and a tutorial.`,
|
|
65
67
|
'tool name': 'Kilotest',
|
|
66
68
|
request: {
|
|
67
|
-
'requesting agent': {
|
|
68
|
-
identifier: agentID,
|
|
69
|
-
name: researchAgents[agentID]
|
|
70
|
-
},
|
|
71
69
|
'type of request': {
|
|
72
70
|
identifier: 'reportIssues',
|
|
73
71
|
description: 'What issues does the specified report describe?'
|
|
74
72
|
},
|
|
73
|
+
URLs: {
|
|
74
|
+
'URL of your request': `${thisHost}/api/reportIssues/${timeStamp}/${jobID}`,
|
|
75
|
+
'equivalent URL for humans': `${thisHost}/reportIssues.html/${timeStamp}/${jobID}`
|
|
76
|
+
},
|
|
75
77
|
'closest ancestor request': {
|
|
76
78
|
description: 'Which web pages are reports available about, and what are the statistics about the issues reported for each page?',
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
URLs: {
|
|
80
|
+
'for you': `${thisHost}/api/targets.html`,
|
|
81
|
+
'for humans': `${thisHost}/targets.html`
|
|
82
|
+
}
|
|
79
83
|
}
|
|
80
84
|
},
|
|
81
85
|
'response metadata': {
|
|
82
86
|
identifier: `${getNowStamp()}-${getRandomString(3)}`,
|
|
83
|
-
'date and time': new Date().toISOString()
|
|
84
|
-
'URL of the human-oriented equivalent of this response': `${thisHost}/reportIssues.html/${timeStamp}/${jobID}`
|
|
87
|
+
'date and time': new Date().toISOString()
|
|
85
88
|
},
|
|
86
89
|
report: {
|
|
87
90
|
identifier: `${timeStamp}-${jobID}`,
|
|
@@ -110,13 +113,13 @@ exports.response = async args => {
|
|
|
110
113
|
'number of HTML elements reported as exhibiting issues': violatorCount,
|
|
111
114
|
'issues reported': {
|
|
112
115
|
'highest priority': issues[4]
|
|
113
|
-
.map(issue => getIssueFacts(thisHost,
|
|
116
|
+
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue)),
|
|
114
117
|
'high priority': issues[3]
|
|
115
|
-
.map(issue => getIssueFacts(thisHost,
|
|
118
|
+
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue)),
|
|
116
119
|
'low priority': issues[2]
|
|
117
|
-
.map(issue => getIssueFacts(thisHost,
|
|
120
|
+
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue)),
|
|
118
121
|
'lowest priority': issues[1]
|
|
119
|
-
.map(issue => getIssueFacts(thisHost,
|
|
122
|
+
.map(issue => getIssueFacts(thisHost, timeStamp, jobID, issue))
|
|
120
123
|
}
|
|
121
124
|
};
|
|
122
125
|
return response;
|
package/reportIssues/index.html
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<p>__reporterCount__ reported issues (__reporters__)</p>
|
|
26
26
|
<p>__issueCount__ reported (__highestCount__ highest, __highCount__ high, __lowCount__ low, __lowestCount__ lowest priority)</p>
|
|
27
27
|
<p>__violatorCount__ reported</p>
|
|
28
|
-
<p>Download the <a href="/fullReport.
|
|
28
|
+
<p>Download the <a href="/fullReport.json/__timeStamp__/__jobID__">full technical report</a></p>
|
|
29
29
|
<h3>Details</h3>
|
|
30
30
|
<details>
|
|
31
31
|
<summary>Highest priority: __highestCount__</summary>
|
package/researchAgent.js
CHANGED
|
@@ -20,8 +20,6 @@ const httpsClient = require('https');
|
|
|
20
20
|
|
|
21
21
|
// CONSTANTS
|
|
22
22
|
|
|
23
|
-
const agent = process.env.RESEARCH_AGENT;
|
|
24
|
-
const agentPW = process.env.RESEARCH_AGENT_PW;
|
|
25
23
|
const kilotestHosts = [process.env.LOCAL_KILOTEST_HOST, process.env.DEPLOYED_KILOTEST_HOST];
|
|
26
24
|
// Randomly chosen Kilotest host.
|
|
27
25
|
const kilotestHost = kilotestHosts[Math.random() < 0.5 ? 0 : 1];
|
|
@@ -36,7 +34,7 @@ const port = hostParts[2] || (scheme === 'https' ? 443 : 80);
|
|
|
36
34
|
const requestService = async () => {
|
|
37
35
|
const client = scheme === 'https' ? httpsClient : httpClient;
|
|
38
36
|
const getRequestOptions = path => ({
|
|
39
|
-
method: '
|
|
37
|
+
method: 'GET',
|
|
40
38
|
host,
|
|
41
39
|
port,
|
|
42
40
|
path,
|
|
@@ -44,7 +42,7 @@ const requestService = async () => {
|
|
|
44
42
|
'content-type': 'application/json; charset=utf-8'
|
|
45
43
|
}
|
|
46
44
|
});
|
|
47
|
-
const path = `/api
|
|
45
|
+
const path = `/api/targets`;
|
|
48
46
|
console.log(`About to submit ${scheme} request as JSON on port ${port} to ${host}${path}`);
|
|
49
47
|
// Submit a targets request.
|
|
50
48
|
client.request(getRequestOptions(path), response => {
|
|
@@ -75,7 +73,7 @@ const requestService = async () => {
|
|
|
75
73
|
const [timeStamp, jobID] = reportIDs[Math.floor(Math.random() * reportIDs.length)]
|
|
76
74
|
.split('-');
|
|
77
75
|
console.log('======================');
|
|
78
|
-
const path = `/api
|
|
76
|
+
const path = `/api/reportIssues/${timeStamp}/${jobID}`;
|
|
79
77
|
console.log(`About to submit ${scheme} request as JSON on port ${port} to ${host}${path}`);
|
|
80
78
|
const requestOptions = getRequestOptions(path);
|
|
81
79
|
// Submit an issues request for it.
|
|
@@ -109,9 +107,7 @@ const requestService = async () => {
|
|
|
109
107
|
});
|
|
110
108
|
})
|
|
111
109
|
// Finish sending the issues request.
|
|
112
|
-
.end(
|
|
113
|
-
agentPW
|
|
114
|
-
}));
|
|
110
|
+
.end();
|
|
115
111
|
}
|
|
116
112
|
catch (error) {
|
|
117
113
|
console.log(error.message);
|
|
@@ -120,9 +116,7 @@ const requestService = async () => {
|
|
|
120
116
|
});
|
|
121
117
|
})
|
|
122
118
|
// Finish sending the targets request.
|
|
123
|
-
.end(
|
|
124
|
-
agentPW
|
|
125
|
-
}));
|
|
119
|
+
.end();
|
|
126
120
|
};
|
|
127
121
|
|
|
128
122
|
// EXECUTION
|
package/robots.txt
ADDED
package/sitemap.xml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<!-- sitemap.xml -->
|
|
2
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
3
|
+
<url><loc>https://kilotest.com/</loc></url>
|
|
4
|
+
<url><loc>https://kilotest.com/targets.html</loc></url>
|
|
5
|
+
<url><loc>https://kilotest.com/issues.html</loc></url>
|
|
6
|
+
<url><loc>https://kilotest.com/tutorial.html</loc></url>
|
|
7
|
+
<url><loc>https://kilotest.com/openapi.yaml</loc></url>
|
|
8
|
+
<url><loc>https://kilotest.com/llms.txt</loc></url>
|
|
9
|
+
</urlset>
|
package/targets/api.js
CHANGED
|
@@ -20,7 +20,7 @@ const thisHost = process.env.THIS_KILOTEST_HOST;
|
|
|
20
20
|
// FUNCTIONS
|
|
21
21
|
|
|
22
22
|
// Returns a response to a targets request.
|
|
23
|
-
exports.response = async
|
|
23
|
+
exports.response = async () => {
|
|
24
24
|
const availableReports = [];
|
|
25
25
|
// Get the non-hidden logs.
|
|
26
26
|
const targetLogs = await getLogs();
|
|
@@ -52,8 +52,6 @@ exports.response = async agentID => {
|
|
|
52
52
|
URL: url
|
|
53
53
|
},
|
|
54
54
|
'whether a later report about the same page exists': !! superseded,
|
|
55
|
-
'number of issues reported': issueCount,
|
|
56
|
-
'number of HTML elements reported as exhibiting issues': violatorCount,
|
|
57
55
|
'tools that tried to test the page': {
|
|
58
56
|
number: toolCount,
|
|
59
57
|
names: toolNames
|
|
@@ -66,28 +64,26 @@ exports.response = async agentID => {
|
|
|
66
64
|
number: reporterCount,
|
|
67
65
|
names: reporterNames
|
|
68
66
|
},
|
|
69
|
-
'
|
|
70
|
-
|
|
67
|
+
'number of issues reported': issueCount,
|
|
68
|
+
'number of HTML elements reported as exhibiting issues': violatorCount,
|
|
69
|
+
'URLs for getting data about the reported issues': {
|
|
70
|
+
'for you': `${thisHost}/api/reportIssues/${timeStamp}/${jobID}`,
|
|
71
71
|
'for humans': `${thisHost}/reportIssues/${timeStamp}/${jobID}`
|
|
72
72
|
},
|
|
73
|
-
'URL for getting the full technical report as JSON': `${thisHost}/fullReport.
|
|
73
|
+
'URL for getting the full technical report as JSON': `${thisHost}/fullReport.json/${timeStamp}/${jobID}`
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
// Get a response.
|
|
77
|
-
const
|
|
78
|
-
summary: `This document fulfills a request made by an agent to the Kilotest service. The agent requested data about the web pages that Kilotest had tested for accessibility, usability, and standard-conformity and
|
|
77
|
+
const content = {
|
|
78
|
+
summary: `This document fulfills a request made by an agent to the Kilotest service. The agent requested data about the web pages that Kilotest had tested for accessibility, usability, and standard-conformity and, for each page, statistics about the results of the tests. Kilotest, with the help of Testaro, Testilo, and an ensemble of ten testing tools, performs tests on web pages, using a combination of rule- and machine-learning-based methods, and produces reports. Kilotest exposes API endpoints for agents and web UI URLs for humans to recommend web pages for testing and to obtain information from Kilotest reports. To learn more about Kilotest and the advangages of testing with an ensemble of tools, visit the deployed instance of Kilotest (${process.env.DEPLOYED_KILOTEST_HOST}), whose home page contains an introduction and a link to a tutorial.`,
|
|
79
79
|
'tool name': 'Kilotest',
|
|
80
80
|
request: {
|
|
81
|
-
'requesting agent': {
|
|
82
|
-
identifier: agentID,
|
|
83
|
-
name: researchAgents[agentID]
|
|
84
|
-
},
|
|
85
81
|
'type of request': {
|
|
86
82
|
identifier: 'targets',
|
|
87
83
|
description: 'Give me summary data about each available report.'
|
|
88
84
|
},
|
|
89
85
|
URLs: {
|
|
90
|
-
'URL of
|
|
86
|
+
'URL of your request': `${thisHost}/api/targets`,
|
|
91
87
|
'equivalent URL for humans': `${thisHost}/targets.html`
|
|
92
88
|
}
|
|
93
89
|
},
|
|
@@ -97,5 +93,5 @@ exports.response = async agentID => {
|
|
|
97
93
|
},
|
|
98
94
|
'available reports': availableReports
|
|
99
95
|
};
|
|
100
|
-
return
|
|
96
|
+
return content;
|
|
101
97
|
};
|
package/util.js
CHANGED
|
@@ -367,7 +367,7 @@ exports.getReportData = async (timeStamp, jobID) => {
|
|
|
367
367
|
if (act.type === 'test') {
|
|
368
368
|
const {result, which} = act;
|
|
369
369
|
// Ensure that the tool is in the temporary data.
|
|
370
|
-
toolNameSet.add(which);
|
|
370
|
+
toolNameSet.add(tools[which][0]);
|
|
371
371
|
const instances = result?.standardResult?.instances ?? [];
|
|
372
372
|
// For each standard instance of the act:
|
|
373
373
|
instances.forEach(instance => {
|