aria-ease 3.0.2 → 4.0.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 +6 -6
- package/bin/cli.cjs +90 -22
- package/bin/cli.js +1 -1
- package/bin/{contractTestRunnerPlaywright-TLQZGKW7.js → contractTestRunnerPlaywright-EZLNNJV5.js} +43 -10
- package/bin/{test-Q7W3WQEA.js → test-45KMD4F4.js} +47 -12
- package/dist/{contractTestRunnerPlaywright-7U2O33SR.js → contractTestRunnerPlaywright-UQQI5MYS.js} +43 -10
- package/dist/index.cjs +663 -38
- package/dist/index.d.cts +88 -6
- package/dist/index.d.ts +88 -6
- package/dist/index.js +616 -28
- package/dist/src/{Types.d-uG0Hm1yK.d.ts → Types.d-BrHSyS03.d.cts} +17 -0
- package/dist/src/{Types.d-uG0Hm1yK.d.cts → Types.d-BrHSyS03.d.ts} +17 -0
- package/dist/src/accordion/index.cjs +159 -0
- package/dist/src/accordion/index.d.cts +19 -2
- package/dist/src/accordion/index.d.ts +19 -2
- package/dist/src/accordion/index.js +159 -1
- package/dist/src/block/index.cjs +1 -1
- package/dist/src/block/index.d.cts +6 -2
- package/dist/src/block/index.d.ts +6 -2
- package/dist/src/block/index.js +1 -1
- package/dist/src/checkbox/index.cjs +129 -0
- package/dist/src/checkbox/index.d.cts +15 -2
- package/dist/src/checkbox/index.d.ts +15 -2
- package/dist/src/checkbox/index.js +129 -1
- package/dist/src/combobox/index.d.cts +1 -1
- package/dist/src/combobox/index.d.ts +1 -1
- package/dist/src/menu/index.cjs +13 -15
- package/dist/src/menu/index.d.cts +1 -1
- package/dist/src/menu/index.d.ts +1 -1
- package/dist/src/menu/index.js +13 -15
- package/dist/src/radio/index.cjs +122 -0
- package/dist/src/radio/index.d.cts +17 -2
- package/dist/src/radio/index.d.ts +17 -2
- package/dist/src/radio/index.js +122 -1
- package/dist/src/toggle/index.cjs +145 -0
- package/dist/src/toggle/index.d.cts +17 -2
- package/dist/src/toggle/index.d.ts +17 -2
- package/dist/src/toggle/index.js +145 -1
- package/dist/src/utils/test/{contractTestRunnerPlaywright-7U2O33SR.js → contractTestRunnerPlaywright-UQQI5MYS.js} +43 -10
- package/dist/src/utils/test/index.cjs +90 -22
- package/dist/src/utils/test/index.d.cts +5 -4
- package/dist/src/utils/test/index.d.ts +5 -4
- package/dist/src/utils/test/index.js +47 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ updateAccordionTriggerAriaAttributes(
|
|
|
172
172
|
"accordion-container", // Container ID
|
|
173
173
|
"accordion-trigger", // Shared class for triggers
|
|
174
174
|
accordionStates, // State array
|
|
175
|
-
0 // Index of trigger that changed
|
|
175
|
+
0, // Index of trigger that changed
|
|
176
176
|
);
|
|
177
177
|
```
|
|
178
178
|
|
|
@@ -223,7 +223,7 @@ function handleCheckboxClick(index) {
|
|
|
223
223
|
"checkbox-group",
|
|
224
224
|
"custom-checkbox",
|
|
225
225
|
checkboxStates,
|
|
226
|
-
index
|
|
226
|
+
index,
|
|
227
227
|
);
|
|
228
228
|
}
|
|
229
229
|
```
|
|
@@ -314,14 +314,14 @@ Aria-Ease includes a built-in testing framework for automated accessibility vali
|
|
|
314
314
|
import { testUiComponent } from "aria-ease/test";
|
|
315
315
|
|
|
316
316
|
// In your test file (Vitest, Jest, etc.)
|
|
317
|
-
test("
|
|
318
|
-
const { container } = render(<
|
|
317
|
+
test("combobox is accessible", async () => {
|
|
318
|
+
const { container } = render(<Combobox />);
|
|
319
319
|
|
|
320
320
|
// Runs axe-core + contract tests
|
|
321
321
|
const result = await testUiComponent(
|
|
322
|
-
"
|
|
322
|
+
"combobox",
|
|
323
323
|
container,
|
|
324
|
-
"http://localhost:3000" // Optional: full E2E with Playwright
|
|
324
|
+
"http://localhost:3000", // Optional: full E2E with Playwright
|
|
325
325
|
);
|
|
326
326
|
|
|
327
327
|
expect(result.violations).toHaveLength(0);
|
package/bin/cli.cjs
CHANGED
|
@@ -13261,7 +13261,7 @@ async function runContractTests(componentName, component) {
|
|
|
13261
13261
|
const staticFailed = 0;
|
|
13262
13262
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
13263
13263
|
reporter.summary(failures);
|
|
13264
|
-
return { passes, failures };
|
|
13264
|
+
return { passes, failures, skipped };
|
|
13265
13265
|
}
|
|
13266
13266
|
var import_promises, import_meta;
|
|
13267
13267
|
var init_contractTestRunner = __esm({
|
|
@@ -13308,6 +13308,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13308
13308
|
reporter.start(componentName, totalTests);
|
|
13309
13309
|
const failures = [];
|
|
13310
13310
|
const passes = [];
|
|
13311
|
+
const skipped = [];
|
|
13311
13312
|
let browser = null;
|
|
13312
13313
|
try {
|
|
13313
13314
|
browser = await import_playwright3.chromium.launch({ headless: true });
|
|
@@ -13315,13 +13316,13 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13315
13316
|
const page = await context.newPage();
|
|
13316
13317
|
await page.goto(url, {
|
|
13317
13318
|
waitUntil: "domcontentloaded",
|
|
13318
|
-
timeout:
|
|
13319
|
+
timeout: 9e4
|
|
13319
13320
|
});
|
|
13320
13321
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
13321
13322
|
if (!mainSelector) {
|
|
13322
13323
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
13323
13324
|
}
|
|
13324
|
-
await page.waitForSelector(mainSelector, { timeout:
|
|
13325
|
+
await page.waitForSelector(mainSelector, { timeout: 9e4 });
|
|
13325
13326
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
13326
13327
|
await page.waitForFunction(
|
|
13327
13328
|
(selector) => {
|
|
@@ -13329,7 +13330,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13329
13330
|
return trigger && trigger.getAttribute("data-menu-initialized") === "true";
|
|
13330
13331
|
},
|
|
13331
13332
|
componentContract.selectors.trigger,
|
|
13332
|
-
{ timeout:
|
|
13333
|
+
{ timeout: 6e3 }
|
|
13333
13334
|
).catch(() => {
|
|
13334
13335
|
console.warn("Menu initialization signal not detected, continuing with tests...");
|
|
13335
13336
|
});
|
|
@@ -13419,11 +13420,43 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13419
13420
|
if (componentContract.selectors.input) {
|
|
13420
13421
|
const inputElement = page.locator(componentContract.selectors.input).first();
|
|
13421
13422
|
await inputElement.clear();
|
|
13422
|
-
await page.waitForTimeout(
|
|
13423
|
+
await page.waitForTimeout(100);
|
|
13424
|
+
}
|
|
13425
|
+
}
|
|
13426
|
+
}
|
|
13427
|
+
}
|
|
13428
|
+
let shouldSkipTest = false;
|
|
13429
|
+
for (const act of action) {
|
|
13430
|
+
if (act.type === "keypress" && (act.target === "submenuTrigger" || act.target === "submenu")) {
|
|
13431
|
+
const submenuSelector = componentContract.selectors[act.target];
|
|
13432
|
+
if (submenuSelector) {
|
|
13433
|
+
const submenuCount = await page.locator(submenuSelector).count();
|
|
13434
|
+
if (submenuCount === 0) {
|
|
13435
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
|
|
13436
|
+
shouldSkipTest = true;
|
|
13437
|
+
break;
|
|
13438
|
+
}
|
|
13439
|
+
}
|
|
13440
|
+
}
|
|
13441
|
+
}
|
|
13442
|
+
if (!shouldSkipTest) {
|
|
13443
|
+
for (const assertion of assertions) {
|
|
13444
|
+
if (assertion.target === "submenu" || assertion.target === "submenuTrigger") {
|
|
13445
|
+
const submenuSelector = componentContract.selectors[assertion.target];
|
|
13446
|
+
if (submenuSelector) {
|
|
13447
|
+
const submenuCount = await page.locator(submenuSelector).count();
|
|
13448
|
+
if (submenuCount === 0) {
|
|
13449
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${assertion.target} element not found (optional submenu test)`);
|
|
13450
|
+
shouldSkipTest = true;
|
|
13451
|
+
break;
|
|
13452
|
+
}
|
|
13423
13453
|
}
|
|
13424
13454
|
}
|
|
13425
13455
|
}
|
|
13426
13456
|
}
|
|
13457
|
+
if (shouldSkipTest) {
|
|
13458
|
+
continue;
|
|
13459
|
+
}
|
|
13427
13460
|
for (const act of action) {
|
|
13428
13461
|
if (act.type === "focus") {
|
|
13429
13462
|
const focusSelector = componentContract.selectors[act.target];
|
|
@@ -13432,7 +13465,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13432
13465
|
continue;
|
|
13433
13466
|
}
|
|
13434
13467
|
await page.locator(focusSelector).first().focus();
|
|
13435
|
-
await page.waitForTimeout(
|
|
13468
|
+
await page.waitForTimeout(100);
|
|
13436
13469
|
}
|
|
13437
13470
|
if (act.type === "type" && act.value) {
|
|
13438
13471
|
const typeSelector = componentContract.selectors[act.target];
|
|
@@ -13441,7 +13474,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13441
13474
|
continue;
|
|
13442
13475
|
}
|
|
13443
13476
|
await page.locator(typeSelector).first().fill(act.value);
|
|
13444
|
-
await page.waitForTimeout(
|
|
13477
|
+
await page.waitForTimeout(100);
|
|
13445
13478
|
}
|
|
13446
13479
|
if (act.type === "click") {
|
|
13447
13480
|
if (act.target === "document") {
|
|
@@ -13491,7 +13524,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13491
13524
|
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
13492
13525
|
await page.waitForTimeout(100);
|
|
13493
13526
|
await page.keyboard.press(keyValue);
|
|
13494
|
-
await page.waitForTimeout(
|
|
13527
|
+
await page.waitForTimeout(100);
|
|
13495
13528
|
} else {
|
|
13496
13529
|
const keypressSelector = componentContract.selectors[act.target];
|
|
13497
13530
|
if (!keypressSelector) {
|
|
@@ -13520,7 +13553,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13520
13553
|
continue;
|
|
13521
13554
|
}
|
|
13522
13555
|
await relativeElement.hover();
|
|
13523
|
-
await page.waitForTimeout(
|
|
13556
|
+
await page.waitForTimeout(100);
|
|
13524
13557
|
} else {
|
|
13525
13558
|
const hoverSelector = componentContract.selectors[act.target];
|
|
13526
13559
|
if (!hoverSelector) {
|
|
@@ -13528,7 +13561,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13528
13561
|
continue;
|
|
13529
13562
|
}
|
|
13530
13563
|
await page.locator(hoverSelector).first().hover();
|
|
13531
|
-
await page.waitForTimeout(
|
|
13564
|
+
await page.waitForTimeout(100);
|
|
13532
13565
|
}
|
|
13533
13566
|
}
|
|
13534
13567
|
await page.waitForTimeout(100);
|
|
@@ -13670,7 +13703,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
13670
13703
|
} finally {
|
|
13671
13704
|
if (browser) await browser.close();
|
|
13672
13705
|
}
|
|
13673
|
-
return { passes, failures };
|
|
13706
|
+
return { passes, failures, skipped };
|
|
13674
13707
|
}
|
|
13675
13708
|
var import_playwright3, import_fs, import_meta2;
|
|
13676
13709
|
var init_contractTestRunnerPlaywright = __esm({
|
|
@@ -13687,15 +13720,49 @@ var init_contractTestRunnerPlaywright = __esm({
|
|
|
13687
13720
|
|
|
13688
13721
|
// src/utils/test/src/test.ts
|
|
13689
13722
|
async function testUiComponent(componentName, component, url) {
|
|
13690
|
-
|
|
13723
|
+
if (!componentName || typeof componentName !== "string") {
|
|
13724
|
+
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
13725
|
+
}
|
|
13726
|
+
if (!component || !(component instanceof HTMLElement)) {
|
|
13727
|
+
throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
|
|
13728
|
+
}
|
|
13729
|
+
if (url && typeof url !== "string") {
|
|
13730
|
+
throw new Error("\u274C testUiComponent url parameter must be a string");
|
|
13731
|
+
}
|
|
13732
|
+
let results;
|
|
13733
|
+
try {
|
|
13734
|
+
results = await (0, import_jest_axe.axe)(component);
|
|
13735
|
+
} catch (error) {
|
|
13736
|
+
throw new Error(
|
|
13737
|
+
`\u274C Axe accessibility scan failed
|
|
13738
|
+
Error: ${error instanceof Error ? error.message : String(error)}`
|
|
13739
|
+
);
|
|
13740
|
+
}
|
|
13691
13741
|
let contract;
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
|
|
13696
|
-
|
|
13697
|
-
|
|
13698
|
-
|
|
13742
|
+
try {
|
|
13743
|
+
if (url) {
|
|
13744
|
+
console.log(`\u{1F3AD} Running Playwright E2E tests on ${url}`);
|
|
13745
|
+
try {
|
|
13746
|
+
new URL(url);
|
|
13747
|
+
} catch {
|
|
13748
|
+
throw new Error(
|
|
13749
|
+
`\u274C Invalid URL format: "${url}"
|
|
13750
|
+
URL must include protocol (e.g., "http://localhost:5173/test")`
|
|
13751
|
+
);
|
|
13752
|
+
}
|
|
13753
|
+
const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
|
|
13754
|
+
contract = await runContractTestsPlaywright2(componentName, url);
|
|
13755
|
+
} else {
|
|
13756
|
+
console.log(`\u{1F9EA} Running jsdom tests (limited event handling)`);
|
|
13757
|
+
console.log(`Some tests may be skipped or yield false positives/negatives.
|
|
13758
|
+
For full coverage, run with a URL to enable Playwright E2E tests.`);
|
|
13759
|
+
contract = await runContractTests(componentName, component);
|
|
13760
|
+
}
|
|
13761
|
+
} catch (error) {
|
|
13762
|
+
if (error instanceof Error) {
|
|
13763
|
+
throw error;
|
|
13764
|
+
}
|
|
13765
|
+
throw new Error(`\u274C Contract test execution failed: ${String(error)}`);
|
|
13699
13766
|
}
|
|
13700
13767
|
const result = {
|
|
13701
13768
|
violations: results.violations,
|
|
@@ -13706,10 +13773,11 @@ async function testUiComponent(componentName, component, url) {
|
|
|
13706
13773
|
const mode = url ? "Playwright" : "jsdom";
|
|
13707
13774
|
throw new Error(
|
|
13708
13775
|
`
|
|
13709
|
-
\u274C ${contract.failures.length}
|
|
13710
|
-
\u2705 ${contract.passes.length}
|
|
13776
|
+
\u274C ${contract.failures.length} accessibility contract test${contract.failures.length > 1 ? "s" : ""} failed (${mode} mode)
|
|
13777
|
+
\u2705 ${contract.passes.length} test${contract.passes.length > 1 ? "s" : ""} passed
|
|
13711
13778
|
|
|
13712
|
-
\u{1F4CB} Review the detailed test report above for specific failures
|
|
13779
|
+
\u{1F4CB} Review the detailed test report above for specific failures.
|
|
13780
|
+
\u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`
|
|
13713
13781
|
);
|
|
13714
13782
|
}
|
|
13715
13783
|
if (results.violations.length > 0) {
|
package/bin/cli.js
CHANGED
|
@@ -204,7 +204,7 @@ program.command("audit").description("Run axe-core powered accessibility audit o
|
|
|
204
204
|
console.log(chalk.green("\n\u{1F389} All audits completed."));
|
|
205
205
|
});
|
|
206
206
|
program.command("test").description("Run core a11y accessibility standard tests on UI components").action(async () => {
|
|
207
|
-
const { runTest } = await import("./test-
|
|
207
|
+
const { runTest } = await import("./test-45KMD4F4.js");
|
|
208
208
|
runTest();
|
|
209
209
|
});
|
|
210
210
|
program.command("help").description("Display help information").action(() => {
|
package/bin/{contractTestRunnerPlaywright-TLQZGKW7.js → contractTestRunnerPlaywright-EZLNNJV5.js}
RENAMED
|
@@ -35,6 +35,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
35
35
|
reporter.start(componentName, totalTests);
|
|
36
36
|
const failures = [];
|
|
37
37
|
const passes = [];
|
|
38
|
+
const skipped = [];
|
|
38
39
|
let browser = null;
|
|
39
40
|
try {
|
|
40
41
|
browser = await chromium.launch({ headless: true });
|
|
@@ -42,13 +43,13 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
42
43
|
const page = await context.newPage();
|
|
43
44
|
await page.goto(url, {
|
|
44
45
|
waitUntil: "domcontentloaded",
|
|
45
|
-
timeout:
|
|
46
|
+
timeout: 9e4
|
|
46
47
|
});
|
|
47
48
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
48
49
|
if (!mainSelector) {
|
|
49
50
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
50
51
|
}
|
|
51
|
-
await page.waitForSelector(mainSelector, { timeout:
|
|
52
|
+
await page.waitForSelector(mainSelector, { timeout: 9e4 });
|
|
52
53
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
53
54
|
await page.waitForFunction(
|
|
54
55
|
(selector) => {
|
|
@@ -56,7 +57,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
56
57
|
return trigger && trigger.getAttribute("data-menu-initialized") === "true";
|
|
57
58
|
},
|
|
58
59
|
componentContract.selectors.trigger,
|
|
59
|
-
{ timeout:
|
|
60
|
+
{ timeout: 6e3 }
|
|
60
61
|
).catch(() => {
|
|
61
62
|
console.warn("Menu initialization signal not detected, continuing with tests...");
|
|
62
63
|
});
|
|
@@ -146,11 +147,43 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
146
147
|
if (componentContract.selectors.input) {
|
|
147
148
|
const inputElement = page.locator(componentContract.selectors.input).first();
|
|
148
149
|
await inputElement.clear();
|
|
149
|
-
await page.waitForTimeout(
|
|
150
|
+
await page.waitForTimeout(100);
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
}
|
|
155
|
+
let shouldSkipTest = false;
|
|
156
|
+
for (const act of action) {
|
|
157
|
+
if (act.type === "keypress" && (act.target === "submenuTrigger" || act.target === "submenu")) {
|
|
158
|
+
const submenuSelector = componentContract.selectors[act.target];
|
|
159
|
+
if (submenuSelector) {
|
|
160
|
+
const submenuCount = await page.locator(submenuSelector).count();
|
|
161
|
+
if (submenuCount === 0) {
|
|
162
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
|
|
163
|
+
shouldSkipTest = true;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (!shouldSkipTest) {
|
|
170
|
+
for (const assertion of assertions) {
|
|
171
|
+
if (assertion.target === "submenu" || assertion.target === "submenuTrigger") {
|
|
172
|
+
const submenuSelector = componentContract.selectors[assertion.target];
|
|
173
|
+
if (submenuSelector) {
|
|
174
|
+
const submenuCount = await page.locator(submenuSelector).count();
|
|
175
|
+
if (submenuCount === 0) {
|
|
176
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${assertion.target} element not found (optional submenu test)`);
|
|
177
|
+
shouldSkipTest = true;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (shouldSkipTest) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
154
187
|
for (const act of action) {
|
|
155
188
|
if (act.type === "focus") {
|
|
156
189
|
const focusSelector = componentContract.selectors[act.target];
|
|
@@ -159,7 +192,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
159
192
|
continue;
|
|
160
193
|
}
|
|
161
194
|
await page.locator(focusSelector).first().focus();
|
|
162
|
-
await page.waitForTimeout(
|
|
195
|
+
await page.waitForTimeout(100);
|
|
163
196
|
}
|
|
164
197
|
if (act.type === "type" && act.value) {
|
|
165
198
|
const typeSelector = componentContract.selectors[act.target];
|
|
@@ -168,7 +201,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
168
201
|
continue;
|
|
169
202
|
}
|
|
170
203
|
await page.locator(typeSelector).first().fill(act.value);
|
|
171
|
-
await page.waitForTimeout(
|
|
204
|
+
await page.waitForTimeout(100);
|
|
172
205
|
}
|
|
173
206
|
if (act.type === "click") {
|
|
174
207
|
if (act.target === "document") {
|
|
@@ -218,7 +251,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
218
251
|
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
219
252
|
await page.waitForTimeout(100);
|
|
220
253
|
await page.keyboard.press(keyValue);
|
|
221
|
-
await page.waitForTimeout(
|
|
254
|
+
await page.waitForTimeout(100);
|
|
222
255
|
} else {
|
|
223
256
|
const keypressSelector = componentContract.selectors[act.target];
|
|
224
257
|
if (!keypressSelector) {
|
|
@@ -247,7 +280,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
247
280
|
continue;
|
|
248
281
|
}
|
|
249
282
|
await relativeElement.hover();
|
|
250
|
-
await page.waitForTimeout(
|
|
283
|
+
await page.waitForTimeout(100);
|
|
251
284
|
} else {
|
|
252
285
|
const hoverSelector = componentContract.selectors[act.target];
|
|
253
286
|
if (!hoverSelector) {
|
|
@@ -255,7 +288,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
255
288
|
continue;
|
|
256
289
|
}
|
|
257
290
|
await page.locator(hoverSelector).first().hover();
|
|
258
|
-
await page.waitForTimeout(
|
|
291
|
+
await page.waitForTimeout(100);
|
|
259
292
|
}
|
|
260
293
|
}
|
|
261
294
|
await page.waitForTimeout(100);
|
|
@@ -397,7 +430,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
397
430
|
} finally {
|
|
398
431
|
if (browser) await browser.close();
|
|
399
432
|
}
|
|
400
|
-
return { passes, failures };
|
|
433
|
+
return { passes, failures, skipped };
|
|
401
434
|
}
|
|
402
435
|
export {
|
|
403
436
|
runContractTestsPlaywright
|
|
@@ -12741,20 +12741,54 @@ async function runContractTests(componentName, component) {
|
|
|
12741
12741
|
const staticFailed = 0;
|
|
12742
12742
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
12743
12743
|
reporter.summary(failures);
|
|
12744
|
-
return { passes, failures };
|
|
12744
|
+
return { passes, failures, skipped };
|
|
12745
12745
|
}
|
|
12746
12746
|
|
|
12747
12747
|
// src/utils/test/src/test.ts
|
|
12748
12748
|
async function testUiComponent(componentName, component, url) {
|
|
12749
|
-
|
|
12749
|
+
if (!componentName || typeof componentName !== "string") {
|
|
12750
|
+
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
12751
|
+
}
|
|
12752
|
+
if (!component || !(component instanceof HTMLElement)) {
|
|
12753
|
+
throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
|
|
12754
|
+
}
|
|
12755
|
+
if (url && typeof url !== "string") {
|
|
12756
|
+
throw new Error("\u274C testUiComponent url parameter must be a string");
|
|
12757
|
+
}
|
|
12758
|
+
let results;
|
|
12759
|
+
try {
|
|
12760
|
+
results = await axe(component);
|
|
12761
|
+
} catch (error) {
|
|
12762
|
+
throw new Error(
|
|
12763
|
+
`\u274C Axe accessibility scan failed
|
|
12764
|
+
Error: ${error instanceof Error ? error.message : String(error)}`
|
|
12765
|
+
);
|
|
12766
|
+
}
|
|
12750
12767
|
let contract;
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
|
|
12768
|
+
try {
|
|
12769
|
+
if (url) {
|
|
12770
|
+
console.log(`\u{1F3AD} Running Playwright E2E tests on ${url}`);
|
|
12771
|
+
try {
|
|
12772
|
+
new URL(url);
|
|
12773
|
+
} catch {
|
|
12774
|
+
throw new Error(
|
|
12775
|
+
`\u274C Invalid URL format: "${url}"
|
|
12776
|
+
URL must include protocol (e.g., "http://localhost:5173/test")`
|
|
12777
|
+
);
|
|
12778
|
+
}
|
|
12779
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-EZLNNJV5.js");
|
|
12780
|
+
contract = await runContractTestsPlaywright(componentName, url);
|
|
12781
|
+
} else {
|
|
12782
|
+
console.log(`\u{1F9EA} Running jsdom tests (limited event handling)`);
|
|
12783
|
+
console.log(`Some tests may be skipped or yield false positives/negatives.
|
|
12784
|
+
For full coverage, run with a URL to enable Playwright E2E tests.`);
|
|
12785
|
+
contract = await runContractTests(componentName, component);
|
|
12786
|
+
}
|
|
12787
|
+
} catch (error) {
|
|
12788
|
+
if (error instanceof Error) {
|
|
12789
|
+
throw error;
|
|
12790
|
+
}
|
|
12791
|
+
throw new Error(`\u274C Contract test execution failed: ${String(error)}`);
|
|
12758
12792
|
}
|
|
12759
12793
|
const result = {
|
|
12760
12794
|
violations: results.violations,
|
|
@@ -12765,10 +12799,11 @@ async function testUiComponent(componentName, component, url) {
|
|
|
12765
12799
|
const mode = url ? "Playwright" : "jsdom";
|
|
12766
12800
|
throw new Error(
|
|
12767
12801
|
`
|
|
12768
|
-
\u274C ${contract.failures.length}
|
|
12769
|
-
\u2705 ${contract.passes.length}
|
|
12802
|
+
\u274C ${contract.failures.length} accessibility contract test${contract.failures.length > 1 ? "s" : ""} failed (${mode} mode)
|
|
12803
|
+
\u2705 ${contract.passes.length} test${contract.passes.length > 1 ? "s" : ""} passed
|
|
12770
12804
|
|
|
12771
|
-
\u{1F4CB} Review the detailed test report above for specific failures
|
|
12805
|
+
\u{1F4CB} Review the detailed test report above for specific failures.
|
|
12806
|
+
\u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`
|
|
12772
12807
|
);
|
|
12773
12808
|
}
|
|
12774
12809
|
if (results.violations.length > 0) {
|
package/dist/{contractTestRunnerPlaywright-7U2O33SR.js → contractTestRunnerPlaywright-UQQI5MYS.js}
RENAMED
|
@@ -33,6 +33,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
33
33
|
reporter.start(componentName, totalTests);
|
|
34
34
|
const failures = [];
|
|
35
35
|
const passes = [];
|
|
36
|
+
const skipped = [];
|
|
36
37
|
let browser = null;
|
|
37
38
|
try {
|
|
38
39
|
browser = await chromium.launch({ headless: true });
|
|
@@ -40,13 +41,13 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
40
41
|
const page = await context.newPage();
|
|
41
42
|
await page.goto(url, {
|
|
42
43
|
waitUntil: "domcontentloaded",
|
|
43
|
-
timeout:
|
|
44
|
+
timeout: 9e4
|
|
44
45
|
});
|
|
45
46
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
46
47
|
if (!mainSelector) {
|
|
47
48
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
48
49
|
}
|
|
49
|
-
await page.waitForSelector(mainSelector, { timeout:
|
|
50
|
+
await page.waitForSelector(mainSelector, { timeout: 9e4 });
|
|
50
51
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
51
52
|
await page.waitForFunction(
|
|
52
53
|
(selector) => {
|
|
@@ -54,7 +55,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
54
55
|
return trigger && trigger.getAttribute("data-menu-initialized") === "true";
|
|
55
56
|
},
|
|
56
57
|
componentContract.selectors.trigger,
|
|
57
|
-
{ timeout:
|
|
58
|
+
{ timeout: 6e3 }
|
|
58
59
|
).catch(() => {
|
|
59
60
|
console.warn("Menu initialization signal not detected, continuing with tests...");
|
|
60
61
|
});
|
|
@@ -144,11 +145,43 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
144
145
|
if (componentContract.selectors.input) {
|
|
145
146
|
const inputElement = page.locator(componentContract.selectors.input).first();
|
|
146
147
|
await inputElement.clear();
|
|
147
|
-
await page.waitForTimeout(
|
|
148
|
+
await page.waitForTimeout(100);
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
}
|
|
151
152
|
}
|
|
153
|
+
let shouldSkipTest = false;
|
|
154
|
+
for (const act of action) {
|
|
155
|
+
if (act.type === "keypress" && (act.target === "submenuTrigger" || act.target === "submenu")) {
|
|
156
|
+
const submenuSelector = componentContract.selectors[act.target];
|
|
157
|
+
if (submenuSelector) {
|
|
158
|
+
const submenuCount = await page.locator(submenuSelector).count();
|
|
159
|
+
if (submenuCount === 0) {
|
|
160
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${act.target} element not found (optional submenu test)`);
|
|
161
|
+
shouldSkipTest = true;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!shouldSkipTest) {
|
|
168
|
+
for (const assertion of assertions) {
|
|
169
|
+
if (assertion.target === "submenu" || assertion.target === "submenuTrigger") {
|
|
170
|
+
const submenuSelector = componentContract.selectors[assertion.target];
|
|
171
|
+
if (submenuSelector) {
|
|
172
|
+
const submenuCount = await page.locator(submenuSelector).count();
|
|
173
|
+
if (submenuCount === 0) {
|
|
174
|
+
reporter.reportTest(dynamicTest, "skip", `Skipping test - ${assertion.target} element not found (optional submenu test)`);
|
|
175
|
+
shouldSkipTest = true;
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (shouldSkipTest) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
152
185
|
for (const act of action) {
|
|
153
186
|
if (act.type === "focus") {
|
|
154
187
|
const focusSelector = componentContract.selectors[act.target];
|
|
@@ -157,7 +190,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
157
190
|
continue;
|
|
158
191
|
}
|
|
159
192
|
await page.locator(focusSelector).first().focus();
|
|
160
|
-
await page.waitForTimeout(
|
|
193
|
+
await page.waitForTimeout(100);
|
|
161
194
|
}
|
|
162
195
|
if (act.type === "type" && act.value) {
|
|
163
196
|
const typeSelector = componentContract.selectors[act.target];
|
|
@@ -166,7 +199,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
166
199
|
continue;
|
|
167
200
|
}
|
|
168
201
|
await page.locator(typeSelector).first().fill(act.value);
|
|
169
|
-
await page.waitForTimeout(
|
|
202
|
+
await page.waitForTimeout(100);
|
|
170
203
|
}
|
|
171
204
|
if (act.type === "click") {
|
|
172
205
|
if (act.target === "document") {
|
|
@@ -216,7 +249,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
216
249
|
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
217
250
|
await page.waitForTimeout(100);
|
|
218
251
|
await page.keyboard.press(keyValue);
|
|
219
|
-
await page.waitForTimeout(
|
|
252
|
+
await page.waitForTimeout(100);
|
|
220
253
|
} else {
|
|
221
254
|
const keypressSelector = componentContract.selectors[act.target];
|
|
222
255
|
if (!keypressSelector) {
|
|
@@ -245,7 +278,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
245
278
|
continue;
|
|
246
279
|
}
|
|
247
280
|
await relativeElement.hover();
|
|
248
|
-
await page.waitForTimeout(
|
|
281
|
+
await page.waitForTimeout(100);
|
|
249
282
|
} else {
|
|
250
283
|
const hoverSelector = componentContract.selectors[act.target];
|
|
251
284
|
if (!hoverSelector) {
|
|
@@ -253,7 +286,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
253
286
|
continue;
|
|
254
287
|
}
|
|
255
288
|
await page.locator(hoverSelector).first().hover();
|
|
256
|
-
await page.waitForTimeout(
|
|
289
|
+
await page.waitForTimeout(100);
|
|
257
290
|
}
|
|
258
291
|
}
|
|
259
292
|
await page.waitForTimeout(100);
|
|
@@ -395,7 +428,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
395
428
|
} finally {
|
|
396
429
|
if (browser) await browser.close();
|
|
397
430
|
}
|
|
398
|
-
return { passes, failures };
|
|
431
|
+
return { passes, failures, skipped };
|
|
399
432
|
}
|
|
400
433
|
export {
|
|
401
434
|
runContractTestsPlaywright
|