aria-ease 6.8.0 → 6.9.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 (40) hide show
  1. package/README.md +68 -6
  2. package/bin/AccordionComponentStrategy-4ZEIQ2V6.js +42 -0
  3. package/bin/ComboboxComponentStrategy-OGRVZXAF.js +64 -0
  4. package/bin/MenuComponentStrategy-JAMTCSNF.js +81 -0
  5. package/bin/TabsComponentStrategy-3SQURPMX.js +29 -0
  6. package/bin/buildContracts-GBOY7UXG.js +437 -0
  7. package/bin/{chunk-VPBHLMAS.js → chunk-LMSKLN5O.js} +21 -0
  8. package/bin/chunk-PK5L2SAF.js +17 -0
  9. package/bin/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
  10. package/bin/cli.cjs +991 -128
  11. package/bin/cli.js +33 -2
  12. package/bin/{configLoader-XRF6VM4J.js → configLoader-Q6A4JLKW.js} +1 -1
  13. package/{dist/contractTestRunnerPlaywright-UAOFNS7Z.js → bin/contractTestRunnerPlaywright-ZZNWDUYP.js} +270 -219
  14. package/bin/{test-WRIJHN6H.js → test-OND56UUL.js} +97 -10
  15. package/dist/AccordionComponentStrategy-4ZEIQ2V6.js +42 -0
  16. package/dist/ComboboxComponentStrategy-OGRVZXAF.js +64 -0
  17. package/dist/MenuComponentStrategy-JAMTCSNF.js +81 -0
  18. package/dist/TabsComponentStrategy-3SQURPMX.js +29 -0
  19. package/dist/chunk-PK5L2SAF.js +17 -0
  20. package/dist/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
  21. package/dist/{configLoader-IT4PWCJB.js → configLoader-WTGJAP4Z.js} +21 -0
  22. package/{bin/contractTestRunnerPlaywright-UAOFNS7Z.js → dist/contractTestRunnerPlaywright-XBWJZMR3.js} +270 -219
  23. package/dist/index.cjs +794 -90
  24. package/dist/index.d.cts +136 -1
  25. package/dist/index.d.ts +136 -1
  26. package/dist/index.js +415 -10
  27. package/dist/src/utils/test/AccordionComponentStrategy-WRHZOEN6.js +38 -0
  28. package/dist/src/utils/test/ComboboxComponentStrategy-5AECQSRN.js +60 -0
  29. package/dist/src/utils/test/MenuComponentStrategy-VKZQYLBE.js +77 -0
  30. package/dist/src/utils/test/TabsComponentStrategy-BKG53SEV.js +26 -0
  31. package/dist/src/utils/test/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
  32. package/dist/src/utils/test/{configLoader-LD4RV2WQ.js → configLoader-YE2CYGDG.js} +21 -0
  33. package/dist/src/utils/test/{contractTestRunnerPlaywright-IRJOAEMT.js → contractTestRunnerPlaywright-LC5OAVXB.js} +262 -200
  34. package/dist/src/utils/test/dsl/index.cjs +320 -0
  35. package/dist/src/utils/test/dsl/index.d.cts +136 -0
  36. package/dist/src/utils/test/dsl/index.d.ts +136 -0
  37. package/dist/src/utils/test/dsl/index.js +318 -0
  38. package/dist/src/utils/test/index.cjs +472 -88
  39. package/dist/src/utils/test/index.js +97 -12
  40. package/package.json +9 -3
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  normalizeLevel,
6
6
  normalizeStrictness,
7
7
  resolveEnforcement
8
- } from "./chunk-2TOYEY5L.js";
8
+ } from "./chunk-XERMSYEH.js";
9
9
  import "./chunk-I2KLQ2HA.js";
10
10
 
11
11
  // src/accordion/src/makeAccordionAccessible/makeAccordionAccessible.ts
@@ -1494,6 +1494,323 @@ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation
1494
1494
  return { activateTab, cleanup, refresh };
1495
1495
  }
1496
1496
 
