aria-ease 6.12.1 → 6.14.0

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.
@@ -35,7 +35,7 @@ var StrategyRegistry = class {
35
35
  registerBuiltInStrategies() {
36
36
  this.builtInStrategies.set(
37
37
  "menu",
38
- () => import("./MenuComponentStrategy-JAMTCSNF.js").then(
38
+ () => import("./MenuComponentStrategy-VYCC2XOM.js").then(
39
39
  (m) => m.MenuComponentStrategy
40
40
  )
41
41
  );
@@ -558,13 +558,21 @@ var AssertionRunner = class {
558
558
  /**
559
559
  * Validate focus assertion
560
560
  */
561
- async validateFocus(target, targetName, failureMessage, testDescription) {
561
+ async validateFocus(target, targetName, expectedFocus, failureMessage, testDescription) {
562
562
  try {
563
- await (0, test_exports.expect)(target).toBeFocused({ timeout: this.timeoutMs });
564
- return {
565
- success: true,
566
- passMessage: `${targetName} has focus as expected. Test: "${testDescription}".`
567
- };
563
+ if (expectedFocus) {
564
+ await (0, test_exports.expect)(target).toBeFocused({ timeout: this.timeoutMs });
565
+ return {
566
+ success: true,
567
+ passMessage: `${targetName} has focus as expected. Test: "${testDescription}".`
568
+ };
569
+ } else {
570
+ await (0, test_exports.expect)(target).not.toBeFocused({ timeout: this.timeoutMs });
571
+ return {
572
+ success: true,
573
+ passMessage: `${targetName} does not have focus as expected. Test: "${testDescription}".`
574
+ };
575
+ }
568
576
  } catch {
569
577
  const actualFocus = await this.page.evaluate(() => {
570
578
  const focused = document.activeElement;
@@ -650,7 +658,9 @@ var AssertionRunner = class {
650
658
  }
651
659
  return { success: false, failMessage: "Missing expectedValue for toHaveValue assertion" };
652
660
  case "toHaveFocus":
653
- return this.validateFocus(target, assertion.target, assertion.failureMessage || "", testDescription);
661
+ return this.validateFocus(target, assertion.target, true, assertion.failureMessage || "", testDescription);
662
+ case "notToHaveFocus":
663
+ return this.validateFocus(target, assertion.target, false, assertion.failureMessage || "", testDescription);
654
664
  case "toHaveRole":
655
665
  if (assertion.expectedValue !== void 0) {
656
666
  return this.validateRole(target, assertion.target, assertion.expectedValue, assertion.failureMessage || "", testDescription);
@@ -667,12 +677,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
667
677
  const componentConfig = config?.test?.components?.find((c) => c.name === componentName);
668
678
  const isCustomContract = !!componentConfig?.contractPath;
669
679
  const reporter = new ContractReporter(true, isCustomContract);
670
- const defaultTimeouts = {
671
- actionTimeoutMs: 400,
672
- assertionTimeoutMs: 400,
673
- navigationTimeoutMs: 3e4,
674
- componentReadyTimeoutMs: 5e3
675
- };
680
+ const defaultTimeouts = { actionTimeoutMs: 400, assertionTimeoutMs: 400, navigationTimeoutMs: 3e4, componentReadyTimeoutMs: 5e3 };
676
681
  const globalDisableTimeouts = config?.test?.disableTimeouts === true;
677
682
  const componentDisableTimeouts = componentConfig?.disableTimeouts === true;
678
683
  const disableTimeouts = componentDisableTimeouts || globalDisableTimeouts;
@@ -684,11 +689,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
684
689
  }
685
690
  return value;
686
691
  };
687
- const actionTimeoutMs = resolveTimeout(
688
- componentConfig?.actionTimeoutMs,
689
- config?.test?.actionTimeoutMs,
690
- defaultTimeouts.actionTimeoutMs
691
- );
692
+ const actionTimeoutMs = resolveTimeout(componentConfig?.actionTimeoutMs, config?.test?.actionTimeoutMs, defaultTimeouts.actionTimeoutMs);
692
693
  const assertionTimeoutMs = resolveTimeout(
693
694
  componentConfig?.assertionTimeoutMs,
694
695
  config?.test?.assertionTimeoutMs,
@@ -788,8 +789,8 @@ This usually means:
788
789
  );
789
790
  }
790
791
  reporter.start(componentName, totalTests, apgUrl);
791
- if (componentName === "menu" && componentContract.selectors.trigger) {
792
- await page.locator(componentContract.selectors.trigger).first().waitFor({ state: "attached", timeout: componentReadyTimeoutMs }).catch(() => {
792
+ if (componentName === "menu" && componentContract.selectors.main) {
793
+ await page.locator(componentContract.selectors.main).first().waitFor({ state: "visible", timeout: componentReadyTimeoutMs }).catch(() => {
793
794
  });
794
795
  }
795
796
  const hasSubmenuCapability = componentName === "menu" && !!componentContract.selectors.submenuTrigger ? await page.locator(componentContract.selectors.submenuTrigger).count() > 0 : false;
@@ -798,7 +799,42 @@ This usually means:
798
799
  let staticFailed = 0;
799
800
  let staticWarnings = 0;
800
801
  for (const rel of componentContract.relationships || []) {
802
+ if (strategy && typeof strategy.resetState === "function") {
803
+ try {
804
+ await strategy.resetState(page);
805
+ } catch (err) {
806
+ warnings.push(`Warning: resetState failed before relationship test: ${err instanceof Error ? err.message : String(err)}`);
807
+ }
808
+ }
801
809
  const relationshipLevel = normalizeLevel(rel.level);
810
+ if (Array.isArray(rel.setup) && rel.setup.length > 0) {
811
+ let isAllowedType2 = function(t) {
812
+ return allowedTypes.includes(t);
813
+ };
814
+ var isAllowedType = isAllowedType2;
815
+ const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
816
+ const relDescription = rel.type === "aria-reference" ? `${rel.from}.${rel.attribute} references ${rel.to}` : `${rel.parent} contains ${rel.child}`;
817
+ const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
818
+ const toSetupAction = (a) => ({
819
+ ...a,
820
+ type: isAllowedType2(a.type) ? a.type : "click"
821
+ });
822
+ const setupActions = rel.setup.map(toSetupAction);
823
+ const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, relDescription, ["submenu", "submenuTrigger", "submenuItems"]);
824
+ if (setupResult.skip) {
825
+ skipped.push(setupResult.message || "Setup action skipped");
826
+ reporter.reportStaticTest(relDescription, "skip", setupResult.message, relationshipLevel);
827
+ continue;
828
+ }
829
+ if (!setupResult.success) {
830
+ const failure = `Relationship setup failed: ${setupResult.error}`;
831
+ const outcome = classifyFailure(failure, rel.level);
832
+ if (outcome.status === "fail") staticFailed += 1;
833
+ if (outcome.status === "warn") staticWarnings += 1;
834
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
835
+ continue;
836
+ }
837
+ }
802
838
  if (componentName === "menu" && !hasSubmenuCapability) {
803
839
  const involvesSubmenu = isSubmenuRelation(rel);
804
840
  if (involvesSubmenu) {
@@ -971,7 +1007,53 @@ This usually means:
971
1007
  }
972
1008
  }
973
1009
  const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
1010
+ async function runSetupActions(setup, actionExecutor, strategy2, page2, description, skipKeywords = []) {
1011
+ if (!Array.isArray(setup) || setup.length === 0) return { success: true };
1012
+ if (strategy2 && typeof strategy2.resetState === "function") {
1013
+ await strategy2.resetState(page2);
1014
+ }
1015
+ for (const setupAct of setup) {
1016
+ let setupResult = { success: true };
1017
+ try {
1018
+ if (setupAct.type === "focus") {
1019
+ if (setupAct.target === "relative" && setupAct.relativeTarget) {
1020
+ setupResult = await actionExecutor.focus("relative", setupAct.relativeTarget);
1021
+ } else {
1022
+ setupResult = await actionExecutor.focus(setupAct.target);
1023
+ }
1024
+ } else if (setupAct.type === "type" && setupAct.value) {
1025
+ setupResult = await actionExecutor.type(setupAct.target, setupAct.value);
1026
+ } else if (setupAct.type === "click") {
1027
+ setupResult = await actionExecutor.click(setupAct.target, setupAct.relativeTarget);
1028
+ } else if (setupAct.type === "keypress" && setupAct.key) {
1029
+ setupResult = await actionExecutor.keypress(setupAct.target, setupAct.key);
1030
+ } else if (setupAct.type === "hover") {
1031
+ setupResult = await actionExecutor.hover(setupAct.target, setupAct.relativeTarget);
1032
+ } else {
1033
+ continue;
1034
+ }
1035
+ } catch (err) {
1036
+ setupResult = { success: false, error: err instanceof Error ? err.message : String(err) };
1037
+ }
1038
+ if (!setupResult.success) {
1039
+ const setupMsg = setupResult.error || "Setup action failed";
1040
+ const isSkip = skipKeywords.some((kw) => description.includes(kw) || setupMsg.includes(kw));
1041
+ if (isSkip) {
1042
+ return { success: false, skip: true, message: `Skipping test - capability not present: ${setupMsg}` };
1043
+ }
1044
+ return { success: false, error: setupMsg };
1045
+ }
1046
+ }
1047
+ return { success: true };
1048
+ }
974
1049
  for (const test of componentContract.static[0]?.assertions || []) {
1050
+ if (strategy && typeof strategy.resetState === "function") {
1051
+ try {
1052
+ await strategy.resetState(page);
1053
+ } catch (err) {
1054
+ warnings.push(`Warning: resetState failed before static test: ${err instanceof Error ? err.message : String(err)}`);
1055
+ }
1056
+ }
975
1057
  if (test.target === "relative") continue;
976
1058
  const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
977
1059
  const staticLevel = normalizeLevel(test.level);
@@ -981,6 +1063,33 @@ This usually means:
981
1063
  reporter.reportStaticTest(staticDescription, "skip", skipMessage, staticLevel);
982
1064
  continue;
983
1065
  }
1066
+ if (Array.isArray(test.setup) && test.setup.length > 0) {
1067
+ let isAllowedType2 = function(t) {
1068
+ return allowedTypes.includes(t);
1069
+ };
1070
+ var isAllowedType = isAllowedType2;
1071
+ const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
1072
+ const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
1073
+ const toSetupAction = (a) => ({
1074
+ ...a,
1075
+ type: isAllowedType2(a.type) ? a.type : "click"
1076
+ });
1077
+ const setupActions = test.setup.map(toSetupAction);
1078
+ const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, staticDescription, ["submenu", "submenuTrigger", "submenuItems"]);
1079
+ if (setupResult.skip) {
1080
+ skipped.push(setupResult.message || "Setup action skipped");
1081
+ reporter.reportStaticTest(staticDescription, "skip", setupResult.message, staticLevel);
1082
+ continue;
1083
+ }
1084
+ if (!setupResult.success) {
1085
+ const failure = `Static setup failed: ${setupResult.error}`;
1086
+ const outcome = classifyFailure(failure, test.level);
1087
+ if (outcome.status === "fail") staticFailed += 1;
1088
+ if (outcome.status === "warn") staticWarnings += 1;
1089
+ reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
1090
+ continue;
1091
+ }
1092
+ }
984
1093
  const targetSelector = componentContract.selectors[test.target];
985
1094
  if (!targetSelector) {
986
1095
  const failure = `Selector for target ${test.target} not found.`;
@@ -1107,31 +1216,26 @@ This usually means:
1107
1216
  const dynamicLevel = normalizeLevel(dynamicTest.level);
1108
1217
  const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
1109
1218
  if (Array.isArray(setup) && setup.length > 0) {
1110
- for (const setupAct of setup) {
1111
- let setupResult;
1112
- if (setupAct.type === "focus") {
1113
- if (setupAct.target === "relative" && setupAct.relativeTarget) {
1114
- setupResult = await actionExecutor.focus("relative", setupAct.relativeTarget);
1115
- } else {
1116
- setupResult = await actionExecutor.focus(setupAct.target);
1117
- }
1118
- } else if (setupAct.type === "type" && setupAct.value) {
1119
- setupResult = await actionExecutor.type(setupAct.target, setupAct.value);
1120
- } else if (setupAct.type === "click") {
1121
- setupResult = await actionExecutor.click(setupAct.target, setupAct.relativeTarget);
1122
- } else if (setupAct.type === "keypress" && setupAct.key) {
1123
- setupResult = await actionExecutor.keypress(setupAct.target, setupAct.key);
1124
- } else if (setupAct.type === "hover") {
1125
- setupResult = await actionExecutor.hover(setupAct.target, setupAct.relativeTarget);
1126
- } else {
1127
- continue;
1128
- }
1129
- if (!setupResult.success) {
1130
- const setupMsg = setupResult.error || "Setup action failed";
1131
- const outcome = classifyFailure(`Setup failed: ${setupMsg}`, dynamicTest.level);
1132
- reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, outcome.status, outcome.detail);
1133
- continue;
1134
- }
1219
+ let isAllowedType2 = function(t) {
1220
+ return allowedTypes.includes(t);
1221
+ };
1222
+ var isAllowedType = isAllowedType2;
1223
+ const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
1224
+ const toSetupAction = (a) => ({
1225
+ ...a,
1226
+ type: isAllowedType2(a.type) ? a.type : "click"
1227
+ });
1228
+ const setupActions = setup.map(toSetupAction);
1229
+ const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, dynamicTest.description, ["submenu", "submenuTrigger", "submenuItems"]);
1230
+ if (setupResult.skip) {
1231
+ skipped.push(setupResult.message || "Setup action skipped");
1232
+ reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, "skip", setupResult.message);
1233
+ continue;
1234
+ }
1235
+ if (!setupResult.success) {
1236
+ const outcome = classifyFailure(`Setup failed: ${setupResult.error}`, dynamicTest.level);
1237
+ reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, outcome.status, outcome.detail);
1238
+ continue;
1135
1239
  }
1136
1240
  }
1137
1241
  const failuresBeforeTest = failures.length;
@@ -35,7 +35,7 @@ var StrategyRegistry = class {
35
35
  registerBuiltInStrategies() {
36
36
  this.builtInStrategies.set(
37
37
  "menu",
38
- () => import("./MenuComponentStrategy-JAMTCSNF.js").then(
38
+ () => import("./MenuComponentStrategy-VYCC2XOM.js").then(
39
39
  (m) => m.MenuComponentStrategy
40
40
  )
41
41
  );
@@ -558,13 +558,21 @@ var AssertionRunner = class {
558
558
  /**
559
559
  * Validate focus assertion
560
560
  */
561
- async validateFocus(target, targetName, failureMessage, testDescription) {
561
+ async validateFocus(target, targetName, expectedFocus, failureMessage, testDescription) {
562
562
  try {
563
- await (0, test_exports.expect)(target).toBeFocused({ timeout: this.timeoutMs });
564
- return {
565
- success: true,
566
- passMessage: `${targetName} has focus as expected. Test: "${testDescription}".`
567
- };
563
+ if (expectedFocus) {
564
+ await (0, test_exports.expect)(target).toBeFocused({ timeout: this.timeoutMs });
565
+ return {
566
+ success: true,
567
+ passMessage: `${targetName} has focus as expected. Test: "${testDescription}".`
568
+ };
569
+ } else {
570
+ await (0, test_exports.expect)(target).not.toBeFocused({ timeout: this.timeoutMs });
571
+ return {
572
+ success: true,
573
+ passMessage: `${targetName} does not have focus as expected. Test: "${testDescription}".`
574
+ };
575
+ }
568
576
  } catch {
569
577
  const actualFocus = await this.page.evaluate(() => {
570
578
  const focused = document.activeElement;
@@ -650,7 +658,9 @@ var AssertionRunner = class {
650
658
  }
651
659
  return { success: false, failMessage: "Missing expectedValue for toHaveValue assertion" };
652
660
  case "toHaveFocus":
653
- return this.validateFocus(target, assertion.target, assertion.failureMessage || "", testDescription);
661
+ return this.validateFocus(target, assertion.target, true, assertion.failureMessage || "", testDescription);
662
+ case "notToHaveFocus":
663
+ return this.validateFocus(target, assertion.target, false, assertion.failureMessage || "", testDescription);
654
664
  case "toHaveRole":
655
665
  if (assertion.expectedValue !== void 0) {
656
666
  return this.validateRole(target, assertion.target, assertion.expectedValue, assertion.failureMessage || "", testDescription);
@@ -667,12 +677,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
667
677
  const componentConfig = config?.test?.components?.find((c) => c.name === componentName);
668
678
  const isCustomContract = !!componentConfig?.contractPath;
669
679
  const reporter = new ContractReporter(true, isCustomContract);
670
- const defaultTimeouts = {
671
- actionTimeoutMs: 400,
672
- assertionTimeoutMs: 400,
673
- navigationTimeoutMs: 3e4,
674
- componentReadyTimeoutMs: 5e3
675
- };
680
+ const defaultTimeouts = { actionTimeoutMs: 400, assertionTimeoutMs: 400, navigationTimeoutMs: 3e4, componentReadyTimeoutMs: 5e3 };
676
681
  const globalDisableTimeouts = config?.test?.disableTimeouts === true;
677
682
  const componentDisableTimeouts = componentConfig?.disableTimeouts === true;
678
683
  const disableTimeouts = componentDisableTimeouts || globalDisableTimeouts;
@@ -684,11 +689,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
684
689
  }
685
690
  return value;
686
691
  };
687
- const actionTimeoutMs = resolveTimeout(
688
- componentConfig?.actionTimeoutMs,
689
- config?.test?.actionTimeoutMs,
690
- defaultTimeouts.actionTimeoutMs
691
- );
692
+ const actionTimeoutMs = resolveTimeout(componentConfig?.actionTimeoutMs, config?.test?.actionTimeoutMs, defaultTimeouts.actionTimeoutMs);
692
693
  const assertionTimeoutMs = resolveTimeout(
693
694
  componentConfig?.assertionTimeoutMs,
694
695
  config?.test?.assertionTimeoutMs,
@@ -788,8 +789,8 @@ This usually means:
788
789
  );
789
790
  }
790
791
  reporter.start(componentName, totalTests, apgUrl);
791
- if (componentName === "menu" && componentContract.selectors.trigger) {
792
- await page.locator(componentContract.selectors.trigger).first().waitFor({ state: "attached", timeout: componentReadyTimeoutMs }).catch(() => {
792
+ if (componentName === "menu" && componentContract.selectors.main) {
793
+ await page.locator(componentContract.selectors.main).first().waitFor({ state: "visible", timeout: componentReadyTimeoutMs }).catch(() => {
793
794
  });
794
795
  }
795
796
  const hasSubmenuCapability = componentName === "menu" && !!componentContract.selectors.submenuTrigger ? await page.locator(componentContract.selectors.submenuTrigger).count() > 0 : false;
@@ -798,7 +799,42 @@ This usually means:
798
799
  let staticFailed = 0;
799
800
  let staticWarnings = 0;
800
801
  for (const rel of componentContract.relationships || []) {
802
+ if (strategy && typeof strategy.resetState === "function") {
803
+ try {
804
+ await strategy.resetState(page);
805
+ } catch (err) {
806
+ warnings.push(`Warning: resetState failed before relationship test: ${err instanceof Error ? err.message : String(err)}`);
807
+ }
808
+ }
801
809
  const relationshipLevel = normalizeLevel(rel.level);
810
+ if (Array.isArray(rel.setup) && rel.setup.length > 0) {
811
+ let isAllowedType2 = function(t) {
812
+ return allowedTypes.includes(t);
813
+ };
814
+ var isAllowedType = isAllowedType2;
815
+ const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
816
+ const relDescription = rel.type === "aria-reference" ? `${rel.from}.${rel.attribute} references ${rel.to}` : `${rel.parent} contains ${rel.child}`;
817
+ const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
818
+ const toSetupAction = (a) => ({
819
+ ...a,
820
+ type: isAllowedType2(a.type) ? a.type : "click"
821
+ });
822
+ const setupActions = rel.setup.map(toSetupAction);
823
+ const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, relDescription, ["submenu", "submenuTrigger", "submenuItems"]);
824
+ if (setupResult.skip) {
825
+ skipped.push(setupResult.message || "Setup action skipped");
826
+ reporter.reportStaticTest(relDescription, "skip", setupResult.message, relationshipLevel);
827
+ continue;
828
+ }
829
+ if (!setupResult.success) {
830
+ const failure = `Relationship setup failed: ${setupResult.error}`;
831
+ const outcome = classifyFailure(failure, rel.level);
832
+ if (outcome.status === "fail") staticFailed += 1;
833
+ if (outcome.status === "warn") staticWarnings += 1;
834
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
835
+ continue;
836
+ }
837
+ }
802
838
  if (componentName === "menu" && !hasSubmenuCapability) {
803
839
  const involvesSubmenu = isSubmenuRelation(rel);
804
840
  if (involvesSubmenu) {
@@ -971,7 +1007,53 @@ This usually means:
971
1007
  }
972
1008
  }
973
1009
  const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
1010
+ async function runSetupActions(setup, actionExecutor, strategy2, page2, description, skipKeywords = []) {
1011
+ if (!Array.isArray(setup) || setup.length === 0) return { success: true };
1012
+ if (strategy2 && typeof strategy2.resetState === "function") {
1013
+ await strategy2.resetState(page2);
1014
+ }
1015
+ for (const setupAct of setup) {
1016
+ let setupResult = { success: true };
1017
+ try {
1018
+ if (setupAct.type === "focus") {
1019
+ if (setupAct.target === "relative" && setupAct.relativeTarget) {
1020
+ setupResult = await actionExecutor.focus("relative", setupAct.relativeTarget);
1021
+ } else {
1022
+ setupResult = await actionExecutor.focus(setupAct.target);
1023
+ }
1024
+ } else if (setupAct.type === "type" && setupAct.value) {
1025
+ setupResult = await actionExecutor.type(setupAct.target, setupAct.value);
1026
+ } else if (setupAct.type === "click") {
1027
+ setupResult = await actionExecutor.click(setupAct.target, setupAct.relativeTarget);
1028
+ } else if (setupAct.type === "keypress" && setupAct.key) {
1029
+ setupResult = await actionExecutor.keypress(setupAct.target, setupAct.key);
1030
+ } else if (setupAct.type === "hover") {
1031
+ setupResult = await actionExecutor.hover(setupAct.target, setupAct.relativeTarget);
1032
+ } else {
1033
+ continue;
1034
+ }
1035
+ } catch (err) {
1036
+ setupResult = { success: false, error: err instanceof Error ? err.message : String(err) };
1037
+ }
1038
+ if (!setupResult.success) {
1039
+ const setupMsg = setupResult.error || "Setup action failed";
1040
+ const isSkip = skipKeywords.some((kw) => description.includes(kw) || setupMsg.includes(kw));
1041
+ if (isSkip) {
1042
+ return { success: false, skip: true, message: `Skipping test - capability not present: ${setupMsg}` };
1043
+ }
1044
+ return { success: false, error: setupMsg };
1045
+ }
1046
+ }
1047
+ return { success: true };
1048
+ }
974
1049
  for (const test of componentContract.static[0]?.assertions || []) {
1050
+ if (strategy && typeof strategy.resetState === "function") {
1051
+ try {
1052
+ await strategy.resetState(page);
1053
+ } catch (err) {
1054
+ warnings.push(`Warning: resetState failed before static test: ${err instanceof Error ? err.message : String(err)}`);
1055
+ }
1056
+ }
975
1057
  if (test.target === "relative") continue;
976
1058
  const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
977
1059
  const staticLevel = normalizeLevel(test.level);
@@ -981,6 +1063,33 @@ This usually means:
981
1063
  reporter.reportStaticTest(staticDescription, "skip", skipMessage, staticLevel);
982
1064
  continue;
983
1065
  }
1066
+ if (Array.isArray(test.setup) && test.setup.length > 0) {
1067
+ let isAllowedType2 = function(t) {
1068
+ return allowedTypes.includes(t);
1069
+ };
1070
+ var isAllowedType = isAllowedType2;
1071
+ const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
1072
+ const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
1073
+ const toSetupAction = (a) => ({
1074
+ ...a,
1075
+ type: isAllowedType2(a.type) ? a.type : "click"
1076
+ });
1077
+ const setupActions = test.setup.map(toSetupAction);
1078
+ const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, staticDescription, ["submenu", "submenuTrigger", "submenuItems"]);
1079
+ if (setupResult.skip) {
1080
+ skipped.push(setupResult.message || "Setup action skipped");
1081
+ reporter.reportStaticTest(staticDescription, "skip", setupResult.message, staticLevel);
1082
+ continue;
1083
+ }
1084
+ if (!setupResult.success) {
1085
+ const failure = `Static setup failed: ${setupResult.error}`;
1086
+ const outcome = classifyFailure(failure, test.level);
1087
+ if (outcome.status === "fail") staticFailed += 1;
1088
+ if (outcome.status === "warn") staticWarnings += 1;
1089
+ reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
1090
+ continue;
1091
+ }
1092
+ }
984
1093
  const targetSelector = componentContract.selectors[test.target];
985
1094
  if (!targetSelector) {
986
1095
  const failure = `Selector for target ${test.target} not found.`;
@@ -1107,31 +1216,26 @@ This usually means:
1107
1216
  const dynamicLevel = normalizeLevel(dynamicTest.level);
1108
1217
  const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
1109
1218
  if (Array.isArray(setup) && setup.length > 0) {
1110
- for (const setupAct of setup) {
1111
- let setupResult;
1112
- if (setupAct.type === "focus") {
1113
- if (setupAct.target === "relative" && setupAct.relativeTarget) {
1114
- setupResult = await actionExecutor.focus("relative", setupAct.relativeTarget);
1115
- } else {
1116
- setupResult = await actionExecutor.focus(setupAct.target);
1117
- }
1118
- } else if (setupAct.type === "type" && setupAct.value) {
1119
- setupResult = await actionExecutor.type(setupAct.target, setupAct.value);
1120
- } else if (setupAct.type === "click") {
1121
- setupResult = await actionExecutor.click(setupAct.target, setupAct.relativeTarget);
1122
- } else if (setupAct.type === "keypress" && setupAct.key) {
1123
- setupResult = await actionExecutor.keypress(setupAct.target, setupAct.key);
1124
- } else if (setupAct.type === "hover") {
1125
- setupResult = await actionExecutor.hover(setupAct.target, setupAct.relativeTarget);
1126
- } else {
1127
- continue;
1128
- }
1129
- if (!setupResult.success) {
1130
- const setupMsg = setupResult.error || "Setup action failed";
1131
- const outcome = classifyFailure(`Setup failed: ${setupMsg}`, dynamicTest.level);
1132
- reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, outcome.status, outcome.detail);
1133
- continue;
1134
- }
1219
+ let isAllowedType2 = function(t) {
1220
+ return allowedTypes.includes(t);
1221
+ };
1222
+ var isAllowedType = isAllowedType2;
1223
+ const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
1224
+ const toSetupAction = (a) => ({
1225
+ ...a,
1226
+ type: isAllowedType2(a.type) ? a.type : "click"
1227
+ });
1228
+ const setupActions = setup.map(toSetupAction);
1229
+ const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, dynamicTest.description, ["submenu", "submenuTrigger", "submenuItems"]);
1230
+ if (setupResult.skip) {
1231
+ skipped.push(setupResult.message || "Setup action skipped");
1232
+ reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, "skip", setupResult.message);
1233
+ continue;
1234
+ }
1235
+ if (!setupResult.success) {
1236
+ const outcome = classifyFailure(`Setup failed: ${setupResult.error}`, dynamicTest.level);
1237
+ reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, outcome.status, outcome.detail);
1238
+ continue;
1135
1239
  }
1136
1240
  }
1137
1241
  const failuresBeforeTest = failures.length;