@jrpool/kilotest 34.0.0 → 35.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 +15 -23
- package/llms-full.txt +21 -2
- package/llms.txt +5 -4
- package/mcp.js +24 -1
- package/openapi.yaml +141 -6
- package/package.json +4 -1
- package/reportIssue/api.js +164 -0
- package/researchAgent.js +24 -7
- package/testRecForm/api.js +6 -0
- package/util.js +2 -2
package/index.js
CHANGED
|
@@ -69,8 +69,6 @@ const claimedPath = path.join(jobsPath, 'claimed');
|
|
|
69
69
|
const failedPath = path.join(jobsPath, 'failed');
|
|
70
70
|
const testaroAgent = process.env.TESTARO_AGENT;
|
|
71
71
|
const testaroAgentPW = process.env.TESTARO_AGENT_PW;
|
|
72
|
-
const researchAgent = process.env.RESEARCH_AGENT;
|
|
73
|
-
const researchAgentPW = process.env.RESEARCH_AGENT_PW;
|
|
74
72
|
// Values that may require alerts.
|
|
75
73
|
const balancePath = path.join(__dirname, 'aiService0Balance.json');
|
|
76
74
|
const WAVE_THRESHOLD = Number(process.env.WAVE_BALANCE_THRESHOLD);
|
|
@@ -339,27 +337,21 @@ const requestHandler = async (request, response) => {
|
|
|
339
337
|
}
|
|
340
338
|
// Otherwise, if the service is provision of issue statistics for a report:
|
|
341
339
|
else if (service === 'reportIssues') {
|
|
342
|
-
// Get the
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const responseData = await require(path.join(__dirname, 'reportIssues', 'api'))
|
|
358
|
-
.response(specs);
|
|
359
|
-
// Send them.
|
|
360
|
-
setHeaders('application/json', null, 'high');
|
|
361
|
-
response.end(JSON.stringify(responseData));
|
|
362
|
-
}
|
|
340
|
+
// Get the response (potentially error) data.
|
|
341
|
+
const responseData = await require(path.join(__dirname, 'reportIssues', 'api'))
|
|
342
|
+
.response(specs);
|
|
343
|
+
// Send them.
|
|
344
|
+
setHeaders('application/json', null, 'high');
|
|
345
|
+
response.end(JSON.stringify(responseData));
|
|
346
|
+
}
|
|
347
|
+
// Otherwise, if the service is the description of an issue from a report:
|
|
348
|
+
else if (service === 'reportIssue') {
|
|
349
|
+
// Get the response (potentially error) data.
|
|
350
|
+
const responseData = await require(path.join(__dirname, 'reportIssue', 'api'))
|
|
351
|
+
.response(specs);
|
|
352
|
+
// Send them.
|
|
353
|
+
setHeaders('application/json', null, 'high');
|
|
354
|
+
response.end(JSON.stringify(responseData));
|
|
363
355
|
}
|
|
364
356
|
// Otherwise, i.e. if the service is invalid:
|
|
365
357
|
else {
|
package/llms-full.txt
CHANGED
|
@@ -95,6 +95,25 @@ The implementation is currently in an alpha phase and offers 4 tools:
|
|
|
95
95
|
- `timeStamp`: initial segment of report identifier
|
|
96
96
|
- `jobID`: final segment of report identifier
|
|
97
97
|
- source of parameters: response to a `summarizeQualityOfAllTestedWebPages` request
|
|
98
|
+
- `describeOneIssueFromOneReport`
|
|
99
|
+
- method: `GET`
|
|
100
|
+
- purpose: provide details about one issue from one report
|
|
101
|
+
- path: `/api/reportIssue/{issueID}/{timeStamp}/{jobID}`
|
|
102
|
+
- parameters
|
|
103
|
+
- `issueID`: identifier of the issue
|
|
104
|
+
- `timeStamp`: initial segment of report identifier
|
|
105
|
+
- `jobID`: final segment of report identifier
|
|
106
|
+
- source of parameters: response to a `describeQualityOfOneWebPage` request
|
|
107
|
+
- `describeOneViolatorOfOneIssueFromOneReport`
|
|
108
|
+
- method: `GET`
|
|
109
|
+
- purpose: provide diagnoses about one issue exhibited by one HTML element in one report [NOT YET IMPLEMENTED]
|
|
110
|
+
- path: `/api/diagnoses/{issueID}/{timeStamp}/{jobID}/{catalogIndex}`
|
|
111
|
+
- parameters
|
|
112
|
+
- `issueID`: identifier of the issue
|
|
113
|
+
- `timeStamp`: initial segment of report identifier
|
|
114
|
+
- `jobID`: final segment of report identifier
|
|
115
|
+
- `catalogIndex`: index of the catalog entry
|
|
116
|
+
- source of parameters: response to a `describeOneIssueFromOneReport` request
|
|
98
117
|
- `recommendQualityTestingOfOneWebPage`
|
|
99
118
|
- method: `POST`
|
|
100
119
|
- purpose: recommend quality testing of one web page about which no report is available
|
|
@@ -125,9 +144,9 @@ An [OpenAPI specification for Kilotest](https://kilotest.com/openapi.yaml) is av
|
|
|
125
144
|
|
|
126
145
|
## How to report results to users
|
|
127
146
|
|
|
128
|
-
When you use Kilotest tools, you are free to perform any appropriate interpretation and condensation of the results for presentation to users. However, users expect the sources of facts and interpretations to be truthfully represented. Do not meld facts from Kilotest tools with interpretations of your own
|
|
147
|
+
When you use Kilotest tools, you are free to perform any appropriate interpretation and condensation of the results for presentation to users. However, users expect the sources of facts and interpretations to be truthfully represented. Do not meld facts from Kilotest tools with interpretations of your own in a manner that implies attribution of both to Kilotest.
|
|
129
148
|
|
|
130
|
-
An example of a **poor** attribution is “Here are the results from Kilotest: …. The page is at risk of …. Among all the issues, … are critical and require immediate attention.”
|
|
149
|
+
An example of a **poor** attribution is “Here are the results from Kilotest: …. The page is at risk of …. Among all the issues, … are critical and require immediate attention.” This attribution is poor because it incorrectly implies interpretetations and advice to Kilotest.
|
|
131
150
|
|
|
132
151
|
An example of a **good** attribution is “Kilotest reported these issues: …. On the basis of these results, I conclude that there is a serious risk of …, and, if you are responsible for maintaining the page, I recommend ….”
|
|
133
152
|
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Kilotest
|
|
2
2
|
|
|
3
|
-
Kilotest helps language models and AI platforms to give thorough, accurate and truthful answers when users ask about the front-end quality (i.e. accessibility, usability, and standards conformity) of web pages. Kilotest integrates ten independent rule engines to test web
|
|
3
|
+
Kilotest helps language models and AI platforms to give thorough, accurate and truthful answers when users ask about the front-end quality (i.e. accessibility, usability, and standards conformity) of web pages. Kilotest integrates ten independent rule engines to test any public web page and creates a structured report of the results. Kilotest tools can provide multipage, single-page, and element-level test results.
|
|
4
4
|
|
|
5
5
|
In the API section below, all endpoints require `GET` requests except as noted.
|
|
6
6
|
|
|
@@ -8,9 +8,10 @@ There is a [detailed explanation of Kilotest and its capabilities for LLMs](http
|
|
|
8
8
|
|
|
9
9
|
## API
|
|
10
10
|
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
-
|
|
11
|
+
- [Find any available reports about a specified web page](/api/identifyReportsAboutMatchingWebPages)
|
|
12
|
+
- [Get summaries of all available reports](/api/targets)
|
|
13
|
+
- Get summarized test results from one report: `/api/reportIssues/{timeStamp}/{jobID}`
|
|
14
|
+
- Get details about one issue from one report: `/api/reportIssue/{issueID}/{timeStamp}/{jobID}`
|
|
14
15
|
- Recommend testing of a web page: `/api/testRecForm` (requires `POST` request)
|
|
15
16
|
- [OpenAPI specification for Kilotest](https://kilotest.com/openapi.yaml)
|
|
16
17
|
|
package/mcp.js
CHANGED
|
@@ -12,6 +12,7 @@ const {isReportAvailable, isURL} = require('./util');
|
|
|
12
12
|
const targetAPI = require('./target/api');
|
|
13
13
|
const targetsAPI = require('./targets/api');
|
|
14
14
|
const reportIssuesAPI = require('./reportIssues/api');
|
|
15
|
+
const reportIssueAPI = require('./reportIssue/api');
|
|
15
16
|
const testRecFormAPI = require('./testRecForm/api');
|
|
16
17
|
|
|
17
18
|
// FUNCTIONS
|
|
@@ -61,7 +62,7 @@ const createMCPServer = () => {
|
|
|
61
62
|
server.registerTool(
|
|
62
63
|
'describeQualityOfOneWebPage',
|
|
63
64
|
{
|
|
64
|
-
description: 'Returns data from a specified Kilotest report about issues
|
|
65
|
+
description: 'Returns data from a specified Kilotest report about issues for the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The required timeStamp and jobID parameters identify the report and are obtained from a summarizeQualityOfAllTestedWebPages response.',
|
|
65
66
|
inputSchema: {
|
|
66
67
|
timeStamp: z.string().describe('Report timestamp in YYMMDDTHHMM format, e.g. 260503T0432'),
|
|
67
68
|
jobID: z.string().describe('Job identifier, e.g. x9z')
|
|
@@ -79,6 +80,28 @@ const createMCPServer = () => {
|
|
|
79
80
|
return {content: [{type: 'text', text: JSON.stringify(result)}]};
|
|
80
81
|
}
|
|
81
82
|
);
|
|
83
|
+
server.registerTool(
|
|
84
|
+
'describeOneIssueFromOneReport',
|
|
85
|
+
{
|
|
86
|
+
description: 'Returns data from a specified Kilotest report about one of the issues for the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The required issueID, timeStamp, and jobID parameters identify the issue and the report and are obtained from a describeQualityOfOneWebPage response.',
|
|
87
|
+
inputSchema: {
|
|
88
|
+
issueID: z.string().describe('Issue identifier, e.g. contrastPoor'),
|
|
89
|
+
timeStamp: z.string().describe('Report timestamp in YYMMDDTHHMM format, e.g. 260503T0432'),
|
|
90
|
+
jobID: z.string().describe('Job identifier, e.g. x9z')
|
|
91
|
+
},
|
|
92
|
+
annotations: {
|
|
93
|
+
title: 'Describe one issue from one report',
|
|
94
|
+
readOnlyHint: true,
|
|
95
|
+
idempotentHint: true,
|
|
96
|
+
destructiveHint: false,
|
|
97
|
+
openWorldHint: false
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
async ({issueID, timeStamp, jobID}) => {
|
|
101
|
+
const result = await reportIssueAPI.response([issueID, timeStamp, jobID]);
|
|
102
|
+
return {content: [{type: 'text', text: JSON.stringify(result)}]};
|
|
103
|
+
}
|
|
104
|
+
);
|
|
82
105
|
server.registerTool(
|
|
83
106
|
'recommendQualityTestingOfOneWebPage',
|
|
84
107
|
{
|
package/openapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest Agent API
|
|
4
|
-
description: Kilotest tests web pages for front-end quality (i.e. accessibility, usability, and standards conformity) using an ensemble of ten independent rule engines that employ rule-based and machine-learning-based methods. This API enables AI agents to recommend web pages for testing, discover available test reports, and retrieve data from reports. For background on Kilotest and the advantages of ensemble testing, visit https://kilotest.com.
|
|
4
|
+
description: Kilotest tests web pages for front-end quality (i.e. accessibility, usability, and standards conformity) using an ensemble of ten independent rule engines that employ rule-based and machine-learning-based methods. This API enables AI agents to recommend web pages for testing, discover available test reports, and retrieve data from reports. Each report describes results of one job that tested one web page. For background on Kilotest and the advantages of ensemble testing, visit https://kilotest.com.
|
|
5
5
|
version: 1.0.0
|
|
6
6
|
contact:
|
|
7
7
|
name: Kilotest
|
|
@@ -101,9 +101,9 @@ paths:
|
|
|
101
101
|
|
|
102
102
|
/api/reportIssue/{issueID}/{timeStamp}/{jobID}:
|
|
103
103
|
get:
|
|
104
|
-
operationId:
|
|
105
|
-
summary: Describe
|
|
106
|
-
description: Get issue-specific data from a specified report about the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The data describe the issue, all of the HTML elements of the page that have the issue, and, for each such element, URLs for retrieving diagnoses by rule engines of the issue on the element.
|
|
104
|
+
operationId: describeOneIssueFromOneReport
|
|
105
|
+
summary: Describe one issue from one report
|
|
106
|
+
description: Get issue-specific data from a specified report about the front-end quality (i.e. accessibility, usability, and standards conformity) of a web page. The data describe the issue, all of the HTML elements of the page that have the issue, and, for each such element, URLs for retrieving diagnoses by rule engines of the issue on the element.
|
|
107
107
|
parameters:
|
|
108
108
|
- name: issueID
|
|
109
109
|
in: path
|
|
@@ -118,14 +118,18 @@ paths:
|
|
|
118
118
|
required: true
|
|
119
119
|
schema:
|
|
120
120
|
type: string
|
|
121
|
+
examples:
|
|
122
|
+
- 20271124T1647
|
|
121
123
|
- name: jobID
|
|
122
124
|
in: path
|
|
123
125
|
required: true
|
|
124
126
|
schema:
|
|
125
127
|
type: string
|
|
128
|
+
examples:
|
|
129
|
+
- x58
|
|
126
130
|
responses:
|
|
127
131
|
'200':
|
|
128
|
-
description: Details about the specified issue
|
|
132
|
+
description: Details about the specified issue from the specified report
|
|
129
133
|
content:
|
|
130
134
|
application/json:
|
|
131
135
|
schema:
|
|
@@ -440,8 +444,139 @@ components:
|
|
|
440
444
|
issues reported:
|
|
441
445
|
$ref: '#/components/schemas/IssuesByPriority'
|
|
442
446
|
|
|
447
|
+
HTMLElementDetail:
|
|
448
|
+
type: object
|
|
449
|
+
description: Details about an HTML element reported as exhibiting an issue.
|
|
450
|
+
properties:
|
|
451
|
+
identifier:
|
|
452
|
+
type: string
|
|
453
|
+
description: Unique identifier of the element in the catalog of the report.
|
|
454
|
+
tag name:
|
|
455
|
+
type: string
|
|
456
|
+
examples:
|
|
457
|
+
- IMG
|
|
458
|
+
id attribute:
|
|
459
|
+
type: string
|
|
460
|
+
description: Value of the element's id attribute, if any.
|
|
461
|
+
start tag:
|
|
462
|
+
type: string
|
|
463
|
+
description: The opening tag of the element, including attributes.
|
|
464
|
+
inner text:
|
|
465
|
+
type: string
|
|
466
|
+
description: First and last lines of the inner text, if any, of the element.
|
|
467
|
+
inner text usable as a text fragment for linking:
|
|
468
|
+
type: boolean
|
|
469
|
+
description: Whether the inner text can be used as a text fragment for URL linking.
|
|
470
|
+
x, y, width, height in pixels:
|
|
471
|
+
type: array
|
|
472
|
+
items:
|
|
473
|
+
type: string
|
|
474
|
+
description: Bounding box coordinates and dimensions of the element.
|
|
475
|
+
XPath:
|
|
476
|
+
type: string
|
|
477
|
+
description: XPath of the element in the document.
|
|
478
|
+
|
|
443
479
|
ReportIssueResponse:
|
|
444
480
|
allOf:
|
|
445
481
|
- $ref: '#/components/schemas/CommonResponseFields'
|
|
446
482
|
- type: object
|
|
447
|
-
|
|
483
|
+
properties:
|
|
484
|
+
report:
|
|
485
|
+
type: object
|
|
486
|
+
properties:
|
|
487
|
+
identifier:
|
|
488
|
+
type: string
|
|
489
|
+
examples:
|
|
490
|
+
- 20280216T1353-4r9
|
|
491
|
+
creation date:
|
|
492
|
+
type: string
|
|
493
|
+
format: date-time
|
|
494
|
+
days since the creation date:
|
|
495
|
+
type: integer
|
|
496
|
+
tested web page:
|
|
497
|
+
type: object
|
|
498
|
+
properties:
|
|
499
|
+
description:
|
|
500
|
+
type: string
|
|
501
|
+
examples:
|
|
502
|
+
- ARQ Space Exploration Corporation
|
|
503
|
+
- Edlir Velaj PF
|
|
504
|
+
URL:
|
|
505
|
+
type: string
|
|
506
|
+
format: uri
|
|
507
|
+
examples:
|
|
508
|
+
- https://tiendalibros.com/
|
|
509
|
+
rule engines that tried to test the page:
|
|
510
|
+
type: array
|
|
511
|
+
items:
|
|
512
|
+
$ref: '#/components/schemas/RuleEngineInfo'
|
|
513
|
+
rule engines that were unable to test the page:
|
|
514
|
+
type: array
|
|
515
|
+
items:
|
|
516
|
+
$ref: '#/components/schemas/RuleEngineFailure'
|
|
517
|
+
rule engines that reported issues:
|
|
518
|
+
$ref: '#/components/schemas/RuleEnginesSummary'
|
|
519
|
+
number of issues reported:
|
|
520
|
+
type: object
|
|
521
|
+
properties:
|
|
522
|
+
total:
|
|
523
|
+
type: integer
|
|
524
|
+
by priority:
|
|
525
|
+
type: object
|
|
526
|
+
properties:
|
|
527
|
+
highest priority:
|
|
528
|
+
type: integer
|
|
529
|
+
high priority:
|
|
530
|
+
type: integer
|
|
531
|
+
low priority:
|
|
532
|
+
type: integer
|
|
533
|
+
lowest priority:
|
|
534
|
+
type: integer
|
|
535
|
+
number of HTML elements reported as exhibiting issues:
|
|
536
|
+
type: integer
|
|
537
|
+
level of the issue:
|
|
538
|
+
type: integer
|
|
539
|
+
enum:
|
|
540
|
+
- 1
|
|
541
|
+
- 2
|
|
542
|
+
- 3
|
|
543
|
+
- 4
|
|
544
|
+
description: Priority level of the issue (4 = highest, 1 = lowest).
|
|
545
|
+
facts about the issue:
|
|
546
|
+
type: object
|
|
547
|
+
description: Details about the specified issue and the HTML elements exhibiting it.
|
|
548
|
+
properties:
|
|
549
|
+
identifier:
|
|
550
|
+
type: string
|
|
551
|
+
examples:
|
|
552
|
+
- imageNoText
|
|
553
|
+
summary:
|
|
554
|
+
type: string
|
|
555
|
+
description: Brief natural-language label for the issue.
|
|
556
|
+
examples:
|
|
557
|
+
- image not named
|
|
558
|
+
related WCAG 2.2 standard:
|
|
559
|
+
type: object
|
|
560
|
+
description: The WCAG 2.2 success criterion or guideline most closely related to the issue.
|
|
561
|
+
properties:
|
|
562
|
+
layer:
|
|
563
|
+
type: string
|
|
564
|
+
enum:
|
|
565
|
+
- guideline
|
|
566
|
+
- success criterion
|
|
567
|
+
'numeric identifier':
|
|
568
|
+
type: string
|
|
569
|
+
pattern: '^\d\.\d(\.\d+)?$'
|
|
570
|
+
examples:
|
|
571
|
+
- 1.1.1
|
|
572
|
+
impact on a user:
|
|
573
|
+
type: string
|
|
574
|
+
description: How this issue is likely to affect users.
|
|
575
|
+
rule engines reporting the issue:
|
|
576
|
+
$ref: '#/components/schemas/RuleEnginesSummary'
|
|
577
|
+
number of HTML elements reported as exhibiting the issue:
|
|
578
|
+
type: integer
|
|
579
|
+
HTML elements reported as exhibiting the issue:
|
|
580
|
+
type: array
|
|
581
|
+
items:
|
|
582
|
+
$ref: '#/components/schemas/HTMLElementDetail'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jrpool/kilotest",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "35.0.0",
|
|
4
4
|
"description": "An ensemble testing service with a focus on accessibility",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,5 +36,8 @@
|
|
|
36
36
|
"@eslint/markdown": "^7.5.1",
|
|
37
37
|
"eslint": "^10.0.3",
|
|
38
38
|
"globals": "^17.4.0"
|
|
39
|
+
},
|
|
40
|
+
"allowScripts": {
|
|
41
|
+
"fsevents@2.3.2": true
|
|
39
42
|
}
|
|
40
43
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/*
|
|
2
|
+
api.js
|
|
3
|
+
Responds to the report-issue request.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
const {getData} = require('../reportIssues/util');
|
|
9
|
+
const {
|
|
10
|
+
getDateTime,
|
|
11
|
+
getNowStamp,
|
|
12
|
+
getRandomString,
|
|
13
|
+
getReport,
|
|
14
|
+
getToolsFacts,
|
|
15
|
+
isHidden,
|
|
16
|
+
tools
|
|
17
|
+
} = require('../util');
|
|
18
|
+
|
|
19
|
+
// FUNCTIONS
|
|
20
|
+
|
|
21
|
+
// Gets facts about an issue.
|
|
22
|
+
const getIssueFacts = async (timeStamp, jobID, issue) => {
|
|
23
|
+
const {issueID, reporterCount, reporters, summary, violatorCount, wcag, why} = issue;
|
|
24
|
+
const wcagType = wcag.length === 3 ? 'guideline' : 'success criterion';
|
|
25
|
+
// Get the report.
|
|
26
|
+
const report = await getReport(timeStamp, jobID);
|
|
27
|
+
const violatorIndexSet = new Set();
|
|
28
|
+
const {acts, catalog} = report;
|
|
29
|
+
// For each act of the report:
|
|
30
|
+
acts.forEach(act => {
|
|
31
|
+
// If it is a test act:
|
|
32
|
+
if (act.type === 'test') {
|
|
33
|
+
const {result} = act;
|
|
34
|
+
const instances = result?.standardResult?.instances ?? [];
|
|
35
|
+
// For each standard instance of the act:
|
|
36
|
+
instances.forEach(instance => {
|
|
37
|
+
const {catalogIndex} = instance;
|
|
38
|
+
// If its issue is the issue to be described and the instance has a catalog index:
|
|
39
|
+
if (instance.issueID === issueID && catalogIndex) {
|
|
40
|
+
// Ensure that the violator is in the temporary data.
|
|
41
|
+
violatorIndexSet.add(catalogIndex);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
identifier: issueID,
|
|
48
|
+
summary,
|
|
49
|
+
'related WCAG 2.2 standard': {
|
|
50
|
+
layer: wcagType,
|
|
51
|
+
'numeric identifier': wcag
|
|
52
|
+
},
|
|
53
|
+
'impact on a user': why,
|
|
54
|
+
'rule engines reporting the issue': {
|
|
55
|
+
'number': reporterCount,
|
|
56
|
+
'names': reporters.map(tool => tool.toolName)
|
|
57
|
+
},
|
|
58
|
+
'number of HTML elements reported as exhibiting the issue': violatorCount,
|
|
59
|
+
'HTML elements reported as exhibiting the issue': Array.from(violatorIndexSet).map(index => ({
|
|
60
|
+
identifier: String(index),
|
|
61
|
+
'tag name': catalog[index].tagName,
|
|
62
|
+
'id attribute': catalog[index].id,
|
|
63
|
+
'start tag': catalog[index].startTag,
|
|
64
|
+
'inner text': catalog[index].text,
|
|
65
|
+
'inner text usable as a text fragment for linking': catalog[index].textLinkable,
|
|
66
|
+
'x, y, width, height in pixels': catalog[index].boxID.split(':'),
|
|
67
|
+
'XPath': catalog[index].pathID
|
|
68
|
+
}))
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
// Returns a response to a report-issue request.
|
|
72
|
+
exports.response = async args => {
|
|
73
|
+
const [issueID, timeStamp, jobID] = args;
|
|
74
|
+
const reportIsHidden = await isHidden(timeStamp, jobID);
|
|
75
|
+
// If the report is not available:
|
|
76
|
+
if (reportIsHidden) {
|
|
77
|
+
// Return this.
|
|
78
|
+
return {
|
|
79
|
+
status: 'error',
|
|
80
|
+
message: 'Report not available'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Otherwise, i.e. if the report is available, get data on the report and its issues.
|
|
84
|
+
const data = await getData(timeStamp, jobID);
|
|
85
|
+
const {pageData, issuesData} = data;
|
|
86
|
+
const {what, url, daysAgo} = pageData;
|
|
87
|
+
const {issueCount, issues, preventions, reporterCount, reporters, violatorCount} = issuesData;
|
|
88
|
+
let issue;
|
|
89
|
+
// Get the level of and the data on the issue.
|
|
90
|
+
const issueLevel = [4, 3, 2, 1].find(level => {
|
|
91
|
+
issue = issues[level].find(issue => issue.issueID === issueID);
|
|
92
|
+
return issue;
|
|
93
|
+
});
|
|
94
|
+
// If the issue was not one of those in the report:
|
|
95
|
+
if (! issue) {
|
|
96
|
+
// Return this.
|
|
97
|
+
return {
|
|
98
|
+
status: 'error',
|
|
99
|
+
message: 'Issue not found in the report'
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const preventedTools = Object.entries(preventions).map(prevention => ({
|
|
103
|
+
name: tools[prevention[0]][0],
|
|
104
|
+
'reason for failure': prevention[1]
|
|
105
|
+
}));
|
|
106
|
+
const thisHost = process.env.THIS_KILOTEST_HOST;
|
|
107
|
+
// Get a response.
|
|
108
|
+
const content = {
|
|
109
|
+
summary: `This document fulfills a request made by a language model to a Kilotest tool. The model requested data, drawn from a Kilotest report, about one of the issues for the front-end quality (i.e. accessibility, usability, and standard-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 advangages 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.`,
|
|
110
|
+
'tool collection name': 'Kilotest',
|
|
111
|
+
'tool name': 'describeOneIssueFromOneReport',
|
|
112
|
+
request: {
|
|
113
|
+
'type of request': {
|
|
114
|
+
identifier: 'reportIssue',
|
|
115
|
+
description: 'Describe one issue from one repeort.'
|
|
116
|
+
},
|
|
117
|
+
method: 'GET',
|
|
118
|
+
URLs: {
|
|
119
|
+
'URL of your request': `${thisHost}/api/reportIssue/${issueID}/${timeStamp}/${jobID}`,
|
|
120
|
+
'equivalent URL for humans': `${thisHost}/reportIssue.html/${issueID}/${timeStamp}/${jobID}`
|
|
121
|
+
},
|
|
122
|
+
'closest ancestor request': {
|
|
123
|
+
identifier: 'describeQualityOfOneWebPage',
|
|
124
|
+
description: 'Describe the quality of one web page.',
|
|
125
|
+
URLs: {
|
|
126
|
+
'for you': `${thisHost}/api/reportIssues`,
|
|
127
|
+
'for humans': `${thisHost}/reportIssues.html`
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
'response metadata': {
|
|
132
|
+
identifier: `${getNowStamp()}-${getRandomString(3)}`,
|
|
133
|
+
'date and time': new Date().toISOString()
|
|
134
|
+
},
|
|
135
|
+
report: {
|
|
136
|
+
identifier: `${timeStamp}-${jobID}`,
|
|
137
|
+
'creation date': getDateTime(timeStamp),
|
|
138
|
+
'days since the creation date': daysAgo
|
|
139
|
+
},
|
|
140
|
+
'tested web page': {
|
|
141
|
+
description: what,
|
|
142
|
+
URL: url
|
|
143
|
+
},
|
|
144
|
+
'rule engines that tried to test the page': getToolsFacts(Object.keys(tools)),
|
|
145
|
+
'rule engines that were unable to test the page': preventedTools,
|
|
146
|
+
'rule engines that reported issues': {
|
|
147
|
+
number: reporterCount,
|
|
148
|
+
names: reporters.map(tool => tool.toolName)
|
|
149
|
+
},
|
|
150
|
+
'number of issues reported': {
|
|
151
|
+
total: issueCount,
|
|
152
|
+
'by priority': {
|
|
153
|
+
'highest priority': issues[4].length,
|
|
154
|
+
'high priority': issues[3].length,
|
|
155
|
+
'low priority': issues[2].length,
|
|
156
|
+
'lowest priority': issues[1].length
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
'number of HTML elements reported as exhibiting issues': violatorCount,
|
|
160
|
+
'level of the issue': issueLevel,
|
|
161
|
+
'facts about the issue': await getIssueFacts(timeStamp, jobID, issue)
|
|
162
|
+
};
|
|
163
|
+
return content;
|
|
164
|
+
};
|
package/researchAgent.js
CHANGED
|
@@ -61,7 +61,7 @@ const getContent = async response => {
|
|
|
61
61
|
resolve(content);
|
|
62
62
|
}
|
|
63
63
|
// If it is not JSON:
|
|
64
|
-
catch
|
|
64
|
+
catch {
|
|
65
65
|
// Return this.
|
|
66
66
|
resolve({error: `Response content not JSON (${contentString})`});
|
|
67
67
|
}
|
|
@@ -139,9 +139,7 @@ const requestService = async () => {
|
|
|
139
139
|
if (content.error) {
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
|
-
console.log(
|
|
143
|
-
'======================\nRequest 4: Summarize the issues in a report'
|
|
144
|
-
);
|
|
142
|
+
console.log('======================\nRequest 4: Summarize the issues in a report');
|
|
145
143
|
[timeStamp, jobID] = report.identifier?.split('-') ?? ['', ''];
|
|
146
144
|
if (! (timeStamp && jobID)) {
|
|
147
145
|
return;
|
|
@@ -152,7 +150,26 @@ const requestService = async () => {
|
|
|
152
150
|
if (content.error) {
|
|
153
151
|
return;
|
|
154
152
|
}
|
|
155
|
-
console.log('======================\nRequest 5:
|
|
153
|
+
console.log('======================\nRequest 5: Describe one issue from one report');
|
|
154
|
+
if (content['number of HTML elements reported as exhibiting issues'] === 0) {
|
|
155
|
+
console.log('reportIssue request cannot be submitted, because no issues were reported');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
// Get the issue IDs.
|
|
159
|
+
const issueIDs = Object
|
|
160
|
+
.values(content['issues reported'])
|
|
161
|
+
.map(levelIssues => levelIssues.map(issue => issue.identifier))
|
|
162
|
+
.flat();
|
|
163
|
+
// Choose one at random.
|
|
164
|
+
const issueID = issueIDs[Math.floor(Math.random() * issueIDs.length)];
|
|
165
|
+
method = 'GET';
|
|
166
|
+
path = `/api/reportIssue/${issueID}/${timeStamp}/${jobID}`;
|
|
167
|
+
content = await submitRequest(results, path, method);
|
|
168
|
+
if (content.error) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
console.log('======================\nRequest 6: Make a permitted test recommendation');
|
|
156
173
|
method = 'POST';
|
|
157
174
|
path = '/api/testRecForm';
|
|
158
175
|
content = await submitRequest(results, path, method, {
|
|
@@ -163,8 +180,8 @@ const requestService = async () => {
|
|
|
163
180
|
if (content.error) {
|
|
164
181
|
return;
|
|
165
182
|
}
|
|
166
|
-
console.log('======================\nRequest
|
|
167
|
-
|
|
183
|
+
console.log('======================\nRequest 7: Make an illicit test recommendation');
|
|
184
|
+
await submitRequest(results, path, method, {
|
|
168
185
|
'description of the web page': description,
|
|
169
186
|
'URL of the web page': url,
|
|
170
187
|
'reason for testing the web page': 'Just testing'
|
package/testRecForm/api.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// IMPORTS
|
|
7
7
|
|
|
8
|
+
const {sendAlert} = require('../alerts')
|
|
8
9
|
const {
|
|
9
10
|
getNowStamp,
|
|
10
11
|
getRandomString,
|
|
@@ -18,6 +19,11 @@ exports.response = async (what, url, why) => {
|
|
|
18
19
|
const thisHost = process.env.THIS_KILOTEST_HOST;
|
|
19
20
|
// Record the recommendation.
|
|
20
21
|
await updateRecs(what, url, why);
|
|
22
|
+
// Alert a manager about it.
|
|
23
|
+
await sendAlert(
|
|
24
|
+
`Kilotest: new test recommendation in the API`,
|
|
25
|
+
`Target: ${what}\nURL: ${url}\nReason: ${why}`
|
|
26
|
+
);
|
|
21
27
|
// Get a response.
|
|
22
28
|
const content = {
|
|
23
29
|
summary: `This response acknowledges a request made by a language model to a Kilotest tool. The model recommended that Kilotest test, for the first time, the ${what} web page at ${url} for front-end quality (i.e. accessibility, usability, and standards conformity). A Kilotest manager usually approves a recommendation within a day. When the recommendation is approved, the testing will be performed and results will become available. You can check for the availability of the results at ${thisHost}/api/targets. Kilotest performs its testing with the help of Testaro, Testilo, and an ensemble of ten rule engines, using a combination of rule- and machine-learning-based methods. Kilotest exposes several API endpoints and several web UI URLs to obtain information from Kilotest reports. To learn more about Kilotest and the advangages 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.`,
|
package/util.js
CHANGED
|
@@ -640,7 +640,7 @@ const updateRecs = exports.updateRecs = async (what, url, why) => {
|
|
|
640
640
|
// Save the revised recommendations.
|
|
641
641
|
await fs.writeFile(recsPath, getJSON(recs));
|
|
642
642
|
};
|
|
643
|
-
// Processes a test or retest recommendation.
|
|
643
|
+
// Processes a test or retest recommendation in the UI.
|
|
644
644
|
exports.processRec = async (testType, dirName, what, url, why) => {
|
|
645
645
|
// If the recommendation is valid:
|
|
646
646
|
if (
|
|
@@ -658,7 +658,7 @@ exports.processRec = async (testType, dirName, what, url, why) => {
|
|
|
658
658
|
console.log(`Test recommendation received for ${what}: ${plainWhy}`);
|
|
659
659
|
// Alert a manager about it.
|
|
660
660
|
await sendAlert(
|
|
661
|
-
`Kilotest: new ${testType} recommendation`,
|
|
661
|
+
`Kilotest: new ${testType} recommendation in the UI`,
|
|
662
662
|
`Target: ${what}\nURL: ${url}\nReason: ${plainWhy}`
|
|
663
663
|
);
|
|
664
664
|
// Get the template.
|