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