aria-ease 6.10.0 → 6.12.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.
Files changed (60) hide show
  1. package/README.md +2 -2
  2. package/{bin/buildContracts-S22V7AGV.js → dist/buildContracts-FT6KWUJN.js} +3 -3
  3. package/dist/{chunk-XERMSYEH.js → chunk-FZ7GMIJB.js} +0 -21
  4. package/{bin/chunk-NI3MQCAS.js → dist/chunk-GJGUY643.js} +2 -2
  5. package/{bin → dist}/cli.cjs +30 -67
  6. package/{bin → dist}/cli.js +4 -4
  7. package/dist/{configLoader-DWHOHXHL.js → configLoader-Q7N5XV4P.js} +2 -2
  8. package/{bin/configLoader-UJZHQBYS.js → dist/configLoader-REHK3S3Q.js} +1 -1
  9. package/{bin/contractTestRunnerPlaywright-QDXSK3FE.js → dist/contractTestRunnerPlaywright-DIXP5DQ3.js} +5 -20
  10. package/dist/{contractTestRunnerPlaywright-WNWQYSXZ.js → contractTestRunnerPlaywright-EWAWQVHT.js} +5 -20
  11. package/dist/index.cjs +164 -122
  12. package/dist/index.d.cts +3 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.js +155 -71
  15. package/dist/src/{Types.d-yGC2bBaB.d.ts → Types.d-DYfYR3Vc.d.cts} +1 -1
  16. package/dist/src/{Types.d-yGC2bBaB.d.cts → Types.d-DYfYR3Vc.d.ts} +1 -1
  17. package/dist/src/accordion/index.d.cts +1 -1
  18. package/dist/src/accordion/index.d.ts +1 -1
  19. package/dist/src/block/index.d.cts +1 -1
  20. package/dist/src/block/index.d.ts +1 -1
  21. package/dist/src/checkbox/index.d.cts +1 -1
  22. package/dist/src/checkbox/index.d.ts +1 -1
  23. package/dist/src/combobox/index.d.cts +1 -1
  24. package/dist/src/combobox/index.d.ts +1 -1
  25. package/dist/src/menu/index.d.cts +1 -1
  26. package/dist/src/menu/index.d.ts +1 -1
  27. package/dist/src/radio/index.d.cts +1 -1
  28. package/dist/src/radio/index.d.ts +1 -1
  29. package/dist/src/tabs/index.d.cts +1 -1
  30. package/dist/src/tabs/index.d.ts +1 -1
  31. package/dist/src/toggle/index.d.cts +1 -1
  32. package/dist/src/toggle/index.d.ts +1 -1
  33. package/dist/src/utils/test/{chunk-XERMSYEH.js → chunk-FZ7GMIJB.js} +1 -21
  34. package/dist/src/utils/test/{configLoader-SHJSRG2A.js → configLoader-NA7IBCS3.js} +2 -2
  35. package/dist/src/utils/test/{contractTestRunnerPlaywright-Z2AHXSNM.js → contractTestRunnerPlaywright-CIZOXYRW.js} +5 -19
  36. package/dist/src/utils/test/dsl/index.cjs +139 -60
  37. package/dist/src/utils/test/dsl/index.d.cts +3 -0
  38. package/dist/src/utils/test/dsl/index.d.ts +3 -0
  39. package/dist/src/utils/test/dsl/index.js +139 -60
  40. package/dist/src/utils/test/index.cjs +19 -55
  41. package/dist/src/utils/test/index.js +16 -10
  42. package/{bin/test-O3J4ZPQR.js → dist/test-HBPCSYH5.js} +16 -11
  43. package/package.json +5 -7
  44. package/bin/AccordionComponentStrategy-4ZEIQ2V6.js +0 -42
  45. package/bin/ComboboxComponentStrategy-OGRVZXAF.js +0 -64
  46. package/bin/MenuComponentStrategy-JAMTCSNF.js +0 -81
  47. package/bin/TabsComponentStrategy-3SQURPMX.js +0 -29
  48. package/bin/chunk-I2KLQ2HA.js +0 -22
  49. package/bin/chunk-PK5L2SAF.js +0 -17
  50. package/bin/chunk-XERMSYEH.js +0 -363
  51. package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +0 -290
  52. package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +0 -463
  53. package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +0 -562
  54. package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +0 -361
  55. /package/{bin → dist}/audit-RM6TCZ5C.js +0 -0
  56. /package/{bin → dist}/badgeHelper-JOWO6RQG.js +0 -0
  57. /package/{bin → dist}/chunk-JJEPLK7L.js +0 -0
  58. /package/{bin → dist}/cli.d.cts +0 -0
  59. /package/{bin → dist}/cli.d.ts +0 -0
  60. /package/{bin → dist}/formatters-32KQIIYS.js +0 -0
