aria-ease 5.0.3 → 6.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 +21 -11
- package/{dist/src/utils/test/chunk-TUWQNVQJ.js → bin/chunk-7RMRFSJL.js} +51 -2
- package/bin/cli.cjs +125 -61
- package/bin/cli.js +1 -1
- package/bin/{contractTestRunnerPlaywright-O22AQ4RK.js → contractTestRunnerPlaywright-PMSOP5FY.js} +32 -27
- package/bin/{test-JNQFZBJA.js → test-X6K2LCMO.js} +44 -37
- package/dist/{chunk-KJ33RDSC.js → chunk-PDZQOXUN.js} +48 -2
- package/dist/{contractTestRunnerPlaywright-7ZOM7ZMG.js → contractTestRunnerPlaywright-ZO6GM4TU.js} +32 -27
- package/dist/index.cjs +126 -61
- package/dist/index.d.cts +8 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +44 -37
- package/{bin/chunk-TUWQNVQJ.js → dist/src/utils/test/chunk-7RMRFSJL.js} +45 -5
- package/dist/src/utils/test/{contractTestRunnerPlaywright-P5QZAIDR.js → contractTestRunnerPlaywright-CREJNINL.js} +30 -24
- package/dist/src/utils/test/contracts/ComboboxContract.json +2 -1
- package/dist/src/utils/test/contracts/MenuContract.json +2 -1
- package/dist/src/utils/test/index.cjs +120 -59
- package/dist/src/utils/test/index.d.cts +8 -3
- package/dist/src/utils/test/index.d.ts +8 -3
- package/dist/src/utils/test/index.js +43 -38
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -428,20 +428,30 @@ blockInstance.current.cleanup();
|
|
|
428
428
|
Aria-Ease includes a built-in testing framework for automated accessibility validation:
|
|
429
429
|
|
|
430
430
|
```javascript
|
|
431
|
-
import {
|
|
431
|
+
import { describe, test, afterAll } from "vitest";
|
|
432
|
+
import { testUiComponent, cleanupTests } from "aria-ease/test";
|
|
433
|
+
import { render } from "@testing-library/react";
|
|
434
|
+
import ShopifyUserMenu from "../src/components/menus/ShopifyUserMenu";
|
|
432
435
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
+
afterAll(async () => {
|
|
437
|
+
await cleanupTests();
|
|
438
|
+
});
|
|
436
439
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
440
|
+
describe("Shopify User Menu Accessibility Test", () => {
|
|
441
|
+
test("renders Shopify user menu without accessibility violation(s)", async () => {
|
|
442
|
+
await testUiComponent(
|
|
443
|
+
"menu",
|
|
444
|
+
null,
|
|
445
|
+
"http://localhost:5173/test-harness?component=menu",
|
|
446
|
+
); // For full component interaction test. Uses Playwright to test interaction and behaviors
|
|
447
|
+
}, 6000);
|
|
448
|
+
});
|
|
443
449
|
|
|
444
|
-
|
|
450
|
+
describe("Shopify User Menu Accessibility Test", () => {
|
|
451
|
+
test("renders Shopify user menu without accessibility violation(s)", async () => {
|
|
452
|
+
const { container } = render(<ShopifyUserMenu />);
|
|
453
|
+
await testUiComponent("menu", container, null); // For fast limited static tests. Doesn't test for interaction and behaviors
|
|
454
|
+
});
|
|
445
455
|
});
|
|
446
456
|
```
|
|
447
457
|
|
|
@@ -186,7 +186,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
186
186
|
} else if (totalFailures === 0) {
|
|
187
187
|
this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
|
|
188
188
|
if (this.skipped > 0) {
|
|
189
|
-
this.log(`\u25CB ${this.skipped} tests skipped
|
|
189
|
+
this.log(`\u25CB ${this.skipped} tests skipped`);
|
|
190
190
|
}
|
|
191
191
|
if (this.optionalSuggestions > 0) {
|
|
192
192
|
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
@@ -235,4 +235,53 @@ ${"\u2550".repeat(60)}`);
|
|
|
235
235
|
}
|
|
236
236
|
};
|
|
237
237
|
|
|
238
|
-
|
|
238
|
+
// src/utils/test/contract/playwrightTestHarness.ts
|
|
239
|
+
import { chromium } from "playwright";
|
|
240
|
+
var sharedBrowser = null;
|
|
241
|
+
var sharedContext = null;
|
|
242
|
+
async function getOrCreateBrowser() {
|
|
243
|
+
if (!sharedBrowser) {
|
|
244
|
+
sharedBrowser = await chromium.launch({
|
|
245
|
+
headless: true,
|
|
246
|
+
// Launch with clean browser profile - no extensions, no user data
|
|
247
|
+
args: [
|
|
248
|
+
"--disable-extensions",
|
|
249
|
+
"--disable-blink-features=AutomationControlled"
|
|
250
|
+
]
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return sharedBrowser;
|
|
254
|
+
}
|
|
255
|
+
async function getOrCreateContext() {
|
|
256
|
+
if (!sharedContext) {
|
|
257
|
+
const browser = await getOrCreateBrowser();
|
|
258
|
+
sharedContext = await browser.newContext({
|
|
259
|
+
// Isolated context - no permissions, no geolocation, etc.
|
|
260
|
+
permissions: [],
|
|
261
|
+
// Ignore HTTPS errors for local dev servers
|
|
262
|
+
ignoreHTTPSErrors: true
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return sharedContext;
|
|
266
|
+
}
|
|
267
|
+
async function createTestPage() {
|
|
268
|
+
const context = await getOrCreateContext();
|
|
269
|
+
return await context.newPage();
|
|
270
|
+
}
|
|
271
|
+
async function closeSharedBrowser() {
|
|
272
|
+
if (sharedContext) {
|
|
273
|
+
await sharedContext.close();
|
|
274
|
+
sharedContext = null;
|
|
275
|
+
}
|
|
276
|
+
if (sharedBrowser) {
|
|
277
|
+
await sharedBrowser.close();
|
|
278
|
+
sharedBrowser = null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export {
|
|
283
|
+
contract_default,
|
|
284
|
+
ContractReporter,
|
|
285
|
+
createTestPage,
|
|
286
|
+
closeSharedBrowser
|
|
287
|
+
};
|
package/bin/cli.cjs
CHANGED
|
@@ -468,7 +468,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
468
468
|
} else if (totalFailures === 0) {
|
|
469
469
|
this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
|
|
470
470
|
if (this.skipped > 0) {
|
|
471
|
-
this.log(`\u25CB ${this.skipped} tests skipped
|
|
471
|
+
this.log(`\u25CB ${this.skipped} tests skipped`);
|
|
472
472
|
}
|
|
473
473
|
if (this.optionalSuggestions > 0) {
|
|
474
474
|
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
@@ -590,6 +590,56 @@ var init_contractTestRunner = __esm({
|
|
|
590
590
|
}
|
|
591
591
|
});
|
|
592
592
|
|
|
593
|
+
// src/utils/test/contract/playwrightTestHarness.ts
|
|
594
|
+
async function getOrCreateBrowser() {
|
|
595
|
+
if (!sharedBrowser) {
|
|
596
|
+
sharedBrowser = await import_playwright3.chromium.launch({
|
|
597
|
+
headless: true,
|
|
598
|
+
// Launch with clean browser profile - no extensions, no user data
|
|
599
|
+
args: [
|
|
600
|
+
"--disable-extensions",
|
|
601
|
+
"--disable-blink-features=AutomationControlled"
|
|
602
|
+
]
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
return sharedBrowser;
|
|
606
|
+
}
|
|
607
|
+
async function getOrCreateContext() {
|
|
608
|
+
if (!sharedContext) {
|
|
609
|
+
const browser = await getOrCreateBrowser();
|
|
610
|
+
sharedContext = await browser.newContext({
|
|
611
|
+
// Isolated context - no permissions, no geolocation, etc.
|
|
612
|
+
permissions: [],
|
|
613
|
+
// Ignore HTTPS errors for local dev servers
|
|
614
|
+
ignoreHTTPSErrors: true
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
return sharedContext;
|
|
618
|
+
}
|
|
619
|
+
async function createTestPage() {
|
|
620
|
+
const context = await getOrCreateContext();
|
|
621
|
+
return await context.newPage();
|
|
622
|
+
}
|
|
623
|
+
async function closeSharedBrowser() {
|
|
624
|
+
if (sharedContext) {
|
|
625
|
+
await sharedContext.close();
|
|
626
|
+
sharedContext = null;
|
|
627
|
+
}
|
|
628
|
+
if (sharedBrowser) {
|
|
629
|
+
await sharedBrowser.close();
|
|
630
|
+
sharedBrowser = null;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
var import_playwright3, sharedBrowser, sharedContext;
|
|
634
|
+
var init_playwrightTestHarness = __esm({
|
|
635
|
+
"src/utils/test/contract/playwrightTestHarness.ts"() {
|
|
636
|
+
"use strict";
|
|
637
|
+
import_playwright3 = require("playwright");
|
|
638
|
+
sharedBrowser = null;
|
|
639
|
+
sharedContext = null;
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
593
643
|
// node_modules/@playwright/test/index.mjs
|
|
594
644
|
var test_exports = {};
|
|
595
645
|
__export(test_exports, {
|
|
@@ -625,30 +675,35 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
625
675
|
const failures = [];
|
|
626
676
|
const passes = [];
|
|
627
677
|
const skipped = [];
|
|
628
|
-
let
|
|
678
|
+
let page = null;
|
|
679
|
+
const useNavigation = !!url;
|
|
629
680
|
try {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
681
|
+
page = await createTestPage();
|
|
682
|
+
if (url) {
|
|
683
|
+
await page.goto(url, {
|
|
684
|
+
waitUntil: "domcontentloaded",
|
|
685
|
+
timeout: 3e4
|
|
686
|
+
});
|
|
687
|
+
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
688
|
+
}
|
|
638
689
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
639
690
|
if (!mainSelector) {
|
|
640
691
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
641
692
|
}
|
|
642
|
-
|
|
693
|
+
const elementTimeout = useNavigation ? 3e4 : 5e3;
|
|
694
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: elementTimeout });
|
|
643
695
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
644
696
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
645
697
|
state: "visible",
|
|
646
|
-
timeout:
|
|
698
|
+
timeout: 5e3
|
|
647
699
|
}).catch(() => {
|
|
648
700
|
console.warn("Menu trigger not visible, continuing with tests...");
|
|
649
701
|
});
|
|
650
702
|
}
|
|
651
703
|
async function resolveRelativeTarget(selector, relative) {
|
|
704
|
+
if (!page) {
|
|
705
|
+
throw new Error("Page is not initialized");
|
|
706
|
+
}
|
|
652
707
|
const items = await page.locator(selector).all();
|
|
653
708
|
switch (relative) {
|
|
654
709
|
case "first":
|
|
@@ -718,8 +773,8 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
718
773
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
719
774
|
const { action, assertions } = dynamicTest;
|
|
720
775
|
const failuresBeforeTest = failures.length;
|
|
721
|
-
if (componentContract.selectors.
|
|
722
|
-
const popupSelector = componentContract.selectors.
|
|
776
|
+
if (componentContract.selectors.popup) {
|
|
777
|
+
const popupSelector = componentContract.selectors.popup;
|
|
723
778
|
if (!popupSelector) continue;
|
|
724
779
|
const popupElement = page.locator(popupSelector).first();
|
|
725
780
|
const isPopupVisible = await popupElement.isVisible().catch(() => false);
|
|
@@ -735,25 +790,27 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
735
790
|
const closeElement = page.locator(closeSelector).first();
|
|
736
791
|
await closeElement.focus();
|
|
737
792
|
await page.keyboard.press("Escape");
|
|
738
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
793
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
739
794
|
}
|
|
740
795
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
741
796
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
742
797
|
await triggerElement.click();
|
|
743
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
798
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
744
799
|
}
|
|
745
800
|
if (!menuClosed) {
|
|
746
801
|
await page.mouse.click(10, 10);
|
|
747
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
802
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
748
803
|
}
|
|
749
804
|
if (!menuClosed) {
|
|
750
|
-
|
|
751
|
-
|
|
805
|
+
if (useNavigation) {
|
|
806
|
+
throw new Error(
|
|
807
|
+
`\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
|
|
752
808
|
1. Escape key
|
|
753
809
|
2. Clicking trigger
|
|
754
810
|
3. Clicking outside
|
|
755
811
|
This indicates a problem with the menu component's close functionality.`
|
|
756
|
-
|
|
812
|
+
);
|
|
813
|
+
}
|
|
757
814
|
}
|
|
758
815
|
if (componentContract.selectors.input) {
|
|
759
816
|
await page.locator(componentContract.selectors.input).first().clear();
|
|
@@ -924,7 +981,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
924
981
|
}
|
|
925
982
|
if (assertion.assertion === "toBeVisible") {
|
|
926
983
|
try {
|
|
927
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
984
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
|
|
928
985
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
929
986
|
} catch {
|
|
930
987
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -938,7 +995,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
938
995
|
}
|
|
939
996
|
if (assertion.assertion === "notToBeVisible") {
|
|
940
997
|
try {
|
|
941
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
998
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
|
|
942
999
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
943
1000
|
} catch {
|
|
944
1001
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -1040,19 +1097,19 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
1040
1097
|
}
|
|
1041
1098
|
}
|
|
1042
1099
|
} finally {
|
|
1043
|
-
if (
|
|
1100
|
+
if (page) await page.close();
|
|
1044
1101
|
}
|
|
1045
1102
|
return { passes, failures, skipped };
|
|
1046
1103
|
}
|
|
1047
|
-
var
|
|
1104
|
+
var import_fs, import_meta2;
|
|
1048
1105
|
var init_contractTestRunnerPlaywright = __esm({
|
|
1049
1106
|
"src/utils/test/contract/contractTestRunnerPlaywright.ts"() {
|
|
1050
1107
|
"use strict";
|
|
1051
|
-
import_playwright3 = require("playwright");
|
|
1052
1108
|
init_test();
|
|
1053
1109
|
import_fs = require("fs");
|
|
1054
1110
|
init_contract();
|
|
1055
1111
|
init_ContractReporter();
|
|
1112
|
+
init_playwrightTestHarness();
|
|
1056
1113
|
import_meta2 = {};
|
|
1057
1114
|
}
|
|
1058
1115
|
});
|
|
@@ -1062,56 +1119,58 @@ async function testUiComponent(componentName, component, url) {
|
|
|
1062
1119
|
if (!componentName || typeof componentName !== "string") {
|
|
1063
1120
|
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
1064
1121
|
}
|
|
1065
|
-
if (!component || !(component instanceof HTMLElement)) {
|
|
1066
|
-
throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
|
|
1122
|
+
if (!url && (!component || !(component instanceof HTMLElement))) {
|
|
1123
|
+
throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
|
|
1067
1124
|
}
|
|
1068
1125
|
if (url && typeof url !== "string") {
|
|
1069
1126
|
throw new Error("\u274C testUiComponent url parameter must be a string");
|
|
1070
1127
|
}
|
|
1071
1128
|
let results;
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1129
|
+
if (component) {
|
|
1130
|
+
try {
|
|
1131
|
+
results = await (0, import_jest_axe.axe)(component);
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
throw new Error(
|
|
1134
|
+
`\u274C Axe accessibility scan failed
|
|
1077
1135
|
Error: ${error instanceof Error ? error.message : String(error)}`
|
|
1078
|
-
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
} else {
|
|
1139
|
+
results = { violations: [] };
|
|
1079
1140
|
}
|
|
1080
|
-
async function checkDevServer(
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
try {
|
|
1089
|
-
const response = await fetch(serverUrl, {
|
|
1090
|
-
method: "HEAD",
|
|
1091
|
-
signal: AbortSignal.timeout(1e3)
|
|
1092
|
-
});
|
|
1093
|
-
if (response.ok || response.status === 304) {
|
|
1094
|
-
return serverUrl;
|
|
1095
|
-
}
|
|
1096
|
-
} catch {
|
|
1097
|
-
return null;
|
|
1141
|
+
async function checkDevServer(url2) {
|
|
1142
|
+
try {
|
|
1143
|
+
const response = await fetch(url2, {
|
|
1144
|
+
method: "HEAD",
|
|
1145
|
+
signal: AbortSignal.timeout(1e3)
|
|
1146
|
+
});
|
|
1147
|
+
if (response.ok || response.status === 304) {
|
|
1148
|
+
return url2;
|
|
1098
1149
|
}
|
|
1150
|
+
} catch {
|
|
1151
|
+
return null;
|
|
1099
1152
|
}
|
|
1100
1153
|
return null;
|
|
1101
1154
|
}
|
|
1102
1155
|
let contract;
|
|
1103
1156
|
try {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1157
|
+
if (url) {
|
|
1158
|
+
const devServerUrl = await checkDevServer(url);
|
|
1159
|
+
if (devServerUrl) {
|
|
1160
|
+
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
1161
|
+
const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
|
|
1162
|
+
contract = await runContractTestsPlaywright2(componentName, devServerUrl);
|
|
1163
|
+
} else {
|
|
1164
|
+
throw new Error(
|
|
1165
|
+
`\u274C Dev server not running at ${url}
|
|
1166
|
+
Please start your dev server and try again.`
|
|
1167
|
+
);
|
|
1168
|
+
}
|
|
1169
|
+
} else if (component) {
|
|
1170
|
+
console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
|
|
1114
1171
|
contract = await runContractTests(componentName, component);
|
|
1172
|
+
} else {
|
|
1173
|
+
throw new Error("\u274C Either component or URL must be provided");
|
|
1115
1174
|
}
|
|
1116
1175
|
} catch (error) {
|
|
1117
1176
|
if (error instanceof Error) {
|
|
@@ -1153,12 +1212,16 @@ ${violationDetails}
|
|
|
1153
1212
|
}
|
|
1154
1213
|
return result;
|
|
1155
1214
|
}
|
|
1215
|
+
async function cleanupTests() {
|
|
1216
|
+
await closeSharedBrowser();
|
|
1217
|
+
}
|
|
1156
1218
|
var import_jest_axe, runTest;
|
|
1157
1219
|
var init_test2 = __esm({
|
|
1158
1220
|
"src/utils/test/src/test.ts"() {
|
|
1159
1221
|
"use strict";
|
|
1160
1222
|
import_jest_axe = require("jest-axe");
|
|
1161
1223
|
init_contractTestRunner();
|
|
1224
|
+
init_playwrightTestHarness();
|
|
1162
1225
|
runTest = async () => {
|
|
1163
1226
|
};
|
|
1164
1227
|
if (typeof window === "undefined") {
|
|
@@ -1189,6 +1252,7 @@ var init_test2 = __esm({
|
|
|
1189
1252
|
// src/utils/test/index.ts
|
|
1190
1253
|
var test_exports2 = {};
|
|
1191
1254
|
__export(test_exports2, {
|
|
1255
|
+
cleanupTests: () => cleanupTests,
|
|
1192
1256
|
runTest: () => runTest,
|
|
1193
1257
|
testUiComponent: () => testUiComponent
|
|
1194
1258
|
});
|
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-X6K2LCMO.js");
|
|
208
208
|
runTest();
|
|
209
209
|
});
|
|
210
210
|
program.command("help").description("Display help information").action(() => {
|
package/bin/{contractTestRunnerPlaywright-O22AQ4RK.js → contractTestRunnerPlaywright-PMSOP5FY.js}
RENAMED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ContractReporter,
|
|
3
|
-
contract_default
|
|
4
|
-
|
|
3
|
+
contract_default,
|
|
4
|
+
createTestPage
|
|
5
|
+
} from "./chunk-7RMRFSJL.js";
|
|
5
6
|
import {
|
|
6
7
|
__export,
|
|
7
8
|
__reExport
|
|
8
9
|
} from "./chunk-I2KLQ2HA.js";
|
|
9
10
|
|
|
10
|
-
// src/utils/test/contract/contractTestRunnerPlaywright.ts
|
|
11
|
-
import { chromium } from "playwright";
|
|
12
|
-
|
|
13
11
|
// node_modules/@playwright/test/index.mjs
|
|
14
12
|
var test_exports = {};
|
|
15
13
|
__export(test_exports, {
|
|
@@ -37,30 +35,35 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
37
35
|
const failures = [];
|
|
38
36
|
const passes = [];
|
|
39
37
|
const skipped = [];
|
|
40
|
-
let
|
|
38
|
+
let page = null;
|
|
39
|
+
const useNavigation = !!url;
|
|
41
40
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
page = await createTestPage();
|
|
42
|
+
if (url) {
|
|
43
|
+
await page.goto(url, {
|
|
44
|
+
waitUntil: "domcontentloaded",
|
|
45
|
+
timeout: 3e4
|
|
46
|
+
});
|
|
47
|
+
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
48
|
+
}
|
|
50
49
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
51
50
|
if (!mainSelector) {
|
|
52
51
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
53
52
|
}
|
|
54
|
-
|
|
53
|
+
const elementTimeout = useNavigation ? 3e4 : 5e3;
|
|
54
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: elementTimeout });
|
|
55
55
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
56
56
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
57
57
|
state: "visible",
|
|
58
|
-
timeout:
|
|
58
|
+
timeout: 5e3
|
|
59
59
|
}).catch(() => {
|
|
60
60
|
console.warn("Menu trigger not visible, continuing with tests...");
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
async function resolveRelativeTarget(selector, relative) {
|
|
64
|
+
if (!page) {
|
|
65
|
+
throw new Error("Page is not initialized");
|
|
66
|
+
}
|
|
64
67
|
const items = await page.locator(selector).all();
|
|
65
68
|
switch (relative) {
|
|
66
69
|
case "first":
|
|
@@ -130,8 +133,8 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
130
133
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
131
134
|
const { action, assertions } = dynamicTest;
|
|
132
135
|
const failuresBeforeTest = failures.length;
|
|
133
|
-
if (componentContract.selectors.
|
|
134
|
-
const popupSelector = componentContract.selectors.
|
|
136
|
+
if (componentContract.selectors.popup) {
|
|
137
|
+
const popupSelector = componentContract.selectors.popup;
|
|
135
138
|
if (!popupSelector) continue;
|
|
136
139
|
const popupElement = page.locator(popupSelector).first();
|
|
137
140
|
const isPopupVisible = await popupElement.isVisible().catch(() => false);
|
|
@@ -147,25 +150,27 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
147
150
|
const closeElement = page.locator(closeSelector).first();
|
|
148
151
|
await closeElement.focus();
|
|
149
152
|
await page.keyboard.press("Escape");
|
|
150
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
153
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
151
154
|
}
|
|
152
155
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
153
156
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
154
157
|
await triggerElement.click();
|
|
155
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
158
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
156
159
|
}
|
|
157
160
|
if (!menuClosed) {
|
|
158
161
|
await page.mouse.click(10, 10);
|
|
159
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
162
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
160
163
|
}
|
|
161
164
|
if (!menuClosed) {
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
if (useNavigation) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
|
|
164
168
|
1. Escape key
|
|
165
169
|
2. Clicking trigger
|
|
166
170
|
3. Clicking outside
|
|
167
171
|
This indicates a problem with the menu component's close functionality.`
|
|
168
|
-
|
|
172
|
+
);
|
|
173
|
+
}
|
|
169
174
|
}
|
|
170
175
|
if (componentContract.selectors.input) {
|
|
171
176
|
await page.locator(componentContract.selectors.input).first().clear();
|
|
@@ -336,7 +341,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
336
341
|
}
|
|
337
342
|
if (assertion.assertion === "toBeVisible") {
|
|
338
343
|
try {
|
|
339
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
344
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
|
|
340
345
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
341
346
|
} catch {
|
|
342
347
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -350,7 +355,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
350
355
|
}
|
|
351
356
|
if (assertion.assertion === "notToBeVisible") {
|
|
352
357
|
try {
|
|
353
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
358
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
|
|
354
359
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
355
360
|
} catch {
|
|
356
361
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -452,7 +457,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
452
457
|
}
|
|
453
458
|
}
|
|
454
459
|
} finally {
|
|
455
|
-
if (
|
|
460
|
+
if (page) await page.close();
|
|
456
461
|
}
|
|
457
462
|
return { passes, failures, skipped };
|
|
458
463
|
}
|