@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 CHANGED
@@ -4423,5 +4423,5 @@
4423
4423
  }
4424
4424
  }
4425
4425
  ],
4426
- "version": "60.1.4"
4426
+ "version": "60.1.5"
4427
4427
  }
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.4
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.4
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.4",
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.4",
9
+ "version": "60.1.5",
10
10
  "remotes": [
11
11
  {
12
12
  "type": "streamable-http",
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.home {
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.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
245
  font-size: 1.4rem;
246
246
  }
247
- body.home li, body.home p {
247
+ body.styled li, body.styled p {
248
248
  line-height: 1.6;
249
249
  }
250
- body.home p {
250
+ body.styled p {
251
251
  margin: 0.6rem 0;
252
252
  }
253
- body.home header {
253
+ body.styled header {
254
254
  background: var(--card);
255
255
  border-bottom: 1px solid var(--line);
256
256
  }
257
- body.home .header-inner {
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.home .brand {
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.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 {
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.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,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.home .hero h1 {
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.home .hero .lede {
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.home details {
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.home summary {
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.home summary:hover {
334
+ body.styled summary:hover {
327
335
  background: #eef3f3;
328
336
  border-radius: 0.75rem;
329
337
  }
330
- body.home details[open] summary {
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.home details > :not(summary) {
342
+ body.styled details > :not(summary) {
335
343
  padding-inline: 1.25rem;
336
344
  }
337
- body.home details h2 {
345
+ body.styled details h2 {
338
346
  margin: 1.5rem 0 0.5rem;
339
347
  }
340
- body.home details h3 {
348
+ body.styled details h3 {
341
349
  margin: 1.35rem 0 0.35rem;
342
350
  font-size: 1.08rem;
343
351
  }
344
- body.home .nav-section {
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.home .nav-section:last-child {
357
+ body.styled .nav-section:last-child {
350
358
  padding-bottom: 5rem;
351
359
  }
352
- body.home .nav-section h2 {
360
+ body.styled .nav-section h2 {
353
361
  margin: 0 0 1.25rem;
354
362
  letter-spacing: -0.01em;
355
363
  }
356
- body.home ul.nav {
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.home ul.nav a {
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.home ul.nav a:hover {
383
+ body.styled ul.nav a:hover {
376
384
  background: #eef4f4;
377
385
  border-color: var(--accent);
378
386
  }
379
- body.home .card-title {
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.home .card-title::after {
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.home ul.nav a:hover .card-title {
401
+ body.styled ul.nav a:hover .card-title {
394
402
  text-decoration-thickness: 0.18em;
395
403
  }
396
- body.home .card-desc {
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.home ul.nav a {
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
+ }
@@ -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>
@@ -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;