aria-ease 6.9.0 → 6.10.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 (43) hide show
  1. package/README.md +3 -3
  2. package/bin/{buildContracts-GBOY7UXG.js → buildContracts-S22V7AGV.js} +28 -0
  3. package/bin/{chunk-LMSKLN5O.js → chunk-NI3MQCAS.js} +34 -0
  4. package/bin/cli.cjs +235 -20
  5. package/bin/cli.js +4 -4
  6. package/bin/{configLoader-Q6A4JLKW.js → configLoader-UJZHQBYS.js} +1 -1
  7. package/{dist/contractTestRunnerPlaywright-XBWJZMR3.js → bin/contractTestRunnerPlaywright-QDXSK3FE.js} +173 -20
  8. package/bin/{test-OND56UUL.js → test-O3J4ZPQR.js} +2 -2
  9. package/dist/{configLoader-WTGJAP4Z.js → configLoader-DWHOHXHL.js} +34 -0
  10. package/{bin/contractTestRunnerPlaywright-ZZNWDUYP.js → dist/contractTestRunnerPlaywright-WNWQYSXZ.js} +173 -20
  11. package/dist/index.cjs +506 -312
  12. package/dist/index.d.cts +54 -54
  13. package/dist/index.d.ts +54 -54
  14. package/dist/index.js +298 -291
  15. package/dist/src/{Types.d-DYfYR3Vc.d.cts → Types.d-yGC2bBaB.d.cts} +1 -1
  16. package/dist/src/{Types.d-DYfYR3Vc.d.ts → Types.d-yGC2bBaB.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.cjs +21 -7
  24. package/dist/src/combobox/index.d.cts +1 -1
  25. package/dist/src/combobox/index.d.ts +1 -1
  26. package/dist/src/combobox/index.js +21 -7
  27. package/dist/src/menu/index.d.cts +1 -1
  28. package/dist/src/menu/index.d.ts +1 -1
  29. package/dist/src/radio/index.d.cts +1 -1
  30. package/dist/src/radio/index.d.ts +1 -1
  31. package/dist/src/tabs/index.d.cts +1 -1
  32. package/dist/src/tabs/index.d.ts +1 -1
  33. package/dist/src/toggle/index.d.cts +1 -1
  34. package/dist/src/toggle/index.d.ts +1 -1
  35. package/dist/src/utils/test/{configLoader-YE2CYGDG.js → configLoader-SHJSRG2A.js} +34 -0
  36. package/dist/src/utils/test/{contractTestRunnerPlaywright-LC5OAVXB.js → contractTestRunnerPlaywright-Z2AHXSNM.js} +173 -20
  37. package/dist/src/utils/test/dsl/index.cjs +313 -0
  38. package/dist/src/utils/test/dsl/index.d.cts +136 -0
  39. package/dist/src/utils/test/dsl/index.d.ts +136 -0
  40. package/dist/src/utils/test/dsl/index.js +311 -0
  41. package/dist/src/utils/test/index.cjs +207 -20
  42. package/dist/src/utils/test/index.js +2 -2
  43. package/package.json +7 -6
package/dist/index.js CHANGED
@@ -1022,16 +1022,12 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
1022
1022
  }
