aria-ease 6.2.0 → 6.2.2

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 (38) hide show
  1. package/README.md +5 -17
  2. package/bin/{audit-5JJ3XK46.js → audit-RM6TCZ5C.js} +17 -5
  3. package/bin/cli.cjs +260 -133
  4. package/bin/cli.js +18 -15
  5. package/bin/{contractTestRunnerPlaywright-HL2VPEEV.js → contractTestRunnerPlaywright-ACAWN34W.js} +227 -115
  6. package/bin/{test-HH2EW2NM.js → test-A3ESFXOR.js} +1 -1
  7. package/dist/{contractTestRunnerPlaywright-EXEBWWPC.js → contractTestRunnerPlaywright-O7FF7GV4.js} +227 -115
  8. package/dist/index.cjs +229 -116
  9. package/dist/index.d.cts +10 -0
  10. package/dist/index.d.ts +10 -0
  11. package/dist/index.js +3 -2
  12. package/dist/src/{Types.d-CBuuHF3d.d.cts → Types.d-CRjhbrcw.d.cts} +11 -1
  13. package/dist/src/{Types.d-CBuuHF3d.d.ts → Types.d-CRjhbrcw.d.ts} +11 -1
  14. package/dist/src/accordion/index.d.cts +1 -1
  15. package/dist/src/accordion/index.d.ts +1 -1
  16. package/dist/src/block/index.d.cts +1 -1
  17. package/dist/src/block/index.d.ts +1 -1
  18. package/dist/src/checkbox/index.d.cts +1 -1
  19. package/dist/src/checkbox/index.d.ts +1 -1
  20. package/dist/src/combobox/index.cjs +1 -1
  21. package/dist/src/combobox/index.d.cts +1 -1
  22. package/dist/src/combobox/index.d.ts +1 -1
  23. package/dist/src/combobox/index.js +1 -1
  24. package/dist/src/menu/index.d.cts +1 -1
  25. package/dist/src/menu/index.d.ts +1 -1
  26. package/dist/src/radio/index.cjs +1 -0
  27. package/dist/src/radio/index.d.cts +1 -1
  28. package/dist/src/radio/index.d.ts +1 -1
  29. package/dist/src/radio/index.js +1 -0
  30. package/dist/src/toggle/index.d.cts +1 -1
  31. package/dist/src/toggle/index.d.ts +1 -1
  32. package/dist/src/utils/test/{contractTestRunnerPlaywright-LJHY3AB4.js → contractTestRunnerPlaywright-7BPRTIN4.js} +227 -115
  33. package/dist/src/utils/test/contracts/AccordionContract.json +1 -0
  34. package/dist/src/utils/test/contracts/ComboboxContract.json +1 -0
  35. package/dist/src/utils/test/contracts/MenuContract.json +1 -0
  36. package/dist/src/utils/test/index.cjs +227 -115
  37. package/dist/src/utils/test/index.js +1 -1
  38. package/package.json +1 -1
package/bin/cli.js CHANGED
@@ -128,7 +128,7 @@ var program = new Command();
128
128
  program.name("aria-ease").description("Run accessibility tests and audits").version("2.2.3");
