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.
- package/README.md +3 -3
- package/bin/{buildContracts-GBOY7UXG.js → buildContracts-S22V7AGV.js} +28 -0
- package/bin/{chunk-LMSKLN5O.js → chunk-NI3MQCAS.js} +34 -0
- package/bin/cli.cjs +235 -20
- package/bin/cli.js +4 -4
- package/bin/{configLoader-Q6A4JLKW.js → configLoader-UJZHQBYS.js} +1 -1
- package/{dist/contractTestRunnerPlaywright-XBWJZMR3.js → bin/contractTestRunnerPlaywright-QDXSK3FE.js} +173 -20
- package/bin/{test-OND56UUL.js → test-O3J4ZPQR.js} +2 -2
- package/dist/{configLoader-WTGJAP4Z.js → configLoader-DWHOHXHL.js} +34 -0
- package/{bin/contractTestRunnerPlaywright-ZZNWDUYP.js → dist/contractTestRunnerPlaywright-WNWQYSXZ.js} +173 -20
- package/dist/index.cjs +506 -312
- package/dist/index.d.cts +54 -54
- package/dist/index.d.ts +54 -54
- package/dist/index.js +298 -291
- package/dist/src/{Types.d-DYfYR3Vc.d.cts → Types.d-yGC2bBaB.d.cts} +1 -1
- package/dist/src/{Types.d-DYfYR3Vc.d.ts → Types.d-yGC2bBaB.d.ts} +1 -1
- package/dist/src/accordion/index.d.cts +1 -1
- package/dist/src/accordion/index.d.ts +1 -1
- package/dist/src/block/index.d.cts +1 -1
- package/dist/src/block/index.d.ts +1 -1
- package/dist/src/checkbox/index.d.cts +1 -1
- package/dist/src/checkbox/index.d.ts +1 -1
- package/dist/src/combobox/index.cjs +21 -7
- package/dist/src/combobox/index.d.cts +1 -1
- package/dist/src/combobox/index.d.ts +1 -1
- package/dist/src/combobox/index.js +21 -7
- package/dist/src/menu/index.d.cts +1 -1
- package/dist/src/menu/index.d.ts +1 -1
- package/dist/src/radio/index.d.cts +1 -1
- package/dist/src/radio/index.d.ts +1 -1
- package/dist/src/tabs/index.d.cts +1 -1
- package/dist/src/tabs/index.d.ts +1 -1
- package/dist/src/toggle/index.d.cts +1 -1
- package/dist/src/toggle/index.d.ts +1 -1
- 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 +313 -0
- package/dist/src/utils/test/dsl/index.d.cts +136 -0
- package/dist/src/utils/test/dsl/index.d.ts +136 -0
- package/dist/src/utils/test/dsl/index.js +311 -0
- package/dist/src/utils/test/index.cjs +207 -20
- package/dist/src/utils/test/index.js +2 -2
- 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.
|
|
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/
|
|
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
|
|
1507
|
-
constructor(
|
|
1508
|
-
this.
|
|
1509
|
-
this.
|
|
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
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
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
|
-
|
|
1581
|
-
this.
|
|
1679
|
+
selectors(selectors) {
|
|
1680
|
+
this.selectorsValue = selectors;
|
|
1582
1681
|
return this;
|
|
1583
1682
|
}
|
|
1584
|
-
|
|
1585
|
-
|
|
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
|
-
|
|
1589
|
-
|
|
1590
|
-
target
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
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
|
-
|
|
1598
|
-
this.
|
|
1599
|
-
}
|
|
1600
|
-
recommended() {
|
|
1601
|
-
this.finalize("recommended");
|
|
1709
|
+
when(event) {
|
|
1710
|
+
return new DynamicTestBuilder(this, this.statePack, event);
|
|
1602
1711
|
}
|
|
1603
|
-
|
|
1604
|
-
this.
|
|
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
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
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
|
|
1627
|
-
constructor(
|
|
1628
|
-
this.
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
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
|
-
|
|
1640
|
-
this.
|
|
1741
|
+
on(target) {
|
|
1742
|
+
this._on = target;
|
|
1641
1743
|
return this;
|
|
1642
1744
|
}
|
|
1643
|
-
|
|
1644
|
-
this.
|
|
1745
|
+
given(states) {
|
|
1746
|
+
this._given = Array.isArray(states) ? states : [states];
|
|
1645
1747
|
return this;
|
|
1646
1748
|
}
|
|
1647
|
-
|
|
1648
|
-
|
|
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
|
-
|
|
1684
|
-
|
|
1753
|
+
describe(desc) {
|
|
1754
|
+
this._desc = desc;
|
|
1685
1755
|
return this;
|
|
1686
1756
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1757
|
+
required() {
|
|
1758
|
+
this._level = "required";
|
|
1759
|
+
this._finalize();
|
|
1760
|
+
return this.parent;
|
|
1689
1761
|
}
|
|
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
|
-
}
|
|
1762
|
+
optional() {
|
|
1763
|
+
this._level = "optional";
|
|
1764
|
+
this._finalize();
|
|
1765
|
+
return this.parent;
|
|
1726
1766
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
2051
|
-
|
|
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
|
-
|
|
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
|
|
2080
|
+
contract
|
|
2074
2081
|
};
|
|
2075
|
-
if (
|
|
2082
|
+
if (contract.failures.length > 0 && url === "Playwright") {
|
|
2076
2083
|
throw new Error(
|
|
2077
2084
|
`
|
|
2078
|
-
\u274C ${
|
|
2079
|
-
\u2705 ${
|
|
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
|
-
|
|
2161
|
+
createContract,
|
|
2155
2162
|
makeAccordionAccessible,
|
|
2156
2163
|
makeBlockAccessible,
|
|
2157
2164
|
makeCheckboxAccessible,
|