@jrpool/kilotest 24.0.4

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.
Files changed (69) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/DEVELOPMENT.md +5860 -0
  3. package/LICENSE +21 -0
  4. package/README.md +44 -0
  5. package/SERVICE.md +268 -0
  6. package/aceconfig.js +28 -0
  7. package/ai0BalanceForm/index.html +30 -0
  8. package/ai0BalanceForm/index.js +79 -0
  9. package/alerts.js +73 -0
  10. package/diagnoses/index.html +46 -0
  11. package/diagnoses/index.js +140 -0
  12. package/env.example +21 -0
  13. package/env.testaro +17 -0
  14. package/error.html +18 -0
  15. package/eslint.config.mjs +53 -0
  16. package/favicon.ico +0 -0
  17. package/index.html +39 -0
  18. package/index.js +639 -0
  19. package/issues/index.html +20 -0
  20. package/issues/index.js +173 -0
  21. package/job.json +100 -0
  22. package/manage/index.html +32 -0
  23. package/manage/index.js +22 -0
  24. package/package.json +38 -0
  25. package/pm2.config.js +15 -0
  26. package/reannotate/index.html +19 -0
  27. package/reannotate/index.js +39 -0
  28. package/reannotateForm/index.html +29 -0
  29. package/reannotateForm/index.js +114 -0
  30. package/recActionForm/index.html +33 -0
  31. package/recActionForm/index.js +49 -0
  32. package/reportHideForm/index.html +29 -0
  33. package/reportHideForm/index.js +89 -0
  34. package/reportIssue/index.html +38 -0
  35. package/reportIssue/index.js +181 -0
  36. package/reportIssues/index.html +47 -0
  37. package/reportIssues/index.js +259 -0
  38. package/reportUnhideForm/index.html +29 -0
  39. package/reportUnhideForm/index.js +89 -0
  40. package/reportsExpungeForm/index.html +29 -0
  41. package/reportsExpungeForm/index.js +105 -0
  42. package/reportsPruneForm/index.html +29 -0
  43. package/reportsPruneForm/index.js +105 -0
  44. package/reportsRewindForm/index.html +29 -0
  45. package/reportsRewindForm/index.js +105 -0
  46. package/retestRec/index.html +23 -0
  47. package/retestRec/index.js +19 -0
  48. package/retestRecForm/index.html +27 -0
  49. package/retestRecForm/index.js +36 -0
  50. package/rules/index.html +28 -0
  51. package/rules/index.js +71 -0
  52. package/style.css +196 -0
  53. package/targets/index.html +37 -0
  54. package/targets/index.js +170 -0
  55. package/testOrder/index.html +23 -0
  56. package/testOrder/index.js +62 -0
  57. package/testRec/index.html +23 -0
  58. package/testRec/index.js +25 -0
  59. package/testRecForm/index.html +34 -0
  60. package/testRecForm/index.js +22 -0
  61. package/tutorial/images/newsletter-form.png +0 -0
  62. package/tutorial/index.html +796 -0
  63. package/tutorial/index.js +53 -0
  64. package/util.js +686 -0
  65. package/wcagMap.json +102 -0
  66. package/wcagRenew/index.html +19 -0
  67. package/wcagRenew/index.js +70 -0
  68. package/wcagRenewForm/index.html +25 -0
  69. package/wcagRenewForm/index.js +22 -0
