aria-ease 6.12.1 → 6.14.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/dist/{MenuComponentStrategy-JAMTCSNF.js → MenuComponentStrategy-VYCC2XOM.js} +5 -5
- package/dist/{buildContracts-FT6KWUJN.js → buildContracts-VIV6GM56.js} +0 -28
- package/dist/cli.cjs +154 -78
- package/dist/cli.js +2 -2
- package/dist/{contractTestRunnerPlaywright-H24LQ45R.js → contractTestRunnerPlaywright-B2HLZKKK.js} +150 -46
- package/dist/{contractTestRunnerPlaywright-NL3JNJYH.js → contractTestRunnerPlaywright-RWK52C7S.js} +150 -46
- package/dist/index.cjs +564 -85
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +411 -36
- package/dist/src/combobox/index.cjs +3 -0
- package/dist/src/combobox/index.js +3 -0
- package/dist/src/utils/test/{MenuComponentStrategy-VKZQYLBE.js → MenuComponentStrategy-6XWU5KLW.js} +5 -5
- package/dist/src/utils/test/{contractTestRunnerPlaywright-5FT6K2WN.js → contractTestRunnerPlaywright-5FIGA5G4.js} +150 -46
- package/dist/src/utils/test/dsl/index.cjs +407 -35
- package/dist/src/utils/test/dsl/index.d.cts +24 -1
- package/dist/src/utils/test/dsl/index.d.ts +24 -1
- package/dist/src/utils/test/dsl/index.js +407 -35
- package/dist/src/utils/test/index.cjs +154 -50
- package/dist/src/utils/test/index.js +1 -1
- package/dist/{test-FYSJXQWO.js → test-WDBS5JWO.js} +1 -1
- package/package.json +1 -1
|
@@ -585,7 +585,7 @@ var init_MenuComponentStrategy = __esm({
|
|
|
585
585
|
if (!closeSelector && this.selectors.focusable) {
|
|
586
586
|
closeSelector = this.selectors.focusable;
|
|
587
587
|
} else if (!closeSelector) {
|
|
588
|
-
closeSelector = this.selectors.
|
|
588
|
+
closeSelector = this.selectors.main;
|
|
589
589
|
}
|
|
590
590
|
if (closeSelector) {
|
|
591
591
|
const closeElement = page.locator(closeSelector).first();
|
|
@@ -593,8 +593,8 @@ var init_MenuComponentStrategy = __esm({
|
|
|
593
593
|
await page.keyboard.press("Escape");
|
|
594
594
|
menuClosed = await test.expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
595
595
|
}
|
|
596
|
-
if (!menuClosed && this.selectors.
|
|
597
|
-
const triggerElement = page.locator(this.selectors.
|
|
596
|
+
if (!menuClosed && this.selectors.main) {
|
|
597
|
+
const triggerElement = page.locator(this.selectors.main).first();
|
|
598
598
|
await triggerElement.click({ timeout: this.actionTimeoutMs });
|
|
599
599
|
menuClosed = await test.expect(popupElement).toBeHidden({ timeout: this.assertionTimeoutMs }).then(() => true).catch(() => false);
|
|
600
600
|
}
|
|
@@ -614,8 +614,8 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
614
614
|
if (this.selectors.input) {
|
|
615
615
|
await page.locator(this.selectors.input).first().clear();
|
|
616
616
|
}
|
|
617
|
-
if (this.selectors.
|
|
618
|
-
const triggerElement = page.locator(this.selectors.
|
|
617
|
+
if (this.selectors.main) {
|
|
618
|
+
const triggerElement = page.locator(this.selectors.main).first();
|
|
619
619
|
await triggerElement.focus();
|
|
620
620
|
}
|
|
621
621
|
}
|
|
@@ -1385,13 +1385,21 @@ var init_AssertionRunner = __esm({
|
|
|
1385
1385
|
/**
|
|
1386
1386
|
* Validate focus assertion
|
|
1387
1387
|
*/
|
|
1388
|
-
async validateFocus(target, targetName, failureMessage, testDescription) {
|
|
1388
|
+
async validateFocus(target, targetName, expectedFocus, failureMessage, testDescription) {
|
|
1389
1389
|
try {
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1390
|
+
if (expectedFocus) {
|
|
1391
|
+
await test.expect(target).toBeFocused({ timeout: this.timeoutMs });
|
|
1392
|
+
return {
|
|
1393
|
+
success: true,
|
|
1394
|
+
passMessage: `${targetName} has focus as expected. Test: "${testDescription}".`
|
|
1395
|
+
};
|
|
1396
|
+
} else {
|
|
1397
|
+
await test.expect(target).not.toBeFocused({ timeout: this.timeoutMs });
|
|
1398
|
+
return {
|
|
1399
|
+
success: true,
|
|
1400
|
+
passMessage: `${targetName} does not have focus as expected. Test: "${testDescription}".`
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1395
1403
|
} catch {
|
|
1396
1404
|
const actualFocus = await this.page.evaluate(() => {
|
|
1397
1405
|
const focused = document.activeElement;
|
|
@@ -1477,7 +1485,9 @@ var init_AssertionRunner = __esm({
|
|
|
1477
1485
|
}
|
|
1478
1486
|
return { success: false, failMessage: "Missing expectedValue for toHaveValue assertion" };
|
|
1479
1487
|
case "toHaveFocus":
|
|
1480
|
-
return this.validateFocus(target, assertion.target, assertion.failureMessage || "", testDescription);
|
|
1488
|
+
return this.validateFocus(target, assertion.target, true, assertion.failureMessage || "", testDescription);
|
|
1489
|
+
case "notToHaveFocus":
|
|
1490
|
+
return this.validateFocus(target, assertion.target, false, assertion.failureMessage || "", testDescription);
|
|
1481
1491
|
case "toHaveRole":
|
|
1482
1492
|
if (assertion.expectedValue !== void 0) {
|
|
1483
1493
|
return this.validateRole(target, assertion.target, assertion.expectedValue, assertion.failureMessage || "", testDescription);
|
|
@@ -1500,12 +1510,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
|
|
|
1500
1510
|
const componentConfig = config?.test?.components?.find((c) => c.name === componentName);
|
|
1501
1511
|
const isCustomContract = !!componentConfig?.contractPath;
|
|
1502
1512
|
const reporter = new ContractReporter(true, isCustomContract);
|
|
1503
|
-
const defaultTimeouts = {
|
|
1504
|
-
actionTimeoutMs: 400,
|
|
1505
|
-
assertionTimeoutMs: 400,
|
|
1506
|
-
navigationTimeoutMs: 3e4,
|
|
1507
|
-
componentReadyTimeoutMs: 5e3
|
|
1508
|
-
};
|
|
1513
|
+
const defaultTimeouts = { actionTimeoutMs: 400, assertionTimeoutMs: 400, navigationTimeoutMs: 3e4, componentReadyTimeoutMs: 5e3 };
|
|
1509
1514
|
const globalDisableTimeouts = config?.test?.disableTimeouts === true;
|
|
1510
1515
|
const componentDisableTimeouts = componentConfig?.disableTimeouts === true;
|
|
1511
1516
|
const disableTimeouts = componentDisableTimeouts || globalDisableTimeouts;
|
|
@@ -1517,11 +1522,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
|
|
|
1517
1522
|
}
|
|
1518
1523
|
return value;
|
|
1519
1524
|
};
|
|
1520
|
-
const actionTimeoutMs = resolveTimeout(
|
|
1521
|
-
componentConfig?.actionTimeoutMs,
|
|
1522
|
-
config?.test?.actionTimeoutMs,
|
|
1523
|
-
defaultTimeouts.actionTimeoutMs
|
|
1524
|
-
);
|
|
1525
|
+
const actionTimeoutMs = resolveTimeout(componentConfig?.actionTimeoutMs, config?.test?.actionTimeoutMs, defaultTimeouts.actionTimeoutMs);
|
|
1525
1526
|
const assertionTimeoutMs = resolveTimeout(
|
|
1526
1527
|
componentConfig?.assertionTimeoutMs,
|
|
1527
1528
|
config?.test?.assertionTimeoutMs,
|
|
@@ -1621,8 +1622,8 @@ This usually means:
|
|
|
1621
1622
|
);
|
|
1622
1623
|
}
|
|
1623
1624
|
reporter.start(componentName, totalTests, apgUrl);
|
|
1624
|
-
if (componentName === "menu" && componentContract.selectors.
|
|
1625
|
-
await page.locator(componentContract.selectors.
|
|
1625
|
+
if (componentName === "menu" && componentContract.selectors.main) {
|
|
1626
|
+
await page.locator(componentContract.selectors.main).first().waitFor({ state: "visible", timeout: componentReadyTimeoutMs }).catch(() => {
|
|
1626
1627
|
});
|
|
1627
1628
|
}
|
|
1628
1629
|
const hasSubmenuCapability = componentName === "menu" && !!componentContract.selectors.submenuTrigger ? await page.locator(componentContract.selectors.submenuTrigger).count() > 0 : false;
|
|
@@ -1631,7 +1632,42 @@ This usually means:
|
|
|
1631
1632
|
let staticFailed = 0;
|
|
1632
1633
|
let staticWarnings = 0;
|
|
1633
1634
|
for (const rel of componentContract.relationships || []) {
|
|
1635
|
+
if (strategy && typeof strategy.resetState === "function") {
|
|
1636
|
+
try {
|
|
1637
|
+
await strategy.resetState(page);
|
|
1638
|
+
} catch (err) {
|
|
1639
|
+
warnings.push(`Warning: resetState failed before relationship test: ${err instanceof Error ? err.message : String(err)}`);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1634
1642
|
const relationshipLevel = normalizeLevel(rel.level);
|
|
1643
|
+
if (Array.isArray(rel.setup) && rel.setup.length > 0) {
|
|
1644
|
+
let isAllowedType2 = function(t) {
|
|
1645
|
+
return allowedTypes.includes(t);
|
|
1646
|
+
};
|
|
1647
|
+
var isAllowedType = isAllowedType2;
|
|
1648
|
+
const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
|
|
1649
|
+
const relDescription = rel.type === "aria-reference" ? `${rel.from}.${rel.attribute} references ${rel.to}` : `${rel.parent} contains ${rel.child}`;
|
|
1650
|
+
const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
|
|
1651
|
+
const toSetupAction = (a) => ({
|
|
1652
|
+
...a,
|
|
1653
|
+
type: isAllowedType2(a.type) ? a.type : "click"
|
|
1654
|
+
});
|
|
1655
|
+
const setupActions = rel.setup.map(toSetupAction);
|
|
1656
|
+
const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, relDescription, ["submenu", "submenuTrigger", "submenuItems"]);
|
|
1657
|
+
if (setupResult.skip) {
|
|
1658
|
+
skipped.push(setupResult.message || "Setup action skipped");
|
|
1659
|
+
reporter.reportStaticTest(relDescription, "skip", setupResult.message, relationshipLevel);
|
|
1660
|
+
continue;
|
|
1661
|
+
}
|
|
1662
|
+
if (!setupResult.success) {
|
|
1663
|
+
const failure = `Relationship setup failed: ${setupResult.error}`;
|
|
1664
|
+
const outcome = classifyFailure(failure, rel.level);
|
|
1665
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
1666
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
1667
|
+
reporter.reportStaticTest(relDescription, outcome.status, outcome.detail, outcome.level);
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1635
1671
|
if (componentName === "menu" && !hasSubmenuCapability) {
|
|
1636
1672
|
const involvesSubmenu = isSubmenuRelation(rel);
|
|
1637
1673
|
if (involvesSubmenu) {
|
|
@@ -1804,7 +1840,53 @@ This usually means:
|
|
|
1804
1840
|
}
|
|
1805
1841
|
}
|
|
1806
1842
|
const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
1843
|
+
async function runSetupActions(setup, actionExecutor, strategy2, page2, description, skipKeywords = []) {
|
|
1844
|
+
if (!Array.isArray(setup) || setup.length === 0) return { success: true };
|
|
1845
|
+
if (strategy2 && typeof strategy2.resetState === "function") {
|
|
1846
|
+
await strategy2.resetState(page2);
|
|
1847
|
+
}
|
|
1848
|
+
for (const setupAct of setup) {
|
|
1849
|
+
let setupResult = { success: true };
|
|
1850
|
+
try {
|
|
1851
|
+
if (setupAct.type === "focus") {
|
|
1852
|
+
if (setupAct.target === "relative" && setupAct.relativeTarget) {
|
|
1853
|
+
setupResult = await actionExecutor.focus("relative", setupAct.relativeTarget);
|
|
1854
|
+
} else {
|
|
1855
|
+
setupResult = await actionExecutor.focus(setupAct.target);
|
|
1856
|
+
}
|
|
1857
|
+
} else if (setupAct.type === "type" && setupAct.value) {
|
|
1858
|
+
setupResult = await actionExecutor.type(setupAct.target, setupAct.value);
|
|
1859
|
+
} else if (setupAct.type === "click") {
|
|
1860
|
+
setupResult = await actionExecutor.click(setupAct.target, setupAct.relativeTarget);
|
|
1861
|
+
} else if (setupAct.type === "keypress" && setupAct.key) {
|
|
1862
|
+
setupResult = await actionExecutor.keypress(setupAct.target, setupAct.key);
|
|
1863
|
+
} else if (setupAct.type === "hover") {
|
|
1864
|
+
setupResult = await actionExecutor.hover(setupAct.target, setupAct.relativeTarget);
|
|
1865
|
+
} else {
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
} catch (err) {
|
|
1869
|
+
setupResult = { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
1870
|
+
}
|
|
1871
|
+
if (!setupResult.success) {
|
|
1872
|
+
const setupMsg = setupResult.error || "Setup action failed";
|
|
1873
|
+
const isSkip = skipKeywords.some((kw) => description.includes(kw) || setupMsg.includes(kw));
|
|
1874
|
+
if (isSkip) {
|
|
1875
|
+
return { success: false, skip: true, message: `Skipping test - capability not present: ${setupMsg}` };
|
|
1876
|
+
}
|
|
1877
|
+
return { success: false, error: setupMsg };
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return { success: true };
|
|
1881
|
+
}
|
|
1807
1882
|
for (const test of componentContract.static[0]?.assertions || []) {
|
|
1883
|
+
if (strategy && typeof strategy.resetState === "function") {
|
|
1884
|
+
try {
|
|
1885
|
+
await strategy.resetState(page);
|
|
1886
|
+
} catch (err) {
|
|
1887
|
+
warnings.push(`Warning: resetState failed before static test: ${err instanceof Error ? err.message : String(err)}`);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1808
1890
|
if (test.target === "relative") continue;
|
|
1809
1891
|
const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
|
|
1810
1892
|
const staticLevel = normalizeLevel(test.level);
|
|
@@ -1814,6 +1896,33 @@ This usually means:
|
|
|
1814
1896
|
reporter.reportStaticTest(staticDescription, "skip", skipMessage, staticLevel);
|
|
1815
1897
|
continue;
|
|
1816
1898
|
}
|
|
1899
|
+
if (Array.isArray(test.setup) && test.setup.length > 0) {
|
|
1900
|
+
let isAllowedType2 = function(t) {
|
|
1901
|
+
return allowedTypes.includes(t);
|
|
1902
|
+
};
|
|
1903
|
+
var isAllowedType = isAllowedType2;
|
|
1904
|
+
const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
|
|
1905
|
+
const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
|
|
1906
|
+
const toSetupAction = (a) => ({
|
|
1907
|
+
...a,
|
|
1908
|
+
type: isAllowedType2(a.type) ? a.type : "click"
|
|
1909
|
+
});
|
|
1910
|
+
const setupActions = test.setup.map(toSetupAction);
|
|
1911
|
+
const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, staticDescription, ["submenu", "submenuTrigger", "submenuItems"]);
|
|
1912
|
+
if (setupResult.skip) {
|
|
1913
|
+
skipped.push(setupResult.message || "Setup action skipped");
|
|
1914
|
+
reporter.reportStaticTest(staticDescription, "skip", setupResult.message, staticLevel);
|
|
1915
|
+
continue;
|
|
1916
|
+
}
|
|
1917
|
+
if (!setupResult.success) {
|
|
1918
|
+
const failure = `Static setup failed: ${setupResult.error}`;
|
|
1919
|
+
const outcome = classifyFailure(failure, test.level);
|
|
1920
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
1921
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
1922
|
+
reporter.reportStaticTest(staticDescription, outcome.status, outcome.detail, outcome.level);
|
|
1923
|
+
continue;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1817
1926
|
const targetSelector = componentContract.selectors[test.target];
|
|
1818
1927
|
if (!targetSelector) {
|
|
1819
1928
|
const failure = `Selector for target ${test.target} not found.`;
|
|
@@ -1940,31 +2049,26 @@ This usually means:
|
|
|
1940
2049
|
const dynamicLevel = normalizeLevel(dynamicTest.level);
|
|
1941
2050
|
const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
|
|
1942
2051
|
if (Array.isArray(setup) && setup.length > 0) {
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
}
|
|
1962
|
-
|
|
1963
|
-
const setupMsg = setupResult.error || "Setup action failed";
|
|
1964
|
-
const outcome = classifyFailure(`Setup failed: ${setupMsg}`, dynamicTest.level);
|
|
1965
|
-
reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, outcome.status, outcome.detail);
|
|
1966
|
-
continue;
|
|
1967
|
-
}
|
|
2052
|
+
let isAllowedType2 = function(t) {
|
|
2053
|
+
return allowedTypes.includes(t);
|
|
2054
|
+
};
|
|
2055
|
+
var isAllowedType = isAllowedType2;
|
|
2056
|
+
const allowedTypes = ["focus", "type", "click", "keypress", "hover"];
|
|
2057
|
+
const toSetupAction = (a) => ({
|
|
2058
|
+
...a,
|
|
2059
|
+
type: isAllowedType2(a.type) ? a.type : "click"
|
|
2060
|
+
});
|
|
2061
|
+
const setupActions = setup.map(toSetupAction);
|
|
2062
|
+
const setupResult = await runSetupActions(setupActions, actionExecutor, strategy, page, dynamicTest.description, ["submenu", "submenuTrigger", "submenuItems"]);
|
|
2063
|
+
if (setupResult.skip) {
|
|
2064
|
+
skipped.push(setupResult.message || "Setup action skipped");
|
|
2065
|
+
reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, "skip", setupResult.message);
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
if (!setupResult.success) {
|
|
2069
|
+
const outcome = classifyFailure(`Setup failed: ${setupResult.error}`, dynamicTest.level);
|
|
2070
|
+
reporter.reportTest({ description: dynamicTest.description, level: dynamicLevel }, outcome.status, outcome.detail);
|
|
2071
|
+
continue;
|
|
1968
2072
|
}
|
|
1969
2073
|
}
|
|
1970
2074
|
const failuresBeforeTest = failures.length;
|
|
@@ -228,7 +228,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
228
228
|
const devServerUrl = await checkDevServer(url);
|
|
229
229
|
if (devServerUrl) {
|
|
230
230
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
231
|
-
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-
|
|
231
|
+
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-5FIGA5G4.js');
|
|
232
232
|
contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
|
|
233
233
|
} else {
|
|
234
234
|
throw new Error(
|
|
@@ -240,7 +240,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
240
240
|
const devServerUrl = await checkDevServer(url);
|
|
241
241
|
if (devServerUrl) {
|
|
242
242
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
243
|
-
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-
|
|
243
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-B2HLZKKK.js");
|
|
244
244
|
contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness, config, configBaseDir);
|
|
245
245
|
} else {
|
|
246
246
|
throw new Error(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aria-ease",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.14.0",
|
|
4
4
|
"description": "Accessibility infrastructure for the entire frontend engineering lifecycle. Build accessible patterns, run automated audits, verify component interactions, and gate deployments — all in one system.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"type": "module",
|