aria-ease 6.8.0 → 6.9.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 +68 -6
- package/bin/AccordionComponentStrategy-4ZEIQ2V6.js +42 -0
- package/bin/ComboboxComponentStrategy-OGRVZXAF.js +64 -0
- package/bin/MenuComponentStrategy-JAMTCSNF.js +81 -0
- package/bin/TabsComponentStrategy-3SQURPMX.js +29 -0
- package/bin/buildContracts-GBOY7UXG.js +437 -0
- package/bin/{chunk-VPBHLMAS.js → chunk-LMSKLN5O.js} +21 -0
- package/bin/chunk-PK5L2SAF.js +17 -0
- package/bin/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
- package/bin/cli.cjs +991 -128
- package/bin/cli.js +33 -2
- package/bin/{configLoader-XRF6VM4J.js → configLoader-Q6A4JLKW.js} +1 -1
- package/{dist/contractTestRunnerPlaywright-UAOFNS7Z.js → bin/contractTestRunnerPlaywright-ZZNWDUYP.js} +270 -219
- package/bin/{test-WRIJHN6H.js → test-OND56UUL.js} +97 -10
- package/dist/AccordionComponentStrategy-4ZEIQ2V6.js +42 -0
- package/dist/ComboboxComponentStrategy-OGRVZXAF.js +64 -0
- package/dist/MenuComponentStrategy-JAMTCSNF.js +81 -0
- package/dist/TabsComponentStrategy-3SQURPMX.js +29 -0
- package/dist/chunk-PK5L2SAF.js +17 -0
- package/dist/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
- package/dist/{configLoader-IT4PWCJB.js → configLoader-WTGJAP4Z.js} +21 -0
- package/{bin/contractTestRunnerPlaywright-UAOFNS7Z.js → dist/contractTestRunnerPlaywright-XBWJZMR3.js} +270 -219
- package/dist/index.cjs +794 -90
- package/dist/index.d.cts +136 -1
- package/dist/index.d.ts +136 -1
- package/dist/index.js +415 -10
- package/dist/src/utils/test/AccordionComponentStrategy-WRHZOEN6.js +38 -0
- package/dist/src/utils/test/ComboboxComponentStrategy-5AECQSRN.js +60 -0
- package/dist/src/utils/test/MenuComponentStrategy-VKZQYLBE.js +77 -0
- package/dist/src/utils/test/TabsComponentStrategy-BKG53SEV.js +26 -0
- package/dist/src/utils/test/{chunk-2TOYEY5L.js → chunk-XERMSYEH.js} +12 -3
- package/dist/src/utils/test/{configLoader-LD4RV2WQ.js → configLoader-YE2CYGDG.js} +21 -0
- package/dist/src/utils/test/{contractTestRunnerPlaywright-IRJOAEMT.js → contractTestRunnerPlaywright-LC5OAVXB.js} +262 -200
- package/dist/src/utils/test/dsl/index.cjs +320 -0
- package/dist/src/utils/test/dsl/index.d.cts +136 -0
- package/dist/src/utils/test/dsl/index.d.ts +136 -0
- package/dist/src/utils/test/dsl/index.js +318 -0
- package/dist/src/utils/test/index.cjs +472 -88
- package/dist/src/utils/test/index.js +97 -12
- package/package.json +9 -3
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// src/utils/test/dsl/index.ts
|
|
2
|
+
var FluentContract = class {
|
|
3
|
+
constructor(jsonContract) {
|
|
4
|
+
this.jsonContract = jsonContract;
|
|
5
|
+
}
|
|
6
|
+
toJSON() {
|
|
7
|
+
return this.jsonContract;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var StaticTargetBuilder = class {
|
|
11
|
+
constructor(targetName, sink) {
|
|
12
|
+
this.targetName = targetName;
|
|
13
|
+
this.sink = sink;
|
|
14
|
+
}
|
|
15
|
+
has(attribute, expectedValue) {
|
|
16
|
+
const create = (level) => {
|
|
17
|
+
this.sink.push({
|
|
18
|
+
target: this.targetName,
|
|
19
|
+
attribute,
|
|
20
|
+
expectedValue,
|
|
21
|
+
failureMessage: `Expected ${this.targetName} to have ${attribute}${expectedValue !== void 0 ? `=${expectedValue}` : ""}.`,
|
|
22
|
+
level
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
required: () => create("required"),
|
|
27
|
+
recommended: () => create("recommended"),
|
|
28
|
+
optional: () => create("optional")
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var StaticBuilder = class {
|
|
33
|
+
constructor(sink) {
|
|
34
|
+
this.sink = sink;
|
|
35
|
+
}
|
|
36
|
+
target(targetName) {
|
|
37
|
+
return new StaticTargetBuilder(targetName, this.sink);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var DynamicChain = class {
|
|
41
|
+
constructor(key, testsSink, selectors) {
|
|
42
|
+
this.key = key;
|
|
43
|
+
this.testsSink = testsSink;
|
|
44
|
+
this.selectors = selectors;
|
|
45
|
+
}
|
|
46
|
+
selectorTarget = "";
|
|
47
|
+
actions = [];
|
|
48
|
+
assertions = [];
|
|
49
|
+
explicitDescription = "";
|
|
50
|
+
on(target) {
|
|
51
|
+
this.selectorTarget = target;
|
|
52
|
+
this.actions.push({ type: "keypress", target, key: this.key });
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
describe(description) {
|
|
56
|
+
this.explicitDescription = description;
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
focus(targetExpression) {
|
|
60
|
+
const parsed = this.parseRelativeExpression(targetExpression);
|
|
61
|
+
if (parsed) {
|
|
62
|
+
if (!this.selectors[parsed.selectorKey]) {
|
|
63
|
+
const availableSelectors = Object.keys(this.selectors).sort().join(", ") || "(none)";
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Invalid focus target expression "${targetExpression}": selector "${parsed.selectorKey}" is not defined. Available selectors: ${availableSelectors}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (!this.selectors.relative && this.selectors[parsed.selectorKey]) {
|
|
69
|
+
this.selectors.relative = this.selectors[parsed.selectorKey];
|
|
70
|
+
}
|
|
71
|
+
this.assertions.push({
|
|
72
|
+
target: "relative",
|
|
73
|
+
assertion: "toHaveFocus",
|
|
74
|
+
relativeTarget: parsed.relativeTarget
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
this.assertions.push({
|
|
78
|
+
target: targetExpression,
|
|
79
|
+
assertion: "toHaveFocus"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
visible(target) {
|
|
85
|
+
this.assertions.push({ target, assertion: "toBeVisible" });
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
hidden(target) {
|
|
89
|
+
this.assertions.push({ target, assertion: "notToBeVisible" });
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
has(target, attribute, expectedValue) {
|
|
93
|
+
this.assertions.push({
|
|
94
|
+
target,
|
|
95
|
+
assertion: "toHaveAttribute",
|
|
96
|
+
attribute,
|
|
97
|
+
expectedValue
|
|
98
|
+
});
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
required() {
|
|
102
|
+
this.finalize("required");
|
|
103
|
+
}
|
|
104
|
+
recommended() {
|
|
105
|
+
this.finalize("recommended");
|
|
106
|
+
}
|
|
107
|
+
optional() {
|
|
108
|
+
this.finalize("optional");
|
|
109
|
+
}
|
|
110
|
+
finalize(level) {
|
|
111
|
+
if (!this.selectorTarget) {
|
|
112
|
+
throw new Error("Dynamic contract chain requires .on(<selectorKey>) before level terminator.");
|
|
113
|
+
}
|
|
114
|
+
const description = this.explicitDescription || `Pressing ${this.key} on ${this.selectorTarget} satisfies expected behavior.`;
|
|
115
|
+
this.testsSink.push({
|
|
116
|
+
description,
|
|
117
|
+
level,
|
|
118
|
+
action: this.actions,
|
|
119
|
+
assertions: this.assertions.map((a) => ({ ...a, level }))
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
parseRelativeExpression(input) {
|
|
123
|
+
const match = input.match(/^(next|previous|first|last)\(([^)]+)\)$/);
|
|
124
|
+
if (!match) return null;
|
|
125
|
+
const relativeTarget = match[1];
|
|
126
|
+
const selectorKey = match[2].trim();
|
|
127
|
+
return { relativeTarget, selectorKey };
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
var ContractBuilder = class {
|
|
131
|
+
constructor(componentName) {
|
|
132
|
+
this.componentName = componentName;
|
|
133
|
+
}
|
|
134
|
+
metaValue = {};
|
|
135
|
+
selectorsValue = {};
|
|
136
|
+
relationshipInvariants = [];
|
|
137
|
+
staticAssertions = [];
|
|
138
|
+
dynamicTests = [];
|
|
139
|
+
meta(meta) {
|
|
140
|
+
this.metaValue = { ...this.metaValue, ...meta };
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
selectors(selectors) {
|
|
144
|
+
this.selectorsValue = { ...this.selectorsValue, ...selectors };
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
relationship(invariant) {
|
|
148
|
+
this.relationshipInvariants.push(invariant);
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
relationships(builderFn) {
|
|
152
|
+
builderFn({
|
|
153
|
+
ariaReference: (from, attribute, to) => {
|
|
154
|
+
const create = (level) => {
|
|
155
|
+
this.relationshipInvariants.push({
|
|
156
|
+
type: "aria-reference",
|
|
157
|
+
from,
|
|
158
|
+
attribute,
|
|
159
|
+
to,
|
|
160
|
+
level
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
required: () => create("required"),
|
|
165
|
+
recommended: () => create("recommended"),
|
|
166
|
+
optional: () => create("optional")
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
contains: (parent, child) => {
|
|
170
|
+
const create = (level) => {
|
|
171
|
+
this.relationshipInvariants.push({
|
|
172
|
+
type: "contains",
|
|
173
|
+
parent,
|
|
174
|
+
child,
|
|
175
|
+
level
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
return {
|
|
179
|
+
required: () => create("required"),
|
|
180
|
+
recommended: () => create("recommended"),
|
|
181
|
+
optional: () => create("optional")
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
static(builderFn) {
|
|
188
|
+
builderFn(new StaticBuilder(this.staticAssertions));
|
|
189
|
+
return this;
|
|
190
|
+
}
|
|
191
|
+
when(key) {
|
|
192
|
+
return new DynamicChain(key, this.dynamicTests, this.selectorsValue);
|
|
193
|
+
}
|
|
194
|
+
validateRelationshipInvariants() {
|
|
195
|
+
if (this.relationshipInvariants.length === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const selectorKeys = new Set(Object.keys(this.selectorsValue));
|
|
199
|
+
const available = Object.keys(this.selectorsValue).sort().join(", ");
|
|
200
|
+
const errors = [];
|
|
201
|
+
this.relationshipInvariants.forEach((invariant, index) => {
|
|
202
|
+
const prefix = `relationships[${index}] (${invariant.type})`;
|
|
203
|
+
if (invariant.type === "aria-reference") {
|
|
204
|
+
if (!selectorKeys.has(invariant.from)) {
|
|
205
|
+
errors.push(`${prefix}: "from" references unknown selector "${invariant.from}"`);
|
|
206
|
+
}
|
|
207
|
+
if (!selectorKeys.has(invariant.to)) {
|
|
208
|
+
errors.push(`${prefix}: "to" references unknown selector "${invariant.to}"`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (invariant.type === "contains") {
|
|
212
|
+
if (!selectorKeys.has(invariant.parent)) {
|
|
213
|
+
errors.push(`${prefix}: "parent" references unknown selector "${invariant.parent}"`);
|
|
214
|
+
}
|
|
215
|
+
if (!selectorKeys.has(invariant.child)) {
|
|
216
|
+
errors.push(`${prefix}: "child" references unknown selector "${invariant.child}"`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
if (errors.length > 0) {
|
|
221
|
+
const availableSelectorsMessage = available.length > 0 ? available : "(none)";
|
|
222
|
+
throw new Error(
|
|
223
|
+
[
|
|
224
|
+
`Contract invariant validation failed for component "${this.componentName}".`,
|
|
225
|
+
...errors.map((error) => `- ${error}`),
|
|
226
|
+
`Available selectors: ${availableSelectorsMessage}`
|
|
227
|
+
].join("\n")
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
validateStaticTargets() {
|
|
232
|
+
const selectorKeys = new Set(Object.keys(this.selectorsValue));
|
|
233
|
+
const available = Object.keys(this.selectorsValue).sort().join(", ") || "(none)";
|
|
234
|
+
const errors = [];
|
|
235
|
+
this.staticAssertions.forEach((assertion, index) => {
|
|
236
|
+
if (!selectorKeys.has(assertion.target)) {
|
|
237
|
+
errors.push(`static.assertions[${index}]: target "${assertion.target}" is not defined in selectors`);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
if (errors.length > 0) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
[
|
|
243
|
+
`Contract static target validation failed for component "${this.componentName}".`,
|
|
244
|
+
...errors.map((error) => `- ${error}`),
|
|
245
|
+
`Available selectors: ${available}`
|
|
246
|
+
].join("\n")
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
validateDynamicTargets() {
|
|
251
|
+
const selectorKeys = new Set(Object.keys(this.selectorsValue));
|
|
252
|
+
const available = Object.keys(this.selectorsValue).sort().join(", ") || "(none)";
|
|
253
|
+
const errors = [];
|
|
254
|
+
const isValidActionTarget = (target) => {
|
|
255
|
+
return selectorKeys.has(target) || target === "document" || target === "relative";
|
|
256
|
+
};
|
|
257
|
+
const isValidAssertionTarget = (target) => {
|
|
258
|
+
return selectorKeys.has(target) || target === "relative";
|
|
259
|
+
};
|
|
260
|
+
this.dynamicTests.forEach((test, testIndex) => {
|
|
261
|
+
test.action.forEach((action, actionIndex) => {
|
|
262
|
+
if (!isValidActionTarget(action.target)) {
|
|
263
|
+
errors.push(
|
|
264
|
+
`dynamic[${testIndex}].action[${actionIndex}]: target "${action.target}" is not defined in selectors`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
test.assertions.forEach((assertion, assertionIndex) => {
|
|
269
|
+
if (!isValidAssertionTarget(assertion.target)) {
|
|
270
|
+
errors.push(
|
|
271
|
+
`dynamic[${testIndex}].assertions[${assertionIndex}]: target "${assertion.target}" is not defined in selectors`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
if (assertion.target === "relative" && !this.selectorsValue.relative) {
|
|
275
|
+
errors.push(
|
|
276
|
+
`dynamic[${testIndex}].assertions[${assertionIndex}]: target "relative" requires selectors.relative to be defined`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
if (errors.length > 0) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
[
|
|
284
|
+
`Contract dynamic target validation failed for component "${this.componentName}".`,
|
|
285
|
+
...errors.map((error) => `- ${error}`),
|
|
286
|
+
`Available selectors: ${available}`,
|
|
287
|
+
`Allowed special targets: document, relative`
|
|
288
|
+
].join("\n")
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
build() {
|
|
293
|
+
this.validateRelationshipInvariants();
|
|
294
|
+
this.validateStaticTargets();
|
|
295
|
+
this.validateDynamicTargets();
|
|
296
|
+
const fallbackId = this.metaValue.id || `aria-ease.contract.${this.componentName}`;
|
|
297
|
+
return {
|
|
298
|
+
meta: {
|
|
299
|
+
id: fallbackId,
|
|
300
|
+
version: this.metaValue.version || "1.0.0",
|
|
301
|
+
description: this.metaValue.description || `Fluent contract for ${this.componentName}`,
|
|
302
|
+
source: this.metaValue.source,
|
|
303
|
+
W3CName: this.metaValue.W3CName
|
|
304
|
+
},
|
|
305
|
+
selectors: this.selectorsValue,
|
|
306
|
+
relationships: this.relationshipInvariants,
|
|
307
|
+
static: [{ assertions: this.staticAssertions }],
|
|
308
|
+
dynamic: this.dynamicTests
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
function createContract(componentName, define) {
|
|
313
|
+
const builder = new ContractBuilder(componentName);
|
|
314
|
+
define(builder);
|
|
315
|
+
return new FluentContract(builder.build());
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export { createContract };
|