1497
+ // src/utils/test/dsl/index.ts
1498
+ var FluentContract = class {
1499
+ constructor(jsonContract) {
1500
+ this.jsonContract = jsonContract;
1501
+ }
1502
+ toJSON() {
1503
+ return this.jsonContract;
1504
+ }
1505
+ };
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;
1554
+ }
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
+ }
1578
+ return this;
1579
+ }
1580
+ visible(target) {
1581
+ this.assertions.push({ target, assertion: "toBeVisible" });
1582
+ return this;
1583
+ }
1584
+ hidden(target) {
1585
+ this.assertions.push({ target, assertion: "notToBeVisible" });
1586
+ return this;
1587
+ }
1588
+ has(target, attribute, expectedValue) {
1589
+ this.assertions.push({
1590
+ target,
1591
+ assertion: "toHaveAttribute",
1592
+ attribute,
1593
+ expectedValue
1594
+ });
1595
+ return this;
1596
+ }
1597
+ required() {
1598
+ this.finalize("required");
1599
+ }
1600
+ recommended() {
1601
+ this.finalize("recommended");
1602
+ }
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
+ });
1617
+ }
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 };
1624
+ }
1625
+ };
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 };
1637
+ return this;
1638
+ }
1639
+ selectors(selectors) {
1640
+ this.selectorsValue = { ...this.selectorsValue, ...selectors };
1641
+ return this;
1642
+ }
1643
+ relationship(invariant) {
1644
+ this.relationshipInvariants.push(invariant);
1645
+ return this;
1646
+ }
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
+ });
1681
+ return this;
1682
+ }
1683
+ static(builderFn) {
1684
+ builderFn(new StaticBuilder(this.staticAssertions));
1685
+ return this;
1686
+ }
1687
+ when(key) {
1688
+ return new DynamicChain(key, this.dynamicTests, this.selectorsValue);
1689
+ }
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
+ }
1726
+ }
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`);
1734
+ }
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";
1755
+ };
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
+ });
1776
+ });
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
+ }
1807
+ };
1808
+ function createContract(componentName, define) {
1809
+ const builder = new ContractBuilder(componentName);
1810
+ define(builder);
1811
+ return new FluentContract(builder.build());
1812
+ }
1813
+
1497
1814
  // src/utils/test/src/test.ts
1498
1815
  import { axe } from "jest-axe";
1499
1816
 
@@ -1510,7 +1827,7 @@ async function runContractTests(componentName, component, strictness) {
1510
1827
  const resolvedPath = new URL(contractPath, import.meta.url).pathname;
1511
1828
  const contractData = await fs.readFile(resolvedPath, "utf-8");
1512
1829
  const componentContract = JSON.parse(contractData);
1513
- const totalTests = componentContract.static[0].assertions.length + componentContract.dynamic.length;
1830
+ const totalTests = (componentContract.relationships?.length || 0) + (componentContract.static[0]?.assertions.length || 0) + componentContract.dynamic.length;
1514
1831
  reporter.start(componentName, totalTests);
1515
1832
  const failures = [];
1516
1833
  const passes = [];
@@ -1534,6 +1851,82 @@ async function runContractTests(componentName, component, strictness) {
1534
1851
  let staticPassed = 0;
1535
1852
  let staticFailed = 0;
1536
1853
  let staticWarnings = 0;
1854
+ for (const rel of componentContract.relationships || []) {
1855
+ const relationshipLevel = normalizeLevel(rel.level);
1856
+ if (rel.type === "aria-reference") {
1857
+ const fromSelector = componentContract.selectors[rel.from];
1858
+ const toSelector = componentContract.selectors[rel.to];
1859
+ const relDescription = `${rel.from}.${rel.attribute} references ${rel.to}`;
1860
+ if (!fromSelector || !toSelector) {
1861
+ const outcome = classifyFailure(`Relationship selector missing: from="${rel.from}" or to="${rel.to}" not found in selectors.`, rel.level);
1862
+ if (outcome.status === "fail") staticFailed += 1;
1863
+ if (outcome.status === "warn") staticWarnings += 1;
1864
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
1865
+ continue;
1866
+ }
1867
+ const fromTarget = component.querySelector(fromSelector);
1868
+ const toTarget = component.querySelector(toSelector);
1869
+ if (!fromTarget || !toTarget) {
1870
+ const outcome = classifyFailure(`Relationship target not found: ${!fromTarget ? rel.from : rel.to}.`, rel.level);
1871
+ if (outcome.status === "fail") staticFailed += 1;
1872
+ if (outcome.status === "warn") staticWarnings += 1;
1873
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
1874
+ continue;
1875
+ }
1876
+ const toId = toTarget.getAttribute("id");
1877
+ const attrValue = fromTarget.getAttribute(rel.attribute) || "";
1878
+ if (!toId) {
1879
+ const outcome = classifyFailure(`Relationship target "${rel.to}" must have an id for ${rel.attribute} validation.`, rel.level);
1880
+ if (outcome.status === "fail") staticFailed += 1;
1881
+ if (outcome.status === "warn") staticWarnings += 1;
1882
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
1883
+ continue;
1884
+ }
1885
+ const references = attrValue.split(/\s+/).filter(Boolean);
1886
+ if (!references.includes(toId)) {
1887
+ const outcome = classifyFailure(`Expected ${rel.from} ${rel.attribute} to reference id "${toId}", found "${attrValue}".`, rel.level);
1888
+ if (outcome.status === "fail") staticFailed += 1;
1889
+ if (outcome.status === "warn") staticWarnings += 1;
1890
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
1891
+ continue;
1892
+ }
1893
+ passes.push(`Relationship valid: ${rel.from}.${rel.attribute} -> ${rel.to} (id=${toId}).`);
1894
+ staticPassed += 1;
1895
+ reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
1896
+ continue;
1897
+ }
1898
+ if (rel.type === "contains") {
1899
+ const parentSelector = componentContract.selectors[rel.parent];
1900
+ const childSelector = componentContract.selectors[rel.child];
1901
+ const relDescription = `${rel.parent} contains ${rel.child}`;
1902
+ if (!parentSelector || !childSelector) {
1903
+ const outcome = classifyFailure(`Relationship selector missing: parent="${rel.parent}" or child="${rel.child}" not found in selectors.`, rel.level);
1904
+ if (outcome.status === "fail") staticFailed += 1;
1905
+ if (outcome.status === "warn") staticWarnings += 1;
1906
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
1907
+ continue;
1908
+ }
1909
+ const parentTarget = component.querySelector(parentSelector);
1910
+ if (!parentTarget) {
1911
+ const outcome = classifyFailure(`Relationship parent target not found: ${rel.parent}.`, rel.level);
1912
+ if (outcome.status === "fail") staticFailed += 1;
1913
+ if (outcome.status === "warn") staticWarnings += 1;
1914
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
1915
+ continue;
1916
+ }
1917
+ const nestedChild = parentTarget.querySelector(childSelector);
1918
+ if (!nestedChild) {
1919
+ const outcome = classifyFailure(`Expected ${rel.parent} to contain descendant matching selector for ${rel.child}.`, rel.level);
1920
+ if (outcome.status === "fail") staticFailed += 1;
1921
+ if (outcome.status === "warn") staticWarnings += 1;
1922
+ reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
1923
+ continue;
1924
+ }
1925
+ passes.push(`Relationship valid: ${rel.parent} contains ${rel.child}.`);
1926
+ staticPassed += 1;
1927
+ reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
1928
+ }
1929
+ }
1537
1930
  for (const test of componentContract.static[0].assertions) {
1538
1931
  if (test.target !== "relative") {
1539
1932
  const staticLevel = normalizeLevel(test.level);
@@ -1589,6 +1982,7 @@ async function runContractTests(componentName, component, strictness) {
1589
1982
  }
1590
1983
 
1591
1984
  // src/utils/test/src/test.ts
1985
+ import path from "path";
1592
1986
  async function testUiComponent(componentName, component, url, options = {}) {
1593
1987
  if (!componentName || typeof componentName !== "string") {
1594
1988
  throw new Error("\u274C testUiComponent requires a valid componentName (string)");
@@ -1627,14 +2021,24 @@ Error: ${error instanceof Error ? error.message : String(error)}`
1627
2021
  return null;
