@jrpool/kilotest 60.1.3 → 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 CHANGED
@@ -4423,5 +4423,5 @@
4423
4423
  }
4424
4424
  }
4425
4425
  ],
4426
- "version": "60.1.3"
4426
+ "version": "60.1.5"
4427
4427
  }
package/logo.svg CHANGED
@@ -1,6 +1,6 @@
1
1
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" aria-hidden="true">
2
2
  <style>
3
- .lens { stroke: #086b6b; }
3
+ .lens { stroke: #005c5c; }
4
4
  .zero { fill: #12a05c; }
5
5
  @media (prefers-color-scheme: dark) {
6
6
  .lens { stroke: #3fb8b8; }
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.3
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.3
5
+ version: 60.1.5
6
6
  license:
7
7
  name: MIT
8
8
  identifier: MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jrpool/kilotest",
3
- "version": "60.1.3",
3
+ "version": "60.1.5",
4
4
  "description": "Ensemble testing for front-end web quality",
5
5
  "type": "module",
6
6
  "main": "index.ts",
package/server.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/jrpool/kilotest",
7
7
  "source": "github"
8
8
  },
9
- "version": "60.1.3",
9
+ "version": "60.1.5",
10
10
  "remotes": [
11
11
  {
12
12
  "type": "streamable-http",
package/style.css CHANGED
@@ -208,10 +208,10 @@ ul.xPathList li {
208
208
  padding-left: 0;
209
209
  overflow-wrap: break-word;
210
210
  }
211
- body.home {
211
+ body.styled {
212
212
  --ink: #18212b;
213
- --muted: #4c5a66;
214
- --accent: #086b6b;
213
+ --muted: #424e59;
214
+ --accent: #005c5c;
215
215
  --accent-dark: #064f4f;
216
216
  --bg: #f6f8f8;
217
217
  --card: #fff;
@@ -224,36 +224,37 @@ body.home {
224
224
  color: var(--ink);
225
225
  background: var(--bg);
226
226
  }
227
- body.home, body.home *, body.home *::before, body.home *::after {
227
+ body.styled, body.styled *, body.styled *::before, body.styled *::after {
228
228
  box-sizing: border-box;
229
229
  }
230
- body.home a {
230
+ body.styled a {
231
231
  color: var(--accent);
232
232
  text-underline-offset: 0.15em;
233
233
  }
234
- body.home a:hover {
234
+ body.styled a:hover {
235
235
  color: var(--accent-dark);
236
236
  background-color: transparent;
237
237
  }
238
- body.home a:focus-visible, body.home summary:focus-visible {
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.home h2 {
243
+ body.styled h2 {
244
244
  color: var(--ink);
245
+ font-size: 1.4rem;
245
246
  }
246
- body.home li, body.home p {
247
+ body.styled li, body.styled p {
247
248
  line-height: 1.6;
248
249
  }
249
- body.home p {
250
+ body.styled p {
250
251
  margin: 0.6rem 0;
251
252
  }
252
- body.home header {
253
+ body.styled header {
253
254
  background: var(--card);
254
255
  border-bottom: 1px solid var(--line);
255
256
  }
256
- body.home .header-inner {
257
+ body.styled .header-inner {
257
258
  max-width: 64rem;
258
259
  margin-inline: auto;
259
260
  padding: 1.1rem 1.5rem;
@@ -261,35 +262,42 @@ body.home .header-inner {
261
262
  align-items: baseline;
262
263
  gap: 0.75rem;
263
264
  }
264
- body.home .brand {
265
- display: inline-flex;
266
- align-items: center;
267
- gap: 0.55rem;
265
+ body.styled .brand {
268
266
  font-weight: 800;
269
267
  font-size: 1.25rem;
270
268
  letter-spacing: -0.02em;
271
269
  }
272
- body.home .brand svg {
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 {
273
279
  width: 1.35rem;
274
280
  height: 1.35rem;
281
+ margin-right: 0.55rem;
282
+ vertical-align: -0.2em;
275
283
  }
276
- body.home .brand-tag {
284
+ body.styled .brand-tag {
277
285
  font-size: 0.85rem;
278
286
  color: var(--muted);
279
287
  }
280
- body.home .hero {
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.home .eyebrow {
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.home .eyebrow::before {
300
+ body.styled .eyebrow::before {
293
301
  content: "";
294
302
  display: inline-block;
295
303
  width: 1.4rem;
@@ -297,20 +305,19 @@ body.home .eyebrow::before {
297
305
  margin: 0 0.5rem 0.22rem 0;
298
306
  background: var(--accent);
299
307
  }
300
- body.home .hero h1 {
308
+ body.styled .hero h1 {
301
309
  margin: 0 0 1rem;
302
- max-width: 24ch;
303
- font-size: clamp(2rem, 4.5vw, 3.1rem);
304
- line-height: 1.12;
310
+ font-size: clamp(2rem, 4.5vw, 2.6rem);
311
+ line-height: 1.5;
305
312
  letter-spacing: -0.03em;
306
313
  }
307
- body.home .hero .lede {
314
+ body.styled .hero .lede {
308
315
  margin: 0 0 1.75rem;
309
316
  max-width: 52ch;
310
317
  font-size: 1.2rem;
311
318
  color: var(--muted);
312
319
  }
313
- body.home details {
320
+ body.styled details {
314
321
  max-width: 52rem;
315
322
  margin: 0 0 1rem;
316
323
  padding: 0;
@@ -318,45 +325,43 @@ body.home details {
318
325
  border: 1px solid var(--line);
319
326
  border-radius: 0.75rem;
320
327
  }
321
- body.home summary {
328
+ body.styled summary {
322
329
  min-height: 44px;
323
330
  padding: 0.9rem 1.25rem;
324
331
  font-weight: 600;
325
332
  cursor: pointer;
326
333
  }
327
- body.home summary:hover {
334
+ body.styled summary:hover {
328
335
  background: #eef3f3;
329
336
  border-radius: 0.75rem;
330
337
  }
331
- body.home details[open] summary {
338
+ body.styled details[open] summary {
332
339
  border-bottom: 1px solid var(--line);
333
340
  border-radius: 0.75rem 0.75rem 0 0;
334
341
  }
335
- body.home details > :not(summary) {
336
- margin-inline: 1.5rem;
342
+ body.styled details > :not(summary) {
343
+ padding-inline: 1.25rem;
337
344
  }
338
- body.home details h2 {
345
+ body.styled details h2 {
339
346
  margin: 1.5rem 0 0.5rem;
340
- font-size: 1.3rem;
341
347
  }
342
- body.home details h3 {
348
+ body.styled details h3 {
343
349
  margin: 1.35rem 0 0.35rem;
344
350
  font-size: 1.08rem;
345
351
  }
346
- body.home .nav-section {
352
+ body.styled .nav-section {
347
353
  max-width: 64rem;
348
354
  margin-inline: auto;
349
355
  padding: 2.25rem 1.5rem 0;
350
356
  }
351
- body.home .nav-section:last-child {
357
+ body.styled .nav-section:last-child {
352
358
  padding-bottom: 5rem;
353
359
  }
354
- body.home .nav-section h2 {
360
+ body.styled .nav-section h2 {
355
361
  margin: 0 0 1.25rem;
356
- font-size: 1.4rem;
357
362
  letter-spacing: -0.01em;
358
363
  }
359
- body.home ul.nav {
364
+ body.styled ul.nav {
360
365
  margin: 0;
361
366
  padding: 0;
362
367
  list-style: none;
@@ -364,7 +369,7 @@ body.home ul.nav {
364
369
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
365
370
  gap: 1rem;
366
371
  }
367
- body.home ul.nav a {
372
+ body.styled ul.nav a {
368
373
  display: block;
369
374
  min-height: 44px;
370
375
  height: 100%;
@@ -375,11 +380,11 @@ body.home ul.nav a {
375
380
  text-decoration: none;
376
381
  transition: background-color 0.12s ease-out, border-color 0.12s ease-out;
377
382
  }
378
- body.home ul.nav a:hover {
383
+ body.styled ul.nav a:hover {
379
384
  background: #eef4f4;
380
385
  border-color: var(--accent);
381
386
  }
382
- body.home .card-title {
387
+ body.styled .card-title {
383
388
  font-weight: 700;
384
389
  font-size: 1.05rem;
385
390
  color: var(--accent);
@@ -387,23 +392,47 @@ body.home .card-title {
387
392
  text-decoration-thickness: 0.09em;
388
393
  text-underline-offset: 0.2em;
389
394
  }
390
- body.home .card-title::after {
395
+ body.styled .card-title::after {
391
396
  content: "\00a0→";
392
397
  display: inline-block;
393
398
  text-decoration: none;
394
399
  font-weight: 700;
395
400
  }
396
- body.home ul.nav a:hover .card-title {
401
+ body.styled ul.nav a:hover .card-title {
397
402
  text-decoration-thickness: 0.18em;
398
403
  }
399
- body.home .card-desc {
404
+ body.styled .card-desc {
400
405
  display: block;
401
406
  margin: 0.4rem 0 0;
402
407
  font-size: 0.9rem;
403
408
  color: var(--muted);
404
409
  }
405
410
  @media (prefers-reduced-motion: reduce) {
406
- body.home ul.nav a {
411
+ body.styled ul.nav a {
407
412
  transition: none;
408
413
  }
409
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
+ }
@@ -51,7 +51,7 @@
51
51
  }
52
52
  </script>
53
53
  </head>
54
- <body class="home">
54
+ <body class="styled">
55
55
  <header>
56
56
  <div class="header-inner">
57
57
  <span class="brand">__logo__Kilotest</span>
@@ -76,7 +76,10 @@
76
76
  <h2>Features</h2>
77
77
  <p>With Kilotest, you can get results of already performed tests and propose web pages for testing. (Immediate testing on request may become available in a future version.)</p>
78
78
  <p>There is a <a href="/capability.md">capability document</a> that summarizes what Kilotest can do for human and AI-agent users.</p>
79
- <h2>How to use Kilotest</h2>
79
+ </details>
80
+ <details>
81
+ <summary>How to use Kilotest</summary>
82
+ <h2>Who are you?</h2>
80
83
  <h3>If you are a human user on the web</h3>
81
84
  <p>You can use Kilotest starting on this page.</p>
82
85
  <p>If your main interest is accessibility, feel free to learn more about it with our <a href="/tutorialWeb.html">tutorial</a>.</p>
@@ -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
- <main>
10
- <h1>Pages tested by <a href="/">Kilotest</a></h1>
11
- <h2>Requested for testing</h2>
12
- <ol>
13
- __requests__
14
- </ol>
15
- <p>__noRequests__</p>
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>
@@ -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(8);
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
- // Add a no-queued message, if applicable, to the query.
64
- query.noQueued = lines.queue.length ? '' : 'No pages are queued for testing.';
65
- // Add a no-claimed message, if applicable, to the query.
66
- query.noClaimed = lines.claimed.length ? '' : 'No pages are being tested now.';
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;