129
129
  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) => {
130
130
  console.log(chalk.cyanBright("\u{1F680} Starting accessibility audit...\n"));
131
- const { runAudit } = await import("./audit-5JJ3XK46.js");
131
+ const { runAudit } = await import("./audit-RM6TCZ5C.js");
132
132
  const { formatResults } = await import("./formatters-32KQIIYS.js");
133
133
  const { config, configPath, errors } = await loadConfig(process.cwd());
134
134
  if (configPath) {
@@ -155,22 +155,25 @@ program.command("audit").description("Run axe-core powered accessibility audit o
155
155
  process.exit(1);
156
156
  }
157
157
  const allResults = [];
158
- const auditOptions = {
159
- timeout: config.audit?.timeout,
160
- waitUntil: config.audit?.waitUntil
161
- };
162
- for (const url of urls) {
163
- console.log(chalk.yellow(`\u{1F50E} Auditing: ${url}`));
164
- try {
165
- const result = await runAudit(url, auditOptions);
166
- allResults.push({ url, result });
167
- console.log(chalk.green(`\u2705 Completed audit for ${url}
158
+ const { createAuditBrowser } = await import("./audit-RM6TCZ5C.js");
159
+ const browser = await createAuditBrowser();
160
+ try {
161
+ const auditOptions = { browser };
162
+ for (const url of urls) {
163
+ console.log(chalk.yellow(`\u{1F50E} Auditing: ${url}`));
164
+ try {
165
+ const result = await runAudit(url, auditOptions);
166
+ allResults.push({ url, result });
167
+ console.log(chalk.green(`\u2705 Completed audit for ${url}
168
168
  `));
169
- } catch (error) {
170
- if (error instanceof Error && error.message) {
171
- console.log(chalk.red(`\u274C Failed auditing ${url}: ${error.message}`));
169
+ } catch (error) {
170
+ if (error instanceof Error && error.message) {
171
+ console.log(chalk.red(`\u274C Failed auditing ${url}: ${error.message}`));
172
+ }
172
173
  }
173
174
  }
175
+ } finally {
176
+ await browser.close();
174
177
  }
175
178
  const hasResults = allResults.some((r) => r.result && r.result.violations && r.result.violations.length > 0);
176
179
  if (!hasResults) {
@@ -204,7 +207,7 @@ program.command("audit").description("Run axe-core powered accessibility audit o
204
207
  console.log(chalk.green("\n\u{1F389} All audits completed."));
205
208
  });
206
209
  program.command("test").description("Run core a11y accessibility standard tests on UI components").action(async () => {
207
- const { runTest } = await import("./test-HH2EW2NM.js");
210
+ const { runTest } = await import("./test-A3ESFXOR.js");
208
211
  runTest();
209
212
  });
210
213
  program.command("help").description("Display help information").action(() => {
@@ -21,6 +21,11 @@ import { default as default2 } from "playwright/test";
21
21
  import { readFileSync } from "fs";
22
22
  async function runContractTestsPlaywright(componentName, url) {
23
23
  const reporter = new ContractReporter(true);
24
+ const actionTimeoutMs = 400;
25
+ const assertionTimeoutMs = 400;
26
+ function isBrowserClosedError(error) {
27
+ return error instanceof Error && error.message.includes("Target page, context or browser has been closed");
28
+ }
24
29
  const contractTyped = contract_default;
25
30
  const contractPath = contractTyped[componentName]?.path;
26
31
  if (!contractPath) {
@@ -39,17 +44,29 @@ async function runContractTestsPlaywright(componentName, url) {
39
44
  try {
40
45
  page = await createTestPage();
41
46
  if (url) {
42
- await page.goto(url, {
43
- waitUntil: "domcontentloaded",
44
- timeout: 3e4
45
- });
47
+ try {
48
+ await page.goto(url, {
49
+ waitUntil: "domcontentloaded",
50
+ timeout: 3e4
51
+ });
52
+ } catch (error) {
53
+ throw new Error(
54
+ `Failed to navigate to ${url}. Ensure dev server is running and accessible. Original error: ${error instanceof Error ? error.message : String(error)}`
55
+ );
56
+ }
46
57
  await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
47
58
  }
48
59
  const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
49
60
  if (!mainSelector) {
50
- throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
61
+ throw new Error(`CRITICAL: No main selector (trigger, input, or container) found in contract for ${componentName}`);
62
+ }
63
+ try {
64
+ await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
65
+ } catch (error) {
66
+ throw new Error(
67
+ `CRITICAL: Component element '${mainSelector}' not found on page within 30s. This usually means the component didn't render or the contract selector is incorrect. Original error: ${error instanceof Error ? error.message : String(error)}`
68
+ );
51
69
  }
52
- await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
53
70
  if (componentName === "menu" && componentContract.selectors.trigger) {
54
71
  await page.locator(componentContract.selectors.trigger).first().waitFor({
55
72
  state: "visible",
@@ -129,6 +146,13 @@ async function runContractTestsPlaywright(componentName, url) {
129
146
  }
130
147
  }
131
148
  for (const dynamicTest of componentContract.dynamic || []) {
149
+ if (!page || page.isClosed()) {
150
+ console.warn(`
151
+ \u26A0\uFE0F Browser closed - skipping remaining ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests
152
+ `);
153
+ failures.push(`CRITICAL: Browser/page closed before completing all tests. ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests skipped.`);
154
+ break;
155
+ }
132
156
  const { action, assertions } = dynamicTest;
133
157
  const failuresBeforeTest = failures.length;
134
158
  if (componentContract.selectors.popup) {
@@ -148,16 +172,16 @@ async function runContractTestsPlaywright(componentName, url) {
148
172
  const closeElement = page.locator(closeSelector).first();
149
173
  await closeElement.focus();
150
174
  await page.keyboard.press("Escape");
151
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
175
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
152
176
  }
153
177
  if (!menuClosed && componentContract.selectors.trigger) {
154
178
  const triggerElement = page.locator(componentContract.selectors.trigger).first();
155
- await triggerElement.click();
156
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
179
+ await triggerElement.click({ timeout: actionTimeoutMs });
180
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
157
181
  }
158
182
  if (!menuClosed) {
159
183
  await page.mouse.click(10, 10);
160
- menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
184
+ menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
161
185
  }
162
186
  if (!menuClosed) {
163
187
  throw new Error(
@@ -186,9 +210,9 @@ This indicates a problem with the menu component's close functionality.`
186
210
  const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
187
211
  const triggerPanel = await trigger.getAttribute("aria-controls");
188
212
  if (isExpanded && triggerPanel) {
189
- await trigger.click();
213
+ await trigger.click({ timeout: actionTimeoutMs });
190
214
  const panel = page.locator(`#${triggerPanel}`);
191
- await (0, test_exports.expect)(panel).toBeHidden({ timeout: 1e3 }).catch(() => {
215
+ await (0, test_exports.expect)(panel).toBeHidden({ timeout: assertionTimeoutMs }).catch(() => {
192
216
  });
193
217
  }
194
218
  }
@@ -227,134 +251,192 @@ This indicates a problem with the menu component's close functionality.`
227
251
  continue;
228
252
  }
229
253
  for (const act of action) {
254
+ if (!page || page.isClosed()) {
255
+ failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
256
+ break;
257
+ }
230
258
  if (act.type === "focus") {
231
- const focusSelector = componentContract.selectors[act.target];
232
- if (!focusSelector) {
233
- failures.push(`Selector for focus target ${act.target} not found.`);
259
+ try {
260
+ const focusSelector = componentContract.selectors[act.target];
261
+ if (!focusSelector) {
262
+ failures.push(`Selector for focus target ${act.target} not found.`);
263
+ continue;
264
+ }
265
+ await page.locator(focusSelector).first().focus({ timeout: actionTimeoutMs });
266
+ } catch (error) {
267
+ if (isBrowserClosedError(error)) {
268
+ failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
269
+ break;
270
+ }
271
+ failures.push(`Failed to focus ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
234
272
  continue;
235
273
  }
236
- await page.locator(focusSelector).first().focus();
237
274
  }
238
275
  if (act.type === "type" && act.value) {
239
- const typeSelector = componentContract.selectors[act.target];
240
- if (!typeSelector) {
241
- failures.push(`Selector for type target ${act.target} not found.`);
276
+ try {
277
+ const typeSelector = componentContract.selectors[act.target];
278
+ if (!typeSelector) {
279
+ failures.push(`Selector for type target ${act.target} not found.`);
280
+ continue;
281
+ }
282
+ await page.locator(typeSelector).first().fill(act.value, { timeout: actionTimeoutMs });
283
+ } catch (error) {
284
+ if (isBrowserClosedError(error)) {
285
+ failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
286
+ break;
287
+ }
288
+ failures.push(`Failed to type into ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
242
289
  continue;
243
290
  }
244
- await page.locator(typeSelector).first().fill(act.value);
245
291
  }
246
292
  if (act.type === "click") {
247
- if (act.target === "document") {
248
- await page.mouse.click(10, 10);
249
- } else if (act.target === "relative" && act.relativeTarget) {
250
- const relativeSelector = componentContract.selectors.relative;
251
- if (!relativeSelector) {
252
- failures.push(`Relative selector not defined for click action.`);
253
- continue;
254
- }
255
- const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
256
- if (!relativeElement) {
257
- failures.push(`Could not resolve relative target ${act.relativeTarget} for click.`);
258
- continue;
293
+ try {
294
+ if (act.target === "document") {
295
+ await page.mouse.click(10, 10);
296
+ } else if (act.target === "relative" && act.relativeTarget) {
297
+ const relativeSelector = componentContract.selectors.relative;
298
+ if (!relativeSelector) {
299
+ failures.push(`Relative selector not defined for click action.`);
300
+ continue;
301
+ }
302
+ const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
303
+ if (!relativeElement) {
304
+ failures.push(`Could not resolve relative target ${act.relativeTarget} for click.`);
305
+ continue;
306
+ }
307
+ await relativeElement.click({ timeout: actionTimeoutMs });
308
+ } else {
309
+ const actionSelector = componentContract.selectors[act.target];
310
+ if (!actionSelector) {
311
+ failures.push(`Selector for action target ${act.target} not found.`);
312
+ continue;
313
+ }
314
+ await page.locator(actionSelector).first().click({ timeout: actionTimeoutMs });
259
315
  }
260
- await relativeElement.click();
261
- } else {
262
- const actionSelector = componentContract.selectors[act.target];
263
- if (!actionSelector) {
264
- failures.push(`Selector for action target ${act.target} not found.`);
265
- continue;
316
+ } catch (error) {
317
+ if (isBrowserClosedError(error)) {
318
+ failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
319
+ break;
266
320
  }
267
- await page.locator(actionSelector).first().click();
321
+ failures.push(`Failed to click ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
322
+ continue;
268
323
  }
269
324
  }
270
325
  if (act.type === "keypress" && act.key) {
271
- const keyMap = {
272
- "Space": "Space",
273
- "Enter": "Enter",
274
- "Escape": "Escape",
275
- "Arrow Up": "ArrowUp",
276
- "Arrow Down": "ArrowDown",
277
- "Arrow Left": "ArrowLeft",
278
- "Arrow Right": "ArrowRight",
279
- "Home": "Home",
280
- "End": "End",
281
- "Tab": "Tab"
282
- };
283
- let keyValue = keyMap[act.key] || act.key;
284
- if (keyValue === "Space") {
285
- keyValue = " ";
286
- } else if (keyValue.includes(" ")) {
287
- keyValue = keyValue.replace(/ /g, "");
288
- }
289
- if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
290
- await page.keyboard.press(keyValue);
291
- } else {
292
- const keypressSelector = componentContract.selectors[act.target];
293
- if (!keypressSelector) {
294
- failures.push(`Selector for keypress target ${act.target} not found.`);
295
- continue;
326
+ try {
327
+ const keyMap = {
328
+ "Space": "Space",
329
+ "Enter": "Enter",
330
+ "Escape": "Escape",
331
+ "Arrow Up": "ArrowUp",
332
+ "Arrow Down": "ArrowDown",
333
+ "Arrow Left": "ArrowLeft",
334
+ "Arrow Right": "ArrowRight",
335
+ "Home": "Home",
336
+ "End": "End",
337
+ "Tab": "Tab"
338
+ };
339
+ let keyValue = keyMap[act.key] || act.key;
340
+ if (keyValue === "Space") {
341
+ keyValue = " ";
342
+ } else if (keyValue.includes(" ")) {
343
+ keyValue = keyValue.replace(/ /g, "");
296
344
  }
297
- const target = page.locator(keypressSelector).first();
298
- const elementCount = await target.count();
299
- if (elementCount === 0) {
300
- reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
345
+ if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
346
+ await page.keyboard.press(keyValue);
347
+ } else {
348
+ const keypressSelector = componentContract.selectors[act.target];
349
+ if (!keypressSelector) {
350
+ failures.push(`Selector for keypress target ${act.target} not found.`);
351
+ continue;
352
+ }
353
+ const target = page.locator(keypressSelector).first();
354
+ const elementCount = await target.count();
355
+ if (elementCount === 0) {
356
+ reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
357
+ break;
358
+ }
359
+ await target.press(keyValue, { timeout: actionTimeoutMs });
360
+ }
361
+ } catch (error) {
362
+ if (isBrowserClosedError(error)) {
363
+ failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
301
364
  break;
302
365
  }
303
- await target.press(keyValue);
366
+ failures.push(`Failed to press ${act.key} on ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
367
+ continue;
304
368
  }
305
369
  }
306
370
  if (act.type === "hover") {
307
- if (act.target === "relative" && act.relativeTarget) {
371
+ try {
372
+ if (act.target === "relative" && act.relativeTarget) {
373
+ const relativeSelector = componentContract.selectors.relative;
374
+ if (!relativeSelector) {
375
+ failures.push(`Relative selector not defined for hover action.`);
376
+ continue;
377
+ }
378
+ const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
379
+ if (!relativeElement) {
380
+ failures.push(`Could not resolve relative target ${act.relativeTarget} for hover.`);
381
+ continue;
382
+ }
383
+ await relativeElement.hover({ timeout: actionTimeoutMs });
384
+ } else {
385
+ const hoverSelector = componentContract.selectors[act.target];
386
+ if (!hoverSelector) {
387
+ failures.push(`Selector for hover target ${act.target} not found.`);
388
+ continue;
389
+ }
390
+ await page.locator(hoverSelector).first().hover({ timeout: actionTimeoutMs });
391
+ }
392
+ } catch (error) {
393
+ if (isBrowserClosedError(error)) {
394
+ failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
395
+ break;
396
+ }
397
+ failures.push(`Failed to hover ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
398
+ continue;
399
+ }
400
+ }
401
+ }
402
+ for (const assertion of assertions) {
403
+ if (!page || page.isClosed()) {
404
+ failures.push(`CRITICAL: Browser/page closed before completing all tests. Increase test timeout or reduce test complexity.`);
405
+ break;
406
+ }
407
+ let target;
408
+ try {
409
+ if (assertion.target === "relative") {
308
410
  const relativeSelector = componentContract.selectors.relative;
309
411
  if (!relativeSelector) {
310
- failures.push(`Relative selector not defined for hover action.`);
412
+ failures.push("Relative selector is not defined in the contract.");
311
413
  continue;
312
414
  }
313
- const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
314
- if (!relativeElement) {
315
- failures.push(`Could not resolve relative target ${act.relativeTarget} for hover.`);
415
+ const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
416
+ if (!relativeTargetValue) {
417
+ failures.push("Relative target or expected value is not defined.");
316
418
  continue;
317
419
  }
318
- await relativeElement.hover();
420
+ target = await resolveRelativeTarget(relativeSelector, relativeTargetValue);
319
421
  } else {
320
- const hoverSelector = componentContract.selectors[act.target];
321
- if (!hoverSelector) {
322
- failures.push(`Selector for hover target ${act.target} not found.`);
422
+ const assertionSelector = componentContract.selectors[assertion.target];
423
+ if (!assertionSelector) {
424
+ failures.push(`Selector for assertion target ${assertion.target} not found.`);
323
425
  continue;
324
426
  }
325
- await page.locator(hoverSelector).first().hover();
427
+ target = page.locator(assertionSelector).first();
326
428
  }
327
- }
328
- }
329
- for (const assertion of assertions) {
330
- let target;
331
- if (assertion.target === "relative") {
332
- const relativeSelector = componentContract.selectors.relative;
333
- if (!relativeSelector) {
334
- failures.push("Relative selector is not defined in the contract.");
335
- continue;
336
- }
337
- const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
338
- if (!relativeTargetValue) {
339
- failures.push("Relative target or expected value is not defined.");
429
+ if (!target) {
430
+ failures.push(`Target ${assertion.target} not found.`);
340
431
  continue;
341
432
  }
342
- target = await resolveRelativeTarget(relativeSelector, relativeTargetValue);
343
- } else {
344
- const assertionSelector = componentContract.selectors[assertion.target];
345
- if (!assertionSelector) {
346
- failures.push(`Selector for assertion target ${assertion.target} not found.`);
347
- continue;
348
- }
349
- target = page.locator(assertionSelector).first();
350
- }
351
- if (!target) {
352
- failures.push(`Target ${assertion.target} not found.`);
433
+ } catch (error) {
434
+ failures.push(`Failed to resolve target ${assertion.target}: ${error instanceof Error ? error.message : String(error)}`);
353
435
  continue;
354
436
  }
355
437
  if (assertion.assertion === "toBeVisible") {
356
438
  try {
357
- await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
439
+ await (0, test_exports.expect)(target).toBeVisible({ timeout: assertionTimeoutMs });
358
440
  passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
359
441
  } catch {
360
442
  const debugState = await page.evaluate((sel) => {
@@ -368,7 +450,7 @@ This indicates a problem with the menu component's close functionality.`
368
450
  }
369
451
  if (assertion.assertion === "notToBeVisible") {
370
452
  try {
371
- await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
453
+ await (0, test_exports.expect)(target).toBeHidden({ timeout: assertionTimeoutMs });
372
454
  passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
373
455
  } catch {
374
456
  const debugState = await page.evaluate((sel) => {
@@ -390,7 +472,7 @@ This indicates a problem with the menu component's close functionality.`
390
472
  failures.push(assertion.failureMessage + ` ${assertion.target} "${assertion.attribute}" should not be empty, found "${attributeValue}".`);
391
473
  }
392
474
  } else {
393
- await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: 3e3 });
475
+ await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: assertionTimeoutMs });
394
476
  passes.push(`${assertion.target} has expected "${assertion.attribute}". Test: "${dynamicTest.description}".`);
395
477
  }
396
478
  } catch {
@@ -420,7 +502,7 @@ This indicates a problem with the menu component's close functionality.`
420
502
  }
421
503
  if (assertion.assertion === "toHaveFocus") {
422
504
  try {
423
- await (0, test_exports.expect)(target).toBeFocused({ timeout: 5e3 });
505
+ await (0, test_exports.expect)(target).toBeFocused({ timeout: assertionTimeoutMs });
424
506
  passes.push(`${assertion.target} has focus as expected. Test: "${dynamicTest.description}".`);
425
507
  } catch {
426
508
  const actualFocus = await page.evaluate(() => {
@@ -455,18 +537,48 @@ This indicates a problem with the menu component's close functionality.`
455
537
  reporter.summary(failures);
456
538
  } catch (error) {
457
539
  if (error instanceof Error) {
458
- if (error.message.includes("Executable doesn't exist")) {
459
- console.error("\n\u274C Playwright browsers not found!\n");
540
+ if (error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch")) {
541
+ console.error("\n\u274C CRITICAL: Playwright browsers not found!\n");
460
542
  console.log("\u{1F4E6} Run: npx playwright install chromium\n");
461
- failures.push("Playwright browser not installed. Run: npx playwright install chromium");
462
- } else if (error.message.includes("net::ERR_CONNECTION_REFUSED")) {
463
- console.error("\n\u274C Cannot connect to dev server!\n");
543
+ failures.push("CRITICAL: Playwright browser not installed. Run: npx playwright install chromium");
544
+ } else if (error.message.includes("net::ERR_CONNECTION_REFUSED") || error.message.includes("NS_ERROR_CONNECTION_REFUSED")) {
545
+ console.error("\n\u274C CRITICAL: Cannot connect to dev server!\n");
464
546
  console.log(` Make sure your dev server is running at ${url}
465
547
  `);
466
- failures.push(`Dev server not running at ${url}`);
548
+ failures.push(`CRITICAL: Dev server not running at ${url}`);
549
+ } else if (error.message.includes("Timeout") && error.message.includes("waitFor")) {
550
+ console.error("\n\u274C CRITICAL: Component not found on page!\n");
551
+ console.log(` The component selector could not be found within 30 seconds.
552
+ `);
553
+ console.log(` This usually means:
554
+ `);
555
+ console.log(` - The component didn't render
556
+ `);
557
+ console.log(` - The URL is incorrect
558
+ `);
559
+ console.log(` - The component selector in the contract is wrong
560
+ `);
561
+ failures.push(`CRITICAL: Component element not found on page - ${error.message}`);
562
+ } else if (error.message.includes("Target page, context or browser has been closed")) {
563
+ console.error("\n\u274C CRITICAL: Browser/page was closed unexpectedly!\n");
564
+ console.log(` This usually means:
565
+ `);
566
+ console.log(` - The test timeout was too short
567
+ `);
568
+ console.log(` - The browser crashed
569
+ `);
570
+ console.log(` - An external process killed the browser
571
+ `);
572
+ failures.push(`CRITICAL: Browser/page closed unexpectedly - ${error.message}`);
573
+ } else if (error.message.includes("FATAL")) {
574
+ console.error(`
575
+ ${error.message}
576
+ `);
577
+ failures.push(error.message);
467
578
  } else {
468
- console.error("\u274C Playwright test error:", error.message);
469
- failures.push(`Test error: ${error.message}`);
579
+ console.error("\n\u274C UNEXPECTED ERROR:", error.message);
580
+ console.error("Stack:", error.stack);
581
+ failures.push(`UNEXPECTED ERROR: ${error.message}`);
470
582
  }
471
583
  }
472
584
  } finally {
@@ -114,7 +114,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
114
114
  const devServerUrl = await checkDevServer(url);
115
115
  if (devServerUrl) {
116
116
  console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
117
- const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-HL2VPEEV.js");
117
+ const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-ACAWN34W.js");
118
118
  contract = await runContractTestsPlaywright(componentName, devServerUrl);
119
119
  } else {
120
120
  throw new Error(