@jrpool/kilotest 60.1.4 → 60.1.5
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/lhm.plugin.json +1 -1
- package/openapi-3.0.yaml +1 -1
- package/openapi.yaml +1 -1
- package/package.json +1 -1
- package/server.json +1 -1
- package/style.css +68 -36
- package/web/home/index.html +1 -1
- package/web/listPages/index.html +10 -18
- package/web/listPages/index.ts +35 -15
package/lhm.plugin.json
CHANGED
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.1.
|
|
5
|
+
version: 60.1.5
|
|
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.1.
|
|
5
|
+
version: 60.1.5
|
|
6
6
|
license:
|
|
7
7
|
name: MIT
|
|
8
8
|
identifier: MIT
|
package/package.json
CHANGED
package/server.json
CHANGED
package/style.css
CHANGED
|
@@ -208,7 +208,7 @@ ul.xPathList li {
|
|
|
208
208
|
padding-left: 0;
|
|
209
209
|
overflow-wrap: break-word;
|
|
210
210
|
}
|
|
211
|
-
body.
|
|
211
|
+
body.styled {
|
|
212
212
|
--ink: #18212b;
|
|
213
213
|
--muted: #424e59;
|
|
214
214
|
--accent: #005c5c;
|
|
@@ -224,37 +224,37 @@ body.home {
|
|
|
224
224
|
color: var(--ink);
|
|
225
225
|
background: var(--bg);
|
|
226
226
|
}
|
|
227
|
-
body.
|
|
227
|
+
body.styled, body.styled *, body.styled *::before, body.styled *::after {
|
|
228
228
|
box-sizing: border-box;
|
|
229
229
|
}
|
|
230
|
-
body.
|
|
230
|
+
body.styled a {
|
|
231
231
|
color: var(--accent);
|
|
232
232
|
text-underline-offset: 0.15em;
|
|
233
233
|
}
|
|
234
|
-
body.
|
|
234
|
+
body.styled a:hover {
|
|
235
235
|
color: var(--accent-dark);
|
|
236
236
|
background-color: transparent;
|
|
237
237
|
}
|
|
238
|
-
body.
|
|
238
|
+
body.styled a:focus-visible, body.styled summary:focus-visible {
|
|
239
239
|
outline: 3px solid var(--focus);
|
|
240
240
|
outline-offset: 2px;
|
|
241
241
|
border-radius: 4px;
|
|
242
242
|
}
|
|
243
|
-
body.
|
|
243
|
+
body.styled h2 {
|
|
244
244
|
color: var(--ink);
|
|
245
245
|
font-size: 1.4rem;
|
|
246
246
|
}
|
|
247
|
-
body.
|
|
247
|
+
body.styled li, body.styled p {
|
|
248
248
|
line-height: 1.6;
|
|
249
249
|
}
|
|
250
|
-
body.
|
|
250
|
+
body.styled p {
|
|
251
251
|
margin: 0.6rem 0;
|
|
252
252
|
}
|
|
253
|
-
body.
|
|
253
|
+
body.styled header {
|
|
254
254
|
background: var(--card);
|
|
255
255
|
border-bottom: 1px solid var(--line);
|
|
256
256
|
}
|
|
257
|
-
body.
|
|
257
|
+
body.styled .header-inner {
|
|
258
258
|
max-width: 64rem;
|
|
259
259
|
margin-inline: auto;
|
|
260
260
|
padding: 1.1rem 1.5rem;
|
|
@@ -262,34 +262,42 @@ body.home .header-inner {
|
|
|
262
262
|
align-items: baseline;
|
|
263
263
|
gap: 0.75rem;
|
|
264
264
|
}
|
|
265
|
-
body.
|
|
265
|
+
body.styled .brand {
|
|
266
266
|
font-weight: 800;
|
|
267
267
|
font-size: 1.25rem;
|
|
268
268
|
letter-spacing: -0.02em;
|
|
269
269
|
}
|
|
270
|
-
body.
|
|
270
|
+
body.styled a.brand {
|
|
271
|
+
color: var(--accent);
|
|
272
|
+
text-decoration: none;
|
|
273
|
+
}
|
|
274
|
+
body.styled a.brand .brand-name {
|
|
275
|
+
text-decoration: underline;
|
|
276
|
+
text-underline-offset: 0.15em;
|
|
277
|
+
}
|
|
278
|
+
body.styled .brand svg {
|
|
271
279
|
width: 1.35rem;
|
|
272
280
|
height: 1.35rem;
|
|
273
281
|
margin-right: 0.55rem;
|
|
274
282
|
vertical-align: -0.2em;
|
|
275
283
|
}
|
|
276
|
-
body.
|
|
284
|
+
body.styled .brand-tag {
|
|
277
285
|
font-size: 0.85rem;
|
|
278
286
|
color: var(--muted);
|
|
279
287
|
}
|
|
280
|
-
body.
|
|
288
|
+
body.styled .hero {
|
|
281
289
|
max-width: 64rem;
|
|
282
290
|
margin-inline: auto;
|
|
283
291
|
padding: 4rem 1.5rem 0;
|
|
284
292
|
}
|
|
285
|
-
body.
|
|
293
|
+
body.styled .eyebrow {
|
|
286
294
|
margin: 0 0 0.75rem;
|
|
287
295
|
font-size: 0.85rem;
|
|
288
296
|
font-weight: 700;
|
|
289
297
|
letter-spacing: 0.04em;
|
|
290
298
|
color: var(--accent);
|
|
291
299
|
}
|
|
292
|
-
body.
|
|
300
|
+
body.styled .eyebrow::before {
|
|
293
301
|
content: "";
|
|
294
302
|
display: inline-block;
|
|
295
303
|
width: 1.4rem;
|
|
@@ -297,19 +305,19 @@ body.home .eyebrow::before {
|
|
|
297
305
|
margin: 0 0.5rem 0.22rem 0;
|
|
298
306
|
background: var(--accent);
|
|
299
307
|
}
|
|
300
|
-
body.
|
|
308
|
+
body.styled .hero h1 {
|
|
301
309
|
margin: 0 0 1rem;
|
|
302
310
|
font-size: clamp(2rem, 4.5vw, 2.6rem);
|
|
303
311
|
line-height: 1.5;
|
|
304
312
|
letter-spacing: -0.03em;
|
|
305
313
|
}
|
|
306
|
-
body.
|
|
314
|
+
body.styled .hero .lede {
|
|
307
315
|
margin: 0 0 1.75rem;
|
|
308
316
|
max-width: 52ch;
|
|
309
317
|
font-size: 1.2rem;
|
|
310
318
|
color: var(--muted);
|
|
311
319
|
}
|
|
312
|
-
body.
|
|
320
|
+
body.styled details {
|
|
313
321
|
max-width: 52rem;
|
|
314
322
|
margin: 0 0 1rem;
|
|
315
323
|
padding: 0;
|
|
@@ -317,43 +325,43 @@ body.home details {
|
|
|
317
325
|
border: 1px solid var(--line);
|
|
318
326
|
border-radius: 0.75rem;
|
|
319
327
|
}
|
|
320
|
-
body.
|
|
328
|
+
body.styled summary {
|
|
321
329
|
min-height: 44px;
|
|
322
330
|
padding: 0.9rem 1.25rem;
|
|
323
331
|
font-weight: 600;
|
|
324
332
|
cursor: pointer;
|
|
325
333
|
}
|
|
326
|
-
body.
|
|
334
|
+
body.styled summary:hover {
|
|
327
335
|
background: #eef3f3;
|
|
328
336
|
border-radius: 0.75rem;
|
|
329
337
|
}
|
|
330
|
-
body.
|
|
338
|
+
body.styled details[open] summary {
|
|
331
339
|
border-bottom: 1px solid var(--line);
|
|
332
340
|
border-radius: 0.75rem 0.75rem 0 0;
|
|
333
341
|
}
|
|
334
|
-
body.
|
|
342
|
+
body.styled details > :not(summary) {
|
|
335
343
|
padding-inline: 1.25rem;
|
|
336
344
|
}
|
|
337
|
-
body.
|
|
345
|
+
body.styled details h2 {
|
|
338
346
|
margin: 1.5rem 0 0.5rem;
|
|
339
347
|
}
|
|
340
|
-
body.
|
|
348
|
+
body.styled details h3 {
|
|
341
349
|
margin: 1.35rem 0 0.35rem;
|
|
342
350
|
font-size: 1.08rem;
|
|
343
351
|
}
|
|
344
|
-
body.
|
|
352
|
+
body.styled .nav-section {
|
|
345
353
|
max-width: 64rem;
|
|
346
354
|
margin-inline: auto;
|
|
347
355
|
padding: 2.25rem 1.5rem 0;
|
|
348
356
|
}
|
|
349
|
-
body.
|
|
357
|
+
body.styled .nav-section:last-child {
|
|
350
358
|
padding-bottom: 5rem;
|
|
351
359
|
}
|
|
352
|
-
body.
|
|
360
|
+
body.styled .nav-section h2 {
|
|
353
361
|
margin: 0 0 1.25rem;
|
|
354
362
|
letter-spacing: -0.01em;
|
|
355
363
|
}
|
|
356
|
-
body.
|
|
364
|
+
body.styled ul.nav {
|
|
357
365
|
margin: 0;
|
|
358
366
|
padding: 0;
|
|
359
367
|
list-style: none;
|
|
@@ -361,7 +369,7 @@ body.home ul.nav {
|
|
|
361
369
|
grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
|
|
362
370
|
gap: 1rem;
|
|
363
371
|
}
|
|
364
|
-
body.
|
|
372
|
+
body.styled ul.nav a {
|
|
365
373
|
display: block;
|
|
366
374
|
min-height: 44px;
|
|
367
375
|
height: 100%;
|
|
@@ -372,11 +380,11 @@ body.home ul.nav a {
|
|
|
372
380
|
text-decoration: none;
|
|
373
381
|
transition: background-color 0.12s ease-out, border-color 0.12s ease-out;
|
|
374
382
|
}
|
|
375
|
-
body.
|
|
383
|
+
body.styled ul.nav a:hover {
|
|
376
384
|
background: #eef4f4;
|
|
377
385
|
border-color: var(--accent);
|
|
378
386
|
}
|
|
379
|
-
body.
|
|
387
|
+
body.styled .card-title {
|
|
380
388
|
font-weight: 700;
|
|
381
389
|
font-size: 1.05rem;
|
|
382
390
|
color: var(--accent);
|
|
@@ -384,23 +392,47 @@ body.home .card-title {
|
|
|
384
392
|
text-decoration-thickness: 0.09em;
|
|
385
393
|
text-underline-offset: 0.2em;
|
|
386
394
|
}
|
|
387
|
-
body.
|
|
395
|
+
body.styled .card-title::after {
|
|
388
396
|
content: "\00a0→";
|
|
389
397
|
display: inline-block;
|
|
390
398
|
text-decoration: none;
|
|
391
399
|
font-weight: 700;
|
|
392
400
|
}
|
|
393
|
-
body.
|
|
401
|
+
body.styled ul.nav a:hover .card-title {
|
|
394
402
|
text-decoration-thickness: 0.18em;
|
|
395
403
|
}
|
|
396
|
-
body.
|
|
404
|
+
body.styled .card-desc {
|
|
397
405
|
display: block;
|
|
398
406
|
margin: 0.4rem 0 0;
|
|
399
407
|
font-size: 0.9rem;
|
|
400
408
|
color: var(--muted);
|
|
401
409
|
}
|
|
402
410
|
@media (prefers-reduced-motion: reduce) {
|
|
403
|
-
body.
|
|
411
|
+
body.styled ul.nav a {
|
|
404
412
|
transition: none;
|
|
405
413
|
}
|
|
406
414
|
}
|
|
415
|
+
body.styled .page {
|
|
416
|
+
max-width: 64rem;
|
|
417
|
+
margin-inline: auto;
|
|
418
|
+
padding: 2.5rem 1.5rem 3.5rem;
|
|
419
|
+
}
|
|
420
|
+
body.styled .page h1 {
|
|
421
|
+
margin: 0 0 1.5rem;
|
|
422
|
+
font-size: 2rem;
|
|
423
|
+
letter-spacing: -0.02em;
|
|
424
|
+
}
|
|
425
|
+
body.styled .page h2 {
|
|
426
|
+
margin: 2rem 0 0.75rem;
|
|
427
|
+
}
|
|
428
|
+
body.styled .page ol, body.styled .page ul {
|
|
429
|
+
margin: 0.5rem 0 1rem;
|
|
430
|
+
}
|
|
431
|
+
body.styled ul.links {
|
|
432
|
+
column-width: 20rem;
|
|
433
|
+
column-gap: 2.5rem;
|
|
434
|
+
}
|
|
435
|
+
body.styled ul.links li {
|
|
436
|
+
margin-bottom: 0.4rem;
|
|
437
|
+
break-inside: avoid;
|
|
438
|
+
}
|
package/web/home/index.html
CHANGED
package/web/listPages/index.html
CHANGED
|
@@ -5,25 +5,17 @@
|
|
|
5
5
|
<meta name="keywords" content="report,accessibility,a11y">
|
|
6
6
|
<title>Pages tested | Kilotest</title>
|
|
7
7
|
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
<body class="styled">
|
|
9
|
+
<header>
|
|
10
|
+
<div class="header-inner">
|
|
11
|
+
<a class="brand" href="/">__logo__<span class="brand-name">Kilotest</span></a>
|
|
12
|
+
<span class="brand-tag">Ensemble front-end testing</span>
|
|
13
|
+
</div>
|
|
14
|
+
</header>
|
|
15
|
+
<main class="page">
|
|
16
|
+
<h1>Pages tested</h1>
|
|
17
|
+
__jobSections__
|
|
16
18
|
<p>Should Kilotest <a href="requestNewTestForm.html">test a page</a> that is not listed below?</p>
|
|
17
|
-
<h2>Queued for testing</h2>
|
|
18
|
-
<ol>
|
|
19
|
-
__queue__
|
|
20
|
-
</ol>
|
|
21
|
-
<p>__noQueued__</p>
|
|
22
|
-
<h2>Being tested now</h2>
|
|
23
|
-
<ul>
|
|
24
|
-
__claimed__
|
|
25
|
-
</ul>
|
|
26
|
-
<p>__noClaimed__</p>
|
|
27
19
|
<h2>Already tested</h2>
|
|
28
20
|
__testedPages__
|
|
29
21
|
</main>
|
package/web/listPages/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ import path from 'node:path';
|
|
|
19
19
|
|
|
20
20
|
// Adds parameters to a query for the answer page.
|
|
21
21
|
const populateQuery = async (query: Record<string, any>) => {
|
|
22
|
-
const margin = ' '.repeat(
|
|
22
|
+
const margin = ' '.repeat(6);
|
|
23
23
|
// Initialize the classes of lines.
|
|
24
24
|
const lines: {requests: string[], queue: string[], claimed: string[], tested: string[]} = {
|
|
25
25
|
requests: [],
|
|
@@ -35,17 +35,11 @@ const populateQuery = async (query: Record<string, any>) => {
|
|
|
35
35
|
testRequests[url]!.forEach((req: any) => {
|
|
36
36
|
const {description, reason} = req;
|
|
37
37
|
// Add a line.
|
|
38
|
-
lines.requests.push(`${margin}<li><code>${url}</code> (${description}): ${reason}</li>`);
|
|
38
|
+
lines.requests.push(`${margin} <li><code>${url}</code> (${description}): ${reason}</li>`);
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
// Sort the lines in alphabetical order by URL and secondarily by proposed name.
|
|
42
42
|
lines.requests.sort();
|
|
43
|
-
// Add the lines to the query.
|
|
44
|
-
query.requests = lines.requests.join('\n');
|
|
45
|
-
// Add a no-requests message, if applicable, to the query.
|
|
46
|
-
query.noRequests = lines.requests.length
|
|
47
|
-
? 'Kilotest managers can <a href="enqueueForm.html">approve or reject a request</a>.'
|
|
48
|
-
: 'No requests await approval now.';
|
|
49
43
|
// Get the file names of all queued and claimed jobs.
|
|
50
44
|
const jobFileNames = await getJobNames();
|
|
51
45
|
// For each job category:
|
|
@@ -55,15 +49,41 @@ const populateQuery = async (query: Record<string, any>) => {
|
|
|
55
49
|
// Get the job.
|
|
56
50
|
const job = await getObject(path.join(jobsPath(), category, fileName)) as {target: {url: string, what: string}};
|
|
57
51
|
// Add a line.
|
|
58
|
-
lines[category].push(`${margin}<li><code>${job.target.url}</code> (${job.target.what})</li>`);
|
|
52
|
+
lines[category].push(`${margin} <li><code>${job.target.url}</code> (${job.target.what})</li>`);
|
|
59
53
|
}
|
|
60
|
-
// Add the lines to the query.
|
|
61
|
-
query[category] = lines[category].join('\n');
|
|
62
54
|
}
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
// Add a
|
|
66
|
-
|
|
55
|
+
// Initialize the conditional activity sections.
|
|
56
|
+
const sections: string[] = [];
|
|
57
|
+
// Add a section for the test requests, if there are any.
|
|
58
|
+
if (lines.requests.length) {
|
|
59
|
+
sections.push([
|
|
60
|
+
`${margin}<h2>Requested for testing</h2>`,
|
|
61
|
+
`${margin}<ol>`,
|
|
62
|
+
...lines.requests,
|
|
63
|
+
`${margin}</ol>`,
|
|
64
|
+
`${margin}<p>Kilotest managers can <a href="enqueueForm.html">approve or reject a request</a>.</p>`
|
|
65
|
+
].join('\n'));
|
|
66
|
+
}
|
|
67
|
+
// Add a section for the queued jobs, if there are any.
|
|
68
|
+
if (lines.queue.length) {
|
|
69
|
+
sections.push([
|
|
70
|
+
`${margin}<h2>Queued for testing</h2>`,
|
|
71
|
+
`${margin}<ol>`,
|
|
72
|
+
...lines.queue,
|
|
73
|
+
`${margin}</ol>`
|
|
74
|
+
].join('\n'));
|
|
75
|
+
}
|
|
76
|
+
// Add a section for the claimed jobs, if there are any.
|
|
77
|
+
if (lines.claimed.length) {
|
|
78
|
+
sections.push([
|
|
79
|
+
`${margin}<h2>Being tested now</h2>`,
|
|
80
|
+
`${margin}<ul>`,
|
|
81
|
+
...lines.claimed,
|
|
82
|
+
`${margin}</ul>`
|
|
83
|
+
].join('\n'));
|
|
84
|
+
}
|
|
85
|
+
// Add the sections to the query.
|
|
86
|
+
query.jobSections = sections.join('\n');
|
|
67
87
|
// Get extracts of the latest reports about all tested pages, sorted by page description.
|
|
68
88
|
const latestExtracts = await getReportExtracts(true);
|
|
69
89
|
const pageCount = latestExtracts.length;
|