aria-ease 5.0.2 → 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 +23 -13
- package/bin/{chunk-Y2FZWFX7.js → chunk-7RMRFSJL.js} +52 -2
- package/bin/cli.cjs +139 -125
- package/bin/cli.js +1 -1
- package/bin/{contractTestRunnerPlaywright-FFEJJCQO.js → contractTestRunnerPlaywright-PMSOP5FY.js} +42 -91
- package/bin/{test-RHQ2LJVG.js → test-X6K2LCMO.js} +44 -37
- package/dist/{chunk-AVCZIF6G.js → chunk-PDZQOXUN.js} +52 -2
- package/dist/{contractTestRunnerPlaywright-BFHRDJTJ.js → contractTestRunnerPlaywright-ZO6GM4TU.js} +42 -91
- package/dist/index.cjs +142 -127
- package/dist/index.d.cts +11 -6
- package/dist/index.d.ts +11 -6
- package/dist/index.js +46 -39
- package/dist/src/accordion/index.cjs +2 -2
- package/dist/src/accordion/index.d.cts +3 -3
- package/dist/src/accordion/index.d.ts +3 -3
- package/dist/src/accordion/index.js +2 -2
- package/dist/src/utils/test/{chunk-AVCZIF6G.js → chunk-7RMRFSJL.js} +48 -19
- package/dist/src/utils/test/{contractTestRunnerPlaywright-BFHRDJTJ.js → contractTestRunnerPlaywright-CREJNINL.js} +43 -98
- 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 +135 -166
- 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 +5 -5
package/README.md
CHANGED
|
@@ -175,7 +175,7 @@ useEffect(() => {
|
|
|
175
175
|
accordionId: "accordion-container",
|
|
176
176
|
triggersClass: "accordion-trigger",
|
|
177
177
|
panelsClass: "accordion-panel",
|
|
178
|
-
|
|
178
|
+
allowMultipleOpen: false, // Only one panel open at a time (default)
|
|
179
179
|
});
|
|
180
180
|
|
|
181
181
|
return () => accordion.cleanup();
|
|
@@ -214,7 +214,7 @@ makeAccordionAccessible({
|
|
|
214
214
|
accordionId: "faq-div",
|
|
215
215
|
triggersClass: "dropdown-button",
|
|
216
216
|
panelsClass: "accordion-panel",
|
|
217
|
-
|
|
217
|
+
allowMultipleOpen: false, // Only one panel open at a time
|
|
218
218
|
});
|
|
219
219
|
```
|
|
220
220
|
|
|
@@ -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
|
|
|
@@ -7,6 +7,10 @@ var contract_default = {
|
|
|
7
7
|
combobox: {
|
|
8
8
|
path: "./contracts/ComboboxContract.json",
|
|
9
9
|
component: "combobox"
|
|
10
|
+
},
|
|
11
|
+
accordion: {
|
|
12
|
+
path: "./contracts/AccordionContract.json",
|
|
13
|
+
component: "accordion"
|
|
10
14
|
}
|
|
11
15
|
};
|
|
12
16
|
|
|
@@ -182,7 +186,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
182
186
|
} else if (totalFailures === 0) {
|
|
183
187
|
this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
|
|
184
188
|
if (this.skipped > 0) {
|
|
185
|
-
this.log(`\u25CB ${this.skipped} tests skipped
|
|
189
|
+
this.log(`\u25CB ${this.skipped} tests skipped`);
|
|
186
190
|
}
|
|
187
191
|
if (this.optionalSuggestions > 0) {
|
|
188
192
|
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
@@ -231,7 +235,53 @@ ${"\u2550".repeat(60)}`);
|
|
|
231
235
|
}
|
|
232
236
|
};
|
|
233
237
|
|
|
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
|
+
|
|
234
282
|
export {
|
|
235
283
|
contract_default,
|
|
236
|
-
ContractReporter
|
|
284
|
+
ContractReporter,
|
|
285
|
+
createTestPage,
|
|
286
|
+
closeSharedBrowser
|
|
237
287
|
};
|
package/bin/cli.cjs
CHANGED
|
@@ -283,6 +283,10 @@ var init_contract = __esm({
|
|
|
283
283
|
combobox: {
|
|
284
284
|
path: "./contracts/ComboboxContract.json",
|
|
285
285
|
component: "combobox"
|
|
286
|
+
},
|
|
287
|
+
accordion: {
|
|
288
|
+
path: "./contracts/AccordionContract.json",
|
|
289
|
+
component: "accordion"
|
|
286
290
|
}
|
|
287
291
|
};
|
|
288
292
|
}
|
|
@@ -464,7 +468,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
464
468
|
} else if (totalFailures === 0) {
|
|
465
469
|
this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
|
|
466
470
|
if (this.skipped > 0) {
|
|
467
|
-
this.log(`\u25CB ${this.skipped} tests skipped
|
|
471
|
+
this.log(`\u25CB ${this.skipped} tests skipped`);
|
|
468
472
|
}
|
|
469
473
|
if (this.optionalSuggestions > 0) {
|
|
470
474
|
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
@@ -586,6 +590,56 @@ var init_contractTestRunner = __esm({
|
|
|
586
590
|
}
|
|
587
591
|
});
|
|
588
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
|
+
|
|
589
643
|
// node_modules/@playwright/test/index.mjs
|
|
590
644
|
var test_exports = {};
|
|
591
645
|
__export(test_exports, {
|
|
@@ -621,34 +675,35 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
621
675
|
const failures = [];
|
|
622
676
|
const passes = [];
|
|
623
677
|
const skipped = [];
|
|
624
|
-
let
|
|
678
|
+
let page = null;
|
|
679
|
+
const useNavigation = !!url;
|
|
625
680
|
try {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
+
}
|
|
633
689
|
const mainSelector = componentContract.selectors.trigger || componentContract.selectors.input || componentContract.selectors.container;
|
|
634
690
|
if (!mainSelector) {
|
|
635
691
|
throw new Error(`No main selector (trigger, input, or container) found in contract for ${componentName}`);
|
|
636
692
|
}
|
|
637
|
-
|
|
693
|
+
const elementTimeout = useNavigation ? 3e4 : 5e3;
|
|
694
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: elementTimeout });
|
|
638
695
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
639
|
-
await page.
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
componentContract.selectors.trigger,
|
|
645
|
-
{ timeout: 1e4 }
|
|
646
|
-
).catch(() => {
|
|
647
|
-
console.warn("Menu initialization signal not detected, continuing with tests...");
|
|
696
|
+
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
697
|
+
state: "visible",
|
|
698
|
+
timeout: 5e3
|
|
699
|
+
}).catch(() => {
|
|
700
|
+
console.warn("Menu trigger not visible, continuing with tests...");
|
|
648
701
|
});
|
|
649
|
-
await page.waitForTimeout(300);
|
|
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,11 +773,11 @@ 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
|
-
const isPopupVisible = await popupElement.isVisible();
|
|
780
|
+
const isPopupVisible = await popupElement.isVisible().catch(() => false);
|
|
726
781
|
if (isPopupVisible) {
|
|
727
782
|
let menuClosed = false;
|
|
728
783
|
let closeSelector = componentContract.selectors.input;
|
|
@@ -734,73 +789,35 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
734
789
|
if (closeSelector) {
|
|
735
790
|
const closeElement = page.locator(closeSelector).first();
|
|
736
791
|
await closeElement.focus();
|
|
737
|
-
await page.waitForTimeout(200);
|
|
738
792
|
await page.keyboard.press("Escape");
|
|
739
|
-
menuClosed = await
|
|
740
|
-
(selector) => {
|
|
741
|
-
const popup = document.querySelector(selector);
|
|
742
|
-
return popup && getComputedStyle(popup).display === "none";
|
|
743
|
-
},
|
|
744
|
-
popupSelector,
|
|
745
|
-
{ timeout: 3e3 }
|
|
746
|
-
).then(() => true).catch(() => false);
|
|
793
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
747
794
|
}
|
|
748
795
|
if (!menuClosed && componentContract.selectors.trigger) {
|
|
749
796
|
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
750
797
|
await triggerElement.click();
|
|
751
|
-
await
|
|
752
|
-
menuClosed = await page.waitForFunction(
|
|
753
|
-
(selector) => {
|
|
754
|
-
const popup = document.querySelector(selector);
|
|
755
|
-
return popup && getComputedStyle(popup).display === "none";
|
|
756
|
-
},
|
|
757
|
-
popupSelector,
|
|
758
|
-
{ timeout: 3e3 }
|
|
759
|
-
).then(() => true).catch(() => false);
|
|
798
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
760
799
|
}
|
|
761
800
|
if (!menuClosed) {
|
|
762
801
|
await page.mouse.click(10, 10);
|
|
763
|
-
await
|
|
764
|
-
menuClosed = await page.waitForFunction(
|
|
765
|
-
(selector) => {
|
|
766
|
-
const popup = document.querySelector(selector);
|
|
767
|
-
return popup && getComputedStyle(popup).display === "none";
|
|
768
|
-
},
|
|
769
|
-
popupSelector,
|
|
770
|
-
{ timeout: 3e3 }
|
|
771
|
-
).then(() => true).catch(() => false);
|
|
772
|
-
if (menuClosed) {
|
|
773
|
-
console.log("\u{1F3AF} Strategy 3 (Click outside) worked");
|
|
774
|
-
}
|
|
802
|
+
menuClosed = await (0, test_exports.expect)(popupElement).toBeHidden({ timeout: 2e3 }).then(() => true).catch(() => false);
|
|
775
803
|
}
|
|
776
804
|
if (!menuClosed) {
|
|
777
|
-
|
|
778
|
-
|
|
805
|
+
if (useNavigation) {
|
|
806
|
+
throw new Error(
|
|
807
|
+
`\u274C FATAL: Cannot close menu between tests. Menu remains visible after trying:
|
|
779
808
|
1. Escape key
|
|
780
809
|
2. Clicking trigger
|
|
781
810
|
3. Clicking outside
|
|
782
811
|
This indicates a problem with the menu component's close functionality.`
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
786
|
-
await page.waitForFunction(
|
|
787
|
-
(selector) => {
|
|
788
|
-
const trigger = document.querySelector(selector);
|
|
789
|
-
return document.activeElement === trigger;
|
|
790
|
-
},
|
|
791
|
-
componentContract.selectors.trigger,
|
|
792
|
-
{ timeout: 2e3 }
|
|
793
|
-
).catch(async () => {
|
|
794
|
-
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
795
|
-
await triggerElement.focus();
|
|
796
|
-
await page.waitForTimeout(200);
|
|
797
|
-
});
|
|
812
|
+
);
|
|
813
|
+
}
|
|
798
814
|
}
|
|
799
|
-
await page.waitForTimeout(500);
|
|
800
815
|
if (componentContract.selectors.input) {
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
816
|
+
await page.locator(componentContract.selectors.input).first().clear();
|
|
817
|
+
}
|
|
818
|
+
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
819
|
+
const triggerElement = page.locator(componentContract.selectors.trigger).first();
|
|
820
|
+
await triggerElement.focus();
|
|
804
821
|
}
|
|
805
822
|
}
|
|
806
823
|
}
|
|
@@ -844,7 +861,6 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
844
861
|
continue;
|
|
845
862
|
}
|
|
846
863
|
await page.locator(focusSelector).first().focus();
|
|
847
|
-
await page.waitForTimeout(100);
|
|
848
864
|
}
|
|
849
865
|
if (act.type === "type" && act.value) {
|
|
850
866
|
const typeSelector = componentContract.selectors[act.target];
|
|
@@ -853,7 +869,6 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
853
869
|
continue;
|
|
854
870
|
}
|
|
855
871
|
await page.locator(typeSelector).first().fill(act.value);
|
|
856
|
-
await page.waitForTimeout(100);
|
|
857
872
|
}
|
|
858
873
|
if (act.type === "click") {
|
|
859
874
|
if (act.target === "document") {
|
|
@@ -870,7 +885,6 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
870
885
|
continue;
|
|
871
886
|
}
|
|
872
887
|
await relativeElement.click();
|
|
873
|
-
await page.waitForTimeout(componentName === "menu" ? 800 : 200);
|
|
874
888
|
} else {
|
|
875
889
|
const actionSelector = componentContract.selectors[act.target];
|
|
876
890
|
if (!actionSelector) {
|
|
@@ -878,7 +892,6 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
878
892
|
continue;
|
|
879
893
|
}
|
|
880
894
|
await page.locator(actionSelector).first().click();
|
|
881
|
-
await page.waitForTimeout(componentName === "menu" ? 800 : 200);
|
|
882
895
|
}
|
|
883
896
|
}
|
|
884
897
|
if (act.type === "keypress" && act.key) {
|
|
@@ -901,9 +914,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
901
914
|
keyValue = keyValue.replace(/ /g, "");
|
|
902
915
|
}
|
|
903
916
|
if (act.target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
904
|
-
await page.waitForTimeout(componentName === "menu" ? 200 : 100);
|
|
905
917
|
await page.keyboard.press(keyValue);
|
|
906
|
-
await page.waitForTimeout(componentName === "menu" ? 300 : 100);
|
|
907
918
|
} else {
|
|
908
919
|
const keypressSelector = componentContract.selectors[act.target];
|
|
909
920
|
if (!keypressSelector) {
|
|
@@ -932,7 +943,6 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
932
943
|
continue;
|
|
933
944
|
}
|
|
934
945
|
await relativeElement.hover();
|
|
935
|
-
await page.waitForTimeout(100);
|
|
936
946
|
} else {
|
|
937
947
|
const hoverSelector = componentContract.selectors[act.target];
|
|
938
948
|
if (!hoverSelector) {
|
|
@@ -940,12 +950,9 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
940
950
|
continue;
|
|
941
951
|
}
|
|
942
952
|
await page.locator(hoverSelector).first().hover();
|
|
943
|
-
await page.waitForTimeout(100);
|
|
944
953
|
}
|
|
945
954
|
}
|
|
946
|
-
await page.waitForTimeout(componentName === "menu" ? 200 : 100);
|
|
947
955
|
}
|
|
948
|
-
await page.waitForTimeout(componentName === "menu" ? 300 : 100);
|
|
949
956
|
for (const assertion of assertions) {
|
|
950
957
|
let target;
|
|
951
958
|
if (assertion.target === "relative") {
|
|
@@ -974,7 +981,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
974
981
|
}
|
|
975
982
|
if (assertion.assertion === "toBeVisible") {
|
|
976
983
|
try {
|
|
977
|
-
await (0, test_exports.expect)(target).toBeVisible({ timeout:
|
|
984
|
+
await (0, test_exports.expect)(target).toBeVisible({ timeout: 2e3 });
|
|
978
985
|
passes.push(`${assertion.target} is visible as expected. Test: "${dynamicTest.description}".`);
|
|
979
986
|
} catch {
|
|
980
987
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -988,7 +995,7 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
988
995
|
}
|
|
989
996
|
if (assertion.assertion === "notToBeVisible") {
|
|
990
997
|
try {
|
|
991
|
-
await (0, test_exports.expect)(target).toBeHidden({ timeout:
|
|
998
|
+
await (0, test_exports.expect)(target).toBeHidden({ timeout: 2e3 });
|
|
992
999
|
passes.push(`${assertion.target} is not visible as expected. Test: "${dynamicTest.description}".`);
|
|
993
1000
|
} catch {
|
|
994
1001
|
const debugState = await page.evaluate((sel) => {
|
|
@@ -1090,19 +1097,19 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
1090
1097
|
}
|
|
1091
1098
|
}
|
|
1092
1099
|
} finally {
|
|
1093
|
-
if (
|
|
1100
|
+
if (page) await page.close();
|
|
1094
1101
|
}
|
|
1095
1102
|
return { passes, failures, skipped };
|
|
1096
1103
|
}
|
|
1097
|
-
var
|
|
1104
|
+
var import_fs, import_meta2;
|
|
1098
1105
|
var init_contractTestRunnerPlaywright = __esm({
|
|
1099
1106
|
"src/utils/test/contract/contractTestRunnerPlaywright.ts"() {
|
|
1100
1107
|
"use strict";
|
|
1101
|
-
import_playwright3 = require("playwright");
|
|
1102
1108
|
init_test();
|
|
1103
1109
|
import_fs = require("fs");
|
|
1104
1110
|
init_contract();
|
|
1105
1111
|
init_ContractReporter();
|
|
1112
|
+
init_playwrightTestHarness();
|
|
1106
1113
|
import_meta2 = {};
|
|
1107
1114
|
}
|
|
1108
1115
|
});
|
|
@@ -1112,56 +1119,58 @@ async function testUiComponent(componentName, component, url) {
|
|
|
1112
1119
|
if (!componentName || typeof componentName !== "string") {
|
|
1113
1120
|
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
1114
1121
|
}
|
|
1115
|
-
if (!component || !(component instanceof HTMLElement)) {
|
|
1116
|
-
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");
|
|
1117
1124
|
}
|
|
1118
1125
|
if (url && typeof url !== "string") {
|
|
1119
1126
|
throw new Error("\u274C testUiComponent url parameter must be a string");
|
|
1120
1127
|
}
|
|
1121
1128
|
let results;
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
|
1127
1135
|
Error: ${error instanceof Error ? error.message : String(error)}`
|
|
1128
|
-
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
} else {
|
|
1139
|
+
results = { violations: [] };
|
|
1129
1140
|
}
|
|
1130
|
-
async function checkDevServer(
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
try {
|
|
1139
|
-
const response = await fetch(serverUrl, {
|
|
1140
|
-
method: "HEAD",
|
|
1141
|
-
signal: AbortSignal.timeout(1e3)
|
|
1142
|
-
});
|
|
1143
|
-
if (response.ok || response.status === 304) {
|
|
1144
|
-
return serverUrl;
|
|
1145
|
-
}
|
|
1146
|
-
} catch {
|
|
1147
|
-
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;
|
|
1148
1149
|
}
|
|
1150
|
+
} catch {
|
|
1151
|
+
return null;
|
|
1149
1152
|
}
|
|
1150
1153
|
return null;
|
|
1151
1154
|
}
|
|
1152
1155
|
let contract;
|
|
1153
1156
|
try {
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
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`);
|
|
1164
1171
|
contract = await runContractTests(componentName, component);
|
|
1172
|
+
} else {
|
|
1173
|
+
throw new Error("\u274C Either component or URL must be provided");
|
|
1165
1174
|
}
|
|
1166
1175
|
} catch (error) {
|
|
1167
1176
|
if (error instanceof Error) {
|
|
@@ -1203,12 +1212,16 @@ ${violationDetails}
|
|
|
1203
1212
|
}
|
|
1204
1213
|
return result;
|
|
1205
1214
|
}
|
|
1215
|
+
async function cleanupTests() {
|
|
1216
|
+
await closeSharedBrowser();
|
|
1217
|
+
}
|
|
1206
1218
|
var import_jest_axe, runTest;
|
|
1207
1219
|
var init_test2 = __esm({
|
|
1208
1220
|
"src/utils/test/src/test.ts"() {
|
|
1209
1221
|
"use strict";
|
|
1210
1222
|
import_jest_axe = require("jest-axe");
|
|
1211
1223
|
init_contractTestRunner();
|
|
1224
|
+
init_playwrightTestHarness();
|
|
1212
1225
|
runTest = async () => {
|
|
1213
1226
|
};
|
|
1214
1227
|
if (typeof window === "undefined") {
|
|
@@ -1239,6 +1252,7 @@ var init_test2 = __esm({
|
|
|
1239
1252
|
// src/utils/test/index.ts
|
|
1240
1253
|
var test_exports2 = {};
|
|
1241
1254
|
__export(test_exports2, {
|
|
1255
|
+
cleanupTests: () => cleanupTests,
|
|
1242
1256
|
runTest: () => runTest,
|
|
1243
1257
|
testUiComponent: () => testUiComponent
|
|
1244
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(() => {
|