aria-ease 6.6.0 → 6.7.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 +75 -15
- package/bin/{chunk-LKN5PRYD.js → chunk-2TOYEY5L.js} +87 -35
- package/bin/chunk-VPBHLMAS.js +127 -0
- package/bin/cli.cjs +380 -231
- package/bin/cli.js +8 -123
- package/bin/configLoader-XRF6VM4J.js +7 -0
- package/{dist/contractTestRunnerPlaywright-PC6JOYYV.js → bin/contractTestRunnerPlaywright-UAOFNS7Z.js} +98 -59
- package/bin/{test-LP723IXM.js → test-WRIJHN6H.js} +65 -24
- package/dist/{chunk-LKN5PRYD.js → chunk-2TOYEY5L.js} +87 -35
- package/dist/configLoader-IT4PWCJB.js +128 -0
- package/{bin/contractTestRunnerPlaywright-PC6JOYYV.js → dist/contractTestRunnerPlaywright-UAOFNS7Z.js} +98 -59
- package/dist/index.cjs +404 -125
- package/dist/index.d.cts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +83 -29
- package/dist/src/menu/index.cjs +18 -5
- package/dist/src/menu/index.js +18 -5
- package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +8 -8
- package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +4 -4
- package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +44 -19
- package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +3 -3
- package/dist/src/utils/test/{chunk-LKN5PRYD.js → chunk-2TOYEY5L.js} +85 -36
- package/dist/src/utils/test/configLoader-LD4RV2WQ.js +126 -0
- package/dist/src/utils/test/{contractTestRunnerPlaywright-RGKMGXND.js → contractTestRunnerPlaywright-IRJOAEMT.js} +94 -58
- package/dist/src/utils/test/index.cjs +380 -119
- package/dist/src/utils/test/index.d.cts +7 -1
- package/dist/src/utils/test/index.d.ts +7 -1
- package/dist/src/utils/test/index.js +61 -23
- package/package.json +1 -1
|
@@ -7,8 +7,11 @@ interface ContractTestResult {
|
|
|
7
7
|
passes: string[];
|
|
8
8
|
failures: string[];
|
|
9
9
|
skipped: string[];
|
|
10
|
+
warnings?: string[];
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
type StrictnessMode = 'minimal' | 'balanced' | 'strict' | 'paranoid';
|
|
14
|
+
|
|
12
15
|
/**
|
|
13
16
|
* Runs static and interactions accessibility test on UI components.
|
|
14
17
|
* @param {string} componentName The name of the component contract to test against
|
|
@@ -16,7 +19,10 @@ interface ContractTestResult {
|
|
|
16
19
|
* @param {string} url Optional URL to run full Playwright E2E tests. If omitted, uses isolated component testing with page.setContent()
|
|
17
20
|
*/
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
type TestAuditOptions = {
|
|
23
|
+
strictness?: StrictnessMode;
|
|
24
|
+
};
|
|
25
|
+
declare function testUiComponent(componentName: string, component: HTMLElement | null, url: string | null, options?: TestAuditOptions): Promise<JestAxeResult>;
|
|
20
26
|
declare let runTest: () => Promise<ContractTestResult>;
|
|
21
27
|
/**
|
|
22
28
|
* Cleanup function to close the shared Playwright browser
|
|
@@ -7,8 +7,11 @@ interface ContractTestResult {
|
|
|
7
7
|
passes: string[];
|
|
8
8
|
failures: string[];
|
|
9
9
|
skipped: string[];
|
|
10
|
+
warnings?: string[];
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
type StrictnessMode = 'minimal' | 'balanced' | 'strict' | 'paranoid';
|
|
14
|
+
|
|
12
15
|
/**
|
|
13
16
|
* Runs static and interactions accessibility test on UI components.
|
|
14
17
|
* @param {string} componentName The name of the component contract to test against
|
|
@@ -16,7 +19,10 @@ interface ContractTestResult {
|
|
|
16
19
|
* @param {string} url Optional URL to run full Playwright E2E tests. If omitted, uses isolated component testing with page.setContent()
|
|
17
20
|
*/
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
type TestAuditOptions = {
|
|
23
|
+
strictness?: StrictnessMode;
|
|
24
|
+
};
|
|
25
|
+
declare function testUiComponent(componentName: string, component: HTMLElement | null, url: string | null, options?: TestAuditOptions): Promise<JestAxeResult>;
|
|
20
26
|
declare let runTest: () => Promise<ContractTestResult>;
|
|
21
27
|
/**
|
|
22
28
|
* Cleanup function to close the shared Playwright browser
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { closeSharedBrowser, ContractReporter, contract_default } from './chunk-
|
|
1
|
+
import { normalizeStrictness, closeSharedBrowser, ContractReporter, contract_default, normalizeLevel, resolveEnforcement } from './chunk-2TOYEY5L.js';
|
|
2
2
|
import { axe } from 'jest-axe';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
|
|
5
|
-
async function runContractTests(componentName, component) {
|
|
5
|
+
async function runContractTests(componentName, component, strictness) {
|
|
6
6
|
const reporter = new ContractReporter(false);
|
|
7
|
+
const strictnessMode = normalizeStrictness(strictness);
|
|
7
8
|
const contractTyped = contract_default;
|
|
8
9
|
const contractPath = contractTyped[componentName]?.path;
|
|
9
10
|
if (!contractPath) {
|
|
@@ -17,19 +18,42 @@ async function runContractTests(componentName, component) {
|
|
|
17
18
|
const failures = [];
|
|
18
19
|
const passes = [];
|
|
19
20
|
const skipped = [];
|
|
20
|
-
const
|
|
21
|
+
const warnings = [];
|
|
22
|
+
const classifyFailure = (message, levelRaw) => {
|
|
23
|
+
const level = normalizeLevel(levelRaw);
|
|
24
|
+
const enforcement = resolveEnforcement(level, strictnessMode);
|
|
25
|
+
if (enforcement === "error") {
|
|
26
|
+
failures.push(message);
|
|
27
|
+
return { status: "fail", level, detail: message };
|
|
28
|
+
}
|
|
29
|
+
if (enforcement === "warning") {
|
|
30
|
+
warnings.push(message);
|
|
31
|
+
return { status: "warn", level, detail: message };
|
|
32
|
+
}
|
|
33
|
+
const ignoredMessage = `${message} (ignored by strictness=${strictnessMode}, level=${level})`;
|
|
34
|
+
skipped.push(ignoredMessage);
|
|
35
|
+
return { status: "skip", level, detail: ignoredMessage };
|
|
36
|
+
};
|
|
37
|
+
let staticPassed = 0;
|
|
38
|
+
let staticFailed = 0;
|
|
39
|
+
let staticWarnings = 0;
|
|
21
40
|
for (const test of componentContract.static[0].assertions) {
|
|
22
41
|
if (test.target !== "relative") {
|
|
42
|
+
const staticLevel = normalizeLevel(test.level);
|
|
23
43
|
const selector = componentContract.selectors[test.target];
|
|
24
44
|
if (!selector) {
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
const outcome = classifyFailure(`Selector for target ${test.target} not found.`, test.level);
|
|
46
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
47
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
48
|
+
reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
|
|
27
49
|
continue;
|
|
28
50
|
}
|
|
29
51
|
const target = component.querySelector(selector);
|
|
30
52
|
if (!target) {
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
const outcome = classifyFailure(`Target ${test.target} not found.`, test.level);
|
|
54
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
55
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
56
|
+
reporter.reportStaticTest(`${test.target} has required ARIA attributes`, outcome.status, outcome.detail, outcome.level);
|
|
33
57
|
continue;
|
|
34
58
|
}
|
|
35
59
|
const attributeValue = target.getAttribute(test.attribute);
|
|
@@ -37,35 +61,38 @@ async function runContractTests(componentName, component) {
|
|
|
37
61
|
const attributes = test.attribute.split(" | ");
|
|
38
62
|
const hasAnyAttribute = attributes.some((attr) => target.hasAttribute(attr));
|
|
39
63
|
if (!hasAnyAttribute) {
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
const outcome = classifyFailure(test.failureMessage + ` None of the attributes "${test.attribute}" are present.`, test.level);
|
|
65
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
66
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
67
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}`, outcome.status, outcome.detail, outcome.level);
|
|
42
68
|
} else {
|
|
43
69
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
44
|
-
|
|
70
|
+
staticPassed += 1;
|
|
71
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}`, "pass", void 0, staticLevel);
|
|
45
72
|
}
|
|
46
73
|
} else if (!attributeValue || !test.expectedValue.split(" | ").includes(attributeValue)) {
|
|
47
|
-
|
|
48
|
-
|
|
74
|
+
const outcome = classifyFailure(test.failureMessage + ` Attribute value does not match expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`, test.level);
|
|
75
|
+
if (outcome.status === "fail") staticFailed += 1;
|
|
76
|
+
if (outcome.status === "warn") staticWarnings += 1;
|
|
77
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}="${test.expectedValue}"`, outcome.status, outcome.detail, outcome.level);
|
|
49
78
|
} else {
|
|
50
79
|
passes.push(`Attribute value matches expected value. Expected: ${test.expectedValue}, Found: ${attributeValue}`);
|
|
51
|
-
|
|
80
|
+
staticPassed += 1;
|
|
81
|
+
reporter.reportStaticTest(`${test.target} has ${test.attribute}="${attributeValue}"`, "pass", void 0, staticLevel);
|
|
52
82
|
}
|
|
53
83
|
}
|
|
54
84
|
}
|
|
55
85
|
for (const dynamicTest of componentContract.dynamic) {
|
|
56
86
|
skipped.push(dynamicTest.description);
|
|
57
|
-
reporter.reportTest(dynamicTest, "skip");
|
|
87
|
+
reporter.reportTest({ description: dynamicTest.description, level: dynamicTest.level }, "skip");
|
|
58
88
|
}
|
|
59
|
-
|
|
60
|
-
const staticFailed = failures.length - failuresBeforeStatic;
|
|
61
|
-
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
62
|
-
reporter.reportStatic(staticPassed, staticFailed);
|
|
89
|
+
reporter.reportStatic(staticPassed, staticFailed, staticWarnings);
|
|
63
90
|
reporter.summary(failures);
|
|
64
|
-
return { passes, failures, skipped };
|
|
91
|
+
return { passes, failures, skipped, warnings };
|
|
65
92
|
}
|
|
66
93
|
|
|
67
94
|
// src/utils/test/src/test.ts
|
|
68
|
-
async function testUiComponent(componentName, component, url) {
|
|
95
|
+
async function testUiComponent(componentName, component, url, options = {}) {
|
|
69
96
|
if (!componentName || typeof componentName !== "string") {
|
|
70
97
|
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
71
98
|
}
|
|
@@ -102,14 +129,25 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
102
129
|
}
|
|
103
130
|
return null;
|
|
104
131
|
}
|
|
132
|
+
let strictness = normalizeStrictness(options.strictness);
|
|
133
|
+
if (options.strictness === void 0 && typeof window === "undefined") {
|
|
134
|
+
try {
|
|
135
|
+
const { loadConfig } = await import('./configLoader-LD4RV2WQ.js');
|
|
136
|
+
const { config } = await loadConfig(process.cwd());
|
|
137
|
+
const componentStrictness = config.test?.components?.find((comp) => comp?.name === componentName)?.strictness;
|
|
138
|
+
strictness = normalizeStrictness(componentStrictness ?? config.test?.strictness);
|
|
139
|
+
} catch {
|
|
140
|
+
strictness = "balanced";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
105
143
|
let contract;
|
|
106
144
|
try {
|
|
107
145
|
if (url) {
|
|
108
146
|
const devServerUrl = await checkDevServer(url);
|
|
109
147
|
if (devServerUrl) {
|
|
110
148
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
111
|
-
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-
|
|
112
|
-
contract = await runContractTestsPlaywright(componentName, devServerUrl);
|
|
149
|
+
const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-IRJOAEMT.js');
|
|
150
|
+
contract = await runContractTestsPlaywright(componentName, devServerUrl, strictness);
|
|
113
151
|
} else {
|
|
114
152
|
throw new Error(
|
|
115
153
|
`\u274C Dev server not running at ${url}
|
|
@@ -118,7 +156,7 @@ Please start your dev server and try again.`
|
|
|
118
156
|
}
|
|
119
157
|
} else if (component) {
|
|
120
158
|
console.log(`\u{1F3AD} Running component contract tests in JSDOM mode`);
|
|
121
|
-
contract = await runContractTests(componentName, component);
|
|
159
|
+
contract = await runContractTests(componentName, component, strictness);
|
|
122
160
|
} else {
|
|
123
161
|
throw new Error("\u274C Either component or URL must be provided");
|
|
124
162
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aria-ease",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0",
|
|
4
4
|
"description": "Accessibility infrastructure for the entire frontend engineering lifecycle. Build accessible patterns, run automated audits, verify component interactions, and gate deployments — all in one system.",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"type": "module",
|