aria-ease 6.10.0 → 6.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/{bin/buildContracts-S22V7AGV.js → dist/buildContracts-FT6KWUJN.js} +3 -3
- package/dist/{chunk-XERMSYEH.js → chunk-FZ7GMIJB.js} +0 -21
- package/{bin/chunk-NI3MQCAS.js → dist/chunk-GJGUY643.js} +2 -2
- package/{bin → dist}/cli.cjs +30 -67
- package/{bin → dist}/cli.js +4 -4
- package/dist/{configLoader-DWHOHXHL.js → configLoader-Q7N5XV4P.js} +2 -2
- package/{bin/configLoader-UJZHQBYS.js → dist/configLoader-REHK3S3Q.js} +1 -1
- package/{bin/contractTestRunnerPlaywright-QDXSK3FE.js → dist/contractTestRunnerPlaywright-DIXP5DQ3.js} +5 -20
- package/dist/{contractTestRunnerPlaywright-WNWQYSXZ.js → contractTestRunnerPlaywright-EWAWQVHT.js} +5 -20
- package/dist/index.cjs +164 -122
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +155 -71
- package/dist/src/{Types.d-yGC2bBaB.d.ts → Types.d-DYfYR3Vc.d.cts} +1 -1
- package/dist/src/{Types.d-yGC2bBaB.d.cts → Types.d-DYfYR3Vc.d.ts} +1 -1
- package/dist/src/accordion/index.d.cts +1 -1
- package/dist/src/accordion/index.d.ts +1 -1
- package/dist/src/block/index.d.cts +1 -1
- package/dist/src/block/index.d.ts +1 -1
- package/dist/src/checkbox/index.d.cts +1 -1
- package/dist/src/checkbox/index.d.ts +1 -1
- package/dist/src/combobox/index.d.cts +1 -1
- package/dist/src/combobox/index.d.ts +1 -1
- package/dist/src/menu/index.d.cts +1 -1
- package/dist/src/menu/index.d.ts +1 -1
- package/dist/src/radio/index.d.cts +1 -1
- package/dist/src/radio/index.d.ts +1 -1
- package/dist/src/tabs/index.d.cts +1 -1
- package/dist/src/tabs/index.d.ts +1 -1
- package/dist/src/toggle/index.d.cts +1 -1
- package/dist/src/toggle/index.d.ts +1 -1
- package/dist/src/utils/test/{chunk-XERMSYEH.js → chunk-FZ7GMIJB.js} +1 -21
- package/dist/src/utils/test/{configLoader-SHJSRG2A.js → configLoader-NA7IBCS3.js} +2 -2
- package/dist/src/utils/test/{contractTestRunnerPlaywright-Z2AHXSNM.js → contractTestRunnerPlaywright-CIZOXYRW.js} +5 -19
- package/dist/src/utils/test/dsl/index.cjs +139 -60
- package/dist/src/utils/test/dsl/index.d.cts +3 -0
- package/dist/src/utils/test/dsl/index.d.ts +3 -0
- package/dist/src/utils/test/dsl/index.js +139 -60
- package/dist/src/utils/test/index.cjs +19 -55
- package/dist/src/utils/test/index.js +16 -10
- package/{bin/test-O3J4ZPQR.js → dist/test-HBPCSYH5.js} +16 -11
- package/package.json +5 -7
- package/bin/AccordionComponentStrategy-4ZEIQ2V6.js +0 -42
- package/bin/ComboboxComponentStrategy-OGRVZXAF.js +0 -64
- package/bin/MenuComponentStrategy-JAMTCSNF.js +0 -81
- package/bin/TabsComponentStrategy-3SQURPMX.js +0 -29
- package/bin/chunk-I2KLQ2HA.js +0 -22
- package/bin/chunk-PK5L2SAF.js +0 -17
- package/bin/chunk-XERMSYEH.js +0 -363
- package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +0 -290
- package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +0 -463
- package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +0 -562
- package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +0 -361
- /package/{bin → dist}/audit-RM6TCZ5C.js +0 -0
- /package/{bin → dist}/badgeHelper-JOWO6RQG.js +0 -0
- /package/{bin → dist}/chunk-JJEPLK7L.js +0 -0
- /package/{bin → dist}/cli.d.cts +0 -0
- /package/{bin → dist}/cli.d.ts +0 -0
- /package/{bin → dist}/formatters-32KQIIYS.js +0 -0
package/bin/chunk-XERMSYEH.js
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
// src/utils/test/contract/contract.json
|
|
2
|
-
var contract_default = {
|
|
3
|
-
menu: {
|
|
4
|
-
path: "./aria-contracts/menu/menu.contract.json",
|
|
5
|
-
component: "menu"
|
|
6
|
-
},
|
|
7
|
-
"combobox.listbox": {
|
|
8
|
-
path: "./aria-contracts/combobox/combobox.listbox.contract.json",
|
|
9
|
-
component: "combobox.listbox"
|
|
10
|
-
},
|
|
11
|
-
accordion: {
|
|
12
|
-
path: "./aria-contracts/accordion/accordion.contract.json",
|
|
13
|
-
component: "accordion"
|
|
14
|
-
},
|
|
15
|
-
tabs: {
|
|
16
|
-
path: "./aria-contracts/tabs/tabs.contract.json",
|
|
17
|
-
component: "tabs"
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
// src/utils/test/src/ContractReporter.ts
|
|
22
|
-
var ContractReporter = class {
|
|
23
|
-
startTime = 0;
|
|
24
|
-
componentName = "";
|
|
25
|
-
staticPasses = 0;
|
|
26
|
-
staticFailures = 0;
|
|
27
|
-
staticWarnings = 0;
|
|
28
|
-
dynamicResults = [];
|
|
29
|
-
totalTests = 0;
|
|
30
|
-
skipped = 0;
|
|
31
|
-
warnings = 0;
|
|
32
|
-
isPlaywright = false;
|
|
33
|
-
isCustomContract = false;
|
|
34
|
-
apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
|
|
35
|
-
hasPrintedStaticSection = false;
|
|
36
|
-
hasPrintedDynamicSection = false;
|
|
37
|
-
constructor(isPlaywright = false, isCustomContract = false) {
|
|
38
|
-
this.isPlaywright = isPlaywright;
|
|
39
|
-
this.isCustomContract = isCustomContract;
|
|
40
|
-
}
|
|
41
|
-
log(message) {
|
|
42
|
-
process.stderr.write(message + "\n");
|
|
43
|
-
}
|
|
44
|
-
start(componentName, totalTests, apgUrl) {
|
|
45
|
-
this.startTime = Date.now();
|
|
46
|
-
this.componentName = componentName;
|
|
47
|
-
this.totalTests = totalTests;
|
|
48
|
-
this.hasPrintedStaticSection = false;
|
|
49
|
-
this.hasPrintedDynamicSection = false;
|
|
50
|
-
if (apgUrl) {
|
|
51
|
-
this.apgUrl = apgUrl;
|
|
52
|
-
}
|
|
53
|
-
const mode = this.isPlaywright ? "Playwright (Real Browser)" : "jsdom (Fast)";
|
|
54
|
-
this.log(`
|
|
55
|
-
${"\u2550".repeat(60)}`);
|
|
56
|
-
this.log(`\u{1F50D} Testing ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} Component - ${mode}`);
|
|
57
|
-
this.log(`${"\u2550".repeat(60)}
|
|
58
|
-
`);
|
|
59
|
-
}
|
|
60
|
-
reportStatic(passes, failures, warnings = 0) {
|
|
61
|
-
this.staticPasses = passes;
|
|
62
|
-
this.staticFailures = failures;
|
|
63
|
-
this.staticWarnings = warnings;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Report individual static test pass
|
|
67
|
-
*/
|
|
68
|
-
reportStaticTest(description, status, failureMessage, level) {
|
|
69
|
-
if (!this.hasPrintedStaticSection) {
|
|
70
|
-
this.log(`${"\u2500".repeat(60)}`);
|
|
71
|
-
this.log(`\u{1F9EA} Static Assertions`);
|
|
72
|
-
this.log(`${"\u2500".repeat(60)}`);
|
|
73
|
-
this.hasPrintedStaticSection = true;
|
|
74
|
-
}
|
|
75
|
-
const icon = status === "pass" ? "\u2713" : status === "warn" ? "\u26A0" : status === "skip" ? "\u25CB" : "\u2717";
|
|
76
|
-
this.log(` ${icon} ${description}`);
|
|
77
|
-
if (level) {
|
|
78
|
-
this.log(` \u21B3 level=${level}`);
|
|
79
|
-
}
|
|
80
|
-
if ((status === "fail" || status === "warn" || status === "skip") && failureMessage) {
|
|
81
|
-
this.log(` \u21B3 ${failureMessage}`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Report individual dynamic test result
|
|
86
|
-
*/
|
|
87
|
-
reportTest(test, status, failureMessage) {
|
|
88
|
-
if (!this.hasPrintedDynamicSection) {
|
|
89
|
-
this.log("");
|
|
90
|
-
this.log(`${"\u2500".repeat(60)}`);
|
|
91
|
-
this.log(`\u2328\uFE0F Dynamic Interaction Tests`);
|
|
92
|
-
this.log(`${"\u2500".repeat(60)}`);
|
|
93
|
-
this.hasPrintedDynamicSection = true;
|
|
94
|
-
}
|
|
95
|
-
const result = {
|
|
96
|
-
description: test.description,
|
|
97
|
-
status,
|
|
98
|
-
failureMessage,
|
|
99
|
-
level: test.level
|
|
100
|
-
};
|
|
101
|
-
if (status === "skip") {
|
|
102
|
-
result.skipReason = "Requires real browser (addEventListener events)";
|
|
103
|
-
}
|
|
104
|
-
this.dynamicResults.push(result);
|
|
105
|
-
const icons = { pass: "\u2713", fail: "\u2717", warn: "\u26A0", skip: "\u25CB" };
|
|
106
|
-
const levelPrefix = test.level ? `[${test.level.toUpperCase()}] ` : "";
|
|
107
|
-
this.log(` ${icons[status]} ${levelPrefix}${test.description}`);
|
|
108
|
-
if (status === "skip" && !this.isPlaywright) {
|
|
109
|
-
this.log(` \u21B3 Skipped in jsdom (runs in Playwright)`);
|
|
110
|
-
}
|
|
111
|
-
if (status === "fail" && failureMessage) {
|
|
112
|
-
this.log(` \u21B3 ${failureMessage}`);
|
|
113
|
-
}
|
|
114
|
-
if (status === "warn" && failureMessage) {
|
|
115
|
-
this.log(` \u21B3 ${failureMessage}`);
|
|
116
|
-
}
|
|
117
|
-
if (status === "skip" && failureMessage) {
|
|
118
|
-
this.log(` \u21B3 ${failureMessage}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Report all failures with actionable context
|
|
123
|
-
*/
|
|
124
|
-
reportFailures(failures) {
|
|
125
|
-
if (failures.length === 0) return;
|
|
126
|
-
this.log(`
|
|
127
|
-
${"\u2500".repeat(60)}`);
|
|
128
|
-
this.log(`\u274C Failures (${failures.length}):
|
|
129
|
-
`);
|
|
130
|
-
failures.forEach((failure, index) => {
|
|
131
|
-
this.log(`${index + 1}. ${failure}`);
|
|
132
|
-
if (failure.includes("aria-")) {
|
|
133
|
-
this.log(` \u{1F4A1} Add the missing ARIA attribute to improve screen reader support`);
|
|
134
|
-
} else if (failure.includes("focus")) {
|
|
135
|
-
this.log(` \u{1F4A1} Check keyboard event handlers and focus management`);
|
|
136
|
-
} else if (failure.includes("visible")) {
|
|
137
|
-
this.log(` \u{1F4A1} Verify display/visibility styles and state management`);
|
|
138
|
-
}
|
|
139
|
-
this.log("");
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
reportWarnings() {
|
|
143
|
-
const warnings = this.dynamicResults.filter((r) => r.status === "warn");
|
|
144
|
-
if (warnings.length === 0 && this.staticWarnings === 0) return;
|
|
145
|
-
this.log(`
|
|
146
|
-
${"\u2500".repeat(60)}`);
|
|
147
|
-
this.log(`\u26A0\uFE0F Warnings (${this.staticWarnings + warnings.length}):
|
|
148
|
-
`);
|
|
149
|
-
this.log(`These checks are failing but treated as warnings under the active strictness mode.
|
|
150
|
-
`);
|
|
151
|
-
warnings.forEach((test, index) => {
|
|
152
|
-
this.log(`${index + 1}. ${test.description}`);
|
|
153
|
-
if (test.failureMessage) {
|
|
154
|
-
this.log(` \u21B3 ${test.failureMessage}`);
|
|
155
|
-
}
|
|
156
|
-
if (test.level) {
|
|
157
|
-
this.log(` \u21B3 level=${test.level}`);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
if (this.apgUrl) {
|
|
161
|
-
this.log(`
|
|
162
|
-
Reference: ${this.apgUrl}
|
|
163
|
-
`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Report skipped tests with helpful context
|
|
168
|
-
*/
|
|
169
|
-
reportSkipped() {
|
|
170
|
-
if (this.skipped === 0 || this.isPlaywright) return;
|
|
171
|
-
const skippedTests = this.dynamicResults.filter((r) => r.status === "skip");
|
|
172
|
-
this.log(`
|
|
173
|
-
${"\u2500".repeat(60)}`);
|
|
174
|
-
this.log(`\u2139\uFE0F Skipped Tests (${this.skipped}):
|
|
175
|
-
`);
|
|
176
|
-
this.log(`These tests use native keyboard events via addEventListener,`);
|
|
177
|
-
this.log(`which jsdom cannot simulate. They run successfully in Playwright.
|
|
178
|
-
`);
|
|
179
|
-
skippedTests.forEach((test, index) => {
|
|
180
|
-
this.log(`${index + 1}. ${test.description}`);
|
|
181
|
-
});
|
|
182
|
-
this.log(`
|
|
183
|
-
\u{1F4A1} Run with Playwright for full validation:`);
|
|
184
|
-
this.log(` testUiComponent('${this.componentName}', null, 'http://localhost:5173/test-harness?component=component_name')
|
|
185
|
-
`);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Generate final summary with statistics
|
|
189
|
-
*/
|
|
190
|
-
summary(failures) {
|
|
191
|
-
const duration = Date.now() - this.startTime;
|
|
192
|
-
const dynamicPasses = this.dynamicResults.filter((r) => r.status === "pass").length;
|
|
193
|
-
const dynamicFailures = this.dynamicResults.filter((r) => r.status === "fail").length;
|
|
194
|
-
const dynamicWarnings = this.dynamicResults.filter((r) => r.status === "warn").length;
|
|
195
|
-
this.skipped = this.dynamicResults.filter((r) => r.status === "skip").length;
|
|
196
|
-
this.warnings = this.staticWarnings + dynamicWarnings;
|
|
197
|
-
const totalPasses = this.staticPasses + dynamicPasses;
|
|
198
|
-
const totalFailures = this.staticFailures + dynamicFailures;
|
|
199
|
-
const totalRun = totalPasses + totalFailures + this.warnings;
|
|
200
|
-
const getComponentMessage = () => {
|
|
201
|
-
const componentDisplayName = `${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)}`;
|
|
202
|
-
if (this.isCustomContract) {
|
|
203
|
-
return `${componentDisplayName} component validates against your custom accessibility policy \u2713`;
|
|
204
|
-
}
|
|
205
|
-
return `${componentDisplayName} component meets Aria-Ease baseline WAI-ARIA expectations \u2713`;
|
|
206
|
-
};
|
|
207
|
-
if (failures.length > 0) {
|
|
208
|
-
this.reportFailures(failures);
|
|
209
|
-
}
|
|
210
|
-
this.reportWarnings();
|
|
211
|
-
this.reportSkipped();
|
|
212
|
-
this.log(`
|
|
213
|
-
${"\u2550".repeat(60)}`);
|
|
214
|
-
this.log(`\u{1F4CA} Summary
|
|
215
|
-
`);
|
|
216
|
-
if (totalFailures === 0 && this.skipped === 0 && this.warnings === 0) {
|
|
217
|
-
this.log(`\u2705 All ${totalRun} tests passed!`);
|
|
218
|
-
this.log(` ${getComponentMessage()}`);
|
|
219
|
-
} else if (totalFailures === 0) {
|
|
220
|
-
this.log(`\u2705 ${totalPasses}/${totalRun} tests passed`);
|
|
221
|
-
if (this.skipped > 0) {
|
|
222
|
-
this.log(`\u25CB ${this.skipped} tests skipped`);
|
|
223
|
-
}
|
|
224
|
-
if (this.warnings > 0) {
|
|
225
|
-
this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings > 1 ? "s" : ""}`);
|
|
226
|
-
}
|
|
227
|
-
this.log(` ${getComponentMessage()}`);
|
|
228
|
-
} else {
|
|
229
|
-
this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
|
|
230
|
-
this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);
|
|
231
|
-
if (this.warnings > 0) {
|
|
232
|
-
this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings > 1 ? "s" : ""}`);
|
|
233
|
-
}
|
|
234
|
-
if (this.skipped > 0) {
|
|
235
|
-
this.log(`\u25CB ${this.skipped} test${this.skipped > 1 ? "s" : ""} skipped`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
this.log(`\u23F1\uFE0F Duration: ${duration}ms`);
|
|
239
|
-
this.log(`${"\u2550".repeat(60)}
|
|
240
|
-
`);
|
|
241
|
-
if (totalFailures > 0) {
|
|
242
|
-
this.log(`\u{1F527} Next Steps:`);
|
|
243
|
-
this.log(` 1. Review the failures above`);
|
|
244
|
-
this.log(` 2. Fix ARIA attributes and keyboard handlers`);
|
|
245
|
-
this.log(` 3. Re-run tests to verify fixes
|
|
246
|
-
`);
|
|
247
|
-
} else if (!this.isPlaywright && this.skipped > 0) {
|
|
248
|
-
this.log(`\u2728 Optional: Run Playwright tests for complete validation
|
|
249
|
-
`);
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
passes: totalPasses,
|
|
253
|
-
failures: totalFailures,
|
|
254
|
-
skipped: this.skipped,
|
|
255
|
-
duration
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Report an error during test execution
|
|
260
|
-
*/
|
|
261
|
-
error(message, context) {
|
|
262
|
-
this.log(`
|
|
263
|
-
\u274C Error: ${message}`);
|
|
264
|
-
if (context) {
|
|
265
|
-
this.log(` Context: ${context}`);
|
|
266
|
-
}
|
|
267
|
-
this.log("");
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
// src/utils/test/src/strictness.ts
|
|
272
|
-
var FALLBACK_LEVEL = "required";
|
|
273
|
-
function normalizeLevel(level) {
|
|
274
|
-
if (level === "required" || level === "recommended" || level === "optional") {
|
|
275
|
-
return level;
|
|
276
|
-
}
|
|
277
|
-
return FALLBACK_LEVEL;
|
|
278
|
-
}
|
|
279
|
-
function normalizeStrictness(strictness) {
|
|
280
|
-
if (strictness === "minimal" || strictness === "balanced" || strictness === "strict" || strictness === "paranoid") {
|
|
281
|
-
return strictness;
|
|
282
|
-
}
|
|
283
|
-
return "balanced";
|
|
284
|
-
}
|
|
285
|
-
function resolveEnforcement(level, strictness) {
|
|
286
|
-
const matrix = {
|
|
287
|
-
minimal: {
|
|
288
|
-
required: "error",
|
|
289
|
-
recommended: "ignore",
|
|
290
|
-
optional: "ignore"
|
|
291
|
-
},
|
|
292
|
-
balanced: {
|
|
293
|
-
required: "error",
|
|
294
|
-
recommended: "warning",
|
|
295
|
-
optional: "ignore"
|
|
296
|
-
},
|
|
297
|
-
strict: {
|
|
298
|
-
required: "error",
|
|
299
|
-
recommended: "error",
|
|
300
|
-
optional: "warning"
|
|
301
|
-
},
|
|
302
|
-
paranoid: {
|
|
303
|
-
required: "error",
|
|
304
|
-
recommended: "error",
|
|
305
|
-
optional: "error"
|
|
306
|
-
}
|
|
307
|
-
};
|
|
308
|
-
return matrix[strictness][level];
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// src/utils/test/src/playwrightTestHarness.ts
|
|
312
|
-
import { chromium } from "playwright";
|
|
313
|
-
var sharedBrowser = null;
|
|
314
|
-
var sharedContext = null;
|
|
315
|
-
async function getOrCreateBrowser() {
|
|
316
|
-
if (!sharedBrowser) {
|
|
317
|
-
sharedBrowser = await chromium.launch({
|
|
318
|
-
headless: true,
|
|
319
|
-
// Launch with clean browser profile - no extensions, no user data
|
|
320
|
-
args: [
|
|
321
|
-
"--disable-extensions",
|
|
322
|
-
"--disable-blink-features=AutomationControlled"
|
|
323
|
-
]
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
return sharedBrowser;
|
|
327
|
-
}
|
|
328
|
-
async function getOrCreateContext() {
|
|
329
|
-
if (!sharedContext) {
|
|
330
|
-
const browser = await getOrCreateBrowser();
|
|
331
|
-
sharedContext = await browser.newContext({
|
|
332
|
-
// Isolated context - no permissions, no geolocation, etc.
|
|
333
|
-
permissions: [],
|
|
334
|
-
// Ignore HTTPS errors for local dev servers
|
|
335
|
-
ignoreHTTPSErrors: true
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
return sharedContext;
|
|
339
|
-
}
|
|
340
|
-
async function createTestPage() {
|
|
341
|
-
const context = await getOrCreateContext();
|
|
342
|
-
return await context.newPage();
|
|
343
|
-
}
|
|
344
|
-
async function closeSharedBrowser() {
|
|
345
|
-
if (sharedContext) {
|
|
346
|
-
await sharedContext.close();
|
|
347
|
-
sharedContext = null;
|
|
348
|
-
}
|
|
349
|
-
if (sharedBrowser) {
|
|
350
|
-
await sharedBrowser.close();
|
|
351
|
-
sharedBrowser = null;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
export {
|
|
356
|
-
contract_default,
|
|
357
|
-
ContractReporter,
|
|
358
|
-
normalizeLevel,
|
|
359
|
-
normalizeStrictness,
|
|
360
|
-
resolveEnforcement,
|
|
361
|
-
createTestPage,
|
|
362
|
-
closeSharedBrowser
|
|
363
|
-
};
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"meta": {
|
|
3
|
-
"id": "aria-ease.contract.accordion",
|
|
4
|
-
"version": "1.0.0",
|
|
5
|
-
"created": "09-02-2026",
|
|
6
|
-
"lastUpdated": "19-03-2026",
|
|
7
|
-
"description": "ARIA Accordion interaction contract. Validates the ARIA and interaction contract for a custom accordion component following the ARIA Authoring Practices Guide accordion with show/hide pattern.",
|
|
8
|
-
"source": {
|
|
9
|
-
"apg": "https://www.w3.org/WAI/ARIA/apg/patterns/accordion/",
|
|
10
|
-
"wcag": ["2.2 AA"]
|
|
11
|
-
},
|
|
12
|
-
"W3CName": "Accordion"
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
"selectors": {
|
|
16
|
-
"trigger": "[data-accordion] [aria-controls][aria-expanded], [aria-controls][aria-expanded]",
|
|
17
|
-
"focusable": "[data-accordion] [aria-controls][aria-expanded], [aria-controls][aria-expanded]",
|
|
18
|
-
"relative": "[data-accordion] [aria-controls][aria-expanded], [aria-controls][aria-expanded]",
|
|
19
|
-
"panel": "[role='region'][aria-labelledby], [role='region'][aria-labelledby]"
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
"observables": {
|
|
23
|
-
"observable": "focus | visible | attribute | role",
|
|
24
|
-
"target": "trigger | relative | panel | focusable",
|
|
25
|
-
"relative": "first | last | next | previous"
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
"relationships": [
|
|
29
|
-
{
|
|
30
|
-
"type": "aria-reference",
|
|
31
|
-
"from": "trigger",
|
|
32
|
-
"attribute": "aria-controls",
|
|
33
|
-
"to": "panel"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"type": "aria-reference",
|
|
37
|
-
"from": "panel",
|
|
38
|
-
"attribute": "aria-labelledby",
|
|
39
|
-
"to": "trigger"
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
|
|
43
|
-
"static": [
|
|
44
|
-
{
|
|
45
|
-
"assertions": [
|
|
46
|
-
{
|
|
47
|
-
"target": "trigger",
|
|
48
|
-
"assertion": "toHaveAttribute",
|
|
49
|
-
"attribute": "aria-expanded",
|
|
50
|
-
"expectedValue": "true | false",
|
|
51
|
-
"failureMessage": "Accordion trigger button doesn't conform to the ARIA Accordion Button pattern as specified in APG 1.2. Accordion trigger button should have 'aria-expanded=true | false' attribute. This helps assistive technology to keep track of the open or close state of the accordion panel div that the button controls."
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"target": "trigger",
|
|
55
|
-
"assertion": "toHaveAttribute",
|
|
56
|
-
"attribute": "aria-controls",
|
|
57
|
-
"failureMessage": "Accordion trigger button doesn't conform to the ARIA Accordion Button pattern as specified in APG 1.2. Accordion trigger button should have 'aria-controls' attribute that points to the id of the accordion panel div it controls with 'role=region'. This helps assistive technology to identify the accordion panel div that the button controls."
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"target": "panel",
|
|
61
|
-
"assertion": "toHaveAttribute",
|
|
62
|
-
"attribute": "aria-labelledby | aria-label",
|
|
63
|
-
"failureMessage": "Accordion panel doesn't conform to the ARIA Accordion pattern as specified in APG 1.2. Accordion panel should have 'aria-labelledby' attribute that points to the id of the trigger button that controls it."
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
],
|
|
68
|
-
|
|
69
|
-
"dynamic": [
|
|
70
|
-
{
|
|
71
|
-
"description": "Clicking the trigger expands a collapsed panel.",
|
|
72
|
-
"action": [
|
|
73
|
-
{ "type": "click", "target": "trigger" }
|
|
74
|
-
],
|
|
75
|
-
"assertions": [
|
|
76
|
-
{
|
|
77
|
-
"target": "panel",
|
|
78
|
-
"assertion": "toBeVisible",
|
|
79
|
-
"failureMessage": "Panel should be visible after clicking the trigger."
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"target": "trigger",
|
|
83
|
-
"assertion": "toHaveAttribute",
|
|
84
|
-
"attribute": "aria-expanded",
|
|
85
|
-
"expectedValue": "true",
|
|
86
|
-
"failureMessage": "Trigger's aria-expanded attribute should be true after clicking to expand."
|
|
87
|
-
}
|
|
88
|
-
]
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
"description": "Clicking the trigger again collapses an expanded panel.",
|
|
92
|
-
"action": [
|
|
93
|
-
{ "type": "click", "target": "trigger" },
|
|
94
|
-
{ "type": "click", "target": "trigger" }
|
|
95
|
-
],
|
|
96
|
-
"assertions": [
|
|
97
|
-
{
|
|
98
|
-
"target": "panel",
|
|
99
|
-
"assertion": "notToBeVisible",
|
|
100
|
-
"failureMessage": "Panel should not be visible after clicking the trigger twice."
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
"target": "trigger",
|
|
104
|
-
"assertion": "toHaveAttribute",
|
|
105
|
-
"attribute": "aria-expanded",
|
|
106
|
-
"expectedValue": "false",
|
|
107
|
-
"failureMessage": "Trigger's aria-expanded attribute should be false after collapsing."
|
|
108
|
-
}
|
|
109
|
-
]
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"description": "Pressing Enter on trigger expands a collapsed panel.",
|
|
113
|
-
"action": [
|
|
114
|
-
{ "type": "keypress", "target": "trigger", "key": "Enter" }
|
|
115
|
-
],
|
|
116
|
-
"assertions": [
|
|
117
|
-
{
|
|
118
|
-
"target": "panel",
|
|
119
|
-
"assertion": "toBeVisible",
|
|
120
|
-
"failureMessage": "Panel should be visible after pressing Enter on trigger."
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
"target": "trigger",
|
|
124
|
-
"assertion": "toHaveAttribute",
|
|
125
|
-
"attribute": "aria-expanded",
|
|
126
|
-
"expectedValue": "true",
|
|
127
|
-
"failureMessage": "Trigger's should have aria-expanded=true after pressing Enter."
|
|
128
|
-
}
|
|
129
|
-
]
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
"description": "Pressing Space on trigger expands a collapsed panel.",
|
|
133
|
-
"action": [
|
|
134
|
-
{ "type": "keypress", "target": "trigger", "key": "Space" }
|
|
135
|
-
],
|
|
136
|
-
"assertions": [
|
|
137
|
-
{
|
|
138
|
-
"target": "panel",
|
|
139
|
-
"assertion": "toBeVisible",
|
|
140
|
-
"failureMessage": "Panel should be visible after pressing Space on trigger."
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"target": "trigger",
|
|
144
|
-
"assertion": "toHaveAttribute",
|
|
145
|
-
"attribute": "aria-expanded",
|
|
146
|
-
"expectedValue": "true",
|
|
147
|
-
"failureMessage": "Trigger's should have aria-expanded=true after pressing Space."
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
"description": "Pressing Enter on trigger collapses an expanded panel.",
|
|
153
|
-
"action": [
|
|
154
|
-
{ "type": "keypress", "target": "trigger", "key": "Enter" },
|
|
155
|
-
{ "type": "keypress", "target": "trigger", "key": "Enter" }
|
|
156
|
-
],
|
|
157
|
-
"assertions": [
|
|
158
|
-
{
|
|
159
|
-
"target": "panel",
|
|
160
|
-
"assertion": "notToBeVisible",
|
|
161
|
-
"failureMessage": "Panel should not be visible after pressing Enter on expanded trigger."
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
"target": "trigger",
|
|
165
|
-
"assertion": "toHaveAttribute",
|
|
166
|
-
"attribute": "aria-expanded",
|
|
167
|
-
"expectedValue": "false",
|
|
168
|
-
"failureMessage": "Trigger's aria-expanded should be false after collapsing with Enter."
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
"description": "Pressing Space on trigger collapses an expanded panel.",
|
|
174
|
-
"action": [
|
|
175
|
-
{ "type": "keypress", "target": "trigger", "key": "Space" },
|
|
176
|
-
{ "type": "keypress", "target": "trigger", "key": "Space" }
|
|
177
|
-
],
|
|
178
|
-
"assertions": [
|
|
179
|
-
{
|
|
180
|
-
"target": "panel",
|
|
181
|
-
"assertion": "notToBeVisible",
|
|
182
|
-
"failureMessage": "Panel should not be visible after pressing Space on expanded trigger."
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
"target": "trigger",
|
|
186
|
-
"assertion": "toHaveAttribute",
|
|
187
|
-
"attribute": "aria-expanded",
|
|
188
|
-
"expectedValue": "false",
|
|
189
|
-
"failureMessage": "Trigger's aria-expanded should be false after collapsing with Space."
|
|
190
|
-
}
|
|
191
|
-
]
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
"description": "Down Arrow moves focus to next accordion trigger.",
|
|
195
|
-
"isMultiple": true,
|
|
196
|
-
"level": "optional",
|
|
197
|
-
"action": [
|
|
198
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowDown" }
|
|
199
|
-
],
|
|
200
|
-
"assertions": [
|
|
201
|
-
{
|
|
202
|
-
"target": "relative",
|
|
203
|
-
"assertion": "toHaveFocus",
|
|
204
|
-
"expectedValue": "second",
|
|
205
|
-
"failureMessage": "Focus should move to the next accordion trigger after pressing Down Arrow."
|
|
206
|
-
}
|
|
207
|
-
]
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
"description": "Up Arrow moves focus to previous accordion trigger.",
|
|
211
|
-
"isMultiple": true,
|
|
212
|
-
"level": "optional",
|
|
213
|
-
"action": [
|
|
214
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowUp" }
|
|
215
|
-
],
|
|
216
|
-
"assertions": [
|
|
217
|
-
{
|
|
218
|
-
"target": "relative",
|
|
219
|
-
"assertion": "toHaveFocus",
|
|
220
|
-
"expectedValue": "first",
|
|
221
|
-
"failureMessage": "Focus should move to the previous accordion trigger after pressing Up Arrow."
|
|
222
|
-
}
|
|
223
|
-
]
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
"description": "Home moves focus to first accordion trigger.",
|
|
227
|
-
"isMultiple": true,
|
|
228
|
-
"level": "optional",
|
|
229
|
-
"action": [
|
|
230
|
-
{ "type": "keypress", "target": "focusable", "key": "Home" }
|
|
231
|
-
],
|
|
232
|
-
"assertions": [
|
|
233
|
-
{
|
|
234
|
-
"target": "relative",
|
|
235
|
-
"assertion": "toHaveFocus",
|
|
236
|
-
"expectedValue": "first",
|
|
237
|
-
"failureMessage": "Focus should move to the first accordion trigger after pressing Home."
|
|
238
|
-
}
|
|
239
|
-
]
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
"description": "End moves focus to last accordion trigger.",
|
|
243
|
-
"isMultiple": true,
|
|
244
|
-
"level": "optional",
|
|
245
|
-
"action": [
|
|
246
|
-
{ "type": "keypress", "target": "focusable", "key": "End" }
|
|
247
|
-
],
|
|
248
|
-
"assertions": [
|
|
249
|
-
{
|
|
250
|
-
"target": "relative",
|
|
251
|
-
"assertion": "toHaveFocus",
|
|
252
|
-
"expectedValue": "last",
|
|
253
|
-
"failureMessage": "Focus should move to the last accordion trigger after pressing End."
|
|
254
|
-
}
|
|
255
|
-
]
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
"description": "Down Arrow wraps focus from last trigger to first trigger.",
|
|
259
|
-
"isMultiple": true,
|
|
260
|
-
"level": "optional",
|
|
261
|
-
"action": [
|
|
262
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowDown" }
|
|
263
|
-
],
|
|
264
|
-
"assertions": [
|
|
265
|
-
{
|
|
266
|
-
"target": "relative",
|
|
267
|
-
"assertion": "toHaveFocus",
|
|
268
|
-
"expectedValue": "first",
|
|
269
|
-
"failureMessage": "Focus should wrap from last to first accordion trigger after pressing Down Arrow."
|
|
270
|
-
}
|
|
271
|
-
]
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
"description": "Up Arrow wraps focus from first trigger to last trigger.",
|
|
275
|
-
"isMultiple": true,
|
|
276
|
-
"level": "optional",
|
|
277
|
-
"action": [
|
|
278
|
-
{ "type": "keypress", "target": "focusable", "key": "ArrowUp" }
|
|
279
|
-
],
|
|
280
|
-
"assertions": [
|
|
281
|
-
{
|
|
282
|
-
"target": "relative",
|
|
283
|
-
"assertion": "toHaveFocus",
|
|
284
|
-
"expectedValue": "last",
|
|
285
|
-
"failureMessage": "Focus should wrap from first to last accordion trigger after pressing Up Arrow."
|
|
286
|
-
}
|
|
287
|
-
]
|
|
288
|
-
}
|
|
289
|
-
]
|
|
290
|
-
}
|