aria-ease 6.4.10 → 6.5.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/bin/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +34 -18
- package/bin/cli.cjs +67 -35
- package/bin/cli.js +1 -1
- package/{dist/contractTestRunnerPlaywright-7BCEDPZF.js → bin/contractTestRunnerPlaywright-7F756CFB.js} +26 -12
- package/bin/{test-JGKWOL6J.js → test-C3CMRHSI.js} +7 -5
- package/dist/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +34 -18
- package/{bin/contractTestRunnerPlaywright-7BCEDPZF.js → dist/contractTestRunnerPlaywright-7F756CFB.js} +26 -12
- package/dist/index.cjs +73 -41
- package/dist/index.js +14 -12
- package/dist/src/{Types.d-DYfYR3Vc.d.cts → Types.d-yGC2bBaB.d.cts} +1 -1
- package/dist/src/{Types.d-DYfYR3Vc.d.ts → Types.d-yGC2bBaB.d.ts} +1 -1
- package/dist/src/accordion/index.d.cts +1 -1
- package/dist/src/accordion/index.d.ts +1 -1
- package/dist/src/block/index.d.cts +1 -1
- package/dist/src/block/index.d.ts +1 -1
- package/dist/src/checkbox/index.d.cts +1 -1
- package/dist/src/checkbox/index.d.ts +1 -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 +7 -7
- package/dist/src/menu/index.d.cts +1 -1
- package/dist/src/menu/index.d.ts +1 -1
- package/dist/src/menu/index.js +7 -7
- package/dist/src/radio/index.d.cts +1 -1
- package/dist/src/radio/index.d.ts +1 -1
- package/dist/src/tabs/index.d.cts +1 -1
- package/dist/src/tabs/index.d.ts +1 -1
- package/dist/src/toggle/index.d.cts +1 -1
- package/dist/src/toggle/index.d.ts +1 -1
- package/dist/src/utils/test/{contracts/AccordionContract.json → aria-contracts/accordion/accordion.contract.json} +20 -7
- package/dist/src/utils/test/{contracts/ComboboxContract.json → aria-contracts/combobox/combobox.listbox.contract.json} +17 -17
- package/dist/src/utils/test/{contracts/MenuContract.json → aria-contracts/menu/menu.contract.json} +42 -1
- package/dist/src/utils/test/{contracts/TabsContract.json → aria-contracts/tabs/tabs.contract.json} +20 -7
- package/dist/src/utils/test/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +33 -17
- package/dist/src/utils/test/{contractTestRunnerPlaywright-O7FF3X67.js → contractTestRunnerPlaywright-HL73FADJ.js} +25 -11
- package/dist/src/utils/test/index.cjs +65 -33
- package/dist/src/utils/test/index.js +6 -4
- package/package.json +2 -2
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
// src/utils/test/contract/contract.json
|
|
2
2
|
var contract_default = {
|
|
3
3
|
menu: {
|
|
4
|
-
path: "./contracts/
|
|
4
|
+
path: "./aria-contracts/menu/menu.contract.json",
|
|
5
5
|
component: "menu"
|
|
6
6
|
},
|
|
7
|
-
combobox: {
|
|
8
|
-
path: "./contracts/
|
|
9
|
-
component: "combobox"
|
|
7
|
+
"combobox.listbox": {
|
|
8
|
+
path: "./aria-contracts/combobox/combobox.listbox.contract.json",
|
|
9
|
+
component: "combobox.listbox"
|
|
10
10
|
},
|
|
11
11
|
accordion: {
|
|
12
|
-
path: "./contracts/
|
|
12
|
+
path: "./aria-contracts/accordion/accordion.contract.json",
|
|
13
13
|
component: "accordion"
|
|
14
14
|
},
|
|
15
15
|
tabs: {
|
|
16
|
-
path: "./contracts/
|
|
16
|
+
path: "./aria-contracts/tabs/tabs.contract.json",
|
|
17
17
|
component: "tabs"
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
// src/utils/test/
|
|
21
|
+
// src/utils/test/src/ContractReporter.ts
|
|
22
22
|
var ContractReporter = class {
|
|
23
23
|
startTime = 0;
|
|
24
24
|
componentName = "";
|
|
@@ -30,6 +30,8 @@ var ContractReporter = class {
|
|
|
30
30
|
optionalSuggestions = 0;
|
|
31
31
|
isPlaywright = false;
|
|
32
32
|
apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
|
|
33
|
+
hasPrintedStaticSection = false;
|
|
34
|
+
hasPrintedDynamicSection = false;
|
|
33
35
|
constructor(isPlaywright = false) {
|
|
34
36
|
this.isPlaywright = isPlaywright;
|
|
35
37
|
}
|
|
@@ -40,30 +42,32 @@ var ContractReporter = class {
|
|
|
40
42
|
this.startTime = Date.now();
|
|
41
43
|
this.componentName = componentName;
|
|
42
44
|
this.totalTests = totalTests;
|
|
45
|
+
this.hasPrintedStaticSection = false;
|
|
46
|
+
this.hasPrintedDynamicSection = false;
|
|
43
47
|
if (apgUrl) {
|
|
44
48
|
this.apgUrl = apgUrl;
|
|
45
49
|
}
|
|
46
50
|
const mode = this.isPlaywright ? "Playwright (Real Browser)" : "jsdom (Fast)";
|
|
47
51
|
this.log(`
|
|
48
52
|
${"\u2550".repeat(60)}`);
|
|
49
|
-
this.log(`\u{1F50D} Testing ${componentName} Component - ${mode}`);
|
|
53
|
+
this.log(`\u{1F50D} Testing ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} Component - ${mode}`);
|
|
50
54
|
this.log(`${"\u2550".repeat(60)}
|
|
51
55
|
`);
|
|
52
56
|
}
|
|
53
57
|
reportStatic(passes, failures) {
|
|
54
58
|
this.staticPasses = passes;
|
|
55
59
|
this.staticFailures = failures;
|
|
56
|
-
const icon = failures === 0 ? "\u2705" : "\u274C";
|
|
57
|
-
const status = failures === 0 ? "PASS" : "FAIL";
|
|
58
|
-
this.log("");
|
|
59
|
-
this.log(`${icon} Static ARIA Tests: ${status}`);
|
|
60
|
-
this.log(` ${passes}/${passes + failures} required attributes present
|
|
61
|
-
`);
|
|
62
60
|
}
|
|
63
61
|
/**
|
|
64
62
|
* Report individual static test pass
|
|
65
63
|
*/
|
|
66
64
|
reportStaticTest(description, passed, failureMessage) {
|
|
65
|
+
if (!this.hasPrintedStaticSection) {
|
|
66
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
67
|
+
this.log(`\u{1F9EA} Static Assertions`);
|
|
68
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
69
|
+
this.hasPrintedStaticSection = true;
|
|
70
|
+
}
|
|
67
71
|
const icon = passed ? "\u2713" : "\u2717";
|
|
68
72
|
this.log(` ${icon} ${description}`);
|
|
69
73
|
if (!passed && failureMessage) {
|
|
@@ -74,6 +78,13 @@ ${"\u2550".repeat(60)}`);
|
|
|
74
78
|
* Report individual dynamic test result
|
|
75
79
|
*/
|
|
76
80
|
reportTest(test, status, failureMessage) {
|
|
81
|
+
if (!this.hasPrintedDynamicSection) {
|
|
82
|
+
this.log("");
|
|
83
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
84
|
+
this.log(`\u2328\uFE0F Dynamic Interaction Tests`);
|
|
85
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
86
|
+
this.hasPrintedDynamicSection = true;
|
|
87
|
+
}
|
|
77
88
|
const result = {
|
|
78
89
|
description: test.description,
|
|
79
90
|
status,
|
|
@@ -160,7 +171,7 @@ ${"\u2500".repeat(60)}`);
|
|
|
160
171
|
});
|
|
161
172
|
this.log(`
|
|
162
173
|
\u{1F4A1} Run with Playwright for full validation:`);
|
|
163
|
-
this.log(` testUiComponent('${this.componentName}',
|
|
174
|
+
this.log(` testUiComponent('${this.componentName}', null, 'http://localhost:5173/test-harness?component=component_name')
|
|
164
175
|
`);
|
|
165
176
|
}
|
|
166
177
|
/**
|
|
@@ -184,9 +195,14 @@ ${"\u2500".repeat(60)}`);
|
|
|
184
195
|
${"\u2550".repeat(60)}`);
|
|
185
196
|
this.log(`\u{1F4CA} Summary
|
|
186
197
|
`);
|
|
198
|
+
const staticIcon = this.staticFailures === 0 ? "\u2705" : "\u274C";
|
|
199
|
+
const staticStatus = this.staticFailures === 0 ? "PASS" : "FAIL";
|
|
200
|
+
this.log(`${staticIcon} Static ARIA Tests: ${staticStatus}`);
|
|
201
|
+
this.log(` ${this.staticPasses}/${this.staticPasses + this.staticFailures} required attributes present`);
|
|
202
|
+
this.log("");
|
|
187
203
|
if (totalFailures === 0 && this.skipped === 0 && this.optionalSuggestions === 0) {
|
|
188
204
|
this.log(`\u2705 All ${totalRun} tests passed!`);
|
|
189
|
-
this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
205
|
+
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
190
206
|
} else if (totalFailures === 0) {
|
|
191
207
|
this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
|
|
192
208
|
if (this.skipped > 0) {
|
|
@@ -195,7 +211,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
195
211
|
if (this.optionalSuggestions > 0) {
|
|
196
212
|
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
197
213
|
}
|
|
198
|
-
this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
214
|
+
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
199
215
|
} else {
|
|
200
216
|
this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
|
|
201
217
|
this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);
|
|
@@ -239,7 +255,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
239
255
|
}
|
|
240
256
|
};
|
|
241
257
|
|
|
242
|
-
// src/utils/test/
|
|
258
|
+
// src/utils/test/src/playwrightTestHarness.ts
|
|
243
259
|
import { chromium } from "playwright";
|
|
244
260
|
var sharedBrowser = null;
|
|
245
261
|
var sharedContext = null;
|
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
ContractReporter,
|
|
3
3
|
contract_default,
|
|
4
4
|
createTestPage
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AUJAN4RK.js";
|
|
6
6
|
import {
|
|
7
7
|
__export,
|
|
8
8
|
__reExport
|
|
9
9
|
} from "./chunk-I2KLQ2HA.js";
|
|
10
10
|
|
|
11
|
-
// src/utils/test/
|
|
11
|
+
// src/utils/test/src/contractTestRunnerPlaywright.ts
|
|
12
12
|
import { readFileSync as readFileSync2 } from "fs";
|
|
13
13
|
|
|
14
14
|
// node_modules/@playwright/test/index.mjs
|
|
@@ -233,7 +233,7 @@ var ComponentDetector = class {
|
|
|
233
233
|
const contractData = readFileSync(resolvedPath, "utf-8");
|
|
234
234
|
const componentContract = JSON.parse(contractData);
|
|
235
235
|
const selectors = componentContract.selectors;
|
|
236
|
-
if (componentName
|
|
236
|
+
if (componentName.includes("combobox")) {
|
|
237
237
|
const mainSelector = selectors.input || selectors.container;
|
|
238
238
|
return new ComboboxComponentStrategy(mainSelector, selectors, actionTimeoutMs, assertionTimeoutMs);
|
|
239
239
|
}
|
|
@@ -732,7 +732,7 @@ var AssertionRunner = class {
|
|
|
732
732
|
}
|
|
733
733
|
};
|
|
734
734
|
|
|
735
|
-
// src/utils/test/
|
|
735
|
+
// src/utils/test/src/contractTestRunnerPlaywright.ts
|
|
736
736
|
async function runContractTestsPlaywright(componentName, url) {
|
|
737
737
|
const reporter = new ContractReporter(true);
|
|
738
738
|
const actionTimeoutMs = 400;
|
|
@@ -772,7 +772,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
772
772
|
throw new Error(`CRITICAL: No selector found in contract for ${componentName}`);
|
|
773
773
|
}
|
|
774
774
|
try {
|
|
775
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout:
|
|
775
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
776
776
|
} catch (error) {
|
|
777
777
|
throw new Error(
|
|
778
778
|
`
|
|
@@ -787,24 +787,29 @@ This usually means:
|
|
|
787
787
|
reporter.start(componentName, totalTests, apgUrl);
|
|
788
788
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
789
789
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
790
|
-
state: "
|
|
790
|
+
state: "attached",
|
|
791
791
|
timeout: 5e3
|
|
792
792
|
}).catch(() => {
|
|
793
|
-
console.warn("Menu trigger not visible, continuing with tests...");
|
|
794
793
|
});
|
|
795
794
|
}
|
|
795
|
+
const failuresBeforeStatic = failures.length;
|
|
796
796
|
const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
797
797
|
for (const test of componentContract.static[0]?.assertions || []) {
|
|
798
798
|
if (test.target === "relative") continue;
|
|
799
|
+
const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
|
|
799
800
|
const targetSelector = componentContract.selectors[test.target];
|
|
800
801
|
if (!targetSelector) {
|
|
801
|
-
|
|
802
|
+
const failure = `Selector for target ${test.target} not found.`;
|
|
803
|
+
failures.push(failure);
|
|
804
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
802
805
|
continue;
|
|
803
806
|
}
|
|
804
807
|
const target = page.locator(targetSelector).first();
|
|
805
808
|
const exists = await target.count() > 0;
|
|
806
809
|
if (!exists) {
|
|
807
|
-
|
|
810
|
+
const failure = `Target ${test.target} not found.`;
|
|
811
|
+
failures.push(failure);
|
|
812
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
808
813
|
continue;
|
|
809
814
|
}
|
|
810
815
|
const isRedundantCheck = (selector, attrName, expectedVal) => {
|
|
@@ -838,13 +843,19 @@ This usually means:
|
|
|
838
843
|
}
|
|
839
844
|
}
|
|
840
845
|
if (!hasAny && !allRedundant) {
|
|
841
|
-
|
|
846
|
+
const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
|
|
847
|
+
failures.push(failure);
|
|
848
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
842
849
|
} else if (!allRedundant && hasAny) {
|
|
843
850
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
851
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
852
|
+
} else {
|
|
853
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
844
854
|
}
|
|
845
855
|
} else {
|
|
846
856
|
if (isRedundantCheck(targetSelector, test.attribute, test.expectedValue)) {
|
|
847
857
|
passes.push(`${test.attribute}="${test.expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
|
|
858
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
848
859
|
} else {
|
|
849
860
|
const result = await staticAssertionRunner.validateAttribute(
|
|
850
861
|
target,
|
|
@@ -856,8 +867,10 @@ This usually means:
|
|
|
856
867
|
);
|
|
857
868
|
if (result.success && result.passMessage) {
|
|
858
869
|
passes.push(result.passMessage);
|
|
870
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
859
871
|
} else if (!result.success && result.failMessage) {
|
|
860
872
|
failures.push(result.failMessage);
|
|
873
|
+
reporter.reportStaticTest(staticDescription, false, result.failMessage);
|
|
861
874
|
}
|
|
862
875
|
}
|
|
863
876
|
}
|
|
@@ -936,8 +949,9 @@ This usually means:
|
|
|
936
949
|
reporter.reportTest(dynamicTest, testPassed ? "pass" : "fail", failureMessage);
|
|
937
950
|
}
|
|
938
951
|
}
|
|
939
|
-
const
|
|
940
|
-
const staticFailed =
|
|
952
|
+
const staticTotal = componentContract.static[0].assertions.length;
|
|
953
|
+
const staticFailed = failures.length - failuresBeforeStatic;
|
|
954
|
+
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
941
955
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
942
956
|
reporter.summary(failures);
|
|
943
957
|
} catch (error) {
|
package/dist/index.cjs
CHANGED
|
@@ -37,29 +37,29 @@ var init_contract = __esm({
|
|
|
37
37
|
"src/utils/test/contract/contract.json"() {
|
|
38
38
|
contract_default = {
|
|
39
39
|
menu: {
|
|
40
|
-
path: "./contracts/
|
|
40
|
+
path: "./aria-contracts/menu/menu.contract.json",
|
|
41
41
|
component: "menu"
|
|
42
42
|
},
|
|
43
|
-
combobox: {
|
|
44
|
-
path: "./contracts/
|
|
45
|
-
component: "combobox"
|
|
43
|
+
"combobox.listbox": {
|
|
44
|
+
path: "./aria-contracts/combobox/combobox.listbox.contract.json",
|
|
45
|
+
component: "combobox.listbox"
|
|
46
46
|
},
|
|
47
47
|
accordion: {
|
|
48
|
-
path: "./contracts/
|
|
48
|
+
path: "./aria-contracts/accordion/accordion.contract.json",
|
|
49
49
|
component: "accordion"
|
|
50
50
|
},
|
|
51
51
|
tabs: {
|
|
52
|
-
path: "./contracts/
|
|
52
|
+
path: "./aria-contracts/tabs/tabs.contract.json",
|
|
53
53
|
component: "tabs"
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
// src/utils/test/
|
|
59
|
+
// src/utils/test/src/ContractReporter.ts
|
|
60
60
|
var ContractReporter;
|
|
61
61
|
var init_ContractReporter = __esm({
|
|
62
|
-
"src/utils/test/
|
|
62
|
+
"src/utils/test/src/ContractReporter.ts"() {
|
|
63
63
|
"use strict";
|
|
64
64
|
ContractReporter = class {
|
|
65
65
|
startTime = 0;
|
|
@@ -72,6 +72,8 @@ var init_ContractReporter = __esm({
|
|
|
72
72
|
optionalSuggestions = 0;
|
|
73
73
|
isPlaywright = false;
|
|
74
74
|
apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
|
|
75
|
+
hasPrintedStaticSection = false;
|
|
76
|
+
hasPrintedDynamicSection = false;
|
|
75
77
|
constructor(isPlaywright = false) {
|
|
76
78
|
this.isPlaywright = isPlaywright;
|
|
77
79
|
}
|
|
@@ -82,30 +84,32 @@ var init_ContractReporter = __esm({
|
|
|
82
84
|
this.startTime = Date.now();
|
|
83
85
|
this.componentName = componentName;
|
|
84
86
|
this.totalTests = totalTests;
|
|
87
|
+
this.hasPrintedStaticSection = false;
|
|
88
|
+
this.hasPrintedDynamicSection = false;
|
|
85
89
|
if (apgUrl) {
|
|
86
90
|
this.apgUrl = apgUrl;
|
|
87
91
|
}
|
|
88
92
|
const mode = this.isPlaywright ? "Playwright (Real Browser)" : "jsdom (Fast)";
|
|
89
93
|
this.log(`
|
|
90
94
|
${"\u2550".repeat(60)}`);
|
|
91
|
-
this.log(`\u{1F50D} Testing ${componentName} Component - ${mode}`);
|
|
95
|
+
this.log(`\u{1F50D} Testing ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} Component - ${mode}`);
|
|
92
96
|
this.log(`${"\u2550".repeat(60)}
|
|
93
97
|
`);
|
|
94
98
|
}
|
|
95
99
|
reportStatic(passes, failures) {
|
|
96
100
|
this.staticPasses = passes;
|
|
97
101
|
this.staticFailures = failures;
|
|
98
|
-
const icon = failures === 0 ? "\u2705" : "\u274C";
|
|
99
|
-
const status = failures === 0 ? "PASS" : "FAIL";
|
|
100
|
-
this.log("");
|
|
101
|
-
this.log(`${icon} Static ARIA Tests: ${status}`);
|
|
102
|
-
this.log(` ${passes}/${passes + failures} required attributes present
|
|
103
|
-
`);
|
|
104
102
|
}
|
|
105
103
|
/**
|
|
106
104
|
* Report individual static test pass
|
|
107
105
|
*/
|
|
108
106
|
reportStaticTest(description, passed, failureMessage) {
|
|
107
|
+
if (!this.hasPrintedStaticSection) {
|
|
108
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
109
|
+
this.log(`\u{1F9EA} Static Assertions`);
|
|
110
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
111
|
+
this.hasPrintedStaticSection = true;
|
|
112
|
+
}
|
|
109
113
|
const icon = passed ? "\u2713" : "\u2717";
|
|
110
114
|
this.log(` ${icon} ${description}`);
|
|
111
115
|
if (!passed && failureMessage) {
|
|
@@ -116,6 +120,13 @@ ${"\u2550".repeat(60)}`);
|
|
|
116
120
|
* Report individual dynamic test result
|
|
117
121
|
*/
|
|
118
122
|
reportTest(test, status, failureMessage) {
|
|
123
|
+
if (!this.hasPrintedDynamicSection) {
|
|
124
|
+
this.log("");
|
|
125
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
126
|
+
this.log(`\u2328\uFE0F Dynamic Interaction Tests`);
|
|
127
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
128
|
+
this.hasPrintedDynamicSection = true;
|
|
129
|
+
}
|
|
119
130
|
const result = {
|
|
120
131
|
description: test.description,
|
|
121
132
|
status,
|
|
@@ -202,7 +213,7 @@ ${"\u2500".repeat(60)}`);
|
|
|
202
213
|
});
|
|
203
214
|
this.log(`
|
|
204
215
|
\u{1F4A1} Run with Playwright for full validation:`);
|
|
205
|
-
this.log(` testUiComponent('${this.componentName}',
|
|
216
|
+
this.log(` testUiComponent('${this.componentName}', null, 'http://localhost:5173/test-harness?component=component_name')
|
|
206
217
|
`);
|
|
207
218
|
}
|
|
208
219
|
/**
|
|
@@ -226,9 +237,14 @@ ${"\u2500".repeat(60)}`);
|
|
|
226
237
|
${"\u2550".repeat(60)}`);
|
|
227
238
|
this.log(`\u{1F4CA} Summary
|
|
228
239
|
`);
|
|
240
|
+
const staticIcon = this.staticFailures === 0 ? "\u2705" : "\u274C";
|
|
241
|
+
const staticStatus = this.staticFailures === 0 ? "PASS" : "FAIL";
|
|
242
|
+
this.log(`${staticIcon} Static ARIA Tests: ${staticStatus}`);
|
|
243
|
+
this.log(` ${this.staticPasses}/${this.staticPasses + this.staticFailures} required attributes present`);
|
|
244
|
+
this.log("");
|
|
229
245
|
if (totalFailures === 0 && this.skipped === 0 && this.optionalSuggestions === 0) {
|
|
230
246
|
this.log(`\u2705 All ${totalRun} tests passed!`);
|
|
231
|
-
this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
247
|
+
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
232
248
|
} else if (totalFailures === 0) {
|
|
233
249
|
this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
|
|
234
250
|
if (this.skipped > 0) {
|
|
@@ -237,7 +253,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
237
253
|
if (this.optionalSuggestions > 0) {
|
|
238
254
|
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
239
255
|
}
|
|
240
|
-
this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
256
|
+
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
241
257
|
} else {
|
|
242
258
|
this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
|
|
243
259
|
this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);
|
|
@@ -283,7 +299,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
283
299
|
}
|
|
284
300
|
});
|
|
285
301
|
|
|
286
|
-
// src/utils/test/
|
|
302
|
+
// src/utils/test/src/playwrightTestHarness.ts
|
|
287
303
|
async function getOrCreateBrowser() {
|
|
288
304
|
if (!sharedBrowser) {
|
|
289
305
|
sharedBrowser = await import_playwright.chromium.launch({
|
|
@@ -325,7 +341,7 @@ async function closeSharedBrowser() {
|
|
|
325
341
|
}
|
|
326
342
|
var import_playwright, sharedBrowser, sharedContext;
|
|
327
343
|
var init_playwrightTestHarness = __esm({
|
|
328
|
-
"src/utils/test/
|
|
344
|
+
"src/utils/test/src/playwrightTestHarness.ts"() {
|
|
329
345
|
"use strict";
|
|
330
346
|
import_playwright = require("playwright");
|
|
331
347
|
sharedBrowser = null;
|
|
@@ -597,7 +613,7 @@ var init_ComponentDetector = __esm({
|
|
|
597
613
|
const contractData = (0, import_fs.readFileSync)(resolvedPath, "utf-8");
|
|
598
614
|
const componentContract = JSON.parse(contractData);
|
|
599
615
|
const selectors = componentContract.selectors;
|
|
600
|
-
if (componentName
|
|
616
|
+
if (componentName.includes("combobox")) {
|
|
601
617
|
const mainSelector = selectors.input || selectors.container;
|
|
602
618
|
return new ComboboxComponentStrategy(mainSelector, selectors, actionTimeoutMs, assertionTimeoutMs);
|
|
603
619
|
}
|
|
@@ -1119,7 +1135,7 @@ var init_AssertionRunner = __esm({
|
|
|
1119
1135
|
}
|
|
1120
1136
|
});
|
|
1121
1137
|
|
|
1122
|
-
// src/utils/test/
|
|
1138
|
+
// src/utils/test/src/contractTestRunnerPlaywright.ts
|
|
1123
1139
|
var contractTestRunnerPlaywright_exports = {};
|
|
1124
1140
|
__export(contractTestRunnerPlaywright_exports, {
|
|
1125
1141
|
runContractTestsPlaywright: () => runContractTestsPlaywright
|
|
@@ -1163,7 +1179,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
1163
1179
|
throw new Error(`CRITICAL: No selector found in contract for ${componentName}`);
|
|
1164
1180
|
}
|
|
1165
1181
|
try {
|
|
1166
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout:
|
|
1182
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
1167
1183
|
} catch (error) {
|
|
1168
1184
|
throw new Error(
|
|
1169
1185
|
`
|
|
@@ -1178,24 +1194,29 @@ This usually means:
|
|
|
1178
1194
|
reporter.start(componentName, totalTests, apgUrl);
|
|
1179
1195
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
1180
1196
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
1181
|
-
state: "
|
|
1197
|
+
state: "attached",
|
|
1182
1198
|
timeout: 5e3
|
|
1183
1199
|
}).catch(() => {
|
|
1184
|
-
console.warn("Menu trigger not visible, continuing with tests...");
|
|
1185
1200
|
});
|
|
1186
1201
|
}
|
|
1202
|
+
const failuresBeforeStatic = failures.length;
|
|
1187
1203
|
const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
1188
1204
|
for (const test of componentContract.static[0]?.assertions || []) {
|
|
1189
1205
|
if (test.target === "relative") continue;
|
|
1206
|
+
const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
|
|
1190
1207
|
const targetSelector = componentContract.selectors[test.target];
|
|
1191
1208
|
if (!targetSelector) {
|
|
1192
|
-
|
|
1209
|
+
const failure = `Selector for target ${test.target} not found.`;
|
|
1210
|
+
failures.push(failure);
|
|
1211
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1193
1212
|
continue;
|
|
1194
1213
|
}
|
|
1195
1214
|
const target = page.locator(targetSelector).first();
|
|
1196
1215
|
const exists = await target.count() > 0;
|
|
1197
1216
|
if (!exists) {
|
|
1198
|
-
|
|
1217
|
+
const failure = `Target ${test.target} not found.`;
|
|
1218
|
+
failures.push(failure);
|
|
1219
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1199
1220
|
continue;
|
|
1200
1221
|
}
|
|
1201
1222
|
const isRedundantCheck = (selector, attrName, expectedVal) => {
|
|
@@ -1229,13 +1250,19 @@ This usually means:
|
|
|
1229
1250
|
}
|
|
1230
1251
|
}
|
|
1231
1252
|
if (!hasAny && !allRedundant) {
|
|
1232
|
-
|
|
1253
|
+
const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
|
|
1254
|
+
failures.push(failure);
|
|
1255
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1233
1256
|
} else if (!allRedundant && hasAny) {
|
|
1234
1257
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
1258
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1259
|
+
} else {
|
|
1260
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1235
1261
|
}
|
|
1236
1262
|
} else {
|
|
1237
1263
|
if (isRedundantCheck(targetSelector, test.attribute, test.expectedValue)) {
|
|
1238
1264
|
passes.push(`${test.attribute}="${test.expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
|
|
1265
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1239
1266
|
} else {
|
|
1240
1267
|
const result = await staticAssertionRunner.validateAttribute(
|
|
1241
1268
|
target,
|
|
@@ -1247,8 +1274,10 @@ This usually means:
|
|
|
1247
1274
|
);
|
|
1248
1275
|
if (result.success && result.passMessage) {
|
|
1249
1276
|
passes.push(result.passMessage);
|
|
1277
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1250
1278
|
} else if (!result.success && result.failMessage) {
|
|
1251
1279
|
failures.push(result.failMessage);
|
|
1280
|
+
reporter.reportStaticTest(staticDescription, false, result.failMessage);
|
|
1252
1281
|
}
|
|
1253
1282
|
}
|
|
1254
1283
|
}
|
|
@@ -1327,8 +1356,9 @@ This usually means:
|
|
|
1327
1356
|
reporter.reportTest(dynamicTest, testPassed ? "pass" : "fail", failureMessage);
|
|
1328
1357
|
}
|
|
1329
1358
|
}
|
|
1330
|
-
const
|
|
1331
|
-
const staticFailed =
|
|
1359
|
+
const staticTotal = componentContract.static[0].assertions.length;
|
|
1360
|
+
const staticFailed = failures.length - failuresBeforeStatic;
|
|
1361
|
+
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
1332
1362
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
1333
1363
|
reporter.summary(failures);
|
|
1334
1364
|
} catch (error) {
|
|
@@ -1358,7 +1388,7 @@ Make sure your dev server is running at ${url}`);
|
|
|
1358
1388
|
}
|
|
1359
1389
|
var import_fs2, import_meta3;
|
|
1360
1390
|
var init_contractTestRunnerPlaywright = __esm({
|
|
1361
|
-
"src/utils/test/
|
|
1391
|
+
"src/utils/test/src/contractTestRunnerPlaywright.ts"() {
|
|
1362
1392
|
"use strict";
|
|
1363
1393
|
import_fs2 = require("fs");
|
|
1364
1394
|
init_contract();
|
|
@@ -2035,6 +2065,13 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2035
2065
|
};
|
|
2036
2066
|
return nodeListLike;
|
|
2037
2067
|
}
|
|
2068
|
+
function intializeMenuItems() {
|
|
2069
|
+
const items = getItems();
|
|
2070
|
+
items.forEach((item) => {
|
|
2071
|
+
item.setAttribute("role", "menuitem");
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
intializeMenuItems();
|
|
2038
2075
|
function isItemInNestedSubmenu(item) {
|
|
2039
2076
|
let parent = item.parentElement;
|
|
2040
2077
|
while (parent && parent !== menuDiv) {
|
|
@@ -2147,13 +2184,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2147
2184
|
}
|
|
2148
2185
|
}
|
|
2149
2186
|
}
|
|
2150
|
-
function intializeMenuItems() {
|
|
2151
|
-
const items = getItems();
|
|
2152
|
-
items.forEach((item) => {
|
|
2153
|
-
item.setAttribute("role", "menuitem");
|
|
2154
|
-
});
|
|
2155
|
-
}
|
|
2156
|
-
intializeMenuItems();
|
|
2157
2187
|
function handleTriggerClick() {
|
|
2158
2188
|
const isOpen = triggerButton.getAttribute("aria-expanded") === "true";
|
|
2159
2189
|
if (isOpen) {
|
|
@@ -2929,7 +2959,7 @@ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation
|
|
|
2929
2959
|
// src/utils/test/src/test.ts
|
|
2930
2960
|
var import_jest_axe = require("jest-axe");
|
|
2931
2961
|
|
|
2932
|
-
// src/utils/test/
|
|
2962
|
+
// src/utils/test/src/contractTestRunner.ts
|
|
2933
2963
|
init_contract();
|
|
2934
2964
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
2935
2965
|
init_ContractReporter();
|
|
@@ -2949,6 +2979,7 @@ async function runContractTests(componentName, component) {
|
|
|
2949
2979
|
const failures = [];
|
|
2950
2980
|
const passes = [];
|
|
2951
2981
|
const skipped = [];
|
|
2982
|
+
const failuresBeforeStatic = failures.length;
|
|
2952
2983
|
for (const test of componentContract.static[0].assertions) {
|
|
2953
2984
|
if (test.target !== "relative") {
|
|
2954
2985
|
const selector = componentContract.selectors[test.target];
|
|
@@ -2987,8 +3018,9 @@ async function runContractTests(componentName, component) {
|
|
|
2987
3018
|
skipped.push(dynamicTest.description);
|
|
2988
3019
|
reporter.reportTest(dynamicTest, "skip");
|
|
2989
3020
|
}
|
|
2990
|
-
const
|
|
2991
|
-
const staticFailed =
|
|
3021
|
+
const staticTotal = componentContract.static[0].assertions.length;
|
|
3022
|
+
const staticFailed = failures.length - failuresBeforeStatic;
|
|
3023
|
+
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
2992
3024
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
2993
3025
|
reporter.summary(failures);
|
|
2994
3026
|
return { passes, failures, skipped };
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
ContractReporter,
|
|
3
3
|
closeSharedBrowser,
|
|
4
4
|
contract_default
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AUJAN4RK.js";
|
|
6
6
|
import "./chunk-I2KLQ2HA.js";
|
|
7
7
|
|
|
8
8
|
// src/accordion/src/makeAccordionAccessible/makeAccordionAccessible.ts
|
|
@@ -539,6 +539,13 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
539
539
|
};
|
|
540
540
|
return nodeListLike;
|
|
541
541
|
}
|
|
542
|
+
function intializeMenuItems() {
|
|
543
|
+
const items = getItems();
|
|
544
|
+
items.forEach((item) => {
|
|
545
|
+
item.setAttribute("role", "menuitem");
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
intializeMenuItems();
|
|
542
549
|
function isItemInNestedSubmenu(item) {
|
|
543
550
|
let parent = item.parentElement;
|
|
544
551
|
while (parent && parent !== menuDiv) {
|
|
@@ -651,13 +658,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
651
658
|
}
|
|
652
659
|
}
|
|
653
660
|
}
|
|
654
|
-
function intializeMenuItems() {
|
|
655
|
-
const items = getItems();
|
|
656
|
-
items.forEach((item) => {
|
|
657
|
-
item.setAttribute("role", "menuitem");
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
intializeMenuItems();
|
|
661
661
|
function handleTriggerClick() {
|
|
662
662
|
const isOpen = triggerButton.getAttribute("aria-expanded") === "true";
|
|
663
663
|
if (isOpen) {
|
|
@@ -1433,7 +1433,7 @@ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation
|
|
|
1433
1433
|
// src/utils/test/src/test.ts
|
|
1434
1434
|
import { axe } from "jest-axe";
|
|
1435
1435
|
|
|
1436
|
-
// src/utils/test/
|
|
1436
|
+
// src/utils/test/src/contractTestRunner.ts
|
|
1437
1437
|
import fs from "fs/promises";
|
|
1438
1438
|
async function runContractTests(componentName, component) {
|
|
1439
1439
|
const reporter = new ContractReporter(false);
|
|
@@ -1450,6 +1450,7 @@ async function runContractTests(componentName, component) {
|
|
|
1450
1450
|
const failures = [];
|
|
1451
1451
|
const passes = [];
|
|
1452
1452
|
const skipped = [];
|
|
1453
|
+
const failuresBeforeStatic = failures.length;
|
|
1453
1454
|
for (const test of componentContract.static[0].assertions) {
|
|
1454
1455
|
if (test.target !== "relative") {
|
|
1455
1456
|
const selector = componentContract.selectors[test.target];
|
|
@@ -1488,8 +1489,9 @@ async function runContractTests(componentName, component) {
|
|
|
1488
1489
|
skipped.push(dynamicTest.description);
|
|
1489
1490
|
reporter.reportTest(dynamicTest, "skip");
|
|
1490
1491
|
}
|
|
1491
|
-
const
|
|
1492
|
-
const staticFailed =
|
|
1492
|
+
const staticTotal = componentContract.static[0].assertions.length;
|
|
1493
|
+
const staticFailed = failures.length - failuresBeforeStatic;
|
|
1494
|
+
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
1493
1495
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
1494
1496
|
reporter.summary(failures);
|
|
1495
1497
|
return { passes, failures, skipped };
|
|
@@ -1539,7 +1541,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
1539
1541
|
const devServerUrl = await checkDevServer(url);
|
|
1540
1542
|
if (devServerUrl) {
|
|
1541
1543
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
1542
|
-
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-
|
|
1544
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-7F756CFB.js");
|
|
1543
1545
|
contract = await runContractTestsPlaywright(componentName, devServerUrl);
|
|
1544
1546
|
} else {
|
|
1545
1547
|
throw new Error(
|
|
@@ -88,4 +88,4 @@ interface MenuCallback {
|
|
|
88
88
|
onOpenChange?: (isOpen: boolean) => void;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export type {
|
|
91
|
+
export type { AccessibilityInstance as A, ComboboxConfig as C, MenuConfig as M, TabsConfig as T, AccordionConfig as a };
|
|
@@ -88,4 +88,4 @@ interface MenuCallback {
|
|
|
88
88
|
onOpenChange?: (isOpen: boolean) => void;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export type {
|
|
91
|
+
export type { AccessibilityInstance as A, ComboboxConfig as C, MenuConfig as M, TabsConfig as T, AccordionConfig as a };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as AccordionConfig, A as AccessibilityInstance } from '../Types.d-yGC2bBaB.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Makes an accordion accessible by managing ARIA attributes, keyboard interaction, and state.
|