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.
- package/README.md +5 -17
- package/bin/cli.cjs +227 -115
- package/bin/cli.js +1 -1
- package/bin/{contractTestRunnerPlaywright-HL2VPEEV.js → contractTestRunnerPlaywright-ACAWN34W.js} +227 -115
- package/bin/{test-HH2EW2NM.js → test-A3ESFXOR.js} +1 -1
- package/dist/{contractTestRunnerPlaywright-EXEBWWPC.js → contractTestRunnerPlaywright-O7FF7GV4.js} +227 -115
- package/dist/index.cjs +229 -116
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +3 -2
- package/dist/src/{Types.d-CxWrr421.d.ts → Types.d-CRjhbrcw.d.cts} +10 -0
- package/dist/src/{Types.d-CxWrr421.d.cts → Types.d-CRjhbrcw.d.ts} +10 -0
- package/dist/src/accordion/index.d.cts +1 -1
- package/dist/src/accordion/index.d.ts +1 -1
- package/dist/src/block/index.d.cts +1 -1
- package/dist/src/block/index.d.ts +1 -1
- package/dist/src/checkbox/index.d.cts +1 -1
- package/dist/src/checkbox/index.d.ts +1 -1
- package/dist/src/combobox/index.cjs +1 -1
- package/dist/src/combobox/index.d.cts +1 -1
- package/dist/src/combobox/index.d.ts +1 -1
- package/dist/src/combobox/index.js +1 -1
- package/dist/src/menu/index.d.cts +1 -1
- package/dist/src/menu/index.d.ts +1 -1
- package/dist/src/radio/index.cjs +1 -0
- package/dist/src/radio/index.d.cts +1 -1
- package/dist/src/radio/index.d.ts +1 -1
- package/dist/src/radio/index.js +1 -0
- package/dist/src/toggle/index.d.cts +1 -1
- package/dist/src/toggle/index.d.ts +1 -1
- package/dist/src/utils/test/{contractTestRunnerPlaywright-LJHY3AB4.js → contractTestRunnerPlaywright-7BPRTIN4.js} +227 -115
- package/dist/src/utils/test/contracts/AccordionContract.json +1 -0
- package/dist/src/utils/test/contracts/ComboboxContract.json +1 -0
- package/dist/src/utils/test/contracts/MenuContract.json +1 -0
- package/dist/src/utils/test/index.cjs +227 -115
- package/dist/src/utils/test/index.js +1 -1
- package/package.json +1 -1
|
@@ -321,6 +321,11 @@ __export(contractTestRunnerPlaywright_exports, {
|
|
|
321
321
|
});
|
|
322
322
|
async function runContractTestsPlaywright(componentName, url) {
|
|
323
323
|
const reporter = new ContractReporter(true);
|
|
324
|
+
const actionTimeoutMs = 400;
|
|
325
|
+
const assertionTimeoutMs = 400;
|
|
326
|
+
function isBrowserClosedError(error) {
|
|
327
|
+
return error instanceof Error && error.message.includes("Target page, context or browser has been closed");
|
|
328
|
+
}
|
|
324
329
|
const contractTyped = contract_default;
|
|
325
330
|
const contractPath = contractTyped[componentName]?.path;
|
|
326
331
|
if (!contractPath) {
|
|
@@ -339,17 +344,29 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
339
344
|
try {
|
|
340
345
|
page = await createTestPage();
|
|
341
346
|
if (url) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
347
|
+
try {
|
|
348
|
+
await page.goto(url, {
|
|
349
|
+
waitUntil: "domcontentloaded",
|
|
350
|
+
timeout: 3e4
|
|
351
|
+
});
|
|
352
|
+
} catch (error) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
`Failed to navigate to ${url}. Ensure dev server is running and accessible. Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
346
357
|
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
347
358
|
}
|
|
348
359
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
349
360
|
if (!mainSelector) {
|
|
350
|
-
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
361
|
+
throw new Error(`CRITICAL: No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
365
|
+
} catch (error) {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`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)}`
|
|
368
|
+
);
|
|
351
369
|
}
|
|
352
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
353
370
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
354
371
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
355
372
|
state: "visible",
|
|
@@ -429,6 +446,13 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
429
446
|
}
|
|
430
447
|
}
|
|
431
448
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
449
|
+
if (!page || page.isClosed()) {
|
|
450
|
+
console.warn(`
|
|
451
|
+
\u26A0\uFE0F Browser closed - skipping remaining ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests
|
|
452
|
+
`);
|
|
453
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests skipped.`);
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
432
456
|
const { action, assertions } = dynamicTest;
|
|
433
457
|
const failuresBeforeTest = failures.length;
|
|
434
458
|
if (componentContract.selectors.popup) {
|
|
@@ -448,16 +472,16 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
448
472
|
const closeElement = page.locator(closeSelector).first();
|
|
449
473
|
await closeElement.focus();
|
|
450
474
|
await page.keyboard.press("Escape");
|
|
451
|
-
menuClosed = await test.expect(popupElement).toBeHidden({ timeout:
|
|
475
|
+
menuClosed = await test.expect(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
452
476
|
}
|
|
453
477
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
454
478
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
455
|
-
await triggerElement.click();
|
|
456
|
-
menuClosed = await test.expect(popupElement).toBeHidden({ timeout:
|
|
479
|
+
await triggerElement.click({ timeout: actionTimeoutMs });
|
|
480
|
+
menuClosed = await test.expect(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
457
481
|
}
|
|
458
482
|
if (!menuClosed) {
|
|
459
483
|
await page.mouse.click(10, 10);
|
|
460
|
-
menuClosed = await test.expect(popupElement).toBeHidden({ timeout:
|
|
484
|
+
menuClosed = await test.expect(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
461
485
|
}
|
|
462
486
|
if (!menuClosed) {
|
|
463
487
|
throw new Error(
|
|
@@ -486,9 +510,9 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
486
510
|
const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
|
|
487
511
|
const triggerPanel = await trigger.getAttribute("aria-controls");
|
|
488
512
|
if (isExpanded && triggerPanel) {
|
|
489
|
-
await trigger.click();
|
|
513
|
+
await trigger.click({ timeout: actionTimeoutMs });
|
|
490
514
|
const panel = page.locator(`#${triggerPanel}`);
|
|
491
|
-
await test.expect(panel).toBeHidden({ timeout:
|
|
515
|
+
await test.expect(panel).toBeHidden({ timeout: assertionTimeoutMs }).catch(() => {
|
|
492
516
|
});
|
|
493
517
|
}
|
|
494
518
|
}
|
|
@@ -527,134 +551,192 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
527
551
|
continue;
|
|
528
552
|
}
|
|
529
553
|
for (const act of action) {
|
|
554
|
+
if (!page || page.isClosed()) {
|
|
555
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
530
558
|
if (act.type === "focus") {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
559
|
+
try {
|
|
560
|
+
const focusSelector = componentContract.selectors[act.target];
|
|
561
|
+
if (!focusSelector) {
|
|
562
|
+
failures.push(`Selector for focus target ${act.target} not found.`);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
await page.locator(focusSelector).first().focus({ timeout: actionTimeoutMs });
|
|
566
|
+
} catch (error) {
|
|
567
|
+
if (isBrowserClosedError(error)) {
|
|
568
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
failures.push(`Failed to focus ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
534
572
|
continue;
|
|
535
573
|
}
|
|
536
|
-
await page.locator(focusSelector).first().focus();
|
|
537
574
|
}
|
|
538
575
|
if (act.type === "type" && act.value) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
576
|
+
try {
|
|
577
|
+
const typeSelector = componentContract.selectors[act.target];
|
|
578
|
+
if (!typeSelector) {
|
|
579
|
+
failures.push(`Selector for type target ${act.target} not found.`);
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
await page.locator(typeSelector).first().fill(act.value, { timeout: actionTimeoutMs });
|
|
583
|
+
} catch (error) {
|
|
584
|
+
if (isBrowserClosedError(error)) {
|
|
585
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
failures.push(`Failed to type into ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
542
589
|
continue;
|
|
543
590
|
}
|
|
544
|
-
await page.locator(typeSelector).first().fill(act.value);
|
|
545
591
|
}
|
|
546
592
|
if (act.type === "click") {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
593
|
+
try {
|
|
594
|
+
if (act.target === "document") {
|
|
595
|
+
await page.mouse.click(10, 10);
|
|
596
|
+
} else if (act.target === "relative" && act.relativeTarget) {
|
|
597
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
598
|
+
if (!relativeSelector) {
|
|
599
|
+
failures.push(`Relative selector not defined for click action.`);
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
603
|
+
if (!relativeElement) {
|
|
604
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for click.`);
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
await relativeElement.click({ timeout: actionTimeoutMs });
|
|
608
|
+
} else {
|
|
609
|
+
const actionSelector = componentContract.selectors[act.target];
|
|
610
|
+
if (!actionSelector) {
|
|
611
|
+
failures.push(`Selector for action target ${act.target} not found.`);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
await page.locator(actionSelector).first().click({ timeout: actionTimeoutMs });
|
|
559
615
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
failures.push(`Selector for action target ${act.target} not found.`);
|
|
565
|
-
continue;
|
|
616
|
+
} catch (error) {
|
|
617
|
+
if (isBrowserClosedError(error)) {
|
|
618
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
619
|
+
break;
|
|
566
620
|
}
|
|
567
|
-
|
|
621
|
+
failures.push(`Failed to click ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
622
|
+
continue;
|
|
568
623
|
}
|
|
569
624
|
}
|
|
570
625
|
if (act.type === "keypress" && act.key) {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
keyValue
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
590
|
-
await page.keyboard.press(keyValue);
|
|
591
|
-
} else {
|
|
592
|
-
const keypressSelector = componentContract.selectors[act.target];
|
|
593
|
-
if (!keypressSelector) {
|
|
594
|
-
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
595
|
-
continue;
|
|
626
|
+
try {
|
|
627
|
+
const keyMap = {
|
|
628
|
+
"Space": "Space",
|
|
629
|
+
"Enter": "Enter",
|
|
630
|
+
"Escape": "Escape",
|
|
631
|
+
"Arrow Up": "ArrowUp",
|
|
632
|
+
"Arrow Down": "ArrowDown",
|
|
633
|
+
"Arrow Left": "ArrowLeft",
|
|
634
|
+
"Arrow Right": "ArrowRight",
|
|
635
|
+
"Home": "Home",
|
|
636
|
+
"End": "End",
|
|
637
|
+
"Tab": "Tab"
|
|
638
|
+
};
|
|
639
|
+
let keyValue = keyMap[act.key] || act.key;
|
|
640
|
+
if (keyValue === "Space") {
|
|
641
|
+
keyValue = " ";
|
|
642
|
+
} else if (keyValue.includes(" ")) {
|
|
643
|
+
keyValue = keyValue.replace(/ /g, "");
|
|
596
644
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
645
|
+
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
646
|
+
await page.keyboard.press(keyValue);
|
|
647
|
+
} else {
|
|
648
|
+
const keypressSelector = componentContract.selectors[act.target];
|
|
649
|
+
if (!keypressSelector) {
|
|
650
|
+
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const target = page.locator(keypressSelector).first();
|
|
654
|
+
const elementCount = await target.count();
|
|
655
|
+
if (elementCount === 0) {
|
|
656
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
await target.press(keyValue, { timeout: actionTimeoutMs });
|
|
660
|
+
}
|
|
661
|
+
} catch (error) {
|
|
662
|
+
if (isBrowserClosedError(error)) {
|
|
663
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
601
664
|
break;
|
|
602
665
|
}
|
|
603
|
-
|
|
666
|
+
failures.push(`Failed to press ${act.key} on ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
667
|
+
continue;
|
|
604
668
|
}
|
|
605
669
|
}
|
|
606
670
|
if (act.type === "hover") {
|
|
607
|
-
|
|
671
|
+
try {
|
|
672
|
+
if (act.target === "relative" && act.relativeTarget) {
|
|
673
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
674
|
+
if (!relativeSelector) {
|
|
675
|
+
failures.push(`Relative selector not defined for hover action.`);
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
679
|
+
if (!relativeElement) {
|
|
680
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for hover.`);
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
await relativeElement.hover({ timeout: actionTimeoutMs });
|
|
684
|
+
} else {
|
|
685
|
+
const hoverSelector = componentContract.selectors[act.target];
|
|
686
|
+
if (!hoverSelector) {
|
|
687
|
+
failures.push(`Selector for hover target ${act.target} not found.`);
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
await page.locator(hoverSelector).first().hover({ timeout: actionTimeoutMs });
|
|
691
|
+
}
|
|
692
|
+
} catch (error) {
|
|
693
|
+
if (isBrowserClosedError(error)) {
|
|
694
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
failures.push(`Failed to hover ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
for (const assertion of assertions) {
|
|
703
|
+
if (!page || page.isClosed()) {
|
|
704
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. Increase test timeout or reduce test complexity.`);
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
let target;
|
|
708
|
+
try {
|
|
709
|
+
if (assertion.target === "relative") {
|
|
608
710
|
const relativeSelector = componentContract.selectors.relative;
|
|
609
711
|
if (!relativeSelector) {
|
|
610
|
-
failures.push(
|
|
712
|
+
failures.push("Relative selector is not defined in the contract.");
|
|
611
713
|
continue;
|
|
612
714
|
}
|
|
613
|
-
const
|
|
614
|
-
if (!
|
|
615
|
-
failures.push(
|
|
715
|
+
const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
|
|
716
|
+
if (!relativeTargetValue) {
|
|
717
|
+
failures.push("Relative target or expected value is not defined.");
|
|
616
718
|
continue;
|
|
617
719
|
}
|
|
618
|
-
await
|
|
720
|
+
target = await resolveRelativeTarget(relativeSelector, relativeTargetValue);
|
|
619
721
|
} else {
|
|
620
|
-
const
|
|
621
|
-
if (!
|
|
622
|
-
failures.push(`Selector for
|
|
722
|
+
const assertionSelector = componentContract.selectors[assertion.target];
|
|
723
|
+
if (!assertionSelector) {
|
|
724
|
+
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
623
725
|
continue;
|
|
624
726
|
}
|
|
625
|
-
|
|
727
|
+
target = page.locator(assertionSelector).first();
|
|
626
728
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
for (const assertion of assertions) {
|
|
630
|
-
let target;
|
|
631
|
-
if (assertion.target === "relative") {
|
|
632
|
-
const relativeSelector = componentContract.selectors.relative;
|
|
633
|
-
if (!relativeSelector) {
|
|
634
|
-
failures.push("Relative selector is not defined in the contract.");
|
|
729
|
+
if (!target) {
|
|
730
|
+
failures.push(`Target ${assertion.target} not found.`);
|
|
635
731
|
continue;
|
|
636
732
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
failures.push("Relative target or expected value is not defined.");
|
|
640
|
-
continue;
|
|
641
|
-
}
|
|
642
|
-
target = await resolveRelativeTarget(relativeSelector, relativeTargetValue);
|
|
643
|
-
} else {
|
|
644
|
-
const assertionSelector = componentContract.selectors[assertion.target];
|
|
645
|
-
if (!assertionSelector) {
|
|
646
|
-
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
647
|
-
continue;
|
|
648
|
-
}
|
|
649
|
-
target = page.locator(assertionSelector).first();
|
|
650
|
-
}
|
|
651
|
-
if (!target) {
|
|
652
|
-
failures.push(`Target ${assertion.target} not found.`);
|
|
733
|
+
} catch (error) {
|
|
734
|
+
failures.push(`Failed to resolve target ${assertion.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
653
735
|
continue;
|
|
654
736
|
}
|
|
655
737
|
if (assertion.assertion === "toBeVisible") {
|
|
656
738
|
try {
|
|
657
|
-
await test.expect(target).toBeVisible({ timeout:
|
|
739
|
+
await test.expect(target).toBeVisible({ timeout: assertionTimeoutMs });
|
|
658
740
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
659
741
|
} catch {
|
|
660
742
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -668,7 +750,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
668
750
|
}
|
|
669
751
|
if (assertion.assertion === "notToBeVisible") {
|
|
670
752
|
try {
|
|
671
|
-
await test.expect(target).toBeHidden({ timeout:
|
|
753
|
+
await test.expect(target).toBeHidden({ timeout: assertionTimeoutMs });
|
|
672
754
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
673
755
|
} catch {
|
|
674
756
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -690,7 +772,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
690
772
|
failures.push(assertion.failureMessage + ` ${assertion.target} "${assertion.attribute}" should not be empty, found "${attributeValue}".`);
|
|
691
773
|
}
|
|
692
774
|
} else {
|
|
693
|
-
await test.expect(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout:
|
|
775
|
+
await test.expect(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: assertionTimeoutMs });
|
|
694
776
|
passes.push(`${assertion.target} has expected "${assertion.attribute}". Test: "${dynamicTest.description}".`);
|
|
695
777
|
}
|
|
696
778
|
} catch {
|
|
@@ -720,7 +802,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
720
802
|
}
|
|
721
803
|
if (assertion.assertion === "toHaveFocus") {
|
|
722
804
|
try {
|
|
723
|
-
await test.expect(target).toBeFocused({ timeout:
|
|
805
|
+
await test.expect(target).toBeFocused({ timeout: assertionTimeoutMs });
|
|
724
806
|
passes.push(`${assertion.target} has focus as expected. Test: "${dynamicTest.description}".`);
|
|
725
807
|
} catch {
|
|
726
808
|
const actualFocus = await page.evaluate(() => {
|
|
@@ -755,18 +837,48 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
755
837
|
reporter.summary(failures);
|
|
756
838
|
} catch (error) {
|
|
757
839
|
if (error instanceof Error) {
|
|
758
|
-
if (error.message.includes("Executable doesn't exist")) {
|
|
759
|
-
console.error("\n\u274C Playwright browsers not found!\n");
|
|
840
|
+
if (error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch")) {
|
|
841
|
+
console.error("\n\u274C CRITICAL: Playwright browsers not found!\n");
|
|
760
842
|
console.log("\u{1F4E6} Run: npx playwright install chromium\n");
|
|
761
|
-
failures.push("Playwright browser not installed. Run: npx playwright install chromium");
|
|
762
|
-
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED")) {
|
|
763
|
-
console.error("\n\u274C Cannot connect to dev server!\n");
|
|
843
|
+
failures.push("CRITICAL: Playwright browser not installed. Run: npx playwright install chromium");
|
|
844
|
+
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED") || error.message.includes("NS_ERROR_CONNECTION_REFUSED")) {
|
|
845
|
+
console.error("\n\u274C CRITICAL: Cannot connect to dev server!\n");
|
|
764
846
|
console.log(` Make sure your dev server is running at ${url}
|
|
765
847
|
`);
|
|
766
|
-
failures.push(`Dev server not running at ${url}`);
|
|
848
|
+
failures.push(`CRITICAL: Dev server not running at ${url}`);
|
|
849
|
+
} else if (error.message.includes("Timeout") && error.message.includes("waitFor")) {
|
|
850
|
+
console.error("\n\u274C CRITICAL: Component not found on page!\n");
|
|
851
|
+
console.log(` The component selector could not be found within 30 seconds.
|
|
852
|
+
`);
|
|
853
|
+
console.log(` This usually means:
|
|
854
|
+
`);
|
|
855
|
+
console.log(` - The component didn't render
|
|
856
|
+
`);
|
|
857
|
+
console.log(` - The URL is incorrect
|
|
858
|
+
`);
|
|
859
|
+
console.log(` - The component selector in the contract is wrong
|
|
860
|
+
`);
|
|
861
|
+
failures.push(`CRITICAL: Component element not found on page - ${error.message}`);
|
|
862
|
+
} else if (error.message.includes("Target page, context or browser has been closed")) {
|
|
863
|
+
console.error("\n\u274C CRITICAL: Browser/page was closed unexpectedly!\n");
|
|
864
|
+
console.log(` This usually means:
|
|
865
|
+
`);
|
|
866
|
+
console.log(` - The test timeout was too short
|
|
867
|
+
`);
|
|
868
|
+
console.log(` - The browser crashed
|
|
869
|
+
`);
|
|
870
|
+
console.log(` - An external process killed the browser
|
|
871
|
+
`);
|
|
872
|
+
failures.push(`CRITICAL: Browser/page closed unexpectedly - ${error.message}`);
|
|
873
|
+
} else if (error.message.includes("FATAL")) {
|
|
874
|
+
console.error(`
|
|
875
|
+
${error.message}
|
|
876
|
+
`);
|
|
877
|
+
failures.push(error.message);
|
|
767
878
|
} else {
|
|
768
|
-
console.error("\u274C
|
|
769
|
-
|
|
879
|
+
console.error("\n\u274C UNEXPECTED ERROR:", error.message);
|
|
880
|
+
console.error("Stack:", error.stack);
|
|
881
|
+
failures.push(`UNEXPECTED ERROR: ${error.message}`);
|
|
770
882
|
}
|
|
771
883
|
}
|
|
772
884
|
} finally {
|
|
@@ -106,7 +106,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
106
106
|
const devServerUrl = await checkDevServer(url);
|
|
107
107
|
if (devServerUrl) {
|
|
108
108
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
109
|
-
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-
|
|
109
|
+
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-7BPRTIN4.js');
|
|
110
110
|
contract = await runContractTestsPlaywright(componentName, devServerUrl);
|
|
111
111
|
} else {
|
|
112
112
|
throw new Error(
|