aria-ease 6.9.1 → 6.11.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.
- package/README.md +3 -3
- package/{bin/buildContracts-GBOY7UXG.js → dist/buildContracts-FT6KWUJN.js} +31 -3
- package/{bin/chunk-LMSKLN5O.js → dist/chunk-NI3MQCAS.js} +34 -0
- package/{bin → dist}/cli.cjs +239 -24
- package/{bin → dist}/cli.js +4 -4
- package/dist/{configLoader-WTGJAP4Z.js → configLoader-DWHOHXHL.js} +34 -0
- package/{bin/configLoader-Q6A4JLKW.js → dist/configLoader-UJZHQBYS.js} +1 -1
- package/{bin/contractTestRunnerPlaywright-ZZNWDUYP.js → dist/contractTestRunnerPlaywright-QDXSK3FE.js} +173 -20
- package/dist/{contractTestRunnerPlaywright-XBWJZMR3.js → contractTestRunnerPlaywright-WNWQYSXZ.js} +173 -20
- package/dist/index.cjs +568 -298
- package/dist/index.d.cts +53 -53
- package/dist/index.d.ts +53 -53
- package/dist/index.js +364 -281
- package/dist/src/combobox/index.cjs +21 -7
- package/dist/src/combobox/index.js +21 -7
- package/dist/src/utils/test/{configLoader-YE2CYGDG.js → configLoader-SHJSRG2A.js} +34 -0
- package/dist/src/utils/test/{contractTestRunnerPlaywright-LC5OAVXB.js → contractTestRunnerPlaywright-Z2AHXSNM.js} +173 -20
- package/dist/src/utils/test/dsl/index.cjs +338 -269
- package/dist/src/utils/test/dsl/index.d.cts +53 -53
- package/dist/src/utils/test/dsl/index.d.ts +53 -53
- package/dist/src/utils/test/dsl/index.js +338 -269
- package/dist/src/utils/test/index.cjs +207 -20
- package/dist/src/utils/test/index.js +2 -2
- package/{bin/test-OND56UUL.js → dist/test-O3J4ZPQR.js} +2 -2
- package/package.json +4 -5
- package/bin/AccordionComponentStrategy-4ZEIQ2V6.js +0 -42
- package/bin/ComboboxComponentStrategy-OGRVZXAF.js +0 -64
- package/bin/MenuComponentStrategy-JAMTCSNF.js +0 -81
- package/bin/TabsComponentStrategy-3SQURPMX.js +0 -29
- package/bin/chunk-I2KLQ2HA.js +0 -22
- package/bin/chunk-PK5L2SAF.js +0 -17
- package/bin/chunk-XERMSYEH.js +0 -363
- /package/{bin → dist}/audit-RM6TCZ5C.js +0 -0
- /package/{bin → dist}/badgeHelper-JOWO6RQG.js +0 -0
- /package/{bin → dist}/chunk-JJEPLK7L.js +0 -0
- /package/{bin → dist}/cli.d.cts +0 -0
- /package/{bin → dist}/cli.d.ts +0 -0
- /package/{bin → dist}/formatters-32KQIIYS.js +0 -0
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.
|
|
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,315 +1508,384 @@ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation
|
|
|
1494
1508
|
return { activateTab, cleanup, refresh };
|
|
1495
1509
|
}
|
|
1496
1510
|
|
|
1497
|
-
// src/utils/test/dsl/
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
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
|
-
};
|
|
1511
|
+
// src/utils/test/dsl/src/state-packs/comboboxStatePack.ts
|
|
1512
|
+
function hasCapabilities(ctx, requiredCaps) {
|
|
1513
|
+
return requiredCaps.some((cap) => ctx.capabilities.includes(cap));
|
|
1514
|
+
}
|
|
1515
|
+
function resolveSetup(setup, ctx) {
|
|
1516
|
+
if (Array.isArray(setup) && setup.length && !setup[0].when) {
|
|
1517
|
+
setup = [{ when: ["keyboard"], steps: () => setup }];
|
|
1526
1518
|
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1519
|
+
for (const strat of setup) {
|
|
1520
|
+
if (hasCapabilities(ctx, strat.when)) {
|
|
1521
|
+
return strat.steps(ctx);
|
|
1522
|
+
}
|
|
1531
1523
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1524
|
+
throw new Error(
|
|
1525
|
+
`No setup strategy matches capabilities: ${ctx.capabilities.join(", ")}`
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
var COMBOBOX_STATES = {
|
|
1529
|
+
"listbox.open": {
|
|
1530
|
+
setup: [
|
|
1531
|
+
{
|
|
1532
|
+
when: ["keyboard", "textInput"],
|
|
1533
|
+
steps: () => [
|
|
1534
|
+
{ type: "keypress", target: "input", key: "ArrowDown" }
|
|
1535
|
+
]
|
|
1536
|
+
},
|
|
1537
|
+
{
|
|
1538
|
+
when: ["pointer"],
|
|
1539
|
+
steps: () => [
|
|
1540
|
+
{ type: "click", target: "button" }
|
|
1541
|
+
]
|
|
1542
|
+
}
|
|
1543
|
+
],
|
|
1544
|
+
assertion: isComboboxOpen
|
|
1545
|
+
},
|
|
1546
|
+
"listbox.closed": {
|
|
1547
|
+
setup: [
|
|
1548
|
+
{
|
|
1549
|
+
when: ["keyboard"],
|
|
1550
|
+
steps: () => [
|
|
1551
|
+
/* { type: "keypress", target: "input", key: "Escape" } */
|
|
1552
|
+
]
|
|
1553
|
+
},
|
|
1554
|
+
{
|
|
1555
|
+
when: ["pointer"],
|
|
1556
|
+
steps: () => [
|
|
1557
|
+
/* { type: "click", target: "button" } */
|
|
1558
|
+
]
|
|
1559
|
+
}
|
|
1560
|
+
],
|
|
1561
|
+
assertion: isComboboxClosed
|
|
1562
|
+
},
|
|
1563
|
+
"input.focused": {
|
|
1564
|
+
setup: [
|
|
1565
|
+
{
|
|
1566
|
+
when: ["keyboard"],
|
|
1567
|
+
steps: () => [
|
|
1568
|
+
{ type: "focus", target: "input" }
|
|
1569
|
+
]
|
|
1570
|
+
}
|
|
1571
|
+
],
|
|
1572
|
+
assertion: isInputFocused
|
|
1573
|
+
},
|
|
1574
|
+
"input.filled": {
|
|
1575
|
+
setup: [
|
|
1576
|
+
{
|
|
1577
|
+
when: ["keyboard", "textInput"],
|
|
1578
|
+
steps: () => [
|
|
1579
|
+
{ type: "type", target: "input", value: "test" }
|
|
1580
|
+
]
|
|
1581
|
+
}
|
|
1582
|
+
],
|
|
1583
|
+
assertion: isInputFilled
|
|
1584
|
+
},
|
|
1585
|
+
"activeOption.first": {
|
|
1586
|
+
requires: ["listbox.open"],
|
|
1587
|
+
setup: [
|
|
1588
|
+
{
|
|
1589
|
+
when: ["keyboard"],
|
|
1590
|
+
steps: () => [
|
|
1591
|
+
{ type: "keypress", target: "input", key: "ArrowDown" }
|
|
1592
|
+
]
|
|
1593
|
+
}
|
|
1594
|
+
],
|
|
1595
|
+
assertion: isActiveDescendantNotEmpty
|
|
1596
|
+
},
|
|
1597
|
+
"activeOption.last": {
|
|
1598
|
+
requires: ["activeOption.first"],
|
|
1599
|
+
setup: [
|
|
1600
|
+
{
|
|
1601
|
+
when: ["keyboard"],
|
|
1602
|
+
steps: () => [
|
|
1603
|
+
{ type: "keypress", target: "input", key: "ArrowUp" }
|
|
1604
|
+
]
|
|
1605
|
+
}
|
|
1606
|
+
],
|
|
1607
|
+
assertion: isActiveDescendantNotEmpty
|
|
1608
|
+
},
|
|
1609
|
+
"selectedOption.first": {
|
|
1610
|
+
requires: ["listbox.open"],
|
|
1611
|
+
setup: [
|
|
1612
|
+
{
|
|
1613
|
+
when: ["pointer"],
|
|
1614
|
+
steps: () => [
|
|
1615
|
+
{ type: "click", target: "relative", relativeTarget: "first" }
|
|
1616
|
+
]
|
|
1617
|
+
}
|
|
1618
|
+
],
|
|
1619
|
+
assertion: () => isAriaSelected("first")
|
|
1620
|
+
},
|
|
1621
|
+
"selectedOption.last": {
|
|
1622
|
+
requires: ["listbox.open"],
|
|
1623
|
+
setup: [
|
|
1624
|
+
{
|
|
1625
|
+
when: ["pointer"],
|
|
1626
|
+
steps: () => [
|
|
1627
|
+
{ type: "click", target: "relative", relativeTarget: "last" }
|
|
1628
|
+
]
|
|
1629
|
+
}
|
|
1630
|
+
],
|
|
1631
|
+
assertion: () => isAriaSelected("last")
|
|
1534
1632
|
}
|
|
1535
1633
|
};
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
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
|
-
});
|
|
1634
|
+
function isComboboxOpen() {
|
|
1635
|
+
return [
|
|
1636
|
+
{
|
|
1637
|
+
target: "listbox",
|
|
1638
|
+
assertion: "toBeVisible",
|
|
1639
|
+
failureMessage: "Expected listbox to be visible"
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
target: "input",
|
|
1643
|
+
assertion: "toHaveAttribute",
|
|
1644
|
+
attribute: "aria-expanded",
|
|
1645
|
+
expectedValue: "true",
|
|
1646
|
+
failureMessage: "Expect combobox input to have aria-expanded='true'"
|
|
1577
1647
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
this.assertions.push({
|
|
1590
|
-
target,
|
|
1648
|
+
];
|
|
1649
|
+
}
|
|
1650
|
+
function isComboboxClosed() {
|
|
1651
|
+
return [
|
|
1652
|
+
{
|
|
1653
|
+
target: "listbox",
|
|
1654
|
+
assertion: "notToBeVisible",
|
|
1655
|
+
failureMessage: "Expected listbox to be closed"
|
|
1656
|
+
},
|
|
1657
|
+
{
|
|
1658
|
+
target: "input",
|
|
1591
1659
|
assertion: "toHaveAttribute",
|
|
1592
|
-
attribute,
|
|
1593
|
-
expectedValue
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1660
|
+
attribute: "aria-expanded",
|
|
1661
|
+
expectedValue: "false",
|
|
1662
|
+
failureMessage: "Expect combobox input to have aria-expanded='false'"
|
|
1663
|
+
}
|
|
1664
|
+
];
|
|
1665
|
+
}
|
|
1666
|
+
function isActiveDescendantNotEmpty() {
|
|
1667
|
+
return [
|
|
1668
|
+
{
|
|
1669
|
+
target: "input",
|
|
1670
|
+
assertion: "toHaveAttribute",
|
|
1671
|
+
attribute: "aria-activedescendant",
|
|
1672
|
+
expectedValue: "!empty",
|
|
1673
|
+
failureMessage: "Expected aria-activedescendant to not be empty"
|
|
1674
|
+
}
|
|
1675
|
+
];
|
|
1676
|
+
}
|
|
1677
|
+
function isAriaSelected(index) {
|
|
1678
|
+
return [
|
|
1679
|
+
{
|
|
1680
|
+
target: "relative",
|
|
1681
|
+
relativeTarget: index,
|
|
1682
|
+
assertion: "toHaveAttribute",
|
|
1683
|
+
attribute: "aria-selected",
|
|
1684
|
+
expectedValue: "true",
|
|
1685
|
+
failureMessage: `Expected ${index} option to have aria-selected='true'`
|
|
1686
|
+
}
|
|
1687
|
+
];
|
|
1688
|
+
}
|
|
1689
|
+
function isInputFocused() {
|
|
1690
|
+
return [
|
|
1691
|
+
{
|
|
1692
|
+
target: "input",
|
|
1693
|
+
assertion: "toHaveFocus",
|
|
1694
|
+
failureMessage: "Expected input to be focused"
|
|
1695
|
+
}
|
|
1696
|
+
];
|
|
1697
|
+
}
|
|
1698
|
+
function isInputFilled() {
|
|
1699
|
+
return [
|
|
1700
|
+
{
|
|
1701
|
+
target: "input",
|
|
1702
|
+
assertion: "toHaveValue",
|
|
1703
|
+
expectedValue: "test",
|
|
1704
|
+
failureMessage: "Expected input to have the value 'test'"
|
|
1705
|
+
}
|
|
1706
|
+
];
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// src/utils/test/dsl/src/contractBuilder.ts
|
|
1710
|
+
var STATE_PACKS = {
|
|
1711
|
+
"combobox.listbox": COMBOBOX_STATES
|
|
1712
|
+
// Add more mappings as needed
|
|
1713
|
+
};
|
|
1714
|
+
var FluentContract = class {
|
|
1715
|
+
constructor(jsonContract) {
|
|
1716
|
+
this.jsonContract = jsonContract;
|
|
1617
1717
|
}
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
if (!match) return null;
|
|
1621
|
-
const relativeTarget = match[1];
|
|
1622
|
-
const selectorKey = match[2].trim();
|
|
1623
|
-
return { relativeTarget, selectorKey };
|
|
1718
|
+
toJSON() {
|
|
1719
|
+
return this.jsonContract;
|
|
1624
1720
|
}
|
|
1625
1721
|
};
|
|
1626
1722
|
var ContractBuilder = class {
|
|
1627
1723
|
constructor(componentName) {
|
|
1628
1724
|
this.componentName = componentName;
|
|
1725
|
+
this.statePack = STATE_PACKS[componentName] || {};
|
|
1629
1726
|
}
|
|
1630
1727
|
metaValue = {};
|
|
1631
1728
|
selectorsValue = {};
|
|
1632
1729
|
relationshipInvariants = [];
|
|
1633
1730
|
staticAssertions = [];
|
|
1634
1731
|
dynamicTests = [];
|
|
1732
|
+
statePack;
|
|
1635
1733
|
meta(meta) {
|
|
1636
|
-
this.metaValue =
|
|
1734
|
+
this.metaValue = meta;
|
|
1637
1735
|
return this;
|
|
1638
1736
|
}
|
|
1639
1737
|
selectors(selectors) {
|
|
1640
|
-
this.selectorsValue =
|
|
1738
|
+
this.selectorsValue = selectors;
|
|
1641
1739
|
return this;
|
|
1642
1740
|
}
|
|
1643
|
-
|
|
1644
|
-
|
|
1741
|
+
relationships(fn) {
|
|
1742
|
+
const api = {
|
|
1743
|
+
ariaReference: (from, attribute, to) => ({
|
|
1744
|
+
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" })
|
|
1746
|
+
}),
|
|
1747
|
+
contains: (parent, child) => ({
|
|
1748
|
+
required: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "required" }),
|
|
1749
|
+
optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" })
|
|
1750
|
+
})
|
|
1751
|
+
};
|
|
1752
|
+
fn(api);
|
|
1645
1753
|
return this;
|
|
1646
1754
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
this.
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
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
|
-
});
|
|
1755
|
+
static(fn) {
|
|
1756
|
+
const api = {
|
|
1757
|
+
target: (target) => ({
|
|
1758
|
+
has: (attribute, expectedValue) => ({
|
|
1759
|
+
required: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "required" }),
|
|
1760
|
+
optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" })
|
|
1761
|
+
})
|
|
1762
|
+
})
|
|
1763
|
+
};
|
|
1764
|
+
fn(api);
|
|
1681
1765
|
return this;
|
|
1682
1766
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1767
|
+
when(event) {
|
|
1768
|
+
return new DynamicTestBuilder(this, this.statePack, event);
|
|
1769
|
+
}
|
|
1770
|
+
addDynamicTest(test) {
|
|
1771
|
+
this.dynamicTests.push(test);
|
|
1772
|
+
}
|
|
1773
|
+
build() {
|
|
1774
|
+
return {
|
|
1775
|
+
meta: this.metaValue,
|
|
1776
|
+
selectors: this.selectorsValue,
|
|
1777
|
+
relationships: this.relationshipInvariants.length ? this.relationshipInvariants : void 0,
|
|
1778
|
+
static: this.staticAssertions.length ? [{ assertions: this.staticAssertions }] : [],
|
|
1779
|
+
dynamic: this.dynamicTests
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
var DynamicTestBuilder = class {
|
|
1784
|
+
constructor(parent, statePack, event) {
|
|
1785
|
+
this.parent = parent;
|
|
1786
|
+
this.statePack = statePack;
|
|
1787
|
+
this.event = event;
|
|
1788
|
+
}
|
|
1789
|
+
_as;
|
|
1790
|
+
_on;
|
|
1791
|
+
_given = [];
|
|
1792
|
+
_then = [];
|
|
1793
|
+
_desc = "";
|
|
1794
|
+
_level = "required";
|
|
1795
|
+
as(actionType) {
|
|
1796
|
+
this._as = actionType;
|
|
1685
1797
|
return this;
|
|
1686
1798
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1799
|
+
on(target) {
|
|
1800
|
+
this._on = target;
|
|
1801
|
+
return this;
|
|
1689
1802
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
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
|
-
}
|
|
1803
|
+
given(states) {
|
|
1804
|
+
this._given = Array.isArray(states) ? states : [states];
|
|
1805
|
+
return this;
|
|
1726
1806
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
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
|
-
}
|
|
1807
|
+
then(states) {
|
|
1808
|
+
this._then = Array.isArray(states) ? states : [states];
|
|
1809
|
+
return this;
|
|
1745
1810
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1811
|
+
describe(desc) {
|
|
1812
|
+
this._desc = desc;
|
|
1813
|
+
return this;
|
|
1814
|
+
}
|
|
1815
|
+
required() {
|
|
1816
|
+
this._level = "required";
|
|
1817
|
+
this._finalize();
|
|
1818
|
+
return this.parent;
|
|
1819
|
+
}
|
|
1820
|
+
optional() {
|
|
1821
|
+
this._level = "optional";
|
|
1822
|
+
this._finalize();
|
|
1823
|
+
return this.parent;
|
|
1824
|
+
}
|
|
1825
|
+
recommended() {
|
|
1826
|
+
this._level = "recommended";
|
|
1827
|
+
this._finalize();
|
|
1828
|
+
return this.parent;
|
|
1829
|
+
}
|
|
1830
|
+
_finalize() {
|
|
1831
|
+
const capabilityMap = {
|
|
1832
|
+
keypress: "keyboard",
|
|
1833
|
+
click: "pointer",
|
|
1834
|
+
type: "textInput",
|
|
1835
|
+
focus: "keyboard",
|
|
1836
|
+
hover: "pointer"
|
|
1837
|
+
// add more mappings as needed
|
|
1755
1838
|
};
|
|
1756
|
-
this.
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1839
|
+
const capability = capabilityMap[this._as || "keyboard"] || (this._as || "keyboard");
|
|
1840
|
+
const ctx = { capabilities: [capability] };
|
|
1841
|
+
const resolveAllSetups = (stateName, visited = /* @__PURE__ */ new Set()) => {
|
|
1842
|
+
if (visited.has(stateName)) return [];
|
|
1843
|
+
visited.add(stateName);
|
|
1844
|
+
const s = this.statePack[stateName];
|
|
1845
|
+
if (!s) return [];
|
|
1846
|
+
let actions = [];
|
|
1847
|
+
if (Array.isArray(s.requires)) {
|
|
1848
|
+
for (const req of s.requires) {
|
|
1849
|
+
actions = actions.concat(resolveAllSetups(req, visited));
|
|
1762
1850
|
}
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1851
|
+
}
|
|
1852
|
+
if (s.setup) actions = actions.concat(resolveSetup(s.setup, ctx));
|
|
1853
|
+
return actions;
|
|
1854
|
+
};
|
|
1855
|
+
const setup = [];
|
|
1856
|
+
for (const state of this._given) {
|
|
1857
|
+
setup.push(...resolveAllSetups(state));
|
|
1858
|
+
}
|
|
1859
|
+
const assertions = [];
|
|
1860
|
+
for (const state of this._then) {
|
|
1861
|
+
const s = this.statePack[state];
|
|
1862
|
+
if (s && s.assertion !== void 0) {
|
|
1863
|
+
let value = s.assertion;
|
|
1864
|
+
if (typeof value === "function") {
|
|
1865
|
+
try {
|
|
1866
|
+
value = value();
|
|
1867
|
+
} catch (e) {
|
|
1868
|
+
throw new Error(`Error calling assertion function for state '${state}': ${e.message}`);
|
|
1869
|
+
}
|
|
1774
1870
|
}
|
|
1775
|
-
|
|
1871
|
+
if (Array.isArray(value)) assertions.push(...value);
|
|
1872
|
+
else assertions.push(value);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
const action = [
|
|
1876
|
+
{
|
|
1877
|
+
type: this._as,
|
|
1878
|
+
target: this._on,
|
|
1879
|
+
key: this._as === "keypress" ? this.event : void 0
|
|
1880
|
+
}
|
|
1881
|
+
];
|
|
1882
|
+
this.parent.addDynamicTest({
|
|
1883
|
+
description: this._desc || "",
|
|
1884
|
+
level: this._level,
|
|
1885
|
+
action,
|
|
1886
|
+
assertions,
|
|
1887
|
+
...setup.length ? { setup } : {}
|
|
1776
1888
|
});
|
|
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
1889
|
}
|
|
1807
1890
|
};
|
|
1808
1891
|
function createContract(componentName, define) {
|
|
@@ -2025,7 +2108,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
2025
2108
|
let configBaseDir = typeof process !== "undefined" ? process.cwd() : "";
|
|
2026
2109
|
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
2027
2110
|
try {
|
|
2028
|
-
const { loadConfig } = await import("./configLoader-
|
|
2111
|
+
const { loadConfig } = await import("./configLoader-DWHOHXHL.js");
|
|
2029
2112
|
const result2 = await loadConfig(process.cwd());
|
|
2030
2113
|
config = result2.config;
|
|
2031
2114
|
if (result2.configPath) {
|
|
@@ -2047,7 +2130,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
2047
2130
|
const devServerUrl = await checkDevServer(url);
|
|
2048
2131
|
if (devServerUrl) {
|
|
2049
2132
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
2050
|
-
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-
|
|
2133
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-WNWQYSXZ.js");
|
|
2051
2134
|
contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
|
|
2052
2135
|
} else {
|
|
2053
2136
|
throw new Error(
|