aria-ease 6.11.0 → 6.12.1

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 (37) hide show
  1. package/README.md +3 -3
  2. package/dist/{ComboboxComponentStrategy-OGRVZXAF.js → ComboboxComponentStrategy-DU342VMB.js} +7 -7
  3. package/dist/RelativeTargetResolver-DJAITO6D.js +7 -0
  4. package/dist/{audit-RM6TCZ5C.js → audit-JYEPKLHR.js} +5 -0
  5. package/dist/{chunk-XERMSYEH.js → chunk-4DU5Z5BR.js} +0 -23
  6. package/dist/{chunk-NI3MQCAS.js → chunk-GJGUY643.js} +2 -2
  7. package/dist/chunk-GLT43UVH.js +43 -0
  8. package/dist/cli.cjs +147 -84
  9. package/dist/cli.js +5 -5
  10. package/dist/{configLoader-DWHOHXHL.js → configLoader-Q7N5XV4P.js} +2 -2
  11. package/dist/{configLoader-UJZHQBYS.js → configLoader-REHK3S3Q.js} +1 -1
  12. package/dist/{contractTestRunnerPlaywright-QDXSK3FE.js → contractTestRunnerPlaywright-H24LQ45R.js} +113 -72
  13. package/dist/{contractTestRunnerPlaywright-WNWQYSXZ.js → contractTestRunnerPlaywright-NL3JNJYH.js} +113 -72
  14. package/dist/index.cjs +248 -112
  15. package/dist/index.d.cts +3 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.js +124 -41
  18. package/dist/src/combobox/index.cjs +1 -0
  19. package/dist/src/combobox/index.js +1 -0
  20. package/dist/src/utils/test/{ComboboxComponentStrategy-5AECQSRN.js → ComboboxComponentStrategy-XKQ72RFD.js} +7 -7
  21. package/dist/src/utils/test/RelativeTargetResolver-G2XDN2VV.js +1 -0
  22. package/dist/src/utils/test/{chunk-XERMSYEH.js → chunk-4DU5Z5BR.js} +1 -23
  23. package/dist/src/utils/test/chunk-GLT43UVH.js +41 -0
  24. package/dist/src/utils/test/{configLoader-SHJSRG2A.js → configLoader-NA7IBCS3.js} +2 -2
  25. package/dist/src/utils/test/{contractTestRunnerPlaywright-Z2AHXSNM.js → contractTestRunnerPlaywright-5FT6K2WN.js} +111 -71
  26. package/dist/src/utils/test/dsl/index.cjs +106 -29
  27. package/dist/src/utils/test/dsl/index.d.cts +3 -0
  28. package/dist/src/utils/test/dsl/index.d.ts +3 -0
  29. package/dist/src/utils/test/dsl/index.js +106 -29
  30. package/dist/src/utils/test/index.cjs +135 -76
  31. package/dist/src/utils/test/index.js +17 -11
  32. package/dist/{test-O3J4ZPQR.js → test-FYSJXQWO.js} +17 -12
  33. package/package.json +3 -4
  34. package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +0 -290
  35. package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +0 -463
  36. package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +0 -562
  37. package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +0 -361
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;
package/dist/index.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import {
2
2
  ContractReporter,
3
3
  closeSharedBrowser,
4
- contract_default,
5
4
  normalizeLevel,
6
5
  normalizeStrictness,
7
6
  resolveEnforcement
8
- } from "./chunk-XERMSYEH.js";
7
+ } from "./chunk-4DU5Z5BR.js";
9
8
  import "./chunk-I2KLQ2HA.js";
10
9
 
11
10
  // src/accordion/src/makeAccordionAccessible/makeAccordionAccessible.ts