package/dist/index.cjs CHANGED
@@ -31,31 +31,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  ));
32
32
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
33
 
34
- // src/utils/test/contract/contract.json
35
- var contract_default;
36
- var init_contract = __esm({
37
- "src/utils/test/contract/contract.json"() {
38
- contract_default = {
39
- menu: {
40
- path: "./aria-contracts/menu/menu.contract.json",
41
- component: "menu"
42
- },
43
- "combobox.listbox": {
44
- path: "./aria-contracts/combobox/combobox.listbox.contract.json",
45
- component: "combobox.listbox"
46
- },
47
- accordion: {
48
- path: "./aria-contracts/accordion/accordion.contract.json",
49
- component: "accordion"
50
- },
51
- tabs: {
52
- path: "./aria-contracts/tabs/tabs.contract.json",
53
- component: "tabs"
54
- }
55
- };
56
- }
57
- });
58
-
59
34
  // src/utils/test/src/ContractReporter.ts
60
35
  var ContractReporter;
61
36
  var init_ContractReporter = __esm({
@@ -480,8 +455,8 @@ function validateConfig(config) {
480
455
  if (typeof comp.name !== "string") {
481
456
  errors.push(`test.components[${idx}].name must be a string`);
482
457
  }
483
- if (comp.path !== void 0 && typeof comp.path !== "string") {
484
- errors.push(`test.components[${idx}].path must be a string when provided`);
458
+ if (comp.contractPath !== void 0 && typeof comp.contractPath !== "string") {
459
+ errors.push(`test.components[${idx}].contractPath must be a string when provided`);
485
460
  }
486
461
  if (comp.strategyPath !== void 0 && typeof comp.strategyPath !== "string") {
487
462
  errors.push(`test.components[${idx}].strategyPath must be a string when provided`);
@@ -885,12 +860,6 @@ var init_StrategyRegistry = __esm({
885
860
  (m) => m.TabsComponentStrategy
886
861
  )
887
862
  );
888
- this.builtInStrategies.set(
889
- "combobox.listbox",
890
- () => Promise.resolve().then(() => (init_ComboboxComponentStrategy(), ComboboxComponentStrategy_exports)).then(
891
- (m) => m.ComboboxComponentStrategy
892
- )
893
- );
894
863
  }
895
864
  /**
896
865
  * Load a strategy - either from custom path or built-in registry
@@ -937,15 +906,14 @@ var init_StrategyRegistry = __esm({
937
906
  });
938
907
 
939
908
  // src/utils/test/src/ComponentDetector.ts
940
- var import_fs, import_path3, import_meta2, ComponentDetector;
909
+ var import_fs, import_path3, import_meta, ComponentDetector;
941
910
  var init_ComponentDetector = __esm({
942
911
  "src/utils/test/src/ComponentDetector.ts"() {
943
912
  "use strict";
944
913
  import_fs = require("fs");
945
914
  import_path3 = __toESM(require("path"), 1);
946
- init_contract();
947
915
  init_StrategyRegistry();
948
- import_meta2 = {};
916
+ import_meta = {};
949
917
  ComponentDetector = class {
950
918
  static strategyRegistry = new StrategyRegistry();
951
919
  static isComponentConfig(value) {
@@ -965,11 +933,7 @@ var init_ComponentDetector = __esm({
965
933
  */
966
934
  static async detect(componentName, componentConfig, actionTimeoutMs = 400, assertionTimeoutMs = 400, configBaseDir) {
967
935
  const typedComponentConfig = this.isComponentConfig(componentConfig) ? componentConfig : void 0;
968
- let contractPath = typedComponentConfig?.path;
969
- if (!contractPath) {
970
- const contractTyped = contract_default;
971
- contractPath = contractTyped[componentName]?.path;
972
- }
936
+ const contractPath = typedComponentConfig?.contractPath;
973
937
  if (!contractPath) {
974
938
  throw new Error(`Contract path not found for component: ${componentName}`);
975
939
  }
@@ -988,7 +952,7 @@ var init_ComponentDetector = __esm({
988
952
  (0, import_fs.readFileSync)(cwdResolved, "utf-8");
989
953
  return cwdResolved;
990
954
  } catch {
991
- return new URL(contractPath, import_meta2.url).pathname;
955
+ return new URL(contractPath, import_meta.url).pathname;
992
956
  }
993
957
  })();
994
958
  const contractData = (0, import_fs.readFileSync)(resolvedPath, "utf-8");
@@ -1002,7 +966,7 @@ var init_ComponentDetector = __esm({
1002
966
  if (!strategyClass) {
1003
967
  return null;
1004
968
  }
1005
- const mainSelector = selectors.trigger || selectors.input || selectors.tablist || selectors.container;
969
+ const mainSelector = selectors.main;
1006
970
  if (componentName === "tabs") {
1007
971
  return new strategyClass(mainSelector, selectors);
1008
972
  }
@@ -1577,7 +1541,7 @@ __export(contractTestRunnerPlaywright_exports, {
1577
1541
  });
1578
1542
  async function runContractTestsPlaywright(componentName, url, strictness, config, configBaseDir) {
1579
1543
  const componentConfig = config?.test?.components?.find((c) => c.name === componentName);
1580
- const isCustomContract = !!componentConfig?.path;
1544
+ const isCustomContract = !!componentConfig?.contractPath;
1581
1545
  const reporter = new ContractReporter(true, isCustomContract);
1582
1546
  const defaultTimeouts = {
1583
1547
  actionTimeoutMs: 400,
@@ -1617,11 +1581,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
1617
1581
  defaultTimeouts.componentReadyTimeoutMs
1618
1582
  );
1619
1583
  const strictnessMode = normalizeStrictness(strictness);
1620
- let contractPath = componentConfig?.path;
1621
- if (!contractPath) {
1622
- const contractTyped = contract_default;
1623
- contractPath = contractTyped[componentName]?.path;
1624
- }
1584
+ const contractPath = componentConfig?.contractPath;
1625
1585
  if (!contractPath) {
1626
1586
  throw new Error(`Contract path not found for component: ${componentName}`);
1627
1587
  }
@@ -1640,7 +1600,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
1640
1600
  (0, import_fs2.readFileSync)(cwdResolved, "utf-8");
1641
1601
  return cwdResolved;
1642
1602
  } catch {
1643
- return new URL(contractPath, import_meta3.url).pathname;
1603
+ return new URL(contractPath, import_meta2.url).pathname;
1644
1604
  }
1645
1605
  })();
1646
1606
  const contractData = (0, import_fs2.readFileSync)(resolvedPath, "utf-8");
@@ -2112,20 +2072,19 @@ This usually means:
2112
2072
  }
2113
2073
  return { passes, failures, skipped, warnings };
2114
2074
  }
2115
- var import_fs2, import_path4, import_meta3;
2075
+ var import_fs2, import_path4, import_meta2;
2116
2076
  var init_contractTestRunnerPlaywright = __esm({
2117
2077
  "src/utils/test/src/contractTestRunnerPlaywright.ts"() {
2118
2078
  "use strict";
2119
2079
  import_fs2 = require("fs");
2120
2080
  import_path4 = __toESM(require("path"), 1);
2121
- init_contract();
2122
2081
  init_playwrightTestHarness();
2123
2082
  init_ComponentDetector();
2124
2083
  init_ContractReporter();
2125
2084
  init_ActionExecutor();
2126
2085
  init_AssertionRunner();
2127
2086
  init_strictness();
2128
- import_meta3 = {};
2087
+ import_meta2 = {};
2129
2088
  }
2130
2089
  });
2131
2090
 
@@ -3761,90 +3720,141 @@ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation
3761
3720
  }
3762
3721
 
3763
3722
  // src/utils/test/dsl/src/state-packs/comboboxStatePack.ts
3723
+ function hasCapabilities(ctx, requiredCaps) {
3724
+ return requiredCaps.some((cap) => ctx.capabilities.includes(cap));
3725
+ }
3726
+ function resolveSetup(setup, ctx) {
3727
+ if (Array.isArray(setup) && setup.length && !setup[0].when) {
3728
+ setup = [{ when: ["keyboard"], steps: () => setup }];
3729
+ }
3730
+ for (const strat of setup) {
3731
+ if (hasCapabilities(ctx, strat.when)) {
3732
+ return strat.steps(ctx);
3733
+ }
3734
+ }
3735
+ throw new Error(
3736
+ `No setup strategy matches capabilities: ${ctx.capabilities.join(", ")}`
3737
+ );
3738
+ }
3764
3739
  var COMBOBOX_STATES = {
3765
3740
  "listbox.open": {
3766
- setup: openCombobox(),
3767
- assertion: isComboboxOpen()
3741
+ setup: [
3742
+ {
3743
+ when: ["keyboard", "textInput"],
3744
+ steps: () => [
3745
+ { type: "keypress", target: "input", key: "ArrowDown" }
3746
+ ]
3747
+ },
3748
+ {
3749
+ when: ["pointer"],
3750
+ steps: () => [
3751
+ { type: "click", target: "button" }
3752
+ ]
3753
+ }
3754
+ ],
3755
+ assertion: isComboboxOpen
3768
3756
  },
3769
3757
  "listbox.closed": {
3770
- setup: closeCombobox(),
3771
- assertion: isComboboxClosed()
3758
+ setup: [
3759
+ {
3760
+ when: ["keyboard"],
3761
+ steps: () => [
3762
+ /* { type: "keypress", target: "input", key: "Escape" } */
3763
+ ]
3764
+ },
3765
+ {
3766
+ when: ["pointer"],
3767
+ steps: () => [
3768
+ /* { type: "click", target: "button" } */
3769
+ ]
3770
+ }
3771
+ ],
3772
+ assertion: isComboboxClosed
3772
3773
  },
3773
3774
  "input.focused": {
3774
- setup: focusInput(),
3775
- assertion: [
3776
- ...isInputFocused()
3777
- ]
3775
+ setup: [
3776
+ {
3777
+ when: ["keyboard"],
3778
+ steps: () => [
3779
+ { type: "focus", target: "input" }
3780
+ ]
3781
+ }
3782
+ ],
3783
+ assertion: isInputFocused
3778
3784
  },
3779
3785
  "input.filled": {
3780
- setup: fillInput(),
3781
- assertion: [
3782
- ...isInputFilled()
3783
- ]
3786
+ setup: [
3787
+ {
3788
+ when: ["keyboard", "textInput"],
3789
+ steps: () => [
3790
+ { type: "type", target: "input", value: "test" }
3791
+ ]
3792
+ }
3793
+ ],
3794
+ assertion: isInputFilled
3784
3795
  },
3785
3796
  "activeOption.first": {
3786
3797
  requires: ["listbox.open"],
3787
3798
  setup: [
3788
- { type: "keypress", target: "input", key: "ArrowDown" }
3799
+ {
3800
+ when: ["keyboard"],
3801
+ steps: () => [
3802
+ { type: "keypress", target: "input", key: "ArrowDown" }
3803
+ ]
3804
+ }
3789
3805
  ],
3790
- assertion: [
3791
- ...isActiveDescendantNotEmpty()
3792
- ]
3806
+ assertion: isActiveDescendantNotEmpty
3793
3807
  },
3794
3808
  "activeOption.last": {
3795
3809
  requires: ["activeOption.first"],
3796
3810
  setup: [
3797
- { type: "keypress", target: "input", key: "ArrowUp" }
3811
+ {
3812
+ when: ["keyboard"],
3813
+ steps: () => [
3814
+ { type: "keypress", target: "input", key: "ArrowUp" }
3815
+ ]
3816
+ }
3798
3817
  ],
3799
- assertion: [
3800
- ...isActiveDescendantNotEmpty()
3801
- ]
3818
+ assertion: isActiveDescendantNotEmpty
3802
3819
  },
3803
3820
  "selectedOption.first": {
3804
3821
  requires: ["listbox.open"],
3805
3822
  setup: [
3806
- { type: "click", target: "relative", relativeTarget: "first" }
3823
+ {
3824
+ when: ["pointer"],
3825
+ steps: () => [
3826
+ { type: "click", target: "relative", relativeTarget: "first" }
3827
+ ]
3828
+ }
3807
3829
  ],
3808
- assertion: [
3809
- ...isAriaSelected("first")
3810
- ]
3830
+ assertion: () => isAriaSelected("first")
3811
3831
  },
3812
3832
  "selectedOption.last": {
3813
3833
  requires: ["listbox.open"],
3814
3834
  setup: [
3815
- { type: "click", target: "relative", relativeTarget: "last" }
3835
+ {
3836
+ when: ["pointer"],
3837
+ steps: () => [
3838
+ { type: "click", target: "relative", relativeTarget: "last" }
3839
+ ]
3840
+ }
3816
3841
  ],
3817
- assertion: [
3818
- ...isAriaSelected("first")
3819
- ]
3842
+ assertion: () => isAriaSelected("last")
3820
3843
  }
3821
3844
  };
3822
- function openCombobox() {
3823
- return [
3824
- { type: "keypress", target: "input", key: "ArrowDown" }
3825
- ];
3826
- }
3827
- function closeCombobox() {
3828
- return [
3829
- { type: "keypress", target: "input", key: "Escape" }
3830
- ];
3831
- }
3832
- function focusInput() {
3833
- return [
3834
- { type: "focus", target: "input" }
3835
- ];
3836
- }
3837
- function fillInput() {
3838
- return [
3839
- { type: "type", target: "input", value: "test" }
3840
- ];
3841
- }
3842
3845
  function isComboboxOpen() {
3843
3846
  return [
3844
3847
  {
3845
3848
  target: "listbox",
3846
3849
  assertion: "toBeVisible",
3847
3850
  failureMessage: "Expected listbox to be visible"
3851
+ },
3852
+ {
3853
+ target: "input",
3854
+ assertion: "toHaveAttribute",
3855
+ attribute: "aria-expanded",
3856
+ expectedValue: "true",
3857
+ failureMessage: "Expect combobox input to have aria-expanded='true'"
3848
3858
  }
3849
3859
  ];
3850
3860
  }
@@ -3854,6 +3864,13 @@ function isComboboxClosed() {
3854
3864
  target: "listbox",
3855
3865
  assertion: "notToBeVisible",
3856
3866
  failureMessage: "Expected listbox to be closed"
3867
+ },
3868
+ {
3869
+ target: "input",
3870
+ assertion: "toHaveAttribute",
3871
+ attribute: "aria-expanded",
3872
+ expectedValue: "false",
3873
+ failureMessage: "Expect combobox input to have aria-expanded='false'"
3857
3874
  }
3858
3875
  ];