1023
1023
  function setActiveDescendant(index) {
1024
1024
  const visibleItems = getVisibleItems();
1025
- visibleItems.forEach((item) => {
1026
- item.setAttribute("aria-selected", "false");
1027
- });
1028
1025
  if (index >= 0 && index < visibleItems.length) {
1029
1026
  const activeItem = visibleItems[index];
1030
1027
  const itemId = activeItem.id || `${listBoxId}-option-${index}`;
1031
1028
  if (!activeItem.id) {
1032
1029
  activeItem.id = itemId;
1033
1030
  }
1034
- activeItem.setAttribute("aria-selected", "true");
1035
1031
  comboboxInput.setAttribute("aria-activedescendant", itemId);
1036
1032
  if (typeof activeItem.scrollIntoView === "function") {
1037
1033
  activeItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
@@ -1064,8 +1060,6 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
1064
1060
  comboboxInput.setAttribute("aria-activedescendant", "");
1065
1061
  listBox.style.display = "none";
1066
1062
  activeIndex = -1;
1067
- const visibleItems = getVisibleItems();
1068
- visibleItems.forEach((item) => item.setAttribute("aria-selected", "false"));
1069
1063
  if (callback?.onOpenChange) {
1070
1064
  try {
1071
1065
  callback.onOpenChange(false);
@@ -1077,6 +1071,7 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
1077
1071
  function selectOption(item) {
1078
1072
  const value = item.textContent?.trim() || "";
1079
1073
  comboboxInput.value = value;
1074
+ item.setAttribute("aria-selected", "true");
1080
1075
  closeListbox();
1081
1076
  if (callback?.onSelect) {
1082
1077
  try {
@@ -1123,6 +1118,10 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
1123
1118
  } else if (comboboxInput.value) {
1124
1119
  event.preventDefault();
1125
1120
  comboboxInput.value = "";
1121
+ const visibleItems2 = getVisibleItems();
1122
+ visibleItems2.forEach((item) => {
1123
+ if (item.getAttribute("aria-selected") === "true") item.setAttribute("aria-selected", "false");
1124
+ });
1126
1125
  if (callback?.onClear) {
1127
1126
  try {
1128
1127
  callback.onClear();
@@ -1201,9 +1200,24 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
1201
1200
  function initializeOptions() {
1202
1201
  const items = listBox.querySelectorAll(`.${listBoxItemsClass}`);
1203
1202
  if (items.length === 0) return;
1203
+ let selectedValue = null;
1204
+ for (const item of items) {
1205
+ if (item.getAttribute("aria-selected") === "true") {
1206
+ selectedValue = item.textContent?.trim() || null;
1207
+ break;
1208
+ }
1209
+ }
1210
+ if (!selectedValue && comboboxInput.value) {
1211
+ selectedValue = comboboxInput.value.trim();
1212
+ }
1204
1213
  items.forEach((item, index) => {
1205
1214
  item.setAttribute("role", "option");
1206
- item.setAttribute("aria-selected", "false");
1215
+ const itemValue = item.textContent?.trim() || "";
1216
+ if (selectedValue && itemValue === selectedValue) {
1217
+ item.setAttribute("aria-selected", "true");
1218
+ } else {
1219
+ item.setAttribute("aria-selected", "false");
1220
+ }
1207
1221
  const currentId = item.getAttribute("id");
1208
1222
  if (!currentId || currentId === "") {
1209
1223
  const itemId = `${listBoxId}-option-${index}`;
@@ -1494,7 +1508,151 @@ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation
1494
1508
  return { activateTab, cleanup, refresh };
1495
1509
  }
1496
1510
 
1497
- // src/utils/test/dsl/index.ts
1511
+ // src/utils/test/dsl/src/state-packs/comboboxStatePack.ts
1512
+ var COMBOBOX_STATES = {
1513
+ "listbox.open": {
1514
+ setup: openCombobox(),
1515
+ assertion: isComboboxOpen()
1516
+ },
1517
+ "listbox.closed": {
1518
+ setup: closeCombobox(),
1519
+ assertion: isComboboxClosed()
1520
+ },
1521
+ "input.focused": {
1522
+ setup: focusInput(),
1523
+ assertion: [
1524
+ ...isInputFocused()
1525
+ ]
1526
+ },
1527
+ "input.filled": {
1528
+ setup: fillInput(),
1529
+ assertion: [
1530
+ ...isInputFilled()
1531
+ ]
1532
+ },
1533
+ "activeOption.first": {
1534
+ requires: ["listbox.open"],
1535
+ setup: [
1536
+ { type: "keypress", target: "input", key: "ArrowDown" }
1537
+ ],
1538
+ assertion: [
1539
+ ...isActiveDescendantNotEmpty()
1540
+ ]
1541
+ },
1542
+ "activeOption.last": {
1543
+ requires: ["activeOption.first"],
1544
+ setup: [
1545
+ { type: "keypress", target: "input", key: "ArrowUp" }
1546
+ ],
1547
+ assertion: [
1548
+ ...isActiveDescendantNotEmpty()
1549
+ ]
1550
+ },
1551
+ "selectedOption.first": {
1552
+ requires: ["listbox.open"],
1553
+ setup: [
1554
+ { type: "click", target: "relative", relativeTarget: "first" }
1555
+ ],
1556
+ assertion: [
1557
+ ...isAriaSelected("first")
1558
+ ]
1559
+ },
1560
+ "selectedOption.last": {
1561
+ requires: ["listbox.open"],
1562
+ setup: [
1563
+ { type: "click", target: "relative", relativeTarget: "last" }
1564
+ ],
1565
+ assertion: [
1566
+ ...isAriaSelected("first")
1567
+ ]
1568
+ }
1569
+ };
1570
+ function openCombobox() {
1571
+ return [
1572
+ { type: "keypress", target: "input", key: "ArrowDown" }
1573
+ ];
1574
+ }
1575
+ function closeCombobox() {
1576
+ return [
1577
+ { type: "keypress", target: "input", key: "Escape" }
1578
+ ];
1579
+ }
1580
+ function focusInput() {
1581
+ return [
1582
+ { type: "focus", target: "input" }
1583
+ ];
1584
+ }
1585
+ function fillInput() {
1586
+ return [
1587
+ { type: "type", target: "input", value: "test" }
1588
+ ];
1589
+ }
1590
+ function isComboboxOpen() {
1591
+ return [
1592
+ {
1593
+ target: "listbox",
1594
+ assertion: "toBeVisible",
1595
+ failureMessage: "Expected listbox to be visible"
1596
+ }
1597
+ ];
1598
+ }
1599
+ function isComboboxClosed() {
1600
+ return [
1601
+ {
1602
+ target: "listbox",
1603
+ assertion: "notToBeVisible",
1604
+ failureMessage: "Expected listbox to be closed"
1605
+ }
1606
+ ];
1607
+ }
1608
+ function isActiveDescendantNotEmpty() {
1609
+ return [
1610
+ {
1611
+ target: "input",
1612
+ assertion: "toHaveAttribute",
1613
+ attribute: "aria-activedescendant",
1614
+ expectedValue: "!empty",
1615
+ failureMessage: "Expected aria-activedescendant to not be empty"
1616
+ }
1617
+ ];
1618
+ }
1619
+ function isAriaSelected(index) {
1620
+ return [
1621
+ {
1622
+ target: "relative",
1623
+ relativeTarget: index,
1624
+ assertion: "toHaveAttribute",
1625
+ attribute: "aria-selected",
1626
+ expectedValue: "true",
1627
+ failureMessage: `Expected aria-selected on ${index} option to be true`
1628
+ }
1629
+ ];
1630
+ }
1631
+ function isInputFocused() {
1632
+ return [
1633
+ {
1634
+ target: "input",
1635
+ assertion: "toHaveFocus",
1636
+ failureMessage: "Expected input to be focused"
1637
+ }
1638
+ ];
1639
+ }
1640
+ function isInputFilled() {
1641
+ return [
1642
+ {
1643
+ target: "input",
1644
+ assertion: "toHaveValue",
1645
+ expectedValue: "test",
1646
+ failureMessage: "Expected input to have the value 'test'"
1647
+ }
1648
+ ];
1649
+ }
1650
+
1651
+ // src/utils/test/dsl/src/contractBuilder.ts
1652
+ var STATE_PACKS = {
1653
+ "combobox.listbox": COMBOBOX_STATES
1654
+ // Add more mappings as needed
1655
+ };
1498
1656
  var FluentContract = class {
1499
1657
  constructor(jsonContract) {
1500
1658
  this.jsonContract = jsonContract;
@@ -1503,309 +1661,158 @@ var FluentContract = class {
1503
1661
  return this.jsonContract;
1504
1662
  }
1505
1663
  };
1506
- var StaticTargetBuilder = class {
1507
- constructor(targetName, sink) {
1508
- this.targetName = targetName;
1509
- this.sink = sink;
1510
- }
1511
- has(attribute, expectedValue) {
1512
- const create = (level) => {
1513
- this.sink.push({
1514
- target: this.targetName,
1515
- attribute,
1516
- expectedValue,
1517
- failureMessage: `Expected ${this.targetName} to have ${attribute}${expectedValue !== void 0 ? `=${expectedValue}` : ""}.`,
1518
- level
1519
- });
1520
- };
1521
- return {
1522
- required: () => create("required"),
1523
- recommended: () => create("recommended"),
1524
- optional: () => create("optional")
1525
- };
1526
- }
1527
- };
1528
- var StaticBuilder = class {
1529
- constructor(sink) {
1530
- this.sink = sink;
1531
- }
1532
- target(targetName) {
1533
- return new StaticTargetBuilder(targetName, this.sink);
1534
- }
1535
- };
1536
- var DynamicChain = class {
1537
- constructor(key, testsSink, selectors) {
1538
- this.key = key;
1539
- this.testsSink = testsSink;
1540
- this.selectors = selectors;
1541
- }
1542
- selectorTarget = "";
1543
- actions = [];
1544
- assertions = [];
1545
- explicitDescription = "";
1546
- on(target) {
1547
- this.selectorTarget = target;
1548
- this.actions.push({ type: "keypress", target, key: this.key });
1549
- return this;
1550
- }
1551
- describe(description) {
1552
- this.explicitDescription = description;
1553
- return this;
1664
+ var ContractBuilder = class {
1665
+ constructor(componentName) {
1666
+ this.componentName = componentName;
1667
+ this.statePack = STATE_PACKS[componentName] || {};
1554
1668
  }
1555
- focus(targetExpression) {
1556
- const parsed = this.parseRelativeExpression(targetExpression);
1557
- if (parsed) {
1558
- if (!this.selectors[parsed.selectorKey]) {
1559
- const availableSelectors = Object.keys(this.selectors).sort().join(", ") || "(none)";
1560
- throw new Error(
1561
- `Invalid focus target expression "${targetExpression}": selector "${parsed.selectorKey}" is not defined. Available selectors: ${availableSelectors}`
1562
- );
1563
- }
1564
- if (!this.selectors.relative && this.selectors[parsed.selectorKey]) {
1565
- this.selectors.relative = this.selectors[parsed.selectorKey];
1566
- }
1567
- this.assertions.push({
1568
- target: "relative",
1569
- assertion: "toHaveFocus",
1570
- relativeTarget: parsed.relativeTarget
1571
- });
1572
- } else {
1573
- this.assertions.push({
1574
- target: targetExpression,
1575
- assertion: "toHaveFocus"
1576
- });
1577
- }
1669
+ metaValue = {};
1670
+ selectorsValue = {};
1671
+ relationshipInvariants = [];
1672
+ staticAssertions = [];
1673
+ dynamicTests = [];
1674
+ statePack;
1675
+ meta(meta) {
1676
+ this.metaValue = meta;
1578
1677
  return this;
1579
1678
  }
1580
- visible(target) {
1581
- this.assertions.push({ target, assertion: "toBeVisible" });
1679
+ selectors(selectors) {
1680
+ this.selectorsValue = selectors;
1582
1681
  return this;
1583
1682
  }
1584
- hidden(target) {
1585
- this.assertions.push({ target, assertion: "notToBeVisible" });
1683
+ relationships(fn) {
1684
+ const api = {
1685
+ ariaReference: (from, attribute, to) => ({
1686
+ required: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "required" }),
1687
+ optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" })
1688
+ }),
1689
+ contains: (parent, child) => ({
1690
+ required: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "required" }),
1691
+ optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" })
1692
+ })
1693
+ };
1694
+ fn(api);
1586
1695
  return this;
1587
1696
  }
1588
- has(target, attribute, expectedValue) {
1589
- this.assertions.push({
1590
- target,
1591
- assertion: "toHaveAttribute",
1592
- attribute,
1593
- expectedValue
1594
- });
1697
+ static(fn) {
1698
+ const api = {
1699
+ target: (target) => ({
1700
+ has: (attribute, expectedValue) => ({
1701
+ required: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "required" }),
1702
+ optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" })
1703
+ })
1704
+ })
1705
+ };
1706
+ fn(api);
1595
1707
  return this;
1596
1708
  }
1597
- required() {
1598
- this.finalize("required");
1599
- }
1600
- recommended() {
1601
- this.finalize("recommended");
1709
+ when(event) {
1710
+ return new DynamicTestBuilder(this, this.statePack, event);
1602
1711
  }
1603
- optional() {
1604
- this.finalize("optional");
1605
- }
1606
- finalize(level) {
1607
- if (!this.selectorTarget) {
1608
- throw new Error("Dynamic contract chain requires .on(<selectorKey>) before level terminator.");
1609
- }
1610
- const description = this.explicitDescription || `Pressing ${this.key} on ${this.selectorTarget} satisfies expected behavior.`;
1611
- this.testsSink.push({
1612
- description,
1613
- level,
1614
- action: this.actions,
1615
- assertions: this.assertions.map((a) => ({ ...a, level }))
1616
- });
1712
+ addDynamicTest(test) {
1713
+ this.dynamicTests.push(test);
1617
1714
  }
1618
- parseRelativeExpression(input) {
1619
- const match = input.match(/^(next|previous|first|last)\(([^)]+)\)$/);
1620
- if (!match) return null;
1621
- const relativeTarget = match[1];
1622
- const selectorKey = match[2].trim();
1623
- return { relativeTarget, selectorKey };
1715
+ build() {
1716
+ return {
1717
+ meta: this.metaValue,
1718
+ selectors: this.selectorsValue,
1719
+ relationships: this.relationshipInvariants.length ? this.relationshipInvariants : void 0,
1720
+ static: this.staticAssertions.length ? [{ assertions: this.staticAssertions }] : [],
1721
+ dynamic: this.dynamicTests
1722
+ };
1624
1723
  }
1625
1724
  };
1626
- var ContractBuilder = class {
1627
- constructor(componentName) {
1628
- this.componentName = componentName;
1629
- }
1630
- metaValue = {};
1631
- selectorsValue = {};
1632
- relationshipInvariants = [];
1633
- staticAssertions = [];
1634
- dynamicTests = [];
1635
- meta(meta) {
1636
- this.metaValue = { ...this.metaValue, ...meta };
1725
+ var DynamicTestBuilder = class {
1726
+ constructor(parent, statePack, event) {
1727
+ this.parent = parent;
1728
+ this.statePack = statePack;
1729
+ this.event = event;
1730
+ }
1731
+ _as;
1732
+ _on;
1733
+ _given = [];
1734
+ _then = [];
1735
+ _desc = "";
1736
+ _level = "required";
1737
+ as(actionType) {
1738
+ this._as = actionType;
1637
1739
  return this;
1638
1740
  }
1639
- selectors(selectors) {
1640
- this.selectorsValue = { ...this.selectorsValue, ...selectors };
1741
+ on(target) {
1742
+ this._on = target;
1641
1743
  return this;
1642
1744
  }
1643
- relationship(invariant) {
1644
- this.relationshipInvariants.push(invariant);
1745
+ given(states) {
1746
+ this._given = Array.isArray(states) ? states : [states];
1645
1747
  return this;
1646
1748
  }
1647
- relationships(builderFn) {
1648
- builderFn({
1649
- ariaReference: (from, attribute, to) => {
1650
- const create = (level) => {
1651
- this.relationshipInvariants.push({
1652
- type: "aria-reference",
1653
- from,
1654
- attribute,
1655
- to,
1656
- level
1657
- });
1658
- };
1659
- return {
1660
- required: () => create("required"),
1661
- recommended: () => create("recommended"),
1662
- optional: () => create("optional")
1663
- };
1664
- },
1665
- contains: (parent, child) => {
1666
- const create = (level) => {
1667
- this.relationshipInvariants.push({
1668
- type: "contains",
1669
- parent,
1670
- child,
1671
- level
1672
- });
1673
- };
1674
- return {
1675
- required: () => create("required"),
1676
- recommended: () => create("recommended"),
1677
- optional: () => create("optional")
1678
- };
1679
- }
1680
- });
1749
+ then(states) {
1750
+ this._then = Array.isArray(states) ? states : [states];
1681
1751
  return this;
1682
1752
  }
1683
- static(builderFn) {
1684
- builderFn(new StaticBuilder(this.staticAssertions));
1753
+ describe(desc) {
1754
+ this._desc = desc;
1685
1755
  return this;
1686
1756
  }
1687
- when(key) {
1688
- return new DynamicChain(key, this.dynamicTests, this.selectorsValue);
1757
+ required() {
1758
+ this._level = "required";
1759
+ this._finalize();
1760
+ return this.parent;
1689
1761
  }
1690
- validateRelationshipInvariants() {
1691
- if (this.relationshipInvariants.length === 0) {
1692
- return;
1693
- }
1694
- const selectorKeys = new Set(Object.keys(this.selectorsValue));
1695
- const available = Object.keys(this.selectorsValue).sort().join(", ");
1696
- const errors = [];
1697
- this.relationshipInvariants.forEach((invariant, index) => {
1698
- const prefix = `relationships[${index}] (${invariant.type})`;
1699
- if (invariant.type === "aria-reference") {
1700
- if (!selectorKeys.has(invariant.from)) {
1701
- errors.push(`${prefix}: "from" references unknown selector "${invariant.from}"`);
1702
- }
1703
- if (!selectorKeys.has(invariant.to)) {
1704
- errors.push(`${prefix}: "to" references unknown selector "${invariant.to}"`);
1705
- }
1706
- }
1707
- if (invariant.type === "contains") {
1708
- if (!selectorKeys.has(invariant.parent)) {
1709
- errors.push(`${prefix}: "parent" references unknown selector "${invariant.parent}"`);
1710
- }
1711
- if (!selectorKeys.has(invariant.child)) {
1712
- errors.push(`${prefix}: "child" references unknown selector "${invariant.child}"`);
1713
- }
1714
- }
1715
- });
1716
- if (errors.length > 0) {
1717
- const availableSelectorsMessage = available.length > 0 ? available : "(none)";
1718
- throw new Error(
1719
- [
1720
- `Contract invariant validation failed for component "${this.componentName}".`,
1721
- ...errors.map((error) => `- ${error}`),
1722
- `Available selectors: ${availableSelectorsMessage}`
1723
- ].join("\n")
1724
- );
1725
- }
1762
+ optional() {
1763
+ this._level = "optional";
1764
+ this._finalize();
1765
+ return this.parent;
1726
1766
  }
1727
- validateStaticTargets() {
1728
- const selectorKeys = new Set(Object.keys(this.selectorsValue));
1729
- const available = Object.keys(this.selectorsValue).sort().join(", ") || "(none)";
1730
- const errors = [];
1731
- this.staticAssertions.forEach((assertion, index) => {
1732
- if (!selectorKeys.has(assertion.target)) {
1733
- errors.push(`static.assertions[${index}]: target "${assertion.target}" is not defined in selectors`);
1767
+ recommended() {
1768
+ this._level = "recommended";
1769
+ this._finalize();
1770
+ return this.parent;
1771
+ }
1772
+ _finalize() {
1773
+ const resolveSetup = (stateName, visited = /* @__PURE__ */ new Set()) => {
1774
+ if (visited.has(stateName)) return [];
1775
+ visited.add(stateName);
1776
+ const s = this.statePack[stateName];
1777
+ if (!s) return [];
1778
+ let actions = [];
1779
+ if (Array.isArray(s.requires)) {
1780
+ for (const req of s.requires) {
1781
+ actions = actions.concat(resolveSetup(req, visited));
1782
+ }
1734
1783
  }
1735
- });
1736
- if (errors.length > 0) {
1737
- throw new Error(
1738
- [
1739
- `Contract static target validation failed for component "${this.componentName}".`,
1740
- ...errors.map((error) => `- ${error}`),
1741
- `Available selectors: ${available}`
1742
- ].join("\n")
1743
- );
1744
- }
1745
- }
1746
- validateDynamicTargets() {
1747
- const selectorKeys = new Set(Object.keys(this.selectorsValue));
1748
- const available = Object.keys(this.selectorsValue).sort().join(", ") || "(none)";
1749
- const errors = [];
1750
- const isValidActionTarget = (target) => {
1751
- return selectorKeys.has(target) || target === "document" || target === "relative";
1752
- };
1753
- const isValidAssertionTarget = (target) => {
1754
- return selectorKeys.has(target) || target === "relative";
1784
+ if (s.setup) actions = actions.concat(s.setup);
1785
+ return actions;
1755
1786
  };
1756
- this.dynamicTests.forEach((test, testIndex) => {
1757
- test.action.forEach((action, actionIndex) => {
1758
- if (!isValidActionTarget(action.target)) {
1759
- errors.push(
1760
- `dynamic[${testIndex}].action[${actionIndex}]: target "${action.target}" is not defined in selectors`
1761
- );
1762
- }
1763
- });
1764
- test.assertions.forEach((assertion, assertionIndex) => {
1765
- if (!isValidAssertionTarget(assertion.target)) {
1766
- errors.push(
1767
- `dynamic[${testIndex}].assertions[${assertionIndex}]: target "${assertion.target}" is not defined in selectors`
1768
- );
1769
- }
1770
- if (assertion.target === "relative" && !this.selectorsValue.relative) {
1771
- errors.push(
1772
- `dynamic[${testIndex}].assertions[${assertionIndex}]: target "relative" requires selectors.relative to be defined`
1773
- );
1774
- }
1775
- });
1787
+ const setup = [];
1788
+ for (const state of this._given) {
1789
+ setup.push(...resolveSetup(state));
1790
+ }
1791
+ const assertions = [];
1792
+ for (const state of this._then) {
1793
+ const s = this.statePack[state];
1794
+ if (s && s.assertion) {
1795
+ if (Array.isArray(s.assertion)) assertions.push(...s.assertion);
1796
+ else assertions.push(s.assertion);
1797
+ }
1798
+ }
1799
+ const action = [
1800
+ {
1801
+ type: this._as,
1802
+ target: this._on,
1803
+ key: this._as === "keypress" ? this.event : void 0
1804
+ }
1805
+ ];
1806
+ this.parent.addDynamicTest({
1807
+ description: this._desc || "",
1808
+ level: this._level,
1809
+ action,
1810
+ assertions,
1811
+ ...setup.length ? { setup } : {}
1776
1812
  });
1777
- if (errors.length > 0) {
1778
- throw new Error(
1779
- [
1780
- `Contract dynamic target validation failed for component "${this.componentName}".`,
1781
- ...errors.map((error) => `- ${error}`),
1782
- `Available selectors: ${available}`,
1783
- `Allowed special targets: document, relative`
1784
- ].join("\n")
1785
- );
1786
- }
1787
- }
1788
- build() {
1789
- this.validateRelationshipInvariants();
1790
- this.validateStaticTargets();
1791
- this.validateDynamicTargets();
1792
- const fallbackId = this.metaValue.id || `aria-ease.contract.${this.componentName}`;
1793
- return {
1794
- meta: {
1795
- id: fallbackId,
1796
- version: this.metaValue.version || "1.0.0",
1797
- description: this.metaValue.description || `Fluent contract for ${this.componentName}`,
1798
- source: this.metaValue.source,
1799
- W3CName: this.metaValue.W3CName
1800
- },
1801
- selectors: this.selectorsValue,
1802
- relationships: this.relationshipInvariants,
1803
- static: [{ assertions: this.staticAssertions }],
1804
- dynamic: this.dynamicTests
1805
- };
1806
1813
  }
1807
1814
  };
1808
- function contract(componentName, define) {
1815
+ function createContract(componentName, define) {
1809
1816
  const builder = new ContractBuilder(componentName);
1810
1817
  define(builder);
1811
1818
  return new FluentContract(builder.build());
@@ -2025,7 +2032,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
2025
2032
  let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
2026
2033
  if (typeof process !== "undefined" && typeof process.cwd === "function") {
2027
2034
  try {
2028
- const { loadConfig } = await import("./configLoader-WTGJAP4Z.js");
2035
+ const { loadConfig } = await import("./configLoader-DWHOHXHL.js");
2029
2036
  const result2 = await loadConfig(process.cwd());
2030
2037
  config = result2.config;
2031
2038
  if (result2.configPath) {
@@ -2041,14 +2048,14 @@ Error: ${error instanceof Error ? error.message : String(error)}`
2041
2048
  }
2042
2049
  }
2043
2050
  }
2044
- let contract2;
2051
+ let contract;
2045
2052
  try {
2046
2053
  if (url) {
2047
2054
  const devServerUrl = await checkDevServer(url);
2048
2055
  if (devServerUrl) {
2049
2056
  console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
2050
- const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-XBWJZMR3.js");
2051
- contract2 = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
2057
+ const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-WNWQYSXZ.js");
2058
+ contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
2052
2059
  } else {
2053
2060
  throw new Error(
2054
2061
  `\u274C Dev server not running at ${url}
@@ -2057,7 +2064,7 @@ Please start your dev server and try again.`
2057
2064
  }
2058
2065
  } else if (component) {
2059
2066
  console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
2060
- contract2 = await runContractTests(componentName, component, strictness);
2067
+ contract = await runContractTests(componentName, component, strictness);
2061
2068
  } else {
2062
2069
  throw new Error("\u274C Either component or URL must be provided");
2063
2070
  }
@@ -2070,13 +2077,13 @@ Please start your dev server and try again.`
2070
2077
  const result = {
2071
2078
  violations: results.violations,
2072
2079
  raw: results,
2073
- contract: contract2
2080
+ contract
2074
2081
  };
2075
- if (contract2.failures.length > 0 && url === "Playwright") {
2082
+ if (contract.failures.length > 0 && url === "Playwright") {
2076
2083
  throw new Error(
2077
2084
  `
2078
- \u274C ${contract2.failures.length} accessibility contract test${contract2.failures.length > 1 ? "s" : ""} failed (Playwright mode)
2079
- \u2705 ${contract2.passes.length} test${contract2.passes.length > 1 ? "s" : ""} passed
2085
+ \u274C ${contract.failures.length} accessibility contract test${contract.failures.length > 1 ? "s" : ""} failed (Playwright mode)
2086
+ \u2705 ${contract.passes.length} test${contract.passes.length > 1 ? "s" : ""} passed
2080
2087
 
2081
2088
  \u{1F4CB} Review the detailed test report above for specific failures.
2082
2089
  \u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`
@@ -2151,7 +2158,7 @@ async function cleanupTests() {
2151
2158
  }
2152
2159
  export {
2153
2160
  cleanupTests,
2154
- contract,
2161
+ createContract,
2155
2162
  makeAccordionAccessible,
2156
2163
  makeBlockAccessible,
2157
2164
  makeCheckboxAccessible,