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
package/dist/cli.cjs CHANGED
@@ -1,525 +1,34 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __esm = (fn, res) => function __init() {
10
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
- };
12
- var __export = (target, all) => {
13
- for (var name in all)
14
- __defProp(target, name, { get: all[name], enumerable: true });
15
- };
16
- var __copyProps = (to, from, except, desc) => {
17
- if (from && typeof from === "object" || typeof from === "function") {
18
- for (let key of __getOwnPropNames(from))
19
- if (!__hasOwnProp.call(to, key) && key !== except)
20
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
- }
22
- return to;
23
- };
24
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
25
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
- // If the importer is in node compatibility mode or this is not an ESM
27
- // file that has been converted to a CommonJS file using a Babel-
28
- // compatible transform (i.e. "__esModule" has not been set), then set
29
- // "default" to the CommonJS "module.exports" for node compatibility.
30
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
- mod
32
- ));
33
-
34
- // src/utils/cli/configLoader.ts
35
- var configLoader_exports = {};
36
- __export(configLoader_exports, {
37
- loadConfig: () => loadConfig
38
- });
39
- function validateConfig(config) {
40
- const errors = [];
41
- if (!config || typeof config !== "object") {
42
- errors.push("Config must be an object");
43
- return { valid: false, errors };
44
- }
45
- const cfg = config;
46
- if (cfg.audit !== void 0) {
47
- if (typeof cfg.audit !== "object" || cfg.audit === null) {
48
- errors.push("audit must be an object");
49
- } else {
50
- if (cfg.audit.urls !== void 0) {
51
- if (!Array.isArray(cfg.audit.urls)) {
52
- errors.push("audit.urls must be an array");
53
- } else if (cfg.audit.urls.some((url) => typeof url !== "string")) {
54
- errors.push("audit.urls must contain only strings");
55
- }
56
- }
57
- if (cfg.audit.output !== void 0) {
58
- if (typeof cfg.audit.output !== "object") {
59
- errors.push("audit.output must be an object");
60
- } else {
61
- const output = cfg.audit.output;
62
- if (output.format !== void 0) {
63
- if (!["json", "csv", "html", "all"].includes(output.format)) {
64
- errors.push("audit.output.format must be one of: json, csv, html, all");
65
- }
66
- }
67
- if (output.out !== void 0 && typeof output.out !== "string") {
68
- errors.push("audit.output.out must be a string");
69
- }
70
- }
71
- }
72
- }
73
- }
74
- if (cfg.test !== void 0) {
75
- if (typeof cfg.test !== "object" || cfg.test === null) {
76
- errors.push("test must be an object");
77
- } else {
78
- if (cfg.test.disableTimeouts !== void 0 && typeof cfg.test.disableTimeouts !== "boolean") {
79
- errors.push("test.disableTimeouts must be a boolean when provided");
80
- }
81
- const testTimeoutFields = [
82
- "actionTimeoutMs",
83
- "assertionTimeoutMs",
84
- "navigationTimeoutMs",
85
- "componentReadyTimeoutMs"
86
- ];
87
- for (const field of testTimeoutFields) {
88
- const value = cfg.test[field];
89
- if (value !== void 0) {
90
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
91
- errors.push(`test.${field} must be a non-negative number when provided`);
92
- }
93
- }
94
- }
95
- if (cfg.test.components !== void 0) {
96
- if (!Array.isArray(cfg.test.components)) {
97
- errors.push("test.components must be an array");
98
- } else {
99
- cfg.test.components.forEach((comp, idx) => {
100
- if (typeof comp !== "object" || comp === null) {
101
- errors.push(`test.components[${idx}] must be an object`);
102
- } else {
103
- if (typeof comp.name !== "string") {
104
- errors.push(`test.components[${idx}].name must be a string`);
105
- }
106
- if (comp.contractPath !== void 0 && typeof comp.contractPath !== "string") {
107
- errors.push(`test.components[${idx}].contractPath must be a string when provided`);
108
- }
109
- if (comp.strategyPath !== void 0 && typeof comp.strategyPath !== "string") {
110
- errors.push(`test.components[${idx}].strategyPath must be a string when provided`);
111
- }
112
- if (comp.strictness !== void 0 && !["minimal", "balanced", "strict", "paranoid"].includes(comp.strictness)) {
113
- errors.push(`test.components[${idx}].strictness must be one of: minimal, balanced, strict, paranoid`);
114
- }
115
- if (comp.disableTimeouts !== void 0 && typeof comp.disableTimeouts !== "boolean") {
116
- errors.push(`test.components[${idx}].disableTimeouts must be a boolean when provided`);
117
- }
118
- const componentTimeoutFields = [
119
- "actionTimeoutMs",
120
- "assertionTimeoutMs",
121
- "navigationTimeoutMs",
122
- "componentReadyTimeoutMs"
123
- ];
124
- for (const field of componentTimeoutFields) {
125
- const value = comp[field];
126
- if (value !== void 0) {
127
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
128
- errors.push(`test.components[${idx}].${field} must be a non-negative number when provided`);
129
- }
130
- }
131
- }
132
- }
133
- });
134
- }
135
- }
136
- if (cfg.test.strictness !== void 0) {
137
- if (!["minimal", "balanced", "strict", "paranoid"].includes(cfg.test.strictness)) {
138
- errors.push("test.strictness must be one of: minimal, balanced, strict, paranoid");
139
- }
140
- }
141
- }
142
- }
143
- if (cfg.contracts !== void 0) {
144
- if (!Array.isArray(cfg.contracts)) {
145
- errors.push("contracts must be an array");
146
- } else {
147
- cfg.contracts.forEach((contract, idx) => {
148
- if (typeof contract !== "object" || contract === null) {
149
- errors.push(`contracts[${idx}] must be an object`);
150
- } else {
151
- if (typeof contract.src !== "string") {
152
- errors.push(`contracts[${idx}].src is required and must be a string`);
153
- }
154
- if (contract.out !== void 0 && typeof contract.out !== "string") {
155
- errors.push(`contracts[${idx}].out must be a string`);
156
- }
157
- }
158
- });
159
- }
160
- }
161
- return { valid: errors.length === 0, errors };
162
- }
163
- async function loadConfigFile(filePath) {
164
- try {
165
- const ext = import_path.default.extname(filePath);
166
- if (ext === ".json") {
167
- const content = await import_fs_extra.default.readFile(filePath, "utf-8");
168
- return JSON.parse(content);
169
- } else if ([".js", ".mjs", ".cjs", ".ts"].includes(ext)) {
170
- const imported = await import(filePath);
171
- return imported.default || imported;
172
- }
173
- return null;
174
- } catch {
175
- return null;
176
- }
177
- }
178
- async function loadConfig(cwd = process.cwd()) {
179
- const configNames = [
180
- "ariaease.config.js",
181
- "ariaease.config.mjs",
182
- "ariaease.config.cjs",
183
- "ariaease.config.json",
184
- "ariaease.config.ts"
185
- ];
186
- let loadedConfig = null;
187
- let foundPath = null;
188
- const errors = [];
189
- for (const name of configNames) {
190
- const configPath = import_path.default.resolve(cwd, name);
191
- if (await import_fs_extra.default.pathExists(configPath)) {
192
- foundPath = configPath;
193
- loadedConfig = await loadConfigFile(configPath);
194
- if (loadedConfig === null) {
195
- errors.push(`Found config at ${name} but failed to load it. Check for syntax errors.`);
196
- continue;
197
- }
198
- const validation = validateConfig(loadedConfig);
199
- if (!validation.valid) {
200
- errors.push(`Config validation failed in ${name}:`);
201
- errors.push(...validation.errors.map((err) => ` - ${err}`));
202
- loadedConfig = null;
203
- continue;
204
- }
205
- break;
206
- }
207
- }
208
- return {
209
- config: loadedConfig || {},
210
- configPath: loadedConfig ? foundPath : null,
211
- errors
212
- };
213
- }
214
- var import_path, import_fs_extra;
215
- var init_configLoader = __esm({
216
- "src/utils/cli/configLoader.ts"() {
217
- "use strict";
218
- import_path = __toESM(require("path"), 1);
219
- import_fs_extra = __toESM(require("fs-extra"), 1);
220
- }
221
- });
222
-
223
- // src/utils/cli/badgeHelper.ts
224
- var badgeHelper_exports = {};
225
- __export(badgeHelper_exports, {
226
- BADGE_CONFIGS: () => BADGE_CONFIGS,
227
- displayAllBadges: () => displayAllBadges,
228
- displayBadgeInfo: () => displayBadgeInfo,
229
- getBadgeMarkdown: () => getBadgeMarkdown,
230
- promptAddBadge: () => promptAddBadge
231
- });
232
- function getBadgeMarkdown(badgeType) {
233
- const config = BADGE_CONFIGS[badgeType];
234
- return `[![${config.label}](${config.markdownUrl})](https://github.com/aria-ease/aria-ease)`;
235
- }
236
- function displayBadgeInfo(badgeType) {
237
- const markdown = getBadgeMarkdown(badgeType);
238
- console.log(import_chalk.default.cyan("\n\u{1F3C5} Show your accessibility commitment!"));
239
- console.log(import_chalk.default.white(" Add this badge to your README.md:\n"));
240
- console.log(import_chalk.default.green(" " + markdown));
241
- console.log(import_chalk.default.dim("\n This helps others discover accessibility tools and shows you care!\n"));
242
- }
243
- async function promptAddBadge(badgeType, cwd = process.cwd()) {
244
- const readmePath = import_path2.default.join(cwd, "README.md");
245
- const readmeExists = await import_fs_extra2.default.pathExists(readmePath);
246
- if (!readmeExists) {
247
- console.log(import_chalk.default.yellow(" \u2139\uFE0F No README.md found in current directory"));
248
- return;
249
- }
250
- const readmeContent = await import_fs_extra2.default.readFile(readmePath, "utf-8");
251
- const markdown = getBadgeMarkdown(badgeType);
252
- if (readmeContent.includes(markdown) || readmeContent.includes(BADGE_CONFIGS[badgeType].fileName)) {
253
- console.log(import_chalk.default.gray(" \u2713 Badge already in README.md"));
254
- return;
255
- }
256
- const rl = import_readline.default.createInterface({
257
- input: process.stdin,
258
- output: process.stdout
259
- });
260
- const answer = await new Promise((resolve) => {
261
- rl.question(import_chalk.default.cyan(" Add badge to README.md now? (y/n): "), (ans) => {
262
- rl.close();
263
- resolve(ans.toLowerCase().trim());
264
- });
265
- });
266
- if (answer === "y" || answer === "yes") {
267
- await addBadgeToReadme(readmePath, readmeContent, markdown);
268
- console.log(import_chalk.default.green(" \u2713 Badge added to README.md!"));
269
- } else {
270
- console.log(import_chalk.default.gray(" Skipped. You can add it manually anytime."));
271
- }
272
- }
273
- async function addBadgeToReadme(readmePath, content, badge) {
274
- const lines = content.split("\n");
275
- let insertIndex = 0;
276
- for (let i = 0; i < lines.length; i++) {
277
- const line = lines[i].trim();
278
- if (line.startsWith("[![") || line.startsWith("[!")) {
279
- insertIndex = i + 1;
280
- continue;
281
- }
282
- if (insertIndex > 0 && !line.startsWith("[![") && !line.startsWith("[!") && line.length > 0) {
283
- break;
284
- }
285
- if (insertIndex === 0 && line.startsWith("#")) {
286
- insertIndex = i + 2;
287
- break;
288
- }
289
- }
290
- if (insertIndex === 0) {
291
- insertIndex = 1;
292
- }
293
- lines.splice(insertIndex, 0, badge);
294
- await import_fs_extra2.default.writeFile(readmePath, lines.join("\n"), "utf-8");
295
- }
296
- function displayAllBadges() {
297
- console.log(import_chalk.default.cyan("\n\u{1F4CD} Available badges:"));
298
- console.log(import_chalk.default.white("\n For audits:"));
299
- console.log(import_chalk.default.green(" " + getBadgeMarkdown("audit")));
300
- console.log(import_chalk.default.white("\n For component testing:"));
301
- console.log(import_chalk.default.green(" " + getBadgeMarkdown("component")));
302
- console.log(import_chalk.default.white("\n For both (verified):"));
303
- console.log(import_chalk.default.green(" " + getBadgeMarkdown("verified")));
304
- console.log("");
305
- }
306
- var import_fs_extra2, import_path2, import_chalk, import_readline, BADGE_CONFIGS;
307
- var init_badgeHelper = __esm({
308
- "src/utils/cli/badgeHelper.ts"() {
309
- "use strict";
310
- import_fs_extra2 = __toESM(require("fs-extra"), 1);
311
- import_path2 = __toESM(require("path"), 1);
312
- import_chalk = __toESM(require("chalk"), 1);
313
- import_readline = __toESM(require("readline"), 1);
314
- BADGE_CONFIGS = {
315
- audit: {
316
- type: "audit",
317
- fileName: "audited-by-aria-ease.svg",
318
- label: "Audited by aria-ease",
319
- markdownUrl: "https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/audited-by-aria-ease.svg"
320
- },
321
- component: {
322
- type: "component",
323
- fileName: "components-tested-aria-ease.svg",
324
- label: "Components tested: aria-ease",
325
- markdownUrl: "https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/components-tested-aria-ease.svg"
326
- },
327
- verified: {
328
- type: "verified",
329
- fileName: "verified-by-aria-ease.svg",
330
- label: "Verified by aria-ease",
331
- markdownUrl: "https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/verified-by-aria-ease.svg"
332
- }
333
- };
334
- }
335
- });
336
-
337
- // src/utils/audit/src/audit/audit.ts
338
- var audit_exports = {};
339
- __export(audit_exports, {
340
- createAuditBrowser: () => createAuditBrowser,
341
- runAudit: () => runAudit
342
- });
343
- async function createAuditBrowser() {
344
- return await import_playwright2.chromium.launch({ headless: true });
345
- }
346
- async function runAudit(url, options) {
347
- let browser = options.browser;
348
- let browserCreated = false;
349
- const timeout = 6e4;
350
- const waitUntil = "domcontentloaded";
351
- try {
352
- if (!browser) {
353
- browser = await import_playwright2.chromium.launch({ headless: true });
354
- browserCreated = true;
355
- }
356
- const context = await browser.newContext();
357
- const page = await context.newPage();
358
- await page.goto(url, { waitUntil, timeout });
359
- try {
360
- await page.waitForSelector("main", { state: "visible", timeout });
361
- } catch (waitError) {
362
- console.warn(`\u26A0\uFE0F Warning: <main> landmark not found or not visible on ${url} after ${timeout}ms. Audit will continue, but results may be inaccurate. Consider adding a <main> element to improve audit accuracy. ${waitError instanceof Error ? waitError.message : String(waitError)}`);
363
- }
364
- const axe2 = new import_playwright.default({ page });
365
- const axeResults = await axe2.analyze();
366
- await page.close();
367
- await context.close();
368
- return axeResults;
369
- } catch (error) {
370
- if (error instanceof Error) {
371
- if (error.message.includes("Executable doesn't exist")) {
372
- console.error("\n\u274C Playwright browsers not found!\n");
373
- console.log("\u{1F4E6} First-time setup required:");
374
- console.log(" Run: npx playwright install chromium\n");
375
- console.log("\u{1F4A1} This downloads the browser needed for auditing (~200MB)");
376
- console.log(" You only need to do this once.\n");
377
- } else if (error.message.includes("page.goto: net::ERR_CONNECTION_REFUSED")) {
378
- console.error("\n\u274C Server Not Running!\n");
379
- console.log(" Make sure your server is running before auditing URL");
380
- console.log(" Run: npm run dev # or your start command");
381
- } else if (error.message.includes("page.goto: Protocol error (Page.navigate): Cannot navigate to invalid URL")) {
382
- console.error("\n\u274C Cannot audit invalid URL\n");
383
- } else {
384
- console.error("\u274C Audit error:", error.message);
385
- console.log(" Make sure you provide a valid URL");
386
- }
387
- } else {
388
- console.error("\u274C Audit error (non-Error):", String(error));
389
- }
390
- throw error;
391
- } finally {
392
- if (browser && browserCreated) {
393
- await browser.close();
394
- }
395
- }
396
- }
397
- var import_playwright, import_playwright2;
398
- var init_audit = __esm({
399
- "src/utils/audit/src/audit/audit.ts"() {
400
- "use strict";
401
- import_playwright = __toESM(require("@axe-core/playwright"), 1);
402
- import_playwright2 = require("playwright");
403
- }
404
- });
405
-
406
- // src/utils/audit/src/formatters/formatters.ts
407
- var formatters_exports = {};
408
- __export(formatters_exports, {
409
- formatResults: () => formatResults
410
- });
411
- function formatResults(allResults, format) {
412
- switch (format) {
413
- case "json":
414
- return JSON.stringify(
415
- allResults.flatMap(
416
- ({ url, result }) => result ? result.violations.flatMap(
417
- (v) => v.nodes.map((n) => ({
418
- URL: url,
419
- Rule: v.id,
420
- Impact: v.impact,
421
- Description: v.description,
422
- Target: n.target,
423
- FailureSummary: n.failureSummary
424
- }))
425
- ) : []
426
- ),
427
- null,
428
- 2
429
- );
430
- case "csv":
431
- return toCSV(allResults);
432
- case "html":
433
- return toHTML(allResults);
434
- default:
435
- return "";
436
- }
437
- }
438
- function toCSV(allResults) {
439
- const rows = ["URL,Rule,Impact,Description,Target,FailureSummary"];
440
- allResults.forEach(({ url, result }) => {
441
- if (result) {
442
- result.violations.forEach((v) => {
443
- v.nodes.forEach((n) => {
444
- rows.push(
445
- escapeCSV(url) + "," + escapeCSV(v.id) + "," + escapeCSV(v.impact) + "," + escapeCSV(v.description) + "," + escapeCSV(Array.isArray(n.target) ? n.target.join("; ") : String(n.target)) + "," + escapeCSV(n.failureSummary ?? "")
446
- );
447
- });
448
- });
449
- }
450
- });
451
- return rows.join("\n");
452
- }
453
- function escapeCSV(value) {
454
- const s = String(value ?? "");
455
- return `"${s.replace(/"/g, '""')}"`;
456
- }
457
- function toHTML(allResults) {
458
- const summary = {
459
- pagesAudited: 0,
460
- pagesWithViolations: 0,
461
- totalViolations: 0,
462
- distinctRules: /* @__PURE__ */ new Set(),
463
- impactCounts: /* @__PURE__ */ new Map()
464
- };
465
- allResults.forEach(({ result }) => {
466
- if (!result) return;
467
- summary.pagesAudited++;
468
- const pageViolations = result.violations.reduce((acc, v) => {
469
- const nodesCount = (v.nodes || []).length;
470
- if (nodesCount > 0) {
471
- summary.distinctRules.add(v.id);
472
- summary.totalViolations += nodesCount;
473
- acc += nodesCount;
474
- const impact = String(v.impact ?? "unknown");
475
- summary.impactCounts.set(impact, (summary.impactCounts.get(impact) || 0) + nodesCount);
476
- }
477
- return acc;
478
- }, 0);
479
- if (pageViolations > 0) summary.pagesWithViolations++;
480
- });
481
- const rows = [];
482
- allResults.forEach(({ url, result }) => {
483
- if (!result) return;
484
- result.violations.forEach((v) => {
485
- v.nodes.forEach((n) => {
486
- const target = Array.isArray(n.target) ? n.target.join("; ") : String(n.target);
487
- rows.push(`
2
+ "use strict";var os=Object.create;var He=Object.defineProperty;var ns=Object.getOwnPropertyDescriptor;var as=Object.getOwnPropertyNames;var cs=Object.getPrototypeOf,ls=Object.prototype.hasOwnProperty;var L=(c,e)=>()=>(c&&(e=c(c=0)),e);var K=(c,e)=>{for(var t in e)He(c,t,{get:e[t],enumerable:!0})},qe=(c,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of as(e))!ls.call(c,s)&&s!==t&&He(c,s,{get:()=>e[s],enumerable:!(r=ns(e,s))||r.enumerable});return c},Ce=(c,e,t)=>(qe(c,e,"default"),t&&qe(t,e,"default")),j=(c,e,t)=>(t=c!=null?os(cs(c)):{},qe(e||!c||!c.__esModule?He(t,"default",{value:c,enumerable:!0}):t,c));var _e={};K(_e,{loadConfig:()=>Je});function us(c){let e=[];if(!c||typeof c!="object")return e.push("Config must be an object"),{valid:!1,errors:e};let t=c;if(t.audit!==void 0){if(typeof t.audit!="object"||t.audit===null)e.push("audit must be an object");else if(t.audit.urls!==void 0&&(Array.isArray(t.audit.urls)?t.audit.urls.some(r=>typeof r!="string")&&e.push("audit.urls must contain only strings"):e.push("audit.urls must be an array")),t.audit.output!==void 0)if(typeof t.audit.output!="object")e.push("audit.output must be an object");else{let r=t.audit.output;r.format!==void 0&&(["json","csv","html","all"].includes(r.format)||e.push("audit.output.format must be one of: json, csv, html, all")),r.out!==void 0&&typeof r.out!="string"&&e.push("audit.output.out must be a string")}}if(t.test!==void 0)if(typeof t.test!="object"||t.test===null)e.push("test must be an object");else{t.test.disableTimeouts!==void 0&&typeof t.test.disableTimeouts!="boolean"&&e.push("test.disableTimeouts must be a boolean when provided");let r=["actionTimeoutMs","assertionTimeoutMs","navigationTimeoutMs","componentReadyTimeoutMs"];for(let s of r){let i=t.test[s];i!==void 0&&(typeof i!="number"||!Number.isFinite(i)||i<0)&&e.push(`test.${s} must be a non-negative number when provided`)}t.test.components!==void 0&&(Array.isArray(t.test.components)?t.test.components.forEach((s,i)=>{if(typeof s!="object"||s===null)e.push(`test.components[${i}] must be an object`);else{typeof s.name!="string"&&e.push(`test.components[${i}].name must be a string`),s.contractPath!==void 0&&typeof s.contractPath!="string"&&e.push(`test.components[${i}].contractPath must be a string when provided`),s.strategyPath!==void 0&&typeof s.strategyPath!="string"&&e.push(`test.components[${i}].strategyPath must be a string when provided`),s.strictness!==void 0&&!["minimal","balanced","strict","paranoid"].includes(s.strictness)&&e.push(`test.components[${i}].strictness must be one of: minimal, balanced, strict, paranoid`),s.disableTimeouts!==void 0&&typeof s.disableTimeouts!="boolean"&&e.push(`test.components[${i}].disableTimeouts must be a boolean when provided`);let o=["actionTimeoutMs","assertionTimeoutMs","navigationTimeoutMs","componentReadyTimeoutMs"];for(let a of o){let l=s[a];l!==void 0&&(typeof l!="number"||!Number.isFinite(l)||l<0)&&e.push(`test.components[${i}].${a} must be a non-negative number when provided`)}}}):e.push("test.components must be an array")),t.test.strictness!==void 0&&(["minimal","balanced","strict","paranoid"].includes(t.test.strictness)||e.push("test.strictness must be one of: minimal, balanced, strict, paranoid"))}return t.contracts!==void 0&&(Array.isArray(t.contracts)?t.contracts.forEach((r,s)=>{typeof r!="object"||r===null?e.push(`contracts[${s}] must be an object`):(typeof r.src!="string"&&e.push(`contracts[${s}].src is required and must be a string`),r.out!==void 0&&typeof r.out!="string"&&e.push(`contracts[${s}].out must be a string`))}):e.push("contracts must be an array")),{valid:e.length===0,errors:e}}async function ps(c){try{let e=Ne.default.extname(c);if(e===".json"){let t=await We.default.readFile(c,"utf-8");return JSON.parse(t)}else if([".js",".mjs",".cjs",".ts"].includes(e)){let t=await import(c);return t.default||t}return null}catch{return null}}async function Je(c=process.cwd()){let e=["ariaease.config.js","ariaease.config.mjs","ariaease.config.cjs","ariaease.config.json","ariaease.config.ts"],t=null,r=null,s=[];for(let i of e){let o=Ne.default.resolve(c,i);if(await We.default.pathExists(o)){if(r=o,t=await ps(o),t===null){s.push(`Found config at ${i} but failed to load it. Check for syntax errors.`);continue}let a=us(t);if(!a.valid){s.push(`Config validation failed in ${i}:`),s.push(...a.errors.map(l=>` - ${l}`)),t=null;continue}break}}return{config:t||{},configPath:t?r:null,errors:s}}var Ne,We,ke=L(()=>{"use strict";Ne=j(require("path"),1),We=j(require("fs-extra"),1)});var wt={};K(wt,{BADGE_CONFIGS:()=>ze,displayAllBadges:()=>ds,displayBadgeInfo:()=>Ge,getBadgeMarkdown:()=>fe,promptAddBadge:()=>Ye});function fe(c){let e=ze[c];return`[![${e.label}](${e.markdownUrl})](https://github.com/aria-ease/aria-ease)`}function Ge(c){let e=fe(c);console.log(O.default.cyan(`
3
+ \u{1F3C5} Show your accessibility commitment!`)),console.log(O.default.white(` Add this badge to your README.md:
4
+ `)),console.log(O.default.green(" "+e)),console.log(O.default.dim(`
5
+ This helps others discover accessibility tools and shows you care!
6
+ `))}async function Ye(c,e=process.cwd()){let t=ht.default.join(e,"README.md");if(!await Ae.default.pathExists(t)){console.log(O.default.yellow(" \u2139\uFE0F No README.md found in current directory"));return}let s=await Ae.default.readFile(t,"utf-8"),i=fe(c);if(s.includes(i)||s.includes(ze[c].fileName)){console.log(O.default.gray(" \u2713 Badge already in README.md"));return}let o=yt.default.createInterface({input:process.stdin,output:process.stdout}),a=await new Promise(l=>{o.question(O.default.cyan(" Add badge to README.md now? (y/n): "),p=>{o.close(),l(p.toLowerCase().trim())})});a==="y"||a==="yes"?(await fs(t,s,i),console.log(O.default.green(" \u2713 Badge added to README.md!"))):console.log(O.default.gray(" Skipped. You can add it manually anytime."))}async function fs(c,e,t){let r=e.split(`
7
+ `),s=0;for(let i=0;i<r.length;i++){let o=r[i].trim();if(o.startsWith("[![")||o.startsWith("[!")){s=i+1;continue}if(s>0&&!o.startsWith("[![")&&!o.startsWith("[!")&&o.length>0)break;if(s===0&&o.startsWith("#")){s=i+2;break}}s===0&&(s=1),r.splice(s,0,t),await Ae.default.writeFile(c,r.join(`
8
+ `),"utf-8")}function ds(){console.log(O.default.cyan(`
9
+ \u{1F4CD} Available badges:`)),console.log(O.default.white(`
10
+ For audits:`)),console.log(O.default.green(" "+fe("audit"))),console.log(O.default.white(`
11
+ For component testing:`)),console.log(O.default.green(" "+fe("component"))),console.log(O.default.white(`
12
+ For both (verified):`)),console.log(O.default.green(" "+fe("verified"))),console.log("")}var Ae,ht,O,yt,ze,Ke=L(()=>{"use strict";Ae=j(require("fs-extra"),1),ht=j(require("path"),1),O=j(require("chalk"),1),yt=j(require("readline"),1),ze={audit:{type:"audit",fileName:"audited-by-aria-ease.svg",label:"Audited by aria-ease",markdownUrl:"https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/audited-by-aria-ease.svg"},component:{type:"component",fileName:"components-tested-aria-ease.svg",label:"Components tested: aria-ease",markdownUrl:"https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/components-tested-aria-ease.svg"},verified:{type:"verified",fileName:"verified-by-aria-ease.svg",label:"Verified by aria-ease",markdownUrl:"https://cdn.jsdelivr.net/gh/aria-ease/aria-ease@main/badges/verified-by-aria-ease.svg"}}});var Xe={};K(Xe,{createAuditBrowser:()=>gs,runAudit:()=>ms});async function gs(){return await Qe.chromium.launch({headless:!0})}async function ms(c,e){let t=e.browser,r=!1,s=6e4,i="domcontentloaded";try{t||(t=await Qe.chromium.launch({headless:!0}),r=!0);let o=await t.newContext(),a=await o.newPage();await a.goto(c,{waitUntil:i,timeout:s});try{await a.waitForSelector("main",{state:"visible",timeout:s})}catch(f){console.warn(`\u26A0\uFE0F Warning: <main> landmark not found or not visible on ${c} after ${s}ms. Audit will continue, but results may be inaccurate. Consider adding a <main> element to improve audit accuracy. ${f instanceof Error?f.message:String(f)}`)}let p=await new bt.default({page:a}).analyze();return await a.close(),await o.close(),p}catch(o){throw o instanceof Error?o.message.includes("Executable doesn't exist")?(console.error(`
13
+ \u274C Playwright browsers not found!
14
+ `),console.log("\u{1F4E6} First-time setup required:"),console.log(` Run: npx playwright install chromium
15
+ `),console.log("\u{1F4A1} This downloads the browser needed for auditing (~200MB)"),console.log(` You only need to do this once.
16
+ `)):o.message.includes("page.goto: net::ERR_CONNECTION_REFUSED")?(console.error(`
17
+ \u274C Server Not Running!
18
+ `),console.log(" Make sure your server is running before auditing URL"),console.log(" Run: npm run dev # or your start command")):o.message.includes("page.goto: Protocol error (Page.navigate): Cannot navigate to invalid URL")?console.error(`
19
+ \u274C Cannot audit invalid URL
20
+ `):(console.error("\u274C Audit error:",o.message),console.log(" Make sure you provide a valid URL")):console.error("\u274C Audit error (non-Error):",String(o)),o}finally{t&&r&&await t.close()}}var bt,Qe,Ze=L(()=>{"use strict";bt=j(require("@axe-core/playwright"),1),Qe=require("playwright")});var $t={};K($t,{formatResults:()=>hs});function hs(c,e){switch(e){case"json":return JSON.stringify(c.flatMap(({url:t,result:r})=>r?r.violations.flatMap(s=>s.nodes.map(i=>({URL:t,Rule:s.id,Impact:s.impact,Description:s.description,Target:i.target,FailureSummary:i.failureSummary}))):[]),null,2);case"csv":return ys(c);case"html":return ws(c);default:return""}}function ys(c){let e=["URL,Rule,Impact,Description,Target,FailureSummary"];return c.forEach(({url:t,result:r})=>{r&&r.violations.forEach(s=>{s.nodes.forEach(i=>{e.push(de(t)+","+de(s.id)+","+de(s.impact)+","+de(s.description)+","+de(Array.isArray(i.target)?i.target.join("; "):String(i.target))+","+de(i.failureSummary??""))})})}),e.join(`
21
+ `)}function de(c){return`"${String(c??"").replace(/"/g,'""')}"`}function ws(c){let e={pagesAudited:0,pagesWithViolations:0,totalViolations:0,distinctRules:new Set,impactCounts:new Map};c.forEach(({result:p})=>{if(!p)return;e.pagesAudited++,p.violations.reduce((m,h)=>{let P=(h.nodes||[]).length;if(P>0){e.distinctRules.add(h.id),e.totalViolations+=P,m+=P;let q=String(h.impact??"unknown");e.impactCounts.set(q,(e.impactCounts.get(q)||0)+P)}return m},0)>0&&e.pagesWithViolations++});let t=[];c.forEach(({url:p,result:f})=>{f&&f.violations.forEach(m=>{m.nodes.forEach(h=>{let P=Array.isArray(h.target)?h.target.join("; "):String(h.target);t.push(`
488
22
  <tr>
489
- <td class="nowrap">${escapeHTML(url)}</td>
490
- <td class="nowrap">${escapeHTML(v.id)}</td>
491
- <td class="impact ${escapeClass(String(v.impact ?? "unknown"))}">${escapeHTML(String(v.impact ?? ""))}</td>
492
- <td class="desc">${escapeHTML(v.description ?? "")}</td>
493
- <td class="target"><code>${escapeHTML(target)}</code></td>
494
- <td class="fail">${escapeHTML(n.failureSummary ?? "").split(/\r?\n/).join("<br/>")}</td>
23
+ <td class="nowrap">${le(p)}</td>
24
+ <td class="nowrap">${le(m.id)}</td>
25
+ <td class="impact ${vt(String(m.impact??"unknown"))}">${le(String(m.impact??""))}</td>
26
+ <td class="desc">${le(m.description??"")}</td>
27
+ <td class="target"><code>${le(P)}</code></td>
28
+ <td class="fail">${le(h.failureSummary??"").split(/\r?\n/).join("<br/>")}</td>
495
29
  </tr>
496
- `);
497
- });
498
- });
499
- });
500
- const impactSummary = Array.from(summary.impactCounts.entries()).map(([impact, count]) => `<li><strong class="impact ${escapeClass(impact)}">${escapeHTML(impact)}</strong>: ${count}</li>`).join("\n");
501
- const d = /* @__PURE__ */ new Date();
502
- const pad = (n) => String(n).padStart(2, "0");
503
- const reportDateTime = `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${d.getFullYear()} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
504
- const headerSummary = `
505
- <section class="summary">
506
- <h2>Report summary</h2>
507
- <ul>
508
- <li><strong>Date:</strong> ${reportDateTime}</li>
509
- <li><strong>Pages audited:</strong> ${summary.pagesAudited}</li>
510
- <li><strong>Pages with violations:</strong> ${summary.pagesWithViolations}</li>
511
- <li><strong>Total violations:</strong> ${summary.totalViolations}</li>
512
- <li><strong>Distinct rules:</strong> ${summary.distinctRules.size}</li>
513
- </ul>
514
- <div class="impact-summary">
515
- <h3>By impact</h3>
516
- <ul class="summary-list">
517
- ${impactSummary || "<li>None</li>"}
518
- </ul>
519
- </div>
520
- </section>
521
- `.trim();
522
- const html = `
30
+ `)})})});let r=Array.from(e.impactCounts.entries()).map(([p,f])=>`<li><strong class="impact ${vt(p)}">${le(p)}</strong>: ${f}</li>`).join(`
31
+ `),s=new Date,i=p=>String(p).padStart(2,"0");return`
523
32
  <!DOCTYPE html>
524
33
  <html lang="en">
525
34
  <head>
@@ -563,7 +72,24 @@ function toHTML(allResults) {
563
72
  </head>
564
73
  <body>
565
74
  <h1>Aria-Ease Accessibility Audit Report</h1>
566
- ${headerSummary}
75
+ ${`
76
+ <section class="summary">
77
+ <h2>Report summary</h2>
78
+ <ul>
79
+ <li><strong>Date:</strong> ${`${i(s.getDate())}-${i(s.getMonth()+1)}-${s.getFullYear()} ${i(s.getHours())}:${i(s.getMinutes())}:${i(s.getSeconds())}`}</li>
80
+ <li><strong>Pages audited:</strong> ${e.pagesAudited}</li>
81
+ <li><strong>Pages with violations:</strong> ${e.pagesWithViolations}</li>
82
+ <li><strong>Total violations:</strong> ${e.totalViolations}</li>
83
+ <li><strong>Distinct rules:</strong> ${e.distinctRules.size}</li>
84
+ </ul>
85
+ <div class="impact-summary">
86
+ <h3>By impact</h3>
87
+ <ul class="summary-list">
88
+ ${r||"<li>None</li>"}
89
+ </ul>
90
+ </div>
91
+ </section>
92
+ `.trim()}
567
93
  <table>
568
94
  <thead>
569
95
  <tr>
@@ -571,2999 +97,111 @@ function toHTML(allResults) {
571
97
  </tr>
572
98
  </thead>
573
99
  <tbody>
574
- ${rows.join("\n") || '<tr><td colspan="6"><em>No violations found.</em></td></tr>'}
100
+ ${t.join(`
101
+ `)||'<tr><td colspan="6"><em>No violations found.</em></td></tr>'}
575
102
  </tbody>
576
103
  </table>
577
104
  </body>
578
105
  </html>
579
- `.trim();
580
- return html;
581
- }
582
- function escapeHTML(str) {
583
- return String(str ?? "").replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
584
- }
585
- function escapeClass(s) {
586
- return String(s ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
587
- }
588
- var init_formatters = __esm({
589
- "src/utils/audit/src/formatters/formatters.ts"() {
590
- "use strict";
591
- }
592
- });
593
-
594
- // src/utils/test/src/ContractReporter.ts
595
- var ContractReporter;
596
- var init_ContractReporter = __esm({
597
- "src/utils/test/src/ContractReporter.ts"() {
598
- "use strict";
599
- ContractReporter = class {
600
- startTime = 0;
601
- componentName = "";
602
- staticPasses = 0;
603
- staticFailures = 0;
604
- staticWarnings = 0;
605
- dynamicResults = [];
606
- totalTests = 0;
607
- skipped = 0;
608
- warnings = 0;
609
- isPlaywright = false;
610
- isCustomContract = false;
611
- apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
612
- hasPrintedStaticSection = false;
613
- hasPrintedDynamicSection = false;
614
- constructor(isPlaywright = false, isCustomContract = false) {
615
- this.isPlaywright = isPlaywright;
616
- this.isCustomContract = isCustomContract;
617
- }
618
- log(message) {
619
- process.stderr.write(message + "\n");
620
- }
621
- start(componentName, totalTests, apgUrl) {
622
- this.startTime = Date.now();
623
- this.componentName = componentName;
624
- this.totalTests = totalTests;
625
- this.hasPrintedStaticSection = false;
626
- this.hasPrintedDynamicSection = false;
627
- if (apgUrl) {
628
- this.apgUrl = apgUrl;
629
- }
630
- const mode = this.isPlaywright ? "Playwright (Real Browser)" : "jsdom (Fast)";
631
- this.log(`
632
- ${"\u2550".repeat(60)}`);
633
- this.log(`\u{1F50D} Testing ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} Component - ${mode}`);
634
- this.log(`${"\u2550".repeat(60)}
635
- `);
636
- }
637
- reportStatic(passes, failures, warnings = 0) {
638
- this.staticPasses = passes;
639
- this.staticFailures = failures;
640
- this.staticWarnings = warnings;
641
- }
642
- /**
643
- * Report individual static test pass
644
- */
645
- reportStaticTest(description, status, failureMessage, level) {
646
- if (!this.hasPrintedStaticSection) {
647
- this.log(`${"\u2500".repeat(60)}`);
648
- this.log(`\u{1F9EA} Static Assertions`);
649
- this.log(`${"\u2500".repeat(60)}`);
650
- this.hasPrintedStaticSection = true;
651
- }
652
- const icon = status === "pass" ? "\u2713" : status === "warn" ? "\u26A0" : status === "skip" ? "\u25CB" : "\u2717";
653
- this.log(` ${icon} ${description}`);
654
- if (level) {
655
- this.log(` \u21B3 level=${level}`);
656
- }
657
- if ((status === "fail" || status === "warn" || status === "skip") && failureMessage) {
658
- this.log(` \u21B3 ${failureMessage}`);
659
- }
660
- }
661
- /**
662
- * Report individual dynamic test result
663
- */
664
- reportTest(test, status, failureMessage) {
665
- if (!this.hasPrintedDynamicSection) {
666
- this.log("");
667
- this.log(`${"\u2500".repeat(60)}`);
668
- this.log(`\u2328\uFE0F Dynamic Interaction Tests`);
669
- this.log(`${"\u2500".repeat(60)}`);
670
- this.hasPrintedDynamicSection = true;
671
- }
672
- const result = {
673
- description: test.description,
674
- status,
675
- failureMessage,
676
- level: test.level
677
- };
678
- if (status === "skip") {
679
- result.skipReason = "Requires real browser (addEventListener events)";
680
- }
681
- this.dynamicResults.push(result);
682
- const icons = { pass: "\u2713", fail: "\u2717", warn: "\u26A0", skip: "\u25CB" };
683
- const levelPrefix = test.level ? `[${test.level.toUpperCase()}] ` : "";
684
- this.log(` ${icons[status]} ${levelPrefix}${test.description}`);
685
- if (status === "skip" && !this.isPlaywright) {
686
- this.log(` \u21B3 Skipped in jsdom (runs in Playwright)`);
687
- }
688
- if (status === "fail" && failureMessage) {
689
- this.log(` \u21B3 ${failureMessage}`);
690
- }
691
- if (status === "warn" && failureMessage) {
692
- this.log(` \u21B3 ${failureMessage}`);
693
- }
694
- if (status === "skip" && failureMessage) {
695
- this.log(` \u21B3 ${failureMessage}`);
696
- }
697
- }
698
- /**
699
- * Report all failures with actionable context
700
- */
701
- reportFailures(failures) {
702
- if (failures.length === 0) return;
703
- this.log(`
704
- ${"\u2500".repeat(60)}`);
705
- this.log(`\u274C Failures (${failures.length}):
706
- `);
707
- failures.forEach((failure, index) => {
708
- this.log(`${index + 1}. ${failure}`);
709
- if (failure.includes("aria-")) {
710
- this.log(` \u{1F4A1} Add the missing ARIA attribute to improve screen reader support`);
711
- } else if (failure.includes("focus")) {
712
- this.log(` \u{1F4A1} Check keyboard event handlers and focus management`);
713
- } else if (failure.includes("visible")) {
714
- this.log(` \u{1F4A1} Verify display/visibility styles and state management`);
715
- }
716
- this.log("");
717
- });
718
- }
719
- reportWarnings() {
720
- const warnings = this.dynamicResults.filter((r) => r.status === "warn");
721
- if (warnings.length === 0 && this.staticWarnings === 0) return;
722
- this.log(`
723
- ${"\u2500".repeat(60)}`);
724
- this.log(`\u26A0\uFE0F Warnings (${this.staticWarnings + warnings.length}):
725
- `);
726
- this.log(`These checks are failing but treated as warnings under the active strictness mode.
727
- `);
728
- warnings.forEach((test, index) => {
729
- this.log(`${index + 1}. ${test.description}`);
730
- if (test.failureMessage) {
731
- this.log(` \u21B3 ${test.failureMessage}`);
732
- }
733
- if (test.level) {
734
- this.log(` \u21B3 level=${test.level}`);
735
- }
736
- });
737
- if (this.apgUrl) {
738
- this.log(`
739
- Reference: ${this.apgUrl}
740
- `);
741
- }
742
- }
743
- /**
744
- * Report skipped tests with helpful context
745
- */
746
- reportSkipped() {
747
- if (this.skipped === 0 || this.isPlaywright) return;
748
- const skippedTests = this.dynamicResults.filter((r) => r.status === "skip");
749
- this.log(`
750
- ${"\u2500".repeat(60)}`);
751
- this.log(`\u2139\uFE0F Skipped Tests (${this.skipped}):
752
- `);
753
- this.log(`These tests use native keyboard events via addEventListener,`);
754
- this.log(`which jsdom cannot simulate. They run successfully in Playwright.
755
- `);
756
- skippedTests.forEach((test, index) => {
757
- this.log(`${index + 1}. ${test.description}`);
758
- });
759
- this.log(`
760
- \u{1F4A1} Run with Playwright for full validation:`);
761
- this.log(` testUiComponent('${this.componentName}', null, 'http://localhost:5173/test-harness?component=component_name')
762
- `);
763
- }
764
- /**
765
- * Generate final summary with statistics
766
- */
767
- summary(failures) {
768
- const duration = Date.now() - this.startTime;
769
- const dynamicPasses = this.dynamicResults.filter((r) => r.status === "pass").length;
770
- const dynamicFailures = this.dynamicResults.filter((r) => r.status === "fail").length;
771
- const dynamicWarnings = this.dynamicResults.filter((r) => r.status === "warn").length;
772
- this.skipped = this.dynamicResults.filter((r) => r.status === "skip").length;
773
- this.warnings = this.staticWarnings + dynamicWarnings;
774
- const totalPasses = this.staticPasses + dynamicPasses;
775
- const totalFailures = this.staticFailures + dynamicFailures;
776
- const totalRun = totalPasses + totalFailures + this.warnings;
777
- const getComponentMessage = () => {
778
- const componentDisplayName = `${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)}`;
779
- if (this.isCustomContract) {
780
- return `${componentDisplayName} component validates against your custom accessibility policy \u2713`;
781
- }
782
- return `${componentDisplayName} component meets Aria-Ease baseline WAI-ARIA expectations \u2713`;
783
- };
784
- if (failures.length > 0) {
785
- this.reportFailures(failures);
786
- }
787
- this.reportWarnings();
788
- this.reportSkipped();
789
- this.log(`
790
- ${"\u2550".repeat(60)}`);
791
- this.log(`\u{1F4CA} Summary
792
- `);
793
- if (totalFailures === 0 && this.skipped === 0 && this.warnings === 0) {
794
- this.log(`\u2705 All ${totalRun} tests passed!`);
795
- this.log(` ${getComponentMessage()}`);
796
- } else if (totalFailures === 0) {
797
- this.log(`\u2705 ${totalPasses}/${totalRun} tests passed`);
798
- if (this.skipped > 0) {
799
- this.log(`\u25CB ${this.skipped} tests skipped`);
800
- }
801
- if (this.warnings > 0) {
802
- this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings > 1 ? "s" : ""}`);
803
- }
804
- this.log(` ${getComponentMessage()}`);
805
- } else {
806
- this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
807
- this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);
808
- if (this.warnings > 0) {
809
- this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings > 1 ? "s" : ""}`);
810
- }
811
- if (this.skipped > 0) {
812
- this.log(`\u25CB ${this.skipped} test${this.skipped > 1 ? "s" : ""} skipped`);
813
- }
814
- }
815
- this.log(`\u23F1\uFE0F Duration: ${duration}ms`);
816
- this.log(`${"\u2550".repeat(60)}
817
- `);
818
- if (totalFailures > 0) {
819
- this.log(`\u{1F527} Next Steps:`);
820
- this.log(` 1. Review the failures above`);
821
- this.log(` 2. Fix ARIA attributes and keyboard handlers`);
822
- this.log(` 3. Re-run tests to verify fixes
823
- `);
824
- } else if (!this.isPlaywright && this.skipped > 0) {
825
- this.log(`\u2728 Optional: Run Playwright tests for complete validation
826
- `);
827
- }
828
- return {
829
- passes: totalPasses,
830
- failures: totalFailures,
831
- skipped: this.skipped,
832
- duration
833
- };
834
- }
835
- /**
836
- * Report an error during test execution
837
- */
838
- error(message, context) {
839
- this.log(`
840
- \u274C Error: ${message}`);
841
- if (context) {
842
- this.log(` Context: ${context}`);
843
- }
844
- this.log("");
845
- }
846
- };
847
- }
848
- });
849
-
850
- // src/utils/test/src/strictness.ts
851
- function normalizeLevel(level) {
852
- if (level === "required" || level === "recommended" || level === "optional") {
853
- return level;
854
- }
855
- return FALLBACK_LEVEL;
856
- }
857
- function normalizeStrictness(strictness) {
858
- if (strictness === "minimal" || strictness === "balanced" || strictness === "strict" || strictness === "paranoid") {
859
- return strictness;
860
- }
861
- return "balanced";
862
- }
863
- function resolveEnforcement(level, strictness) {
864
- const matrix = {
865
- minimal: {
866
- required: "error",
867
- recommended: "ignore",
868
- optional: "ignore"
869
- },
870
- balanced: {
871
- required: "error",
872
- recommended: "warning",
873
- optional: "ignore"
874
- },
875
- strict: {
876
- required: "error",
877
- recommended: "error",
878
- optional: "warning"
879
- },
880
- paranoid: {
881
- required: "error",
882
- recommended: "error",
883
- optional: "error"
884
- }
885
- };
886
- return matrix[strictness][level];
887
- }
888
- var FALLBACK_LEVEL;
889
- var init_strictness = __esm({
890
- "src/utils/test/src/strictness.ts"() {
891
- "use strict";
892
- FALLBACK_LEVEL = "required";
893
- }
894
- });
895
-
896
- // src/utils/test/src/contractTestRunner.ts
897
- async function runContractTests(contractPath, componentName, component, strictness) {
898
- const reporter = new ContractReporter(false);
899
- const strictnessMode = normalizeStrictness(strictness);
900
- if (!contractPath) {
901
- throw new Error(`No contract path provided for component: ${componentName}`);
902
- }
903
- const contractData = await import_promises.default.readFile(contractPath, "utf-8");
904
- const componentContract = JSON.parse(contractData);
905
- const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
906
- reporter.start(componentName, totalTests);
907
- const failures = [];
908
- const passes = [];
909
- const skipped = [];
910
- const warnings = [];
911
- const classifyFailure = (message, levelRaw) => {
912
- const level = normalizeLevel(levelRaw);
913
- const enforcement = resolveEnforcement(level, strictnessMode);
914
- if (enforcement === "error") {
915
- failures.push(message);
916
- return { status: "fail", level, detail: message };
917
- }
918
- if (enforcement === "warning") {
919
- warnings.push(message);
920
- return { status: "warn", level, detail: message };
921
- }
922
- const ignoredMessage = `${message} (ignored by strictness=${strictnessMode}, level=${level})`;
923
- skipped.push(ignoredMessage);
924
- return { status: "skip", level, detail: ignoredMessage };
925
- };
926
- let staticPassed = 0;
927
- let staticFailed = 0;
928
- let staticWarnings = 0;
929
- for (const rel of componentContract.relationships || []) {
930
- const relationshipLevel = normalizeLevel(rel.level);
931
- if (rel.type === "aria-reference") {
932
- const fromSelector = componentContract.selectors[rel.from];
933
- const toSelector = componentContract.selectors[rel.to];
934
- const relDescription = `${rel.from}.${rel.attribute} references ${rel.to}`;
935
- if (!fromSelector || !toSelector) {
936
- const outcome = classifyFailure(`Relationship selector missing: from="${rel.from}" or to="${rel.to}" not found in selectors.`, rel.level);
937
- if (outcome.status === "fail") staticFailed += 1;
938
- if (outcome.status === "warn") staticWarnings += 1;
939
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
940
- continue;
941
- }
942
- const fromTarget = component.querySelector(fromSelector);
943
- const toTarget = component.querySelector(toSelector);
944
- if (!fromTarget || !toTarget) {
945
- const outcome = classifyFailure(`Relationship target not found: ${!fromTarget ? rel.from : rel.to}.`, rel.level);
946
- if (outcome.status === "fail") staticFailed += 1;
947
- if (outcome.status === "warn") staticWarnings += 1;
948
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
949
- continue;
950
- }
951
- const toId = toTarget.getAttribute("id");
952
- const attrValue = fromTarget.getAttribute(rel.attribute) || "";
953
- if (!toId) {
954
- const outcome = classifyFailure(`Relationship target "${rel.to}" must have an id for ${rel.attribute} validation.`, rel.level);
955
- if (outcome.status === "fail") staticFailed += 1;
956
- if (outcome.status === "warn") staticWarnings += 1;
957
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
958
- continue;
959
- }
960
- const references = attrValue.split(/\s+/).filter(Boolean);
961
- if (!references.includes(toId)) {
962
- const outcome = classifyFailure(`Expected ${rel.from} ${rel.attribute} to reference id "${toId}", found "${attrValue}".`, rel.level);
963
- if (outcome.status === "fail") staticFailed += 1;
964
- if (outcome.status === "warn") staticWarnings += 1;
965
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
966
- continue;
967
- }
968
- passes.push(`Relationship valid: ${rel.from}.${rel.attribute} -> ${rel.to} (id=${toId}).`);
969
- staticPassed += 1;
970
- reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
971
- continue;
972
- }
973
- if (rel.type === "contains") {
974
- const parentSelector = componentContract.selectors[rel.parent];
975
- const childSelector = componentContract.selectors[rel.child];
976
- const relDescription = `${rel.parent} contains ${rel.child}`;
977
- if (!parentSelector || !childSelector) {
978
- const outcome = classifyFailure(`Relationship selector missing: parent="${rel.parent}" or child="${rel.child}" not found in selectors.`, rel.level);
979
- if (outcome.status === "fail") staticFailed += 1;
980
- if (outcome.status === "warn") staticWarnings += 1;
981
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
982
- continue;
983
- }
984
- const parentTarget = component.querySelector(parentSelector);
985
- if (!parentTarget) {
986
- const outcome = classifyFailure(`Relationship parent target not found: ${rel.parent}.`, rel.level);
987
- if (outcome.status === "fail") staticFailed += 1;
988
- if (outcome.status === "warn") staticWarnings += 1;
989
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
990
- continue;
991
- }
992
- const nestedChild = parentTarget.querySelector(childSelector);
993
- if (!nestedChild) {
994
- const outcome = classifyFailure(`Expected ${rel.parent} to contain descendant matching selector for ${rel.child}.`, rel.level);
995
- if (outcome.status === "fail") staticFailed += 1;
996
- if (outcome.status === "warn") staticWarnings += 1;
997
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
998
- continue;
999
- }
1000
- passes.push(`Relationship valid: ${rel.parent} contains ${rel.child}.`);
1001
- staticPassed += 1;
1002
- reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
1003
- }
1004
- }
1005
- for (const test of componentContract.static[0].assertions) {
1006
- if (test.target !== "relative") {
1007
- const staticLevel = normalizeLevel(test.level);
1008
- const selector = componentContract.selectors[test.target];
1009
- if (!selector) {
1010
- const outcome = classifyFailure(`Selector for target ${test.target} not found.`, test.level);
1011
- if (outcome.status === "fail") staticFailed += 1;
1012
- if (outcome.status === "warn") staticWarnings += 1;
1013
- reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
1014
- continue;
1015
- }
1016
- const target = component.querySelector(selector);
1017
- if (!target) {
1018
- const outcome = classifyFailure(`Target ${test.target} not found.`, test.level);
1019
- if (outcome.status === "fail") staticFailed += 1;
1020
- if (outcome.status === "warn") staticWarnings += 1;
1021
- reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
1022
- continue;
1023
- }
1024
- const attributeValue = target.getAttribute(test.attribute);
1025
- if (!test.expectedValue) {
1026
- const attributes = test.attribute.split(" | ");
1027
- const hasAnyAttribute = attributes.some((attr) => target.hasAttribute(attr));
1028
- if (!hasAnyAttribute) {
1029
- const outcome = classifyFailure(test.failureMessage + ` None of the attributes "${test.attribute}" are present.`, test.level);
1030
- if (outcome.status === "fail") staticFailed += 1;
1031
- if (outcome.status === "warn") staticWarnings += 1;
1032
- reporter.reportStaticTest(`${test.target} has ${test.attribute}`, outcome.status, outcome.detail, outcome.level);
1033
- } else {
1034
- passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
1035
- staticPassed += 1;
1036
- reporter.reportStaticTest(`${test.target} has ${test.attribute}`, "pass", void 0, staticLevel);
1037
- }
1038
- } else if (!attributeValue || typeof test.expectedValue === "string" && !test.expectedValue.split(" | ").includes(attributeValue)) {
1039
- const outcome = classifyFailure(test.failureMessage + ` Attribute value does not match expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`, test.level);
1040
- if (outcome.status === "fail") staticFailed += 1;
1041
- if (outcome.status === "warn") staticWarnings += 1;
1042
- reporter.reportStaticTest(`${test.target} has ${test.attribute}="${test.expectedValue}"`, outcome.status, outcome.detail, outcome.level);
1043
- } else {
1044
- passes.push(`Attribute value matches expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`);
1045
- staticPassed += 1;
1046
- reporter.reportStaticTest(`${test.target} has ${test.attribute}="${attributeValue}"`, "pass", void 0, staticLevel);
1047
- }
1048
- }
1049
- }
1050
- for (const dynamicTest of componentContract.dynamic) {
1051
- skipped.push(dynamicTest.description);
1052
- reporter.reportTest({ description: dynamicTest.description, level: dynamicTest.level }, "skip");
1053
- }
1054
- reporter.reportStatic(staticPassed, staticFailed, staticWarnings);
1055
- reporter.summary(failures);
1056
- return { passes, failures, skipped, warnings };
1057
- }
1058
- var import_promises;
1059
- var init_contractTestRunner = __esm({
1060
- "src/utils/test/src/contractTestRunner.ts"() {
1061
- "use strict";
1062
- import_promises = __toESM(require("fs/promises"), 1);
1063
- init_ContractReporter();
1064
- init_strictness();
1065
- }
1066
- });
1067
-
1068
- // src/utils/test/src/playwrightTestHarness.ts
1069
- async function getOrCreateBrowser() {
1070
- if (!sharedBrowser) {
1071
- sharedBrowser = await import_playwright3.chromium.launch({
1072
- headless: true,
1073
- // Launch with clean browser profile - no extensions, no user data
1074
- args: [
1075
- "--disable-extensions",
1076
- "--disable-blink-features=AutomationControlled"
1077
- ]
1078
- });
1079
- }
1080
- return sharedBrowser;
1081
- }
1082
- async function getOrCreateContext() {
1083
- if (!sharedContext) {
1084
- const browser = await getOrCreateBrowser();
1085
- sharedContext = await browser.newContext({
1086
- permissions: [],
1087
- ignoreHTTPSErrors: true
1088
- });
1089
- }
1090
- return sharedContext;
1091
- }
1092
- async function createTestPage() {
1093
- const context = await getOrCreateContext();
1094
- return await context.newPage();
1095
- }
1096
- async function closeSharedBrowser() {
1097
- if (sharedContext) {
1098
- await sharedContext.close();
1099
- sharedContext = null;
1100
- }
1101
- if (sharedBrowser) {
1102
- await sharedBrowser.close();
1103
- sharedBrowser = null;
1104
- }
1105
- }
1106
- var import_playwright3, sharedBrowser, sharedContext;
1107
- var init_playwrightTestHarness = __esm({
1108
- "src/utils/test/src/playwrightTestHarness.ts"() {
1109
- "use strict";
1110
- import_playwright3 = require("playwright");
1111
- sharedBrowser = null;
1112
- sharedContext = null;
1113
- }
1114
- });
1115
-
1116
- // node_modules/@playwright/test/index.mjs
1117
- var test_exports = {};
1118
- __export(test_exports, {
1119
- default: () => import_test.default
1120
- });
1121
- var import_test;
1122
- var init_test = __esm({
1123
- "node_modules/@playwright/test/index.mjs"() {
1124
- "use strict";
1125
- __reExport(test_exports, require("playwright/test"));
1126
- import_test = __toESM(require("playwright/test"), 1);
1127
- }
1128
- });
1129
-
1130
- // src/utils/test/src/component-strategies/MenuComponentStrategy.ts
1131
- var MenuComponentStrategy_exports = {};
1132
- __export(MenuComponentStrategy_exports, {
1133
- MenuComponentStrategy: () => MenuComponentStrategy
1134
- });
1135
- var MenuComponentStrategy;
1136
- var init_MenuComponentStrategy = __esm({
1137
- "src/utils/test/src/component-strategies/MenuComponentStrategy.ts"() {
1138
- "use strict";
1139
- init_test();
1140
- MenuComponentStrategy = class {
1141
- constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
1142
- this.mainSelector = mainSelector;
1143
- this.selectors = selectors;
1144
- this.actionTimeoutMs = actionTimeoutMs;
1145
- this.assertionTimeoutMs = assertionTimeoutMs;
1146
- }
1147
- async resetState(page) {
1148
- if (!this.selectors.popup) return;
1149
- const popupSelector = this.selectors.popup;
1150
- const popupElement = page.locator(popupSelector).first();
1151
- const isPopupVisible = await popupElement.isVisible().catch(() => false);
1152
- if (!isPopupVisible) return;
1153
- let menuClosed = false;
1154
- let closeSelector = this.selectors.input;
1155
- if (!closeSelector && this.selectors.focusable) {
1156
- closeSelector = this.selectors.focusable;
1157
- } else if (!closeSelector) {
1158
- closeSelector = this.selectors.trigger;
1159
- }
1160
- if (closeSelector) {
1161
- const closeElement = page.locator(closeSelector).first();
1162
- await closeElement.focus();
1163
- await page.keyboard.press("Escape");
1164
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
1165
- }
1166
- if (!menuClosed && this.selectors.trigger) {
1167
- const triggerElement = page.locator(this.selectors.trigger).first();
1168
- await triggerElement.click({ timeout: this.actionTimeoutMs });
1169
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
1170
- }
1171
- if (!menuClosed) {
1172
- await page.mouse.click(10, 10);
1173
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
1174
- }
1175
- if (!menuClosed) {
1176
- throw new Error(
1177
- `\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
106
+ `.trim()}function le(c){return String(c??"").replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;")}function vt(c){return String(c??"").toLowerCase().replace(/[^a-z0-9]+/g,"-")}var St=L(()=>{"use strict"});async function bs(){return ge||(ge=await Tt.chromium.launch({headless:!0,args:["--disable-extensions","--disable-blink-features=AutomationControlled"]})),ge}async function vs(){return me||(me=await(await bs()).newContext({permissions:[],ignoreHTTPSErrors:!0})),me}async function Ct(){return await(await vs()).newPage()}async function kt(){me&&(await me.close(),me=null),ge&&(await ge.close(),ge=null)}var Tt,ge,me,et=L(()=>{"use strict";Tt=require("playwright"),ge=null,me=null});function he(c){return c==="required"||c==="recommended"||c==="optional"?c:$s}function be(c){return c==="minimal"||c==="balanced"||c==="strict"||c==="paranoid"?c:"balanced"}function At(c,e){return{minimal:{required:"error",recommended:"ignore",optional:"ignore"},balanced:{required:"error",recommended:"warning",optional:"ignore"},strict:{required:"error",recommended:"error",optional:"warning"},paranoid:{required:"error",recommended:"error",optional:"error"}}[e][c]}var $s,tt=L(()=>{"use strict";$s="required"});var I={};K(I,{default:()=>Rt.default});var Rt,ve=L(()=>{"use strict";Ce(I,require("playwright/test"));Rt=j(require("playwright/test"),1)});var Et={};K(Et,{MenuComponentStrategy:()=>st});var st,xt=L(()=>{"use strict";ve();st=class{constructor(e,t,r=400,s=400){this.mainSelector=e;this.selectors=t;this.actionTimeoutMs=r;this.assertionTimeoutMs=s}async resetState(e){if(!this.selectors.popup)return;let t=this.selectors.popup,r=e.locator(t).first();if(!await r.isVisible().catch(()=>!1))return;let i=!1,o=this.selectors.input;if(!o&&this.selectors.focusable?o=this.selectors.focusable:o||(o=this.selectors.main),o&&(await e.locator(o).first().focus(),await e.keyboard.press("Escape"),i=await(0,I.expect)(r).toBeHidden({timeout:this.assertionTimeoutMs}).then(()=>!0).catch(()=>!1)),!i&&this.selectors.main&&(await e.locator(this.selectors.main).first().click({timeout:this.actionTimeoutMs}),i=await(0,I.expect)(r).toBeHidden({timeout:this.assertionTimeoutMs}).then(()=>!0).catch(()=>!1)),i||(await e.mouse.click(10,10),i=await(0,I.expect)(r).toBeHidden({timeout:this.assertionTimeoutMs}).then(()=>!0).catch(()=>!1)),!i)throw new Error(`\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
1178
107
  1. Escape key
1179
108
  2. Clicking trigger
1180
109
  3. Clicking outside
1181
- This indicates a problem with the menu component's close functionality.`
1182
- );
1183
- }
1184
- if (this.selectors.input) {
1185
- await page.locator(this.selectors.input).first().clear();
1186
- }
1187
- if (this.selectors.trigger) {
1188
- const triggerElement = page.locator(this.selectors.trigger).first();
1189
- await triggerElement.focus();
1190
- }
1191
- }
1192
- async shouldSkipTest(test, page) {
1193
- const requiresSubmenu = test.action.some(
1194
- (act) => act.target === "submenu" || act.target === "submenuTrigger" || act.target === "submenuItems"
1195
- ) || test.assertions.some(
1196
- (assertion) => assertion.target === "submenu" || assertion.target === "submenuTrigger" || assertion.target === "submenuItems"
1197
- );
1198
- if (!requiresSubmenu) {
1199
- return false;
1200
- }
1201
- const submenuTriggerSelector = this.selectors.submenuTrigger;
1202
- if (!submenuTriggerSelector) {
1203
- return true;
1204
- }
1205
- const submenuTriggerCount = await page.locator(submenuTriggerSelector).count();
1206
- return submenuTriggerCount === 0;
1207
- }
1208
- getMainSelector() {
1209
- return this.mainSelector;
1210
- }
1211
- };
1212
- }
1213
- });
1214
-
1215
- // src/utils/test/src/component-strategies/AccordionComponentStrategy.ts
1216
- var AccordionComponentStrategy_exports = {};
1217
- __export(AccordionComponentStrategy_exports, {
1218
- AccordionComponentStrategy: () => AccordionComponentStrategy
1219
- });
1220
- var AccordionComponentStrategy;
1221
- var init_AccordionComponentStrategy = __esm({
1222
- "src/utils/test/src/component-strategies/AccordionComponentStrategy.ts"() {
1223
- "use strict";
1224
- init_test();
1225
- AccordionComponentStrategy = class {
1226
- constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
1227
- this.mainSelector = mainSelector;
1228
- this.selectors = selectors;
1229
- this.actionTimeoutMs = actionTimeoutMs;
1230
- this.assertionTimeoutMs = assertionTimeoutMs;
1231
- }
1232
- async resetState(page) {
1233
- if (!this.selectors.panel || !this.selectors.trigger || this.selectors.popup) {
1234
- return;
1235
- }
1236
- const triggerSelector = this.selectors.trigger;
1237
- const panelSelector = this.selectors.panel;
1238
- if (!triggerSelector || !panelSelector) return;
1239
- const allTriggers = await page.locator(triggerSelector).all();
1240
- for (const trigger of allTriggers) {
1241
- const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
1242
- const triggerPanel = await trigger.getAttribute("aria-controls");
1243
- if (isExpanded && triggerPanel) {
1244
- await trigger.click({ timeout: this.actionTimeoutMs });
1245
- const panel = page.locator(`#${triggerPanel}`);
1246
- await (0, test_exports.expect)(panel).toBeHidden({ timeout: this.assertionTimeoutMs }).catch(() => {
1247
- });
1248
- }
1249
- }
1250
- }
1251
- async shouldSkipTest() {
1252
- return false;
1253
- }
1254
- getMainSelector() {
1255
- return this.mainSelector;
1256
- }
1257
- };
1258
- }
1259
- });
1260
-
1261
- // src/utils/test/src/component-strategies/ComboboxComponentStrategy.ts
1262
- var ComboboxComponentStrategy_exports = {};
1263
- __export(ComboboxComponentStrategy_exports, {
1264
- ComboboxComponentStrategy: () => ComboboxComponentStrategy
1265
- });
1266
- var ComboboxComponentStrategy;
1267
- var init_ComboboxComponentStrategy = __esm({
1268
- "src/utils/test/src/component-strategies/ComboboxComponentStrategy.ts"() {
1269
- "use strict";
1270
- init_test();
1271
- ComboboxComponentStrategy = class {
1272
- constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
1273
- this.mainSelector = mainSelector;
1274
- this.selectors = selectors;
1275
- this.actionTimeoutMs = actionTimeoutMs;
1276
- this.assertionTimeoutMs = assertionTimeoutMs;
1277
- }
1278
- async resetState(page) {
1279
- if (!this.selectors.popup) return;
1280
- const popupSelector = this.selectors.popup;
1281
- const popupElement = page.locator(popupSelector).first();
1282
- const isPopupVisible = await popupElement.isVisible().catch(() => false);
1283
- if (!isPopupVisible) return;
1284
- let popupClosed = false;
1285
- let closeSelector = this.selectors.input;
1286
- if (!closeSelector && this.selectors.focusable) {
1287
- closeSelector = this.selectors.focusable;
1288
- } else if (!closeSelector) {
1289
- closeSelector = this.selectors.button;
1290
- }
1291
- if (closeSelector) {
1292
- const closeElement = page.locator(closeSelector).first();
1293
- await closeElement.focus();
1294
- await page.keyboard.press("Escape");
1295
- popupClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
1296
- }
1297
- if (!popupClosed && this.selectors.button) {
1298
- const buttonElement = page.locator(this.selectors.button).first();
1299
- await buttonElement.click({ timeout: this.actionTimeoutMs });
1300
- popupClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
1301
- }
1302
- if (!popupClosed) {
1303
- await page.mouse.click(10, 10);
1304
- popupClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
1305
- }
1306
- if (!popupClosed) {
1307
- throw new Error(
1308
- `\u274C FATAL: Cannot close combobox popup between tests. Popup remains visible after trying:
110
+ This indicates a problem with the menu component's close functionality.`);this.selectors.input&&await e.locator(this.selectors.input).first().clear(),this.selectors.main&&await e.locator(this.selectors.main).first().focus()}async shouldSkipTest(e,t){if(!(e.action.some(o=>o.target==="submenu"||o.target==="submenuTrigger"||o.target==="submenuItems")||e.assertions.some(o=>o.target==="submenu"||o.target==="submenuTrigger"||o.target==="submenuItems")))return!1;let s=this.selectors.submenuTrigger;return s?await t.locator(s).count()===0:!0}getMainSelector(){return this.mainSelector}}});var Mt={};K(Mt,{AccordionComponentStrategy:()=>rt});var rt,Pt=L(()=>{"use strict";ve();rt=class{constructor(e,t,r=400,s=400){this.mainSelector=e;this.selectors=t;this.actionTimeoutMs=r;this.assertionTimeoutMs=s}async resetState(e){if(!this.selectors.panel||!this.selectors.trigger||this.selectors.popup)return;let t=this.selectors.trigger,r=this.selectors.panel;if(!t||!r)return;let s=await e.locator(t).all();for(let i of s){let o=await i.getAttribute("aria-expanded")==="true",a=await i.getAttribute("aria-controls");if(o&&a){await i.click({timeout:this.actionTimeoutMs});let l=e.locator(`#${a}`);await(0,I.expect)(l).toBeHidden({timeout:this.assertionTimeoutMs}).catch(()=>{})}}}async shouldSkipTest(){return!1}getMainSelector(){return this.mainSelector}}});var Bt={};K(Bt,{ComboboxComponentStrategy:()=>it});var it,Lt=L(()=>{"use strict";ve();it=class{constructor(e,t,r=400,s=400){this.mainSelector=e;this.selectors=t;this.actionTimeoutMs=r;this.assertionTimeoutMs=s}async resetState(e){if(!this.selectors.popup)return;let t=this.selectors.popup,r=e.locator(t).first();if(!await r.isVisible().catch(()=>!1))return;let i=!1,o=this.selectors.input;if(!o&&this.selectors.focusable?o=this.selectors.focusable:o||(o=this.selectors.button),o&&(await e.locator(o).first().focus(),await e.keyboard.press("Escape"),i=await(0,I.expect)(r).toBeHidden({timeout:this.assertionTimeoutMs}).then(()=>!0).catch(()=>!1)),!i&&this.selectors.button&&(await e.locator(this.selectors.button).first().click({timeout:this.actionTimeoutMs}),i=await(0,I.expect)(r).toBeHidden({timeout:this.assertionTimeoutMs}).then(()=>!0).catch(()=>!1)),i||(await e.mouse.click(10,10),i=await(0,I.expect)(r).toBeHidden({timeout:this.assertionTimeoutMs}).then(()=>!0).catch(()=>!1)),!i)throw new Error(`\u274C FATAL: Cannot close combobox popup between tests. Popup remains visible after trying:
1309
111
  1. Escape key
1310
112
  2. Clicking button
1311
113
  3. Clicking outside
1312
- This indicates a problem with the combobox component's close functionality.`
1313
- );
1314
- }
1315
- if (this.selectors.input) {
1316
- await page.locator(this.selectors.input).first().clear();
1317
- }
1318
- }
1319
- async shouldSkipTest() {
1320
- return false;
1321
- }
1322
- getMainSelector() {
1323
- return this.mainSelector;
1324
- }
1325
- };
1326
- }
1327
- });
1328
-
1329
- // src/utils/test/src/component-strategies/TabsComponentStrategy.ts
1330
- var TabsComponentStrategy_exports = {};
1331
- __export(TabsComponentStrategy_exports, {
1332
- TabsComponentStrategy: () => TabsComponentStrategy
1333
- });
1334
- var TabsComponentStrategy;
1335
- var init_TabsComponentStrategy = __esm({
1336
- "src/utils/test/src/component-strategies/TabsComponentStrategy.ts"() {
1337
- "use strict";
1338
- TabsComponentStrategy = class {
1339
- constructor(mainSelector, selectors) {
1340
- this.mainSelector = mainSelector;
1341
- this.selectors = selectors;
1342
- }
1343
- async resetState() {
1344
- }
1345
- async shouldSkipTest(test, page) {
1346
- if (test.isVertical !== void 0 && this.selectors.tablist) {
1347
- const tablistSelector = this.selectors.tablist;
1348
- const tablist = page.locator(tablistSelector).first();
1349
- const orientation = await tablist.getAttribute("aria-orientation");
1350
- const isVertical = orientation === "vertical";
1351
- if (test.isVertical !== isVertical) {
1352
- return true;
1353
- }
1354
- }
1355
- return false;
1356
- }
1357
- getMainSelector() {
1358
- return this.mainSelector;
1359
- }
1360
- };
1361
- }
1362
- });
1363
-
1364
- // src/utils/test/src/StrategyRegistry.ts
1365
- var import_path3, import_url, StrategyRegistry;
1366
- var init_StrategyRegistry = __esm({
1367
- "src/utils/test/src/StrategyRegistry.ts"() {
1368
- "use strict";
1369
- import_path3 = __toESM(require("path"), 1);
1370
- import_url = require("url");
1371
- StrategyRegistry = class {
1372
- builtInStrategies = /* @__PURE__ */ new Map();
1373
- constructor() {
1374
- this.registerBuiltInStrategies();
1375
- }
1376
- /**
1377
- * Register built-in strategies
1378
- */
1379
- registerBuiltInStrategies() {
1380
- this.builtInStrategies.set(
1381
- "menu",
1382
- () => Promise.resolve().then(() => (init_MenuComponentStrategy(), MenuComponentStrategy_exports)).then(
1383
- (m) => m.MenuComponentStrategy
1384
- )
1385
- );
1386
- this.builtInStrategies.set(
1387
- "accordion",
1388
- () => Promise.resolve().then(() => (init_AccordionComponentStrategy(), AccordionComponentStrategy_exports)).then(
1389
- (m) => m.AccordionComponentStrategy
1390
- )
1391
- );
1392
- this.builtInStrategies.set(
1393
- "combobox",
1394
- () => Promise.resolve().then(() => (init_ComboboxComponentStrategy(), ComboboxComponentStrategy_exports)).then(
1395
- (m) => m.ComboboxComponentStrategy
1396
- )
1397
- );
1398
- this.builtInStrategies.set(
1399
- "tabs",
1400
- () => Promise.resolve().then(() => (init_TabsComponentStrategy(), TabsComponentStrategy_exports)).then(
1401
- (m) => m.TabsComponentStrategy
1402
- )
1403
- );
1404
- }
1405
- /**
1406
- * Load a strategy - either from custom path or built-in registry
1407
- * @param componentName - Component name (e.g., "menu", "accordion")
1408
- * @param customStrategyPath - Optional custom strategy file path
1409
- * @returns Strategy constructor function or null if not found
1410
- */
1411
- async loadStrategy(componentName, customStrategyPath, configBaseDir) {
1412
- try {
1413
- if (customStrategyPath) {
1414
- try {
1415
- const resolvedCustomPath = import_path3.default.isAbsolute(customStrategyPath) ? customStrategyPath : import_path3.default.resolve(configBaseDir || process.cwd(), customStrategyPath);
1416
- const customModule = await import((0, import_url.pathToFileURL)(resolvedCustomPath).href);
1417
- const strategy = customModule.default || customModule;
1418
- if (!strategy) {
1419
- throw new Error(`No default export found in ${customStrategyPath}`);
1420
- }
1421
- return strategy;
1422
- } catch (error) {
1423
- throw new Error(
1424
- `Failed to load custom strategy from ${customStrategyPath}: ${error instanceof Error ? error.message : String(error)}`
1425
- );
1426
- }
1427
- }
1428
- const builtInLoader = this.builtInStrategies.get(componentName);
1429
- if (!builtInLoader) {
1430
- return null;
1431
- }
1432
- return builtInLoader();
1433
- } catch (error) {
1434
- throw new Error(
1435
- `Strategy loading failed for ${componentName}: ${error instanceof Error ? error.message : String(error)}`
1436
- );
1437
- }
1438
- }
1439
- /**
1440
- * Check if a strategy exists (either built-in or custom path provided)
1441
- */
1442
- has(componentName, customStrategyPath) {
1443
- return !!customStrategyPath || this.builtInStrategies.has(componentName);
1444
- }
1445
- };
1446
- }
1447
- });
1448
-
1449
- // src/utils/test/src/ComponentDetector.ts
1450
- var import_fs, import_path4, import_meta, ComponentDetector;
1451
- var init_ComponentDetector = __esm({
1452
- "src/utils/test/src/ComponentDetector.ts"() {
1453
- "use strict";
1454
- import_fs = require("fs");
1455
- import_path4 = __toESM(require("path"), 1);
1456
- init_StrategyRegistry();
1457
- import_meta = {};
1458
- ComponentDetector = class {
1459
- static strategyRegistry = new StrategyRegistry();
1460
- static isComponentConfig(value) {
1461
- return typeof value === "object" && value !== null;
1462
- }
1463
- /**
1464
- * Detect and instantiate a component strategy
1465
- * Supports:
1466
- * - Built-in strategies (menu, accordion, combobox, tabs)
1467
- * - Custom strategies via config (strategyPath)
1468
- * - Custom contract paths via config (path)
1469
- * @param componentName - Component name
1470
- * @param componentConfig - Component config from ariaease.config.js
1471
- * @param actionTimeoutMs - Action timeout in milliseconds
1472
- * @param assertionTimeoutMs - Assertion timeout in milliseconds
1473
- * @returns Instantiated ComponentStrategy or null
1474
- */
1475
- static async detect(componentName, componentConfig, actionTimeoutMs = 400, assertionTimeoutMs = 400, configBaseDir) {
1476
- const typedComponentConfig = this.isComponentConfig(componentConfig) ? componentConfig : void 0;
1477
- const contractPath = typedComponentConfig?.contractPath;
1478
- if (!contractPath) {
1479
- throw new Error(`Contract path not found for component: ${componentName}`);
1480
- }
1481
- const resolvedPath = (() => {
1482
- if (import_path4.default.isAbsolute(contractPath)) return contractPath;
1483
- if (configBaseDir) {
1484
- const configResolved = import_path4.default.resolve(configBaseDir, contractPath);
1485
- try {
1486
- (0, import_fs.readFileSync)(configResolved, "utf-8");
1487
- return configResolved;
1488
- } catch {
1489
- }
1490
- }
1491
- const cwdResolved = import_path4.default.resolve(process.cwd(), contractPath);
1492
- try {
1493
- (0, import_fs.readFileSync)(cwdResolved, "utf-8");
1494
- return cwdResolved;
1495
- } catch {
1496
- return new URL(contractPath, import_meta.url).pathname;
1497
- }
1498
- })();
1499
- const contractData = (0, import_fs.readFileSync)(resolvedPath, "utf-8");
1500
- const componentContract = JSON.parse(contractData);
1501
- const selectors = componentContract.selectors;
1502
- const strategyClass = await this.strategyRegistry.loadStrategy(
1503
- componentName,
1504
- typedComponentConfig?.strategyPath,
1505
- configBaseDir
1506
- );
1507
- if (!strategyClass) {
1508
- return null;
1509
- }
1510
- const mainSelector = selectors.main;
1511
- if (componentName === "tabs") {
1512
- return new strategyClass(mainSelector, selectors);
1513
- }
1514
- return new strategyClass(
1515
- mainSelector,
1516
- selectors,
1517
- actionTimeoutMs,
1518
- assertionTimeoutMs
1519
- );
1520
- }
1521
- };
1522
- }
1523
- });
1524
-
1525
- // src/utils/test/src/RelativeTargetResolver.ts
1526
- var RelativeTargetResolver_exports = {};
1527
- __export(RelativeTargetResolver_exports, {
1528
- RelativeTargetResolver: () => RelativeTargetResolver
1529
- });
1530
- var RelativeTargetResolver;
1531
- var init_RelativeTargetResolver = __esm({
1532
- "src/utils/test/src/RelativeTargetResolver.ts"() {
1533
- "use strict";
1534
- RelativeTargetResolver = class {
1535
- /**
1536
- * Resolve a relative target like "first", "second", "last", "next", "previous"
1537
- * @param page Playwright page instance
1538
- * @param selector Base selector to find elements
1539
- * @param relative Relative position (first, second, last, next, previous)
1540
- * @returns The resolved Locator or null if not found
1541
- */
1542
- static async resolve(page, selector, relative) {
1543
- const items = await page.locator(selector).all();
1544
- switch (relative) {
1545
- case "first":
1546
- return items[0];
1547
- case "second":
1548
- return items[1];
1549
- case "last":
1550
- return items[items.length - 1];
1551
- case "next": {
1552
- const currentIndex = await page.evaluate(([sel]) => {
1553
- const items2 = Array.from(document.querySelectorAll(sel));
1554
- return items2.indexOf(document.activeElement);
1555
- }, [selector]);
1556
- const nextIndex = (currentIndex + 1) % items.length;
1557
- return items[nextIndex];
1558
- }
1559
- case "previous": {
1560
- const currentIndex = await page.evaluate(([sel]) => {
1561
- const items2 = Array.from(document.querySelectorAll(sel));
1562
- return items2.indexOf(document.activeElement);
1563
- }, [selector]);
1564
- const prevIndex = (currentIndex - 1 + items.length) % items.length;
1565
- return items[prevIndex];
1566
- }
1567
- default:
1568
- return null;
1569
- }
1570
- }
1571
- };
1572
- }
1573
- });
1574
-
1575
- // src/utils/test/src/ActionExecutor.ts
1576
- var ActionExecutor;
1577
- var init_ActionExecutor = __esm({
1578
- "src/utils/test/src/ActionExecutor.ts"() {
1579
- "use strict";
1580
- init_RelativeTargetResolver();
1581
- ActionExecutor = class {
1582
- constructor(page, selectors, timeoutMs = 400) {
1583
- this.page = page;
1584
- this.selectors = selectors;
1585
- this.timeoutMs = timeoutMs;
1586
- }
1587
- /**
1588
- * Check if error is due to browser/page being closed
1589
- */
1590
- isBrowserClosedError(error) {
1591
- return error instanceof Error && error.message.includes("Target page, context or browser has been closed");
1592
- }
1593
- /**
1594
- * Execute focus action
1595
- */
1596
- /**
1597
- * Execute focus action (supports absolute, relative, and virtual focus)
1598
- * @param target - selector key (e.g. "input", "button", "relative", or "virtual")
1599
- * @param relativeTarget - for relative focus (e.g. "first", "last")
1600
- * @param virtualId - for virtual focus (aria-activedescendant value)
1601
- */
1602
- async focus(target, relativeTarget, virtualId) {
1603
- try {
1604
- if (target === "virtual" && virtualId) {
1605
- const mainSelector = this.selectors.main;
1606
- if (!mainSelector) {
1607
- return { success: false, error: `Main selector not defined for virtual focus.` };
1608
- }
1609
- const main = this.page.locator(mainSelector).first();
1610
- const exists = await main.count();
1611
- if (!exists) {
1612
- return { success: false, error: `Main element not found for virtual focus.` };
1613
- }
1614
- await main.evaluate((el, id) => {
1615
- el.setAttribute("aria-activedescendant", id);
1616
- }, virtualId);
1617
- return { success: true };
1618
- }
1619
- if (target === "relative" && relativeTarget) {
1620
- const relativeSelector = this.selectors.relative;
1621
- if (!relativeSelector) {
1622
- return { success: false, error: `Relative selector not defined for focus action.` };
1623
- }
1624
- const element = await RelativeTargetResolver.resolve(this.page, relativeSelector, relativeTarget);
1625
- if (!element) {
1626
- return { success: false, error: `Could not resolve relative target ${relativeTarget} for focus.` };
1627
- }
1628
- await element.focus({ timeout: this.timeoutMs });
1629
- return { success: true };
1630
- }
1631
- const selector = this.selectors[target];
1632
- if (!selector) {
1633
- return { success: false, error: `Selector for focus target ${target} not found.` };
1634
- }
1635
- await this.page.locator(selector).first().focus({ timeout: this.timeoutMs });
1636
- return { success: true };
1637
- } catch (error) {
1638
- if (this.isBrowserClosedError(error)) {
1639
- return {
1640
- success: false,
1641
- error: `CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`,
1642
- shouldBreak: true
1643
- };
1644
- }
1645
- return {
1646
- success: false,
1647
- error: `Failed to focus ${target}: ${error instanceof Error ? error.message : String(error)}`
1648
- };
1649
- }
1650
- }
1651
- /**
1652
- * Execute type/fill action
1653
- */
1654
- async type(target, value) {
1655
- try {
1656
- const selector = this.selectors[target];
1657
- if (!selector) {
1658
- return { success: false, error: `Selector for type target ${target} not found.` };
1659
- }
1660
- await this.page.locator(selector).first().fill(value, { timeout: this.timeoutMs });
1661
- return { success: true };
1662
- } catch (error) {
1663
- if (this.isBrowserClosedError(error)) {
1664
- return {
1665
- success: false,
1666
- error: `CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`,
1667
- shouldBreak: true
1668
- };
1669
- }
1670
- return {
1671
- success: false,
1672
- error: `Failed to type into ${target}: ${error instanceof Error ? error.message : String(error)}`
1673
- };
1674
- }
1675
- }
1676
- /**
1677
- * Execute click action
1678
- */
1679
- async click(target, relativeTarget) {
1680
- try {
1681
- if (target === "document") {
1682
- await this.page.mouse.click(10, 10);
1683
- return { success: true };
1684
- }
1685
- if (target === "relative" && relativeTarget) {
1686
- const relativeSelector = this.selectors.relative;
1687
- if (!relativeSelector) {
1688
- return { success: false, error: `Relative selector not defined for click action.` };
1689
- }
1690
- const element = await RelativeTargetResolver.resolve(this.page, relativeSelector, relativeTarget);
1691
- if (!element) {
1692
- return { success: false, error: `Could not resolve relative target ${relativeTarget} for click.` };
1693
- }
1694
- await element.click({ timeout: this.timeoutMs });
1695
- return { success: true };
1696
- }
1697
- const selector = this.selectors[target];
1698
- if (!selector) {
1699
- return { success: false, error: `Selector for action target ${target} not found.` };
1700
- }
1701
- await this.page.locator(selector).first().click({ timeout: this.timeoutMs });
1702
- return { success: true };
1703
- } catch (error) {
1704
- if (this.isBrowserClosedError(error)) {
1705
- return {
1706
- success: false,
1707
- error: `CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`,
1708
- shouldBreak: true
1709
- };
1710
- }
1711
- return {
1712
- success: false,
1713
- error: `Failed to click ${target}: ${error instanceof Error ? error.message : String(error)}`
1714
- };
1715
- }
1716
- }
1717
- /**
1718
- * Execute keypress action
1719
- */
1720
- async keypress(target, key) {
1721
- try {
1722
- const keyMap = {
1723
- "Space": "Space",
1724
- "Enter": "Enter",
1725
- "Escape": "Escape",
1726
- "Arrow Up": "ArrowUp",
1727
- "Arrow Down": "ArrowDown",
1728
- "Arrow Left": "ArrowLeft",
1729
- "Arrow Right": "ArrowRight",
1730
- "Home": "Home",
1731
- "End": "End",
1732
- "Tab": "Tab"
1733
- };
1734
- let keyValue = keyMap[key] || key;
1735
- if (keyValue === "Space") {
1736
- keyValue = " ";
1737
- } else if (keyValue.includes(" ")) {
1738
- keyValue = keyValue.replace(/ /g, "");
1739
- }
1740
- if (target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape", "Home", "End", "Tab", "Shift+Tab"].includes(keyValue)) {
1741
- await this.page.keyboard.press(keyValue);
1742
- return { success: true };
1743
- }
1744
- const selector = this.selectors[target];
1745
- if (!selector) {
1746
- return { success: false, error: `Selector for keypress target ${target} not found.` };
1747
- }
1748
- const locator = this.page.locator(selector).first();
1749
- const elementCount = await locator.count();
1750
- if (elementCount === 0) {
1751
- return {
1752
- success: false,
1753
- error: `${target} element not found.`,
1754
- shouldBreak: true
1755
- // Signal to skip this test
1756
- };
1757
- }
1758
- await locator.press(keyValue, { timeout: this.timeoutMs });
1759
- return { success: true };
1760
- } catch (error) {
1761
- if (this.isBrowserClosedError(error)) {
1762
- return {
1763
- success: false,
1764
- error: `CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`,
1765
- shouldBreak: true
1766
- };
1767
- }
1768
- return {
1769
- success: false,
1770
- error: `Failed to press ${key} on ${target}: ${error instanceof Error ? error.message : String(error)}`
1771
- };
1772
- }
1773
- }
1774
- /**
1775
- * Execute hover action
1776
- */
1777
- async hover(target, relativeTarget) {
1778
- try {
1779
- if (target === "relative" && relativeTarget) {
1780
- const relativeSelector = this.selectors.relative;
1781
- if (!relativeSelector) {
1782
- return { success: false, error: `Relative selector not defined for hover action.` };
1783
- }
1784
- const element = await RelativeTargetResolver.resolve(this.page, relativeSelector, relativeTarget);
1785
- if (!element) {
1786
- return { success: false, error: `Could not resolve relative target ${relativeTarget} for hover.` };
1787
- }
1788
- await element.hover({ timeout: this.timeoutMs });
1789
- return { success: true };
1790
- }
1791
- const selector = this.selectors[target];
1792
- if (!selector) {
1793
- return { success: false, error: `Selector for hover target ${target} not found.` };
1794
- }
1795
- await this.page.locator(selector).first().hover({ timeout: this.timeoutMs });
1796
- return { success: true };
1797
- } catch (error) {
1798
- if (this.isBrowserClosedError(error)) {
1799
- return {
1800
- success: false,
1801
- error: `CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`,
1802
- shouldBreak: true
1803
- };
1804
- }
1805
- return {
1806
- success: false,
1807
- error: `Failed to hover ${target}: ${error instanceof Error ? error.message : String(error)}`
1808
- };
1809
- }
1810
- }
1811
- };
1812
- }
1813
- });
1814
-
1815
- // src/utils/test/src/AssertionRunner.ts
1816
- var AssertionRunner;
1817
- var init_AssertionRunner = __esm({
1818
- "src/utils/test/src/AssertionRunner.ts"() {
1819
- "use strict";
1820
- init_test();
1821
- init_RelativeTargetResolver();
1822
- AssertionRunner = class {
1823
- constructor(page, selectors, timeoutMs = 400) {
1824
- this.page = page;
1825
- this.selectors = selectors;
1826
- this.timeoutMs = timeoutMs;
1827
- }
1828
- /**
1829
- * Resolve the target element for an assertion
1830
- */
1831
- async resolveTarget(targetName, relativeTarget, selectorKey) {
1832
- try {
1833
- if (targetName === "relative") {
1834
- const relativeSelector = selectorKey ? this.selectors[selectorKey] : this.selectors.relative;
1835
- if (!relativeSelector) {
1836
- return { target: null, error: "Relative selector is not defined in the contract." };
1837
- }
1838
- if (!relativeTarget) {
1839
- return { target: null, error: "Relative target or expected value is not defined." };
1840
- }
1841
- const target = await RelativeTargetResolver.resolve(this.page, relativeSelector, relativeTarget);
1842
- if (!target) {
1843
- return { target: null, error: `Target ${targetName} not found.` };
1844
- }
1845
- return { target };
1846
- }
1847
- const selector = this.selectors[targetName];
1848
- if (!selector) {
1849
- return { target: null, error: `Selector for assertion target ${targetName} not found.` };
1850
- }
1851
- return { target: this.page.locator(selector).first() };
1852
- } catch (error) {
1853
- return {
1854
- target: null,
1855
- error: `Failed to resolve target ${targetName}: ${error instanceof Error ? error.message : String(error)}`
1856
- };
1857
- }
1858
- }
1859
- /**
1860
- * Validate visibility assertion
1861
- */
1862
- async validateVisibility(target, targetName, expectedVisible, failureMessage, testDescription) {
1863
- try {
1864
- if (expectedVisible) {
1865
- await (0, test_exports.expect)(target).toBeVisible({ timeout: this.timeoutMs });
1866
- return {
1867
- success: true,
1868
- passMessage: `${targetName} is visible as expected. Test: "${testDescription}".`
1869
- };
1870
- } else {
1871
- await (0, test_exports.expect)(target).toBeHidden({ timeout: this.timeoutMs });
1872
- return {
1873
- success: true,
1874
- passMessage: `${targetName} is not visible as expected. Test: "${testDescription}".`
1875
- };
1876
- }
1877
- } catch {
1878
- const selector = this.selectors[targetName] || "";
1879
- const debugState = await this.page.evaluate((sel) => {
1880
- const el = sel ? document.querySelector(sel) : null;
1881
- if (!el) return "element not found";
1882
- const styles = window.getComputedStyle(el);
1883
- return `display:${styles.display}, visibility:${styles.visibility}, opacity:${styles.opacity}`;
1884
- }, selector);
1885
- if (expectedVisible) {
1886
- return {
1887
- success: false,
1888
- failMessage: `${failureMessage} (actual: ${debugState})`
1889
- };
1890
- } else {
1891
- return {
1892
- success: false,
1893
- failMessage: `${failureMessage} ${targetName} is still visible (actual: ${debugState}).`
1894
- };
1895
- }
1896
- }
1897
- }
1898
- /**
1899
- * Validate attribute assertion
1900
- */
1901
- async validateAttribute(target, targetName, attribute, expectedValue, failureMessage, testDescription) {
1902
- if (expectedValue === "!empty") {
1903
- const attributeValue2 = await target.getAttribute(attribute);
1904
- if (attributeValue2 && attributeValue2.trim() !== "") {
1905
- return {
1906
- success: true,
1907
- passMessage: `${targetName} has non-empty "${attribute}". Test: "${testDescription}".`
1908
- };
1909
- } else {
1910
- return {
1911
- success: false,
1912
- failMessage: `${failureMessage} ${targetName} "${attribute}" should not be empty, found "${attributeValue2}".`
1913
- };
1914
- }
1915
- }
1916
- if (typeof expectedValue !== "string") {
1917
- console.error("[AssertionRunner] expectedValue is not a string:", expectedValue);
1918
- throw new Error(`AssertionRunner: expectedValue for attribute assertion must be a string, but got: ${JSON.stringify(expectedValue)}`);
1919
- }
1920
- const expectedValues = expectedValue.split(" | ").map((v) => v.trim());
1921
- const attributeValue = await target.getAttribute(attribute);
1922
- if (attributeValue !== null && expectedValues.includes(attributeValue)) {
1923
- return {
1924
- success: true,
1925
- passMessage: `${targetName} has expected "${attribute}". Test: "${testDescription}".`
1926
- };
1927
- } else {
1928
- return {
1929
- success: false,
1930
- failMessage: `${failureMessage} ${targetName} "${attribute}" should be "${expectedValue}", found "${attributeValue}".`
1931
- };
1932
- }
1933
- }
1934
- /**
1935
- * Validate input value assertion
1936
- */
1937
- async validateValue(target, targetName, expectedValue, failureMessage, testDescription) {
1938
- const inputValue = await target.inputValue().catch(() => "");
1939
- if (expectedValue === "!empty") {
1940
- if (inputValue && inputValue.trim() !== "") {
1941
- return {
1942
- success: true,
1943
- passMessage: `${targetName} has non-empty value. Test: "${testDescription}".`
1944
- };
1945
- } else {
1946
- return {
1947
- success: false,
1948
- failMessage: `${failureMessage} ${targetName} value should not be empty, found "${inputValue}".`
1949
- };
1950
- }
1951
- }
1952
- if (expectedValue === "") {
1953
- if (inputValue === "") {
1954
- return {
1955
- success: true,
1956
- passMessage: `${targetName} has empty value. Test: "${testDescription}".`
1957
- };
1958
- } else {
1959
- return {
1960
- success: false,
1961
- failMessage: `${failureMessage} ${targetName} value should be empty, found "${inputValue}".`
1962
- };
1963
- }
1964
- }
1965
- if (inputValue === expectedValue) {
1966
- return {
1967
- success: true,
1968
- passMessage: `${targetName} has expected value. Test: "${testDescription}".`
1969
- };
1970
- } else {
1971
- return {
1972
- success: false,
1973
- failMessage: `${failureMessage} ${targetName} value should be "${expectedValue}", found "${inputValue}".`
1974
- };
1975
- }
1976
- }
1977
- /**
1978
- * Validate focus assertion
1979
- */
1980
- async validateFocus(target, targetName, expectedFocus, failureMessage, testDescription) {
1981
- try {
1982
- if (expectedFocus) {
1983
- await (0, test_exports.expect)(target).toBeFocused({ timeout: this.timeoutMs });
1984
- return {
1985
- success: true,
1986
- passMessage: `${targetName} has focus as expected. Test: "${testDescription}".`
1987
- };
1988
- } else {
1989
- await (0, test_exports.expect)(target).not.toBeFocused({ timeout: this.timeoutMs });
1990
- return {
1991
- success: true,
1992
- passMessage: `${targetName} does not have focus as expected. Test: "${testDescription}".`
1993
- };
1994
- }
1995
- } catch {
1996
- const actualFocus = await this.page.evaluate(() => {
1997
- const focused = document.activeElement;
1998
- return focused ? `${focused.tagName}#${focused.id || "no-id"}.${focused.className || "no-class"}` : "no element focused";
1999
- });
2000
- return {
2001
- success: false,
2002
- failMessage: `${failureMessage} (actual focus: ${actualFocus})`
2003
- };
2004
- }
2005
- }
2006
- /**
2007
- * Validate role assertion
2008
- */
2009
- async validateRole(target, targetName, expectedRole, failureMessage, testDescription) {
2010
- const roleValue = await target.getAttribute("role");
2011
- if (roleValue === expectedRole) {
2012
- return {
2013
- success: true,
2014
- passMessage: `${targetName} has role "${expectedRole}". Test: "${testDescription}".`
2015
- };
2016
- } else {
2017
- return {
2018
- success: false,
2019
- failMessage: `${failureMessage} Expected role "${expectedRole}", found "${roleValue}".`
2020
- };
2021
- }
2022
- }
2023
- /**
2024
- * Main validation method - routes to specific validators
2025
- */
2026
- async validate(assertion, testDescription) {
2027
- if (this.page.isClosed()) {
2028
- return {
2029
- success: false,
2030
- failMessage: `CRITICAL: Browser/page closed before completing all tests. Increase test timeout or reduce test complexity.`
2031
- };
2032
- }
2033
- const { target, error } = await this.resolveTarget(
2034
- assertion.target,
2035
- assertion.relativeTarget || assertion.expectedValue,
2036
- assertion.selectorKey
2037
- );
2038
- if (error || !target) {
2039
- return { success: false, failMessage: error || `Target ${assertion.target} not found.`, target: null };
2040
- }
2041
- if (assertion.target === "input" && assertion.attribute === "aria-activedescendant" && assertion.expectedValue === "!empty" && assertion.relativeTarget && assertion.selectorKey) {
2042
- const optionLocator = await RelativeTargetResolver.resolve(this.page, this.selectors[assertion.selectorKey], assertion.relativeTarget);
2043
- const optionId = optionLocator ? await optionLocator.getAttribute("id") : null;
2044
- const inputId = await target.getAttribute("aria-activedescendant");
2045
- if (optionId && inputId === optionId) {
2046
- return {
2047
- success: true,
2048
- passMessage: `input[aria-activedescendant] matches id of ${assertion.relativeTarget}(${assertion.selectorKey}). Test: "${testDescription}".`
2049
- };
2050
- } else {
2051
- return {
2052
- success: false,
2053
- failMessage: `input[aria-activedescendant] should match id of ${assertion.relativeTarget}(${assertion.selectorKey}), found "${inputId}".`
2054
- };
2055
- }
2056
- }
2057
- switch (assertion.assertion) {
2058
- case "toBeVisible":
2059
- return this.validateVisibility(target, assertion.target, true, assertion.failureMessage || "", testDescription);
2060
- case "notToBeVisible":
2061
- return this.validateVisibility(target, assertion.target, false, assertion.failureMessage || "", testDescription);
2062
- case "toHaveAttribute":
2063
- if (assertion.attribute && assertion.expectedValue !== void 0) {
2064
- return this.validateAttribute(
2065
- target,
2066
- assertion.target,
2067
- assertion.attribute,
2068
- assertion.expectedValue,
2069
- assertion.failureMessage || "",
2070
- testDescription
2071
- );
2072
- }
2073
- return { success: false, failMessage: "Missing attribute or expectedValue for toHaveAttribute assertion" };
2074
- case "toHaveValue":
2075
- if (assertion.expectedValue !== void 0) {
2076
- return this.validateValue(target, assertion.target, assertion.expectedValue, assertion.failureMessage || "", testDescription);
2077
- }
2078
- return { success: false, failMessage: "Missing expectedValue for toHaveValue assertion" };
2079
- case "toHaveFocus":
2080
- return this.validateFocus(target, assertion.target, true, assertion.failureMessage || "", testDescription);
2081
- case "notToHaveFocus":
2082
- return this.validateFocus(target, assertion.target, false, assertion.failureMessage || "", testDescription);
2083
- case "toHaveRole":
2084
- if (assertion.expectedValue !== void 0) {
2085
- return this.validateRole(target, assertion.target, assertion.expectedValue, assertion.failureMessage || "", testDescription);
2086
- }
2087
- return { success: false, failMessage: "Missing expectedValue for toHaveRole assertion" };
2088
- default:
2089
- return { success: false, failMessage: `Unknown assertion type: ${assertion.assertion}` };
2090
- }
2091
- }
2092
- };
2093
- }
2094
- });
2095
-
2096
- // src/utils/test/src/contractTestRunnerPlaywright.ts
2097
- var contractTestRunnerPlaywright_exports = {};
2098
- __export(contractTestRunnerPlaywright_exports, {
2099
- runContractTestsPlaywright: () => runContractTestsPlaywright
2100
- });
2101
- async function runContractTestsPlaywright(componentName, url, strictness, config, configBaseDir) {
2102
- const componentConfig = config?.test?.components?.find((c) => c.name === componentName);
2103
- const isCustomContract = !!componentConfig?.contractPath;
2104
- const reporter = new ContractReporter(true, isCustomContract);
2105
- const defaultTimeouts = {
2106
- actionTimeoutMs: 400,
2107
- assertionTimeoutMs: 400,
2108
- navigationTimeoutMs: 3e4,
2109
- componentReadyTimeoutMs: 5e3
2110
- };
2111
- const globalDisableTimeouts = config?.test?.disableTimeouts === true;
2112
- const componentDisableTimeouts = componentConfig?.disableTimeouts === true;
2113
- const disableTimeouts = componentDisableTimeouts || globalDisableTimeouts;
2114
- const resolveTimeout = (componentValue, globalValue, fallback) => {
2115
- if (disableTimeouts) return 0;
2116
- const value = componentValue ?? globalValue;
2117
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
2118
- return fallback;
2119
- }
2120
- return value;
2121
- };
2122
- const actionTimeoutMs = resolveTimeout(
2123
- componentConfig?.actionTimeoutMs,
2124
- config?.test?.actionTimeoutMs,
2125
- defaultTimeouts.actionTimeoutMs
2126
- );
2127
- const assertionTimeoutMs = resolveTimeout(
2128
- componentConfig?.assertionTimeoutMs,
2129
- config?.test?.assertionTimeoutMs,
2130
- defaultTimeouts.assertionTimeoutMs
2131
- );
2132
- const navigationTimeoutMs = resolveTimeout(
2133
- componentConfig?.navigationTimeoutMs,
2134
- config?.test?.navigationTimeoutMs,
2135
- defaultTimeouts.navigationTimeoutMs
2136
- );
2137
- const componentReadyTimeoutMs = resolveTimeout(
2138
- componentConfig?.componentReadyTimeoutMs,
2139
- config?.test?.componentReadyTimeoutMs,
2140
- defaultTimeouts.componentReadyTimeoutMs
2141
- );
2142
- const strictnessMode = normalizeStrictness(strictness);
2143
- const contractPath = componentConfig?.contractPath;
2144
- if (!contractPath) {
2145
- throw new Error(`Contract path not found for component: ${componentName}`);
2146
- }
2147
- const resolvedPath = (() => {
2148
- if (import_path5.default.isAbsolute(contractPath)) return contractPath;
2149
- if (configBaseDir) {
2150
- const configResolved = import_path5.default.resolve(configBaseDir, contractPath);
2151
- try {
2152
- (0, import_fs2.readFileSync)(configResolved, "utf-8");
2153
- return configResolved;
2154
- } catch {
2155
- }
2156
- }
2157
- const cwdResolved = import_path5.default.resolve(process.cwd(), contractPath);
2158
- try {
2159
- (0, import_fs2.readFileSync)(cwdResolved, "utf-8");
2160
- return cwdResolved;
2161
- } catch {
2162
- return new URL(contractPath, import_meta2.url).pathname;
2163
- }
2164
- })();
2165
- const contractData = (0, import_fs2.readFileSync)(resolvedPath, "utf-8");
2166
- const componentContract = JSON.parse(contractData);
2167
- const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
2168
- const apgUrl = componentContract.meta?.source?.apg;
2169
- const failures = [];
2170
- const warnings = [];
2171
- const passes = [];
2172
- const skipped = [];
2173
- let page = null;
2174
- const classifyFailure = (message, levelRaw) => {
2175
- const level = normalizeLevel(levelRaw);
2176
- const enforcement = resolveEnforcement(level, strictnessMode);
2177
- if (enforcement === "error") {
2178
- failures.push(message);
2179
- return { status: "fail", level, detail: message };
2180
- }
2181
- if (enforcement === "warning") {
2182
- warnings.push(message);
2183
- return { status: "warn", level, detail: message };
2184
- }
2185
- const ignoredMessage = `${message} (ignored by strictness=${strictnessMode}, level=${level})`;
2186
- skipped.push(ignoredMessage);
2187
- return { status: "skip", level, detail: ignoredMessage };
2188
- };
2189
- try {
2190
- page = await createTestPage();
2191
- if (url) {
2192
- try {
2193
- await page.goto(url, {
2194
- waitUntil: "domcontentloaded",
2195
- timeout: navigationTimeoutMs
2196
- });
2197
- } catch (error) {
2198
- throw new Error(
2199
- `Failed to navigate to ${url}. Ensure dev server is running and accessible. Original error: ${error instanceof Error ? error.message : String(error)}`
2200
- );
2201
- }
2202
- await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
2203
- }
2204
- const strategy = await ComponentDetector.detect(componentName, componentConfig, actionTimeoutMs, assertionTimeoutMs, configBaseDir);
2205
- if (!strategy) {
2206
- throw new Error(`Unsupported component: ${componentName}`);
2207
- }
2208
- const mainSelector = strategy.getMainSelector();
2209
- if (!mainSelector) {
2210
- throw new Error(`CRITICAL: No selector found in contract for ${componentName}`);
2211
- }
2212
- try {
2213
- await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: componentReadyTimeoutMs });
2214
- } catch (error) {
2215
- throw new Error(
2216
- `
114
+ This indicates a problem with the combobox component's close functionality.`);this.selectors.input&&await e.locator(this.selectors.input).first().clear()}async shouldSkipTest(){return!1}getMainSelector(){return this.mainSelector}}});var jt={};K(jt,{TabsComponentStrategy:()=>ot});var ot,It=L(()=>{"use strict";ot=class{constructor(e,t){this.mainSelector=e;this.selectors=t}async resetState(){}async shouldSkipTest(e,t){if(e.isVertical!==void 0&&this.selectors.tablist){let r=this.selectors.tablist,o=await t.locator(r).first().getAttribute("aria-orientation")==="vertical";if(e.isVertical!==o)return!0}return!1}getMainSelector(){return this.mainSelector}}});var nt,Ft,Re,Vt=L(()=>{"use strict";nt=j(require("path"),1),Ft=require("url"),Re=class{builtInStrategies=new Map;constructor(){this.registerBuiltInStrategies()}registerBuiltInStrategies(){this.builtInStrategies.set("menu",()=>Promise.resolve().then(()=>(xt(),Et)).then(e=>e.MenuComponentStrategy)),this.builtInStrategies.set("accordion",()=>Promise.resolve().then(()=>(Pt(),Mt)).then(e=>e.AccordionComponentStrategy)),this.builtInStrategies.set("combobox",()=>Promise.resolve().then(()=>(Lt(),Bt)).then(e=>e.ComboboxComponentStrategy)),this.builtInStrategies.set("tabs",()=>Promise.resolve().then(()=>(It(),jt)).then(e=>e.TabsComponentStrategy))}async loadStrategy(e,t,r){try{if(t)try{let i=nt.default.isAbsolute(t)?t:nt.default.resolve(r||process.cwd(),t),o=await import((0,Ft.pathToFileURL)(i).href),a=o.default||o;if(!a)throw new Error(`No default export found in ${t}`);return a}catch(i){throw new Error(`Failed to load custom strategy from ${t}: ${i instanceof Error?i.message:String(i)}`)}let s=this.builtInStrategies.get(e);return s?s():null}catch(s){throw new Error(`Strategy loading failed for ${e}: ${s instanceof Error?s.message:String(s)}`)}}has(e,t){return!!t||this.builtInStrategies.has(e)}}});var Ee,xe,Ss,Me,Ut=L(()=>{"use strict";Ee=require("fs"),xe=j(require("path"),1);Vt();Ss={},Me=class{static strategyRegistry=new Re;static isComponentConfig(e){return typeof e=="object"&&e!==null}static async detect(e,t,r=400,s=400,i){let o=this.isComponentConfig(t)?t:void 0,a=o?.contractPath;if(!a)throw new Error(`Contract path not found for component: ${e}`);let l=(()=>{if(xe.default.isAbsolute(a))return a;if(i){let M=xe.default.resolve(i,a);try{return(0,Ee.readFileSync)(M,"utf-8"),M}catch{}}let q=xe.default.resolve(process.cwd(),a);try{return(0,Ee.readFileSync)(q,"utf-8"),q}catch{return new URL(a,Ss.url).pathname}})(),p=(0,Ee.readFileSync)(l,"utf-8"),m=JSON.parse(p).selectors,h=await this.strategyRegistry.loadStrategy(e,o?.strategyPath,i);if(!h)return null;let P=m.main;return e==="tabs"?new h(P,m):new h(P,m,r,s)}}});var Pe,Dt=L(()=>{"use strict";Pe=class{startTime=0;componentName="";staticPasses=0;staticFailures=0;staticWarnings=0;dynamicResults=[];totalTests=0;skipped=0;warnings=0;isPlaywright=!1;isCustomContract=!1;apgUrl="https://www.w3.org/WAI/ARIA/apg/";hasPrintedStaticSection=!1;hasPrintedDynamicSection=!1;constructor(e=!1,t=!1){this.isPlaywright=e,this.isCustomContract=t}log(e){process.stderr.write(e+`
115
+ `)}start(e,t,r){this.startTime=Date.now(),this.componentName=e,this.totalTests=t,this.hasPrintedStaticSection=!1,this.hasPrintedDynamicSection=!1,r&&(this.apgUrl=r);let s="Playwright (Real Browser)";this.log(`
116
+ ${"\u2550".repeat(60)}`),this.log(`\u{1F50D} Testing ${e.charAt(0).toUpperCase()+e.slice(1)} Component - ${s}`),this.log(`${"\u2550".repeat(60)}
117
+ `)}reportStatic(e,t,r=0){this.staticPasses=e,this.staticFailures=t,this.staticWarnings=r}reportStaticTest(e,t,r,s){this.hasPrintedStaticSection||(this.log(`${"\u2500".repeat(60)}`),this.log("\u{1F9EA} Static Assertions"),this.log(`${"\u2500".repeat(60)}`),this.hasPrintedStaticSection=!0);let i=t==="pass"?"\u2713":t==="warn"?"\u26A0":t==="skip"?"\u25CB":"\u2717";this.log(` ${i} ${e}`),s&&this.log(` \u21B3 level=${s}`),(t==="fail"||t==="warn"||t==="skip")&&r&&this.log(` \u21B3 ${r}`)}reportTest(e,t,r){this.hasPrintedDynamicSection||(this.log(""),this.log(`${"\u2500".repeat(60)}`),this.log("\u2328\uFE0F Dynamic Interaction Tests"),this.log(`${"\u2500".repeat(60)}`),this.hasPrintedDynamicSection=!0);let s={description:e.description,status:t,failureMessage:r,level:e.level};t==="skip"&&(s.skipReason="Requires real browser (addEventListener events)"),this.dynamicResults.push(s);let i={pass:"\u2713",fail:"\u2717",warn:"\u26A0",skip:"\u25CB"},o=e.level?`[${e.level.toUpperCase()}] `:"";this.log(` ${i[t]} ${o}${e.description}`),t==="skip"&&!this.isPlaywright&&this.log(" \u21B3 Skipped (runs only in Playwright)"),t==="fail"&&r&&this.log(` \u21B3 ${r}`),t==="warn"&&r&&this.log(` \u21B3 ${r}`),t==="skip"&&r&&this.log(` \u21B3 ${r}`)}reportFailures(e){e.length!==0&&(this.log(`
118
+ ${"\u2500".repeat(60)}`),this.log(`\u274C Failures (${e.length}):
119
+ `),e.forEach((t,r)=>{this.log(`${r+1}. ${t}`),t.includes("aria-")?this.log(" \u{1F4A1} Add the missing ARIA attribute to improve screen reader support"):t.includes("focus")?this.log(" \u{1F4A1} Check keyboard event handlers and focus management"):t.includes("visible")&&this.log(" \u{1F4A1} Verify display/visibility styles and state management"),this.log("")}))}reportWarnings(){let e=this.dynamicResults.filter(t=>t.status==="warn");e.length===0&&this.staticWarnings===0||(this.log(`
120
+ ${"\u2500".repeat(60)}`),this.log(`\u26A0\uFE0F Warnings (${this.staticWarnings+e.length}):
121
+ `),this.log(`These checks are failing but treated as warnings under the active strictness mode.
122
+ `),e.forEach((t,r)=>{this.log(`${r+1}. ${t.description}`),t.failureMessage&&this.log(` \u21B3 ${t.failureMessage}`),t.level&&this.log(` \u21B3 level=${t.level}`)}),this.apgUrl&&this.log(`
123
+ Reference: ${this.apgUrl}
124
+ `))}reportSkipped(){if(this.skipped===0||this.isPlaywright)return;let e=this.dynamicResults.filter(t=>t.status==="skip");this.log(`
125
+ ${"\u2500".repeat(60)}`),this.log(`\u2139\uFE0F Skipped Tests (${this.skipped}):
126
+ `),this.log("These tests use native keyboard events via addEventListener,"),this.log(`which only run in Playwright (real browser).
127
+ `),e.forEach((t,r)=>{this.log(`${r+1}. ${t.description}`)}),this.log(`
128
+ \u{1F4A1} Run with Playwright for full validation:`),this.log(` testUiComponent('${this.componentName}', null, 'http://localhost:5173/test-harness?component=component_name')
129
+ `)}summary(e){let t=Date.now()-this.startTime,r=this.dynamicResults.filter(f=>f.status==="pass").length,s=this.dynamicResults.filter(f=>f.status==="fail").length,i=this.dynamicResults.filter(f=>f.status==="warn").length;this.skipped=this.dynamicResults.filter(f=>f.status==="skip").length,this.warnings=this.staticWarnings+i;let o=this.staticPasses+r,a=this.staticFailures+s,l=o+a+this.warnings,p=()=>{let f=`${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)}`;return this.isCustomContract?`${f} component validates against your custom accessibility policy \u2713`:`${f} component meets Aria-Ease baseline WAI-ARIA expectations \u2713`};return e.length>0&&this.reportFailures(e),this.reportWarnings(),this.reportSkipped(),this.log(`
130
+ ${"\u2550".repeat(60)}`),this.log(`\u{1F4CA} Summary
131
+ `),a===0&&this.skipped===0&&this.warnings===0?(this.log(`\u2705 All ${l} tests passed!`),this.log(` ${p()}`)):a===0?(this.log(`\u2705 ${o}/${l} tests passed`),this.skipped>0&&this.log(`\u25CB ${this.skipped} tests skipped`),this.warnings>0&&this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings>1?"s":""}`),this.log(` ${p()}`)):(this.log(`\u274C ${a} test${a>1?"s":""} failed`),this.log(`\u2705 ${o} test${o>1?"s":""} passed`),this.warnings>0&&this.log(`\u26A0\uFE0F ${this.warnings} warning${this.warnings>1?"s":""}`),this.skipped>0&&this.log(`\u25CB ${this.skipped} test${this.skipped>1?"s":""} skipped`)),this.log(`\u23F1\uFE0F Duration: ${t}ms`),this.log(`${"\u2550".repeat(60)}
132
+ `),a>0?(this.log("\u{1F527} Next Steps:"),this.log(" 1. Review the failures above"),this.log(" 2. Fix ARIA attributes and keyboard handlers"),this.log(` 3. Re-run tests to verify fixes
133
+ `)):!this.isPlaywright&&this.skipped>0&&this.log(`\u2728 Optional: Run Playwright tests for complete validation
134
+ `),{passes:o,failures:a,skipped:this.skipped,duration:t}}error(e,t){this.log(`
135
+ \u274C Error: ${e}`),t&&this.log(` Context: ${t}`),this.log("")}}});var Ot={};K(Ot,{RelativeTargetResolver:()=>oe});var oe,Be=L(()=>{"use strict";oe=class{static async resolve(e,t,r){let s=await e.locator(t).all();switch(r){case"first":return s[0];case"second":return s[1];case"last":return s[s.length-1];case"next":{let o=(await e.evaluate(([a])=>Array.from(document.querySelectorAll(a)).indexOf(document.activeElement),[t])+1)%s.length;return s[o]}case"previous":{let o=(await e.evaluate(([a])=>Array.from(document.querySelectorAll(a)).indexOf(document.activeElement),[t])-1+s.length)%s.length;return s[o]}default:return null}}}});var ye,qt=L(()=>{"use strict";Be();ye=class{constructor(e,t,r=400){this.page=e;this.selectors=t;this.timeoutMs=r}isBrowserClosedError(e){return e instanceof Error&&e.message.includes("Target page, context or browser has been closed")}async focus(e,t,r){try{if(e==="virtual"&&r){let i=this.selectors.main;if(!i)return{success:!1,error:"Main selector not defined for virtual focus."};let o=this.page.locator(i).first();return await o.count()?(await o.evaluate((l,p)=>{l.setAttribute("aria-activedescendant",p)},r),{success:!0}):{success:!1,error:"Main element not found for virtual focus."}}if(e==="relative"&&t){let i=this.selectors.relative;if(!i)return{success:!1,error:"Relative selector not defined for focus action."};let o=await oe.resolve(this.page,i,t);return o?(await o.focus({timeout:this.timeoutMs}),{success:!0}):{success:!1,error:`Could not resolve relative target ${t} for focus.`}}let s=this.selectors[e];return s?(await this.page.locator(s).first().focus({timeout:this.timeoutMs}),{success:!0}):{success:!1,error:`Selector for focus target ${e} not found.`}}catch(s){return this.isBrowserClosedError(s)?{success:!1,error:"CRITICAL: Browser/page closed during test execution. Remaining actions skipped.",shouldBreak:!0}:{success:!1,error:`Failed to focus ${e}: ${s instanceof Error?s.message:String(s)}`}}}async type(e,t){try{let r=this.selectors[e];return r?(await this.page.locator(r).first().fill(t,{timeout:this.timeoutMs}),{success:!0}):{success:!1,error:`Selector for type target ${e} not found.`}}catch(r){return this.isBrowserClosedError(r)?{success:!1,error:"CRITICAL: Browser/page closed during test execution. Remaining actions skipped.",shouldBreak:!0}:{success:!1,error:`Failed to type into ${e}: ${r instanceof Error?r.message:String(r)}`}}}async click(e,t){try{if(e==="document")return await this.page.mouse.click(10,10),{success:!0};if(e==="relative"&&t){let s=this.selectors.relative;if(!s)return{success:!1,error:"Relative selector not defined for click action."};let i=await oe.resolve(this.page,s,t);return i?(await i.click({timeout:this.timeoutMs}),{success:!0}):{success:!1,error:`Could not resolve relative target ${t} for click.`}}let r=this.selectors[e];return r?(await this.page.locator(r).first().click({timeout:this.timeoutMs}),{success:!0}):{success:!1,error:`Selector for action target ${e} not found.`}}catch(r){return this.isBrowserClosedError(r)?{success:!1,error:"CRITICAL: Browser/page closed during test execution. Remaining actions skipped.",shouldBreak:!0}:{success:!1,error:`Failed to click ${e}: ${r instanceof Error?r.message:String(r)}`}}}async keypress(e,t){try{let s={Space:"Space",Enter:"Enter",Escape:"Escape","Arrow Up":"ArrowUp","Arrow Down":"ArrowDown","Arrow Left":"ArrowLeft","Arrow Right":"ArrowRight",Home:"Home",End:"End",Tab:"Tab"}[t]||t;if(s==="Space"?s=" ":s.includes(" ")&&(s=s.replace(/ /g,"")),e==="focusable"&&["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Escape","Home","End","Tab","Shift+Tab"].includes(s))return await this.page.keyboard.press(s),{success:!0};let i=this.selectors[e];if(!i)return{success:!1,error:`Selector for keypress target ${e} not found.`};let o=this.page.locator(i).first();return await o.count()===0?{success:!1,error:`${e} element not found.`,shouldBreak:!0}:(await o.press(s,{timeout:this.timeoutMs}),{success:!0})}catch(r){return this.isBrowserClosedError(r)?{success:!1,error:"CRITICAL: Browser/page closed during test execution. Remaining actions skipped.",shouldBreak:!0}:{success:!1,error:`Failed to press ${t} on ${e}: ${r instanceof Error?r.message:String(r)}`}}}async hover(e,t){try{if(e==="relative"&&t){let s=this.selectors.relative;if(!s)return{success:!1,error:"Relative selector not defined for hover action."};let i=await oe.resolve(this.page,s,t);return i?(await i.hover({timeout:this.timeoutMs}),{success:!0}):{success:!1,error:`Could not resolve relative target ${t} for hover.`}}let r=this.selectors[e];return r?(await this.page.locator(r).first().hover({timeout:this.timeoutMs}),{success:!0}):{success:!1,error:`Selector for hover target ${e} not found.`}}catch(r){return this.isBrowserClosedError(r)?{success:!1,error:"CRITICAL: Browser/page closed during test execution. Remaining actions skipped.",shouldBreak:!0}:{success:!1,error:`Failed to hover ${e}: ${r instanceof Error?r.message:String(r)}`}}}}});var $e,Ht=L(()=>{"use strict";ve();Be();$e=class{constructor(e,t,r=400){this.page=e;this.selectors=t;this.timeoutMs=r}async resolveTarget(e,t,r){try{if(e==="relative"){let i=r?this.selectors[r]:this.selectors.relative;if(!i)return{target:null,error:"Relative selector is not defined in the contract."};if(!t)return{target:null,error:"Relative target or expected value is not defined."};let o=await oe.resolve(this.page,i,t);return o?{target:o}:{target:null,error:`Target ${e} not found.`}}let s=this.selectors[e];return s?{target:this.page.locator(s).first()}:{target:null,error:`Selector for assertion target ${e} not found.`}}catch(s){return{target:null,error:`Failed to resolve target ${e}: ${s instanceof Error?s.message:String(s)}`}}}async validateVisibility(e,t,r,s,i){try{return r?(await(0,I.expect)(e).toBeVisible({timeout:this.timeoutMs}),{success:!0,passMessage:`${t} is visible as expected. Test: "${i}".`}):(await(0,I.expect)(e).toBeHidden({timeout:this.timeoutMs}),{success:!0,passMessage:`${t} is not visible as expected. Test: "${i}".`})}catch{let o=this.selectors[t]||"",a=await this.page.evaluate(l=>{let p=l?document.querySelector(l):null;if(!p)return"element not found";let f=window.getComputedStyle(p);return`display:${f.display}, visibility:${f.visibility}, opacity:${f.opacity}`},o);return r?{success:!1,failMessage:`${s} (actual: ${a})`}:{success:!1,failMessage:`${s} ${t} is still visible (actual: ${a}).`}}}async validateAttribute(e,t,r,s,i,o){if(s==="!empty"){let p=await e.getAttribute(r);return p&&p.trim()!==""?{success:!0,passMessage:`${t} has non-empty "${r}". Test: "${o}".`}:{success:!1,failMessage:`${i} ${t} "${r}" should not be empty, found "${p}".`}}if(typeof s!="string")throw console.error("[AssertionRunner] expectedValue is not a string:",s),new Error(`AssertionRunner: expectedValue for attribute assertion must be a string, but got: ${JSON.stringify(s)}`);let a=s.split(" | ").map(p=>p.trim()),l=await e.getAttribute(r);return l!==null&&a.includes(l)?{success:!0,passMessage:`${t} has expected "${r}". Test: "${o}".`}:{success:!1,failMessage:`${i} ${t} "${r}" should be "${s}", found "${l}".`}}async validateValue(e,t,r,s,i){let o=await e.inputValue().catch(()=>"");return r==="!empty"?o&&o.trim()!==""?{success:!0,passMessage:`${t} has non-empty value. Test: "${i}".`}:{success:!1,failMessage:`${s} ${t} value should not be empty, found "${o}".`}:r===""?o===""?{success:!0,passMessage:`${t} has empty value. Test: "${i}".`}:{success:!1,failMessage:`${s} ${t} value should be empty, found "${o}".`}:o===r?{success:!0,passMessage:`${t} has expected value. Test: "${i}".`}:{success:!1,failMessage:`${s} ${t} value should be "${r}", found "${o}".`}}async validateFocus(e,t,r,s,i){try{return r?(await(0,I.expect)(e).toBeFocused({timeout:this.timeoutMs}),{success:!0,passMessage:`${t} has focus as expected. Test: "${i}".`}):(await(0,I.expect)(e).not.toBeFocused({timeout:this.timeoutMs}),{success:!0,passMessage:`${t} does not have focus as expected. Test: "${i}".`})}catch{let o=await this.page.evaluate(()=>{let a=document.activeElement;return a?`${a.tagName}#${a.id||"no-id"}.${a.className||"no-class"}`:"no element focused"});return{success:!1,failMessage:`${s} (actual focus: ${o})`}}}async validateRole(e,t,r,s,i){let o=await e.getAttribute("role");return o===r?{success:!0,passMessage:`${t} has role "${r}". Test: "${i}".`}:{success:!1,failMessage:`${s} Expected role "${r}", found "${o}".`}}async validate(e,t){if(this.page.isClosed())return{success:!1,failMessage:"CRITICAL: Browser/page closed before completing all tests. Increase test timeout or reduce test complexity."};let{target:r,error:s}=await this.resolveTarget(e.target,e.relativeTarget||e.expectedValue,e.selectorKey);if(s||!r)return{success:!1,failMessage:s||`Target ${e.target} not found.`,target:null};if(e.target==="input"&&e.attribute==="aria-activedescendant"&&e.expectedValue==="!empty"&&e.relativeTarget&&e.selectorKey){let i=await oe.resolve(this.page,this.selectors[e.selectorKey],e.relativeTarget),o=i?await i.getAttribute("id"):null,a=await r.getAttribute("aria-activedescendant");return o&&a===o?{success:!0,passMessage:`input[aria-activedescendant] matches id of ${e.relativeTarget}(${e.selectorKey}). Test: "${t}".`}:{success:!1,failMessage:`input[aria-activedescendant] should match id of ${e.relativeTarget}(${e.selectorKey}), found "${a}".`}}switch(e.assertion){case"toBeVisible":return this.validateVisibility(r,e.target,!0,e.failureMessage||"",t);case"notToBeVisible":return this.validateVisibility(r,e.target,!1,e.failureMessage||"",t);case"toHaveAttribute":return e.attribute&&e.expectedValue!==void 0?this.validateAttribute(r,e.target,e.attribute,e.expectedValue,e.failureMessage||"",t):{success:!1,failMessage:"Missing attribute or expectedValue for toHaveAttribute assertion"};case"toHaveValue":return e.expectedValue!==void 0?this.validateValue(r,e.target,e.expectedValue,e.failureMessage||"",t):{success:!1,failMessage:"Missing expectedValue for toHaveValue assertion"};case"toHaveFocus":return this.validateFocus(r,e.target,!0,e.failureMessage||"",t);case"notToHaveFocus":return this.validateFocus(r,e.target,!1,e.failureMessage||"",t);case"toHaveRole":return e.expectedValue!==void 0?this.validateRole(r,e.target,e.expectedValue,e.failureMessage||"",t):{success:!1,failMessage:"Missing expectedValue for toHaveRole assertion"};default:return{success:!1,failMessage:`Unknown assertion type: ${e.assertion}`}}}}});var Nt={};K(Nt,{runContractTestsPlaywright:()=>Ts});async function Ts(c,e,t,r,s){let i=r?.test?.components?.find(S=>S.name===c),o=!!i?.contractPath,a=new Pe(!0,o),l={actionTimeoutMs:400,assertionTimeoutMs:400,navigationTimeoutMs:3e4,componentReadyTimeoutMs:5e3},p=r?.test?.disableTimeouts===!0,m=i?.disableTimeouts===!0||p,h=(S,se,re)=>{if(m)return 0;let Q=S??se;return typeof Q!="number"||!Number.isFinite(Q)||Q<0?re:Q},P=h(i?.actionTimeoutMs,r?.test?.actionTimeoutMs,l.actionTimeoutMs),q=h(i?.assertionTimeoutMs,r?.test?.assertionTimeoutMs,l.assertionTimeoutMs),M=h(i?.navigationTimeoutMs,r?.test?.navigationTimeoutMs,l.navigationTimeoutMs),U=h(i?.componentReadyTimeoutMs,r?.test?.componentReadyTimeoutMs,l.componentReadyTimeoutMs),N=be(t),W=i?.contractPath;if(!W)throw new Error(`Contract path not found for component: ${c}`);let _=(()=>{if(je.default.isAbsolute(W))return W;if(s){let se=je.default.resolve(s,W);try{return(0,Le.readFileSync)(se,"utf-8"),se}catch{}}let S=je.default.resolve(process.cwd(),W);try{return(0,Le.readFileSync)(S,"utf-8"),S}catch{return new URL(W,Cs.url).pathname}})(),z=(0,Le.readFileSync)(_,"utf-8"),v=JSON.parse(z),Se=(v.relationships?.length||0)+(v.static[0]?.assertions.length||0)+v.dynamic.length,Fe=v.meta?.source?.apg,te=[],ae=[],ce=[],J=[],R=null,H=(S,se)=>{let re=he(se),Q=At(re,N);if(Q==="error")return te.push(S),{status:"fail",level:re,detail:S};if(Q==="warning")return ae.push(S),{status:"warn",level:re,detail:S};let ie=`${S} (ignored by strictness=${N}, level=${re})`;return J.push(ie),{status:"skip",level:re,detail:ie}};try{if(R=await Ct(),e){try{await R.goto(e,{waitUntil:"domcontentloaded",timeout:M})}catch(n){throw new Error(`Failed to navigate to ${e}. Ensure dev server is running and accessible. Original error: ${n instanceof Error?n.message:String(n)}`)}await R.addStyleTag({content:"* { transition: none !important; animation: none !important; }"})}let S=await Me.detect(c,i,P,q,s);if(!S)throw new Error(`Unsupported component: ${c}`);let se=S.getMainSelector();if(!se)throw new Error(`CRITICAL: No selector found in contract for ${c}`);try{await R.locator(se).first().waitFor({state:"attached",timeout:U})}catch(n){throw new Error(`
2217
136
  \u274C CRITICAL: Component not found on page!
2218
137
  This usually means:
2219
138
  - The component didn't render
2220
139
  - The URL is incorrect
2221
- - The component selector '${mainSelector}' in the contract is wrong
2222
- - Original error: ${error}`
2223
- );
2224
- }
2225
- reporter.start(componentName, totalTests, apgUrl);
2226
- if (componentName === "menu" && componentContract.selectors.trigger) {
2227
- await page.locator(componentContract.selectors.trigger).first().waitFor({ state: "attached", timeout: componentReadyTimeoutMs }).catch(() => {
2228
- });
2229
- }
2230
- const hasSubmenuCapability = componentName === "menu" && !!componentContract.selectors.submenuTrigger ? await page.locator(componentContract.selectors.submenuTrigger).count() > 0 : false;
2231
- const isSubmenuRelation = (rel) => rel.type === "aria-reference" && [rel.from, rel.to].some((name) => ["submenu", "submenuTrigger", "submenuItems"].includes(name || "")) || rel.type === "contains" && [rel.parent, rel.child].some((name) => ["submenu", "submenuTrigger", "submenuItems"].includes(name || ""));
2232
- let staticPassed = 0;
2233
- let staticFailed = 0;
2234
- let staticWarnings = 0;
2235
- for (const rel of componentContract.relationships || []) {
2236
- const relationshipLevel = normalizeLevel(rel.level);
2237
- if (componentName === "menu" && !hasSubmenuCapability) {
2238
- const involvesSubmenu = isSubmenuRelation(rel);
2239
- if (involvesSubmenu) {
2240
- const relDescription = rel.type === "aria-reference" ? `${rel.from}.${rel.attribute} references ${rel.to}` : `${rel.parent} contains ${rel.child}`;
2241
- const skipMessage = `Skipping submenu relationship assertion: no submenu capability detected in rendered component.`;
2242
- skipped.push(skipMessage);
2243
- reporter.reportStaticTest(relDescription, "skip", skipMessage, relationshipLevel);
2244
- continue;
2245
- }
2246
- }
2247
- if (rel.type === "aria-reference") {
2248
- const relDescription = `${rel.from}.${rel.attribute} references ${rel.to}`;
2249
- const fromSelector = componentContract.selectors[rel.from];
2250
- const toSelector = componentContract.selectors[rel.to];
2251
- if (!fromSelector || !toSelector) {
2252
- const outcome = classifyFailure(
2253
- `Relationship selector missing: from="${rel.from}" or to="${rel.to}" not found in selectors.`,
2254
- rel.level
2255
- );
2256
- if (outcome.status === "fail") staticFailed += 1;
2257
- if (outcome.status === "warn") staticWarnings += 1;
2258
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
2259
- continue;
2260
- }
2261
- const fromTarget = page.locator(fromSelector).first();
2262
- const toTarget = page.locator(toSelector).first();
2263
- const fromExists = await fromTarget.count() > 0;
2264
- const toExists = await toTarget.count() > 0;
2265
- if (!fromExists || !toExists) {
2266
- if (componentName === "menu" && isSubmenuRelation(rel)) {
2267
- const skipMessage = "Skipping submenu relationship assertion in static phase: submenu elements are not present until submenu is opened.";
2268
- skipped.push(skipMessage);
2269
- reporter.reportStaticTest(relDescription, "skip", skipMessage, relationshipLevel);
2270
- continue;
2271
- }
2272
- const outcome = classifyFailure(
2273
- `Relationship target not found: ${!fromExists ? rel.from : rel.to}.`,
2274
- rel.level
2275
- );
2276
- if (outcome.status === "fail") staticFailed += 1;
2277
- if (outcome.status === "warn") staticWarnings += 1;
2278
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
2279
- continue;
2280
- }
2281
- const attrValue = await fromTarget.getAttribute(rel.attribute);
2282
- const toId = await toTarget.getAttribute("id");
2283
- if (!toId) {
2284
- const outcome = classifyFailure(
2285
- `Relationship target "${rel.to}" must have an id for ${rel.attribute} validation.`,
2286
- rel.level
2287
- );
2288
- if (outcome.status === "fail") staticFailed += 1;
2289
- if (outcome.status === "warn") staticWarnings += 1;
2290
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
2291
- continue;
2292
- }
2293
- const references = (attrValue || "").split(/\s+/).filter(Boolean);
2294
- const matches = references.includes(toId);
2295
- if (!matches) {
2296
- const outcome = classifyFailure(
2297
- `Expected ${rel.from} ${rel.attribute} to reference id "${toId}", found "${attrValue || ""}".`,
2298
- rel.level
2299
- );
2300
- if (outcome.status === "fail") staticFailed += 1;
2301
- if (outcome.status === "warn") staticWarnings += 1;
2302
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
2303
- continue;
2304
- }
2305
- passes.push(`Relationship valid: ${rel.from}.${rel.attribute} -> ${rel.to} (id=${toId}).`);
2306
- staticPassed += 1;
2307
- reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
2308
- continue;
2309
- }
2310
- if (rel.type === "contains") {
2311
- const relDescription = `${rel.parent} contains ${rel.child}`;
2312
- const parentSelector = componentContract.selectors[rel.parent];
2313
- const childSelector = componentContract.selectors[rel.child];
2314
- if (!parentSelector || !childSelector) {
2315
- const outcome = classifyFailure(
2316
- `Relationship selector missing: parent="${rel.parent}" or child="${rel.child}" not found in selectors.`,
2317
- rel.level
2318
- );
2319
- if (outcome.status === "fail") staticFailed += 1;
2320
- if (outcome.status === "warn") staticWarnings += 1;
2321
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
2322
- continue;
2323
- }
2324
- const parent = page.locator(parentSelector).first();
2325
- const parentExists = await parent.count() > 0;
2326
- if (!parentExists) {
2327
- if (componentName === "menu" && isSubmenuRelation(rel)) {
2328
- const skipMessage = "Skipping submenu relationship assertion in static phase: submenu container is not present until submenu is opened.";
2329
- skipped.push(skipMessage);
2330
- reporter.reportStaticTest(relDescription, "skip", skipMessage, relationshipLevel);
2331
- continue;
2332
- }
2333
- const outcome = classifyFailure(`Relationship parent target not found: ${rel.parent}.`, rel.level);
2334
- if (outcome.status === "fail") staticFailed += 1;
2335
- if (outcome.status === "warn") staticWarnings += 1;
2336
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
2337
- continue;
2338
- }
2339
- const descendants = parent.locator(childSelector);
2340
- const descendantCount = await descendants.count();
2341
- if (descendantCount < 1) {
2342
- if (componentName === "menu" && isSubmenuRelation(rel)) {
2343
- const skipMessage = "Skipping submenu relationship assertion in static phase: submenu descendants are not present until submenu is opened.";
2344
- skipped.push(skipMessage);
2345
- reporter.reportStaticTest(relDescription, "skip", skipMessage, relationshipLevel);
2346
- continue;
2347
- }
2348
- const outcome = classifyFailure(
2349
- `Expected ${rel.parent} to contain descendant matching selector for ${rel.child}.`,
2350
- rel.level
2351
- );
2352
- if (outcome.status === "fail") staticFailed += 1;
2353
- if (outcome.status === "warn") staticWarnings += 1;
2354
- reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
2355
- continue;
2356
- }
2357
- passes.push(`Relationship valid: ${rel.parent} contains ${rel.child}.`);
2358
- staticPassed += 1;
2359
- reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
2360
- }
2361
- }
2362
- async function resolveExpectedValue(expectedValue, selectors, page2, context = {}) {
2363
- if (!expectedValue || typeof expectedValue !== "object" || !("ref" in expectedValue)) return expectedValue;
2364
- let refSelector;
2365
- if (expectedValue.ref === "relative") {
2366
- if (!expectedValue.relativeTarget || !context.relativeBaseSelector) return void 0;
2367
- const baseLocator = page2.locator(context.relativeBaseSelector);
2368
- const count = await baseLocator.count();
2369
- let idx = 0;
2370
- if (expectedValue.relativeTarget === "first") idx = 0;
2371
- else if (expectedValue.relativeTarget === "second") idx = 1;
2372
- else if (expectedValue.relativeTarget === "last") idx = count - 1;
2373
- else if (!isNaN(Number(expectedValue.relativeTarget))) idx = Number(expectedValue.relativeTarget);
2374
- else idx = 0;
2375
- if (idx < 0 || idx >= count) return void 0;
2376
- const relElem = baseLocator.nth(idx);
2377
- return await getPropertyFromLocator(relElem, expectedValue.property || expectedValue.attribute);
2378
- } else {
2379
- refSelector = selectors[expectedValue.ref];
2380
- if (!refSelector) throw new Error(`Selector for ref '${expectedValue.ref}' not found in contract selectors.`);
2381
- const refLocator = page2.locator(refSelector).first();
2382
- return await getPropertyFromLocator(refLocator, expectedValue.property || expectedValue.attribute);
2383
- }
2384
- }
2385
- async function getPropertyFromLocator(locator, property) {
2386
- if (!locator) return void 0;
2387
- if (!property || property === "id") {
2388
- return await locator.getAttribute("id") ?? void 0;
2389
- } else if (property === "class") {
2390
- return await locator.getAttribute("class") ?? void 0;
2391
- } else if (property === "textContent") {
2392
- return await locator.evaluate((el) => el.textContent ?? void 0);
2393
- } else if (property.startsWith("aria-")) {
2394
- return await locator.getAttribute(property) ?? void 0;
2395
- } else if (property.endsWith("*")) {
2396
- const attrs = await locator.evaluate((el) => {
2397
- const out = [];
2398
- for (const attr of Array.from(el.attributes)) {
2399
- if (attr.name.startsWith("aria-")) out.push(`${attr.name}=${attr.value}`);
2400
- }
2401
- return out.join(";");
2402
- });
2403
- return attrs;
2404
- } else {
2405
- return await locator.getAttribute(property) ?? void 0;
2406
- }
2407
- }
2408
- const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
2409
- for (const test of componentContract.static[0]?.assertions || []) {
2410
- if (test.target === "relative") continue;
2411
- const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
2412
- const staticLevel = normalizeLevel(test.level);
2413
- if (componentName === "menu" && test.target === "submenuTrigger" && !hasSubmenuCapability) {
2414
- const skipMessage = `Skipping submenu static assertion for ${test.target}: no submenu capability detected in rendered component.`;
2415
- skipped.push(skipMessage);
2416
- reporter.reportStaticTest(staticDescription, "skip", skipMessage, staticLevel);
2417
- continue;
2418
- }
2419
- const targetSelector = componentContract.selectors[test.target];
2420
- if (!targetSelector) {
2421
- const failure = `Selector for target ${test.target} not found.`;
2422
- const outcome = classifyFailure(failure, test.level);
2423
- if (outcome.status === "fail") staticFailed += 1;
2424
- if (outcome.status === "warn") staticWarnings += 1;
2425
- reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
2426
- continue;
2427
- }
2428
- const target = page.locator(targetSelector).first();
2429
- const exists = await target.count() > 0;
2430
- if (!exists) {
2431
- const failure = `Target ${test.target} not found.`;
2432
- const outcome = classifyFailure(failure, test.level);
2433
- if (outcome.status === "fail") staticFailed += 1;
2434
- if (outcome.status === "warn") staticWarnings += 1;
2435
- reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
2436
- continue;
2437
- }
2438
- const isRedundantCheck = (selector, attrName, expectedVal) => {
2439
- const attrPattern = new RegExp(`\\[${attrName}(?:=["']?([^\\]"']+)["']?)?\\]`);
2440
- const match = selector.match(attrPattern);
2441
- if (!match) return false;
2442
- if (!expectedVal) return true;
2443
- const selectorValue = match[1];
2444
- if (selectorValue) {
2445
- const expectedValues = expectedVal.split(" | ");
2446
- return expectedValues.includes(selectorValue);
2447
- }
2448
- return false;
2449
- };
2450
- let expectedValue = test.expectedValue;
2451
- if (test.expectedValue && typeof test.expectedValue === "object" && "ref" in test.expectedValue) {
2452
- const context = {};
2453
- const relTarget = test.relativeTarget;
2454
- if (test.expectedValue.ref === "relative" && test.target === "relative" && relTarget) {
2455
- const baseSel = componentContract.selectors[relTarget];
2456
- if (!baseSel) throw new Error(`Selector for relativeTarget '${relTarget}' not found in contract selectors.`);
2457
- context.relativeBaseSelector = baseSel;
2458
- } else if (test.expectedValue.ref === "relative" && relTarget) {
2459
- const baseSel = componentContract.selectors[relTarget];
2460
- if (!baseSel) throw new Error(`Selector for relativeTarget '${relTarget}' not found in contract selectors.`);
2461
- context.relativeBaseSelector = baseSel;
2462
- }
2463
- expectedValue = await resolveExpectedValue(test.expectedValue, componentContract.selectors, page, context);
2464
- console.log("Expected value in static check", expectedValue);
2465
- }
2466
- if (!test.expectedValue) {
2467
- const attributes = test.attribute.split(" | ");
2468
- let hasAny = false;
2469
- let allRedundant = true;
2470
- for (const attr of attributes) {
2471
- const attrTrimmed = attr.trim();
2472
- if (isRedundantCheck(targetSelector, attrTrimmed)) {
2473
- passes.push(`${attrTrimmed} on ${test.target} verified by selector (already present in: ${targetSelector}).`);
2474
- hasAny = true;
2475
- continue;
2476
- }
2477
- allRedundant = false;
2478
- const value = await target.getAttribute(attrTrimmed);
2479
- if (value !== null) {
2480
- hasAny = true;
2481
- break;
2482
- }
2483
- }
2484
- if (!hasAny && !allRedundant) {
2485
- const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
2486
- const outcome = classifyFailure(failure, test.level);
2487
- if (outcome.status === "fail") staticFailed += 1;
2488
- if (outcome.status === "warn") staticWarnings += 1;
2489
- reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
2490
- } else if (!allRedundant && hasAny) {
2491
- passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
2492
- staticPassed += 1;
2493
- reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
2494
- } else {
2495
- staticPassed += 1;
2496
- reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
2497
- }
2498
- } else {
2499
- if (isRedundantCheck(targetSelector, test.attribute, typeof expectedValue === "string" ? expectedValue : void 0)) {
2500
- passes.push(`${test.attribute}="${expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
2501
- staticPassed += 1;
2502
- reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
2503
- } else {
2504
- const valueToCheck = expectedValue ?? "";
2505
- const result = await staticAssertionRunner.validateAttribute(
2506
- target,
2507
- test.target,
2508
- test.attribute,
2509
- valueToCheck,
2510
- test.failureMessage,
2511
- "Static ARIA Test"
2512
- );
2513
- if (result.success && result.passMessage) {
2514
- passes.push(result.passMessage);
2515
- staticPassed += 1;
2516
- reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
2517
- } else if (!result.success && result.failMessage) {
2518
- const outcome = classifyFailure(result.failMessage, test.level);
2519
- if (outcome.status === "fail") staticFailed += 1;
2520
- if (outcome.status === "warn") staticWarnings += 1;
2521
- reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
2522
- }
2523
- }
2524
- }
2525
- }
2526
- for (const dynamicTest of componentContract.dynamic || []) {
2527
- if (!page || page.isClosed()) {
2528
- console.warn(`
2529
- \u26A0\uFE0F Browser closed - skipping remaining ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests
2530
- `);
2531
- failures.push(`CRITICAL: Browser/page closed before completing all tests. ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests skipped.`);
2532
- break;
2533
- }
2534
- try {
2535
- await strategy.resetState(page);
2536
- } catch (error) {
2537
- const errorMessage = error instanceof Error ? error.message : String(error);
2538
- reporter.error(errorMessage);
2539
- throw error;
2540
- }
2541
- const { setup = [], action, assertions } = dynamicTest;
2542
- const dynamicLevel = normalizeLevel(dynamicTest.level);
2543
- const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
2544
- if (Array.isArray(setup) && setup.length > 0) {
2545
- for (const setupAct of setup) {
2546
- let setupResult;
2547
- if (setupAct.type === "focus") {
2548
- if (setupAct.target === "relative" && setupAct.relativeTarget) {
2549
- setupResult = await actionExecutor.focus("relative", setupAct.relativeTarget);
2550
- } else {
2551
- setupResult = await actionExecutor.focus(setupAct.target);
2552
- }
2553
- } else if (setupAct.type === "type" && setupAct.value) {
2554
- setupResult = await actionExecutor.type(setupAct.target, setupAct.value);
2555
- } else if (setupAct.type === "click") {
2556
- setupResult = await actionExecutor.click(setupAct.target, setupAct.relativeTarget);
2557
- } else if (setupAct.type === "keypress" && setupAct.key) {
2558
- setupResult = await actionExecutor.keypress(setupAct.target, setupAct.key);
2559
- } else if (setupAct.type === "hover") {
2560
- setupResult = await actionExecutor.hover(setupAct.target, setupAct.relativeTarget);
2561
- } else {
2562
- continue;
2563
- }
2564
- if (!setupResult.success) {
2565
- const setupMsg = setupResult.error || "Setup action failed";
2566
- const outcome = classifyFailure(`Setup failed: ${setupMsg}`, dynamicTest.level);
2567
- reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, outcome.status, outcome.detail);
2568
- continue;
2569
- }
2570
- }
2571
- }
2572
- const failuresBeforeTest = failures.length;
2573
- const warningsBeforeTest = warnings.length;
2574
- const skippedBeforeTest = skipped.length;
2575
- const shouldSkipTest = await strategy.shouldSkipTest(dynamicTest, page);
2576
- if (shouldSkipTest) {
2577
- const skipMessage = `Skipping test - component-specific conditions not met`;
2578
- skipped.push(skipMessage);
2579
- reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, "skip", skipMessage);
2580
- continue;
2581
- }
2582
- const assertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
2583
- let shouldAbortCurrentTest = false;
2584
- let actionOutcome = null;
2585
- for (const act of action) {
2586
- if (!page || page.isClosed()) {
2587
- failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
2588
- shouldAbortCurrentTest = true;
2589
- break;
2590
- }
2591
- let result;
2592
- if (act.type === "focus") {
2593
- if (act.target === "relative" && act.relativeTarget) {
2594
- result = await actionExecutor.focus("relative", act.relativeTarget);
2595
- } else {
2596
- result = await actionExecutor.focus(act.target);
2597
- }
2598
- } else if (act.type === "type" && act.value) {
2599
- result = await actionExecutor.type(act.target, act.value);
2600
- } else if (act.type === "click") {
2601
- result = await actionExecutor.click(act.target, act.relativeTarget);
2602
- } else if (act.type === "keypress" && act.key) {
2603
- result = await actionExecutor.keypress(act.target, act.key);
2604
- } else if (act.type === "hover") {
2605
- result = await actionExecutor.hover(act.target, act.relativeTarget);
2606
- } else {
2607
- continue;
2608
- }
2609
- if (!result.success) {
2610
- if (result.error) {
2611
- const outcome = classifyFailure(result.error, dynamicTest.level);
2612
- actionOutcome = { status: outcome.status, detail: outcome.detail };
2613
- }
2614
- shouldAbortCurrentTest = true;
2615
- break;
2616
- }
2617
- }
2618
- if (shouldAbortCurrentTest) {
2619
- reporter.reportTest(
2620
- { description: dynamicTest.description, level: dynamicLevel },
2621
- actionOutcome?.status || "fail",
2622
- actionOutcome?.detail || failures[failures.length - 1]
2623
- );
2624
- continue;
2625
- }
2626
- for (const assertion of assertions) {
2627
- let expectedValue;
2628
- if (assertion.expectedValue && typeof assertion.expectedValue === "object" && "ref" in assertion.expectedValue) {
2629
- if (assertion.expectedValue.ref === "relative") {
2630
- const { RelativeTargetResolver: RelativeTargetResolver2 } = await Promise.resolve().then(() => (init_RelativeTargetResolver(), RelativeTargetResolver_exports));
2631
- const relativeSelector = componentContract.selectors.relative;
2632
- if (!relativeSelector) throw new Error("Relative selector not defined in contract selectors.");
2633
- const relTarget = assertion.relativeTarget || "first";
2634
- const relElem = await RelativeTargetResolver2.resolve(page, relativeSelector, relTarget);
2635
- if (!relElem) throw new Error(`Could not resolve relative target '${relTarget}' for expectedValue.`);
2636
- const prop = assertion.expectedValue.property || assertion.expectedValue.attribute || "id";
2637
- if (prop === "textContent") {
2638
- expectedValue = await relElem.evaluate((el) => el.textContent ?? void 0);
2639
- } else {
2640
- const attr = await relElem.getAttribute(prop);
2641
- expectedValue = attr === null ? void 0 : attr;
2642
- }
2643
- } else {
2644
- expectedValue = await resolveExpectedValue(assertion.expectedValue, componentContract.selectors, page, {});
2645
- }
2646
- } else if (typeof assertion.expectedValue === "string" || typeof assertion.expectedValue === "undefined") {
2647
- expectedValue = assertion.expectedValue;
2648
- } else {
2649
- expectedValue = "";
2650
- }
2651
- const assertionToRun = { ...assertion, expectedValue };
2652
- const valueToCheck = expectedValue ?? "";
2653
- const result = await assertionRunner.validate({ ...assertionToRun, expectedValue: valueToCheck }, dynamicTest.description);
2654
- if (result.success && result.passMessage) {
2655
- passes.push(result.passMessage);
2656
- } else if (!result.success && result.failMessage) {
2657
- const assertionLevel = normalizeLevel(assertion.level || dynamicTest.level);
2658
- const outcome = classifyFailure(result.failMessage, assertionLevel);
2659
- if (outcome.status === "skip") {
2660
- continue;
2661
- }
2662
- }
2663
- }
2664
- const failuresAfterTest = failures.length;
2665
- const warningsAfterTest = warnings.length;
2666
- const skippedAfterTest = skipped.length;
2667
- if (failuresAfterTest > failuresBeforeTest) {
2668
- reporter.reportTest(
2669
- { description: dynamicTest.description, level: dynamicLevel },
2670
- "fail",
2671
- failures[failures.length - 1]
2672
- );
2673
- } else if (warningsAfterTest > warningsBeforeTest) {
2674
- reporter.reportTest(
2675
- { description: dynamicTest.description, level: dynamicLevel },
2676
- "warn",
2677
- warnings[warnings.length - 1]
2678
- );
2679
- } else if (skippedAfterTest > skippedBeforeTest) {
2680
- reporter.reportTest(
2681
- { description: dynamicTest.description, level: dynamicLevel },
2682
- "skip",
2683
- skipped[skipped.length - 1]
2684
- );
2685
- } else {
2686
- reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, "pass");
2687
- }
2688
- }
2689
- reporter.reportStatic(staticPassed, staticFailed, staticWarnings);
2690
- reporter.summary(failures);
2691
- } catch (error) {
2692
- if (error instanceof Error) {
2693
- if (error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch")) {
2694
- throw new Error("\n\u274C CRITICAL: Playwright browsers not found!\n\u{1F4E6} Run: npx playwright install chromium");
2695
- } else if (error.message.includes("net::ERR_CONNECTION_REFUSED") || error.message.includes("NS_ERROR_CONNECTION_REFUSED")) {
2696
- throw new Error(`
140
+ - The component selector '${se}' in the contract is wrong
141
+ - Original error: ${n}`)}a.start(c,Se,Fe),c==="menu"&&v.selectors.main&&await R.locator(v.selectors.main).first().waitFor({state:"visible",timeout:U}).catch(()=>{});let re=c==="menu"&&v.selectors.submenuTrigger?await R.locator(v.selectors.submenuTrigger).count()>0:!1,Q=n=>n.type==="aria-reference"&&[n.from,n.to].some(g=>["submenu","submenuTrigger","submenuItems"].includes(g||""))||n.type==="contains"&&[n.parent,n.child].some(g=>["submenu","submenuTrigger","submenuItems"].includes(g||"")),ie=0,G=0,Y=0;for(let n of v.relationships||[]){if(S&&typeof S.resetState=="function")try{await S.resetState(R)}catch(b){ae.push(`Warning: resetState failed before relationship test: ${b instanceof Error?b.message:String(b)}`)}let g=he(n.level);if(Array.isArray(n.setup)&&n.setup.length>0){let E=function(u){return A.includes(u)};var pt=E;let b=new ye(R,v.selectors,P),x=n.type==="aria-reference"?`${n.from}.${n.attribute} references ${n.to}`:`${n.parent} contains ${n.child}`,A=["focus","type","click","keypress","hover"],C=u=>({...u,type:E(u.type)?u.type:"click"}),k=n.setup.map(C),y=await Ve(k,b,S,R,x,["submenu","submenuTrigger","submenuItems"]);if(y.skip){J.push(y.message||"Setup action skipped"),a.reportStaticTest(x,"skip",y.message,g);continue}if(!y.success){let u=`Relationship setup failed: ${y.error}`,w=H(u,n.level);w.status==="fail"&&(G+=1),w.status==="warn"&&(Y+=1),a.reportStaticTest(x,w.status,w.detail,w.level);continue}}if(c==="menu"&&!re&&Q(n)){let x=n.type==="aria-reference"?`${n.from}.${n.attribute} references ${n.to}`:`${n.parent} contains ${n.child}`,A="Skipping submenu relationship assertion: no submenu capability detected in rendered component.";J.push(A),a.reportStaticTest(x,"skip",A,g);continue}if(n.type==="aria-reference"){let b=`${n.from}.${n.attribute} references ${n.to}`,x=v.selectors[n.from],A=v.selectors[n.to];if(!x||!A){let T=H(`Relationship selector missing: from="${n.from}" or to="${n.to}" not found in selectors.`,n.level);T.status==="fail"&&(G+=1),T.status==="warn"&&(Y+=1),a.reportStaticTest(b,T.status,T.detail,T.level);continue}let E=R.locator(x).first(),C=R.locator(A).first(),k=await E.count()>0,y=await C.count()>0;if(!k||!y){if(c==="menu"&&Q(n)){let we="Skipping submenu relationship assertion in static phase: submenu elements are not present until submenu is opened.";J.push(we),a.reportStaticTest(b,"skip",we,g);continue}let T=H(`Relationship target not found: ${k?n.to:n.from}.`,n.level);T.status==="fail"&&(G+=1),T.status==="warn"&&(Y+=1),a.reportStaticTest(b,T.status,T.detail,T.level);continue}let u=await E.getAttribute(n.attribute),w=await C.getAttribute("id");if(!w){let T=H(`Relationship target "${n.to}" must have an id for ${n.attribute} validation.`,n.level);T.status==="fail"&&(G+=1),T.status==="warn"&&(Y+=1),a.reportStaticTest(b,T.status,T.detail,T.level);continue}if(!(u||"").split(/\s+/).filter(Boolean).includes(w)){let T=H(`Expected ${n.from} ${n.attribute} to reference id "${w}", found "${u||""}".`,n.level);T.status==="fail"&&(G+=1),T.status==="warn"&&(Y+=1),a.reportStaticTest(b,T.status,T.detail,T.level);continue}ce.push(`Relationship valid: ${n.from}.${n.attribute} -> ${n.to} (id=${w}).`),ie+=1,a.reportStaticTest(b,"pass",void 0,g);continue}if(n.type==="contains"){let b=`${n.parent} contains ${n.child}`,x=v.selectors[n.parent],A=v.selectors[n.child];if(!x||!A){let u=H(`Relationship selector missing: parent="${n.parent}" or child="${n.child}" not found in selectors.`,n.level);u.status==="fail"&&(G+=1),u.status==="warn"&&(Y+=1),a.reportStaticTest(b,u.status,u.detail,u.level);continue}let E=R.locator(x).first();if(!(await E.count()>0)){if(c==="menu"&&Q(n)){let w="Skipping submenu relationship assertion in static phase: submenu container is not present until submenu is opened.";J.push(w),a.reportStaticTest(b,"skip",w,g);continue}let u=H(`Relationship parent target not found: ${n.parent}.`,n.level);u.status==="fail"&&(G+=1),u.status==="warn"&&(Y+=1),a.reportStaticTest(b,u.status,u.detail,u.level);continue}if(await E.locator(A).count()<1){if(c==="menu"&&Q(n)){let w="Skipping submenu relationship assertion in static phase: submenu descendants are not present until submenu is opened.";J.push(w),a.reportStaticTest(b,"skip",w,g);continue}let u=H(`Expected ${n.parent} to contain descendant matching selector for ${n.child}.`,n.level);u.status==="fail"&&(G+=1),u.status==="warn"&&(Y+=1),a.reportStaticTest(b,u.status,u.detail,u.level);continue}ce.push(`Relationship valid: ${n.parent} contains ${n.child}.`),ie+=1,a.reportStaticTest(b,"pass",void 0,g)}}async function ft(n,g,b,x={}){if(!n||typeof n!="object"||!("ref"in n))return n;let A;if(n.ref==="relative"){if(!n.relativeTarget||!x.relativeBaseSelector)return;let E=b.locator(x.relativeBaseSelector),C=await E.count(),k=0;if(n.relativeTarget==="first"?k=0:n.relativeTarget==="second"?k=1:n.relativeTarget==="last"?k=C-1:isNaN(Number(n.relativeTarget))?k=0:k=Number(n.relativeTarget),k<0||k>=C)return;let y=E.nth(k);return await dt(y,n.property||n.attribute)}else{if(A=g[n.ref],!A)throw new Error(`Selector for ref '${n.ref}' not found in contract selectors.`);let E=b.locator(A).first();return await dt(E,n.property||n.attribute)}}async function dt(n,g){if(n)return!g||g==="id"?await n.getAttribute("id")??void 0:g==="class"?await n.getAttribute("class")??void 0:g==="textContent"?await n.evaluate(b=>b.textContent??void 0):g.startsWith("aria-")?await n.getAttribute(g)??void 0:g.endsWith("*")?await n.evaluate(x=>{let A=[];for(let E of Array.from(x.attributes))E.name.startsWith("aria-")&&A.push(`${E.name}=${E.value}`);return A.join(";")}):await n.getAttribute(g)??void 0}let is=new $e(R,v.selectors,q);async function Ve(n,g,b,x,A,E=[]){if(!Array.isArray(n)||n.length===0)return{success:!0};b&&typeof b.resetState=="function"&&await b.resetState(x);for(let C of n){let k={success:!0};try{if(C.type==="focus")C.target==="relative"&&C.relativeTarget?k=await g.focus("relative",C.relativeTarget):k=await g.focus(C.target);else if(C.type==="type"&&C.value)k=await g.type(C.target,C.value);else if(C.type==="click")k=await g.click(C.target,C.relativeTarget);else if(C.type==="keypress"&&C.key)k=await g.keypress(C.target,C.key);else if(C.type==="hover")k=await g.hover(C.target,C.relativeTarget);else continue}catch(y){k={success:!1,error:y instanceof Error?y.message:String(y)}}if(!k.success){let y=k.error||"Setup action failed";return E.some(w=>A.includes(w)||y.includes(w))?{success:!1,skip:!0,message:`Skipping test - capability not present: ${y}`}:{success:!1,error:y}}}return{success:!0}}for(let n of v.static[0]?.assertions||[]){if(S&&typeof S.resetState=="function")try{await S.resetState(R)}catch(y){ae.push(`Warning: resetState failed before static test: ${y instanceof Error?y.message:String(y)}`)}if(n.target==="relative")continue;let g=`${n.target}${n.attribute?` (${n.attribute})`:""}`,b=he(n.level);if(c==="menu"&&n.target==="submenuTrigger"&&!re){let y=`Skipping submenu static assertion for ${n.target}: no submenu capability detected in rendered component.`;J.push(y),a.reportStaticTest(g,"skip",y,b);continue}if(Array.isArray(n.setup)&&n.setup.length>0){let w=function(ne){return u.includes(ne)};var pt=w;let y=new ye(R,v.selectors,P),u=["focus","type","click","keypress","hover"],X=ne=>({...ne,type:w(ne.type)?ne.type:"click"}),F=n.setup.map(X),T=await Ve(F,y,S,R,g,["submenu","submenuTrigger","submenuItems"]);if(T.skip){J.push(T.message||"Setup action skipped"),a.reportStaticTest(g,"skip",T.message,b);continue}if(!T.success){let ne=`Static setup failed: ${T.error}`,d=H(ne,n.level);d.status==="fail"&&(G+=1),d.status==="warn"&&(Y+=1),a.reportStaticTest(g,d.status,d.detail,d.level);continue}}let x=v.selectors[n.target];if(!x){let y=`Selector for target ${n.target} not found.`,u=H(y,n.level);u.status==="fail"&&(G+=1),u.status==="warn"&&(Y+=1),a.reportStaticTest(g,u.status,u.detail,u.level);continue}let A=R.locator(x).first();if(!(await A.count()>0)){let y=`Target ${n.target} not found.`,u=H(y,n.level);u.status==="fail"&&(G+=1),u.status==="warn"&&(Y+=1),a.reportStaticTest(g,u.status,u.detail,u.level);continue}let C=(y,u,w)=>{let X=new RegExp(`\\[${u}(?:=["']?([^\\]"']+)["']?)?\\]`),F=y.match(X);if(!F)return!1;if(!w)return!0;let T=F[1];return T?w.split(" | ").includes(T):!1},k=n.expectedValue;if(n.expectedValue&&typeof n.expectedValue=="object"&&"ref"in n.expectedValue){let y={},u=n.relativeTarget;if(n.expectedValue.ref==="relative"&&n.target==="relative"&&u){let w=v.selectors[u];if(!w)throw new Error(`Selector for relativeTarget '${u}' not found in contract selectors.`);y.relativeBaseSelector=w}else if(n.expectedValue.ref==="relative"&&u){let w=v.selectors[u];if(!w)throw new Error(`Selector for relativeTarget '${u}' not found in contract selectors.`);y.relativeBaseSelector=w}k=await ft(n.expectedValue,v.selectors,R,y),console.log("Expected value in static check",k)}if(n.expectedValue)if(C(x,n.attribute,typeof k=="string"?k:void 0))ce.push(`${n.attribute}="${k}" on ${n.target} verified by selector (already present in: ${x}).`),ie+=1,a.reportStaticTest(g,"pass",void 0,b);else{let y=k??"",u=await is.validateAttribute(A,n.target,n.attribute,y,n.failureMessage,"Static ARIA Test");if(u.success&&u.passMessage)ce.push(u.passMessage),ie+=1,a.reportStaticTest(g,"pass",void 0,b);else if(!u.success&&u.failMessage){let w=H(u.failMessage,n.level);w.status==="fail"&&(G+=1),w.status==="warn"&&(Y+=1),a.reportStaticTest(g,w.status,w.detail,w.level)}}else{let y=n.attribute.split(" | "),u=!1,w=!0;for(let X of y){let F=X.trim();if(C(x,F)){ce.push(`${F} on ${n.target} verified by selector (already present in: ${x}).`),u=!0;continue}if(w=!1,await A.getAttribute(F)!==null){u=!0;break}}if(!u&&!w){let X=n.failureMessage+` None of the attributes "${n.attribute}" are present.`,F=H(X,n.level);F.status==="fail"&&(G+=1),F.status==="warn"&&(Y+=1),a.reportStaticTest(g,F.status,F.detail,F.level)}else!w&&u?(ce.push(`At least one of the attributes "${n.attribute}" exists on the element.`),ie+=1,a.reportStaticTest(g,"pass",void 0,b)):(ie+=1,a.reportStaticTest(g,"pass",void 0,b))}}for(let n of v.dynamic||[]){if(!R||R.isClosed()){console.warn(`
142
+ \u26A0\uFE0F Browser closed - skipping remaining ${v.dynamic.length-v.dynamic.indexOf(n)} tests
143
+ `),te.push(`CRITICAL: Browser/page closed before completing all tests. ${v.dynamic.length-v.dynamic.indexOf(n)} tests skipped.`);break}try{await S.resetState(R)}catch(d){let B=d instanceof Error?d.message:String(d);throw a.error(B),d}let{setup:g=[],action:b,assertions:x}=n,A=he(n.level),E=new ye(R,v.selectors,P);if(Array.isArray(g)&&g.length>0){let B=function(ee){return d.includes(ee)};var pt=B;let d=["focus","type","click","keypress","hover"],pe=ee=>({...ee,type:B(ee.type)?ee.type:"click"}),Ue=g.map(pe),Z=await Ve(Ue,E,S,R,n.description,["submenu","submenuTrigger","submenuItems"]);if(Z.skip){J.push(Z.message||"Setup action skipped"),a.reportTest({description:n.description,level:A},"skip",Z.message);continue}if(!Z.success){let ee=H(`Setup failed: ${Z.error}`,n.level);a.reportTest({description:n.description,level:A},ee.status,ee.detail);continue}}let C=te.length,k=ae.length,y=J.length;if(await S.shouldSkipTest(n,R)){let d="Skipping test - component-specific conditions not met";J.push(d),a.reportTest({description:n.description,level:A},"skip",d);continue}let w=new $e(R,v.selectors,q),X=!1,F=null;for(let d of b){if(!R||R.isClosed()){te.push("CRITICAL: Browser/page closed during test execution. Remaining actions skipped."),X=!0;break}let B;if(d.type==="focus")d.target==="relative"&&d.relativeTarget?B=await E.focus("relative",d.relativeTarget):B=await E.focus(d.target);else if(d.type==="type"&&d.value)B=await E.type(d.target,d.value);else if(d.type==="click")B=await E.click(d.target,d.relativeTarget);else if(d.type==="keypress"&&d.key)B=await E.keypress(d.target,d.key);else if(d.type==="hover")B=await E.hover(d.target,d.relativeTarget);else continue;if(!B.success){if(B.error){let pe=H(B.error,n.level);F={status:pe.status,detail:pe.detail}}X=!0;break}}if(X){a.reportTest({description:n.description,level:A},F?.status||"fail",F?.detail||te[te.length-1]);continue}for(let d of x){let B;if(d.expectedValue&&typeof d.expectedValue=="object"&&"ref"in d.expectedValue)if(d.expectedValue.ref==="relative"){let{RelativeTargetResolver:De}=await Promise.resolve().then(()=>(Be(),Ot)),ee=v.selectors.relative;if(!ee)throw new Error("Relative selector not defined in contract selectors.");let gt=d.relativeTarget||"first",Oe=await De.resolve(R,ee,gt);if(!Oe)throw new Error(`Could not resolve relative target '${gt}' for expectedValue.`);let mt=d.expectedValue.property||d.expectedValue.attribute||"id";if(mt==="textContent")B=await Oe.evaluate(Te=>Te.textContent??void 0);else{let Te=await Oe.getAttribute(mt);B=Te===null?void 0:Te}}else B=await ft(d.expectedValue,v.selectors,R,{});else typeof d.expectedValue=="string"||typeof d.expectedValue>"u"?B=d.expectedValue:B="";let pe={...d,expectedValue:B},Ue=B??"",Z=await w.validate({...pe,expectedValue:Ue},n.description);if(Z.success&&Z.passMessage)ce.push(Z.passMessage);else if(!Z.success&&Z.failMessage){let De=he(d.level||n.level);if(H(Z.failMessage,De).status==="skip")continue}}let T=te.length,we=ae.length,ne=J.length;T>C?a.reportTest({description:n.description,level:A},"fail",te[te.length-1]):we>k?a.reportTest({description:n.description,level:A},"warn",ae[ae.length-1]):ne>y?a.reportTest({description:n.description,level:A},"skip",J[J.length-1]):a.reportTest({description:n.description,level:A},"pass")}a.reportStatic(ie,G,Y),a.summary(te)}catch(S){if(S instanceof Error)throw S.message.includes("Executable doesn't exist")||S.message.includes("browserType.launch")?new Error(`
144
+ \u274C CRITICAL: Playwright browsers not found!
145
+ \u{1F4E6} Run: npx playwright install chromium`):S.message.includes("net::ERR_CONNECTION_REFUSED")||S.message.includes("NS_ERROR_CONNECTION_REFUSED")?new Error(`
2697
146
  \u274C CRITICAL: Cannot connect to dev server!
2698
- Make sure your dev server is running at ${url}`);
2699
- } else if (error.message.includes("Timeout") && error.message.includes("waitFor")) {
2700
- throw new Error(
2701
- `
147
+ Make sure your dev server is running at ${e}`):S.message.includes("Timeout")&&S.message.includes("waitFor")?new Error(`
2702
148
  \u274C CRITICAL: Component not found on page!
2703
- The component selector could not be found within ${componentReadyTimeoutMs}ms.
149
+ The component selector could not be found within ${U}ms.
2704
150
  This usually means:
2705
151
  - The component didn't render
2706
152
  - The URL is incorrect
2707
153
  - The component selector was not provided to the component utility, or a wrong selector was used
2708
- `
2709
- );
2710
- } else if (error.message.includes("Target page, context or browser has been closed")) {
2711
- throw new Error(
2712
- "\n\u274C CRITICAL: Browser/page was closed unexpectedly!\nThis usually means:\n - The test timeout was too short\n - The browser crashed\n - An external process killed the browser"
2713
- );
2714
- } else {
2715
- throw error;
2716
- }
2717
- }
2718
- } finally {
2719
- if (page) await page.close();
2720
- }
2721
- return { passes, failures, skipped, warnings };
2722
- }
2723
- var import_fs2, import_path5, import_meta2;
2724
- var init_contractTestRunnerPlaywright = __esm({
2725
- "src/utils/test/src/contractTestRunnerPlaywright.ts"() {
2726
- "use strict";
2727
- import_fs2 = require("fs");
2728
- import_path5 = __toESM(require("path"), 1);
2729
- init_playwrightTestHarness();
2730
- init_ComponentDetector();
2731
- init_ContractReporter();
2732
- init_ActionExecutor();
2733
- init_AssertionRunner();
2734
- init_strictness();
2735
- import_meta2 = {};
2736
- }
2737
- });
2738
-
2739
- // src/utils/test/src/test.ts
2740
- async function testUiComponent(componentName, component, url, options = {}) {
2741
- if (!componentName || typeof componentName !== "string") {
2742
- throw new Error("\u274C testUiComponent requires a valid componentName (string)");
2743
- }
2744
- if (!url && (!component || !(component instanceof HTMLElement))) {
2745
- throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
2746
- }
2747
- if (url && typeof url !== "string") {
2748
- throw new Error("\u274C testUiComponent url parameter must be a string");
2749
- }
2750
- let results;
2751
- if (component) {
2752
- try {
2753
- results = await (0, import_jest_axe.axe)(component);
2754
- } catch (error) {
2755
- throw new Error(
2756
- `\u274C Axe accessibility scan failed
2757
- Error: ${error instanceof Error ? error.message : String(error)}`
2758
- );
2759
- }
2760
- } else {
2761
- results = { violations: [] };
2762
- }
2763
- async function checkDevServer(url2) {
2764
- try {
2765
- const response = await fetch(url2, {
2766
- method: "HEAD",
2767
- signal: AbortSignal.timeout(1e3)
2768
- });
2769
- if (response.ok || response.status === 304) {
2770
- return url2;
2771
- }
2772
- } catch {
2773
- return null;
2774
- }
2775
- return null;
2776
- }
2777
- let strictness = normalizeStrictness(options.strictness);
2778
- let config = {};
2779
- let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
2780
- if (typeof process !== "undefined" && typeof process.cwd === "function") {
2781
- try {
2782
- const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_configLoader(), configLoader_exports));
2783
- const result2 = await loadConfig2(process.cwd());
2784
- config = result2.config;
2785
- if (result2.configPath) {
2786
- configBaseDir = import_path6.default.dirname(result2.configPath);
2787
- }
2788
- if (options.strictness === void 0) {
2789
- const componentStrictness = config.test?.components?.find((comp) => comp?.name === componentName)?.strictness;
2790
- strictness = normalizeStrictness(componentStrictness ?? config.test?.strictness);
2791
- }
2792
- } catch {
2793
- if (options.strictness === void 0) {
2794
- strictness = "balanced";
2795
- }
2796
- }
2797
- }
2798
- let contract;
2799
- try {
2800
- if (url) {
2801
- const devServerUrl = await checkDevServer(url);
2802
- if (devServerUrl) {
2803
- console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
2804
- const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
2805
- contract = await runContractTestsPlaywright2(componentName, devServerUrl, strictness, config, configBaseDir);
2806
- } else {
2807
- throw new Error(
2808
- `\u274C Dev server not running at ${url}
2809
- Please start your dev server and try again.`
2810
- );
2811
- }
2812
- } else if (component) {
2813
- console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
2814
- const contractPath = config.test?.components?.find((comp) => comp?.name === componentName)?.contractPath;
2815
- if (!contractPath) {
2816
- throw new Error(`\u274C No contract path found for component: ${componentName}`);
2817
- }
2818
- contract = await runContractTests(
2819
- import_path6.default.resolve(configBaseDir, contractPath),
2820
- componentName,
2821
- component,
2822
- strictness
2823
- );
2824
- } else {
2825
- throw new Error("\u274C Either component or URL must be provided");
2826
- }
2827
- } catch (error) {
2828
- if (error instanceof Error) {
2829
- throw error;
2830
- }
2831
- throw new Error(`\u274C Contract test execution failed: ${String(error)}`);
2832
- }
2833
- const result = {
2834
- violations: results.violations,
2835
- raw: results,
2836
- contract
2837
- };
2838
- if (contract.failures.length > 0 && url === "Playwright") {
2839
- throw new Error(
2840
- `
2841
- \u274C ${contract.failures.length} accessibility contract test${contract.failures.length > 1 ? "s" : ""} failed (Playwright mode)
2842
- \u2705 ${contract.passes.length} test${contract.passes.length > 1 ? "s" : ""} passed
154
+ `):S.message.includes("Target page, context or browser has been closed")?new Error(`
155
+ \u274C CRITICAL: Browser/page was closed unexpectedly!
156
+ This usually means:
157
+ - The test timeout was too short
158
+ - The browser crashed
159
+ - An external process killed the browser`):S}finally{R&&await R.close()}return{passes:ce,failures:te,skipped:J,warnings:ae}}var Le,je,Cs,Wt=L(()=>{"use strict";Le=require("fs"),je=j(require("path"),1);et();Ut();Dt();qt();Ht();tt();Cs={}});async function _t(c,e,t={}){if(!c||typeof c!="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 r={violations:[]};async function s(f){try{let m=await fetch(f,{method:"HEAD",signal:AbortSignal.timeout(1e3)});if(m.ok||m.status===304)return f}catch{return null}return null}let i=be(t.strictness),o={},a=typeof process<"u"?process.cwd():"";if(typeof process<"u"&&typeof process.cwd=="function")try{let{loadConfig:f}=await Promise.resolve().then(()=>(ke(),_e)),m=await f(process.cwd());if(o=m.config,m.configPath&&(a=Jt.default.dirname(m.configPath)),t.strictness===void 0){let h=o.test?.components?.find(P=>P?.name===c)?.strictness;i=be(h??o.test?.strictness)}}catch{t.strictness===void 0&&(i="balanced")}let l;try{if(e){let f=await s(e);if(f){console.log(`\u{1F3AD} Running Playwright tests on ${f}`);let{runContractTestsPlaywright:m}=await Promise.resolve().then(()=>(Wt(),Nt));l=await m(c,f,i,o,a)}else throw new Error(`\u274C Dev server not running at ${e}
160
+ 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(f){throw f instanceof Error?f:new Error(`\u274C Contract test execution failed: ${String(f)}`)}let p={violations:r.violations,raw:r,contract:l};if(l.failures.length>0&&e==="Playwright")throw new Error(`
161
+ \u274C ${l.failures.length} accessibility contract test${l.failures.length>1?"s":""} failed (Playwright mode)
162
+ \u2705 ${l.passes.length} test${l.passes.length>1?"s":""} passed
2843
163
 
2844
164
  \u{1F4CB} Review the detailed test report above for specific failures.
2845
- \u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`
2846
- );
2847
- }
2848
- if (results.violations.length > 0) {
2849
- const violationCount = results.violations.length;
2850
- const violationDetails = results.violations.map(
2851
- (v) => `
2852
- - ${v.id}: ${v.description}
2853
- Impact: ${v.impact}
2854
- Affected elements: ${v.nodes.length}
2855
- Help: ${v.helpUrl}`
2856
- ).join("\n");
2857
- throw new Error(
2858
- `
2859
- \u274C ${violationCount} axe accessibility violation${violationCount > 1 ? "s" : ""} detected
2860
- ${violationDetails}
2861
-
2862
- \u{1F4CB} Full details available in result.violations`
2863
- );
2864
- }
2865
- return result;
2866
- }
2867
- async function cleanupTests() {
2868
- await closeSharedBrowser();
2869
- }
2870
- var import_jest_axe, import_path6, runTest;
2871
- var init_test2 = __esm({
2872
- "src/utils/test/src/test.ts"() {
2873
- "use strict";
2874
- import_jest_axe = require("jest-axe");
2875
- init_contractTestRunner();
2876
- init_playwrightTestHarness();
2877
- init_strictness();
2878
- import_path6 = __toESM(require("path"), 1);
2879
- runTest = async () => {
2880
- return {
2881
- passes: [],
2882
- failures: [],
2883
- skipped: []
2884
- };
2885
- };
2886
- if (typeof window === "undefined") {
2887
- runTest = async () => {
2888
- console.log(`\u{1F680} Running component accessibility tests...
2889
- `);
2890
- const { exec } = await import("child_process");
2891
- const chalk4 = (await import("chalk")).default;
2892
- return new Promise((resolve, reject) => {
2893
- exec(
2894
- `npx vitest --run --reporter verbose`,
2895
- async (error, stdout, stderr) => {
2896
- console.log(stdout);
2897
- if (stderr) console.error(stderr);
2898
- const testsPassed = !error || error.code === 0;
2899
- if (testsPassed) {
2900
- try {
2901
- const { displayBadgeInfo: displayBadgeInfo2, promptAddBadge: promptAddBadge2 } = await Promise.resolve().then(() => (init_badgeHelper(), badgeHelper_exports));
2902
- displayBadgeInfo2("component");
2903
- await promptAddBadge2("component", process.cwd());
2904
- console.log(chalk4.dim("\n" + "\u2500".repeat(60)));
2905
- console.log(chalk4.cyan("\u{1F499} Found aria-ease helpful?"));
2906
- console.log(chalk4.white(" \u2022 Star us on GitHub: ") + chalk4.blue.underline("https://github.com/aria-ease/aria-ease"));
2907
- console.log(chalk4.white(" \u2022 Share feedback: ") + chalk4.blue.underline("https://github.com/aria-ease/aria-ease/discussions"));
2908
- console.log(chalk4.dim("\u2500".repeat(60) + "\n"));
2909
- } catch (badgeError) {
2910
- console.error("Warning: Could not display badge prompt:", badgeError);
2911
- }
2912
- resolve({ passes: [], failures: [], skipped: [] });
2913
- process.exit(0);
2914
- } else {
2915
- const exitCode = error?.code || 1;
2916
- reject(new Error(`Tests failed with code ${exitCode}`));
2917
- process.exit(exitCode);
2918
- }
2919
- }
2920
- );
2921
- });
2922
- };
2923
- }
2924
- }
2925
- });
2926
-
2927
- // src/utils/test/index.ts
2928
- var test_exports2 = {};
2929
- __export(test_exports2, {
2930
- cleanupTests: () => cleanupTests,
2931
- runTest: () => runTest,
2932
- testUiComponent: () => testUiComponent
2933
- });
2934
- var init_test3 = __esm({
2935
- "src/utils/test/index.ts"() {
2936
- "use strict";
2937
- init_test2();
2938
- }
2939
- });
2940
-
2941
- // src/utils/test/dsl/src/contractValidator.ts
2942
- function validateContractSchema(contract) {
2943
- const errors = [];
2944
- if (!contract || typeof contract !== "object") {
2945
- return {
2946
- valid: false,
2947
- errors: [{ path: "$", message: "Contract must be an object" }]
2948
- };
2949
- }
2950
- const c = contract;
2951
- if (!c.selectors) {
2952
- errors.push({ path: "$.selectors", message: "selectors is required" });
2953
- } else if (typeof c.selectors !== "object" || c.selectors === null || Array.isArray(c.selectors)) {
2954
- errors.push({ path: "$.selectors", message: "selectors must be an object" });
2955
- } else {
2956
- const selectors = c.selectors;
2957
- Object.entries(selectors).forEach(([key, value]) => {
2958
- if (typeof value !== "string") {
2959
- errors.push({ path: `$.selectors['${key}']`, message: "All selectors must be strings" });
2960
- }
2961
- });
2962
- }
2963
- if (!Array.isArray(c.static)) {
2964
- errors.push({ path: "$.static", message: "static must be an array" });
2965
- } else {
2966
- c.static.forEach((item, idx) => {
2967
- if (typeof item !== "object" || item === null) {
2968
- errors.push({ path: `$.static[${idx}]`, message: "static item must be an object" });
2969
- return;
2970
- }
2971
- const staticItem = item;
2972
- if (!Array.isArray(staticItem.assertions)) {
2973
- errors.push({
2974
- path: `$.static[${idx}].assertions`,
2975
- message: "assertions must be an array"
2976
- });
2977
- return;
2978
- }
2979
- staticItem.assertions.forEach((assertion, assertIdx) => {
2980
- if (typeof assertion !== "object" || assertion === null) {
2981
- errors.push({
2982
- path: `$.static[${idx}].assertions[${assertIdx}]`,
2983
- message: "assertion must be an object"
2984
- });
2985
- return;
2986
- }
2987
- const a = assertion;
2988
- if (typeof a.target !== "string") {
2989
- errors.push({
2990
- path: `$.static[${idx}].assertions[${assertIdx}].target`,
2991
- message: "target is required and must be a string"
2992
- });
2993
- }
2994
- if (typeof a.attribute !== "string") {
2995
- errors.push({
2996
- path: `$.static[${idx}].assertions[${assertIdx}].attribute`,
2997
- message: "attribute is required and must be a string"
2998
- });
2999
- }
3000
- if (typeof a.failureMessage !== "string") {
3001
- errors.push({
3002
- path: `$.static[${idx}].assertions[${assertIdx}].failureMessage`,
3003
- message: "failureMessage is required and must be a string"
3004
- });
3005
- }
3006
- if (a.level !== void 0 && !["required", "recommended", "optional"].includes(a.level)) {
3007
- errors.push({
3008
- path: `$.static[${idx}].assertions[${assertIdx}].level`,
3009
- message: "level must be one of: required, recommended, optional"
3010
- });
3011
- }
3012
- });
3013
- });
3014
- }
3015
- if (!Array.isArray(c.dynamic)) {
3016
- errors.push({ path: "$.dynamic", message: "dynamic must be an array" });
3017
- } else {
3018
- c.dynamic.forEach((item, idx) => {
3019
- if (typeof item !== "object" || item === null) {
3020
- errors.push({ path: `$.dynamic[${idx}]`, message: "dynamic item must be an object" });
3021
- return;
3022
- }
3023
- const dynamicItem = item;
3024
- if (typeof dynamicItem.description !== "string") {
3025
- errors.push({
3026
- path: `$.dynamic[${idx}].description`,
3027
- message: "description is required and must be a string"
3028
- });
3029
- }
3030
- if (!Array.isArray(dynamicItem.action)) {
3031
- errors.push({
3032
- path: `$.dynamic[${idx}].action`,
3033
- message: "action is required and must be an array"
3034
- });
3035
- } else {
3036
- dynamicItem.action.forEach((action, actIdx) => {
3037
- if (typeof action !== "object" || action === null) {
3038
- errors.push({
3039
- path: `$.dynamic[${idx}].action[${actIdx}]`,
3040
- message: "action item must be an object"
3041
- });
3042
- return;
3043
- }
3044
- const a = action;
3045
- if (typeof a.type !== "string") {
3046
- errors.push({
3047
- path: `$.dynamic[${idx}].action[${actIdx}].type`,
3048
- message: "type is required and must be a string"
3049
- });
3050
- }
3051
- if (typeof a.target !== "string") {
3052
- errors.push({
3053
- path: `$.dynamic[${idx}].action[${actIdx}].target`,
3054
- message: "target is required and must be a string"
3055
- });
3056
- }
3057
- });
3058
- }
3059
- if (!Array.isArray(dynamicItem.assertions)) {
3060
- errors.push({
3061
- path: `$.dynamic[${idx}].assertions`,
3062
- message: "assertions is required and must be an array"
3063
- });
3064
- } else {
3065
- dynamicItem.assertions.forEach((assertion, assertIdx) => {
3066
- if (typeof assertion !== "object" || assertion === null) {
3067
- errors.push({
3068
- path: `$.dynamic[${idx}].assertions[${assertIdx}]`,
3069
- message: "assertion must be an object"
3070
- });
3071
- return;
3072
- }
3073
- const a = assertion;
3074
- if (typeof a.target !== "string") {
3075
- errors.push({
3076
- path: `$.dynamic[${idx}].assertions[${assertIdx}].target`,
3077
- message: "target is required and must be a string"
3078
- });
3079
- }
3080
- if (typeof a.assertion !== "string") {
3081
- errors.push({
3082
- path: `$.dynamic[${idx}].assertions[${assertIdx}].assertion`,
3083
- message: "assertion is required and must be a string"
3084
- });
3085
- }
3086
- if (a.level !== void 0 && !["required", "recommended", "optional"].includes(a.level)) {
3087
- errors.push({
3088
- path: `$.dynamic[${idx}].assertions[${assertIdx}].level`,
3089
- message: "level must be one of: required, recommended, optional"
3090
- });
3091
- }
3092
- });
3093
- }
3094
- });
3095
- }
3096
- if (c.relationships !== void 0) {
3097
- if (!Array.isArray(c.relationships)) {
3098
- errors.push({ path: "$.relationships", message: "relationships must be an array" });
3099
- } else {
3100
- c.relationships.forEach((rel, idx) => {
3101
- if (typeof rel !== "object" || rel === null) {
3102
- errors.push({ path: `$.relationships[${idx}]`, message: "relationship must be an object" });
3103
- return;
3104
- }
3105
- const r = rel;
3106
- const type = r.type;
3107
- if (!["aria-reference", "contains"].includes(type)) {
3108
- errors.push({
3109
- path: `$.relationships[${idx}].type`,
3110
- message: "type must be one of: aria-reference, contains"
3111
- });
3112
- }
3113
- if (type === "aria-reference") {
3114
- if (typeof r.from !== "string") {
3115
- errors.push({
3116
- path: `$.relationships[${idx}].from`,
3117
- message: "from is required for aria-reference and must be a string"
3118
- });
3119
- }
3120
- if (typeof r.attribute !== "string") {
3121
- errors.push({
3122
- path: `$.relationships[${idx}].attribute`,
3123
- message: "attribute is required for aria-reference and must be a string"
3124
- });
3125
- }
3126
- if (typeof r.to !== "string") {
3127
- errors.push({
3128
- path: `$.relationships[${idx}].to`,
3129
- message: "to is required for aria-reference and must be a string"
3130
- });
3131
- }
3132
- } else if (type === "contains") {
3133
- if (typeof r.parent !== "string") {
3134
- errors.push({
3135
- path: `$.relationships[${idx}].parent`,
3136
- message: "parent is required for contains and must be a string"
3137
- });
3138
- }
3139
- if (typeof r.child !== "string") {
3140
- errors.push({
3141
- path: `$.relationships[${idx}].child`,
3142
- message: "child is required for contains and must be a string"
3143
- });
3144
- }
3145
- }
3146
- });
3147
- }
3148
- }
3149
- if (c.states !== void 0) {
3150
- if (!Array.isArray(c.states)) {
3151
- errors.push({ path: "$.states", message: "states must be an array" });
3152
- } else {
3153
- c.states.forEach((state, idx) => {
3154
- if (typeof state !== "object" || state === null) {
3155
- errors.push({ path: `$.states[${idx}]`, message: "state must be an object" });
3156
- return;
3157
- }
3158
- const s = state;
3159
- if (typeof s.name !== "string") {
3160
- errors.push({ path: `$.states[${idx}].name`, message: "name is required and must be a string" });
3161
- }
3162
- if (!Array.isArray(s.requires)) {
3163
- errors.push({ path: `$.states[${idx}].requires`, message: "requires is required and must be an array" });
3164
- }
3165
- });
3166
- }
3167
- }
3168
- return {
3169
- valid: errors.length === 0,
3170
- errors
3171
- };
3172
- }
3173
- function validateRelationshipReferences(contract, selectorKeys) {
3174
- const errors = [];
3175
- if (!contract || typeof contract !== "object") {
3176
- return errors;
3177
- }
3178
- const c = contract;
3179
- const relationships = c.relationships;
3180
- if (!Array.isArray(relationships)) {
3181
- return errors;
3182
- }
3183
- relationships.forEach((rel, idx) => {
3184
- const type = rel.type;
3185
- if (type === "aria-reference") {
3186
- const from = rel.from;
3187
- const to = rel.to;
3188
- if (from && !selectorKeys.has(from)) {
3189
- errors.push({
3190
- path: `$.relationships[${idx}].from`,
3191
- message: `Selector '${from}' not found in selectors`
3192
- });
3193
- }
3194
- if (to && !selectorKeys.has(to)) {
3195
- errors.push({
3196
- path: `$.relationships[${idx}].to`,
3197
- message: `Selector '${to}' not found in selectors`
3198
- });
3199
- }
3200
- } else if (type === "contains") {
3201
- const parent = rel.parent;
3202
- const child = rel.child;
3203
- if (parent && !selectorKeys.has(parent)) {
3204
- errors.push({
3205
- path: `$.relationships[${idx}].parent`,
3206
- message: `Selector '${parent}' not found in selectors`
3207
- });
3208
- }
3209
- if (child && !selectorKeys.has(child)) {
3210
- errors.push({
3211
- path: `$.relationships[${idx}].child`,
3212
- message: `Selector '${child}' not found in selectors`
3213
- });
3214
- }
3215
- }
3216
- });
3217
- return errors;
3218
- }
3219
- function validateTargetReferences(contract, selectorKeys) {
3220
- const errors = [];
3221
- if (!contract || typeof contract !== "object") {
3222
- return errors;
3223
- }
3224
- const c = contract;
3225
- const staticItems = c.static;
3226
- if (Array.isArray(staticItems)) {
3227
- staticItems.forEach((item, itemIdx) => {
3228
- const assertions = item.assertions;
3229
- if (Array.isArray(assertions)) {
3230
- assertions.forEach((assertion, assertIdx) => {
3231
- const target = assertion.target;
3232
- if (target && !selectorKeys.has(target)) {
3233
- errors.push({
3234
- path: `$.static[${itemIdx}].assertions[${assertIdx}].target`,
3235
- message: `Selector '${target}' not found in selectors`
3236
- });
3237
- }
3238
- });
3239
- }
3240
- });
3241
- }
3242
- const dynamicItems = c.dynamic;
3243
- const states = c.states;
3244
- const stateNames = new Set((states || []).map((s) => String(s.name)));
3245
- if (Array.isArray(dynamicItems)) {
3246
- dynamicItems.forEach((item, itemIdx) => {
3247
- const given = item.given;
3248
- if (given && !stateNames.has(given)) {
3249
- errors.push({
3250
- path: `$.dynamic[${itemIdx}].given`,
3251
- message: `State '${given}' not found in states`
3252
- });
3253
- }
3254
- const actions = item.action;
3255
- if (Array.isArray(actions)) {
3256
- actions.forEach((action, actIdx) => {
3257
- const target = action.target;
3258
- if (target && target !== "document" && !selectorKeys.has(target)) {
3259
- errors.push({
3260
- path: `$.dynamic[${itemIdx}].action[${actIdx}].target`,
3261
- message: `Selector '${target}' not found in selectors (or use 'document')`
3262
- });
3263
- }
3264
- });
3265
- }
3266
- const assertions = item.assertions;
3267
- if (Array.isArray(assertions)) {
3268
- assertions.forEach((assertion, assertIdx) => {
3269
- const target = assertion.target;
3270
- if (target && target !== "relative" && !selectorKeys.has(target)) {
3271
- errors.push({
3272
- path: `$.dynamic[${itemIdx}].assertions[${assertIdx}].target`,
3273
- message: `Selector '${target}' not found in selectors (or use 'relative')`
3274
- });
3275
- }
3276
- });
3277
- }
3278
- });
3279
- }
3280
- return errors;
3281
- }
3282
- var init_contractValidator = __esm({
3283
- "src/utils/test/dsl/src/contractValidator.ts"() {
3284
- "use strict";
3285
- }
3286
- });
3287
-
3288
- // src/utils/test/dsl/src/buildContracts.ts
3289
- var buildContracts_exports = {};
3290
- __export(buildContracts_exports, {
3291
- buildContracts: () => buildContracts
3292
- });
3293
- async function buildContracts(cwd, config) {
3294
- const errors = [];
3295
- const built = [];
3296
- if (!config.contracts || config.contracts.length === 0) {
3297
- console.log(import_chalk2.default.yellow('\u2139\uFE0F No contracts configured. Add "contracts" array to ariaease.config.js'));
3298
- return { success: true, built, errors };
3299
- }
3300
- try {
3301
- console.log(import_chalk2.default.cyanBright("\n\u{1F3D7}\uFE0F Building contracts...\n"));
3302
- for (const contractSource of config.contracts) {
3303
- const srcPattern = import_path7.default.resolve(cwd, contractSource.src);
3304
- const outDir = contractSource.out ? import_path7.default.resolve(cwd, contractSource.out) : void 0;
3305
- console.log(import_chalk2.default.gray(` Pattern: ${contractSource.src}`));
3306
- if (outDir) {
3307
- console.log(import_chalk2.default.gray(` Output: ${contractSource.out}
3308
- `));
3309
- } else {
3310
- console.log(import_chalk2.default.gray(` Output: Same directory as source
3311
- `));
3312
- }
3313
- const contractFiles = [];
3314
- for await (const file of (0, import_promises2.glob)(srcPattern, { cwd })) {
3315
- contractFiles.push(file);
3316
- }
3317
- if (contractFiles.length === 0) {
3318
- console.log(import_chalk2.default.yellow(`\u26A0\uFE0F No contract files found matching pattern: ${contractSource.src}`));
3319
- continue;
3320
- }
3321
- if (outDir) {
3322
- await import_fs_extra3.default.ensureDir(outDir);
3323
- }
3324
- for (const contractFile of contractFiles) {
3325
- try {
3326
- const absolutePath = contractFile.startsWith("/") ? contractFile : import_path7.default.resolve(cwd, contractFile);
3327
- const module2 = await import(`file://${absolutePath}`);
3328
- let contract = module2.default;
3329
- if (!contract) {
3330
- const namedExports = Object.entries(module2).find(
3331
- ([, value]) => value && typeof value === "object" && "toJSON" in value
3332
- );
3333
- if (namedExports) {
3334
- contract = namedExports[1];
3335
- }
3336
- }
3337
- if (!contract || typeof contract.toJSON !== "function") {
3338
- errors.push(`${import_path7.default.basename(contractFile)}: No contract with toJSON() method found`);
3339
- continue;
3340
- }
3341
- const json = contract.toJSON();
3342
- const schemaValidation = validateContractSchema(json);
3343
- if (!schemaValidation.valid) {
3344
- const errorLines = schemaValidation.errors.map((err) => ` ${import_chalk2.default.red("\u2717")} ${err.path}: ${err.message}`).join("\n");
3345
- const errorMsg = `Schema validation failed:
3346
- ${errorLines}`;
3347
- errors.push(`${import_path7.default.basename(contractFile)}: ${errorMsg}`);
3348
- console.log(import_chalk2.default.red(`\u274C ${import_path7.default.basename(contractFile)}`));
3349
- console.log(import_chalk2.default.red(` ${errorMsg}`));
3350
- continue;
3351
- }
3352
- const selectorKeys = /* @__PURE__ */ new Set();
3353
- if (json && typeof json === "object" && "selectors" in json) {
3354
- const selectors = json.selectors;
3355
- Object.keys(selectors).forEach((key) => selectorKeys.add(key));
3356
- }
3357
- const refErrors = [
3358
- ...validateRelationshipReferences(json, selectorKeys),
3359
- ...validateTargetReferences(json, selectorKeys)
3360
- ];
3361
- if (refErrors.length > 0) {
3362
- const errorLines = refErrors.map((err) => ` ${import_chalk2.default.red("\u2717")} ${err.path}: ${err.message}`).join("\n");
3363
- const errorMsg = `Reference validation failed:
3364
- ${errorLines}`;
3365
- errors.push(`${import_path7.default.basename(contractFile)}: ${errorMsg}`);
3366
- console.log(import_chalk2.default.red(`\u274C ${import_path7.default.basename(contractFile)}`));
3367
- console.log(import_chalk2.default.red(` ${errorMsg}`));
3368
- continue;
3369
- }
3370
- const contractName = import_path7.default.basename(contractFile, import_path7.default.extname(contractFile));
3371
- const outputFile = outDir ? import_path7.default.join(outDir, `${contractName}.json`) : import_path7.default.join(import_path7.default.dirname(absolutePath), `${contractName}.json`);
3372
- await import_fs_extra3.default.writeFile(outputFile, JSON.stringify(json, null, 2) + "\n", "utf-8");
3373
- const relativePath = import_path7.default.relative(cwd, outputFile);
3374
- built.push(relativePath);
3375
- console.log(import_chalk2.default.green(`\u2705 ${import_path7.default.basename(contractFile)} \u2192 ${import_path7.default.basename(outputFile)}`));
3376
- } catch (error) {
3377
- const errorMsg = error instanceof Error ? error.message : String(error);
3378
- const filename = import_path7.default.basename(contractFile);
3379
- errors.push(`${filename}: ${errorMsg}`);
3380
- console.log(import_chalk2.default.red(`\u274C ${filename}`));
3381
- console.log(import_chalk2.default.red(` Error: ${errorMsg}`));
3382
- }
3383
- }
3384
- }
3385
- console.log("");
3386
- if (errors.length === 0) {
3387
- console.log(import_chalk2.default.green(`\u2705 Built ${built.length} contract${built.length !== 1 ? "s" : ""} successfully
3388
- `));
3389
- return { success: true, built, errors };
3390
- } else {
3391
- console.log(import_chalk2.default.yellow(`\u26A0\uFE0F Built ${built.length} contracts with ${errors.length} error${errors.length !== 1 ? "s" : ""}
3392
- `));
3393
- return { success: false, built, errors };
3394
- }
3395
- } catch (error) {
3396
- const errorMsg = error instanceof Error ? error.message : String(error);
3397
- const msg = `Failed to build contracts: ${errorMsg}`;
3398
- errors.push(msg);
3399
- console.log(import_chalk2.default.red(`\u274C ${msg}
3400
- `));
3401
- return { success: false, built, errors };
3402
- }
3403
- }
3404
- var import_path7, import_fs_extra3, import_promises2, import_chalk2;
3405
- var init_buildContracts = __esm({
3406
- "src/utils/test/dsl/src/buildContracts.ts"() {
3407
- "use strict";
3408
- import_path7 = __toESM(require("path"), 1);
3409
- import_fs_extra3 = __toESM(require("fs-extra"), 1);
3410
- import_promises2 = require("fs/promises");
3411
- import_chalk2 = __toESM(require("chalk"), 1);
3412
- init_contractValidator();
3413
- }
3414
- });
3415
-
3416
- // src/utils/cli/cli.ts
3417
- var import_commander = require("commander");
3418
- var import_chalk3 = __toESM(require("chalk"), 1);
3419
- var import_path8 = __toESM(require("path"), 1);
3420
- var import_fs_extra4 = __toESM(require("fs-extra"), 1);
3421
- init_configLoader();
3422
- init_badgeHelper();
3423
- var program = new import_commander.Command();
3424
- program.name("aria-ease").description("Run accessibility tests and audits").version("2.2.3");
3425
- program.command("audit").description("Run axe-core powered accessibility audit on webpages").option("-u, --url <url>", "Single URL to audit").option("-f, --format <format>", "Output format for the audit report: json | csv | html | all", "all").option("-o, --out <path>", "Directory to save the audit report", "./accessibility-reports/audit").action(async (opts) => {
3426
- console.log(import_chalk3.default.cyanBright("\u{1F680} Starting accessibility audit...\n"));
3427
- const { runAudit: runAudit2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
3428
- const { formatResults: formatResults2 } = await Promise.resolve().then(() => (init_formatters(), formatters_exports));
3429
- const needsConfig = !opts.url;
3430
- const { config, configPath, errors } = await loadConfig(process.cwd());
3431
- if (configPath) {
3432
- console.log(import_chalk3.default.green(`\u2705 Loaded config from ${import_path8.default.basename(configPath)}
3433
- `));
3434
- } else if (errors.length > 0 && needsConfig) {
3435
- console.log(import_chalk3.default.red("\u274C Config file errors:\n"));
3436
- errors.forEach((err) => console.log(import_chalk3.default.red(` ${err}`)));
3437
- console.log("");
3438
- process.exit(1);
3439
- } else if (errors.length > 0) {
3440
- console.log(import_chalk3.default.yellow("\u26A0\uFE0F Config file has errors (ignored, using CLI options)\n"));
3441
- } else if (!configPath && needsConfig) {
3442
- console.log(import_chalk3.default.yellow("\u2139\uFE0F No config file found, using CLI options.\n"));
3443
- }
3444
- const urls = [];
3445
- if (opts.url) {
3446
- urls.push(opts.url);
3447
- } else if (config.audit?.urls && Array.isArray(config.audit.urls)) {
3448
- urls.push(...config.audit.urls);
3449
- }
3450
- const format = config.audit?.output && config.audit.output.format || opts.format;
3451
- if (!["json", "csv", "html", "all"].includes(format)) {
3452
- console.log(import_chalk3.default.red('\u274C Invalid format. Use "json", "csv", "html" or "all".'));
3453
- process.exit(1);
3454
- }
3455
- if (urls.length === 0) {
3456
- console.log(import_chalk3.default.red('\u274C No URLs provided. Use --url option or add "urls" in config file'));
3457
- process.exit(1);
3458
- }
3459
- const allResults = [];
3460
- const { createAuditBrowser: createAuditBrowser2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
3461
- const browser = await createAuditBrowser2();
3462
- try {
3463
- const auditOptions = { browser };
3464
- for (const url of urls) {
3465
- console.log(import_chalk3.default.yellow(`\u{1F50E} Auditing: ${url}`));
3466
- try {
3467
- const result = await runAudit2(url, auditOptions);
3468
- allResults.push({ url, result });
3469
- console.log(import_chalk3.default.green(`\u2705 Completed audit for ${url}
3470
- `));
3471
- } catch (error) {
3472
- if (error instanceof Error && error.message) {
3473
- console.log(import_chalk3.default.red(`\u274C Failed auditing ${url}: ${error.message}`));
3474
- }
3475
- }
3476
- }
3477
- } finally {
3478
- await browser.close();
3479
- }
3480
- const hasResults = allResults.some((r) => r.result && r.result.violations && r.result.violations.length > 0);
3481
- if (!hasResults) {
3482
- const auditedCount = allResults.filter((r) => r.result).length;
3483
- if (auditedCount === 0) {
3484
- console.log(import_chalk3.default.red("\u274C No pages were successfully audited."));
3485
- process.exit(1);
3486
- }
3487
- console.log(import_chalk3.default.green("\n\u{1F389} Great news! No static accessibility violations found!"));
3488
- console.log(import_chalk3.default.gray(` Audited ${auditedCount} page${auditedCount > 1 ? "s" : ""} successfully.
3489
- `));
3490
- displayBadgeInfo("audit");
3491
- await promptAddBadge("audit", process.cwd());
3492
- console.log(import_chalk3.default.dim("\n" + "\u2500".repeat(60)));
3493
- console.log(import_chalk3.default.cyan("\u{1F499} Found aria-ease helpful?"));
3494
- console.log(import_chalk3.default.white(" \u2022 Star us on GitHub: ") + import_chalk3.default.blue.underline("https://github.com/aria-ease/aria-ease"));
3495
- console.log(import_chalk3.default.white(" \u2022 Share feedback: ") + import_chalk3.default.blue.underline("https://github.com/aria-ease/aria-ease/discussions"));
3496
- console.log(import_chalk3.default.dim("\u2500".repeat(60) + "\n"));
3497
- process.exit(0);
3498
- }
3499
- async function createReport(format2) {
3500
- const formatted = formatResults2(allResults, format2);
3501
- const out = config.audit?.output && config.audit.output.out || opts.out;
3502
- await import_fs_extra4.default.ensureDir(out);
3503
- const d = /* @__PURE__ */ new Date();
3504
- const pad = (n) => String(n).padStart(2, "0");
3505
- const timestamp = `${pad(d.getDate())}-${pad(d.getMonth() + 1)}-${d.getFullYear()} ${pad(d.getHours())}h ${pad(d.getMinutes())}m ${pad(d.getSeconds())}s`;
3506
- const fileName = `ariaease-report-${timestamp}.${format2}`;
3507
- const filePath = import_path8.default.join(out, fileName);
3508
- await import_fs_extra4.default.writeFile(filePath, formatted, "utf-8");
3509
- console.log(import_chalk3.default.magentaBright(`\u{1F4C1} Report saved to ${filePath}`));
3510
- }
3511
- if (["json", "csv", "html"].includes(format)) {
3512
- await createReport(format);
3513
- } else if (format === "all") {
3514
- await Promise.all(["json", "csv", "html"].map((format2) => createReport(format2)));
3515
- }
3516
- const totalViolations = allResults.reduce((sum, r) => {
3517
- return sum + (r.result?.violations?.length || 0);
3518
- }, 0);
3519
- console.log(import_chalk3.default.red(`
3520
- \u274C Accessibility violations found!`));
3521
- console.log(import_chalk3.default.yellow(` ${totalViolations} violation${totalViolations !== 1 ? "s" : ""} detected across ${allResults.length} page${allResults.length !== 1 ? "s" : ""}.`));
3522
- console.log(import_chalk3.default.gray(` Review the generated report for details.
3523
- `));
3524
- console.log(import_chalk3.default.dim("\n" + "\u2500".repeat(60)));
3525
- console.log(import_chalk3.default.cyan("\u{1F499} Found aria-ease helpful?"));
3526
- console.log(import_chalk3.default.white(" \u2022 Star us on GitHub: ") + import_chalk3.default.blue.underline("https://github.com/aria-ease/aria-ease"));
3527
- console.log(import_chalk3.default.white(" \u2022 Share feedback: ") + import_chalk3.default.blue.underline("https://github.com/aria-ease/aria-ease/discussions"));
3528
- console.log(import_chalk3.default.dim("\u2500".repeat(60) + "\n"));
3529
- process.exit(1);
3530
- });
3531
- program.command("test").description("Run core a11y accessibility standard tests on UI components").action(async () => {
3532
- const { runTest: runTest2 } = await Promise.resolve().then(() => (init_test3(), test_exports2));
3533
- runTest2();
3534
- });
3535
- program.command("build").description("Build accessibility artifacts").addCommand(
3536
- new import_commander.Command("contracts").description("Build DSL contracts to JSON").action(async () => {
3537
- const { buildContracts: buildContracts2 } = await Promise.resolve().then(() => (init_buildContracts(), buildContracts_exports));
3538
- const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_configLoader(), configLoader_exports));
3539
- const cwd = process.cwd();
3540
- const { config, configPath, errors } = await loadConfig2(cwd);
3541
- if (configPath) {
3542
- console.log(import_chalk3.default.green(`\u2705 Loaded config from ${import_path8.default.basename(configPath)}
3543
- `));
3544
- } else if (errors.length > 0) {
3545
- console.log(import_chalk3.default.red("\u274C Config file has errors:\n"));
3546
- errors.forEach((err) => console.log(import_chalk3.default.red(` ${err}`)));
3547
- console.log("");
3548
- process.exit(1);
3549
- }
3550
- const result = await buildContracts2(cwd, config);
3551
- if (!result.success && result.errors.length > 0) {
3552
- console.log(import_chalk3.default.red(`\u274C ${result.errors.length} error${result.errors.length !== 1 ? "s" : ""} occurred during build
3553
- `));
3554
- process.exit(1);
3555
- }
3556
- if (result.built.length === 0 && (!config.contracts || config.contracts.length === 0)) {
3557
- process.exit(0);
3558
- }
3559
- if (result.built.length === 0) {
3560
- console.log(import_chalk3.default.yellow("\u26A0\uFE0F No contracts were built\n"));
3561
- process.exit(1);
3562
- }
3563
- process.exit(0);
3564
- })
3565
- );
3566
- program.command("help").description("Display help information").action(() => {
3567
- program.outputHelp();
3568
- });
3569
- program.parse(process.argv);
165
+ \u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`);if(r.violations.length>0){let f=r.violations.length,m=r.violations.map(h=>`
166
+ - ${h.id}: ${h.description}
167
+ Impact: ${h.impact}
168
+ Affected elements: ${h.nodes.length}
169
+ Help: ${h.helpUrl}`).join(`
170
+ `);throw new Error(`
171
+ \u274C ${f} axe accessibility violation${f>1?"s":""} detected
172
+ ${m}
173
+
174
+ \u{1F4CB} Full details available in result.violations`)}return p}async function zt(){await kt()}var Jt,at,Gt=L(()=>{"use strict";et();tt();Jt=j(require("path"),1);at=async()=>({passes:[],failures:[],skipped:[]});typeof window>"u"&&(at=async()=>{console.log(`\u{1F680} Running component accessibility tests...
175
+ `);let{exec:c}=await import("child_process"),e=(await import("chalk")).default;return new Promise((t,r)=>{c("npx vitest --run --reporter verbose",async(s,i,o)=>{if(console.log(i),o&&console.error(o),!s||s.code===0){try{let{displayBadgeInfo:l,promptAddBadge:p}=await Promise.resolve().then(()=>(Ke(),wt));l("component"),await p("component",process.cwd()),console.log(e.dim(`
176
+ `+"\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)+`
177
+ `))}catch(l){console.error("Warning: Could not display badge prompt:",l)}t({passes:[],failures:[],skipped:[]}),process.exit(0)}else{let l=s?.code||1;r(new Error(`Tests failed with code ${l}`)),process.exit(l)}})})})});var Yt={};K(Yt,{cleanupTests:()=>zt,runTest:()=>at,testUiComponent:()=>_t});var Kt=L(()=>{"use strict";Gt()});function Qt(c){let e=[];if(!c||typeof c!="object")return{valid:!1,errors:[{path:"$",message:"Contract must be an object"}]};let t=c;if(!t.selectors)e.push({path:"$.selectors",message:"selectors is required"});else if(typeof t.selectors!="object"||t.selectors===null||Array.isArray(t.selectors))e.push({path:"$.selectors",message:"selectors must be an object"});else{let r=t.selectors;Object.entries(r).forEach(([s,i])=>{typeof i!="string"&&e.push({path:`$.selectors['${s}']`,message:"All selectors must be strings"})})}return Array.isArray(t.static)?t.static.forEach((r,s)=>{if(typeof r!="object"||r===null){e.push({path:`$.static[${s}]`,message:"static item must be an object"});return}let i=r;if(!Array.isArray(i.assertions)){e.push({path:`$.static[${s}].assertions`,message:"assertions must be an array"});return}i.assertions.forEach((o,a)=>{if(typeof o!="object"||o===null){e.push({path:`$.static[${s}].assertions[${a}]`,message:"assertion must be an object"});return}let l=o;typeof l.target!="string"&&e.push({path:`$.static[${s}].assertions[${a}].target`,message:"target is required and must be a string"}),typeof l.attribute!="string"&&e.push({path:`$.static[${s}].assertions[${a}].attribute`,message:"attribute is required and must be a string"}),typeof l.failureMessage!="string"&&e.push({path:`$.static[${s}].assertions[${a}].failureMessage`,message:"failureMessage is required and must be a string"}),l.level!==void 0&&!["required","recommended","optional"].includes(l.level)&&e.push({path:`$.static[${s}].assertions[${a}].level`,message:"level must be one of: required, recommended, optional"})})}):e.push({path:"$.static",message:"static must be an array"}),Array.isArray(t.dynamic)?t.dynamic.forEach((r,s)=>{if(typeof r!="object"||r===null){e.push({path:`$.dynamic[${s}]`,message:"dynamic item must be an object"});return}let i=r;typeof i.description!="string"&&e.push({path:`$.dynamic[${s}].description`,message:"description is required and must be a string"}),Array.isArray(i.action)?i.action.forEach((o,a)=>{if(typeof o!="object"||o===null){e.push({path:`$.dynamic[${s}].action[${a}]`,message:"action item must be an object"});return}let l=o;typeof l.type!="string"&&e.push({path:`$.dynamic[${s}].action[${a}].type`,message:"type is required and must be a string"}),typeof l.target!="string"&&e.push({path:`$.dynamic[${s}].action[${a}].target`,message:"target is required and must be a string"})}):e.push({path:`$.dynamic[${s}].action`,message:"action is required and must be an array"}),Array.isArray(i.assertions)?i.assertions.forEach((o,a)=>{if(typeof o!="object"||o===null){e.push({path:`$.dynamic[${s}].assertions[${a}]`,message:"assertion must be an object"});return}let l=o;typeof l.target!="string"&&e.push({path:`$.dynamic[${s}].assertions[${a}].target`,message:"target is required and must be a string"}),typeof l.assertion!="string"&&e.push({path:`$.dynamic[${s}].assertions[${a}].assertion`,message:"assertion is required and must be a string"}),l.level!==void 0&&!["required","recommended","optional"].includes(l.level)&&e.push({path:`$.dynamic[${s}].assertions[${a}].level`,message:"level must be one of: required, recommended, optional"})}):e.push({path:`$.dynamic[${s}].assertions`,message:"assertions is required and must be an array"})}):e.push({path:"$.dynamic",message:"dynamic must be an array"}),t.relationships!==void 0&&(Array.isArray(t.relationships)?t.relationships.forEach((r,s)=>{if(typeof r!="object"||r===null){e.push({path:`$.relationships[${s}]`,message:"relationship must be an object"});return}let i=r,o=i.type;["aria-reference","contains"].includes(o)||e.push({path:`$.relationships[${s}].type`,message:"type must be one of: aria-reference, contains"}),o==="aria-reference"?(typeof i.from!="string"&&e.push({path:`$.relationships[${s}].from`,message:"from is required for aria-reference and must be a string"}),typeof i.attribute!="string"&&e.push({path:`$.relationships[${s}].attribute`,message:"attribute is required for aria-reference and must be a string"}),typeof i.to!="string"&&e.push({path:`$.relationships[${s}].to`,message:"to is required for aria-reference and must be a string"})):o==="contains"&&(typeof i.parent!="string"&&e.push({path:`$.relationships[${s}].parent`,message:"parent is required for contains and must be a string"}),typeof i.child!="string"&&e.push({path:`$.relationships[${s}].child`,message:"child is required for contains and must be a string"}))}):e.push({path:"$.relationships",message:"relationships must be an array"})),{valid:e.length===0,errors:e}}function Xt(c,e){let t=[];if(!c||typeof c!="object")return t;let s=c.relationships;return Array.isArray(s)&&s.forEach((i,o)=>{let a=i.type;if(a==="aria-reference"){let l=i.from,p=i.to;l&&!e.has(l)&&t.push({path:`$.relationships[${o}].from`,message:`Selector '${l}' not found in selectors`}),p&&!e.has(p)&&t.push({path:`$.relationships[${o}].to`,message:`Selector '${p}' not found in selectors`})}else if(a==="contains"){let l=i.parent,p=i.child;l&&!e.has(l)&&t.push({path:`$.relationships[${o}].parent`,message:`Selector '${l}' not found in selectors`}),p&&!e.has(p)&&t.push({path:`$.relationships[${o}].child`,message:`Selector '${p}' not found in selectors`})}}),t}function Zt(c,e){let t=[];if(!c||typeof c!="object")return t;let r=c,s=r.static;Array.isArray(s)&&s.forEach((o,a)=>{let l=o.assertions;Array.isArray(l)&&l.forEach((p,f)=>{let m=p.target;m&&!e.has(m)&&t.push({path:`$.static[${a}].assertions[${f}].target`,message:`Selector '${m}' not found in selectors`})})});let i=r.dynamic;return Array.isArray(i)&&i.forEach((o,a)=>{let l=o.action;Array.isArray(l)&&l.forEach((f,m)=>{let h=f.target;h&&h!=="document"&&!e.has(h)&&t.push({path:`$.dynamic[${a}].action[${m}].target`,message:`Selector '${h}' not found in selectors (or use 'document')`})});let p=o.assertions;Array.isArray(p)&&p.forEach((f,m)=>{let h=f.target;h&&h!=="relative"&&!e.has(h)&&t.push({path:`$.dynamic[${a}].assertions[${m}].target`,message:`Selector '${h}' not found in selectors (or use 'relative')`})})}),t}var es=L(()=>{"use strict"});var ss={};K(ss,{buildContracts:()=>ks});async function ks(c,e){let t=[],r=[];if(!e.contracts||e.contracts.length===0)return console.log(V.default.yellow('\u2139\uFE0F No contracts configured. Add "contracts" array to ariaease.config.js')),{success:!0,built:r,errors:t};try{console.log(V.default.cyanBright(`
178
+ \u{1F3D7}\uFE0F Building contracts...
179
+ `));for(let s of e.contracts){let i=D.default.resolve(c,s.src),o=s.out?D.default.resolve(c,s.out):void 0;console.log(V.default.gray(` Pattern: ${s.src}`)),console.log(o?V.default.gray(` Output: ${s.out}
180
+ `):V.default.gray(` Output: Same directory as source
181
+ `));let a=[];for await(let l of(0,ts.glob)(i,{cwd:c}))a.push(l);if(a.length===0){console.log(V.default.yellow(`\u26A0\uFE0F No contract files found matching pattern: ${s.src}`));continue}o&&await ct.default.ensureDir(o);for(let l of a)try{let p=l.startsWith("/")?l:D.default.resolve(c,l),f=await import(`file://${p}`),m=f.default;if(!m){let _=Object.entries(f).find(([,z])=>z&&typeof z=="object"&&"toJSON"in z);_&&(m=_[1])}if(!m||typeof m.toJSON!="function"){t.push(`${D.default.basename(l)}: No contract with toJSON() method found`);continue}let h=m.toJSON(),P=Qt(h);if(!P.valid){let z=`Schema validation failed:
182
+ ${P.errors.map(v=>` ${V.default.red("\u2717")} ${v.path}: ${v.message}`).join(`
183
+ `)}`;t.push(`${D.default.basename(l)}: ${z}`),console.log(V.default.red(`\u274C ${D.default.basename(l)}`)),console.log(V.default.red(` ${z}`));continue}let q=new Set;if(h&&typeof h=="object"&&"selectors"in h){let _=h.selectors;Object.keys(_).forEach(z=>q.add(z))}let M=[...Xt(h,q),...Zt(h,q)];if(M.length>0){let z=`Reference validation failed:
184
+ ${M.map(v=>` ${V.default.red("\u2717")} ${v.path}: ${v.message}`).join(`
185
+ `)}`;t.push(`${D.default.basename(l)}: ${z}`),console.log(V.default.red(`\u274C ${D.default.basename(l)}`)),console.log(V.default.red(` ${z}`));continue}let U=D.default.basename(l,D.default.extname(l)),N=o?D.default.join(o,`${U}.json`):D.default.join(D.default.dirname(p),`${U}.json`);await ct.default.writeFile(N,JSON.stringify(h,null,2)+`
186
+ `,"utf-8");let W=D.default.relative(c,N);r.push(W),console.log(V.default.green(`\u2705 ${D.default.basename(l)} \u2192 ${D.default.basename(N)}`))}catch(p){let f=p instanceof Error?p.message:String(p),m=D.default.basename(l);t.push(`${m}: ${f}`),console.log(V.default.red(`\u274C ${m}`)),console.log(V.default.red(` Error: ${f}`))}}return console.log(""),t.length===0?(console.log(V.default.green(`\u2705 Built ${r.length} contract${r.length!==1?"s":""} successfully
187
+ `)),{success:!0,built:r,errors:t}):(console.log(V.default.yellow(`\u26A0\uFE0F Built ${r.length} contracts with ${t.length} error${t.length!==1?"s":""}
188
+ `)),{success:!1,built:r,errors:t})}catch(s){let o=`Failed to build contracts: ${s instanceof Error?s.message:String(s)}`;return t.push(o),console.log(V.default.red(`\u274C ${o}
189
+ `)),{success:!1,built:r,errors:t}}}var D,ct,ts,V,rs=L(()=>{"use strict";D=j(require("path"),1),ct=j(require("fs-extra"),1),ts=require("fs/promises"),V=j(require("chalk"),1);es()});var ut=require("commander"),$=j(require("chalk"),1),Ie=j(require("path"),1),lt=j(require("fs-extra"),1);ke();Ke();var ue=new ut.Command;ue.name("aria-ease").description("Run accessibility tests and audits").version("2.2.3");ue.command("audit").description("Run axe-core powered accessibility audit on webpages").option("-u, --url <url>","Single URL to audit").option("-f, --format <format>","Output format for the audit report: json | csv | html | all","all").option("-o, --out <path>","Directory to save the audit report","./accessibility-reports/audit").action(async c=>{console.log($.default.cyanBright(`\u{1F680} Starting accessibility audit...
190
+ `));let{runAudit:e}=await Promise.resolve().then(()=>(Ze(),Xe)),{formatResults:t}=await Promise.resolve().then(()=>(St(),$t)),r=!c.url,{config:s,configPath:i,errors:o}=await Je(process.cwd());i?console.log($.default.green(`\u2705 Loaded config from ${Ie.default.basename(i)}
191
+ `)):o.length>0&&r?(console.log($.default.red(`\u274C Config file errors:
192
+ `)),o.forEach(M=>console.log($.default.red(` ${M}`))),console.log(""),process.exit(1)):o.length>0?console.log($.default.yellow(`\u26A0\uFE0F Config file has errors (ignored, using CLI options)
193
+ `)):!i&&r&&console.log($.default.yellow(`\u2139\uFE0F No config file found, using CLI options.
194
+ `));let a=[];c.url?a.push(c.url):s.audit?.urls&&Array.isArray(s.audit.urls)&&a.push(...s.audit.urls);let l=s.audit?.output&&s.audit.output.format||c.format;["json","csv","html","all"].includes(l)||(console.log($.default.red('\u274C Invalid format. Use "json", "csv", "html" or "all".')),process.exit(1)),a.length===0&&(console.log($.default.red('\u274C No URLs provided. Use --url option or add "urls" in config file')),process.exit(1));let p=[],{createAuditBrowser:f}=await Promise.resolve().then(()=>(Ze(),Xe)),m=await f();try{let M={browser:m};for(let U of a){console.log($.default.yellow(`\u{1F50E} Auditing: ${U}`));try{let N=await e(U,M);p.push({url:U,result:N}),console.log($.default.green(`\u2705 Completed audit for ${U}
195
+ `))}catch(N){N instanceof Error&&N.message&&console.log($.default.red(`\u274C Failed auditing ${U}: ${N.message}`))}}}finally{await m.close()}if(!p.some(M=>M.result&&M.result.violations&&M.result.violations.length>0)){let M=p.filter(U=>U.result).length;M===0&&(console.log($.default.red("\u274C No pages were successfully audited.")),process.exit(1)),console.log($.default.green(`
196
+ \u{1F389} Great news! No static accessibility violations found!`)),console.log($.default.gray(` Audited ${M} page${M>1?"s":""} successfully.
197
+ `)),Ge("audit"),await Ye("audit",process.cwd()),console.log($.default.dim(`
198
+ `+"\u2500".repeat(60))),console.log($.default.cyan("\u{1F499} Found aria-ease helpful?")),console.log($.default.white(" \u2022 Star us on GitHub: ")+$.default.blue.underline("https://github.com/aria-ease/aria-ease")),console.log($.default.white(" \u2022 Share feedback: ")+$.default.blue.underline("https://github.com/aria-ease/aria-ease/discussions")),console.log($.default.dim("\u2500".repeat(60)+`
199
+ `)),process.exit(0)}async function P(M){let U=t(p,M),N=s.audit?.output&&s.audit.output.out||c.out;await lt.default.ensureDir(N);let W=new Date,_=Fe=>String(Fe).padStart(2,"0"),v=`ariaease-report-${`${_(W.getDate())}-${_(W.getMonth()+1)}-${W.getFullYear()} ${_(W.getHours())}h ${_(W.getMinutes())}m ${_(W.getSeconds())}s`}.${M}`,Se=Ie.default.join(N,v);await lt.default.writeFile(Se,U,"utf-8"),console.log($.default.magentaBright(`\u{1F4C1} Report saved to ${Se}`))}["json","csv","html"].includes(l)?await P(l):l==="all"&&await Promise.all(["json","csv","html"].map(M=>P(M)));let q=p.reduce((M,U)=>M+(U.result?.violations?.length||0),0);console.log($.default.red(`
200
+ \u274C Accessibility violations found!`)),console.log($.default.yellow(` ${q} violation${q!==1?"s":""} detected across ${p.length} page${p.length!==1?"s":""}.`)),console.log($.default.gray(` Review the generated report for details.
201
+ `)),console.log($.default.dim(`
202
+ `+"\u2500".repeat(60))),console.log($.default.cyan("\u{1F499} Found aria-ease helpful?")),console.log($.default.white(" \u2022 Star us on GitHub: ")+$.default.blue.underline("https://github.com/aria-ease/aria-ease")),console.log($.default.white(" \u2022 Share feedback: ")+$.default.blue.underline("https://github.com/aria-ease/aria-ease/discussions")),console.log($.default.dim("\u2500".repeat(60)+`
203
+ `)),process.exit(1)});ue.command("test").description("Run core a11y accessibility standard tests on UI components").action(async()=>{let{runTest:c}=await Promise.resolve().then(()=>(Kt(),Yt));c()});ue.command("build").description("Build accessibility artifacts").addCommand(new ut.Command("contracts").description("Build DSL contracts to JSON").action(async()=>{let{buildContracts:c}=await Promise.resolve().then(()=>(rs(),ss)),{loadConfig:e}=await Promise.resolve().then(()=>(ke(),_e)),t=process.cwd(),{config:r,configPath:s,errors:i}=await e(t);s?console.log($.default.green(`\u2705 Loaded config from ${Ie.default.basename(s)}
204
+ `)):i.length>0&&(console.log($.default.red(`\u274C Config file has errors:
205
+ `)),i.forEach(a=>console.log($.default.red(` ${a}`))),console.log(""),process.exit(1));let o=await c(t,r);!o.success&&o.errors.length>0&&(console.log($.default.red(`\u274C ${o.errors.length} error${o.errors.length!==1?"s":""} occurred during build
206
+ `)),process.exit(1)),o.built.length===0&&(!r.contracts||r.contracts.length===0)&&process.exit(0),o.built.length===0&&(console.log($.default.yellow(`\u26A0\uFE0F No contracts were built
207
+ `)),process.exit(1)),process.exit(0)}));ue.command("help").description("Display help information").action(()=>{ue.outputHelp()});ue.parse(process.argv);