3859
3876
  }
@@ -3876,7 +3893,7 @@ function isAriaSelected(index) {
3876
3893
  assertion: "toHaveAttribute",
3877
3894
  attribute: "aria-selected",
3878
3895
  expectedValue: "true",
3879
- failureMessage: `Expected aria-selected on ${index} option to be true`
3896
+ failureMessage: `Expected ${index} option to have aria-selected='true'`
3880
3897
  }
3881
3898
  ];
3882
3899
  }
@@ -3902,7 +3919,7 @@ function isInputFilled() {
3902
3919
 
3903
3920
  // src/utils/test/dsl/src/contractBuilder.ts
3904
3921
  var STATE_PACKS = {
3905
- "combobox.listbox": COMBOBOX_STATES
3922
+ "combobox": COMBOBOX_STATES
3906
3923
  // Add more mappings as needed
3907
3924
  };
3908
3925
  var FluentContract = class {
@@ -3936,11 +3953,13 @@ var ContractBuilder = class {
3936
3953
  const api = {
3937
3954
  ariaReference: (from, attribute, to) => ({
3938
3955
  required: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "required" }),
3939
- optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" })
3956
+ optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" }),
3957
+ recommended: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "recommended" })
3940
3958
  }),
3941
3959
  contains: (parent, child) => ({
3942
3960
  required: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "required" }),
