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
package/dist/index.cjs
CHANGED
|
@@ -350,6 +350,11 @@ __export(contractTestRunnerPlaywright_exports, {
|
|
|
350
350
|
});
|
|
351
351
|
async function runContractTestsPlaywright(componentName, url) {
|
|
352
352
|
const reporter = new ContractReporter(true);
|
|
353
|
+
const actionTimeoutMs = 400;
|
|
354
|
+
const assertionTimeoutMs = 400;
|
|
355
|
+
function isBrowserClosedError(error) {
|
|
356
|
+
return error instanceof Error && error.message.includes("Target page, context or browser has been closed");
|
|
357
|
+
}
|
|
353
358
|
const contractTyped = contract_default;
|
|
354
359
|
const contractPath = contractTyped[componentName]?.path;
|
|
355
360
|
if (!contractPath) {
|
|
@@ -368,17 +373,29 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
368
373
|
try {
|
|
369
374
|
page = await createTestPage();
|
|
370
375
|
if (url) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
376
|
+
try {
|
|
377
|
+
await page.goto(url, {
|
|
378
|
+
waitUntil: "domcontentloaded",
|
|
379
|
+
timeout: 3e4
|
|
380
|
+
});
|
|
381
|
+
} catch (error) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`Failed to navigate to ${url}. Ensure dev server is running and accessible. Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
375
386
|
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
376
387
|
}
|
|
377
388
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
378
389
|
if (!mainSelector) {
|
|
379
|
-
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
390
|
+
throw new Error(`CRITICAL: No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
394
|
+
} catch (error) {
|
|
395
|
+
throw new Error(
|
|
396
|
+
`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)}`
|
|
397
|
+
);
|
|
380
398
|
}
|
|
381
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
382
399
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
383
400
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
384
401
|
state: "visible",
|
|
@@ -458,6 +475,13 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
458
475
|
}
|
|
459
476
|
}
|
|
460
477
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
478
|
+
if (!page || page.isClosed()) {
|
|
479
|
+
console.warn(`
|
|
480
|
+
\u26A0\uFE0F Browser closed - skipping remaining ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests
|
|
481
|
+
`);
|
|
482
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests skipped.`);
|
|
483
|
+
break;
|
|
484
|
+
}
|
|
461
485
|
const { action, assertions } = dynamicTest;
|
|
462
486
|
const failuresBeforeTest = failures.length;
|
|
463
487
|
if (componentContract.selectors.popup) {
|
|
@@ -477,16 +501,16 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
477
501
|
const closeElement = page.locator(closeSelector).first();
|
|
478
502
|
await closeElement.focus();
|
|
479
503
|
await page.keyboard.press("Escape");
|
|
480
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
504
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
481
505
|
}
|
|
482
506
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
483
507
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
484
|
-
await triggerElement.click();
|
|
485
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
508
|
+
await triggerElement.click({ timeout: actionTimeoutMs });
|
|
509
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
486
510
|
}
|
|
487
511
|
if (!menuClosed) {
|
|
488
512
|
await page.mouse.click(10, 10);
|
|
489
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
513
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
490
514
|
}
|
|
491
515
|
if (!menuClosed) {
|
|
492
516
|
throw new Error(
|
|
@@ -515,9 +539,9 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
515
539
|
const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
|
|
516
540
|
const triggerPanel = await trigger.getAttribute("aria-controls");
|
|
517
541
|
if (isExpanded && triggerPanel) {
|
|
518
|
-
await trigger.click();
|
|
542
|
+
await trigger.click({ timeout: actionTimeoutMs });
|
|
519
543
|
const panel = page.locator(`#${triggerPanel}`);
|
|
520
|
-
await (0, test_exports.expect)(panel).toBeHidden({ timeout:
|
|
544
|
+
await (0, test_exports.expect)(panel).toBeHidden({ timeout: assertionTimeoutMs }).catch(() => {
|
|
521
545
|
});
|
|
522
546
|
}
|
|
523
547
|
}
|
|
@@ -556,134 +580,192 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
556
580
|
continue;
|
|
557
581
|
}
|
|
558
582
|
for (const act of action) {
|
|
583
|
+
if (!page || page.isClosed()) {
|
|
584
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
559
587
|
if (act.type === "focus") {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
588
|
+
try {
|
|
589
|
+
const focusSelector = componentContract.selectors[act.target];
|
|
590
|
+
if (!focusSelector) {
|
|
591
|
+
failures.push(`Selector for focus target ${act.target} not found.`);
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
await page.locator(focusSelector).first().focus({ timeout: actionTimeoutMs });
|
|
595
|
+
} catch (error) {
|
|
596
|
+
if (isBrowserClosedError(error)) {
|
|
597
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
failures.push(`Failed to focus ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
563
601
|
continue;
|
|
564
602
|
}
|
|
565
|
-
await page.locator(focusSelector).first().focus();
|
|
566
603
|
}
|
|
567
604
|
if (act.type === "type" && act.value) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
605
|
+
try {
|
|
606
|
+
const typeSelector = componentContract.selectors[act.target];
|
|
607
|
+
if (!typeSelector) {
|
|
608
|
+
failures.push(`Selector for type target ${act.target} not found.`);
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
await page.locator(typeSelector).first().fill(act.value, { timeout: actionTimeoutMs });
|
|
612
|
+
} catch (error) {
|
|
613
|
+
if (isBrowserClosedError(error)) {
|
|
614
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
failures.push(`Failed to type into ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
571
618
|
continue;
|
|
572
619
|
}
|
|
573
|
-
await page.locator(typeSelector).first().fill(act.value);
|
|
574
620
|
}
|
|
575
621
|
if (act.type === "click") {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
622
|
+
try {
|
|
623
|
+
if (act.target === "document") {
|
|
624
|
+
await page.mouse.click(10, 10);
|
|
625
|
+
} else if (act.target === "relative" && act.relativeTarget) {
|
|
626
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
627
|
+
if (!relativeSelector) {
|
|
628
|
+
failures.push(`Relative selector not defined for click action.`);
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
632
|
+
if (!relativeElement) {
|
|
633
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for click.`);
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
await relativeElement.click({ timeout: actionTimeoutMs });
|
|
637
|
+
} else {
|
|
638
|
+
const actionSelector = componentContract.selectors[act.target];
|
|
639
|
+
if (!actionSelector) {
|
|
640
|
+
failures.push(`Selector for action target ${act.target} not found.`);
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
await page.locator(actionSelector).first().click({ timeout: actionTimeoutMs });
|
|
588
644
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
failures.push(`Selector for action target ${act.target} not found.`);
|
|
594
|
-
continue;
|
|
645
|
+
} catch (error) {
|
|
646
|
+
if (isBrowserClosedError(error)) {
|
|
647
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
648
|
+
break;
|
|
595
649
|
}
|
|
596
|
-
|
|
650
|
+
failures.push(`Failed to click ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
651
|
+
continue;
|
|
597
652
|
}
|
|
598
653
|
}
|
|
599
654
|
if (act.type === "keypress" && act.key) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
keyValue
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
619
|
-
await page.keyboard.press(keyValue);
|
|
620
|
-
} else {
|
|
621
|
-
const keypressSelector = componentContract.selectors[act.target];
|
|
622
|
-
if (!keypressSelector) {
|
|
623
|
-
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
624
|
-
continue;
|
|
655
|
+
try {
|
|
656
|
+
const keyMap = {
|
|
657
|
+
"Space": "Space",
|
|
658
|
+
"Enter": "Enter",
|
|
659
|
+
"Escape": "Escape",
|
|
660
|
+
"Arrow Up": "ArrowUp",
|
|
661
|
+
"Arrow Down": "ArrowDown",
|
|
662
|
+
"Arrow Left": "ArrowLeft",
|
|
663
|
+
"Arrow Right": "ArrowRight",
|
|
664
|
+
"Home": "Home",
|
|
665
|
+
"End": "End",
|
|
666
|
+
"Tab": "Tab"
|
|
667
|
+
};
|
|
668
|
+
let keyValue = keyMap[act.key] || act.key;
|
|
669
|
+
if (keyValue === "Space") {
|
|
670
|
+
keyValue = " ";
|
|
671
|
+
} else if (keyValue.includes(" ")) {
|
|
672
|
+
keyValue = keyValue.replace(/ /g, "");
|
|
625
673
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
674
|
+
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
675
|
+
await page.keyboard.press(keyValue);
|
|
676
|
+
} else {
|
|
677
|
+
const keypressSelector = componentContract.selectors[act.target];
|
|
678
|
+
if (!keypressSelector) {
|
|
679
|
+
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
const target = page.locator(keypressSelector).first();
|
|
683
|
+
const elementCount = await target.count();
|
|
684
|
+
if (elementCount === 0) {
|
|
685
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
await target.press(keyValue, { timeout: actionTimeoutMs });
|
|
689
|
+
}
|
|
690
|
+
} catch (error) {
|
|
691
|
+
if (isBrowserClosedError(error)) {
|
|
692
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
630
693
|
break;
|
|
631
694
|
}
|
|
632
|
-
|
|
695
|
+
failures.push(`Failed to press ${act.key} on ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
696
|
+
continue;
|
|
633
697
|
}
|
|
634
698
|
}
|
|
635
699
|
if (act.type === "hover") {
|
|
636
|
-
|
|
700
|
+
try {
|
|
701
|
+
if (act.target === "relative" && act.relativeTarget) {
|
|
702
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
703
|
+
if (!relativeSelector) {
|
|
704
|
+
failures.push(`Relative selector not defined for hover action.`);
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
708
|
+
if (!relativeElement) {
|
|
709
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for hover.`);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
await relativeElement.hover({ timeout: actionTimeoutMs });
|
|
713
|
+
} else {
|
|
714
|
+
const hoverSelector = componentContract.selectors[act.target];
|
|
715
|
+
if (!hoverSelector) {
|
|
716
|
+
failures.push(`Selector for hover target ${act.target} not found.`);
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
await page.locator(hoverSelector).first().hover({ timeout: actionTimeoutMs });
|
|
720
|
+
}
|
|
721
|
+
} catch (error) {
|
|
722
|
+
if (isBrowserClosedError(error)) {
|
|
723
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
726
|
+
failures.push(`Failed to hover ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
for (const assertion of assertions) {
|
|
732
|
+
if (!page || page.isClosed()) {
|
|
733
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. Increase test timeout or reduce test complexity.`);
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
let target;
|
|
737
|
+
try {
|
|
738
|
+
if (assertion.target === "relative") {
|
|
637
739
|
const relativeSelector = componentContract.selectors.relative;
|
|
638
740
|
if (!relativeSelector) {
|
|
639
|
-
failures.push(
|
|
741
|
+
failures.push("Relative selector is not defined in the contract.");
|
|
640
742
|
continue;
|
|
641
743
|
}
|
|
642
|
-
const
|
|
643
|
-
if (!
|
|
644
|
-
failures.push(
|
|
744
|
+
const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
|
|
745
|
+
if (!relativeTargetValue) {
|
|
746
|
+
failures.push("Relative target or expected value is not defined.");
|
|
645
747
|
continue;
|
|
646
748
|
}
|
|
647
|
-
await
|
|
749
|
+
target = await resolveRelativeTarget(relativeSelector, relativeTargetValue);
|
|
648
750
|
} else {
|
|
649
|
-
const
|
|
650
|
-
if (!
|
|
651
|
-
failures.push(`Selector for
|
|
751
|
+
const assertionSelector = componentContract.selectors[assertion.target];
|
|
752
|
+
if (!assertionSelector) {
|
|
753
|
+
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
652
754
|
continue;
|
|
653
755
|
}
|
|
654
|
-
|
|
756
|
+
target = page.locator(assertionSelector).first();
|
|
655
757
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
for (const assertion of assertions) {
|
|
659
|
-
let target;
|
|
660
|
-
if (assertion.target === "relative") {
|
|
661
|
-
const relativeSelector = componentContract.selectors.relative;
|
|
662
|
-
if (!relativeSelector) {
|
|
663
|
-
failures.push("Relative selector is not defined in the contract.");
|
|
664
|
-
continue;
|
|
665
|
-
}
|
|
666
|
-
const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
|
|
667
|
-
if (!relativeTargetValue) {
|
|
668
|
-
failures.push("Relative target or expected value is not defined.");
|
|
758
|
+
if (!target) {
|
|
759
|
+
failures.push(`Target ${assertion.target} not found.`);
|
|
669
760
|
continue;
|
|
670
761
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
const assertionSelector = componentContract.selectors[assertion.target];
|
|
674
|
-
if (!assertionSelector) {
|
|
675
|
-
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
676
|
-
continue;
|
|
677
|
-
}
|
|
678
|
-
target = page.locator(assertionSelector).first();
|
|
679
|
-
}
|
|
680
|
-
if (!target) {
|
|
681
|
-
failures.push(`Target ${assertion.target} not found.`);
|
|
762
|
+
} catch (error) {
|
|
763
|
+
failures.push(`Failed to resolve target ${assertion.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
682
764
|
continue;
|
|
683
765
|
}
|
|
684
766
|
if (assertion.assertion === "toBeVisible") {
|
|
685
767
|
try {
|
|
686
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
768
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: assertionTimeoutMs });
|
|
687
769
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
688
770
|
} catch {
|
|
689
771
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -697,7 +779,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
697
779
|
}
|
|
698
780
|
if (assertion.assertion === "notToBeVisible") {
|
|
699
781
|
try {
|
|
700
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
782
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: assertionTimeoutMs });
|
|
701
783
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
702
784
|
} catch {
|
|
703
785
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -719,7 +801,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
719
801
|
failures.push(assertion.failureMessage + ` ${assertion.target} "${assertion.attribute}" should not be empty, found "${attributeValue}".`);
|
|
720
802
|
}
|
|
721
803
|
} else {
|
|
722
|
-
await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout:
|
|
804
|
+
await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: assertionTimeoutMs });
|
|
723
805
|
passes.push(`${assertion.target} has expected "${assertion.attribute}". Test: "${dynamicTest.description}".`);
|
|
724
806
|
}
|
|
725
807
|
} catch {
|
|
@@ -749,7 +831,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
749
831
|
}
|
|
750
832
|
if (assertion.assertion === "toHaveFocus") {
|
|
751
833
|
try {
|
|
752
|
-
await (0, test_exports.expect)(target).toBeFocused({ timeout:
|
|
834
|
+
await (0, test_exports.expect)(target).toBeFocused({ timeout: assertionTimeoutMs });
|
|
753
835
|
passes.push(`${assertion.target} has focus as expected. Test: "${dynamicTest.description}".`);
|
|
754
836
|
} catch {
|
|
755
837
|
const actualFocus = await page.evaluate(() => {
|
|
@@ -784,18 +866,48 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
784
866
|
reporter.summary(failures);
|
|
785
867
|
} catch (error) {
|
|
786
868
|
if (error instanceof Error) {
|
|
787
|
-
if (error.message.includes("Executable doesn't exist")) {
|
|
788
|
-
console.error("\n\u274C Playwright browsers not found!\n");
|
|
869
|
+
if (error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch")) {
|
|
870
|
+
console.error("\n\u274C CRITICAL: Playwright browsers not found!\n");
|
|
789
871
|
console.log("\u{1F4E6} Run: npx playwright install chromium\n");
|
|
790
|
-
failures.push("Playwright browser not installed. Run: npx playwright install chromium");
|
|
791
|
-
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED")) {
|
|
792
|
-
console.error("\n\u274C Cannot connect to dev server!\n");
|
|
872
|
+
failures.push("CRITICAL: Playwright browser not installed. Run: npx playwright install chromium");
|
|
873
|
+
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED") || error.message.includes("NS_ERROR_CONNECTION_REFUSED")) {
|
|
874
|
+
console.error("\n\u274C CRITICAL: Cannot connect to dev server!\n");
|
|
793
875
|
console.log(` Make sure your dev server is running at ${url}
|
|
794
876
|
`);
|
|
795
|
-
failures.push(`Dev server not running at ${url}`);
|
|
877
|
+
failures.push(`CRITICAL: Dev server not running at ${url}`);
|
|
878
|
+
} else if (error.message.includes("Timeout") && error.message.includes("waitFor")) {
|
|
879
|
+
console.error("\n\u274C CRITICAL: Component not found on page!\n");
|
|
880
|
+
console.log(` The component selector could not be found within 30 seconds.
|
|
881
|
+
`);
|
|
882
|
+
console.log(` This usually means:
|
|
883
|
+
`);
|
|
884
|
+
console.log(` - The component didn't render
|
|
885
|
+
`);
|
|
886
|
+
console.log(` - The URL is incorrect
|
|
887
|
+
`);
|
|
888
|
+
console.log(` - The component selector in the contract is wrong
|
|
889
|
+
`);
|
|
890
|
+
failures.push(`CRITICAL: Component element not found on page - ${error.message}`);
|
|
891
|
+
} else if (error.message.includes("Target page, context or browser has been closed")) {
|
|
892
|
+
console.error("\n\u274C CRITICAL: Browser/page was closed unexpectedly!\n");
|
|
893
|
+
console.log(` This usually means:
|
|
894
|
+
`);
|
|
895
|
+
console.log(` - The test timeout was too short
|
|
896
|
+
`);
|
|
897
|
+
console.log(` - The browser crashed
|
|
898
|
+
`);
|
|
899
|
+
console.log(` - An external process killed the browser
|
|
900
|
+
`);
|
|
901
|
+
failures.push(`CRITICAL: Browser/page closed unexpectedly - ${error.message}`);
|
|
902
|
+
} else if (error.message.includes("FATAL")) {
|
|
903
|
+
console.error(`
|
|
904
|
+
${error.message}
|
|
905
|
+
`);
|
|
906
|
+
failures.push(error.message);
|
|
796
907
|
} else {
|
|
797
|
-
console.error("\u274C
|
|
798
|
-
|
|
908
|
+
console.error("\n\u274C UNEXPECTED ERROR:", error.message);
|
|
909
|
+
console.error("Stack:", error.stack);
|
|
910
|
+
failures.push(`UNEXPECTED ERROR: ${error.message}`);
|
|
799
911
|
}
|
|
800
912
|
}
|
|
801
913
|
} finally {
|
|
@@ -1614,6 +1726,7 @@ function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex =
|
|
|
1614
1726
|
selectRadio(nextIndex);
|
|
1615
1727
|
break;
|
|
1616
1728
|
case " ":
|
|
1729
|
+
case "Enter":
|
|
1617
1730
|
event.preventDefault();
|
|
1618
1731
|
selectRadio(index);
|
|
1619
1732
|
break;
|
|
@@ -2052,7 +2165,7 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
|
|
|
2052
2165
|
activeIndex = -1;
|
|
2053
2166
|
setActiveDescendant(-1);
|
|
2054
2167
|
}
|
|
2055
|
-
return { cleanup, refresh };
|
|
2168
|
+
return { cleanup, refresh, openListbox, closeListbox };
|
|
2056
2169
|
}
|
|
2057
2170
|
|
|
2058
2171
|
// src/utils/test/src/test.ts
|
package/dist/index.d.cts
CHANGED
|
@@ -7,25 +7,35 @@ interface JestAxeResult {
|
|
|
7
7
|
interface AccessibilityInstance {
|
|
8
8
|
cleanup: () => void;
|
|
9
9
|
refresh?: () => void;
|
|
10
|
+
|
|
11
|
+
//Menu methods
|
|
10
12
|
openMenu?: () => void;
|
|
11
13
|
closeMenu?: () => void;
|
|
14
|
+
|
|
12
15
|
// Accordion methods
|
|
13
16
|
expandItem?: (index: number) => void;
|
|
14
17
|
collapseItem?: (index: number) => void;
|
|
15
18
|
toggleItem?: (index: number) => void;
|
|
19
|
+
|
|
16
20
|
// Radio methods
|
|
17
21
|
selectRadio?: (index: number) => void;
|
|
18
22
|
getSelectedIndex?: () => number;
|
|
23
|
+
|
|
19
24
|
// Checkbox methods
|
|
20
25
|
toggleCheckbox?: (index: number) => void;
|
|
21
26
|
setCheckboxState?: (index: number, checked: boolean) => void;
|
|
22
27
|
getCheckedStates?: () => boolean[];
|
|
23
28
|
getCheckedIndices?: () => number[];
|
|
29
|
+
|
|
24
30
|
// Toggle methods
|
|
25
31
|
toggleButton?: (index: number) => void;
|
|
26
32
|
setPressed?: (index: number, pressed: boolean) => void;
|
|
27
33
|
getPressedStates?: () => boolean[];
|
|
28
34
|
getPressedIndices?: () => number[];
|
|
35
|
+
|
|
36
|
+
//Combobox methods
|
|
37
|
+
openListbox?: () => void;
|
|
38
|
+
closeListbox?: () => void;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
interface AccordionConfig {
|
package/dist/index.d.ts
CHANGED
|
@@ -7,25 +7,35 @@ interface JestAxeResult {
|
|
|
7
7
|
interface AccessibilityInstance {
|
|
8
8
|
cleanup: () => void;
|
|
9
9
|
refresh?: () => void;
|
|
10
|
+
|
|
11
|
+
//Menu methods
|
|
10
12
|
openMenu?: () => void;
|
|
11
13
|
closeMenu?: () => void;
|
|
14
|
+
|
|
12
15
|
// Accordion methods
|
|
13
16
|
expandItem?: (index: number) => void;
|
|
14
17
|
collapseItem?: (index: number) => void;
|
|
15
18
|
toggleItem?: (index: number) => void;
|
|
19
|
+
|
|
16
20
|
// Radio methods
|
|
17
21
|
selectRadio?: (index: number) => void;
|
|
18
22
|
getSelectedIndex?: () => number;
|
|
23
|
+
|
|
19
24
|
// Checkbox methods
|
|
20
25
|
toggleCheckbox?: (index: number) => void;
|
|
21
26
|
setCheckboxState?: (index: number, checked: boolean) => void;
|
|
22
27
|
getCheckedStates?: () => boolean[];
|
|
23
28
|
getCheckedIndices?: () => number[];
|
|
29
|
+
|
|
24
30
|
// Toggle methods
|
|
25
31
|
toggleButton?: (index: number) => void;
|
|
26
32
|
setPressed?: (index: number, pressed: boolean) => void;
|
|
27
33
|
getPressedStates?: () => boolean[];
|
|
28
34
|
getPressedIndices?: () => number[];
|
|
35
|
+
|
|
36
|
+
//Combobox methods
|
|
37
|
+
openListbox?: () => void;
|
|
38
|
+
closeListbox?: () => void;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
interface AccordionConfig {
|
package/dist/index.js
CHANGED
|
@@ -787,6 +787,7 @@ function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex =
|
|
|
787
787
|
selectRadio(nextIndex);
|
|
788
788
|
break;
|
|
789
789
|
case " ":
|
|
790
|
+
case "Enter":
|
|
790
791
|
event.preventDefault();
|
|
791
792
|
selectRadio(index);
|
|
792
793
|
break;
|
|
@@ -1225,7 +1226,7 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
|
|
|
1225
1226
|
activeIndex = -1;
|
|
1226
1227
|
setActiveDescendant(-1);
|
|
1227
1228
|
}
|
|
1228
|
-
return { cleanup, refresh };
|
|
1229
|
+
return { cleanup, refresh, openListbox, closeListbox };
|
|
1229
1230
|
}
|
|
1230
1231
|
|
|
1231
1232
|
// src/utils/test/src/test.ts
|
|
@@ -1337,7 +1338,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
1337
1338
|
const devServerUrl = await checkDevServer(url);
|
|
1338
1339
|
if (devServerUrl) {
|
|
1339
1340
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
1340
|
-
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-
|
|
1341
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-O7FF7GV4.js");
|
|
1341
1342
|
contract = await runContractTestsPlaywright(componentName, devServerUrl);
|
|
1342
1343
|
} else {
|
|
1343
1344
|
throw new Error(
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
interface AccessibilityInstance {
|
|
2
2
|
cleanup: () => void;
|
|
3
3
|
refresh?: () => void;
|
|
4
|
+
|
|
5
|
+
//Menu methods
|
|
4
6
|
openMenu?: () => void;
|
|
5
7
|
closeMenu?: () => void;
|
|
8
|
+
|
|
6
9
|
// Accordion methods
|
|
7
10
|
expandItem?: (index: number) => void;
|
|
8
11
|
collapseItem?: (index: number) => void;
|
|
9
12
|
toggleItem?: (index: number) => void;
|
|
13
|
+
|
|
10
14
|
// Radio methods
|
|
11
15
|
selectRadio?: (index: number) => void;
|
|
12
16
|
getSelectedIndex?: () => number;
|
|
17
|
+
|
|
13
18
|
// Checkbox methods
|
|
14
19
|
toggleCheckbox?: (index: number) => void;
|
|
15
20
|
setCheckboxState?: (index: number, checked: boolean) => void;
|
|
16
21
|
getCheckedStates?: () => boolean[];
|
|
17
22
|
getCheckedIndices?: () => number[];
|
|
23
|
+
|
|
18
24
|
// Toggle methods
|
|
19
25
|
toggleButton?: (index: number) => void;
|
|
20
26
|
setPressed?: (index: number, pressed: boolean) => void;
|
|
21
27
|
getPressedStates?: () => boolean[];
|
|
22
28
|
getPressedIndices?: () => number[];
|
|
29
|
+
|
|
30
|
+
//Combobox methods
|
|
31
|
+
openListbox?: () => void;
|
|
32
|
+
closeListbox?: () => void;
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
interface AccordionConfig {
|
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
interface AccessibilityInstance {
|
|
2
2
|
cleanup: () => void;
|
|
3
3
|
refresh?: () => void;
|
|
4
|
+
|
|
5
|
+
//Menu methods
|
|
4
6
|
openMenu?: () => void;
|
|
5
7
|
closeMenu?: () => void;
|
|
8
|
+
|
|
6
9
|
// Accordion methods
|
|
7
10
|
expandItem?: (index: number) => void;
|
|
8
11
|
collapseItem?: (index: number) => void;
|
|
9
12
|
toggleItem?: (index: number) => void;
|
|
13
|
+
|
|
10
14
|
// Radio methods
|
|
11
15
|
selectRadio?: (index: number) => void;
|
|
12
16
|
getSelectedIndex?: () => number;
|
|
17
|
+
|
|
13
18
|
// Checkbox methods
|
|
14
19
|
toggleCheckbox?: (index: number) => void;
|
|
15
20
|
setCheckboxState?: (index: number, checked: boolean) => void;
|
|
16
21
|
getCheckedStates?: () => boolean[];
|
|
17
22
|
getCheckedIndices?: () => number[];
|
|
23
|
+
|
|
18
24
|
// Toggle methods
|
|
19
25
|
toggleButton?: (index: number) => void;
|
|
20
26
|
setPressed?: (index: number, pressed: boolean) => void;
|
|
21
27
|
getPressedStates?: () => boolean[];
|
|
22
28
|
getPressedIndices?: () => number[];
|
|
29
|
+
|
|
30
|
+
//Combobox methods
|
|
31
|
+
openListbox?: () => void;
|
|
32
|
+
closeListbox?: () => void;
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
interface AccordionConfig {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as AccordionConfig, a as AccessibilityInstance } from '../Types.d-
|
|
1
|
+
import { A as AccordionConfig, a as AccessibilityInstance } from '../Types.d-CRjhbrcw.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Makes an accordion accessible by managing ARIA attributes, keyboard navigation, and state.
|