1628
2022
  }
1629
2023
  let strictness = normalizeStrictness(options.strictness);
1630
- if (options.strictness === void 0 && typeof window === "undefined") {
2024
+ let config = {};
2025
+ let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
2026
+ if (typeof process !== "undefined" && typeof process.cwd === "function") {
1631
2027
  try {
1632
- const { loadConfig } = await import("./configLoader-IT4PWCJB.js");
1633
- const { config } = await loadConfig(process.cwd());
1634
- const componentStrictness = config.test?.components?.find((comp) => comp?.name === componentName)?.strictness;
1635
- strictness = normalizeStrictness(componentStrictness ?? config.test?.strictness);
2028
+ const { loadConfig } = await import("./configLoader-WTGJAP4Z.js");
2029
+ const result2 = await loadConfig(process.cwd());
2030
+ config = result2.config;
2031
+ if (result2.configPath) {
2032
+ configBaseDir = path.dirname(result2.configPath);
2033
+ }
2034
+ if (options.strictness === void 0) {
2035
+ const componentStrictness = config.test?.components?.find((comp) => comp?.name === componentName)?.strictness;
2036
+ strictness = normalizeStrictness(componentStrictness ?? config.test?.strictness);
2037
+ }
1636
2038
  } catch {
1637
- strictness = "balanced";
2039
+ if (options.strictness === void 0) {
2040
+ strictness = "balanced";
2041
+ }
1638
2042
  }
1639
2043
  }
1640
2044
  let contract;
@@ -1643,8 +2047,8 @@ Error: ${error instanceof Error ? error.message : String(error)}`
1643
2047
  const devServerUrl = await checkDevServer(url);
1644
2048
  if (devServerUrl) {
1645
2049
  console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
1646
- const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-UAOFNS7Z.js");
1647
- contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness);
2050
+ const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-XBWJZMR3.js");
2051
+ contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
1648
2052
  } else {
1649
2053
  throw new Error(
1650
2054
  `\u274C Dev server not running at ${url}
@@ -1747,6 +2151,7 @@ async function cleanupTests() {
1747
2151
  }
1748
2152
  export {
1749
2153
  cleanupTests,
2154
+ createContract,
1750
2155
  makeAccordionAccessible,
1751
2156
  makeBlockAccessible,
1752
2157
  makeCheckboxAccessible,
@@ -0,0 +1,38 @@
1
+ import { expect } from '@playwright/test';
2
+
3
+ // src/utils/test/src/component-strategies/AccordionComponentStrategy.ts
4
+ var AccordionComponentStrategy = class {
5
+ constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
6
+ this.mainSelector = mainSelector;
7
+ this.selectors = selectors;
8
+ this.actionTimeoutMs = actionTimeoutMs;
9
+ this.assertionTimeoutMs = assertionTimeoutMs;
10
+ }
11
+ async resetState(page) {
12
+ if (!this.selectors.panel || !this.selectors.trigger || this.selectors.popup) {
13
+ return;
14
+ }
15
+ const triggerSelector = this.selectors.trigger;
16
+ const panelSelector = this.selectors.panel;
17
+ if (!triggerSelector || !panelSelector) return;
18
+ const allTriggers = await page.locator(triggerSelector).all();
19
+ for (const trigger of allTriggers) {
20
+ const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
21
+ const triggerPanel = await trigger.getAttribute("aria-controls");
22
+ if (isExpanded && triggerPanel) {
23
+ await trigger.click({ timeout: this.actionTimeoutMs });
24
+ const panel = page.locator(`#${triggerPanel}`);
25
+ await expect(panel).toBeHidden({ timeout: this.assertionTimeoutMs }).catch(() => {
26
+ });
27
+ }
28
+ }
29
+ }
30
+ async shouldSkipTest() {
31
+ return false;
32
+ }
33
+ getMainSelector() {
34
+ return this.mainSelector;
35
+ }
36
+ };
37
+
38
+ export { AccordionComponentStrategy };
@@ -0,0 +1,60 @@
1
+ import { expect } from '@playwright/test';
2
+
3
+ // src/utils/test/src/component-strategies/ComboboxComponentStrategy.ts
4
+ var ComboboxComponentStrategy = class {
5
+ constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
6
+ this.mainSelector = mainSelector;
7
+ this.selectors = selectors;
8
+ this.actionTimeoutMs = actionTimeoutMs;
9
+ this.assertionTimeoutMs = assertionTimeoutMs;
10
+ }
11
+ async resetState(page) {
12
+ if (!this.selectors.popup) return;
13
+ const popupSelector = this.selectors.popup;
14
+ const popupElement = page.locator(popupSelector).first();
15
+ const isPopupVisible = await popupElement.isVisible().catch(() => false);
16
+ if (!isPopupVisible) return;
17
+ let listBoxClosed = false;
18
+ let closeSelector = this.selectors.input;
19
+ if (!closeSelector && this.selectors.focusable) {
20
+ closeSelector = this.selectors.focusable;
21
+ } else if (!closeSelector) {
22
+ closeSelector = this.selectors.button;
23
+ }
24
+ if (closeSelector) {
25
+ const closeElement = page.locator(closeSelector).first();
26
+ await closeElement.focus();
27
+ await page.keyboard.press("Escape");
28
+ listBoxClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
29
+ }
30
+ if (!listBoxClosed && this.selectors.button) {
31
+ const buttonElement = page.locator(this.selectors.button).first();
32
+ await buttonElement.click({ timeout: this.actionTimeoutMs });
33
+ listBoxClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
34
+ }
35
+ if (!listBoxClosed) {
36
+ await page.mouse.click(10, 10);
37
+ listBoxClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
38
+ }
39
+ if (!listBoxClosed) {
40
+ throw new Error(
41
+ `\u274C FATAL: Cannot close combobox popup between tests. Popup remains visible after trying:
42
+ 1. Escape key
43
+ 2. Clicking button
44
+ 3. Clicking outside
45
+ This indicates a problem with the combobox component's close functionality.`
46
+ );
47
+ }
48
+ if (this.selectors.input) {
49
+ await page.locator(this.selectors.input).first().clear();
50
+ }
51
+ }
52
+ async shouldSkipTest() {
53
+ return false;
54
+ }
55
+ getMainSelector() {
56
+ return this.mainSelector;
57
+ }
58
+ };
59
+
60
+ export { ComboboxComponentStrategy };
@@ -0,0 +1,77 @@
1
+ import { expect } from '@playwright/test';
2
+
3
+ // src/utils/test/src/component-strategies/MenuComponentStrategy.ts
4
+ var MenuComponentStrategy = class {
5
+ constructor(mainSelector, selectors, actionTimeoutMs = 400, assertionTimeoutMs = 400) {
6
+ this.mainSelector = mainSelector;
7
+ this.selectors = selectors;
8
+ this.actionTimeoutMs = actionTimeoutMs;
9
+ this.assertionTimeoutMs = assertionTimeoutMs;
10
+ }
11
+ async resetState(page) {
12
+ if (!this.selectors.popup) return;
13
+ const popupSelector = this.selectors.popup;
14
+ const popupElement = page.locator(popupSelector).first();
15
+ const isPopupVisible = await popupElement.isVisible().catch(() => false);
16
+ if (!isPopupVisible) return;
17
+ let menuClosed = false;
18
+ let closeSelector = this.selectors.input;
19
+ if (!closeSelector && this.selectors.focusable) {
20
+ closeSelector = this.selectors.focusable;
21
+ } else if (!closeSelector) {
22
+ closeSelector = this.selectors.trigger;
23
+ }
24
+ if (closeSelector) {
25
+ const closeElement = page.locator(closeSelector).first();
26
+ await closeElement.focus();
27
+ await page.keyboard.press("Escape");
28
+ menuClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
29
+ }
30
+ if (!menuClosed && this.selectors.trigger) {
31
+ const triggerElement = page.locator(this.selectors.trigger).first();
32
+ await triggerElement.click({ timeout: this.actionTimeoutMs });
33
+ menuClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
34
+ }
35
+ if (!menuClosed) {
36
+ await page.mouse.click(10, 10);
37
+ menuClosed = await expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
38
+ }
39
+ if (!menuClosed) {
40
+ throw new Error(
41
+ `\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
42
+ 1. Escape key
43
+ 2. Clicking trigger
44
+ 3. Clicking outside
45
+ This indicates a problem with the menu component's close functionality.`
46
+ );
47
+ }
48
+ if (this.selectors.input) {
49
+ await page.locator(this.selectors.input).first().clear();
50
+ }
51
+ if (this.selectors.trigger) {
52
+ const triggerElement = page.locator(this.selectors.trigger).first();
53
+ await triggerElement.focus();
54
+ }
55
+ }
56
+ async shouldSkipTest(test, page) {
57
+ const requiresSubmenu = test.action.some(
58
+ (act) => act.target === "submenu" || act.target === "submenuTrigger" || act.target === "submenuItems"
59
+ ) || test.assertions.some(
60
+ (assertion) => assertion.target === "submenu" || assertion.target === "submenuTrigger" || assertion.target === "submenuItems"
61
+ );
62
+ if (!requiresSubmenu) {
63
+ return false;
64
+ }
65
+ const submenuTriggerSelector = this.selectors.submenuTrigger;
66
+ if (!submenuTriggerSelector) {
67
+ return true;
68
+ }
69
+ const submenuTriggerCount = await page.locator(submenuTriggerSelector).count();
70
+ return submenuTriggerCount === 0;
71
+ }
72
+ getMainSelector() {
73
+ return this.mainSelector;
74
+ }
75
+ };
76
+
77
+ export { MenuComponentStrategy };
@@ -0,0 +1,26 @@
1
+ // src/utils/test/src/component-strategies/TabsComponentStrategy.ts
2
+ var TabsComponentStrategy = class {
3
+ constructor(mainSelector, selectors) {
4
+ this.mainSelector = mainSelector;
5
+ this.selectors = selectors;
6
+ }
7
+ async resetState() {
8
+ }
9
+ async shouldSkipTest(test, page) {
10
+ if (test.isVertical !== void 0 && this.selectors.tablist) {
11
+ const tablistSelector = this.selectors.tablist;
12
+ const tablist = page.locator(tablistSelector).first();
13
+ const orientation = await tablist.getAttribute("aria-orientation");
14
+ const isVertical = orientation === "vertical";
15
+ if (test.isVertical !== isVertical) {
16
+ return true;
17
+ }
18
+ }
19
+ return false;
20
+ }
21
+ getMainSelector() {
22
+ return this.mainSelector;
23
+ }
24
+ };
25
+
26
+ export { TabsComponentStrategy };