3943
- optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" })
3961
+ optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" }),
3962
+ recommended: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "recommended" })
3944
3963
  })
3945
3964
  };
3946
3965
  fn(api);
@@ -3951,7 +3970,8 @@ var ContractBuilder = class {
3951
3970
  target: (target) => ({
3952
3971
  has: (attribute, expectedValue) => ({
3953
3972
  required: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "required" }),
3954
- optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" })
3973
+ optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" }),
3974
+ recommended: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "recommended" })
3955
3975
  })
3956
3976
  })
3957
3977
  };
@@ -4022,7 +4042,17 @@ var DynamicTestBuilder = class {
4022
4042
  return this.parent;
4023
4043
  }
4024
4044
  _finalize() {
4025
- const resolveSetup = (stateName, visited = /* @__PURE__ */ new Set()) => {
4045
+ const capabilityMap = {
4046
+ keypress: "keyboard",
4047
+ click: "pointer",
4048
+ type: "textInput",
4049
+ focus: "keyboard",
4050
+ hover: "pointer"
4051
+ // add more mappings as needed
4052
+ };
4053
+ const capability = capabilityMap[this._as || "keyboard"] || (this._as || "keyboard");
4054
+ const ctx = { capabilities: [capability] };
4055
+ const resolveAllSetups = (stateName, visited = /* @__PURE__ */ new Set()) => {
4026
4056
  if (visited.has(stateName)) return [];
4027
4057
  visited.add(stateName);
4028
4058
  const s = this.statePack[stateName];
@@ -4030,22 +4060,30 @@ var DynamicTestBuilder = class {
4030
4060
  let actions = [];
4031
4061
  if (Array.isArray(s.requires)) {
4032
4062
  for (const req of s.requires) {
4033
- actions = actions.concat(resolveSetup(req, visited));
4063
+ actions = actions.concat(resolveAllSetups(req, visited));
4034
4064
  }
4035
4065
  }
4036
- if (s.setup) actions = actions.concat(s.setup);
4066
+ if (s.setup) actions = actions.concat(resolveSetup(s.setup, ctx));
4037
4067
  return actions;
4038
4068
  };
4039
4069
  const setup = [];
4040
4070
  for (const state of this._given) {
4041
- setup.push(...resolveSetup(state));
4071
+ setup.push(...resolveAllSetups(state));
4042
4072
  }
4043
4073
  const assertions = [];
4044
4074
  for (const state of this._then) {
4045
4075
  const s = this.statePack[state];
4046
- if (s && s.assertion) {
4047
- if (Array.isArray(s.assertion)) assertions.push(...s.assertion);
4048
- else assertions.push(s.assertion);
4076
+ if (s && s.assertion !== void 0) {
4077
+ let value = s.assertion;
4078
+ if (typeof value === "function") {
4079
+ try {
4080
+ value = value();
4081
+ } catch (e) {
4082
+ throw new Error(`Error calling assertion function for state '${state}': ${e.message}`);
4083
+ }
4084
+ }
4085
+ if (Array.isArray(value)) assertions.push(...value);
4086
+ else assertions.push(value);
4049
4087
  }
4050
4088
  }
4051
4089
  const action = [
@@ -4074,21 +4112,16 @@ function createContract(componentName, define) {
4074
4112
  var import_jest_axe = require("jest-axe");
4075
4113
 
4076
4114
  // src/utils/test/src/contractTestRunner.ts
4077
- init_contract();
4078
4115
  var import_promises = __toESM(require("fs/promises"), 1);
4079
4116
  init_ContractReporter();
4080
4117
  init_strictness();
4081
- var import_meta = {};
4082
- async function runContractTests(componentName, component, strictness) {
4118
+ async function runContractTests(contractPath, componentName, component, strictness) {
4083
4119
  const reporter = new ContractReporter(false);
4084
4120
  const strictnessMode = normalizeStrictness(strictness);
4085
- const contractTyped = contract_default;
4086
- const contractPath = contractTyped[componentName]?.path;
4087
4121
  if (!contractPath) {
4088
- throw new Error(`No contract found for component: ${componentName}`);
4122
+ throw new Error(`No contract path provided for component: ${componentName}`);
4089
4123
  }
4090
- const resolvedPath = new URL(contractPath, import_meta.url).pathname;
4091
- const contractData = await import_promises.default.readFile(resolvedPath, "utf-8");
4124
+ const contractData = await import_promises.default.readFile(contractPath, "utf-8");
4092
4125
  const componentContract = JSON.parse(contractData);
4093
4126
  const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
4094
4127
  reporter.start(componentName, totalTests);
@@ -4322,7 +4355,16 @@ Please start your dev server and try again.`
4322
4355
  }
4323
4356
  } else if (component) {
4324
4357
  console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
4325
- contract = await runContractTests(componentName, component, strictness);
4358
+ const contractPath = config.test?.components?.find((comp) => comp?.name === componentName)?.contractPath;
4359
+ if (!contractPath) {
4360
+ throw new Error(`\u274C No contract path found for component: ${componentName}`);
4361
+ }
4362
+ contract = await runContractTests(
4363
+ import_path6.default.resolve(configBaseDir, contractPath),
4364
+ componentName,
4365
+ component,
4366
+ strictness
4367
+ );
4326
4368
  } else {
4327
4369
  throw new Error("\u274C Either component or URL must be provided");
4328
4370
  }
package/dist/index.d.cts CHANGED
@@ -288,10 +288,12 @@ declare class ContractBuilder {
288
288
  ariaReference: (from: string, attribute: string, to: string) => {
289
289
  required: () => void;
290
290
  optional: () => void;
291
+ recommended: () => void;
291
292
  };
292
293
  contains: (parent: string, child: string) => {
293
294
  required: () => void;
294
295
  optional: () => void;
296
+ recommended: () => void;
295
297
  };
296
298
  }) => void): this;
297
299
  static(fn: (s: {
@@ -299,6 +301,7 @@ declare class ContractBuilder {
299
301
  has: (attribute: string, expectedValue: string) => {
300
302
  required: () => void;
301
303
  optional: () => void;
304
+ recommended: () => void;
302
305
  };
303
306
  };
304
307
  }) => void): this;
package/dist/index.d.ts CHANGED
@@ -288,10 +288,12 @@ declare class ContractBuilder {
288
288
  ariaReference: (from: string, attribute: string, to: string) => {
289
289
  required: () => void;
290
290
  optional: () => void;
291
+ recommended: () => void;
291
292
  };
292
293
  contains: (parent: string, child: string) => {
293
294
  required: () => void;
294
295
  optional: () => void;
296
+ recommended: () => void;
295
297
  };
296
298
  }) => void): this;
297
299
  static(fn: (s: {
@@ -299,6 +301,7 @@ declare class ContractBuilder {
299
301
  has: (attribute: string, expectedValue: string) => {
300
302
  required: () => void;
301
303
  optional: () => void;
304
+ recommended: () => void;
302
305
  };
303
306
  };
304
307
  }) => void): this;