@@ -1118,6 +1117,7 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
1118
1117
  } else if (comboboxInput.value) {
1119
1118
  event.preventDefault();
1120
1119
  comboboxInput.value = "";
1120
+ comboboxInput.setAttribute("aria-activedescendant", "");
1121
1121
  const visibleItems2 = getVisibleItems();
1122
1122
  visibleItems2.forEach((item) => {
1123
1123
  if (item.getAttribute("aria-selected") === "true") item.setAttribute("aria-selected", "false");
@@ -1526,7 +1526,7 @@ function resolveSetup(setup, ctx) {
1526
1526
  );
1527
1527
  }
1528
1528
  var COMBOBOX_STATES = {
1529
- "listbox.open": {
1529
+ "popup.open": {
1530
1530
  setup: [
1531
1531
  {
1532
1532
  when: ["keyboard", "textInput"],
@@ -1543,7 +1543,7 @@ var COMBOBOX_STATES = {
1543
1543
  ],
1544
1544
  assertion: isComboboxOpen
1545
1545
  },
1546
- "listbox.closed": {
1546
+ "popup.closed": {
1547
1547
  setup: [
1548
1548
  {
1549
1549
  when: ["keyboard"],
@@ -1558,18 +1558,18 @@ var COMBOBOX_STATES = {
1558
1558
  ]
1559
1559
  }
1560
1560
  ],
1561
- assertion: isComboboxClosed
1561
+ assertion: [...isComboboxClosed(), ...isActiveDescendantEmpty()]
1562
1562
  },
1563
- "input.focused": {
1563
+ "main.focused": {
1564
1564
  setup: [
1565
1565
  {
1566
1566
  when: ["keyboard"],
1567
1567
  steps: () => [
1568
- { type: "focus", target: "input" }
1568
+ { type: "focus", target: "main" }
1569
1569
  ]
1570
1570
  }
1571
1571
  ],
1572
- assertion: isInputFocused
1572
+ assertion: isMainFocused
1573
1573
  },
1574
1574
  "input.filled": {
1575
1575
  setup: [
@@ -1582,8 +1582,19 @@ var COMBOBOX_STATES = {
1582
1582
  ],
1583
1583
  assertion: isInputFilled
1584
1584
  },
1585
+ "input.notFilled": {
1586
+ setup: [
1587
+ {
1588
+ when: ["keyboard", "textInput"],
1589
+ steps: () => [
1590
+ { type: "type", target: "input", value: "" }
1591
+ ]
1592
+ }
1593
+ ],
1594
+ assertion: isInputNotFilled
1595
+ },
1585
1596
  "activeOption.first": {
1586
- requires: ["listbox.open"],
1597
+ requires: ["popup.open"],
1587
1598
  setup: [
1588
1599
  {
1589
1600
  when: ["keyboard"],
@@ -1592,7 +1603,7 @@ var COMBOBOX_STATES = {
1592
1603
  ]
1593
1604
  }
1594
1605
  ],
1595
- assertion: isActiveDescendantNotEmpty
1606
+ assertion: isActiveDescendantFirst
1596
1607
  },
1597
1608
  "activeOption.last": {
1598
1609
  requires: ["activeOption.first"],
@@ -1604,10 +1615,30 @@ var COMBOBOX_STATES = {
1604
1615
  ]
1605
1616
  }
1606
1617
  ],
1618
+ assertion: isActiveDescendantLast
1619
+ },
1620
+ "activeDescendant.notEmpty": {
1621
+ requires: [],
1622
+ setup: [
1623
+ {
1624
+ when: ["keyboard"],
1625
+ steps: () => []
1626
+ }
1627
+ ],
1607
1628
  assertion: isActiveDescendantNotEmpty
1608
1629
  },
1630
+ "activeDescendant.Empty": {
1631
+ requires: [],
1632
+ setup: [
1633
+ {
1634
+ when: ["keyboard"],
1635
+ steps: () => []
1636
+ }
1637
+ ],
1638
+ assertion: isActiveDescendantEmpty
1639
+ },
1609
1640
  "selectedOption.first": {
1610
- requires: ["listbox.open"],
1641
+ requires: ["popup.open"],
1611
1642
  setup: [
1612
1643
  {
1613
1644
  when: ["pointer"],
@@ -1619,7 +1650,7 @@ var COMBOBOX_STATES = {
1619
1650
  assertion: () => isAriaSelected("first")
1620
1651
  },
1621
1652
  "selectedOption.last": {
1622
- requires: ["listbox.open"],
1653
+ requires: ["popup.open"],
1623
1654
  setup: [
1624
1655
  {
1625
1656
  when: ["pointer"],
@@ -1634,43 +1665,76 @@ var COMBOBOX_STATES = {
1634
1665
  function isComboboxOpen() {
1635
1666
  return [
1636
1667
  {
1637
- target: "listbox",
1668
+ target: "popup",
1638
1669
  assertion: "toBeVisible",
1639
- failureMessage: "Expected listbox to be visible"
1670
+ failureMessage: "Expected popup to be visible"
1640
1671
  },
1641
1672
  {
1642
- target: "input",
1673
+ target: "main",
1643
1674
  assertion: "toHaveAttribute",
1644
1675
  attribute: "aria-expanded",
1645
1676
  expectedValue: "true",
1646
- failureMessage: "Expect combobox input to have aria-expanded='true'"
1677
+ failureMessage: "Expect combobox main to have aria-expanded='true'."
1647
1678
  }
1648
1679
  ];
1649
1680
  }
1650
1681
  function isComboboxClosed() {
1651
1682
  return [
1652
1683
  {
1653
- target: "listbox",
1684
+ target: "popup",
1654
1685
  assertion: "notToBeVisible",
1655
- failureMessage: "Expected listbox to be closed"
1686
+ failureMessage: "Expected popup to be closed"
1656
1687
  },
1657
1688
  {
1658
- target: "input",
1689
+ target: "main",
1659
1690
  assertion: "toHaveAttribute",
1660
1691
  attribute: "aria-expanded",
1661
1692
  expectedValue: "false",
1662
- failureMessage: "Expect combobox input to have aria-expanded='false'"
1693
+ failureMessage: "Expect combobox main to have aria-expanded='false'."
1694
+ }
1695
+ ];
1696
+ }
1697
+ function isActiveDescendantFirst() {
1698
+ return [
1699
+ {
1700
+ target: "main",
1701
+ assertion: "toHaveAttribute",
1702
+ attribute: "aria-activedescendant",
1703
+ expectedValue: { ref: "relative", relativeTarget: "first", property: "id" },
1704
+ failureMessage: "Expected aria-activedescendant on main to match the id of the first option."
1705
+ }
1706
+ ];
1707
+ }
1708
+ function isActiveDescendantLast() {
1709
+ return [
1710
+ {
1711
+ target: "main",
1712
+ assertion: "toHaveAttribute",
1713
+ attribute: "aria-activedescendant",
1714
+ expectedValue: { ref: "relative", relativeTarget: "last", property: "id" },
1715
+ failureMessage: "Expected aria-activedescendant on main to match the id of the last option."
1663
1716
  }
1664
1717
  ];
1665
1718
  }
1666
1719
  function isActiveDescendantNotEmpty() {
1667
1720
  return [
1668
1721
  {
1669
- target: "input",
1722
+ target: "main",
1670
1723
  assertion: "toHaveAttribute",
1671
1724
  attribute: "aria-activedescendant",
1672
1725
  expectedValue: "!empty",
1673
- failureMessage: "Expected aria-activedescendant to not be empty"
1726
+ failureMessage: "Expected aria-activedescendant on main to not be empty."
1727
+ }
1728
+ ];
1729
+ }
1730
+ function isActiveDescendantEmpty() {
1731
+ return [
1732
+ {
1733
+ target: "main",
1734
+ assertion: "toHaveAttribute",
1735
+ attribute: "aria-activedescendant",
1736
+ expectedValue: "",
1737
+ failureMessage: "Expected aria-activedescendant on main to be empty."
1674
1738
  }
1675
1739
  ];
1676
1740
  }
@@ -1682,16 +1746,16 @@ function isAriaSelected(index) {
1682
1746
  assertion: "toHaveAttribute",
1683
1747
  attribute: "aria-selected",
1684
1748
  expectedValue: "true",
1685
- failureMessage: `Expected ${index} option to have aria-selected='true'`
1749
+ failureMessage: `Expected ${index} option to have aria-selected='true'.`
1686
1750
  }
1687
1751
  ];
1688
1752
  }
1689
- function isInputFocused() {
1753
+ function isMainFocused() {
1690
1754
  return [
1691
1755
  {
1692
- target: "input",
1756
+ target: "main",
1693
1757
  assertion: "toHaveFocus",
1694
- failureMessage: "Expected input to be focused"
1758
+ failureMessage: "Expected main to be focused."
1695
1759
  }
1696
1760
  ];
1697
1761
  }
@@ -1701,14 +1765,24 @@ function isInputFilled() {
1701
1765
  target: "input",
1702
1766
  assertion: "toHaveValue",
1703
1767
  expectedValue: "test",
1704
- failureMessage: "Expected input to have the value 'test'"
1768
+ failureMessage: "Expected input to have the value 'test'."
1769
+ }
1770
+ ];
1771
+ }
1772
+ function isInputNotFilled() {
1773
+ return [
1774
+ {
1775
+ target: "input",
1776
+ assertion: "toHaveValue",
1777
+ expectedValue: "",
1778
+ failureMessage: "Expected input to have the value ''."
1705
1779
  }
1706
1780
  ];
1707
1781
  }
1708
1782
 
1709
1783
  // src/utils/test/dsl/src/contractBuilder.ts
1710
1784
  var STATE_PACKS = {
1711
- "combobox.listbox": COMBOBOX_STATES
1785
+ "combobox": COMBOBOX_STATES
1712
1786
  // Add more mappings as needed
1713
1787
  };
1714
1788
  var FluentContract = class {
@@ -1742,11 +1816,13 @@ var ContractBuilder = class {
1742
1816
  const api = {
1743
1817
  ariaReference: (from, attribute, to) => ({
1744
1818
  required: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "required" }),
1745
- optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" })
1819
+ optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" }),
1820
+ recommended: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "recommended" })
1746
1821
  }),
1747
1822
  contains: (parent, child) => ({
1748
1823
  required: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "required" }),
1749
- optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" })
1824
+ optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" }),
1825
+ recommended: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "recommended" })
1750
1826
  })
1751
1827
  };
1752
1828
  fn(api);
@@ -1757,7 +1833,8 @@ var ContractBuilder = class {
1757
1833
  target: (target) => ({
1758
1834
  has: (attribute, expectedValue) => ({
1759
1835
  required: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "required" }),
1760
- optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" })
1836
+ optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" }),
1837
+ recommended: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "recommended" })
1761
1838
  })
1762
1839
  })
1763
1840
  };
@@ -1899,16 +1976,13 @@ import { axe } from "jest-axe";
1899
1976
 
1900
1977
  // src/utils/test/src/contractTestRunner.ts
1901
1978
  import fs from "fs/promises";
1902
- async function runContractTests(componentName, component, strictness) {
1979
+ async function runContractTests(contractPath, componentName, component, strictness) {
1903
1980
  const reporter = new ContractReporter(false);
1904
1981
  const strictnessMode = normalizeStrictness(strictness);
1905
- const contractTyped = contract_default;
1906
- const contractPath = contractTyped[componentName]?.path;
1907
1982
  if (!contractPath) {
1908
- throw new Error(`No contract found for component: ${componentName}`);
1983
+ throw new Error(`No contract path provided for component: ${componentName}`);
1909
1984
  }
1910
- const resolvedPath = new URL(contractPath, import.meta.url).pathname;
1911
- const contractData = await fs.readFile(resolvedPath, "utf-8");
1985
+ const contractData = await fs.readFile(contractPath, "utf-8");
1912
1986
  const componentContract = JSON.parse(contractData);
1913
1987
  const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
1914
1988
  reporter.start(componentName, totalTests);
@@ -2043,7 +2117,7 @@ async function runContractTests(componentName, component, strictness) {
2043
2117
  staticPassed += 1;
2044
2118
  reporter.reportStaticTest(`${test.target} has ${test.attribute}`, "pass", void 0, staticLevel);
2045
2119
  }
2046
- } else if (!attributeValue || !test.expectedValue.split(" | ").includes(attributeValue)) {
2120
+ } else if (!attributeValue || typeof test.expectedValue === "string" && !test.expectedValue.split(" | ").includes(attributeValue)) {
2047
2121
  const outcome = classifyFailure(test.failureMessage + ` Attribute value does not match expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`, test.level);
2048
2122
  if (outcome.status === "fail") staticFailed += 1;
2049
2123
  if (outcome.status === "warn") staticWarnings += 1;
@@ -2108,7 +2182,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
2108
2182
  let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
2109
2183
  if (typeof process !== "undefined" && typeof process.cwd === "function") {
2110
2184
  try {
2111
- const { loadConfig } = await import("./configLoader-DWHOHXHL.js");
2185
+ const { loadConfig } = await import("./configLoader-Q7N5XV4P.js");
2112
2186
  const result2 = await loadConfig(process.cwd());
2113
2187
  config = result2.config;
2114
2188
  if (result2.configPath) {
@@ -2130,7 +2204,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
2130
2204
  const devServerUrl = await checkDevServer(url);
2131
2205
  if (devServerUrl) {
2132
2206
  console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
2133
- const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-WNWQYSXZ.js");
2207
+ const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-NL3JNJYH.js");
2134
2208
  contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
2135
2209
  } else {
2136
2210
  throw new Error(
@@ -2140,7 +2214,16 @@ Please start your dev server and try again.`
2140
2214
  }
2141
2215
  } else if (component) {
2142
2216
  console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
2143
- contract = await runContractTests(componentName, component, strictness);
2217
+ const contractPath = config.test?.components?.find((comp) => comp?.name === componentName)?.contractPath;
2218
+ if (!contractPath) {
2219
+ throw new Error(`\u274C No contract path found for component: ${componentName}`);
2220
+ }
2221
+ contract = await runContractTests(
2222
+ path.resolve(configBaseDir, contractPath),
2223
+ componentName,
2224
+ component,
2225
+ strictness
2226
+ );
2144
2227
  } else {
2145
2228
  throw new Error("\u274C Either component or URL must be provided");
2146
2229
  }
@@ -130,6 +130,7 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
130
130
  } else if (comboboxInput.value) {
131
131
  event.preventDefault();
132
132
  comboboxInput.value = "";
133
+ comboboxInput.setAttribute("aria-activedescendant", "");
133
134
  const visibleItems2 = getVisibleItems();
134
135
  visibleItems2.forEach((item) => {
135
136
  if (item.getAttribute("aria-selected") === "true") item.setAttribute("aria-selected", "false");
@@ -128,6 +128,7 @@ function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId,
128
128
  } else if (comboboxInput.value) {
129
129
  event.preventDefault();
130
130
  comboboxInput.value = "";
131
+ comboboxInput.setAttribute("aria-activedescendant", "");
131
132
  const visibleItems2 = getVisibleItems();
132
133
  visibleItems2.forEach((item) => {
133
134
  if (item.getAttribute("aria-selected") === "true") item.setAttribute("aria-selected", "false");
@@ -14,7 +14,7 @@ var ComboboxComponentStrategy = class {
14
14
  const popupElement = page.locator(popupSelector).first();
15
15
  const isPopupVisible = await popupElement.isVisible().catch(() => false);
16
16
  if (!isPopupVisible) return;
17
- let listBoxClosed = false;
17
+ let popupClosed = false;
18
18
  let closeSelector = this.selectors.input;
19
19
  if (!closeSelector && this.selectors.focusable) {
20
20
  closeSelector = this.selectors.focusable;
@@ -25,18 +25,18 @@ var ComboboxComponentStrategy = class {
25
25
  const closeElement = page.locator(closeSelector).first();
26
26
  await closeElement.focus();
27
27
  await page.keyboard.press("Escape");
28
- listBoxClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
28
+ popupClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
29
29
  }
30
- if (!listBoxClosed && this.selectors.button) {
30
+ if (!popupClosed && this.selectors.button) {
31
31
  const buttonElement = page.locator(this.selectors.button).first();
32
32
  await buttonElement.click({ timeout: this.actionTimeoutMs });
33
- listBoxClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
33
+ popupClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
34
34
  }
35
- if (!listBoxClosed) {
35
+ if (!popupClosed) {
36
36
  await page.mouse.click(10, 10);
37
- listBoxClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
37
+ popupClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
38
38
  }
39
- if (!listBoxClosed) {
39
+ if (!popupClosed) {
40
40
  throw new Error(
41
41
  `\u274C FATAL: Cannot close combobox popup between tests. Popup remains visible after trying:
42
42
  1. Escape key
@@ -0,0 +1 @@
1
+ export { RelativeTargetResolver } from './chunk-GLT43UVH.js';
@@ -1,25 +1,5 @@
1
1
  import { chromium } from 'playwright';
2
2
 
3
- // src/utils/test/contract/contract.json
4
- var contract_default = {
5
- menu: {
6
- path: "./aria-contracts/menu/menu.contract.json",
7
- component: "menu"
8
- },
9
- "combobox.listbox": {
10
- path: "./aria-contracts/combobox/combobox.listbox.contract.json",
11
- component: "combobox.listbox"
12
- },
13
- accordion: {
14
- path: "./aria-contracts/accordion/accordion.contract.json",
15
- component: "accordion"
16
- },
17
- tabs: {
18
- path: "./aria-contracts/tabs/tabs.contract.json",
19
- component: "tabs"
20
- }
21
- };
22
-
23
3
  // src/utils/test/src/ContractReporter.ts
24
4
  var ContractReporter = class {
25
5
  startTime = 0;
@@ -328,9 +308,7 @@ async function getOrCreateContext() {
328
308
  if (!sharedContext) {
329
309
  const browser = await getOrCreateBrowser();
330
310
  sharedContext = await browser.newContext({
331
- // Isolated context - no permissions, no geolocation, etc.
332
311
  permissions: [],
333
- // Ignore HTTPS errors for local dev servers
334
312
  ignoreHTTPSErrors: true
335
313
  });
336
314
  }
@@ -351,4 +329,4 @@ async function closeSharedBrowser() {
351
329
  }
352
330
  }
353
331
 
354
- export { ContractReporter, closeSharedBrowser, contract_default, createTestPage, normalizeLevel, normalizeStrictness, resolveEnforcement };
332
+ export { ContractReporter, closeSharedBrowser, createTestPage, normalizeLevel, normalizeStrictness, resolveEnforcement };
@@ -0,0 +1,41 @@
1
+ // src/utils/test/src/RelativeTargetResolver.ts
2
+ var RelativeTargetResolver = class {
3
+ /**
4
+ * Resolve a relative target like "first", "second", "last", "next", "previous"
5
+ * @param page Playwright page instance
6
+ * @param selector Base selector to find elements
7
+ * @param relative Relative position (first, second, last, next, previous)
8
+ * @returns The resolved Locator or null if not found
9
+ */
10
+ static async resolve(page, selector, relative) {
11
+ const items = await page.locator(selector).all();
12
+ switch (relative) {
13
+ case "first":
14
+ return items[0];
15
+ case "second":
16
+ return items[1];
17
+ case "last":
18
+ return items[items.length - 1];
19
+ case "next": {
20
+ const currentIndex = await page.evaluate(([sel]) => {
21
+ const items2 = Array.from(document.querySelectorAll(sel));
22
+ return items2.indexOf(document.activeElement);
23
+ }, [selector]);
24
+ const nextIndex = (currentIndex + 1) % items.length;
25
+ return items[nextIndex];
26
+ }
27
+ case "previous": {
28
+ const currentIndex = await page.evaluate(([sel]) => {
29
+ const items2 = Array.from(document.querySelectorAll(sel));
30
+ return items2.indexOf(document.activeElement);
31
+ }, [selector]);
32
+ const prevIndex = (currentIndex - 1 + items.length) % items.length;
33
+ return items[prevIndex];
34
+ }
35
+ default:
36
+ return null;
37
+ }
38
+ }
39
+ };
40
+
41
+ export { RelativeTargetResolver };
@@ -69,8 +69,8 @@ function validateConfig(config) {
69
69
  if (typeof comp.name !== "string") {
70
70
  errors.push(`test.components[${idx}].name must be a string`);
71
71
  }
72
- if (comp.path !== void 0 && typeof comp.path !== "string") {
73
- errors.push(`test.components[${idx}].path must be a string when provided`);
72
+ if (comp.contractPath !== void 0 && typeof comp.contractPath !== "string") {
73
+ errors.push(`test.components[${idx}].contractPath must be a string when provided`);
74
74
  }
75
75
  if (comp.strategyPath !== void 0 && typeof comp.strategyPath !== "string") {
76
76
  errors.push(`test.components[${idx}].strategyPath must be a string when provided`);