@jrpool/kilotest 46.0.11 → 46.1.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/.devin/config.local.json +22 -1
- package/.github/workflows/ci.yml +1 -1
- package/.github/workflows/publish-mcp.yml +1 -1
- package/README.md +16 -8
- package/api/requestFeature.test.js +6 -2
- package/api/requestRetest.test.js +6 -2
- package/api/requestTest.test.js +6 -2
- package/api/util.js +1 -1
- package/capability.md +51 -0
- package/checkinfo.js +146 -0
- package/docs/AI-TOOLS.md +64 -151
- package/docs/SERVICE.md +1 -1
- package/index.html +4 -3
- package/index.js +27 -19
- package/index.test.js +41 -17
- package/lhm.plugin.json +3218 -0
- package/llms-full.txt +7 -7
- package/llms.txt +2 -1
- package/openapi.yaml +1 -1
- package/package.json +5 -4
- package/scripts/generate-openapi.js +1 -1
- package/smokeTest.js +28 -25
- package/util.test.js +2 -2
- package/web/listReports/listReports.test.js +1 -1
- package/web/reannotate/reannotate.test.js +2 -2
- package/web/requestTest/requestTest.test.js +3 -3
- package/web/rewindReportsForm/rewindReportsForm.test.js +1 -1
- package/web/tutorial/index.html +3 -3
- package/web/tutorial/tutorial.test.js +4 -4
package/llms-full.txt
CHANGED
|
@@ -22,10 +22,10 @@ To provide a more inexpensive, thorough, and accurate assessment, the agent coul
|
|
|
22
22
|
|
|
23
23
|
The tool that tests web pages for front-end quality has attributes that no other such tool has. It:
|
|
24
24
|
|
|
25
|
-
- selects an ensemble of
|
|
25
|
+
- selects an ensemble of 12 rule engines that implement tests for, in total, more than a thousand rules for front-end quality
|
|
26
26
|
- runs the tests of the rule engines
|
|
27
27
|
- combines the reports of the rule engines into a single integrated report
|
|
28
|
-
- consolidates the 1000+ rules having
|
|
28
|
+
- consolidates the 1000+ rules having 12 different naming systems into 300+ “issues” with a uniform naming system
|
|
29
29
|
|
|
30
30
|
The tools that report test results extract from reports statistics and details at various levels of granularity, as required by an agent to fulfill particular user requests.
|
|
31
31
|
|
|
@@ -46,9 +46,9 @@ The rule engines in the Kilotest ensemble take responsibility for determining wh
|
|
|
46
46
|
|
|
47
47
|
## Kilotest versus other MCP servers
|
|
48
48
|
|
|
49
|
-
In the marketplace for MCP servers that test for front-end quality, all servers other than that of Kilotest leverage at most 1 rule engine, most often `axe-core`. The Kilotest testing tool leverages `axe-core` plus
|
|
49
|
+
In the marketplace for MCP servers that test for front-end quality, all servers other than that of Kilotest leverage at most 1 rule engine, most often `axe-core`. The Kilotest testing tool leverages `axe-core` plus 11 other rule engines. This has these main effects:
|
|
50
50
|
|
|
51
|
-
- For web pages that have not yet been tested, a single-rule-engine MCP can provide faster results than Kilotest. A tool running only the `axe-core` tests can complete its work in about 5 seconds or less. The testing tool of Kilotest usually completes its work in 2 to 3 minutes, because all
|
|
51
|
+
- For web pages that have not yet been tested, a single-rule-engine MCP can provide faster results than Kilotest. A tool running only the `axe-core` tests can complete its work in about 5 seconds or less. The testing tool of Kilotest usually completes its work in 2 to 3 minutes, because all 12 rule engines are run, and some of the tests involve navigation and interaction with the page and LLM inference. Moreover, Kilotest currently allows users (both humans and models) to **recommend** new pages for testing but authorizes only Kilotest managers to make the tool proceed with the testing. The wait for manager action can take up to a day. A feature permitting immediate testing ordered by AI agents is planned, but, until it is implemented, Kilotest will be useful for not-yet-tested pages only in long-running (multi-day) workflows.
|
|
52
52
|
- For web pages about which reports are already available, Kilotest can provide faster results than a single-rule-engine MCP server, because Kilotest stores test results for subsequent retrieval. A retrieval from a Kilotest reporting tool can be completed in less than 2 seconds.
|
|
53
53
|
- Kilotest results are more comprehensive than single-rule-engine MCP server results. Every rule engine provides limited coverage of front-end quality, so false negatives (missed defects) are more common with single-rule-engine MCP servers. This difference [has been documented in research](https://arxiv.org/pdf/2304.07591).
|
|
54
54
|
|
|
@@ -165,7 +165,6 @@ More information about Kilotest features and internals:
|
|
|
165
165
|
|
|
166
166
|
- [Kilotest as connector to and collection of AI Tools](https://github.com/jrpool/kilotest/blob/main/docs/AI-TOOLS.md)
|
|
167
167
|
- [OpenAPI specification for Kilotest](https://kilotest.com/openapi.yaml)
|
|
168
|
-
- [Tool caller for testing](https://github.com/jrpool/kilotest/blob/main/api/testSuite.js)
|
|
169
168
|
|
|
170
169
|
### General
|
|
171
170
|
|
|
@@ -173,7 +172,8 @@ More information about Kilotest features and internals:
|
|
|
173
172
|
- [Tutorial, “Accessibility testing strategies”](https://kilotest.com/tutorial.html)
|
|
174
173
|
- [Tutorial on authorizing language models to use Kilotest](https://kilotest.com/qai/)
|
|
175
174
|
- [README file](https://github.com/jrpool/kilotest/blob/main/README.md)
|
|
176
|
-
- [
|
|
177
|
-
- [Testaro rule
|
|
175
|
+
- [Summary of Kilotest capabilities](https://kilotest.com/capability.md)
|
|
176
|
+
- [Testaro rule-engine orchestrator](https://github.com/YRA-Tech/testaro)
|
|
177
|
+
- [Testaro rule classifier](https://github.com/YRA-Tech/testaro-issues)
|
|
178
178
|
- [Research report on Testaro](https://arxiv.org/abs/2309.10167)
|
|
179
179
|
- [Blog post on the Kilotest use case](https://medium.com/cvs-health-tech-blog/how-to-run-a-thousand-accessibility-tests-63692ad120c3)
|
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 an ensemble
|
|
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 an ensemble of twelve independent rule engines to test any public web page. Kilotest creates a structured report of the results and saves the report for future reference. Kilotest API endpoints and MCP tools can provide multipage, single-page, and element-level test results.
|
|
4
4
|
|
|
5
5
|
## API
|
|
6
6
|
|
|
@@ -33,6 +33,7 @@ Kilotest helps language models and AI platforms to give thorough, accurate and t
|
|
|
33
33
|
## More documents
|
|
34
34
|
|
|
35
35
|
- [Detailed explanation of Kilotest and its capabilities for LLMs](https://kilotest.com/llms-full.txt)
|
|
36
|
+
- [Summary of Kilotest capabilities](https://kilotest.com/capability.md)
|
|
36
37
|
- [OpenAPI specification for Kilotest](https://kilotest.com/openapi.yaml)
|
|
37
38
|
- [Kilotest as connector to and collection of AI Tools](https://github.com/jrpool/kilotest/blob/main/docs/AI-TOOLS.md)
|
|
38
39
|
- [Tutorial on authorizing language models to use Kilotest](https://kilotest.com/qai/)
|
package/openapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest API
|
|
4
|
-
description: 'Kilotest is an ensemble testing and reporting service for front-end web quality. It uses an ensemble of
|
|
4
|
+
description: 'Kilotest is an ensemble testing and reporting service for front-end web quality. It uses an ensemble of 12 rule engines to test public web pages for front-end quality (accessibility, usability, and standards conformity). Kilotest acts as a web server for human users, an MCP server for AI platforms, and an API for programmatic access. With this API you can request that a page be tested or retested, discover available reports, incrementally retrieve facts from a report: from what reports are available, to what issues were reported in one report, to what elements violated one issue, to what diagnoses rule engines gave for one violation.'
|
|
5
5
|
version: 2.1.0
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jrpool/kilotest",
|
|
3
|
-
"version": "46.0
|
|
3
|
+
"version": "46.1.0",
|
|
4
4
|
"description": "Ensemble testing for front-end web quality",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=24.16.0"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node index",
|
|
11
|
-
"lint": "npx eslint && npx markdownlint-cli2",
|
|
12
|
-
"test": "c8 node --require ./test/setup.cjs --test --test-concurrency=1 --test-timeout=
|
|
11
|
+
"lint": "npx eslint && out=$(npx markdownlint-cli2 2>&1); code=$?; if [ $code -ne 0 ]; then echo \"$out\"; exit $code; fi",
|
|
12
|
+
"test": "c8 node --require ./test/setup.cjs --test --test-concurrency=1 --test-timeout=100 --test-randomize",
|
|
13
13
|
"test:smoke": "node smokeTest",
|
|
14
|
+
"checkinfo": "node checkinfo",
|
|
14
15
|
"generate:openapi": "node scripts/generate-openapi"
|
|
15
16
|
},
|
|
16
17
|
"repository": {
|
|
@@ -58,7 +58,7 @@ const generate = () => {
|
|
|
58
58
|
openapi: '3.1.0',
|
|
59
59
|
info: {
|
|
60
60
|
title: 'Kilotest API',
|
|
61
|
-
description: 'Kilotest is an ensemble testing and reporting service for front-end web quality. It uses an ensemble of
|
|
61
|
+
description: 'Kilotest is an ensemble testing and reporting service for front-end web quality. It uses an ensemble of 12 rule engines to test public web pages for front-end quality (accessibility, usability, and standards conformity). Kilotest acts as a web server for human users, an MCP server for AI platforms, and an API for programmatic access. With this API you can request that a page be tested or retested, discover available reports, incrementally retrieve facts from a report: from what reports are available, to what issues were reported in one report, to what elements violated one issue, to what diagnoses rule engines gave for one violation.',
|
|
62
62
|
version,
|
|
63
63
|
license: {
|
|
64
64
|
name: license,
|
package/smokeTest.js
CHANGED
|
@@ -14,50 +14,53 @@ const host = process.env.SMOKE_HOST || 'kilotest.com';
|
|
|
14
14
|
// Concrete paths matching each wildcard pattern in the routes table, for smoke testing.
|
|
15
15
|
const concretePaths = {
|
|
16
16
|
GET: {
|
|
17
|
-
'
|
|
17
|
+
'*.html*': '/listReports.html',
|
|
18
18
|
'/': '/',
|
|
19
|
-
'/index.html': '/index.html',
|
|
20
|
-
'/robots.txt': '/robots.txt',
|
|
21
|
-
'/openapi.yaml': '/openapi.yaml',
|
|
22
|
-
'/openapi.json': '/openapi.json',
|
|
23
|
-
'/swagger.yaml': '/swagger.yaml',
|
|
24
|
-
'/swagger.json': '/swagger.json',
|
|
25
19
|
'/api-docs': '/api-docs',
|
|
26
|
-
'/
|
|
20
|
+
'/api/*': '/api/listReports',
|
|
21
|
+
'/capability.md': '/capability.md',
|
|
22
|
+
'/favicon.*': '/favicon.ico',
|
|
23
|
+
'/fullReport.json/*': '/fullReport.json/260101T0000/mix',
|
|
24
|
+
'/index.html': '/index.html',
|
|
27
25
|
'/llms-full.txt': '/llms-full.txt',
|
|
26
|
+
'/llms.txt': '/llms.txt',
|
|
27
|
+
'/mcp': '/mcp',
|
|
28
|
+
'/openapi.json': '/openapi.json',
|
|
29
|
+
'/openapi.yaml': '/openapi.yaml',
|
|
30
|
+
'/robots.txt': '/robots.txt',
|
|
28
31
|
'/sitemap.xml': '/sitemap.xml',
|
|
29
32
|
'/style.css': '/style.css',
|
|
30
|
-
'/
|
|
31
|
-
'/
|
|
32
|
-
'/tutorial/images/*': '/tutorial/images/diagram.png'
|
|
33
|
-
'/favicon.*': '/favicon.ico',
|
|
34
|
-
'*.html*': '/listReports.html'
|
|
33
|
+
'/swagger.json': '/swagger.json',
|
|
34
|
+
'/swagger.yaml': '/swagger.yaml',
|
|
35
|
+
'/tutorial/images/*': '/tutorial/images/diagram.png'
|
|
35
36
|
},
|
|
36
37
|
POST: {
|
|
38
|
+
'/api/*': '/api/requestFeature',
|
|
37
39
|
'/mcp': '/mcp',
|
|
38
|
-
'/requestTest.html': '/requestTest.html',
|
|
39
|
-
'/requestRetest.html/*': '/requestRetest.html/260101T0001/ct',
|
|
40
|
-
'/recAction.html': '/recAction.html',
|
|
41
40
|
'/reannotate.html': '/reannotate.html',
|
|
41
|
+
'/recAction.html': '/recAction.html',
|
|
42
42
|
'/renewWCAG.html': '/renewWCAG.html',
|
|
43
|
+
'/requestRetest.html/*': '/requestRetest.html/260101T0001/ct',
|
|
44
|
+
'/requestTest.html': '/requestTest.html',
|
|
45
|
+
'/tutorialComment.html': '/tutorialComment.html',
|
|
43
46
|
'/worker/job': '/worker/job',
|
|
44
|
-
'/worker/report': '/worker/report'
|
|
45
|
-
'/api/*': '/api/requestFeature',
|
|
46
|
-
'/tutorialComment.html': '/tutorialComment.html'
|
|
47
|
+
'/worker/report': '/worker/report'
|
|
47
48
|
}
|
|
48
49
|
};
|
|
49
50
|
// Minimal POST bodies for paths that require them.
|
|
50
51
|
const postBodies = {
|
|
51
52
|
'/api/requestFeature': {feature: 'smoke test'},
|
|
52
|
-
'/
|
|
53
|
-
'/requestRetest.html/260101T0001/ct': {why: 'smoke test'},
|
|
54
|
-
'/recAction.html': {target: 'https://smoketest.example.com\tSmoke Test Page', authCode: 'invalid'},
|
|
53
|
+
'/mcp': {},
|
|
55
54
|
'/reannotate.html': {authCode: 'invalid'},
|
|
55
|
+
'/recAction.html': {
|
|
56
|
+
target: 'https://smoketest.example.com\tSmoke Test Page', authCode: 'invalid'
|
|
57
|
+
},
|
|
56
58
|
'/renewWCAG.html': {authCode: 'invalid'},
|
|
57
|
-
'/
|
|
58
|
-
'/
|
|
59
|
+
'/requestRetest.html/260101T0001/ct': {why: 'smoke test'},
|
|
60
|
+
'/requestTest.html': {what: 'Smoke Test Page', url: 'https://smoketest.example.com', why: 'smoke test'},
|
|
59
61
|
'/tutorialComment.html': {content: 'smoke test'},
|
|
60
|
-
'/
|
|
62
|
+
'/worker/job': {},
|
|
63
|
+
'/worker/report': {}
|
|
61
64
|
};
|
|
62
65
|
|
|
63
66
|
// FUNCTIONS
|
package/util.test.js
CHANGED
|
@@ -398,7 +398,7 @@ test('processTestRequest returns an error for a mismatched directory name', asyn
|
|
|
398
398
|
assert.equal(result.message, 'Invalid recommendation');
|
|
399
399
|
});
|
|
400
400
|
|
|
401
|
-
test('processTestRequest succeeds and populates the template for a valid request', async () => {
|
|
401
|
+
test('processTestRequest succeeds and populates the template for a valid request', {timeout: 500}, async () => {
|
|
402
402
|
// Reset recs.json to empty before the test.
|
|
403
403
|
await fs.writeFile(recsPath(), '{}\n');
|
|
404
404
|
const result = await processTestRequest('test', requestTestDir, 'Example Page', 'https://example.com', 'because accessibility');
|
|
@@ -409,7 +409,7 @@ test('processTestRequest succeeds and populates the template for a valid request
|
|
|
409
409
|
await fs.writeFile(recsPath(), '{}\n');
|
|
410
410
|
});
|
|
411
411
|
|
|
412
|
-
test('processTestRequest returns a duplicate error for a repeated request', async () => {
|
|
412
|
+
test('processTestRequest returns a duplicate error for a repeated request', {timeout: 500}, async () => {
|
|
413
413
|
// Reset recs.json to empty, then make a successful request.
|
|
414
414
|
await fs.writeFile(recsPath(), '{}\n');
|
|
415
415
|
await processTestRequest('test', requestTestDir, 'Example Page', 'https://example.com', 'because accessibility');
|
|
@@ -88,7 +88,7 @@ test('listReports includes a link to request testing a new page', async () => {
|
|
|
88
88
|
assert.ok(testLink);
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
test('listReports shows recommendations when recs.json has entries', async () => {
|
|
91
|
+
test('listReports shows recommendations when recs.json has entries', {timeout: 500}, async () => {
|
|
92
92
|
const dbDir = path.join(__dirname, '..', '..', 'test', 'fixtures', 'db');
|
|
93
93
|
const recsPath = path.join(dbDir, 'jobs', 'recs.json');
|
|
94
94
|
const originalRecs = await fs.readFile(recsPath, 'utf8');
|
|
@@ -87,7 +87,7 @@ test('answer returns an error when no reports are available', async () => {
|
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
test('answer returns an error when a report fails annotation', async () => {
|
|
90
|
+
test('answer returns an error when a report fails annotation', {timeout: 2000}, async () => {
|
|
91
91
|
// Back up all fixture reports, because annotateReport modifies good reports
|
|
92
92
|
// in place before it reaches the bad one and returns an error.
|
|
93
93
|
const backups = await backupReports();
|
|
@@ -122,7 +122,7 @@ test('answer returns an error when a report fails annotation', async () => {
|
|
|
122
122
|
}
|
|
123
123
|
});
|
|
124
124
|
|
|
125
|
-
test('answer returns ok with an answer page when all reports annotate successfully', async () => {
|
|
125
|
+
test('answer returns ok with an answer page when all reports annotate successfully', {timeout: 2000}, async () => {
|
|
126
126
|
// Back up all fixture reports, because annotateReport modifies them in place.
|
|
127
127
|
const backups = await backupReports();
|
|
128
128
|
try {
|
|
@@ -61,7 +61,7 @@ after(async () => {
|
|
|
61
61
|
|
|
62
62
|
// TESTS
|
|
63
63
|
|
|
64
|
-
test('answer returns ok with a populated answer page for a valid test request', async () => {
|
|
64
|
+
test('answer returns ok with a populated answer page for a valid test request', {timeout: 500}, async () => {
|
|
65
65
|
// Require the module after DB_DIR is set.
|
|
66
66
|
const {answer} = require('./index');
|
|
67
67
|
const result = await answer(
|
|
@@ -81,7 +81,7 @@ test('answer returns ok with a populated answer page for a valid test request',
|
|
|
81
81
|
assert.ok(h1.textContent.includes('Test Page'));
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
test('answer returns an error when the URL is already queued', async () => {
|
|
84
|
+
test('answer returns an error when the URL is already queued', {timeout: 500}, async () => {
|
|
85
85
|
const {answer} = require('./index');
|
|
86
86
|
// Create a queued job with the target URL.
|
|
87
87
|
const queueDir = path.join(fixtureDBDir, 'jobs', 'queue');
|
|
@@ -107,7 +107,7 @@ test('answer returns an error when the URL is already queued', async () => {
|
|
|
107
107
|
}
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
-
test('answer returns an error for an invalid recommendation', async () => {
|
|
110
|
+
test('answer returns an error for an invalid recommendation', {timeout: 500}, async () => {
|
|
111
111
|
const {answer} = require('./index');
|
|
112
112
|
const result = await answer('', 'not-a-url', 'why');
|
|
113
113
|
assert.equal(result.status, 'error');
|
|
@@ -72,7 +72,7 @@ test('rewindReportsForm deletes a latest superseding report with valid auth code
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
test('rewindReportsForm returns an error when a report file is corrupt', async () => {
|
|
75
|
+
test('rewindReportsForm returns an error when a report file is corrupt', {timeout: 500}, async () => {
|
|
76
76
|
const reportPath = path.join(reportsPath(), '260101T0001-ct.json');
|
|
77
77
|
const backup = await fs.readFile(reportPath, 'utf8');
|
|
78
78
|
try {
|
package/web/tutorial/index.html
CHANGED
|
@@ -334,7 +334,7 @@
|
|
|
334
334
|
<section id="testing-page">
|
|
335
335
|
<h3>Testing the page</h3>
|
|
336
336
|
<h4>By rule engines</h4>
|
|
337
|
-
<p>What do rule engines find about this issue when they test this page? For a quick answer, Kilotest subjected the page to testing by
|
|
337
|
+
<p>What do rule engines find about this issue when they test this page? For a quick answer, Kilotest subjected the page to testing by the 10 rule engines that were then in its ensemble (since then, 2 more were added, so now there are 12). Two of the rule engines reported rule violations belonging to this issue:</p>
|
|
338
338
|
<ul>
|
|
339
339
|
<li>The Testaro rule engine reported, <q>input has no autocomplete="email" attribute</q>. In other words, Testaro recognized this input as asking for the user’s email address and therefore as requiring an <code>autocomplete="email"</code> attribute, but not having one.</li>
|
|
340
340
|
<li>The HTML CodeSniffer rule engine, referencing this <code>form</code> element, reported, <q>Invalid autocomplete value: new-password. Element does not belong to Password control group.</q>. In other words, HTML CodeSniffer recognized that this form is not a password input (a form is not an input at all!) and therefore is not eligible for an <code>autocomplete="new-password"</code> attribute, but has one.</li>
|
|
@@ -346,7 +346,7 @@
|
|
|
346
346
|
</ul>
|
|
347
347
|
<p>You might think that this shows rule engines were effective in revealing the defects related to this issue, but this is not a success story. Yes, two rule engines found related defects, but the other eight rule engines did not. Inspection of those eight rule engines reveals that six of them include rules enforcing this standard in some way. So, did these six rule engines report false negatives? Did they fail to enforce their applicable rules here? Perhaps, but perhaps not. Suppose a rule engine has a rule that requires the value of any <code>autocomplete</code> attribute to be in the list of allowed values. Well, <code>new-password</code> is in the list. So that rule engine would, properly, not report a violation of <strong>that</strong> rule.</p>
|
|
348
348
|
<p>We can clearly see from this example that no rule engine will find all accessibility defects, not even all defects that <strong>can</strong> be detected by rule engines. That is why Kilotest uses an <strong>ensemble</strong> of rule engines.</p>
|
|
349
|
-
<p>Of course, running
|
|
349
|
+
<p>Of course, running twelve rule engines also creates a risk of false positives reported by twelve rule engines. In this particular case, there were none. For example, no rule engine claimed to find a defect related to this issue someplace else on the page. Kilotest, like any platform that integrates multiple rule engines, faces greater risks of false positives than a single rule engine would. It combats false positives by investigating user complaints and deprecating rules whose tests are found faulty.</p>
|
|
350
350
|
<h4>By AI agents</h4>
|
|
351
351
|
<p>An AI agent powered by a frontier model was asked to visit the page and report any accessibility defect related to <code>autocomplete</code> attributes. The agent worked for about 3 minutes and then reported a missing <code>autocomplete="email"</code> attribute in the newsletter subscription form. But the page has no newsletter subscription form! It has a form to request a guide, not to subscribe to anything. Moreover, the agent provided code to support its conclusion, and the code differed pervasively from the actual code on the page. This response was a complete fabrication. The agent confessed to it, explaining that it resorted to hallucination when its security restrictions prevented it from accessing the necessary page data.</p>
|
|
352
352
|
<p>A similar AI agent returned correct diagnoses when it was fed the page as an HTML document. That worked, because the two defects relevant to this practicum are discoverable from the HTML of the rendered page. Any defects arising from stylesheets or scripts would have been missed.</p>
|
|
@@ -418,7 +418,7 @@
|
|
|
418
418
|
<legend>Choose the best answer</legend>
|
|
419
419
|
<p>In this practicum, which rule engines discovered both <code>autocomplete</code> defects?</p>
|
|
420
420
|
<ol>
|
|
421
|
-
<li><label><input type="radio" name="q4" value="a"> All
|
|
421
|
+
<li><label><input type="radio" name="q4" value="a"> All 12 rule engines</label></li>
|
|
422
422
|
<li><label><input type="radio" name="q4" value="b"> Six of the rule engines</label></li>
|
|
423
423
|
<li><label><input type="radio" name="q4" value="c"> Only Testaro and HTML CodeSniffer</label></li>
|
|
424
424
|
<li><label><input type="radio" name="q4" value="d"> None of the rule engines</label></li>
|
|
@@ -59,7 +59,7 @@ test('handleComment returns an error for content that is empty after sanitizatio
|
|
|
59
59
|
assert.equal(result.message, 'Comment is empty after sanitization');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
test('handleComment saves a sanitized comment and returns ok', async () => {
|
|
62
|
+
test('handleComment saves a sanitized comment and returns ok', {timeout: 500}, async () => {
|
|
63
63
|
// Replace comments with an empty array for a clean test.
|
|
64
64
|
await fs.writeFile(commentsPath, '[]\n');
|
|
65
65
|
const result = await handleComment('This is a <b>test</b> comment.');
|
|
@@ -70,7 +70,7 @@ test('handleComment saves a sanitized comment and returns ok', async () => {
|
|
|
70
70
|
assert.ok(comments[0].timeStamp);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
test('handleComment strips HTML tags and control characters', async () => {
|
|
73
|
+
test('handleComment strips HTML tags and control characters', {timeout: 500}, async () => {
|
|
74
74
|
await fs.writeFile(commentsPath, '[]\n');
|
|
75
75
|
const result = await handleComment('<img src=x onerror=alert(1)>\x00\x07Hello');
|
|
76
76
|
assert.equal(result.status, 'ok');
|
|
@@ -78,7 +78,7 @@ test('handleComment strips HTML tags and control characters', async () => {
|
|
|
78
78
|
assert.equal(comments[0].content, 'Hello');
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
test('handleComment truncates content to 500 characters', async () => {
|
|
81
|
+
test('handleComment truncates content to 500 characters', {timeout: 500}, async () => {
|
|
82
82
|
await fs.writeFile(commentsPath, '[]\n');
|
|
83
83
|
const longComment = 'x'.repeat(600);
|
|
84
84
|
const result = await handleComment(longComment);
|
|
@@ -87,7 +87,7 @@ test('handleComment truncates content to 500 characters', async () => {
|
|
|
87
87
|
assert.equal(comments[0].content.length, 500);
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
test('handleComment creates comments.json when it does not exist', async () => {
|
|
90
|
+
test('handleComment creates comments.json when it does not exist', {timeout: 500}, async () => {
|
|
91
91
|
await fs.unlink(commentsPath).catch(() => {});
|
|
92
92
|
const result = await handleComment('Test comment for missing file');
|
|
93
93
|
assert.equal(result.status, 'ok');
|