aria-ease 6.12.2 → 7.0.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.
Files changed (93) hide show
  1. package/dist/AccordionComponentStrategy-2SWMNUR6.js +1 -0
  2. package/dist/ComboboxComponentStrategy-YSYLR2U5.js +5 -0
  3. package/dist/MenuComponentStrategy-C22BZEBH.js +5 -0
  4. package/dist/RelativeTargetResolver-T4P25J2M.js +1 -0
  5. package/dist/TabsComponentStrategy-ADEEFJXM.js +1 -0
  6. package/dist/audit-APAPHXRO.js +9 -0
  7. package/dist/badgeHelper-IB5RTMAG.js +11 -0
  8. package/dist/badgeHelper-JSROP5ML.js +1 -0
  9. package/dist/buildContracts-T4XQZBDU.js +13 -0
  10. package/dist/chunk-52I3INNG.js +11 -0
  11. package/dist/chunk-APUMBDOT.js +1 -0
  12. package/dist/chunk-BHNO4ZI3.js +1 -0
  13. package/dist/chunk-CNU4N4AY.js +1 -0
  14. package/dist/chunk-SM6ZKEDR.js +1 -0
  15. package/dist/chunk-ZNQ5BXVJ.js +1 -0
  16. package/dist/cli.cjs +132 -3494
  17. package/dist/cli.js +19 -161
  18. package/dist/configLoader-ZEJVXLX7.js +1 -0
  19. package/dist/configLoader-ZXTSCIP6.js +1 -0
  20. package/dist/contractTestRunnerPlaywright-FOCQTM4L.js +46 -0
  21. package/dist/contractTestRunnerPlaywright-QPU6HZXG.js +46 -0
  22. package/dist/formatters-H3CPDLG5.js +87 -0
  23. package/dist/index.cjs +64 -4657
  24. package/dist/index.d.cts +23 -2
  25. package/dist/index.d.ts +23 -2
  26. package/dist/index.js +17 -2351
  27. package/dist/src/accordion/index.cjs +1 -183
  28. package/dist/src/accordion/index.js +1 -181
  29. package/dist/src/block/index.cjs +1 -124
  30. package/dist/src/block/index.js +1 -122
  31. package/dist/src/checkbox/index.cjs +1 -109
  32. package/dist/src/checkbox/index.js +1 -107
  33. package/dist/src/combobox/index.cjs +1 -265
  34. package/dist/src/combobox/index.js +1 -263
  35. package/dist/src/menu/index.cjs +1 -339
  36. package/dist/src/menu/index.js +1 -337
  37. package/dist/src/radio/index.cjs +1 -117
  38. package/dist/src/radio/index.js +1 -115
  39. package/dist/src/tabs/index.cjs +1 -265
  40. package/dist/src/tabs/index.js +1 -263
  41. package/dist/src/toggle/index.cjs +1 -119
  42. package/dist/src/toggle/index.js +1 -117
  43. package/dist/src/utils/test/AccordionComponentStrategy-X2GSQ5KT.js +1 -0
  44. package/dist/src/utils/test/ComboboxComponentStrategy-SICWLI27.js +5 -0
  45. package/dist/src/utils/test/MenuComponentStrategy-R4VPAHDE.js +5 -0
  46. package/dist/src/utils/test/RelativeTargetResolver-UQQMZHI6.js +1 -0
  47. package/dist/src/utils/test/TabsComponentStrategy-L2PYNEW6.js +1 -0
  48. package/dist/src/utils/test/badgeHelper-ER5ZOHWF.js +11 -0
  49. package/dist/src/utils/test/chunk-APUMBDOT.js +1 -0
  50. package/dist/src/utils/test/chunk-BHNO4ZI3.js +1 -0
  51. package/dist/src/utils/test/configLoader-NCYRL2O6.js +1 -0
  52. package/dist/src/utils/test/contractTestRunnerPlaywright-YZCMF64Q.js +46 -0
  53. package/dist/src/utils/test/dsl/index.cjs +1 -486
  54. package/dist/src/utils/test/dsl/index.d.cts +21 -0
  55. package/dist/src/utils/test/dsl/index.d.ts +21 -0
  56. package/dist/src/utils/test/dsl/index.js +1 -484
  57. package/dist/src/utils/test/index.cjs +64 -2578
  58. package/dist/src/utils/test/index.d.cts +2 -2
  59. package/dist/src/utils/test/index.d.ts +2 -2
  60. package/dist/src/utils/test/index.js +16 -340
  61. package/dist/test-VXSCSKV5.js +19 -0
  62. package/package.json +7 -9
  63. package/dist/AccordionComponentStrategy-4ZEIQ2V6.js +0 -42
  64. package/dist/ComboboxComponentStrategy-DU342VMB.js +0 -64
  65. package/dist/MenuComponentStrategy-JAMTCSNF.js +0 -81
  66. package/dist/RelativeTargetResolver-DJAITO6D.js +0 -7
  67. package/dist/TabsComponentStrategy-3SQURPMX.js +0 -29
  68. package/dist/audit-JYEPKLHR.js +0 -63
  69. package/dist/badgeHelper-JOWO6RQG.js +0 -15
  70. package/dist/badgeHelper-RDOMCC6E.js +0 -108
  71. package/dist/buildContracts-FT6KWUJN.js +0 -465
  72. package/dist/chunk-4DU5Z5BR.js +0 -340
  73. package/dist/chunk-GJGUY643.js +0 -182
  74. package/dist/chunk-GLT43UVH.js +0 -43
  75. package/dist/chunk-I2KLQ2HA.js +0 -22
  76. package/dist/chunk-JJEPLK7L.js +0 -107
  77. package/dist/chunk-PK5L2SAF.js +0 -17
  78. package/dist/configLoader-Q7N5XV4P.js +0 -183
  79. package/dist/configLoader-REHK3S3Q.js +0 -7
  80. package/dist/contractTestRunnerPlaywright-47DCBO4A.js +0 -1300
  81. package/dist/contractTestRunnerPlaywright-UJKXRXBS.js +0 -1300
  82. package/dist/formatters-32KQIIYS.js +0 -183
  83. package/dist/src/utils/test/AccordionComponentStrategy-WRHZOEN6.js +0 -38
  84. package/dist/src/utils/test/ComboboxComponentStrategy-XKQ72RFD.js +0 -60
  85. package/dist/src/utils/test/MenuComponentStrategy-VKZQYLBE.js +0 -77
  86. package/dist/src/utils/test/RelativeTargetResolver-G2XDN2VV.js +0 -1
  87. package/dist/src/utils/test/TabsComponentStrategy-BKG53SEV.js +0 -26
  88. package/dist/src/utils/test/badgeHelper-HZKGOPB4.js +0 -102
  89. package/dist/src/utils/test/chunk-4DU5Z5BR.js +0 -332
  90. package/dist/src/utils/test/chunk-GLT43UVH.js +0 -41
  91. package/dist/src/utils/test/configLoader-NA7IBCS3.js +0 -181
  92. package/dist/src/utils/test/contractTestRunnerPlaywright-AZ4QKLYT.js +0 -1278
  93. package/dist/test-6Y4CIQOM.js +0 -358
