aria-ease 6.9.1 → 6.11.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 +3 -3
- package/{bin/buildContracts-GBOY7UXG.js → dist/buildContracts-FT6KWUJN.js} +31 -3
- package/{bin/chunk-LMSKLN5O.js → dist/chunk-NI3MQCAS.js} +34 -0
- package/{bin → dist}/cli.cjs +239 -24
- package/{bin → dist}/cli.js +4 -4
- package/dist/{configLoader-WTGJAP4Z.js → configLoader-DWHOHXHL.js} +34 -0
- package/{bin/configLoader-Q6A4JLKW.js → dist/configLoader-UJZHQBYS.js} +1 -1
- package/{bin/contractTestRunnerPlaywright-ZZNWDUYP.js → dist/contractTestRunnerPlaywright-QDXSK3FE.js} +173 -20
- package/dist/{contractTestRunnerPlaywright-XBWJZMR3.js → contractTestRunnerPlaywright-WNWQYSXZ.js} +173 -20
- package/dist/index.cjs +568 -298
- package/dist/index.d.cts +53 -53
- package/dist/index.d.ts +53 -53
- package/dist/index.js +364 -281
- package/dist/src/combobox/index.cjs +21 -7
- package/dist/src/combobox/index.js +21 -7
- package/dist/src/utils/test/{configLoader-YE2CYGDG.js → configLoader-SHJSRG2A.js} +34 -0
- package/dist/src/utils/test/{contractTestRunnerPlaywright-LC5OAVXB.js → contractTestRunnerPlaywright-Z2AHXSNM.js} +173 -20
- package/dist/src/utils/test/dsl/index.cjs +338 -269
- package/dist/src/utils/test/dsl/index.d.cts +53 -53
- package/dist/src/utils/test/dsl/index.d.ts +53 -53
- package/dist/src/utils/test/dsl/index.js +338 -269
- package/dist/src/utils/test/index.cjs +207 -20
- package/dist/src/utils/test/index.js +2 -2
- package/{bin/test-OND56UUL.js → dist/test-O3J4ZPQR.js} +2 -2
- package/package.json +4 -5
- package/bin/AccordionComponentStrategy-4ZEIQ2V6.js +0 -42
- package/bin/ComboboxComponentStrategy-OGRVZXAF.js +0 -64
- package/bin/MenuComponentStrategy-JAMTCSNF.js +0 -81
- package/bin/TabsComponentStrategy-3SQURPMX.js +0 -29
- package/bin/chunk-I2KLQ2HA.js +0 -22
- package/bin/chunk-PK5L2SAF.js +0 -17
- package/bin/chunk-XERMSYEH.js +0 -363
- /package/{bin → dist}/audit-RM6TCZ5C.js +0 -0
- /package/{bin → dist}/badgeHelper-JOWO6RQG.js +0 -0
- /package/{bin → dist}/chunk-JJEPLK7L.js +0 -0
- /package/{bin → dist}/cli.d.cts +0 -0
- /package/{bin → dist}/cli.d.ts +0 -0
- /package/{bin → dist}/formatters-32KQIIYS.js +0 -0
|
@@ -1,4 +1,206 @@
|
|
|
1
|
-
// src/utils/test/dsl/
|
|
1
|
+
// src/utils/test/dsl/src/state-packs/comboboxStatePack.ts
|
|
2
|
+
function hasCapabilities(ctx, requiredCaps) {
|
|
3
|
+
return requiredCaps.some((cap) => ctx.capabilities.includes(cap));
|
|
4
|
+
}
|
|
5
|
+
function resolveSetup(setup, ctx) {
|
|
6
|
+
if (Array.isArray(setup) && setup.length && !setup[0].when) {
|
|
7
|
+
setup = [{ when: ["keyboard"], steps: () => setup }];
|
|
8
|
+
}
|
|
9
|
+
for (const strat of setup) {
|
|
10
|
+
if (hasCapabilities(ctx, strat.when)) {
|
|
11
|
+
return strat.steps(ctx);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
throw new Error(
|
|
15
|
+
`No setup strategy matches capabilities: ${ctx.capabilities.join(", ")}`
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
var COMBOBOX_STATES = {
|
|
19
|
+
"listbox.open": {
|
|
20
|
+
setup: [
|
|
21
|
+
{
|
|
22
|
+
when: ["keyboard", "textInput"],
|
|
23
|
+
steps: () => [
|
|
24
|
+
{ type: "keypress", target: "input", key: "ArrowDown" }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
when: ["pointer"],
|
|
29
|
+
steps: () => [
|
|
30
|
+
{ type: "click", target: "button" }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
assertion: isComboboxOpen
|
|
35
|
+
},
|
|
36
|
+
"listbox.closed": {
|
|
37
|
+
setup: [
|
|
38
|
+
{
|
|
39
|
+
when: ["keyboard"],
|
|
40
|
+
steps: () => [
|
|
41
|
+
/* { type: "keypress", target: "input", key: "Escape" } */
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
when: ["pointer"],
|
|
46
|
+
steps: () => [
|
|
47
|
+
/* { type: "click", target: "button" } */
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
assertion: isComboboxClosed
|
|
52
|
+
},
|
|
53
|
+
"input.focused": {
|
|
54
|
+
setup: [
|
|
55
|
+
{
|
|
56
|
+
when: ["keyboard"],
|
|
57
|
+
steps: () => [
|
|
58
|
+
{ type: "focus", target: "input" }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
assertion: isInputFocused
|
|
63
|
+
},
|
|
64
|
+
"input.filled": {
|
|
65
|
+
setup: [
|
|
66
|
+
{
|
|
67
|
+
when: ["keyboard", "textInput"],
|
|
68
|
+
steps: () => [
|
|
69
|
+
{ type: "type", target: "input", value: "test" }
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
assertion: isInputFilled
|
|
74
|
+
},
|
|
75
|
+
"activeOption.first": {
|
|
76
|
+
requires: ["listbox.open"],
|
|
77
|
+
setup: [
|
|
78
|
+
{
|
|
79
|
+
when: ["keyboard"],
|
|
80
|
+
steps: () => [
|
|
81
|
+
{ type: "keypress", target: "input", key: "ArrowDown" }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
assertion: isActiveDescendantNotEmpty
|
|
86
|
+
},
|
|
87
|
+
"activeOption.last": {
|
|
88
|
+
requires: ["activeOption.first"],
|
|
89
|
+
setup: [
|
|
90
|
+
{
|
|
91
|
+
when: ["keyboard"],
|
|
92
|
+
steps: () => [
|
|
93
|
+
{ type: "keypress", target: "input", key: "ArrowUp" }
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
assertion: isActiveDescendantNotEmpty
|
|
98
|
+
},
|
|
99
|
+
"selectedOption.first": {
|
|
100
|
+
requires: ["listbox.open"],
|
|
101
|
+
setup: [
|
|
102
|
+
{
|
|
103
|
+
when: ["pointer"],
|
|
104
|
+
steps: () => [
|
|
105
|
+
{ type: "click", target: "relative", relativeTarget: "first" }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
assertion: () => isAriaSelected("first")
|
|
110
|
+
},
|
|
111
|
+
"selectedOption.last": {
|
|
112
|
+
requires: ["listbox.open"],
|
|
113
|
+
setup: [
|
|
114
|
+
{
|
|
115
|
+
when: ["pointer"],
|
|
116
|
+
steps: () => [
|
|
117
|
+
{ type: "click", target: "relative", relativeTarget: "last" }
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
assertion: () => isAriaSelected("last")
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
function isComboboxOpen() {
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
target: "listbox",
|
|
128
|
+
assertion: "toBeVisible",
|
|
129
|
+
failureMessage: "Expected listbox to be visible"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
target: "input",
|
|
133
|
+
assertion: "toHaveAttribute",
|
|
134
|
+
attribute: "aria-expanded",
|
|
135
|
+
expectedValue: "true",
|
|
136
|
+
failureMessage: "Expect combobox input to have aria-expanded='true'"
|
|
137
|
+
}
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
function isComboboxClosed() {
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
target: "listbox",
|
|
144
|
+
assertion: "notToBeVisible",
|
|
145
|
+
failureMessage: "Expected listbox to be closed"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
target: "input",
|
|
149
|
+
assertion: "toHaveAttribute",
|
|
150
|
+
attribute: "aria-expanded",
|
|
151
|
+
expectedValue: "false",
|
|
152
|
+
failureMessage: "Expect combobox input to have aria-expanded='false'"
|
|
153
|
+
}
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
function isActiveDescendantNotEmpty() {
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
target: "input",
|
|
160
|
+
assertion: "toHaveAttribute",
|
|
161
|
+
attribute: "aria-activedescendant",
|
|
162
|
+
expectedValue: "!empty",
|
|
163
|
+
failureMessage: "Expected aria-activedescendant to not be empty"
|
|
164
|
+
}
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
function isAriaSelected(index) {
|
|
168
|
+
return [
|
|
169
|
+
{
|
|
170
|
+
target: "relative",
|
|
171
|
+
relativeTarget: index,
|
|
172
|
+
assertion: "toHaveAttribute",
|
|
173
|
+
attribute: "aria-selected",
|
|
174
|
+
expectedValue: "true",
|
|
175
|
+
failureMessage: `Expected ${index} option to have aria-selected='true'`
|
|
176
|
+
}
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
function isInputFocused() {
|
|
180
|
+
return [
|
|
181
|
+
{
|
|
182
|
+
target: "input",
|
|
183
|
+
assertion: "toHaveFocus",
|
|
184
|
+
failureMessage: "Expected input to be focused"
|
|
185
|
+
}
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
function isInputFilled() {
|
|
189
|
+
return [
|
|
190
|
+
{
|
|
191
|
+
target: "input",
|
|
192
|
+
assertion: "toHaveValue",
|
|
193
|
+
expectedValue: "test",
|
|
194
|
+
failureMessage: "Expected input to have the value 'test'"
|
|
195
|
+
}
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/utils/test/dsl/src/contractBuilder.ts
|
|
200
|
+
var STATE_PACKS = {
|
|
201
|
+
"combobox.listbox": COMBOBOX_STATES
|
|
202
|
+
// Add more mappings as needed
|
|
203
|
+
};
|
|
2
204
|
var FluentContract = class {
|
|
3
205
|
constructor(jsonContract) {
|
|
4
206
|
this.jsonContract = jsonContract;
|
|
@@ -7,306 +209,173 @@ var FluentContract = class {
|
|
|
7
209
|
return this.jsonContract;
|
|
8
210
|
}
|
|
9
211
|
};
|
|
10
|
-
var
|
|
11
|
-
constructor(
|
|
12
|
-
this.
|
|
13
|
-
this.
|
|
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;
|
|
212
|
+
var ContractBuilder = class {
|
|
213
|
+
constructor(componentName) {
|
|
214
|
+
this.componentName = componentName;
|
|
215
|
+
this.statePack = STATE_PACKS[componentName] || {};
|
|
58
216
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
}
|
|
217
|
+
metaValue = {};
|
|
218
|
+
selectorsValue = {};
|
|
219
|
+
relationshipInvariants = [];
|
|
220
|
+
staticAssertions = [];
|
|
221
|
+
dynamicTests = [];
|
|
222
|
+
statePack;
|
|
223
|
+
meta(meta) {
|
|
224
|
+
this.metaValue = meta;
|
|
82
225
|
return this;
|
|
83
226
|
}
|
|
84
|
-
|
|
85
|
-
this.
|
|
227
|
+
selectors(selectors) {
|
|
228
|
+
this.selectorsValue = selectors;
|
|
86
229
|
return this;
|
|
87
230
|
}
|
|
88
|
-
|
|
89
|
-
|
|
231
|
+
relationships(fn) {
|
|
232
|
+
const api = {
|
|
233
|
+
ariaReference: (from, attribute, to) => ({
|
|
234
|
+
required: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "required" }),
|
|
235
|
+
optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" })
|
|
236
|
+
}),
|
|
237
|
+
contains: (parent, child) => ({
|
|
238
|
+
required: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "required" }),
|
|
239
|
+
optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" })
|
|
240
|
+
})
|
|
241
|
+
};
|
|
242
|
+
fn(api);
|
|
90
243
|
return this;
|
|
91
244
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
target
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
245
|
+
static(fn) {
|
|
246
|
+
const api = {
|
|
247
|
+
target: (target) => ({
|
|
248
|
+
has: (attribute, expectedValue) => ({
|
|
249
|
+
required: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "required" }),
|
|
250
|
+
optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" })
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
};
|
|
254
|
+
fn(api);
|
|
99
255
|
return this;
|
|
100
256
|
}
|
|
101
|
-
|
|
102
|
-
this.
|
|
257
|
+
when(event) {
|
|
258
|
+
return new DynamicTestBuilder(this, this.statePack, event);
|
|
103
259
|
}
|
|
104
|
-
|
|
105
|
-
this.
|
|
260
|
+
addDynamicTest(test) {
|
|
261
|
+
this.dynamicTests.push(test);
|
|
106
262
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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 };
|
|
263
|
+
build() {
|
|
264
|
+
return {
|
|
265
|
+
meta: this.metaValue,
|
|
266
|
+
selectors: this.selectorsValue,
|
|
267
|
+
relationships: this.relationshipInvariants.length ? this.relationshipInvariants : void 0,
|
|
268
|
+
static: this.staticAssertions.length ? [{ assertions: this.staticAssertions }] : [],
|
|
269
|
+
dynamic: this.dynamicTests
|
|
270
|
+
};
|
|
128
271
|
}
|
|
129
272
|
};
|
|
130
|
-
var
|
|
131
|
-
constructor(
|
|
132
|
-
this.
|
|
273
|
+
var DynamicTestBuilder = class {
|
|
274
|
+
constructor(parent, statePack, event) {
|
|
275
|
+
this.parent = parent;
|
|
276
|
+
this.statePack = statePack;
|
|
277
|
+
this.event = event;
|
|
133
278
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
279
|
+
_as;
|
|
280
|
+
_on;
|
|
281
|
+
_given = [];
|
|
282
|
+
_then = [];
|
|
283
|
+
_desc = "";
|
|
284
|
+
_level = "required";
|
|
285
|
+
as(actionType) {
|
|
286
|
+
this._as = actionType;
|
|
141
287
|
return this;
|
|
142
288
|
}
|
|
143
|
-
|
|
144
|
-
this.
|
|
289
|
+
on(target) {
|
|
290
|
+
this._on = target;
|
|
145
291
|
return this;
|
|
146
292
|
}
|
|
147
|
-
|
|
148
|
-
this.
|
|
293
|
+
given(states) {
|
|
294
|
+
this._given = Array.isArray(states) ? states : [states];
|
|
149
295
|
return this;
|
|
150
296
|
}
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
});
|
|
297
|
+
then(states) {
|
|
298
|
+
this._then = Array.isArray(states) ? states : [states];
|
|
185
299
|
return this;
|
|
186
300
|
}
|
|
187
|
-
|
|
188
|
-
|
|
301
|
+
describe(desc) {
|
|
302
|
+
this._desc = desc;
|
|
189
303
|
return this;
|
|
190
304
|
}
|
|
191
|
-
|
|
192
|
-
|
|
305
|
+
required() {
|
|
306
|
+
this._level = "required";
|
|
307
|
+
this._finalize();
|
|
308
|
+
return this.parent;
|
|
193
309
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
310
|
+
optional() {
|
|
311
|
+
this._level = "optional";
|
|
312
|
+
this._finalize();
|
|
313
|
+
return this.parent;
|
|
314
|
+
}
|
|
315
|
+
recommended() {
|
|
316
|
+
this._level = "recommended";
|
|
317
|
+
this._finalize();
|
|
318
|
+
return this.parent;
|
|
319
|
+
}
|
|
320
|
+
_finalize() {
|
|
321
|
+
const capabilityMap = {
|
|
322
|
+
keypress: "keyboard",
|
|
323
|
+
click: "pointer",
|
|
324
|
+
type: "textInput",
|
|
325
|
+
focus: "keyboard",
|
|
326
|
+
hover: "pointer"
|
|
327
|
+
// add more mappings as needed
|
|
328
|
+
};
|
|
329
|
+
const capability = capabilityMap[this._as || "keyboard"] || (this._as || "keyboard");
|
|
330
|
+
const ctx = { capabilities: [capability] };
|
|
331
|
+
const resolveAllSetups = (stateName, visited = /* @__PURE__ */ new Set()) => {
|
|
332
|
+
if (visited.has(stateName)) return [];
|
|
333
|
+
visited.add(stateName);
|
|
334
|
+
const s = this.statePack[stateName];
|
|
335
|
+
if (!s) return [];
|
|
336
|
+
let actions = [];
|
|
337
|
+
if (Array.isArray(s.requires)) {
|
|
338
|
+
for (const req of s.requires) {
|
|
339
|
+
actions = actions.concat(resolveAllSetups(req, visited));
|
|
209
340
|
}
|
|
210
341
|
}
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
342
|
+
if (s.setup) actions = actions.concat(resolveSetup(s.setup, ctx));
|
|
343
|
+
return actions;
|
|
344
|
+
};
|
|
345
|
+
const setup = [];
|
|
346
|
+
for (const state of this._given) {
|
|
347
|
+
setup.push(...resolveAllSetups(state));
|
|
348
|
+
}
|
|
349
|
+
const assertions = [];
|
|
350
|
+
for (const state of this._then) {
|
|
351
|
+
const s = this.statePack[state];
|
|
352
|
+
if (s && s.assertion !== void 0) {
|
|
353
|
+
let value = s.assertion;
|
|
354
|
+
if (typeof value === "function") {
|
|
355
|
+
try {
|
|
356
|
+
value = value();
|
|
357
|
+
} catch (e) {
|
|
358
|
+
throw new Error(`Error calling assertion function for state '${state}': ${e.message}`);
|
|
359
|
+
}
|
|
217
360
|
}
|
|
361
|
+
if (Array.isArray(value)) assertions.push(...value);
|
|
362
|
+
else assertions.push(value);
|
|
218
363
|
}
|
|
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
364
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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`);
|
|
365
|
+
const action = [
|
|
366
|
+
{
|
|
367
|
+
type: this._as,
|
|
368
|
+
target: this._on,
|
|
369
|
+
key: this._as === "keypress" ? this.event : void 0
|
|
238
370
|
}
|
|
371
|
+
];
|
|
372
|
+
this.parent.addDynamicTest({
|
|
373
|
+
description: this._desc || "",
|
|
374
|
+
level: this._level,
|
|
375
|
+
action,
|
|
376
|
+
assertions,
|
|
377
|
+
...setup.length ? { setup } : {}
|
|
239
378
|
});
|
|
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
379
|
}
|
|
311
380
|
};
|
|
312
381
|
function createContract(componentName, define) {
|