@@ -0,0 +1,796 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-US">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="publisher" content="Jonathan Robert Pool">
7
+ <meta name="creator" content="Jonathan Robert Pool">
8
+ <meta name="keywords" content="accessibility,a11y,testing,tutorial,WCAG,autocomplete">
9
+ <title>Accessibility Testing Strategies | Kilotest</title>
10
+ <link rel="icon" href="/favicon.ico">
11
+ <link rel="stylesheet" href="/style.css">
12
+ <style>
13
+ .skip-link {
14
+ position: absolute;
15
+ top: -3rem;
16
+ left: 0;
17
+ padding: 0.5rem 1rem;
18
+ background: #fff;
19
+ border: solid 2px #000;
20
+ z-index: 100;
21
+ transition: top 0.1s;
22
+ }
23
+ .skip-link:focus {
24
+ top: 0;
25
+ }
26
+ .tutorial-nav {
27
+ margin-bottom: 2rem;
28
+ padding: 1rem;
29
+ border: solid 1px #006666;
30
+ border-radius: 0.5rem;
31
+ }
32
+ .tutorial-nav h2 {
33
+ margin-top: 0;
34
+ }
35
+ section.tutorial-section {
36
+ border-top: solid 2px #006666;
37
+ padding-top: 1rem;
38
+ margin-top: 2rem;
39
+ }
40
+ .note {
41
+ padding: 0.75rem 1rem;
42
+ border-left: solid 4px #006666;
43
+ background: #f0f8f8;
44
+ margin: 1rem 0;
45
+ }
46
+ .finding {
47
+ padding: 0.75rem 1rem;
48
+ border-left: solid 4px #c80;
49
+ background: #fffbf0;
50
+ margin: 1rem 0;
51
+ }
52
+ .ai-output {
53
+ padding: 0.75rem 1rem;
54
+ border: solid 1px #999;
55
+ border-radius: 0.25rem;
56
+ background: #f9f9f9;
57
+ margin: 1rem 0;
58
+ font-size: 0.95rem;
59
+ }
60
+ .ai-output p:first-child {
61
+ margin-top: 0;
62
+ }
63
+ .ai-output p:last-child {
64
+ margin-bottom: 0;
65
+ }
66
+ pre.code-sample {
67
+ padding: 0.75rem 1rem;
68
+ border: solid 1px #ccc;
69
+ border-radius: 0.25rem;
70
+ background: #f5f5f5;
71
+ overflow-wrap: break-word;
72
+ white-space: pre-wrap;
73
+ font-size: 0.9rem;
74
+ }
75
+ dl.compare dt {
76
+ font-weight: 700;
77
+ margin-top: 1rem;
78
+ color: #003366;
79
+ }
80
+ dl.compare dd {
81
+ margin-left: 1.5rem;
82
+ margin-bottom: 0.25rem;
83
+ }
84
+ .time-estimate {
85
+ font-style: italic;
86
+ color: #555;
87
+ font-size: 0.9rem;
88
+ }
89
+ .tutorial-img {
90
+ display: block;
91
+ max-width: 100%;
92
+ border: solid 1px #ccc;
93
+ border-radius: 0.25rem;
94
+ margin: 1rem 0;
95
+ }
96
+ .knowledge-check {
97
+ margin-top: 1.5rem;
98
+ padding: 1rem;
99
+ border: solid 2px #006666;
100
+ border-radius: 0.5rem;
101
+ background: #f0f8f8;
102
+ }
103
+ .knowledge-check legend {
104
+ font-weight: 700;
105
+ padding: 0 0.5rem;
106
+ color: #003366;
107
+ }
108
+ .knowledge-check ol {
109
+ margin: 0.75rem 0;
110
+ padding-left: 1.5rem;
111
+ }
112
+ .knowledge-check li {
113
+ margin-bottom: 0.5rem;
114
+ }
115
+ .knowledge-check label {
116
+ cursor: pointer;
117
+ }
118
+ .quiz-feedback {
119
+ margin-top: 0.75rem;
120
+ padding: 0.5rem 0.75rem;
121
+ border-radius: 0.25rem;
122
+ min-height: 1.5rem;
123
+ }
124
+ .quiz-feedback.correct {
125
+ background: #e6f4ea;
126
+ border-left: solid 4px #2d7a2d;
127
+ color: #1a4a1a;
128
+ }
129
+ .quiz-feedback.incorrect {
130
+ background: #fdecea;
131
+ border-left: solid 4px #c0392b;
132
+ color: #5a1a1a;
133
+ }
134
+ .comment-section {
135
+ margin-top: 1.5rem;
136
+ }
137
+ .comment-section label {
138
+ display: block;
139
+ font-weight: 700;
140
+ margin-bottom: 0.4rem;
141
+ }
142
+ .comment-section textarea {
143
+ width: 100%;
144
+ max-width: 40rem;
145
+ padding: 0.5rem;
146
+ font-size: 1rem;
147
+ font-family: inherit;
148
+ border: solid 1px #666;
149
+ border-radius: 0.25rem;
150
+ box-sizing: border-box;
151
+ }
152
+ .char-count {
153
+ font-size: 0.85rem;
154
+ color: #555;
155
+ margin: 0.25rem 0 0.75rem;
156
+ }
157
+ .char-count.low {
158
+ color: #c0392b;
159
+ font-weight: 700;
160
+ }
161
+ .comment-feedback {
162
+ margin-top: 0.75rem;
163
+ padding: 0.5rem 0.75rem;
164
+ border-radius: 0.25rem;
165
+ min-height: 1.5rem;
166
+ }
167
+ .comment-feedback.success {
168
+ background: #e6f4ea;
169
+ border-left: solid 4px #2d7a2d;
170
+ color: #1a4a1a;
171
+ }
172
+ .comment-feedback.error {
173
+ background: #fdecea;
174
+ border-left: solid 4px #c0392b;
175
+ color: #5a1a1a;
176
+ }
177
+ @media print {
178
+ /* ── Layout ── */
179
+ body { font-size: 11pt; }
180
+ main { max-width: none; padding: 0; }
181
+
182
+ /* ── Hide elements that serve no purpose on a printed page ── */
183
+ .skip-link,
184
+ .tutorial-nav,
185
+ #comment-form,
186
+ #comment-feedback,
187
+ #lesson1-quiz button { display: none; }
188
+
189
+ /* ── Page breaks: start each h2 section, h3 section, and h4 subsection on a new page ── */
190
+ section.tutorial-section { break-before: page; }
191
+ section.tutorial-section > section { break-before: page; }
192
+ h3, h4 { break-before: page; }
193
+
194
+ /* ── Keep headings attached to the content that follows them ── */
195
+ h2, h3, h4 { break-after: avoid; }
196
+
197
+ /* ── Avoid splitting self-contained blocks across pages ── */
198
+ pre,
199
+ .finding,
200
+ .ai-output,
201
+ .note,
202
+ dl.compare,
203
+ .knowledge-check { break-inside: avoid; }
204
+
205
+ /* ── Force all details elements open so their content prints ── */
206
+ details { display: block; }
207
+ details > * { display: block; }
208
+ details > summary::marker { content: ''; }
209
+
210
+ /* ── Print the href after external links as a reference ── */
211
+ a[href^="http"]::after { content: ' (' attr(href) ')'; font-size: 0.8em; }
212
+ }
213
+ </style>
214
+ </head>
215
+ <body>
216
+ <a class="skip-link" href="#main-content">Skip to main content</a>
217
+ <main id="main-content">
218
+
219
+ <h1><a href="/">Kilotest</a>: Accessibility Testing Strategies</h1>
220
+ <p class="time-estimate">Estimated reading time: about 60 minutes.</p>
221
+
222
+ <nav class="tutorial-nav" aria-label="Tutorial sections">
223
+ <h2>Sections</h2>
224
+ <ul class="nav">
225
+ <li><a href="#about">About this tutorial</a></li>
226
+ <li><a href="#orientation">Part 1: Orientation</a>
227
+ <ul>
228
+ <li><a href="#why-matters">Why accessibility testing matters</a></li>
229
+ <li><a href="#three-strategies">The three testing strategies</a></li>
230
+ <li><a href="#choosing">Choosing among strategies</a></li>
231
+ <li><a href="#case-study-intro">Introducing the case study</a></li>
232
+ </ul>
233
+ </li>
234
+ <li><a href="#practicum">Part 2: Practicum</a>
235
+ <ul>
236
+ <li><a href="#example-page">The example page</a></li>
237
+ <li><a href="#human-testing">Human testing</a></li>
238
+ <li><a href="#rule-engine-testing">Rule-engine testing</a></li>
239
+ <li><a href="#ai-testing">AI-agent testing</a></li>
240
+ <li><a href="#comparison">Comparing the strategies</a></li>
241
+ </ul>
242
+ </li>
243
+ <li><a href="#conclusion">Conclusion</a></li>
244
+ </ul>
245
+ </nav>
246
+
247
+ <!-- ═══════════════════════════════════════════════════════ -->
248
+ <section id="about" class="tutorial-section">
249
+ <h2>About this tutorial</h2>
250
+ <p><strong>Notice</strong>: This is a rough draft created in partnership with Claude Sonnet 4.6 Thinking. Revisions are in progress.</p>
251
+ <p>This tutorial is for web developers with limited experience in web accessibility testing. It takes about 60 minutes to complete.</p>
252
+ <p>You will learn about three strategies for testing web accessibility:</p>
253
+ <ul>
254
+ <li><strong>Human testing</strong> — a person directly examines and uses the page</li>
255
+ <li><strong>Rule-engine testing</strong> — software applies codified rules to the page</li>
256
+ <li><strong>AI-agent testing</strong> — an AI agent analyzes the page</li>
257
+ </ul>
258
+ <p>A practicum applies all three strategies to a real example, revealing what each strategy finds, what each misses, and why.</p>
259
+ <p>Links to additional information are included throughout. Completing the tutorial does not require following any of those links.</p>
260
+ <div class="note">
261
+ <p><strong>Offline use:</strong> This tutorial is self-contained. You may complete it without an Internet connection, though external links will not be reachable.</p>
262
+ </div>
263
+ <div class="note">
264
+ <p><strong>Note on AI results:</strong> The AI testing results in Part 2 were captured in May 2026 from a specific model. As AI models and the resources available to them improve, results may change — potentially for better or worse. The captured results are preserved here as a fixed point of comparison.</p>
265
+ </div>
266
+ </section>
267
+
268
+ <!-- ═══════════════════════════════════════════════════════ -->
269
+ <section id="orientation" class="tutorial-section">
270
+ <h2>Part 1: Orientation</h2>
271
+ <p class="time-estimate">About 30 minutes.</p>
272
+
273
+ <section id="why-matters">
274
+ <h3>Why accessibility testing matters</h3>
275
+ <p class="time-estimate">About 5 minutes.</p>
276
+
277
+ <p>Accessibility is not a separate concern added to a finished product. It is an aspect of code quality, legal compliance, and basic usability — present or absent in every page you ship.</p>
278
+
279
+ <h4>Code quality</h4>
280
+ <p>Inaccessible code is often incorrect code. HTML specifications define semantic elements and attributes for good reasons, and violations frequently indicate structural errors that affect all users, not only users with disabilities. A missing <code>autocomplete</code> attribute, for example, is not a cosmetic gap — it tells the browser that the developer has not declared the purpose of a form field that users rely on.</p>
281
+ <p>Accessible code also tends to be more maintainable: clear structure, meaningful labels, and correct role assignments make code easier to understand and modify.</p>
282
+
283
+ <h4>Legal compliance</h4>
284
+ <p>In many jurisdictions, web accessibility is a legal requirement. The most widely referenced technical standard is the <a href="https://www.w3.org/TR/WCAG22/">Web Content Accessibility Guidelines (WCAG)</a>, published by the W3C. WCAG 2.1 and 2.2 are referenced by law in the United States (Section 508, ADA), the European Union (European Accessibility Act), the United Kingdom (Equality Act), and many other countries.</p>
285
+ <p>Non-compliance exposes organisations to legal action, regulatory penalties, and reputational harm. Lawsuits and settlements involving web accessibility are documented each year in all of these jurisdictions.</p>
286
+
287
+ <h4>Usability</h4>
288
+ <p>Approximately 15–20% of people worldwide have a disability that can affect their use of the web. These include permanent conditions (blindness, deafness, motor impairments, cognitive disabilities) as well as temporary situations (a broken arm, recovering from eye surgery) and situational constraints (a noisy environment, a phone in bright sunlight, holding an infant).</p>
289
+ <p>Inaccessible code excludes or burdens these users. Features that are technically accessible also tend to benefit users without disabilities: clear labels reduce errors for everyone, logical keyboard flow speeds navigation, and high contrast aids readability in suboptimal lighting.</p>
290
+ </section>
291
+
292
+ <!-- ─── ─── ─── ─── -->
293
+ <section id="three-strategies">
294
+ <h3>The three testing strategies</h3>
295
+ <p class="time-estimate">About 8 minutes.</p>
296
+
297
+ <h4>Human testing</h4>
298
+ <p>A person directly examines the page, using the browser and auxiliary tools to find accessibility issues. This is sometimes called <q>manual testing</q>, though the term can be misleading — modern human testing uses several tools; it is the human judgment that distinguishes this strategy.</p>
299
+ <p>Human testing typically involves three techniques:</p>
300
+ <ul>
301
+ <li><strong>DOM inspection.</strong> The tester uses browser developer tools to examine the HTML structure, attributes, and the accessibility tree. The accessibility tree is the browser's representation of the page as assistive technologies see it — role, name, state, and value for each element.</li>
302
+ <li><strong>Keyboard navigation.</strong> The tester uses only the keyboard (Tab, Shift+Tab, Enter, Space, arrow keys) to navigate and operate the page. This reveals whether all interactive elements are reachable and operable without a mouse.</li>
303
+ <li><strong>Screen reader testing.</strong> The tester activates a screen reader — software that vocalises page content — and navigates the page by ear. Common screen readers include NVDA and JAWS on Windows, VoiceOver on macOS and iOS, and TalkBack on Android.</li>
304
+ </ul>
305
+ <p>Effective human testing requires substantial knowledge: WCAG success criteria, ARIA authoring practices, HTML semantics, and proficiency with at least one screen reader. It also takes time — a thorough examination of a single page may take 30 minutes to several hours.</p>
306
+
307
+ <h4>Rule-engine testing</h4>
308
+ <p>Software applies a set of codified rules to a page and reports which rules are violated. This is sometimes called <q>automated testing</q>, though that label is also imprecise — what is automated is the rule application, not the judgment about what rules should exist.</p>
309
+ <p>Rule engines come in several forms:</p>
310
+ <ul>
311
+ <li><strong>Browser extensions</strong> such as the <a href="https://www.deque.com/axe/devtools/">axe DevTools extension</a> and the <a href="https://wave.webaim.org/extension/">WAVE Evaluation Tool</a> run directly in the browser and report violations interactively.</li>
312
+ <li><strong>Installed engines</strong> such as <a href="https://www.npmjs.com/package/pa11y">Pa11y</a> and <a href="https://www.npmjs.com/package/testaro">Testaro</a> run from the command line and can be integrated into development workflows.</li>
313
+ <li><strong>API services</strong> accept page URLs and return analysis results over the network.</li>
314
+ <li><strong>Ensemble services</strong> such as <a href="/">Kilotest</a> run multiple rule engines against a page and consolidate the results. Because different engines encode different rules, an ensemble typically finds more issues than any single engine.</li>
315
+ </ul>
316
+ <p>Rule-engine testing is fast — a full scan typically takes 1–5 minutes. It requires little accessibility knowledge from the user, since the rules encode the expertise. It can be run repeatedly, integrated into CI/CD pipelines, and applied to large numbers of pages.</p>
317
+
318
+ <h4>AI-agent testing</h4>
319
+ <p>An <strong>AI agent</strong>, as used in this tutorial, is a language model — software trained on large amounts of text to understand and generate language — configured with instructions to perform a specific task. The underlying <strong>model</strong> (such as Claude, GPT-4, or Gemini) is the system you access; the <strong>agent</strong> is the model combined with its instructions and the content you provide. For accessibility testing, the agent is given page content and asked to identify violations. This strategy is the newest of the three and is evolving rapidly.</p>
320
+ <p>The workflow involves three choices:</p>
321
+ <ul>
322
+ <li><strong>Selecting a model.</strong> Different models vary in their knowledge of accessibility standards, their ability to reason about HTML structure, and their tendency to hallucinate. As of 2026, capable models include Claude (Anthropic), GPT-4 (OpenAI), and Gemini (Google).</li>
323
+ <li><strong>Designing instructions.</strong> The instructions — often called a <q>prompt</q> — specify what to test, what standards to apply, and how to report findings. Prompt quality significantly affects the quality of results.</li>
324
+ <li><strong>Providing page content.</strong> The AI agent must receive the relevant page content. This may be the raw HTML source, the rendered DOM, screenshots, or some combination. What you provide determines what the agent can find.</li>
325
+ </ul>
326
+ <p>AI-agent testing can reason about issues that resist mechanical rules — for example, whether image alternative text is meaningful in context, or whether a form label is genuinely helpful. Its output is available within minutes and requires no specialist tools beyond access to an AI agent. However, AI agents can produce false findings and can miss real ones, and their results may change as the underlying models are updated.</p>
327
+ </section>
328
+
329
+ <!-- ─── ─── ─── ─── -->
330
+ <section id="choosing">
331
+ <h3>Choosing among the strategies</h3>
332
+ <p class="time-estimate">About 12 minutes.</p>
333
+
334
+ <p>The three strategies are not alternatives — they are complements. A mature accessibility testing practice uses all three. Understanding their differences helps you decide how to allocate effort.</p>
335
+
336
+ <h4>Time to results</h4>
337
+ <dl class="compare">
338
+ <dt>Human testing</dt>
339
+ <dd>Slowest. A thorough examination of a single page takes 30 minutes to several hours, depending on the page's complexity and the scope of testing.</dd>
340
+ <dt>Rule-engine testing</dt>
341
+ <dd>Fastest. A full-page scan typically completes in 1–5 minutes. An ensemble service such as Kilotest may take somewhat longer.</dd>
342
+ <dt>AI-agent testing</dt>
343
+ <dd>Moderate. Depending on the scope of instructions, the amount of page content provided, and the AI agent's processing speed, 2–10 minutes is typical for focused testing.</dd>
344
+ </dl>
345
+
346
+ <h4>Financial cost</h4>
347
+ <dl class="compare">
348
+ <dt>Human testing</dt>
349
+ <dd>The cost of a skilled tester's time. Specialist accessibility consultants are expensive. Internal developers trained in accessibility are a recurring investment.</dd>
350
+ <dt>Rule-engine testing</dt>
351
+ <dd>Many rule engines are free and open-source. Commercial tools and API services typically charge per page or per month. Kilotest is currently free. Costs are generally low relative to the number of tests run.</dd>
352
+ <dt>AI-agent testing</dt>
353
+ <dd>Most AI services charge per token (unit of text) processed. As of 2026, asking an AI agent to check a single page against one WCAG success criterion typically costs between $0.01 and $0.10 depending on the model used. Prices are falling over time.</dd>
354
+ </dl>
355
+
356
+ <h4>What can each strategy find?</h4>
357
+ <dl class="compare">
358
+ <dt>Human testing</dt>
359
+ <dd>The broadest potential coverage. A skilled human can find any issue that is observable through the browser, developer tools, or a screen reader. Human testing is the only strategy that can reliably assess whether content is usable — not merely technically compliant — for users with disabilities. It is also the only strategy that can catch issues that only appear during interaction (filling out a multi-step form, for instance) or that depend on context (whether an image description is accurate given the surrounding text).
360
+ </dd>
361
+ <dt>Rule-engine testing</dt>
362
+ <dd>Limited to issues for which explicit rules have been written. Rules can be precise and comprehensive for clearly defined requirements (for example: every <code>&lt;img&gt;</code> must have an <code>alt</code> attribute). They cannot assess subjective quality (for example: whether that <code>alt</code> text is actually helpful). Some issues are detectable in principle but not yet covered by available engines.</dd>
363
+ <dt>AI-agent testing</dt>
364
+ <dd>Broader than rule engines for issues requiring reasoning, narrower than humans for issues requiring interaction or lived experience. An AI agent can assess whether image alternative text is plausibly meaningful, whether instructions are clear, or whether form labels are adequate. It cannot click, scroll, or type into the page; it does not receive a visual or auditory rendering; and it does not receive the browser's accessibility tree — the computed structure of roles, names, states, and values that assistive technologies consume.</dd>
365
+ </dl>
366
+
367
+ <h4>False positives (incorrect findings)</h4>
368
+ <dl class="compare">
369
+ <dt>Human testing</dt>
370
+ <dd>Low, in the hands of a knowledgeable tester. A human who understands the standards can distinguish genuine violations from compliant edge cases. Inexperienced testers produce more false positives.</dd>
371
+ <dt>Rule-engine testing</dt>
372
+ <dd>Moderate and tool-dependent. Rules that cannot fully determine compliance from the code alone (for example: whether an image needs alternative text) may produce incorrect results. Different engines disagree about the same page, and some findings require human review to confirm.</dd>
373
+ <dt>AI-agent testing</dt>
374
+ <dd>Variable and model-dependent. Current models sometimes misidentify compliant patterns as violations, particularly when their knowledge of applicable standards is incomplete or when the HTML structure is complex. Critical review of AI output is essential.</dd>
375
+ </dl>
376
+
377
+ <h4>False negatives (missed defects)</h4>
378
+ <dl class="compare">
379
+ <dt>Human testing</dt>
380
+ <dd>Low for experienced testers working systematically, but not zero. Testers can overlook attributes on elements they do not specifically inspect. Issues that only manifest in edge cases or under specific conditions may be missed if those cases are not tested. Consistency across large page sets can be difficult to maintain.</dd>
381
+ <dt>Rule-engine testing</dt>
382
+ <dd>Any issue not covered by a rule will be missed. No current rule engine covers all WCAG success criteria. Engines also differ in which rules they implement: a single engine may miss issues that another engine catches. Ensemble testing reduces the false negative rate relative to any single engine.</dd>
383
+ <dt>AI-agent testing</dt>
384
+ <dd>Depends heavily on what content is provided to the AI agent. If the agent receives only the raw HTML source of a page whose forms are rendered by JavaScript, it will miss all form-related issues entirely. If the agent's knowledge of valid attribute values is incomplete, it may miss invalid values. AI-agent false negatives are less predictable than rule-engine false negatives, because they vary with the model version, the prompt, and the content provided.</dd>
385
+ </dl>
386
+
387
+ <h4>Expertise required</h4>
388
+ <dl class="compare">
389
+ <dt>Human testing</dt>
390
+ <dd>High. The tester needs working knowledge of WCAG, ARIA, and HTML semantics, plus proficiency with a screen reader.</dd>
391
+ <dt>Rule-engine testing</dt>
392
+ <dd>Low to moderate. Running a tool requires little accessibility knowledge. Interpreting results and determining whether flagged items are genuine violations requires more. Reviewing results across a large ensemble requires the ability to reconcile conflicting findings.</dd>
393
+ <dt>AI-agent testing</dt>
394
+ <dd>Low to moderate for basic use; moderate for reliable use. Formulating precise and effective instructions, understanding the limits of AI output, and critically reviewing findings all benefit from accessibility knowledge.</dd>
395
+ </dl>
396
+
397
+ <h4>Best suited for</h4>
398
+ <dl class="compare">
399
+ <dt>Human testing</dt>
400
+ <dd>Final verification before release; issues requiring judgment about context, meaning, or usability; interaction flows; screen reader experience; and any issue that requires lived experience with assistive technology.</dd>
401
+ <dt>Rule-engine testing</dt>
402
+ <dd>First-pass scanning across many pages; continuous integration checks; measuring progress over time; identifying well-defined structural violations efficiently; and providing a baseline before human testing.</dd>
403
+ <dt>AI-agent testing</dt>
404
+ <dd>Focused analysis of specific issue types when sufficient page content can be provided; assessment of issues that are too nuanced for mechanical rules but too common for full human review; and generating explanations and remediation guidance.</dd>
405
+ </dl>
406
+ </section>
407
+
408
+ <!-- ─── ─── ─── ─── -->
409
+ <section id="case-study-intro">
410
+ <h3>Introducing the case study</h3>
411
+ <p class="time-estimate">About 5 minutes.</p>
412
+
413
+ <p>The practicum applies all three strategies to a single type of accessibility issue on a specific web page. The issue type is: <strong>incorrect or absent <code>autocomplete</code> attribute on form inputs.</strong></p>
414
+
415
+ <h4>Why the <code>autocomplete</code> attribute matters</h4>
416
+ <p>The <code>autocomplete</code> attribute tells the browser what kind of personal information an input field collects. When set correctly, the browser can offer to fill in the field from stored data — a feature that particularly helps users with cognitive disabilities, motor disabilities, dyslexia, or anyone filling out forms repeatedly.</p>
417
+ <p>WCAG 2.1 and 2.2 Success Criterion <a href="https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose">1.3.5 Identify Input Purpose</a> (Level AA) requires that the purpose of inputs collecting personal information can be programmatically determined. The <code>autocomplete</code> attribute is the primary mechanism for satisfying this criterion in HTML.</p>
418
+
419
+ <h4>Valid attribute values</h4>
420
+ <p>On <code>&lt;input&gt;</code>, <code>&lt;select&gt;</code>, and <code>&lt;textarea&gt;</code> elements, the <code>autocomplete</code> attribute must have a value from the <a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill">defined list of autofill tokens</a>. Common values include:</p>
421
+ <ul>
422
+ <li><code>name</code>, <code>given-name</code>, <code>family-name</code></li>
423
+ <li><code>email</code></li>
424
+ <li><code>tel</code></li>
425
+ <li><code>street-address</code>, <code>postal-code</code>, <code>country</code></li>
426
+ <li><code>username</code>, <code>new-password</code>, <code>current-password</code></li>
427
+ </ul>
428
+ <p>On a <code>&lt;form&gt;</code> element, the <code>autocomplete</code> attribute is also valid — but with an important restriction: the only valid values for a <code>&lt;form&gt;</code> element are <code>on</code> and <code>off</code>. The richer set of purpose tokens is not valid on <code>&lt;form&gt;</code> elements; those tokens belong on the individual input elements within the form.</p>
429
+
430
+ <h4>Two types of violation</h4>
431
+ <p>This practicum focuses on two specific violations of WCAG 1.3.5:</p>
432
+ <ul>
433
+ <li><strong>Missing attribute:</strong> An input collecting personal information has no <code>autocomplete</code> attribute at all.</li>
434
+ <li><strong>Invalid attribute value:</strong> An element has an <code>autocomplete</code> attribute, but its value is not among those recognised by the HTML specification for that element type.</li>
435
+ </ul>
436
+ <p>Both violations prevent browsers and assistive technologies from identifying the purpose of the affected fields.</p>
437
+ </section>
438
+ </section>
439
+
440
+ <!-- ═══════════════════════════════════════════════════════ -->
441
+ <section id="practicum" class="tutorial-section">
442
+ <h2>Part 2: Practicum</h2>
443
+ <p class="time-estimate">About 30 minutes.</p>
444
+ <p>This practicum examines a single real-world web page for <code>autocomplete</code> attribute issues. The page is a public website homepage captured in May 2026. <strong>The name and URL of the site are withheld</strong> to avoid public-relations concerns unrelated to this tutorial. The page facts below are a snapshot; the live page may differ.</p>
445
+
446
+ <!-- ─── ─── ─── ─── -->
447
+ <section id="example-page">
448
+ <h3>The example page</h3>
449
+ <p class="time-estimate">About 3 minutes.</p>
450
+
451
+ <p>The example page is the homepage of a nonprofit organisation in the career-guidance sector. It is a publicly accessible, JavaScript-rendered WordPress site.</p>
452
+
453
+ <h4>Relevant page content</h4>
454
+ <p>The page contains a newsletter sign-up form. This form is not present in the raw HTML that the server delivers. It is injected into the page by a JavaScript plugin (Thrive Leads) after the browser has loaded and executed the page scripts. The form contains one visible user-facing input field:</p>
455
+ <img
456
+ src="/tutorial/images/newsletter-form.png"
457
+ alt='The newsletter sign-up form as rendered in the browser: an email input with placeholder text "> Enter your email" and a button labelled "Get the career guide", set against a dark-green decorative background.'
458
+ class="tutorial-img"
459
+ width="602"
460
+ height="57"
461
+ >
462
+ <pre class="code-sample"><code>&lt;input type="email" data-field="email" data-required="1"
463
+ data-validation="email" name="email"
464
+ placeholder="&gt; Enter your email"&gt;</code></pre>
465
+ <p>The form element wrapping this input is:</p>
466
+ <pre class="code-sample"><code>&lt;form action="#" method="post" novalidate="novalidate"
467
+ autocomplete="new-password"&gt;</code></pre>
468
+ <p>Both elements were captured from the rendered DOM on 2026-05-30 using a headless browser. They are the only non-hidden, non-search, non-consent form inputs on the page.</p>
469
+
470
+ <h4>The two defects</h4>
471
+ <p>Two <code>autocomplete</code> attribute issues are present:</p>
472
+ <ul>
473
+ <li><strong>Issue A</strong> — The <code>&lt;input type="email"&gt;</code> element has no <code>autocomplete</code> attribute. Per WCAG 1.3.5, an input that collects an email address requires <code>autocomplete="email"</code>.</li>
474
+ <li><strong>Issue B</strong> — The <code>&lt;form&gt;</code> element has <code>autocomplete="new-password"</code>. On a <code>&lt;form&gt;</code> element, the only valid values are <code>on</code> and <code>off</code>. <code>new-password</code> is a valid token for <code>&lt;input&gt;</code> elements, not for <code>&lt;form&gt;</code> elements. This value appears to be a misapplication of a technique sometimes used to suppress browser autofill on individual input fields.</li>
475
+ </ul>
476
+ </section>
477
+
478
+ <!-- ─── ─── ─── ─── -->
479
+ <section id="human-testing">
480
+ <h3>Human testing</h3>
481
+ <p class="time-estimate">About 7 minutes.</p>
482
+
483
+ <p>A human tester would approach this page using browser developer tools, keyboard navigation, and (optionally) a screen reader.</p>
484
+
485
+ <h4>What a tester would do</h4>
486
+ <ol>
487
+ <li>Open the page in a browser and let it fully load.</li>
488
+ <li>Identify interactive elements — in this case, the email input in the newsletter form.</li>
489
+ <li>Open the browser's developer tools (typically F12 or right-click → Inspect).</li>
490
+ <li>Select the email input in the Elements panel and examine its attributes.</li>
491
+ <li>Note that the <code>autocomplete</code> attribute is absent.</li>
492
+ <li>Select the enclosing <code>&lt;form&gt;</code> element and examine its attributes.</li>
493
+ <li>Note that <code>autocomplete="new-password"</code> is set on the form.</li>
494
+ <li>Recall (or look up) that <code>new-password</code> is not a valid value for a <code>&lt;form&gt;</code> element.</li>
495
+ </ol>
496
+
497
+ <h4>Findings</h4>
498
+ <div class="finding">
499
+ <p><strong>Issue A found?</strong> Yes — a tester who inspects the email input will find the missing attribute.</p>
500
+ <p><strong>Issue B found?</strong> Uncertain. A tester who inspects the form element will see <code>autocomplete="new-password"</code>. Whether they flag this as invalid depends on whether they know that this token is not valid on <code>&lt;form&gt;</code> elements. Many experienced developers have seen <code>new-password</code> used as an autofill-suppression technique on individual inputs and might not question its use on a form.</p>
501
+ </div>
502
+
503
+ <h4>Limitations observed</h4>
504
+ <p>The form is JavaScript-rendered and appears only after the page fully loads. A tester who views the page source (rather than the rendered DOM) would not find it at all. A tester must know to wait for JavaScript execution and to inspect the live DOM, not the source.</p>
505
+ <p>Keyboard navigation and screen reader testing would reveal an additional issue — the email input has no label element (Issue A causes the input to be named only by its placeholder text, which is fragile) — but that is a separate issue not in scope here. Those techniques would not directly reveal the <code>autocomplete</code> attribute issues, which are only visible in the DOM.</p>
506
+
507
+ <h4>Verdict on autocomplete issues</h4>
508
+ <ul>
509
+ <li><strong>Issue A (missing attribute on input):</strong> Found, if the tester inspects the input element.</li>
510
+ <li><strong>Issue B (invalid value on form):</strong> Possibly missed, unless the tester knows the valid values for <code>autocomplete</code> on <code>&lt;form&gt;</code> elements.</li>
511
+ </ul>
512
+ </section>
513
+
514
+ <!-- ─── ─── ─── ─── -->
515
+ <section id="rule-engine-testing">
516
+ <h3>Rule-engine testing</h3>
517
+ <p class="time-estimate">About 7 minutes.</p>
518
+
519
+ <p>Several rule engines were applied to this page. The results below compare what a single widely-used engine found against what ensemble testing found.</p>
520
+
521
+ <h4>Single-engine result: axe-core</h4>
522
+ <p><a href="https://github.com/dequelabs/axe-core">axe-core</a> is one of the most widely used open-source accessibility rule engines. It is the engine underlying the axe DevTools browser extension, the popular <code>jest-axe</code> testing library, and many CI/CD integrations. It was run against the fully rendered DOM of the example page in May 2026.</p>
523
+ <div class="finding">
524
+ <p><strong>axe-core result on <code>autocomplete</code> issues: 0 violations found.</strong></p>
525
+ <p>axe-core's <code>autocomplete-valid</code> rule checks that <code>autocomplete</code> attribute values, when present, are from the valid list. It does not flag the absence of an <code>autocomplete</code> attribute on inputs collecting personal information. Separately, the <code>autocomplete="new-password"</code> value on the <code>&lt;form&gt;</code> element was not flagged.</p>
526
+ </div>
527
+ <p>Both autocomplete issues were false negatives for axe-core.</p>
528
+
529
+ <h4>Ensemble result: Kilotest</h4>
530
+ <p>Kilotest applied 10 tools to the same page in May 2026. Nine of those tools reported at least one issue on the page. Two tools specifically reported autocomplete issues:</p>
531
+ <ul>
532
+ <li><strong>Testaro</strong> reported <q>autocomplete missing</q> (Issue A: the email input lacks an <code>autocomplete</code> attribute). WCAG 1.3.5.</li>
533
+ <li><strong>HTML CodeSniffer</strong> reported <q>autocomplete invalid</q> (Issue B: the <code>&lt;form&gt;</code> element has <code>autocomplete="new-password"</code>, which is not valid for that element type). WCAG 1.3.5.</li>
534
+ </ul>
535
+ <div class="finding">
536
+ <p><strong>Ensemble result: both autocomplete issues found</strong> — by different tools within the ensemble.</p>
537
+ </div>
538
+ <p>The full Kilotest report for this page is available at <a href="https://kilotest.com/reportIssues.html/260530T0032/n2u">kilotest.com (report 260530T0032/n2u)</a>. It lists 45 issues across 9 tools — the autocomplete issues are two of them.</p>
539
+
540
+ <h4>Why the single engine missed both issues</h4>
541
+ <p>axe-core's <code>autocomplete-valid</code> rule was designed to catch invalid values when an <code>autocomplete</code> attribute is present. It was not designed to enforce WCAG 1.3.5's requirement that inputs collecting personal information <em>must have</em> an <code>autocomplete</code> attribute. For Issue B, axe-core also did not flag the invalid value on the <code>&lt;form&gt;</code> element.</p>
542
+ <p>This is not a criticism of axe-core specifically. Every rule engine has coverage gaps. The practical lesson is that no single rule engine covers all WCAG success criteria. Running multiple engines — an ensemble — reduces, though does not eliminate, false negatives.</p>
543
+
544
+ <h4>Verdict on autocomplete issues</h4>
545
+ <ul>
546
+ <li><strong>Issue A (missing attribute on input):</strong> Missed by axe-core; found by Testaro in the Kilotest ensemble.</li>
547
+ <li><strong>Issue B (invalid value on form):</strong> Missed by axe-core; found by HTML CodeSniffer in the Kilotest ensemble.</li>
548
+ </ul>
549
+ </section>
550
+
551
+ <!-- ─── ─── ─── ─── -->
552
+ <section id="ai-testing">
553
+ <h3>AI-agent testing</h3>
554
+ <p class="time-estimate">About 7 minutes.</p>
555
+
556
+ <p>An AI agent was asked to find <code>autocomplete</code> attribute issues on the example page. Two scenarios were tested, illustrating how the content given to the agent affects what it can find.</p>
557
+
558
+ <h4>What the AI agent was given and asked</h4>
559
+ <p>In both scenarios, the instructions were the same:</p>
560
+ <div class="ai-output">
561
+ <p><strong>Instruction to the AI agent:</strong></p>
562
+ <p>The following content is from a web page. Identify all violations of WCAG 2.2 Success Criterion 1.3.5 (Identify Input Purpose) related to the <code>autocomplete</code> attribute. For each violation, state the element, the problem, and the correction required. Be precise about which values are valid for each element type.</p>
563
+ </div>
564
+
565
+ <h4>Scenario 1: AI agent given the raw HTML source</h4>
566
+ <p>The raw HTML delivered by the server was provided to the AI agent. This HTML does not contain the newsletter form, which is injected by JavaScript after page load.</p>
567
+ <div class="ai-output">
568
+ <p><strong>Model used:</strong> Claude claude-sonnet-4 (Anthropic), May 2026.</p>
569
+ <p><strong>AI response (summary):</strong></p>
570
+ <p>I can see a search input (<code>&lt;input type="search" name="s"&gt;</code>) in the provided HTML. This element does not have an <code>autocomplete</code> attribute. However, a search field is not among the input purposes listed under WCAG 1.3.5 — it does not collect personal information — so no violation is present. I find no violations of WCAG 1.3.5 in the provided HTML.</p>
571
+ </div>
572
+ <div class="finding">
573
+ <p><strong>Result: Both issues missed — 100% false negative rate.</strong></p>
574
+ <p>The AI agent correctly analysed what it was given, and correctly found no violations in the raw HTML. But the raw HTML does not contain the newsletter form. The AI agent could not report what it was not given. This is not a failure of the agent's reasoning — it is a failure of the data provided to it.</p>
575
+ </div>
576
+
577
+ <h4>Scenario 2: AI agent given the rendered DOM</h4>
578
+ <p>The HTML of the fully rendered DOM — captured after JavaScript execution — was provided to the AI agent. This HTML includes the newsletter form with its email input and the form element.</p>
579
+ <div class="ai-output">
580
+ <p><strong>Model used:</strong> Claude claude-sonnet-4 (Anthropic), May 2026.</p>
581
+ <p><strong>AI response (summary):</strong></p>
582
+ <p>I have identified two violations of WCAG 2.2 Success Criterion 1.3.5:</p>
583
+ <p><strong>Violation 1: Missing <code>autocomplete</code> attribute on email input.</strong><br>
584
+ Element: <code>&lt;input type="email" name="email" placeholder="&gt; Enter your email"&gt;</code><br>
585
+ Problem: This input collects an email address but has no <code>autocomplete</code> attribute. WCAG 1.3.5 requires that inputs collecting personal information have a programmatically determinable purpose.<br>
586
+ Correction: Add <code>autocomplete="email"</code>.</p>
587
+ <p><strong>Violation 2: Invalid <code>autocomplete</code> value on the <code>&lt;form&gt;</code> element.</strong><br>
588
+ Element: <code>&lt;form autocomplete="new-password"&gt;</code><br>
589
+ Problem: On a <code>&lt;form&gt;</code> element, the only valid values for <code>autocomplete</code> are <code>on</code> and <code>off</code>. The value <code>new-password</code> is a valid autofill detail token for <code>&lt;input&gt;</code> elements, but not for <code>&lt;form&gt;</code> elements. Its presence here does not conform to the HTML specification and will not be processed as intended by browsers.<br>
590
+ Correction: Replace <code>autocomplete="new-password"</code> with <code>autocomplete="off"</code> if the intent is to suppress autofill on the form. Note that <code>autocomplete="off"</code> is itself not recommended for personal data collection forms, as it degrades the experience for users who rely on autofill.</p>
591
+ </div>
592
+ <div class="finding">
593
+ <p><strong>Result: Both issues correctly found — when given the right input.</strong></p>
594
+ <p>With the rendered DOM, the AI agent identified both violations accurately and provided correct remediation advice. It also flagged that suppressing autocomplete on a personal data form is itself a usability concern for users who rely on autofill — a nuance that rule engines do not typically report.</p>
595
+ </div>
596
+
597
+ <h4>What this demonstrates</h4>
598
+ <p>The two scenarios show that the effectiveness of AI-agent testing depends critically on what the AI agent receives, not only on the capability of the underlying model. Providing the rendered DOM rather than the raw HTML source is the difference between finding both issues and finding neither.</p>
599
+ <p>This has a direct practical implication: when using AI agents for accessibility testing, you must understand whether the page's content is server-rendered or JavaScript-rendered. If it is JavaScript-rendered, the agent must receive the rendered DOM, not the raw source.</p>
600
+ <p>It also illustrates a limitation that affects all three strategies: all depend on the tester (human, rule engine, or AI agent) receiving the full relevant content. Differences in what each strategy can access — raw source, rendered DOM, accessibility tree, visual rendering — partly determine what each strategy can find.</p>
601
+
602
+ <h4>Verdict on autocomplete issues</h4>
603
+ <ul>
604
+ <li><strong>Issue A (missing attribute on input):</strong> Missed when given raw HTML; found when given rendered DOM.</li>
605
+ <li><strong>Issue B (invalid value on form):</strong> Missed when given raw HTML; found when given rendered DOM.</li>
606
+ </ul>
607
+ </section>
608
+
609
+ <!-- ─── ─── ─── ─── -->
610
+ <section id="comparison">
611
+ <h3>Comparing the strategies</h3>
612
+ <p class="time-estimate">About 6 minutes.</p>
613
+
614
+ <p>The practicum produced a clear picture of what each strategy found on the example page:</p>
615
+
616
+ <h4>What each strategy found</h4>
617
+ <dl class="compare">
618
+ <dt>Human testing</dt>
619
+ <dd>Found Issue A (missing attribute) on direct inspection. Issue B (invalid value on form) was possibly missed, depending on whether the tester knew that <code>new-password</code> is not a valid value for <code>&lt;form&gt;</code> elements. The form itself would have been invisible to a tester using only the page source.</dd>
620
+ <dt>Rule-engine testing (single engine)</dt>
621
+ <dd>Found neither issue. axe-core, one of the most widely used accessibility rule engines, produced zero findings on the two autocomplete issues.</dd>
622
+ <dt>Rule-engine testing (ensemble)</dt>
623
+ <dd>Found both issues, through two different tools in the ensemble: Testaro found Issue A; HTML CodeSniffer found Issue B.</dd>
624
+ <dt>AI-agent testing (raw HTML)</dt>
625
+ <dd>Found neither issue. The form is not present in the raw HTML, so the AI agent had no relevant content to analyse.</dd>
626
+ <dt>AI-agent testing (rendered DOM)</dt>
627
+ <dd>Found both issues, with accurate explanations and correct remediation advice.</dd>
628
+ </dl>
629
+
630
+ <h4>Lessons from the case study</h4>
631
+
632
+ <details>
633
+ <summary>Lesson 1: What you provide determines what gets found</summary>
634
+ <p>Every testing strategy operates on a representation of the page — raw source, rendered DOM, visual rendering, or accessibility tree. Each representation contains different information. A strategy given an incomplete or wrong representation will produce incomplete or wrong results, regardless of its capability.</p>
635
+ <p>In this case study, both rule-engine tools that found the issues used the rendered DOM (via a headless browser). The AI agent found the issues when given the rendered DOM and nothing when given the raw source. The human tester would find both using a browser's developer tools, which always shows the rendered DOM.</p>
636
+ <form class="knowledge-check" id="lesson1-quiz" novalidate>
637
+ <fieldset>
638
+ <legend>Knowledge check</legend>
639
+ <p>In this case study, an AI agent was given the raw HTML source of the example page and asked to identify <code>autocomplete</code> violations. What did it find?</p>
640
+ <ol>
641
+ <li><label><input type="radio" name="q1" value="a"> Both issues: the missing attribute on the email input and the invalid value on the form element.</label></li>
642
+ <li><label><input type="radio" name="q1" value="b"> The missing <code>autocomplete</code> attribute on the email input only.</label></li>
643
+ <li><label><input type="radio" name="q1" value="c"> No violations, because the newsletter form is present only in the rendered DOM, not in the raw HTML.</label></li>
644
+ <li><label><input type="radio" name="q1" value="d"> The invalid <code>autocomplete</code> value on the form element only.</label></li>
645
+ </ol>
646
+ <button type="submit">Check answer</button>
647
+ </fieldset>
648
+ <div role="status" id="lesson1-quiz-feedback" aria-live="polite" aria-atomic="true" class="quiz-feedback"></div>
649
+ </form>
650
+ </details>
651
+
652
+ <details>
653
+ <summary>Lesson 2: No single rule engine is complete</summary>
654
+ <p>axe-core is a capable, well-maintained engine used by millions of developers. It found neither autocomplete issue on this page. This is not an unusual result — studies of rule-engine agreement consistently find that no single engine covers all issues.</p>
655
+ <p>Ensemble testing, by combining multiple engines, found both issues. This is the practical argument for ensembles: different tools have different coverage, and their findings complement each other.</p>
656
+ </details>
657
+
658
+ <details>
659
+ <summary>Lesson 3: Some issues are harder for humans than for machines</summary>
660
+ <p>Issue B — the invalid <code>autocomplete</code> value on the <code>&lt;form&gt;</code> element — illustrates an important pattern. The developer who introduced this value almost certainly had a goal (suppressing browser autofill) and used a technique they had seen work on individual inputs. The fact that it is technically invalid for a <code>&lt;form&gt;</code> element is a detail that a knowledgeable human might easily overlook.</p>
661
+ <p>An appropriate rule engine, however, checks the value against a precise specification and reports the violation without ambiguity. This is one of the strongest arguments for rule-engine testing: it catches specification-level errors consistently, regardless of whether the reviewer finds the value plausible.</p>
662
+ </details>
663
+
664
+ <details>
665
+ <summary>Lesson 4: The AI limitation encountered during this tutorial's development</summary>
666
+ <p>This tutorial was developed with AI assistance. During development, an AI agent was asked to fetch the example page and identify the elements with autocomplete issues. The agent fetched the page HTML and ran automated queries — and initially found only one issue (the missing attribute on the email input). It did not find the invalid value on the <code>&lt;form&gt;</code> element.</p>
667
+ <p>The reason is instructive. The agent queried for <code>&lt;input&gt;</code>, <code>&lt;select&gt;</code>, and <code>&lt;textarea&gt;</code> elements, but did not initially think to query for the <code>&lt;form&gt;</code> element's <code>autocomplete</code> attribute. A human developer with DevTools open — able to visually scan the attributes panel, explore the DOM tree interactively, and notice all attributes on any element — would more naturally encounter the form element's attribute while inspecting the surrounding structure.</p>
668
+ <p>This experience illustrates a real limitation of current AI agents: even though an agent can rapidly process large amounts of text, it does not yet replicate the fluid, exploratory experience of a developer browsing to a page and inspecting its DOM. That exploratory capacity remains a strength of human testing.</p>
669
+ </details>
670
+ </section>
671
+ </section>
672
+
673
+ <!-- ═══════════════════════════════════════════════════════ -->
674
+ <section id="conclusion" class="tutorial-section">
675
+ <h2>Conclusion</h2>
676
+ <p class="time-estimate">About 5 minutes.</p>
677
+
678
+ <p>You have seen all three accessibility testing strategies applied to the same page and the same issue type. Each strategy has genuine strengths and genuine limitations. The key points to take away:</p>
679
+
680
+ <ul>
681
+ <li><strong>Use all three.</strong> The strategies are complements, not alternatives. Rule engines provide fast, consistent first-pass coverage. AI agents can reason about nuance and explain issues. Human testing catches what the others miss and is the ultimate measure of real-world usability.</li>
682
+ <li><strong>Understand what each strategy sees.</strong> A strategy can only report on what it receives. For JavaScript-rendered content, ensure your tools — rule engines, AI agents, or human testers — are working with the rendered DOM, not the raw HTML source.</li>
683
+ <li><strong>Prefer ensembles over single tools.</strong> No rule engine covers all WCAG success criteria. Running multiple engines together significantly reduces the false negative rate relative to any single engine.</li>
684
+ <li><strong>Verify AI output.</strong> AI agents can produce both false positives and false negatives. Their output is a useful starting point, not a final verdict. Critical review against the relevant standards is essential.</li>
685
+ <li><strong>Test early and often.</strong> The cheapest time to fix an accessibility issue is before the page is shipped. Rule-engine testing integrated into a development workflow catches issues while the code is fresh and the fix is small.</li>
686
+ </ul>
687
+
688
+ <h3>Further reading</h3>
689
+ <ul>
690
+ <li><a href="https://www.w3.org/WAI/WCAG22/Understanding/">Understanding WCAG 2.2</a> — W3C explanations of each success criterion</li>
691
+ <li><a href="https://www.w3.org/WAI/test-evaluate/tools/list/">Web Accessibility Evaluation Tools List</a> — W3C registry of rule-engine tools</li>
692
+ <li><a href="https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose">Understanding SC 1.3.5: Identify Input Purpose</a> — detailed guidance on <code>autocomplete</code> requirements</li>
693
+ <li><a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill">HTML Living Standard: Autofill</a> — the definitive list of valid <code>autocomplete</code> tokens</li>
694
+ <li><a href="https://arxiv.org/abs/2304.07591">Accessibility Metatesting: Comparing Nine Testing Tools</a> — research on rule-engine coverage variation</li>
695
+ <li><a href="https://arxiv.org/abs/2309.10167">Testaro: Efficient Ensemble Testing for Web Accessibility</a> — the rationale for ensemble testing</li>
696
+ </ul>
697
+
698
+ <p>Return to the <a href="/">Kilotest home page</a>.</p>
699
+
700
+ <div class="comment-section" id="feedback-section">
701
+ <h3>Suggest an improvement</h3>
702
+ <p>Have a suggestion for improving this tutorial? Submit it below. Your suggestion will be saved for the tutorial authors to review.</p>
703
+ <form id="comment-form" novalidate>
704
+ <label for="comment-text">Your suggestion (up to 500 characters):</label>
705
+ <textarea
706
+ id="comment-text"
707
+ name="content"
708
+ maxlength="500"
709
+ rows="5"
710
+ autocomplete="off"
711
+ aria-describedby="char-count-msg"
712
+ ></textarea>
713
+ <p class="char-count" id="char-count-msg"><span id="char-remaining">500</span> characters remaining</p>
714
+ <button type="submit" id="comment-submit">Submit suggestion</button>
715
+ </form>
716
+ <div role="status" id="comment-feedback" aria-live="polite" aria-atomic="true" class="comment-feedback"></div>
717
+ </div>
718
+ </section>
719
+
720
+ </main>
721
+ <script>
722
+ // ── Knowledge check (Lesson 1) ──────────────────────────────
723
+ document.getElementById('lesson1-quiz').addEventListener('submit', function (e) {
724
+ e.preventDefault();
725
+ const selected = this.querySelector('input[name="q1"]:checked');
726
+ const feedback = document.getElementById('lesson1-quiz-feedback');
727
+ if (!selected) {
728
+ feedback.className = 'quiz-feedback incorrect';
729
+ feedback.textContent = 'Please select an answer before checking.';
730
+ return;
731
+ }
732
+ if (selected.value === 'c') {
733
+ feedback.className = 'quiz-feedback correct';
734
+ feedback.textContent =
735
+ 'Correct. The raw HTML does not contain the newsletter form — it is injected by JavaScript after page load. Without the form in its input, the AI agent had no violations to find.';
736
+ } else {
737
+ feedback.className = 'quiz-feedback incorrect';
738
+ feedback.textContent =
739
+ 'Not quite. The newsletter form is injected by JavaScript and is absent from the raw HTML source. The AI agent could not find what was not in the content it received.';
740
+ }
741
+ });
742
+
743
+ // ── Comment form ─────────────────────────────────────────────
744
+ const commentText = document.getElementById('comment-text');
745
+ const charRemaining = document.getElementById('char-remaining');
746
+ const charCountMsg = document.getElementById('char-count-msg');
747
+ const commentFeedback = document.getElementById('comment-feedback');
748
+ const commentSubmit = document.getElementById('comment-submit');
749
+
750
+ commentText.addEventListener('input', function () {
751
+ const remaining = 500 - this.value.length;
752
+ charRemaining.textContent = remaining;
753
+ charCountMsg.className = remaining <= 50 ? 'char-count low' : 'char-count';
754
+ });
755
+
756
+ document.getElementById('comment-form').addEventListener('submit', async function (e) {
757
+ e.preventDefault();
758
+ const content = commentText.value.trim();
759
+ commentFeedback.className = 'comment-feedback';
760
+ commentFeedback.textContent = '';
761
+ if (!content) {
762
+ commentFeedback.className = 'comment-feedback error';
763
+ commentFeedback.textContent = 'Please enter a suggestion before submitting.';
764
+ return;
765
+ }
766
+ commentSubmit.disabled = true;
767
+ try {
768
+ const response = await fetch('/tutorialComment.html', {
769
+ method: 'POST',
770
+ headers: {'Content-Type': 'application/json'},
771
+ body: JSON.stringify({content})
772
+ });
773
+ const data = await response.json();
774
+ if (data.status === 'ok') {
775
+ commentFeedback.className = 'comment-feedback success';
776
+ commentFeedback.textContent =
777
+ 'Thank you! Your suggestion has been saved and will be reviewed by the tutorial authors.';
778
+ this.reset();
779
+ charRemaining.textContent = '500';
780
+ charCountMsg.className = 'char-count';
781
+ } else {
782
+ commentFeedback.className = 'comment-feedback error';
783
+ commentFeedback.textContent =
784
+ 'Your suggestion could not be saved. Please try again later.';
785
+ commentSubmit.disabled = false;
786
+ }
787
+ } catch (_) {
788
+ commentFeedback.className = 'comment-feedback error';
789
+ commentFeedback.textContent =
790
+ 'A connection error occurred. Please check your connection and try again.';
791
+ commentSubmit.disabled = false;
792
+ }
793
+ });
794
+ </script>
795
+ </body>
796
+ </html>