@@ -16,13 +16,13 @@ type StrictnessMode = 'minimal' | 'balanced' | 'strict' | 'paranoid';
16
16
  * Runs static and interactions accessibility test on UI components.
17
17
  * @param {string} componentName The name of the component contract to test against
18
18
  * @param {HTMLElement} component The UI component to be tested
19
- * @param {string} url Optional URL to run full Playwright E2E tests. If omitted, uses isolated component testing with page.setContent()
19
+ * @param {string} url URL for Playwright E2E tests
20
20
  */
21
21
 
22
22
  type TestAuditOptions = {
23
23
  strictness?: StrictnessMode;
24
24
  };
25
- declare function testUiComponent(componentName: string, component: HTMLElement | null, url: string | null, options?: TestAuditOptions): Promise<JestAxeResult>;
25
+ declare function testUiComponent(componentName: string, url: string | null, options?: TestAuditOptions): Promise<JestAxeResult>;
26
26
  declare let runTest: () => Promise<ContractTestResult>;
27
27
  /**
28
28
  * Cleanup function to close the shared Playwright browser
@@ -16,13 +16,13 @@ type StrictnessMode = 'minimal' | 'balanced' | 'strict' | 'paranoid';
16
16
  * Runs static and interactions accessibility test on UI components.
17
17
  * @param {string} componentName The name of the component contract to test against
18
18
  * @param {HTMLElement} component The UI component to be tested
19
- * @param {string} url Optional URL to run full Playwright E2E tests. If omitted, uses isolated component testing with page.setContent()
19
+ * @param {string} url URL for Playwright E2E tests
20
20
  */
21
21
 
22
22
  type TestAuditOptions = {
23
23
  strictness?: StrictnessMode;
24
24
  };
25
- declare function testUiComponent(componentName: string, component: HTMLElement | null, url: string | null, options?: TestAuditOptions): Promise<JestAxeResult>;
25
+ declare function testUiComponent(componentName: string, url: string | null, options?: TestAuditOptions): Promise<JestAxeResult>;
26
26
  declare let runTest: () => Promise<ContractTestResult>;
