@jrpool/kilotest 60.5.0 → 60.7.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 +2 -1
- package/docs/SERVICE.md +1 -1
- package/index.test.ts +47 -0
- package/index.ts +40 -0
- package/lhm.plugin.json +1 -1
- package/linkValidation.test.ts +1 -0
- package/media/demo-chatgpt.mp4 +0 -0
- package/openapi-3.0.yaml +1 -1
- package/openapi.yaml +1 -1
- package/package.json +1 -1
- package/server.json +1 -1
- package/sitemap.xml +1 -0
- package/smokeTest.ts +2 -0
- package/web/accessibility/index.html +1 -1
- package/web/demo-chatgpt/demo-chatgpt.test.ts +18 -0
- package/web/demo-chatgpt/index.html +279 -0
- package/web/demo-chatgpt/index.ts +19 -0
- package/web/tutorialAI/index.html +2 -0
- package/well-known/.gitkeep +0 -0
package/.devin/config.local.json
CHANGED
package/docs/SERVICE.md
CHANGED
|
@@ -259,7 +259,7 @@ kilotest.com {
|
|
|
259
259
|
# Specify the only paths of forwardable requests.
|
|
260
260
|
@allowedGET {
|
|
261
261
|
method GET
|
|
262
|
-
path /mcp / /index.html /robots.txt /openapi.yaml /openapi.json /swagger.yaml /swagger.json /api-docs /llms.txt /llms-full.txt /capability.md *.html* /fullReport.json/* /api/* /tutorialWeb/images/* /tutorialAI/images/* /qai /qai/comments /favicon.* /style.css /sitemap.xml
|
|
262
|
+
path /mcp / /index.html /.well-known/* /robots.txt /openapi.yaml /openapi.json /swagger.yaml /swagger.json /api-docs /llms.txt /llms-full.txt /capability.md *.html* /fullReport.json/* /api/* /tutorialWeb/images/* /tutorialAI/images/* /media/* /qai /qai/comments /favicon.* /style.css /sitemap.xml
|
|
263
263
|
}
|
|
264
264
|
# UptimeRobot's uptime check is the only known HEAD caller, and it targets only the site
|
|
265
265
|
# root (see "Health monitoring" below), so HEAD is forwarded only for that path, not the
|
package/index.test.ts
CHANGED
|
@@ -451,6 +451,53 @@ test('GET /tutorialAI/images/nonexistent.png returns an error page', async () =>
|
|
|
451
451
|
assert.ok(res.body.includes('Image not found'));
|
|
452
452
|
});
|
|
453
453
|
|
|
454
|
+
test('GET /media/demo-chatgpt.mp4 serves the video', async () => {
|
|
455
|
+
const res = await request('GET', '/media/demo-chatgpt.mp4');
|
|
456
|
+
assert.equal(res.statusCode, 200);
|
|
457
|
+
assert.ok(res.headers['content-type'].includes('video/mp4'));
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('GET /media with an unknown extension serves octet-stream', async () => {
|
|
461
|
+
// Create a temporary media file with an unknown extension.
|
|
462
|
+
const mediaPath = path.join(import.meta.dirname, 'media', 'test.xyz');
|
|
463
|
+
await fs.writeFile(mediaPath, 'fake media data');
|
|
464
|
+
try {
|
|
465
|
+
const res = await request('GET', '/media/test.xyz');
|
|
466
|
+
assert.equal(res.statusCode, 200);
|
|
467
|
+
assert.ok(res.headers['content-type'].includes('application/octet-stream'));
|
|
468
|
+
}
|
|
469
|
+
finally {
|
|
470
|
+
await fs.unlink(mediaPath).catch(() => {});
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test('GET /media/nonexistent.mp4 returns an error page', async () => {
|
|
475
|
+
const res = await request('GET', '/media/nonexistent.mp4');
|
|
476
|
+
assert.equal(res.statusCode, 400);
|
|
477
|
+
assert.ok(res.body.includes('Media file not found'));
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test('GET /.well-known serves a well-known resource as text', async () => {
|
|
481
|
+
// Create a temporary well-known file.
|
|
482
|
+
const knownPath = path.join(import.meta.dirname, 'well-known', 'test-resource');
|
|
483
|
+
await fs.writeFile(knownPath, 'test-token-value');
|
|
484
|
+
try {
|
|
485
|
+
const res = await request('GET', '/.well-known/test-resource');
|
|
486
|
+
assert.equal(res.statusCode, 200);
|
|
487
|
+
assert.ok(res.headers['content-type'].includes('text/plain'));
|
|
488
|
+
assert.ok(res.body.includes('test-token-value'));
|
|
489
|
+
}
|
|
490
|
+
finally {
|
|
491
|
+
await fs.unlink(knownPath).catch(() => {});
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test('GET /.well-known/nonexistent returns an error page', async () => {
|
|
496
|
+
const res = await request('GET', '/.well-known/nonexistent');
|
|
497
|
+
assert.equal(res.statusCode, 400);
|
|
498
|
+
assert.ok(res.body.includes('Well-known resource not found'));
|
|
499
|
+
});
|
|
500
|
+
|
|
454
501
|
test('GET /nonexistent.html returns an abuse error', {timeout: 500}, async () => {
|
|
455
502
|
const res = await request('GET', '/nonexistent.html');
|
|
456
503
|
assert.equal(res.statusCode, 400);
|
package/index.ts
CHANGED
|
@@ -42,6 +42,7 @@ import fs from 'node:fs/promises';
|
|
|
42
42
|
import {handleComment as handleTutorialWebComment} from './web/tutorialWeb/index.ts';
|
|
43
43
|
import {answer as tutorialWeb} from './web/tutorialWeb/index.ts';
|
|
44
44
|
import {answer as accessibility} from './web/accessibility/index.ts';
|
|
45
|
+
import {answer as demoChatgpt} from './web/demo-chatgpt/index.ts';
|
|
45
46
|
import {answer as home} from './web/home/index.ts';
|
|
46
47
|
import {answer as privacy} from './web/privacy/index.ts';
|
|
47
48
|
import {answer as terms} from './web/terms/index.ts';
|
|
@@ -111,6 +112,7 @@ const answer: {
|
|
|
111
112
|
accessibility: PageHandler;
|
|
112
113
|
ai0BalanceForm: PageHandler;
|
|
113
114
|
deleteNotesForm: PageHandler;
|
|
115
|
+
'demo-chatgpt': PageHandler;
|
|
114
116
|
enqueue: PageHandler;
|
|
115
117
|
enqueueForm: PageHandler;
|
|
116
118
|
expungeReportsForm: PageHandler;
|
|
@@ -144,6 +146,7 @@ const answer: {
|
|
|
144
146
|
accessibility,
|
|
145
147
|
ai0BalanceForm,
|
|
146
148
|
deleteNotesForm,
|
|
149
|
+
'demo-chatgpt': demoChatgpt,
|
|
147
150
|
enqueue,
|
|
148
151
|
enqueueForm,
|
|
149
152
|
expungeReportsForm,
|
|
@@ -208,6 +211,7 @@ export const routes = {
|
|
|
208
211
|
GET: [
|
|
209
212
|
'*.html*',
|
|
210
213
|
'/',
|
|
214
|
+
'/.well-known/*',
|
|
211
215
|
'/api-docs',
|
|
212
216
|
'/api/*',
|
|
213
217
|
'/capability.md',
|
|
@@ -217,6 +221,7 @@ export const routes = {
|
|
|
217
221
|
'/llms-full.txt',
|
|
218
222
|
'/llms.txt',
|
|
219
223
|
'/mcp',
|
|
224
|
+
'/media/*',
|
|
220
225
|
'/openapi.json',
|
|
221
226
|
'/openapi.yaml',
|
|
222
227
|
'/qai',
|
|
@@ -914,6 +919,41 @@ const handleRequest = async (request: IncomingMessage, response: ServerResponse)
|
|
|
914
919
|
await serveError({message: 'Invalid service request'}, response, false);
|
|
915
920
|
}
|
|
916
921
|
}
|
|
922
|
+
// Otherwise, if it is for a well-known resource (e.g. the OpenAI domain-verification
|
|
923
|
+
// challenge token):
|
|
924
|
+
else if (pathname.startsWith('/.well-known/')) {
|
|
925
|
+
const knownFile = pathname.slice('/.well-known/'.length);
|
|
926
|
+
const knownPath = path.join(import.meta.dirname, 'well-known', knownFile);
|
|
927
|
+
try {
|
|
928
|
+
const known = await fs.readFile(knownPath);
|
|
929
|
+
setHeaders('text/plain', null, 'low');
|
|
930
|
+
response.end(known);
|
|
931
|
+
}
|
|
932
|
+
catch {
|
|
933
|
+
await serveError({message: 'ERROR: Well-known resource not found'}, response, true);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
// Otherwise, if it is for a media file (e.g. a demonstration video):
|
|
937
|
+
else if (pathname.startsWith('/media/')) {
|
|
938
|
+
const mediaFile = pathname.slice('/media/'.length);
|
|
939
|
+
const mediaPath = path.join(import.meta.dirname, 'media', mediaFile);
|
|
940
|
+
try {
|
|
941
|
+
const media = await fs.readFile(mediaPath);
|
|
942
|
+
const ext = path.extname(mediaFile).toLowerCase();
|
|
943
|
+
const mimeTypes: Record<string, string> = {
|
|
944
|
+
'.mp4': 'video/mp4',
|
|
945
|
+
'.webm': 'video/webm',
|
|
946
|
+
'.mov': 'video/quicktime',
|
|
947
|
+
'.m4v': 'video/mp4'
|
|
948
|
+
};
|
|
949
|
+
const mimeType = mimeTypes[ext] || 'application/octet-stream';
|
|
950
|
+
setHeaders(mimeType, null, 'low');
|
|
951
|
+
response.end(media);
|
|
952
|
+
}
|
|
953
|
+
catch {
|
|
954
|
+
await serveError({message: 'ERROR: Media file not found'}, response, true);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
917
957
|
// Otherwise, if it is for a tutorial image:
|
|
918
958
|
else if (pathname.startsWith('/tutorialWeb/images/') || pathname.startsWith('/tutorialAI/images/')) {
|
|
919
959
|
let imgFile: string;
|
package/lhm.plugin.json
CHANGED
package/linkValidation.test.ts
CHANGED
|
@@ -35,6 +35,7 @@ validPathPatterns.add('/api/*');
|
|
|
35
35
|
validPathPatterns.add('/fullReport.json/*');
|
|
36
36
|
validPathPatterns.add('/tutorialWeb/images/*');
|
|
37
37
|
validPathPatterns.add('/tutorialAI/images/*');
|
|
38
|
+
validPathPatterns.add('/media/*');
|
|
38
39
|
|
|
39
40
|
// Specific page routes (those that end in .html)
|
|
40
41
|
const htmlRoutes = [
|
|
Binary file
|
package/openapi-3.0.yaml
CHANGED
|
@@ -2,7 +2,7 @@ openapi: 3.0.3
|
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest API
|
|
4
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 web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG). 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
|
-
version: 60.
|
|
5
|
+
version: 60.7.0
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
|
8
8
|
url: https://github.com/jrpool/kilotest/blob/main/LICENSE
|
package/openapi.yaml
CHANGED
|
@@ -2,7 +2,7 @@ openapi: 3.1.0
|
|
|
2
2
|
info:
|
|
3
3
|
title: Kilotest API
|
|
4
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 web pages for front-end quality (accessibility, usability, and web standards, including HTML, CSS, ARIA, and WCAG). 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
|
-
version: 60.
|
|
5
|
+
version: 60.7.0
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
|
8
8
|
identifier: MIT
|
package/package.json
CHANGED
package/server.json
CHANGED
package/sitemap.xml
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<url><loc>https://kilotest.com/privacy.html</loc></url>
|
|
10
10
|
<url><loc>https://kilotest.com/terms.html</loc></url>
|
|
11
11
|
<url><loc>https://kilotest.com/accessibility.html</loc></url>
|
|
12
|
+
<url><loc>https://kilotest.com/demo-chatgpt.html</loc></url>
|
|
12
13
|
<url><loc>https://kilotest.com/openapi.yaml</loc></url>
|
|
13
14
|
<url><loc>https://kilotest.com/llms.txt</loc></url>
|
|
14
15
|
<url><loc>https://kilotest.com/llms-full.txt</loc></url>
|
package/smokeTest.ts
CHANGED
|
@@ -16,6 +16,7 @@ const concretePaths: Record<string, Record<string, string>> = {
|
|
|
16
16
|
GET: {
|
|
17
17
|
'*.html*': '/listPages.html',
|
|
18
18
|
'/': '/',
|
|
19
|
+
'/.well-known/*': '/.well-known/openai-apps-challenge',
|
|
19
20
|
'/api-docs': '/api-docs',
|
|
20
21
|
'/api/*': '/api/listPages',
|
|
21
22
|
'/capability.md': '/capability.md',
|
|
@@ -25,6 +26,7 @@ const concretePaths: Record<string, Record<string, string>> = {
|
|
|
25
26
|
'/llms-full.txt': '/llms-full.txt',
|
|
26
27
|
'/llms.txt': '/llms.txt',
|
|
27
28
|
'/mcp': '/mcp',
|
|
29
|
+
'/media/*': '/media/demo-chatgpt.mp4',
|
|
28
30
|
'/openapi.json': '/openapi.json',
|
|
29
31
|
'/openapi.yaml': '/openapi.yaml',
|
|
30
32
|
'/qai': '/qai',
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<h2>Unremediated issues</h2>
|
|
16
16
|
<p>Kilotest has reported these lowest-priority issues:</p>
|
|
17
17
|
<ul>
|
|
18
|
-
<li><strong>large visible-element count</strong> (<code>visibleBulk</code>) on the list-pages page: justified by the existence of more than
|
|
18
|
+
<li><strong>large visible-element count</strong> (<code>visibleBulk</code>) on the list-pages page: justified by the existence of more than 200 tested pages</li>
|
|
19
19
|
<li><strong>inconsistent heading, link, or button styles</strong> (<code>incnsistentStyles</code>) on the list-pages page: under investigation</li>
|
|
20
20
|
</ul>
|
|
21
21
|
<p>The testing of Kilotest pages is incomplete, so additional unremediated issues may remain.</p>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
demo.test.ts
|
|
3
|
+
Unit tests for web/demo/index.ts, covering the answer export.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
import {test} from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import {answer} from './index.ts';
|
|
11
|
+
|
|
12
|
+
// TESTS
|
|
13
|
+
|
|
14
|
+
test('answer returns the demonstration page', async () => {
|
|
15
|
+
const result = await answer();
|
|
16
|
+
assert.equal(result.status, 'ok');
|
|
17
|
+
assert.ok(result.answerPage.includes('ChatGPT demonstration'));
|
|
18
|
+
});
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en-US">
|
|
3
|
+
<head>
|
|
4
|
+
__head__
|
|
5
|
+
<meta name="description" content="Kilotest demonstration: a step-through walkthrough of ChatGPT using the Kilotest tools">
|
|
6
|
+
<title>ChatGPT demonstration | Kilotest</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body class="styled">
|
|
9
|
+
__header__
|
|
10
|
+
<main class="page">
|
|
11
|
+
<h1>ChatGPT demonstration</h1>
|
|
12
|
+
<p>This page shows how ChatGPT uses Kilotest to answer questions about the front-end quality of web pages. Each of the five scenarios below reproduces a real interaction. Step through them at your own pace with the Previous and Next buttons, or press Play to advance automatically. The same interaction is also available as a <a href="/media/demo-chatgpt.mp4">video recording</a>.</p>
|
|
13
|
+
<div class="demo" id="demo">
|
|
14
|
+
<div class="demo-controls">
|
|
15
|
+
<button type="button" id="demo-play" aria-pressed="false"><span aria-hidden="true">▶</span> Play</button>
|
|
16
|
+
<button type="button" id="demo-prev"><span aria-hidden="true">◀</span> Previous</button>
|
|
17
|
+
<button type="button" id="demo-next">Next <span aria-hidden="true">▶</span></button>
|
|
18
|
+
<span id="demo-status" role="status">Scene 1 of 5</span>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="demo-stage" id="demo-stage" aria-live="polite">
|
|
21
|
+
|
|
22
|
+
<section class="frame" data-scene="1">
|
|
23
|
+
<h2>1. Listing tested pages of a website</h2>
|
|
24
|
+
<h3>Prompt 1</h3>
|
|
25
|
+
<div class="msg user"><p><span class="mention">@ Kilotest</span> Name all pages of Kilotest that Kilotest has tested. I need only their names and not their URLs, test dates, test results, or any other information.</p></div>
|
|
26
|
+
<div class="msg tool"><p>Used Kilotest — worked for a few seconds</p></div>
|
|
27
|
+
<h3>Response 1</h3>
|
|
28
|
+
<div class="msg assistant"><p>The assistant at first listed every tested page, not only the Kilotest pages.</p></div>
|
|
29
|
+
<h3>Corrective prompt 1</h3>
|
|
30
|
+
<div class="msg user"><p>You answered the wrong question. I asked you to name all pages of Kilotest that Kilotest has tested. I did not ask you to name all pages that Kilotest has tested.</p></div>
|
|
31
|
+
<div class="msg tool"><p>Used Kilotest — worked for 13s</p></div>
|
|
32
|
+
<h3>Corrected response 1</h3>
|
|
33
|
+
<div class="msg assistant"><ul><li>Kilotest</li><li>Kilotest list diagnoses</li><li>Kilotest list issues</li><li>Kilotest list pages</li><li>Kilotest list violators</li></ul></div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<section class="frame" data-scene="2" hidden>
|
|
37
|
+
<h2>2. Listing issues reported on a page</h2>
|
|
38
|
+
<h3>Prompt 2</h3>
|
|
39
|
+
<div class="msg user"><p><span class="mention">@ Kilotest</span> Name the issues reported in the latest report about the Human Rights Watch contact page. I need only their names and not their descriptions, priorities, or any other information.</p></div>
|
|
40
|
+
<div class="msg tool"><p>Used Kilotest — worked for 9s</p></div>
|
|
41
|
+
<h3>Response 2</h3>
|
|
42
|
+
<div class="msg assistant">
|
|
43
|
+
<ul class="issue-list">
|
|
44
|
+
<li>abbr title missing</li>
|
|
45
|
+
<li>all-capital text</li>
|
|
46
|
+
<li>aria reference invalid</li>
|
|
47
|
+
<li>attribute invalid</li>
|
|
48
|
+
<li>attribute obsolete</li>
|
|
49
|
+
<li>attribute value invalid</li>
|
|
50
|
+
<li>autocomplete invalid</li>
|
|
51
|
+
<li>box size absolute</li>
|
|
52
|
+
<li>button name not visible</li>
|
|
53
|
+
<li>code invalid</li>
|
|
54
|
+
<li>contrast poor</li>
|
|
55
|
+
<li>double br use dubious</li>
|
|
56
|
+
<li>element focusability and operability conflict</li>
|
|
57
|
+
<li>fieldset missing?</li>
|
|
58
|
+
<li>focus indication improvable</li>
|
|
59
|
+
<li>focusable element hidden</li>
|
|
60
|
+
<li>forms not distinctly named</li>
|
|
61
|
+
<li>heading level skipped</li>
|
|
62
|
+
<li>heading structure illogical</li>
|
|
63
|
+
<li>hovering changes content</li>
|
|
64
|
+
<li>ID empty</li>
|
|
65
|
+
<li>ID not unique</li>
|
|
66
|
+
<li>iframe not named</li>
|
|
67
|
+
<li>image not named</li>
|
|
68
|
+
<li>inconsistent heading, link, or button styles</li>
|
|
69
|
+
<li>itemid invalid</li>
|
|
70
|
+
<li>label location dubious</li>
|
|
71
|
+
<li>large visible-element count</li>
|
|
72
|
+
<li>line height low</li>
|
|
73
|
+
<li>link destination not URL</li>
|
|
74
|
+
<li>link href missing</li>
|
|
75
|
+
<li>link indication poor</li>
|
|
76
|
+
<li>link not named</li>
|
|
77
|
+
<li>links named identically</li>
|
|
78
|
+
<li>links not grouped as nav</li>
|
|
79
|
+
<li>list-like br use dubious</li>
|
|
80
|
+
<li>menu navigation nonstandard</li>
|
|
81
|
+
<li>parent invalid</li>
|
|
82
|
+
<li>requirement invalid</li>
|
|
83
|
+
<li>role assigned instead of implicit</li>
|
|
84
|
+
<li>role redundant</li>
|
|
85
|
+
<li>small targets too near to each other</li>
|
|
86
|
+
<li>summary has explicit role</li>
|
|
87
|
+
<li>svg image not named</li>
|
|
88
|
+
<li>tab-opening link action unstated?</li>
|
|
89
|
+
<li>target small</li>
|
|
90
|
+
<li>target very small</li>
|
|
91
|
+
<li>text contrast improvable</li>
|
|
92
|
+
<li>text contrast not testable</li>
|
|
93
|
+
<li>title attribute invalid</li>
|
|
94
|
+
<li>z-index not zero</li>
|
|
95
|
+
</ul>
|
|
96
|
+
</div>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
99
|
+
<section class="frame" data-scene="3" hidden>
|
|
100
|
+
<h2>3. Getting details about an important issue.</h2>
|
|
101
|
+
<h3>Prompt 3</h3>
|
|
102
|
+
<div class="msg user"><p><span class="mention">@ Kilotest</span> In the latest report about the Wikimedia Foundation (home) page, tell me which highest-priority issue was reported by the largest number of rule engines. For that issue, give me the XPath of the element that was reported as a violator by the largest number of rule engines. Finally, for that element and that issue, give me a one-sentence summary of the diagnoses. I do not need an issue explanation, additional information about the element, the number of diagnoses, their full texts, or the number or names of the rule engines that issued them.</p></div>
|
|
103
|
+
<div class="msg tool"><p>Used Kilotest — worked for 21s</p></div>
|
|
104
|
+
<h3>Response 3</h3>
|
|
105
|
+
<div class="msg assistant">
|
|
106
|
+
<p><strong>Highest-priority issue:</strong> Link not named</p>
|
|
107
|
+
<p><strong>XPath(s) tied for the most rule-engine reports:</strong></p>
|
|
108
|
+
<ul>
|
|
109
|
+
<li><code>/html/body/div[2]/footer[1]/div[1]/div[2]/div[1]/figure[1]/a[1]</code></li>
|
|
110
|
+
<li><code>/html/body/div[2]/footer[1]/div[1]/div[2]/div[1]/figure[2]/a[1]</code></li>
|
|
111
|
+
</ul>
|
|
112
|
+
<p><strong>Diagnosis summary:</strong> Both links lack an accessible name, such as discernible text or an equivalent text alternative, making their purpose unclear to screen-reader users.</p>
|
|
113
|
+
</div>
|
|
114
|
+
</section>
|
|
115
|
+
|
|
116
|
+
<section class="frame" data-scene="4" hidden>
|
|
117
|
+
<h2>4. Getting a new page tested.</h2>
|
|
118
|
+
<h3>Prompt 4</h3>
|
|
119
|
+
<div class="msg user"><p><span class="mention">@ Kilotest</span> Request a Kilotest test of Example12345 (<a href="https://example12345.info">https://example12345.info</a>) and let me know whether the request was accepted. If it was, tell me how long I should expect to wait before the results will be available.</p></div>
|
|
120
|
+
<div class="msg tool"><p>Used Kilotest — worked for 12s</p></div>
|
|
121
|
+
<h3>Response 4</h3>
|
|
122
|
+
<div class="msg assistant"><p>Kilotest <strong>did not accept the request</strong> to test Example12345 (<code>https://example12345.info</code>).</p>
|
|
123
|
+
<p>The reason given was that the URL does not resolve to a page that this Kilotest deployment can test.</p>
|
|
124
|
+
<p>Since the request was rejected, there is no expected wait time for results.</p></div>
|
|
125
|
+
</section>
|
|
126
|
+
|
|
127
|
+
<section class="frame" data-scene="5" hidden>
|
|
128
|
+
<h2>5. Checking on changes since the last test of a page.</h2>
|
|
129
|
+
<h3>Prompt 5</h3>
|
|
130
|
+
<div class="msg user"><p><span class="mention">@ Kilotest</span> Order a Kilotest retest of the Example page. When the report is completed, compare it with the previous report about that page and give me two lists: one naming any eliminated issues and the other naming any newly reported issues. I do not need the report dates, the issue counts, issue explanations, or any other information. If the order is rejected, or if the report is not completed within 4 minutes, stop waiting and tell me what happened.</p></div>
|
|
131
|
+
<div class="msg tool"><p>Used Kilotest — worked for 1m 12s</p></div>
|
|
132
|
+
<h3>Response 5</h3>
|
|
133
|
+
<div class="msg assistant">
|
|
134
|
+
<p>Kilotest accepted the retest order, and the report was completed within 4 minutes.</p>
|
|
135
|
+
<p><strong>Eliminated issues</strong></p>
|
|
136
|
+
<p>– None</p>
|
|
137
|
+
<p><strong>Newly reported issues</strong></p>
|
|
138
|
+
<p>– None</p>
|
|
139
|
+
</div>
|
|
140
|
+
</section>
|
|
141
|
+
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
<p>Each scenario corresponds to a real capability of the Kilotest tools: discovering tested pages, listing issues, drilling down to elements and diagnoses, requesting tests, and ordering retests. The responses above reproduce real Kilotest report data.</p>
|
|
145
|
+
</main>
|
|
146
|
+
<script>
|
|
147
|
+
const frames = Array.from(document.querySelectorAll('#demo-stage .frame'));
|
|
148
|
+
const playBtn = document.getElementById('demo-play');
|
|
149
|
+
const prevBtn = document.getElementById('demo-prev');
|
|
150
|
+
const nextBtn = document.getElementById('demo-next');
|
|
151
|
+
const status = document.getElementById('demo-status');
|
|
152
|
+
let current = 0;
|
|
153
|
+
let timer = null;
|
|
154
|
+
|
|
155
|
+
function show(index) {
|
|
156
|
+
current = Math.max(0, Math.min(frames.length - 1, index));
|
|
157
|
+
frames.forEach((frame, i) => { frame.hidden = i !== current; });
|
|
158
|
+
status.textContent = 'Scene ' + (current + 1) + ' of ' + frames.length;
|
|
159
|
+
prevBtn.disabled = current === 0;
|
|
160
|
+
nextBtn.disabled = current === frames.length - 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function stop() {
|
|
164
|
+
if (timer) { clearInterval(timer); timer = null; }
|
|
165
|
+
playBtn.innerHTML = '<span aria-hidden="true">▶</span> Play';
|
|
166
|
+
playBtn.setAttribute('aria-pressed', 'false');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
playBtn.addEventListener('click', function () {
|
|
170
|
+
if (timer) { stop(); return; }
|
|
171
|
+
if (current === frames.length - 1) { show(0); }
|
|
172
|
+
playBtn.innerHTML = '<span aria-hidden="true">⏸</span> Pause';
|
|
173
|
+
playBtn.setAttribute('aria-pressed', 'true');
|
|
174
|
+
timer = setInterval(function () {
|
|
175
|
+
if (current >= frames.length - 1) { stop(); return; }
|
|
176
|
+
show(current + 1);
|
|
177
|
+
}, 6000);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
prevBtn.addEventListener('click', function () { stop(); show(current - 1); });
|
|
181
|
+
nextBtn.addEventListener('click', function () { stop(); show(current + 1); });
|
|
182
|
+
show(0);
|
|
183
|
+
</script>
|
|
184
|
+
<style>
|
|
185
|
+
.demo-controls {
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-wrap: wrap;
|
|
188
|
+
gap: 0.5rem;
|
|
189
|
+
align-items: center;
|
|
190
|
+
margin-bottom: 1rem;
|
|
191
|
+
}
|
|
192
|
+
.demo-controls button {
|
|
193
|
+
min-height: 44px;
|
|
194
|
+
padding: 0.5rem 1rem;
|
|
195
|
+
font-size: 1rem;
|
|
196
|
+
font-family: inherit;
|
|
197
|
+
border: solid 1px var(--line);
|
|
198
|
+
border-radius: 0.375rem;
|
|
199
|
+
background: var(--card);
|
|
200
|
+
color: inherit;
|
|
201
|
+
cursor: pointer;
|
|
202
|
+
}
|
|
203
|
+
.demo-controls button:hover {
|
|
204
|
+
border-color: var(--accent);
|
|
205
|
+
}
|
|
206
|
+
.demo-controls button:disabled {
|
|
207
|
+
opacity: 0.45;
|
|
208
|
+
cursor: default;
|
|
209
|
+
}
|
|
210
|
+
.demo-controls button:disabled:hover {
|
|
211
|
+
border-color: var(--line);
|
|
212
|
+
}
|
|
213
|
+
#demo-status {
|
|
214
|
+
font-size: 0.9rem;
|
|
215
|
+
color: var(--muted);
|
|
216
|
+
}
|
|
217
|
+
.demo-stage .frame {
|
|
218
|
+
border: solid 1px var(--line);
|
|
219
|
+
border-radius: 0.5rem;
|
|
220
|
+
padding: 1rem 1.25rem;
|
|
221
|
+
background: var(--card);
|
|
222
|
+
}
|
|
223
|
+
.demo-stage .frame h2 {
|
|
224
|
+
margin-top: 0;
|
|
225
|
+
font-size: 1.15rem;
|
|
226
|
+
}
|
|
227
|
+
.demo-stage .frame h3 {
|
|
228
|
+
margin: 1rem 0 0.25rem;
|
|
229
|
+
font-size: 0.95rem;
|
|
230
|
+
color: var(--accent);
|
|
231
|
+
}
|
|
232
|
+
.msg {
|
|
233
|
+
margin: 0.75rem 0;
|
|
234
|
+
max-width: 52rem;
|
|
235
|
+
}
|
|
236
|
+
.msg p {
|
|
237
|
+
margin: 0;
|
|
238
|
+
}
|
|
239
|
+
.msg.user {
|
|
240
|
+
display: flex;
|
|
241
|
+
justify-content: flex-end;
|
|
242
|
+
}
|
|
243
|
+
.msg.user p {
|
|
244
|
+
max-width: 42rem;
|
|
245
|
+
padding: 0.55rem 1.1rem;
|
|
246
|
+
background: #e9e9e9;
|
|
247
|
+
border-radius: 1.4rem;
|
|
248
|
+
}
|
|
249
|
+
.msg.tool {
|
|
250
|
+
font-size: 0.9rem;
|
|
251
|
+
color: #888;
|
|
252
|
+
}
|
|
253
|
+
.msg.tool p {
|
|
254
|
+
padding: 0.2rem 0;
|
|
255
|
+
}
|
|
256
|
+
.msg.tool p::after {
|
|
257
|
+
content: " \203A";
|
|
258
|
+
}
|
|
259
|
+
.mention {
|
|
260
|
+
display: inline-block;
|
|
261
|
+
padding: 0.05rem 0.5rem;
|
|
262
|
+
margin-right: 0.25rem;
|
|
263
|
+
font-size: 0.85rem;
|
|
264
|
+
background: #ddf0e8;
|
|
265
|
+
border-radius: 0.4rem;
|
|
266
|
+
}
|
|
267
|
+
.msg.assistant {
|
|
268
|
+
padding: 0.6rem 0;
|
|
269
|
+
}
|
|
270
|
+
.issue-list {
|
|
271
|
+
columns: 14rem 3;
|
|
272
|
+
column-gap: 2rem;
|
|
273
|
+
font-size: 0.9rem;
|
|
274
|
+
margin: 0.5rem 0 0;
|
|
275
|
+
}
|
|
276
|
+
</style>
|
|
277
|
+
__footer__
|
|
278
|
+
</body>
|
|
279
|
+
</html>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
index.ts
|
|
3
|
+
Serves the Kilotest demonstration page.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
import {populateTemplate} from '../../util.ts';
|
|
9
|
+
|
|
10
|
+
// FUNCTIONS
|
|
11
|
+
|
|
12
|
+
// Returns the demonstration page.
|
|
13
|
+
export const answer = async () => {
|
|
14
|
+
const answerPage = await populateTemplate(import.meta.dirname, {});
|
|
15
|
+
return {
|
|
16
|
+
status: 'ok',
|
|
17
|
+
answerPage
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -111,6 +111,8 @@
|
|
|
111
111
|
<li>Toggle the <q>Ask/Allow</q> buttons to show <q>Allow</q>.</li>
|
|
112
112
|
</ol>
|
|
113
113
|
</details>
|
|
114
|
+
<h2>Demonstration</h2>
|
|
115
|
+
<p>A <a href="/demo-chatgpt.html">step-through demonstration</a> shows ChatGPT using Kilotest to discover tested pages, inspect reported issues, and request tests and retests.</p>
|
|
114
116
|
<h2 id="other">Other AI platforms</h2>
|
|
115
117
|
<p>You can connect Kilotest to some other AI platforms, too. The instructions above are limited to consumer platforms that are either free or relatively inexpensive (US$20 per month or less). Business platforms such as ChatGPT Business, and developer platforms such as Devin Desktop, also permit you to enable Kilotest.</p>
|
|
116
118
|
<p>Feel free to submit a comment below about empowering any AI platform to use Kilotest.</p>
|
|
File without changes
|