aria-ease 5.0.3 → 6.0.1
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 +135 -58
- package/bin/cli.js +1 -1
- package/bin/{contractTestRunnerPlaywright-O22AQ4RK.js → contractTestRunnerPlaywright-HL2VPEEV.js} +42 -24
- package/bin/{test-JNQFZBJA.js → test-HH2EW2NM.js} +44 -37
- package/dist/{chunk-KJ33RDSC.js → chunk-PDZQOXUN.js} +48 -2
- package/dist/{contractTestRunnerPlaywright-7ZOM7ZMG.js → contractTestRunnerPlaywright-EXEBWWPC.js} +42 -24
- package/dist/index.cjs +136 -58
- 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-LJHY3AB4.js} +40 -21
- 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 +130 -56
- 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,33 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
625
675
|
const failures = [];
|
|
626
676
|
const passes = [];
|
|
627
677
|
const skipped = [];
|
|
628
|
-
let
|
|
678
|
+
let page = null;
|
|
629
679
|
try {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
680
|
+
page = await createTestPage();
|
|
681
|
+
if (url) {
|
|
682
|
+
await page.goto(url, {
|
|
683
|
+
waitUntil: "domcontentloaded",
|
|
684
|
+
timeout: 3e4
|
|
685
|
+
});
|
|
686
|
+
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
687
|
+
}
|
|
638
688
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
639
689
|
if (!mainSelector) {
|
|
640
690
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
641
691
|
}
|
|
642
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout:
|
|
692
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
643
693
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
644
694
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
645
695
|
state: "visible",
|
|
646
|
-
timeout:
|
|
696
|
+
timeout: 5e3
|
|
647
697
|
}).catch(() => {
|
|
648
698
|
console.warn("Menu trigger not visible, continuing with tests...");
|
|
649
699
|
});
|
|
650
700
|
}
|
|
651
701
|
async function resolveRelativeTarget(selector, relative) {
|
|
702
|
+
if (!page) {
|
|
703
|
+
throw new Error("Page is not initialized");
|
|
704
|
+
}
|
|
652
705
|
const items = await page.locator(selector).all();
|
|
653
706
|
switch (relative) {
|
|
654
707
|
case "first":
|
|
@@ -718,8 +771,8 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
718
771
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
719
772
|
const { action, assertions } = dynamicTest;
|
|
720
773
|
const failuresBeforeTest = failures.length;
|
|
721
|
-
if (componentContract.selectors.
|
|
722
|
-
const popupSelector = componentContract.selectors.
|
|
774
|
+
if (componentContract.selectors.popup) {
|
|
775
|
+
const popupSelector = componentContract.selectors.popup;
|
|
723
776
|
if (!popupSelector) continue;
|
|
724
777
|
const popupElement = page.locator(popupSelector).first();
|
|
725
778
|
const isPopupVisible = await popupElement.isVisible().catch(() => false);
|
|
@@ -735,16 +788,16 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
735
788
|
const closeElement = page.locator(closeSelector).first();
|
|
736
789
|
await closeElement.focus();
|
|
737
790
|
await page.keyboard.press("Escape");
|
|
738
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
791
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
739
792
|
}
|
|
740
793
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
741
794
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
742
795
|
await triggerElement.click();
|
|
743
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
796
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
744
797
|
}
|
|
745
798
|
if (!menuClosed) {
|
|
746
799
|
await page.mouse.click(10, 10);
|
|
747
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
800
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
748
801
|
}
|
|
749
802
|
if (!menuClosed) {
|
|
750
803
|
throw new Error(
|
|
@@ -764,6 +817,23 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
764
817
|
}
|
|
765
818
|
}
|
|
766
819
|
}
|
|
820
|
+
if (componentContract.selectors.panel && componentContract.selectors.trigger && !componentContract.selectors.popup) {
|
|
821
|
+
const triggerSelector = componentContract.selectors.trigger;
|
|
822
|
+
const panelSelector = componentContract.selectors.panel;
|
|
823
|
+
if (triggerSelector && panelSelector) {
|
|
824
|
+
const allTriggers = await page.locator(triggerSelector).all();
|
|
825
|
+
for (const trigger of allTriggers) {
|
|
826
|
+
const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
|
|
827
|
+
const triggerPanel = await trigger.getAttribute("aria-controls");
|
|
828
|
+
if (isExpanded && triggerPanel) {
|
|
829
|
+
await trigger.click();
|
|
830
|
+
const panel = page.locator(`#${triggerPanel}`);
|
|
831
|
+
await (0, test_exports.expect)(panel).toBeHidden({ timeout: 1e3 }).catch(() => {
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
767
837
|
let shouldSkipTest = false;
|
|
768
838
|
for (const act of action) {
|
|
769
839
|
if (act.type === "keypress" && (act.target === "submenuTrigger" || act.target === "submenu")) {
|
|
@@ -924,7 +994,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
924
994
|
}
|
|
925
995
|
if (assertion.assertion === "toBeVisible") {
|
|
926
996
|
try {
|
|
927
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
997
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
|
|
928
998
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
929
999
|
} catch {
|
|
930
1000
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -938,7 +1008,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
938
1008
|
}
|
|
939
1009
|
if (assertion.assertion === "notToBeVisible") {
|
|
940
1010
|
try {
|
|
941
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
1011
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
|
|
942
1012
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
943
1013
|
} catch {
|
|
944
1014
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -1040,19 +1110,19 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
1040
1110
|
}
|
|
1041
1111
|
}
|
|
1042
1112
|
} finally {
|
|
1043
|
-
if (
|
|
1113
|
+
if (page) await page.close();
|
|
1044
1114
|
}
|
|
1045
1115
|
return { passes, failures, skipped };
|
|
1046
1116
|
}
|
|
1047
|
-
var
|
|
1117
|
+
var import_fs, import_meta2;
|
|
1048
1118
|
var init_contractTestRunnerPlaywright = __esm({
|
|
1049
1119
|
"src/utils/test/contract/contractTestRunnerPlaywright.ts"() {
|
|
1050
1120
|
"use strict";
|
|
1051
|
-
import_playwright3 = require("playwright");
|
|
1052
1121
|
init_test();
|
|
1053
1122
|
import_fs = require("fs");
|
|
1054
1123
|
init_contract();
|
|
1055
1124
|
init_ContractReporter();
|
|
1125
|
+
init_playwrightTestHarness();
|
|
1056
1126
|
import_meta2 = {};
|
|
1057
1127
|
}
|
|
1058
1128
|
});
|
|
@@ -1062,56 +1132,58 @@ async function testUiComponent(componentName, component, url) {
|
|
|
1062
1132
|
if (!componentName || typeof componentName !== "string") {
|
|
1063
1133
|
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
1064
1134
|
}
|
|
1065
|
-
if (!component || !(component instanceof HTMLElement)) {
|
|
1066
|
-
throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
|
|
1135
|
+
if (!url && (!component || !(component instanceof HTMLElement))) {
|
|
1136
|
+
throw new Error("\u274C testUiComponent requires either a valid component (HTMLElement) or a URL");
|
|
1067
1137
|
}
|
|
1068
1138
|
if (url && typeof url !== "string") {
|
|
1069
1139
|
throw new Error("\u274C testUiComponent url parameter must be a string");
|
|
1070
1140
|
}
|
|
1071
1141
|
let results;
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1142
|
+
if (component) {
|
|
1143
|
+
try {
|
|
1144
|
+
results = await (0, import_jest_axe.axe)(component);
|
|
1145
|
+
} catch (error) {
|
|
1146
|
+
throw new Error(
|
|
1147
|
+
`\u274C Axe accessibility scan failed
|
|
1077
1148
|
Error: ${error instanceof Error ? error.message : String(error)}`
|
|
1078
|
-
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
} else {
|
|
1152
|
+
results = { violations: [] };
|
|
1079
1153
|
}
|
|
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;
|
|
1154
|
+
async function checkDevServer(url2) {
|
|
1155
|
+
try {
|
|
1156
|
+
const response = await fetch(url2, {
|
|
1157
|
+
method: "HEAD",
|
|
1158
|
+
signal: AbortSignal.timeout(1e3)
|
|
1159
|
+
});
|
|
1160
|
+
if (response.ok || response.status === 304) {
|
|
1161
|
+
return url2;
|
|
1098
1162
|
}
|
|
1163
|
+
} catch {
|
|
1164
|
+
return null;
|
|
1099
1165
|
}
|
|
1100
1166
|
return null;
|
|
1101
1167
|
}
|
|
1102
1168
|
let contract;
|
|
1103
1169
|
try {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1170
|
+
if (url) {
|
|
1171
|
+
const devServerUrl = await checkDevServer(url);
|
|
1172
|
+
if (devServerUrl) {
|
|
1173
|
+
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
1174
|
+
const { runContractTestsPlaywright: runContractTestsPlaywright2 } = await Promise.resolve().then(() => (init_contractTestRunnerPlaywright(), contractTestRunnerPlaywright_exports));
|
|
1175
|
+
contract = await runContractTestsPlaywright2(componentName, devServerUrl);
|
|
1176
|
+
} else {
|
|
1177
|
+
throw new Error(
|
|
1178
|
+
`\u274C Dev server not running at ${url}
|
|
1179
|
+
Please start your dev server and try again.`
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
} else if (component) {
|
|
1183
|
+
console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
|
|
1114
1184
|
contract = await runContractTests(componentName, component);
|
|
1185
|
+
} else {
|
|
1186
|
+
throw new Error("\u274C Either component or URL must be provided");
|
|
1115
1187
|
}
|
|
1116
1188
|
} catch (error) {
|
|
1117
1189
|
if (error instanceof Error) {
|
|
@@ -1153,12 +1225,16 @@ ${violationDetails}
|
|
|
1153
1225
|
}
|
|
1154
1226
|
return result;
|
|
1155
1227
|
}
|
|
1228
|
+
async function cleanupTests() {
|
|
1229
|
+
await closeSharedBrowser();
|
|
1230
|
+
}
|
|
1156
1231
|
var import_jest_axe, runTest;
|
|
1157
1232
|
var init_test2 = __esm({
|
|
1158
1233
|
"src/utils/test/src/test.ts"() {
|
|
1159
1234
|
"use strict";
|
|
1160
1235
|
import_jest_axe = require("jest-axe");
|
|
1161
1236
|
init_contractTestRunner();
|
|
1237
|
+
init_playwrightTestHarness();
|
|
1162
1238
|
runTest = async () => {
|
|
1163
1239
|
};
|
|
1164
1240
|
if (typeof window === "undefined") {
|
|
@@ -1189,6 +1265,7 @@ var init_test2 = __esm({
|
|
|
1189
1265
|
// src/utils/test/index.ts
|
|
1190
1266
|
var test_exports2 = {};
|
|
1191
1267
|
__export(test_exports2, {
|
|
1268
|
+
cleanupTests: () => cleanupTests,
|
|
1192
1269
|
runTest: () => runTest,
|
|
1193
1270
|
testUiComponent: () => testUiComponent
|
|
1194
1271
|
});
|
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-HH2EW2NM.js");
|
|
208
208
|
runTest();
|
|
209
209
|
});
|
|
210
210
|
program.command("help").description("Display help information").action(() => {
|
package/bin/{contractTestRunnerPlaywright-O22AQ4RK.js → contractTestRunnerPlaywright-HL2VPEEV.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,33 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
37
35
|
const failures = [];
|
|
38
36
|
const passes = [];
|
|
39
37
|
const skipped = [];
|
|
40
|
-
let
|
|
38
|
+
let page = null;
|
|
41
39
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
page = await createTestPage();
|
|
41
|
+
if (url) {
|
|
42
|
+
await page.goto(url, {
|
|
43
|
+
waitUntil: "domcontentloaded",
|
|
44
|
+
timeout: 3e4
|
|
45
|
+
});
|
|
46
|
+
await page.addStyleTag({ content: `* { transition: none !important; animation: none !important; }` });
|
|
47
|
+
}
|
|
50
48
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
51
49
|
if (!mainSelector) {
|
|
52
50
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
53
51
|
}
|
|
54
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout:
|
|
52
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
55
53
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
56
54
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
57
55
|
state: "visible",
|
|
58
|
-
timeout:
|
|
56
|
+
timeout: 5e3
|
|
59
57
|
}).catch(() => {
|
|
60
58
|
console.warn("Menu trigger not visible, continuing with tests...");
|
|
61
59
|
});
|
|
62
60
|
}
|
|
63
61
|
async function resolveRelativeTarget(selector, relative) {
|
|
62
|
+
if (!page) {
|
|
63
|
+
throw new Error("Page is not initialized");
|
|
64
|
+
}
|
|
64
65
|
const items = await page.locator(selector).all();
|
|
65
66
|
switch (relative) {
|
|
66
67
|
case "first":
|
|
@@ -130,8 +131,8 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
130
131
|
for (const dynamicTest of componentContract.dynamic || []) {
|
|
131
132
|
const { action, assertions } = dynamicTest;
|
|
132
133
|
const failuresBeforeTest = failures.length;
|
|
133
|
-
if (componentContract.selectors.
|
|
134
|
-
const popupSelector = componentContract.selectors.
|
|
134
|
+
if (componentContract.selectors.popup) {
|
|
135
|
+
const popupSelector = componentContract.selectors.popup;
|
|
135
136
|
if (!popupSelector) continue;
|
|
136
137
|
const popupElement = page.locator(popupSelector).first();
|
|
137
138
|
const isPopupVisible = await popupElement.isVisible().catch(() => false);
|
|
@@ -147,16 +148,16 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
147
148
|
const closeElement = page.locator(closeSelector).first();
|
|
148
149
|
await closeElement.focus();
|
|
149
150
|
await page.keyboard.press("Escape");
|
|
150
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
151
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
151
152
|
}
|
|
152
153
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
153
154
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
154
155
|
await triggerElement.click();
|
|
155
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
156
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
156
157
|
}
|
|
157
158
|
if (!menuClosed) {
|
|
158
159
|
await page.mouse.click(10, 10);
|
|
159
|
-
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout:
|
|
160
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
160
161
|
}
|
|
161
162
|
if (!menuClosed) {
|
|
162
163
|
throw new Error(
|
|
@@ -176,6 +177,23 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
179
|
}
|
|
180
|
+
if (componentContract.selectors.panel && componentContract.selectors.trigger && !componentContract.selectors.popup) {
|
|
181
|
+
const triggerSelector = componentContract.selectors.trigger;
|
|
182
|
+
const panelSelector = componentContract.selectors.panel;
|
|
183
|
+
if (triggerSelector && panelSelector) {
|
|
184
|
+
const allTriggers = await page.locator(triggerSelector).all();
|
|
185
|
+
for (const trigger of allTriggers) {
|
|
186
|
+
const isExpanded = await trigger.getAttribute("aria-expanded") === "true";
|
|
187
|
+
const triggerPanel = await trigger.getAttribute("aria-controls");
|
|
188
|
+
if (isExpanded && triggerPanel) {
|
|
189
|
+
await trigger.click();
|
|
190
|
+
const panel = page.locator(`#${triggerPanel}`);
|
|
191
|
+
await (0, test_exports.expect)(panel).toBeHidden({ timeout: 1e3 }).catch(() => {
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
179
197
|
let shouldSkipTest = false;
|
|
180
198
|
for (const act of action) {
|
|
181
199
|
if (act.type === "keypress" && (act.target === "submenuTrigger" || act.target === "submenu")) {
|
|
@@ -336,7 +354,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
336
354
|
}
|
|
337
355
|
if (assertion.assertion === "toBeVisible") {
|
|
338
356
|
try {
|
|
339
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
357
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
|
|
340
358
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
341
359
|
} catch {
|
|
342
360
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -350,7 +368,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
350
368
|
}
|
|
351
369
|
if (assertion.assertion === "notToBeVisible") {
|
|
352
370
|
try {
|
|
353
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
371
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
|
|
354
372
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
355
373
|
} catch {
|
|
356
374
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -452,7 +470,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
452
470
|
}
|
|
453
471
|
}
|
|
454
472
|
} finally {
|
|
455
|
-
if (
|
|
473
|
+
if (page) await page.close();
|
|
456
474
|
}
|
|
457
475
|
return { passes, failures, skipped };
|
|
458
476
|
}
|