27
27
  /**
28
28
  * Cleanup function to close the shared Playwright browser
@@ -1,343 +1,19 @@
1
- import { normalizeStrictness, closeSharedBrowser, ContractReporter, normalizeLevel, resolveEnforcement } from './chunk-4DU5Z5BR.js';
2
- import { axe } from 'jest-axe';
3
- import fs from 'fs/promises';
4
- import path from 'path';
5
-
6
- async function runContractTests(contractPath, componentName, component, strictness) {
7
- const reporter = new ContractReporter(false);
8
- const strictnessMode = normalizeStrictness(strictness);
9
- if (!contractPath) {
10
- throw new Error(`No contract path provided for component: ${componentName}`);
11
- }
12
- const contractData = await fs.readFile(contractPath, "utf-8");
13
- const componentContract = JSON.parse(contractData);
14
- const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
15
- reporter.start(componentName, totalTests);
16
- const failures = [];
17
- const passes = [];
18
- const skipped = [];
19
- const warnings = [];
20
- const classifyFailure = (message, levelRaw) => {
21
- const level = normalizeLevel(levelRaw);
22
- const enforcement = resolveEnforcement(level, strictnessMode);
23
- if (enforcement === "error") {
24
- failures.push(message);
25
- return { status: "fail", level, detail: message };
26
- }
27
- if (enforcement === "warning") {
28
- warnings.push(message);
29
- return { status: "warn", level, detail: message };
30
- }
31
- const ignoredMessage = `${message} (ignored by strictness=${strictnessMode}, level=${level})`;
32
- skipped.push(ignoredMessage);
33
- return { status: "skip", level, detail: ignoredMessage };
34
- };
35
- let staticPassed = 0;
36
- let staticFailed = 0;
37
- let staticWarnings = 0;
38
- for (const rel of componentContract.relationships || []) {
39
- const relationshipLevel = normalizeLevel(rel.level);
40
- if (rel.type === "aria-reference") {
41
- const fromSelector = componentContract.selectors[rel.from];
42
- const toSelector = componentContract.selectors[rel.to];
43
- const relDescription = `${rel.from}.${rel.attribute} references ${rel.to}`;
44
- if (!fromSelector || !toSelector) {
45
- const outcome = classifyFailure(`Relationship selector missing: from="${rel.from}" or to="${rel.to}" not found in selectors.`, rel.level);
46
- if (outcome.status === "fail") staticFailed += 1;
47
- if (outcome.status === "warn") staticWarnings += 1;
48
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
49
- continue;
50
- }
51
- const fromTarget = component.querySelector(fromSelector);
52
- const toTarget = component.querySelector(toSelector);
53
- if (!fromTarget || !toTarget) {
54
- const outcome = classifyFailure(`Relationship target not found: ${!fromTarget ? rel.from : rel.to}.`, rel.level);
55
- if (outcome.status === "fail") staticFailed += 1;
56
- if (outcome.status === "warn") staticWarnings += 1;
57
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
58
- continue;
59
- }
60
- const toId = toTarget.getAttribute("id");
61
- const attrValue = fromTarget.getAttribute(rel.attribute) || "";
62
- if (!toId) {
63
- const outcome = classifyFailure(`Relationship target "${rel.to}" must have an id for ${rel.attribute} validation.`, rel.level);
64
- if (outcome.status === "fail") staticFailed += 1;
65
- if (outcome.status === "warn") staticWarnings += 1;
66
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
67
- continue;
68
- }
69
- const references = attrValue.split(/\s+/).filter(Boolean);
70
- if (!references.includes(toId)) {
71
- const outcome = classifyFailure(`Expected ${rel.from} ${rel.attribute} to reference id "${toId}", found "${attrValue}".`, rel.level);
72
- if (outcome.status === "fail") staticFailed += 1;
73
- if (outcome.status === "warn") staticWarnings += 1;
74
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
75
- continue;
76
- }
77
- passes.push(`Relationship valid: ${rel.from}.${rel.attribute} -> ${rel.to} (id=${toId}).`);
78
- staticPassed += 1;
79
- reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
80
- continue;
81
- }
82
- if (rel.type === "contains") {
83
- const parentSelector = componentContract.selectors[rel.parent];
84
- const childSelector = componentContract.selectors[rel.child];
85
- const relDescription = `${rel.parent} contains ${rel.child}`;
86
- if (!parentSelector || !childSelector) {
87
- const outcome = classifyFailure(`Relationship selector missing: parent="${rel.parent}" or child="${rel.child}" not found in selectors.`, rel.level);
88
- if (outcome.status === "fail") staticFailed += 1;
89
- if (outcome.status === "warn") staticWarnings += 1;
90
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
91
- continue;
92
- }
93
- const parentTarget = component.querySelector(parentSelector);
94
- if (!parentTarget) {
95
- const outcome = classifyFailure(`Relationship parent target not found: ${rel.parent}.`, rel.level);
96
- if (outcome.status === "fail") staticFailed += 1;
97
- if (outcome.status === "warn") staticWarnings += 1;
98
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
99
- continue;
100
- }
101
- const nestedChild = parentTarget.querySelector(childSelector);
102
- if (!nestedChild) {
103
- const outcome = classifyFailure(`Expected ${rel.parent} to contain descendant matching selector for ${rel.child}.`, rel.level);
104
- if (outcome.status === "fail") staticFailed += 1;
105
- if (outcome.status === "warn") staticWarnings += 1;
106
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
107
- continue;
108
- }
109
- passes.push(`Relationship valid: ${rel.parent} contains ${rel.child}.`);
110
- staticPassed += 1;
111
- reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
112
- }
113
- }
114
- for (const test of componentContract.static[0].assertions) {
115
- if (test.target !== "relative") {
116
- const staticLevel = normalizeLevel(test.level);
117
- const selector = componentContract.selectors[test.target];
118
- if (!selector) {
119
- const outcome = classifyFailure(`Selector for target ${test.target} not found.`, test.level);
120
- if (outcome.status === "fail") staticFailed += 1;
121
- if (outcome.status === "warn") staticWarnings += 1;
122
- reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
123
- continue;
124
- }
125
- const target = component.querySelector(selector);
126
- if (!target) {
127
- const outcome = classifyFailure(`Target ${test.target} not found.`, test.level);
128
- if (outcome.status === "fail") staticFailed += 1;
129
- if (outcome.status === "warn") staticWarnings += 1;
130
- reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
131
- continue;
132
- }
133
- const attributeValue = target.getAttribute(test.attribute);
134
- if (!test.expectedValue) {
135
- const attributes = test.attribute.split(" | ");
136
- const hasAnyAttribute = attributes.some((attr) => target.hasAttribute(attr));
137
- if (!hasAnyAttribute) {
138
- const outcome = classifyFailure(test.failureMessage + ` None of the attributes "${test.attribute}" are present.`, test.level);
139
- if (outcome.status === "fail") staticFailed += 1;
140
- if (outcome.status === "warn") staticWarnings += 1;
141
- reporter.reportStaticTest(`${test.target} has ${test.attribute}`, outcome.status, outcome.detail, outcome.level);
142
- } else {
143
- passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
144
- staticPassed += 1;
145
- reporter.reportStaticTest(`${test.target} has ${test.attribute}`, "pass", void 0, staticLevel);
146
- }
147
- } else if (!attributeValue || typeof test.expectedValue === "string" && !test.expectedValue.split(" | ").includes(attributeValue)) {
148
- const outcome = classifyFailure(test.failureMessage + ` Attribute value does not match expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`, test.level);
149
- if (outcome.status === "fail") staticFailed += 1;
150
- if (outcome.status === "warn") staticWarnings += 1;
151
- reporter.reportStaticTest(`${test.target} has ${test.attribute}="${test.expectedValue}"`, outcome.status, outcome.detail, outcome.level);
152
- } else {
153
- passes.push(`Attribute value matches expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`);
154
- staticPassed += 1;
155
- reporter.reportStaticTest(`${test.target} has ${test.attribute}="${attributeValue}"`, "pass", void 0, staticLevel);
156
- }
157
- }
158
- }
159
- for (const dynamicTest of componentContract.dynamic) {
160
- skipped.push(dynamicTest.description);
161
- reporter.reportTest({ description: dynamicTest.description, level: dynamicTest.level }, "skip");
162
- }
163
- reporter.reportStatic(staticPassed, staticFailed, staticWarnings);
164
- reporter.summary(failures);
165
- return { passes, failures, skipped, warnings };
166
- }
167
- async function testUiComponent(componentName, component, url, options = {}) {
168
- if (!componentName || typeof componentName !== "string") {
169
- throw new Error("\u274C testUiComponent requires a valid componentName (string)");
170
- }
171
- if (!url && (!component || !(component instanceof HTMLElement))) {
172
- throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
173
- }
174
- if (url && typeof url !== "string") {
175
- throw new Error("\u274C testUiComponent url parameter must be a string");
176
- }
177
- let results;
178
- if (component) {
179
- try {
180
- results = await axe(component);
181
- } catch (error) {
182
- throw new Error(
183
- `\u274C Axe accessibility scan failed
184
- Error: ${error instanceof Error ? error.message : String(error)}`
185
- );
186
- }
187
- } else {
188
- results = { violations: [] };
189
- }
190
- async function checkDevServer(url2) {
191
- try {
192
- const response = await fetch(url2, {
193
- method: "HEAD",
194
- signal: AbortSignal.timeout(1e3)
195
- });
196
- if (response.ok || response.status === 304) {
197
- return url2;
198
- }
199
- } catch {
200
- return null;
201
- }
202
- return null;
203
- }
204
- let strictness = normalizeStrictness(options.strictness);
205
- let config = {};
206
- let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
207
- if (typeof process !== "undefined" && typeof process.cwd === "function") {
208
- try {
209
- const { loadConfig } = await import('./configLoader-NA7IBCS3.js');
210
- const result2 = await loadConfig(process.cwd());
211
- config = result2.config;
212
- if (result2.configPath) {
213
- configBaseDir = path.dirname(result2.configPath);
214
- }
215
- if (options.strictness === void 0) {
216
- const componentStrictness = config.test?.components?.find((comp) => comp?.name === componentName)?.strictness;
217
- strictness = normalizeStrictness(componentStrictness ?? config.test?.strictness);
218
- }
219
- } catch {
220
- if (options.strictness === void 0) {
221
- strictness = "balanced";
222
- }
223
- }
224
- }
225
- let contract;
226
- try {
227
- if (url) {
228
- const devServerUrl = await checkDevServer(url);
229
- if (devServerUrl) {
230
- console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
231
- const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-AZ4QKLYT.js');
232
- contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
233
- } else {
234
- throw new Error(
235
- `\u274C Dev server not running at ${url}
236
- Please start your dev server and try again.`
237
- );
238
- }
239
- } else if (component) {
240
- console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
241
- const contractPath = config.test?.components?.find((comp) => comp?.name === componentName)?.contractPath;
242
- if (!contractPath) {
243
- throw new Error(`\u274C No contract path found for component: ${componentName}`);
244
- }
245
- contract = await runContractTests(
246
- path.resolve(configBaseDir, contractPath),
247
- componentName,
248
- component,
249
- strictness
250
- );
251
- } else {
252
- throw new Error("\u274C Either component or URL must be provided");
253
- }
254
- } catch (error) {
255
- if (error instanceof Error) {
256
- throw error;
257
- }
258
- throw new Error(`\u274C Contract test execution failed: ${String(error)}`);
259
- }
260
- const result = {
261
- violations: results.violations,
262
- raw: results,
263
- contract
264
- };
265
- if (contract.failures.length > 0 && url === "Playwright") {
266
- throw new Error(
267
- `
268
- \u274C ${contract.failures.length} accessibility contract test${contract.failures.length > 1 ? "s" : ""} failed (Playwright mode)
269
- \u2705 ${contract.passes.length} test${contract.passes.length > 1 ? "s" : ""} passed
1
+ import {d,b}from'./chunk-APUMBDOT.js';import m from'path';async function y(a,e,c={}){if(!a||typeof a!="string")throw new Error("\u274C testUiComponent requires a valid componentName (string)");if(!e)throw new Error("\u274C testUiComponent requires a URL");if(e&&typeof e!="string")throw new Error("\u274C testUiComponent url parameter must be a string");let i={violations:[]};async function l(t){try{let n=await fetch(t,{method:"HEAD",signal:AbortSignal.timeout(1e3)});if(n.ok||n.status===304)return t}catch{return null}return null}let p=d(c.strictness),o={},d$1=typeof process<"u"?process.cwd():"";if(typeof process<"u"&&typeof process.cwd=="function")try{let{loadConfig:t}=await import('./configLoader-NCYRL2O6.js'),n=await t(process.cwd());if(o=n.config,n.configPath&&(d$1=m.dirname(n.configPath)),c.strictness===void 0){let r=o.test?.components?.find(h=>h?.name===a)?.strictness;p=d(r??o.test?.strictness);}}catch{c.strictness===void 0&&(p="balanced");}let s;try{if(e){let t=await l(e);if(t){console.log(`\u{1F3AD} Running Playwright tests on ${t}`);let{runContractTestsPlaywright:n}=await import('./contractTestRunnerPlaywright-YZCMF64Q.js');s=await n(a,t,p,o,d$1);}else throw new Error(`\u274C Dev server not running at ${e}
2
+ Please start your dev server and try again.`)}else throw new Error("\u274C URL is required for component testing. Please provide a URL to run full accessibility tests with testUiComponent.")}catch(t){throw t instanceof Error?t:new Error(`\u274C Contract test execution failed: ${String(t)}`)}let u={violations:i.violations,raw:i,contract:s};if(s.failures.length>0&&e==="Playwright")throw new Error(`
3
+ \u274C ${s.failures.length} accessibility contract test${s.failures.length>1?"s":""} failed (Playwright mode)
4
+ \u2705 ${s.passes.length} test${s.passes.length>1?"s":""} passed
270
5
 
271
6
  \u{1F4CB} Review the detailed test report above for specific failures.
272
- \u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`
273
- );
274
- }
275
- if (results.violations.length > 0) {
276
- const violationCount = results.violations.length;
277
- const violationDetails = results.violations.map(
278
- (v) => `
279
- - ${v.id}: ${v.description}
280
- Impact: ${v.impact}
281
- Affected elements: ${v.nodes.length}
282
- Help: ${v.helpUrl}`
283
- ).join("\n");
284
- throw new Error(
285
- `
286
- \u274C ${violationCount} axe accessibility violation${violationCount > 1 ? "s" : ""} detected
287
- ${violationDetails}
288
-
289
- \u{1F4CB} Full details available in result.violations`
290
- );
291
- }
292
- return result;
293
- }
294
- var runTest = async () => {
295
- return {
296
- passes: [],
297
- failures: [],
298
- skipped: []
299
- };
300
- };
301
- if (typeof window === "undefined") {
302
- runTest = async () => {
303
- console.log(`\u{1F680} Running component accessibility tests...
304
- `);
305
- const { exec } = await import('child_process');
306
- const chalk = (await import('chalk')).default;
307
- return new Promise((resolve, reject) => {
308
- exec(
309
- `npx vitest --run --reporter verbose`,
310
- async (error, stdout, stderr) => {
311
- console.log(stdout);
312
- if (stderr) console.error(stderr);
313
- const testsPassed = !error || error.code === 0;
314
- if (testsPassed) {
315
- try {
316
- const { displayBadgeInfo, promptAddBadge } = await import('./badgeHelper-HZKGOPB4.js');
317
- displayBadgeInfo("component");
318
- await promptAddBadge("component", process.cwd());
319
- console.log(chalk.dim("\n" + "\u2500".repeat(60)));
320
- console.log(chalk.cyan("\u{1F499} Found aria-ease helpful?"));
321
- console.log(chalk.white(" \u2022 Star us on GitHub: ") + chalk.blue.underline("https://github.com/aria-ease/aria-ease"));
322
- console.log(chalk.white(" \u2022 Share feedback: ") + chalk.blue.underline("https://github.com/aria-ease/aria-ease/discussions"));
323
- console.log(chalk.dim("\u2500".repeat(60) + "\n"));
324
- } catch (badgeError) {
325
- console.error("Warning: Could not display badge prompt:", badgeError);
326
- }
327
- resolve({ passes: [], failures: [], skipped: [] });
328
- process.exit(0);
329
- } else {
330
- const exitCode = error?.code || 1;
331
- reject(new Error(`Tests failed with code ${exitCode}`));
332
- process.exit(exitCode);
333
- }
334
- }
335
- );
336
- });
337
- };
338
- }
339
- async function cleanupTests() {
340
- await closeSharedBrowser();
341
- }
7
+ \u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`);if(i.violations.length>0){let t=i.violations.length,n=i.violations.map(r=>`
8
+ - ${r.id}: ${r.description}
9
+ Impact: ${r.impact}
10
+ Affected elements: ${r.nodes.length}
11
+ Help: ${r.helpUrl}`).join(`
12
+ `);throw new Error(`
13
+ \u274C ${t} axe accessibility violation${t>1?"s":""} detected
14
+ ${n}
342
15
 
343
- export { cleanupTests, runTest, testUiComponent };
16
+ \u{1F4CB} Full details available in result.violations`)}return u}var w=async()=>({passes:[],failures:[],skipped:[]});typeof window>"u"&&(w=async()=>{console.log(`\u{1F680} Running component accessibility tests...
17
+ `);let{exec:a}=await import('child_process'),e=(await import('chalk')).default;return new Promise((c,i)=>{a("npx vitest --run --reporter verbose",async(l,p,o)=>{if(console.log(p),o&&console.error(o),!l||l.code===0){try{let{displayBadgeInfo:s,promptAddBadge:u}=await import('./badgeHelper-ER5ZOHWF.js');s("component"),await u("component",process.cwd()),console.log(e.dim(`
18
+ `+"\u2500".repeat(60))),console.log(e.cyan("\u{1F499} Found aria-ease helpful?")),console.log(e.white(" \u2022 Star us on GitHub: ")+e.blue.underline("https://github.com/aria-ease/aria-ease")),console.log(e.white(" \u2022 Share feedback: ")+e.blue.underline("https://github.com/aria-ease/aria-ease/discussions")),console.log(e.dim("\u2500".repeat(60)+`
19
+ `));}catch(s){console.error("Warning: Could not display badge prompt:",s);}c({passes:[],failures:[],skipped:[]}),process.exit(0);}else {let s=l?.code||1;i(new Error(`Tests failed with code ${s}`)),process.exit(s);}});})});async function v(){await b();}export{v as cleanupTests,w as runTest,y as testUiComponent};
@@ -0,0 +1,19 @@
1
+ import{b as g,d as f}from"./chunk-APUMBDOT.js";import"./chunk-CNU4N4AY.js";import m from"path";async function y(a,e,c={}){if(!a||typeof a!="string")throw new Error("\u274C testUiComponent requires a valid componentName (string)");if(!e)throw new Error("\u274C testUiComponent requires a URL");if(e&&typeof e!="string")throw new Error("\u274C testUiComponent url parameter must be a string");let i={violations:[]};async function l(t){try{let n=await fetch(t,{method:"HEAD",signal:AbortSignal.timeout(1e3)});if(n.ok||n.status===304)return t}catch{return null}return null}let p=f(c.strictness),o={},d=typeof process<"u"?process.cwd():"";if(typeof process<"u"&&typeof process.cwd=="function")try{let{loadConfig:t}=await import("./configLoader-ZXTSCIP6.js"),n=await t(process.cwd());if(o=n.config,n.configPath&&(d=m.dirname(n.configPath)),c.strictness===void 0){let r=o.test?.components?.find(h=>h?.name===a)?.strictness;p=f(r??o.test?.strictness)}}catch{c.strictness===void 0&&(p="balanced")}let s;try{if(e){let t=await l(e);if(t){console.log(`\u{1F3AD} Running Playwright tests on ${t}`);let{runContractTestsPlaywright:n}=await import("./contractTestRunnerPlaywright-FOCQTM4L.js");s=await n(a,t,p,o,d)}else throw new Error(`\u274C Dev server not running at ${e}
2
+ Please start your dev server and try again.`)}else throw new Error("\u274C URL is required for component testing. Please provide a URL to run full accessibility tests with testUiComponent.")}catch(t){throw t instanceof Error?t:new Error(`\u274C Contract test execution failed: ${String(t)}`)}let u={violations:i.violations,raw:i,contract:s};if(s.failures.length>0&&e==="Playwright")throw new Error(`
3
+ \u274C ${s.failures.length} accessibility contract test${s.failures.length>1?"s":""} failed (Playwright mode)
4
+ \u2705 ${s.passes.length} test${s.passes.length>1?"s":""} passed
5
+
6
+ \u{1F4CB} Review the detailed test report above for specific failures.
7
+ \u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`);if(i.violations.length>0){let t=i.violations.length,n=i.violations.map(r=>`
8
+ - ${r.id}: ${r.description}
9
+ Impact: ${r.impact}
10
+ Affected elements: ${r.nodes.length}
11
+ Help: ${r.helpUrl}`).join(`
12
+ `);throw new Error(`
13
+ \u274C ${t} axe accessibility violation${t>1?"s":""} detected
14
+ ${n}
15
+
16
+ \u{1F4CB} Full details available in result.violations`)}return u}var w=async()=>({passes:[],failures:[],skipped:[]});typeof window>"u"&&(w=async()=>{console.log(`\u{1F680} Running component accessibility tests...
17
+ `);let{exec:a}=await import("child_process"),e=(await import("chalk")).default;return new Promise((c,i)=>{a("npx vitest --run --reporter verbose",async(l,p,o)=>{if(console.log(p),o&&console.error(o),!l||l.code===0){try{let{displayBadgeInfo:s,promptAddBadge:u}=await import("./badgeHelper-JSROP5ML.js");s("component"),await u("component",process.cwd()),console.log(e.dim(`
18
+ `+"\u2500".repeat(60))),console.log(e.cyan("\u{1F499} Found aria-ease helpful?")),console.log(e.white(" \u2022 Star us on GitHub: ")+e.blue.underline("https://github.com/aria-ease/aria-ease")),console.log(e.white(" \u2022 Share feedback: ")+e.blue.underline("https://github.com/aria-ease/aria-ease/discussions")),console.log(e.dim("\u2500".repeat(60)+`
19
+ `))}catch(s){console.error("Warning: Could not display badge prompt:",s)}c({passes:[],failures:[],skipped:[]}),process.exit(0)}else{let s=l?.code||1;i(new Error(`Tests failed with code ${s}`)),process.exit(s)}})})});async function v(){await g()}export{v as cleanupTests,w as runTest,y as testUiComponent};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aria-ease",
3
- "version": "6.12.2",
3
+ "version": "7.0.0",
4
4
  "description": "Accessibility infrastructure for the entire frontend engineering lifecycle. Build accessible patterns, run automated audits, verify component interactions, and gate deployments — all in one system.",
5
5
  "main": "dist/index.cjs",
6
6
  "type": "module",
@@ -17,11 +17,11 @@
17
17
  "release:minor": "standard-version --release-as minor",
18
18
  "release:major": "standard-version --release-as major",
19
19
  "clean": "rm -rf dist",
20
- "build:core": "tsup ./index.ts --format esm,cjs --dts --outDir dist --external jest-axe --external @testing-library/react --external @axe-core/playwright --external playwright",
21
- "build:modules": "tsup ./src/combobox/index.ts ./src/accordion/index.ts ./src/block/index.ts ./src/checkbox/index.ts ./src/menu/index.ts ./src/tabs/index.ts ./src/radio/index.ts ./src/toggle/index.ts ./src/contracts/index.ts --format esm,cjs --dts --treeshake --outDir dist/src",
22
- "build:test": "tsup ./src/utils/test/index.ts --format esm,cjs --dts --treeshake --external jest-axe --external @testing-library/react --external playwright --external @playwright/test --outDir dist/src/utils/test",
23
- "build:dsl": "tsup ./src/utils/test/dsl/index.ts --format esm,cjs --dts --treeshake --outDir dist/src/utils/test/dsl",
24
- "build:cli": "tsup ./src/utils/cli/cli.ts --format esm,cjs --dts --outDir dist --external commander --external chalk --external jest-axe --external @testing-library/react --external @axe-core/playwright --external playwright",
20
+ "build:core": "tsup ./index.ts --format esm,cjs --dts --outDir dist --external @testing-library/react --external @axe-core/playwright --external playwright --minify",
21
+ "build:modules": "tsup ./src/combobox/index.ts ./src/accordion/index.ts ./src/block/index.ts ./src/checkbox/index.ts ./src/menu/index.ts ./src/tabs/index.ts ./src/radio/index.ts ./src/toggle/index.ts ./src/contracts/index.ts --format esm,cjs --dts --treeshake --outDir dist/src --minify",
22
+ "build:test": "tsup ./src/utils/test/index.ts --format esm,cjs --dts --treeshake --external @testing-library/react --external playwright --external @playwright/test --outDir dist/src/utils/test --minify",
23
+ "build:dsl": "tsup ./src/utils/test/dsl/index.ts --format esm,cjs --dts --treeshake --outDir dist/src/utils/test/dsl --minify",
24
+ "build:cli": "tsup ./src/utils/cli/cli.ts --format esm,cjs --dts --outDir dist --external commander --external chalk --external @testing-library/react --external @axe-core/playwright --external playwright --minify",
25
25
  "build": "npm run clean && npm run build:core && npm run build:modules && npm run build:test && npm run build:dsl && npm run build:cli"
26
26
  },
27
27
  "repository": {
@@ -54,11 +54,9 @@
54
54
  "@testing-library/dom": "^10.4.1",
55
55
  "@testing-library/react": "^16.3.0",
56
56
  "@types/fs-extra": "^11.0.4",
57
- "@types/jest-axe": "^3.5.9",
58
57
  "benchmark": "^2.1.4",
59
58
  "eslint": "^9.34.0",
60
- "jest-axe": "^10.0.0",
61
- "jsdom": "^26.1.0",
59
+ "jsdom": "^29.0.1",
62
60
  "prettier": "^3.6.2",
63
61
  "standard-version": "^9.5.0",
64
62
  "tsup": "^8.5.0",
@@ -1,42 +0,0 @@
1
- import {
2
- test_exports
3
- } from "./chunk-PK5L2SAF.js";
4
- import "./chunk-I2KLQ2HA.js";
5
-
6
- // src/utils/test/src/component-strategies/AccordionComponentStrategy.ts
7
- var AccordionComponentStrategy = class {
8
- constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
9
- this.mainSelector = mainSelector;
10
- this.selectors = selectors;
11
- this.actionTimeoutMs = actionTimeoutMs;
12
- this.assertionTimeoutMs = assertionTimeoutMs;
13
- }
14
- async resetState(page) {
15
- if (!this.selectors.panel || !this.selectors.trigger || this.selectors.popup) {
16
- return;
17
- }
18
- const triggerSelector = this.selectors.trigger;
19
- const panelSelector = this.selectors.panel;
20
- if (!triggerSelector || !panelSelector) return;
21
- const allTriggers = await page.locator(triggerSelector).all();
22
- for (const trigger of allTriggers) {
23
- const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
24
- const triggerPanel = await trigger.getAttribute("aria-controls");
25
- if (isExpanded && triggerPanel) {
26
- await trigger.click({ timeout: this.actionTimeoutMs });
27
- const panel = page.locator(`#${triggerPanel}`);
28
- await (0, test_exports.expect)(panel).toBeHidden({ timeout: this.assertionTimeoutMs }).catch(() => {
29
- });
30
- }
31
- }
32
- }
33
- async shouldSkipTest() {
34
- return false;
35
- }
36
- getMainSelector() {
37
- return this.mainSelector;
38
- }
39
- };
40
- export {
41
- AccordionComponentStrategy
42
- };
@@ -1,64 +0,0 @@
1
- import {
2
- test_exports
3
- } from "./chunk-PK5L2SAF.js";
4
- import "./chunk-I2KLQ2HA.js";
5
-
6
- // src/utils/test/src/component-strategies/ComboboxComponentStrategy.ts
7
- var ComboboxComponentStrategy = class {
8
- constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
9
- this.mainSelector = mainSelector;
10
- this.selectors = selectors;
11
- this.actionTimeoutMs = actionTimeoutMs;
12
- this.assertionTimeoutMs = assertionTimeoutMs;
13
- }
14
- async resetState(page) {
15
- if (!this.selectors.popup) return;
16
- const popupSelector = this.selectors.popup;
17
- const popupElement = page.locator(popupSelector).first();
18
- const isPopupVisible = await popupElement.isVisible().catch(() => false);
19
- if (!isPopupVisible) return;
20
- let popupClosed = false;
21
- let closeSelector = this.selectors.input;
22
- if (!closeSelector && this.selectors.focusable) {
23
- closeSelector = this.selectors.focusable;
24
- } else if (!closeSelector) {
25
- closeSelector = this.selectors.button;
26
- }
27
- if (closeSelector) {
28
- const closeElement = page.locator(closeSelector).first();
29
- await closeElement.focus();
30
- await page.keyboard.press("Escape");
31
- popupClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
32
- }
33
- if (!popupClosed && this.selectors.button) {
34
- const buttonElement = page.locator(this.selectors.button).first();
35
- await buttonElement.click({ timeout: this.actionTimeoutMs });
36
- popupClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
37
- }
38
- if (!popupClosed) {
39
- await page.mouse.click(10, 10);
40
- popupClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
41
- }
42
- if (!popupClosed) {
43
- throw new Error(
44
- `\u274C FATAL: Cannot close combobox popup between tests. Popup remains visible after trying:
45
- 1. Escape key
46
- 2. Clicking button
47
- 3. Clicking outside
48
- This indicates a problem with the combobox component's close functionality.`
49
- );
50
- }
51
- if (this.selectors.input) {
52
- await page.locator(this.selectors.input).first().clear();
53
- }
54
- }
55
- async shouldSkipTest() {
56
- return false;
57
- }
58
- getMainSelector() {
59
- return this.mainSelector;
60
- }
61
- };
62
- export {
63
- ComboboxComponentStrategy
64
- };
@@ -1,81 +0,0 @@
1
- import {
2
- test_exports
3
- } from "./chunk-PK5L2SAF.js";
4
- import "./chunk-I2KLQ2HA.js";
5
-
6
- // src/utils/test/src/component-strategies/MenuComponentStrategy.ts
7
- var MenuComponentStrategy = class {
8
- constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
9
- this.mainSelector = mainSelector;
10
- this.selectors = selectors;
11
- this.actionTimeoutMs = actionTimeoutMs;
12
- this.assertionTimeoutMs = assertionTimeoutMs;
13
- }
14
- async resetState(page) {
15
- if (!this.selectors.popup) return;
16
- const popupSelector = this.selectors.popup;
17
- const popupElement = page.locator(popupSelector).first();
18
- const isPopupVisible = await popupElement.isVisible().catch(() => false);
19
- if (!isPopupVisible) return;
20
- let menuClosed = false;
21
- let closeSelector = this.selectors.input;
22
- if (!closeSelector && this.selectors.focusable) {
23
- closeSelector = this.selectors.focusable;
24
- } else if (!closeSelector) {
25
- closeSelector = this.selectors.trigger;
26
- }
27
- if (closeSelector) {
28
- const closeElement = page.locator(closeSelector).first();
29
- await closeElement.focus();
30
- await page.keyboard.press("Escape");
31
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
32
- }
33
- if (!menuClosed && this.selectors.trigger) {
34
- const triggerElement = page.locator(this.selectors.trigger).first();
35
- await triggerElement.click({ timeout: this.actionTimeoutMs });
36
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
37
- }
38
- if (!menuClosed) {
39
- await page.mouse.click(10, 10);
40
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
41
- }
42
- if (!menuClosed) {
43
- throw new Error(
44
- `\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
45
- 1. Escape key
46
- 2. Clicking trigger
47
- 3. Clicking outside
48
- This indicates a problem with the menu component's close functionality.`
49
- );
50
- }
51
- if (this.selectors.input) {
52
- await page.locator(this.selectors.input).first().clear();
53
- }
54
- if (this.selectors.trigger) {
55
- const triggerElement = page.locator(this.selectors.trigger).first();
56
- await triggerElement.focus();
57
- }
58
- }
59
- async shouldSkipTest(test, page) {
60
- const requiresSubmenu = test.action.some(
61
- (act) => act.target === "submenu" || act.target === "submenuTrigger" || act.target === "submenuItems"
62
- ) || test.assertions.some(
63
- (assertion) => assertion.target === "submenu" || assertion.target === "submenuTrigger" || assertion.target === "submenuItems"
64
- );
65
- if (!requiresSubmenu) {
66
- return false;
67
- }
68
- const submenuTriggerSelector = this.selectors.submenuTrigger;
69
- if (!submenuTriggerSelector) {
70
- return true;
71
- }
72
- const submenuTriggerCount = await page.locator(submenuTriggerSelector).count();
73
- return submenuTriggerCount === 0;
74
- }
75
- getMainSelector() {
76
- return this.mainSelector;
77
- }
78
- };
79
- export {
80
- MenuComponentStrategy
81
- };
@@ -1,7 +0,0 @@
1
- import {
2
- RelativeTargetResolver
3
- } from "./chunk-GLT43UVH.js";
4
- import "./chunk-I2KLQ2HA.js";
5
- export {
6
- RelativeTargetResolver
7
- };