aria-ease 6.2.1 → 6.2.3
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 -174
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +3 -60
- 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.cjs +0 -22
- package/dist/src/checkbox/index.d.cts +1 -1
- package/dist/src/checkbox/index.d.ts +1 -1
- package/dist/src/checkbox/index.js +0 -22
- 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 -8
- 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 -8
- package/dist/src/toggle/index.cjs +0 -28
- package/dist/src/toggle/index.d.cts +1 -1
- package/dist/src/toggle/index.d.ts +1 -1
- package/dist/src/toggle/index.js +0 -28
- 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/README.md
CHANGED
|
@@ -128,23 +128,11 @@ menu.refresh();
|
|
|
128
128
|
**Required HTML structure:**
|
|
129
129
|
|
|
130
130
|
```html
|
|
131
|
-
<button
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
>
|
|
137
|
-
Menu
|
|
138
|
-
</button>
|
|
139
|
-
<div
|
|
140
|
-
id="dropdown-menu"
|
|
141
|
-
style="display: none;"
|
|
142
|
-
aria-labelledby="menu-button"
|
|
143
|
-
role="menu"
|
|
144
|
-
>
|
|
145
|
-
<a role="menuitem" href="#" class="menu-item">Item 1</a>
|
|
146
|
-
<a role="menuitem" href="#" class="menu-item">Item 2</a>
|
|
147
|
-
<button role="menuitem" class="menu-item">Item 3</button>
|
|
131
|
+
<button id="menu-button" aria-label="Home example menu">Menu</button>
|
|
132
|
+
<div id="dropdown-menu" style="display: none;">
|
|
133
|
+
<a href="#" class="menu-item">Item 1</a>
|
|
134
|
+
<a href="#" class="menu-item">Item 2</a>
|
|
135
|
+
<button class="menu-item">Item 3</button>
|
|
148
136
|
</div>
|
|
149
137
|
```
|
|
150
138
|
|
package/bin/cli.cjs
CHANGED
|
@@ -673,6 +673,11 @@ __export(contractTestRunnerPlaywright_exports, {
|
|
|
673
673
|
});
|
|
674
674
|
async function runContractTestsPlaywright(componentName, url) {
|
|
675
675
|
const reporter = new ContractReporter(true);
|
|
676
|
+
const actionTimeoutMs = 400;
|
|
677
|
+
const assertionTimeoutMs = 400;
|
|
678
|
+
function isBrowserClosedError(error) {
|
|
679
|
+
return error instanceof Error && error.message.includes("Target page, context or browser has been closed");
|
|
680
|
+
}
|
|
676
681
|
const contractTyped = contract_default;
|
|
677
682
|
const contractPath = contractTyped[componentName]?.path;
|
|
678
683
|
if (!contractPath) {
|
|
@@ -691,17 +696,29 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
691
696
|
try {
|
|
692
697
|
page = await createTestPage();
|
|
693
698
|
if (url) {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
699
|
+
try {
|
|
700
|
+
await page.goto(url, {
|
|
701
|
+
waitUntil: "domcontentloaded",
|
|
702
|
+
timeout: 3e4
|
|
703
|
+
});
|
|
704
|
+
} catch (error) {
|
|
705
|
+
throw new Error(
|
|
706
|
+
`Failed to navigate to ${url}. Ensure dev server is running and accessible. Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
707
|
+
);
|
|
708
|
+
}
|
|
698
709
|
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
699
710
|
}
|
|
700
711
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
701
712
|
if (!mainSelector) {
|
|
702
|
-
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
713
|
+
throw new Error(`CRITICAL: No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
714
|
+
}
|
|
715
|
+
try {
|
|
716
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
717
|
+
} catch (error) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`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)}`
|
|
720
|
+
);
|
|
703
721
|
}
|
|
704
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
705
722
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
706
723
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
707
724
|
state: "visible",
|
|
@@ -781,6 +798,13 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
781
798
|
}
|
|
782
799
|
}
|
|
783
800
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
801
|
+
if (!page || page.isClosed()) {
|
|
802
|
+
console.warn(`
|
|
803
|
+
\u26A0\uFE0F Browser closed - skipping remaining ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests
|
|
804
|
+
`);
|
|
805
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. ${componentContract.dynamic.length - componentContract.dynamic.indexOf(dynamicTest)} tests skipped.`);
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
784
808
|
const { action, assertions } = dynamicTest;
|
|
785
809
|
const failuresBeforeTest = failures.length;
|
|
786
810
|
if (componentContract.selectors.popup) {
|
|
@@ -800,16 +824,16 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
800
824
|
const closeElement = page.locator(closeSelector).first();
|
|
801
825
|
await closeElement.focus();
|
|
802
826
|
await page.keyboard.press("Escape");
|
|
803
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
827
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
804
828
|
}
|
|
805
829
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
806
830
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
807
|
-
await triggerElement.click();
|
|
808
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
831
|
+
await triggerElement.click({ timeout: actionTimeoutMs });
|
|
832
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
809
833
|
}
|
|
810
834
|
if (!menuClosed) {
|
|
811
835
|
await page.mouse.click(10, 10);
|
|
812
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
836
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
813
837
|
}
|
|
814
838
|
if (!menuClosed) {
|
|
815
839
|
throw new Error(
|
|
@@ -838,9 +862,9 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
838
862
|
const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
|
|
839
863
|
const triggerPanel = await trigger.getAttribute("aria-controls");
|
|
840
864
|
if (isExpanded && triggerPanel) {
|
|
841
|
-
await trigger.click();
|
|
865
|
+
await trigger.click({ timeout: actionTimeoutMs });
|
|
842
866
|
const panel = page.locator(`#${triggerPanel}`);
|
|
843
|
-
await (0, test_exports.expect)(panel).toBeHidden({ timeout:
|
|
867
|
+
await (0, test_exports.expect)(panel).toBeHidden({ timeout: assertionTimeoutMs }).catch(() => {
|
|
844
868
|
});
|
|
845
869
|
}
|
|
846
870
|
}
|
|
@@ -879,134 +903,192 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
879
903
|
continue;
|
|
880
904
|
}
|
|
881
905
|
for (const act of action) {
|
|
906
|
+
if (!page || page.isClosed()) {
|
|
907
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
908
|
+
break;
|
|
909
|
+
}
|
|
882
910
|
if (act.type === "focus") {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
911
|
+
try {
|
|
912
|
+
const focusSelector = componentContract.selectors[act.target];
|
|
913
|
+
if (!focusSelector) {
|
|
914
|
+
failures.push(`Selector for focus target ${act.target} not found.`);
|
|
915
|
+
continue;
|
|
916
|
+
}
|
|
917
|
+
await page.locator(focusSelector).first().focus({ timeout: actionTimeoutMs });
|
|
918
|
+
} catch (error) {
|
|
919
|
+
if (isBrowserClosedError(error)) {
|
|
920
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
failures.push(`Failed to focus ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
886
924
|
continue;
|
|
887
925
|
}
|
|
888
|
-
await page.locator(focusSelector).first().focus();
|
|
889
926
|
}
|
|
890
927
|
if (act.type === "type" && act.value) {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
928
|
+
try {
|
|
929
|
+
const typeSelector = componentContract.selectors[act.target];
|
|
930
|
+
if (!typeSelector) {
|
|
931
|
+
failures.push(`Selector for type target ${act.target} not found.`);
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
await page.locator(typeSelector).first().fill(act.value, { timeout: actionTimeoutMs });
|
|
935
|
+
} catch (error) {
|
|
936
|
+
if (isBrowserClosedError(error)) {
|
|
937
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
938
|
+
break;
|
|
939
|
+
}
|
|
940
|
+
failures.push(`Failed to type into ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
894
941
|
continue;
|
|
895
942
|
}
|
|
896
|
-
await page.locator(typeSelector).first().fill(act.value);
|
|
897
943
|
}
|
|
898
944
|
if (act.type === "click") {
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
945
|
+
try {
|
|
946
|
+
if (act.target === "document") {
|
|
947
|
+
await page.mouse.click(10, 10);
|
|
948
|
+
} else if (act.target === "relative" && act.relativeTarget) {
|
|
949
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
950
|
+
if (!relativeSelector) {
|
|
951
|
+
failures.push(`Relative selector not defined for click action.`);
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
955
|
+
if (!relativeElement) {
|
|
956
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for click.`);
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
await relativeElement.click({ timeout: actionTimeoutMs });
|
|
960
|
+
} else {
|
|
961
|
+
const actionSelector = componentContract.selectors[act.target];
|
|
962
|
+
if (!actionSelector) {
|
|
963
|
+
failures.push(`Selector for action target ${act.target} not found.`);
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
await page.locator(actionSelector).first().click({ timeout: actionTimeoutMs });
|
|
911
967
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
failures.push(`Selector for action target ${act.target} not found.`);
|
|
917
|
-
continue;
|
|
968
|
+
} catch (error) {
|
|
969
|
+
if (isBrowserClosedError(error)) {
|
|
970
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
971
|
+
break;
|
|
918
972
|
}
|
|
919
|
-
|
|
973
|
+
failures.push(`Failed to click ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
974
|
+
continue;
|
|
920
975
|
}
|
|
921
976
|
}
|
|
922
977
|
if (act.type === "keypress" && act.key) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
keyValue
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
942
|
-
await page.keyboard.press(keyValue);
|
|
943
|
-
} else {
|
|
944
|
-
const keypressSelector = componentContract.selectors[act.target];
|
|
945
|
-
if (!keypressSelector) {
|
|
946
|
-
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
947
|
-
continue;
|
|
978
|
+
try {
|
|
979
|
+
const keyMap = {
|
|
980
|
+
"Space": "Space",
|
|
981
|
+
"Enter": "Enter",
|
|
982
|
+
"Escape": "Escape",
|
|
983
|
+
"Arrow Up": "ArrowUp",
|
|
984
|
+
"Arrow Down": "ArrowDown",
|
|
985
|
+
"Arrow Left": "ArrowLeft",
|
|
986
|
+
"Arrow Right": "ArrowRight",
|
|
987
|
+
"Home": "Home",
|
|
988
|
+
"End": "End",
|
|
989
|
+
"Tab": "Tab"
|
|
990
|
+
};
|
|
991
|
+
let keyValue = keyMap[act.key] || act.key;
|
|
992
|
+
if (keyValue === "Space") {
|
|
993
|
+
keyValue = " ";
|
|
994
|
+
} else if (keyValue.includes(" ")) {
|
|
995
|
+
keyValue = keyValue.replace(/ /g, "");
|
|
948
996
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
997
|
+
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
998
|
+
await page.keyboard.press(keyValue);
|
|
999
|
+
} else {
|
|
1000
|
+
const keypressSelector = componentContract.selectors[act.target];
|
|
1001
|
+
if (!keypressSelector) {
|
|
1002
|
+
failures.push(`Selector for keypress target ${act.target} not found.`);
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
const target = page.locator(keypressSelector).first();
|
|
1006
|
+
const elementCount = await target.count();
|
|
1007
|
+
if (elementCount === 0) {
|
|
1008
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
await target.press(keyValue, { timeout: actionTimeoutMs });
|
|
1012
|
+
}
|
|
1013
|
+
} catch (error) {
|
|
1014
|
+
if (isBrowserClosedError(error)) {
|
|
1015
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
953
1016
|
break;
|
|
954
1017
|
}
|
|
955
|
-
|
|
1018
|
+
failures.push(`Failed to press ${act.key} on ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1019
|
+
continue;
|
|
956
1020
|
}
|
|
957
1021
|
}
|
|
958
1022
|
if (act.type === "hover") {
|
|
959
|
-
|
|
1023
|
+
try {
|
|
1024
|
+
if (act.target === "relative" && act.relativeTarget) {
|
|
1025
|
+
const relativeSelector = componentContract.selectors.relative;
|
|
1026
|
+
if (!relativeSelector) {
|
|
1027
|
+
failures.push(`Relative selector not defined for hover action.`);
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
const relativeElement = await resolveRelativeTarget(relativeSelector, act.relativeTarget);
|
|
1031
|
+
if (!relativeElement) {
|
|
1032
|
+
failures.push(`Could not resolve relative target ${act.relativeTarget} for hover.`);
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
await relativeElement.hover({ timeout: actionTimeoutMs });
|
|
1036
|
+
} else {
|
|
1037
|
+
const hoverSelector = componentContract.selectors[act.target];
|
|
1038
|
+
if (!hoverSelector) {
|
|
1039
|
+
failures.push(`Selector for hover target ${act.target} not found.`);
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
await page.locator(hoverSelector).first().hover({ timeout: actionTimeoutMs });
|
|
1043
|
+
}
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
if (isBrowserClosedError(error)) {
|
|
1046
|
+
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
failures.push(`Failed to hover ${act.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
for (const assertion of assertions) {
|
|
1055
|
+
if (!page || page.isClosed()) {
|
|
1056
|
+
failures.push(`CRITICAL: Browser/page closed before completing all tests. Increase test timeout or reduce test complexity.`);
|
|
1057
|
+
break;
|
|
1058
|
+
}
|
|
1059
|
+
let target;
|
|
1060
|
+
try {
|
|
1061
|
+
if (assertion.target === "relative") {
|
|
960
1062
|
const relativeSelector = componentContract.selectors.relative;
|
|
961
1063
|
if (!relativeSelector) {
|
|
962
|
-
failures.push(
|
|
1064
|
+
failures.push("Relative selector is not defined in the contract.");
|
|
963
1065
|
continue;
|
|
964
1066
|
}
|
|
965
|
-
const
|
|
966
|
-
if (!
|
|
967
|
-
failures.push(
|
|
1067
|
+
const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
|
|
1068
|
+
if (!relativeTargetValue) {
|
|
1069
|
+
failures.push("Relative target or expected value is not defined.");
|
|
968
1070
|
continue;
|
|
969
1071
|
}
|
|
970
|
-
await
|
|
1072
|
+
target = await resolveRelativeTarget(relativeSelector, relativeTargetValue);
|
|
971
1073
|
} else {
|
|
972
|
-
const
|
|
973
|
-
if (!
|
|
974
|
-
failures.push(`Selector for
|
|
1074
|
+
const assertionSelector = componentContract.selectors[assertion.target];
|
|
1075
|
+
if (!assertionSelector) {
|
|
1076
|
+
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
975
1077
|
continue;
|
|
976
1078
|
}
|
|
977
|
-
|
|
1079
|
+
target = page.locator(assertionSelector).first();
|
|
978
1080
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
for (const assertion of assertions) {
|
|
982
|
-
let target;
|
|
983
|
-
if (assertion.target === "relative") {
|
|
984
|
-
const relativeSelector = componentContract.selectors.relative;
|
|
985
|
-
if (!relativeSelector) {
|
|
986
|
-
failures.push("Relative selector is not defined in the contract.");
|
|
987
|
-
continue;
|
|
988
|
-
}
|
|
989
|
-
const relativeTargetValue = assertion.relativeTarget || assertion.expectedValue;
|
|
990
|
-
if (!relativeTargetValue) {
|
|
991
|
-
failures.push("Relative target or expected value is not defined.");
|
|
1081
|
+
if (!target) {
|
|
1082
|
+
failures.push(`Target ${assertion.target} not found.`);
|
|
992
1083
|
continue;
|
|
993
1084
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
const assertionSelector = componentContract.selectors[assertion.target];
|
|
997
|
-
if (!assertionSelector) {
|
|
998
|
-
failures.push(`Selector for assertion target ${assertion.target} not found.`);
|
|
999
|
-
continue;
|
|
1000
|
-
}
|
|
1001
|
-
target = page.locator(assertionSelector).first();
|
|
1002
|
-
}
|
|
1003
|
-
if (!target) {
|
|
1004
|
-
failures.push(`Target ${assertion.target} not found.`);
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
failures.push(`Failed to resolve target ${assertion.target}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1005
1087
|
continue;
|
|
1006
1088
|
}
|
|
1007
1089
|
if (assertion.assertion === "toBeVisible") {
|
|
1008
1090
|
try {
|
|
1009
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
1091
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: assertionTimeoutMs });
|
|
1010
1092
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
1011
1093
|
} catch {
|
|
1012
1094
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -1020,7 +1102,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
1020
1102
|
}
|
|
1021
1103
|
if (assertion.assertion === "notToBeVisible") {
|
|
1022
1104
|
try {
|
|
1023
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
1105
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: assertionTimeoutMs });
|
|
1024
1106
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
1025
1107
|
} catch {
|
|
1026
1108
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -1042,7 +1124,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
1042
1124
|
failures.push(assertion.failureMessage + ` ${assertion.target} "${assertion.attribute}" should not be empty, found "${attributeValue}".`);
|
|
1043
1125
|
}
|
|
1044
1126
|
} else {
|
|
1045
|
-
await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout:
|
|
1127
|
+
await (0, test_exports.expect)(target).toHaveAttribute(assertion.attribute, assertion.expectedValue, { timeout: assertionTimeoutMs });
|
|
1046
1128
|
passes.push(`${assertion.target} has expected "${assertion.attribute}". Test: "${dynamicTest.description}".`);
|
|
1047
1129
|
}
|
|
1048
1130
|
} catch {
|
|
@@ -1072,7 +1154,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
1072
1154
|
}
|
|
1073
1155
|
if (assertion.assertion === "toHaveFocus") {
|
|
1074
1156
|
try {
|
|
1075
|
-
await (0, test_exports.expect)(target).toBeFocused({ timeout:
|
|
1157
|
+
await (0, test_exports.expect)(target).toBeFocused({ timeout: assertionTimeoutMs });
|
|
1076
1158
|
passes.push(`${assertion.target} has focus as expected. Test: "${dynamicTest.description}".`);
|
|
1077
1159
|
} catch {
|
|
1078
1160
|
const actualFocus = await page.evaluate(() => {
|
|
@@ -1107,18 +1189,48 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
1107
1189
|
reporter.summary(failures);
|
|
1108
1190
|
} catch (error) {
|
|
1109
1191
|
if (error instanceof Error) {
|
|
1110
|
-
if (error.message.includes("Executable doesn't exist")) {
|
|
1111
|
-
console.error("\n\u274C Playwright browsers not found!\n");
|
|
1192
|
+
if (error.message.includes("Executable doesn't exist") || error.message.includes("browserType.launch")) {
|
|
1193
|
+
console.error("\n\u274C CRITICAL: Playwright browsers not found!\n");
|
|
1112
1194
|
console.log("\u{1F4E6} Run: npx playwright install chromium\n");
|
|
1113
|
-
failures.push("Playwright browser not installed. Run: npx playwright install chromium");
|
|
1114
|
-
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED")) {
|
|
1115
|
-
console.error("\n\u274C Cannot connect to dev server!\n");
|
|
1195
|
+
failures.push("CRITICAL: Playwright browser not installed. Run: npx playwright install chromium");
|
|
1196
|
+
} else if (error.message.includes("net::ERR_CONNECTION_REFUSED") || error.message.includes("NS_ERROR_CONNECTION_REFUSED")) {
|
|
1197
|
+
console.error("\n\u274C CRITICAL: Cannot connect to dev server!\n");
|
|
1116
1198
|
console.log(` Make sure your dev server is running at ${url}
|
|
1117
1199
|
`);
|
|
1118
|
-
failures.push(`Dev server not running at ${url}`);
|
|
1200
|
+
failures.push(`CRITICAL: Dev server not running at ${url}`);
|
|
1201
|
+
} else if (error.message.includes("Timeout") && error.message.includes("waitFor")) {
|
|
1202
|
+
console.error("\n\u274C CRITICAL: Component not found on page!\n");
|
|
1203
|
+
console.log(` The component selector could not be found within 30 seconds.
|
|
1204
|
+
`);
|
|
1205
|
+
console.log(` This usually means:
|
|
1206
|
+
`);
|
|
1207
|
+
console.log(` - The component didn't render
|
|
1208
|
+
`);
|
|
1209
|
+
console.log(` - The URL is incorrect
|
|
1210
|
+
`);
|
|
1211
|
+
console.log(` - The component selector in the contract is wrong
|
|
1212
|
+
`);
|
|
1213
|
+
failures.push(`CRITICAL: Component element not found on page - ${error.message}`);
|
|
1214
|
+
} else if (error.message.includes("Target page, context or browser has been closed")) {
|
|
1215
|
+
console.error("\n\u274C CRITICAL: Browser/page was closed unexpectedly!\n");
|
|
1216
|
+
console.log(` This usually means:
|
|
1217
|
+
`);
|
|
1218
|
+
console.log(` - The test timeout was too short
|
|
1219
|
+
`);
|
|
1220
|
+
console.log(` - The browser crashed
|
|
1221
|
+
`);
|
|
1222
|
+
console.log(` - An external process killed the browser
|
|
1223
|
+
`);
|
|
1224
|
+
failures.push(`CRITICAL: Browser/page closed unexpectedly - ${error.message}`);
|
|
1225
|
+
} else if (error.message.includes("FATAL")) {
|
|
1226
|
+
console.error(`
|
|
1227
|
+
${error.message}
|
|
1228
|
+
`);
|
|
1229
|
+
failures.push(error.message);
|
|
1119
1230
|
} else {
|
|
1120
|
-
console.error("\u274C
|
|
1121
|
-
|
|
1231
|
+
console.error("\n\u274C UNEXPECTED ERROR:", error.message);
|
|
1232
|
+
console.error("Stack:", error.stack);
|
|
1233
|
+
failures.push(`UNEXPECTED ERROR: ${error.message}`);
|
|
1122
1234
|
}
|
|
1123
1235
|
}
|
|
1124
1236
|
} finally {
|
package/bin/cli.js
CHANGED
|
@@ -207,7 +207,7 @@ program.command("audit").description("Run axe-core powered accessibility audit o
|
|
|
207
207
|
console.log(chalk.green("\n\u{1F389} All audits completed."));
|
|
208
208
|
});
|
|
209
209
|
program.command("test").description("Run core a11y accessibility standard tests on UI components").action(async () => {
|
|
210
|
-
const { runTest } = await import("./test-
|
|
210
|
+
const { runTest } = await import("./test-A3ESFXOR.js");
|
|
211
211
|
runTest();
|
|
212
212
|
});
|
|
213
213
|
program.command("help").description("Display help information").action(() => {
|