walkwright 0.0.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/LICENSE +21 -0
- package/README.md +276 -0
- package/bin/walkwright.js +3 -0
- package/build/aria.d.ts +92 -0
- package/build/aria.js +215 -0
- package/build/aria.js.map +1 -0
- package/build/browser.d.ts +3 -0
- package/build/browser.js +18 -0
- package/build/browser.js.map +1 -0
- package/build/chunk-5CHKYNP7.js +507 -0
- package/build/chunk-5CHKYNP7.js.map +1 -0
- package/build/chunk-6YRKPGUO.js +1197 -0
- package/build/chunk-6YRKPGUO.js.map +1 -0
- package/build/chunk-A452S7R3.js +35 -0
- package/build/chunk-A452S7R3.js.map +1 -0
- package/build/chunk-BODO6HIH.js +60 -0
- package/build/chunk-BODO6HIH.js.map +1 -0
- package/build/chunk-FJI2CMCE.js +439 -0
- package/build/chunk-FJI2CMCE.js.map +1 -0
- package/build/chunk-JYHZVRB2.js +80 -0
- package/build/chunk-JYHZVRB2.js.map +1 -0
- package/build/chunk-KQN5IYN6.js +97 -0
- package/build/chunk-KQN5IYN6.js.map +1 -0
- package/build/chunk-KTXNERHT.js +72 -0
- package/build/chunk-KTXNERHT.js.map +1 -0
- package/build/chunk-MFTC7XL2.js +141 -0
- package/build/chunk-MFTC7XL2.js.map +1 -0
- package/build/chunk-N4SSLHMP.js +108 -0
- package/build/chunk-N4SSLHMP.js.map +1 -0
- package/build/chunk-RNNW7OW2.js +270 -0
- package/build/chunk-RNNW7OW2.js.map +1 -0
- package/build/chunk-UAWBPTDW.js +51 -0
- package/build/chunk-UAWBPTDW.js.map +1 -0
- package/build/chunk-Z7CW5CAY.js +64 -0
- package/build/chunk-Z7CW5CAY.js.map +1 -0
- package/build/cli.d.ts +1 -0
- package/build/cli.js +253 -0
- package/build/cli.js.map +1 -0
- package/build/config.d.ts +13 -0
- package/build/config.js +15 -0
- package/build/config.js.map +1 -0
- package/build/descriptor.d.ts +96 -0
- package/build/descriptor.js +27 -0
- package/build/descriptor.js.map +1 -0
- package/build/device-auth.d.ts +6 -0
- package/build/device-auth.js +11 -0
- package/build/device-auth.js.map +1 -0
- package/build/locator.d.ts +140 -0
- package/build/locator.js +92 -0
- package/build/locator.js.map +1 -0
- package/build/paths.d.ts +11 -0
- package/build/paths.js +22 -0
- package/build/paths.js.map +1 -0
- package/build/pom-kcSMxwX2.d.ts +349 -0
- package/build/pom.d.ts +6 -0
- package/build/pom.js +37 -0
- package/build/pom.js.map +1 -0
- package/build/project.d.ts +13 -0
- package/build/project.js +19 -0
- package/build/project.js.map +1 -0
- package/build/provisioner.d.ts +34 -0
- package/build/provisioner.js +14 -0
- package/build/provisioner.js.map +1 -0
- package/build/recorder.d.ts +6 -0
- package/build/recorder.js +15 -0
- package/build/recorder.js.map +1 -0
- package/build/reporter.d.ts +16 -0
- package/build/reporter.js +426 -0
- package/build/reporter.js.map +1 -0
- package/build/schema.d.ts +49 -0
- package/build/schema.js +16 -0
- package/build/schema.js.map +1 -0
- package/build/storage-state.d.ts +22 -0
- package/build/storage-state.js +42 -0
- package/build/storage-state.js.map +1 -0
- package/build/store.d.ts +17 -0
- package/build/store.js +9 -0
- package/build/store.js.map +1 -0
- package/build/ui.d.ts +49 -0
- package/build/ui.js +56 -0
- package/build/ui.js.map +1 -0
- package/build/upload.d.ts +10 -0
- package/build/upload.js +8 -0
- package/build/upload.js.map +1 -0
- package/build/wait.d.ts +14 -0
- package/build/wait.js +50 -0
- package/build/wait.js.map +1 -0
- package/package.json +52 -0
- package/src/aria.ts +326 -0
- package/src/browser.ts +15 -0
- package/src/cli.ts +295 -0
- package/src/config.ts +16 -0
- package/src/descriptor.ts +179 -0
- package/src/device-auth.ts +188 -0
- package/src/locator.ts +683 -0
- package/src/paths.ts +70 -0
- package/src/pom.ts +2363 -0
- package/src/project.ts +77 -0
- package/src/provisioner.ts +183 -0
- package/src/recorder.ts +877 -0
- package/src/reporter.ts +561 -0
- package/src/schema.ts +37 -0
- package/src/storage-state.ts +71 -0
- package/src/store.ts +96 -0
- package/src/ui.ts +333 -0
- package/src/upload.ts +116 -0
- package/src/wait.ts +63 -0
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createProvisioner
|
|
3
|
+
} from "./chunk-KQN5IYN6.js";
|
|
4
|
+
import {
|
|
5
|
+
createRecorderFixtures
|
|
6
|
+
} from "./chunk-5CHKYNP7.js";
|
|
7
|
+
import {
|
|
8
|
+
appDescriptorSchema
|
|
9
|
+
} from "./chunk-N4SSLHMP.js";
|
|
10
|
+
import {
|
|
11
|
+
isLazyLocator,
|
|
12
|
+
lazyLocatorOf,
|
|
13
|
+
paramNamesOf,
|
|
14
|
+
plainTextOf,
|
|
15
|
+
resolveLocator,
|
|
16
|
+
textFrom
|
|
17
|
+
} from "./chunk-FJI2CMCE.js";
|
|
18
|
+
import {
|
|
19
|
+
__callDispose,
|
|
20
|
+
__using
|
|
21
|
+
} from "./chunk-UAWBPTDW.js";
|
|
22
|
+
|
|
23
|
+
// src/pom.ts
|
|
24
|
+
import { test as playwrightTest } from "@playwright/test";
|
|
25
|
+
function rootOf(instance) {
|
|
26
|
+
if (instance.root === void 0) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"this surface is not mounted at a chain \u2014 scope it with a locator"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return instance.root;
|
|
32
|
+
}
|
|
33
|
+
var coreKey = /* @__PURE__ */ Symbol("walkwright.surface");
|
|
34
|
+
function isSurface(value) {
|
|
35
|
+
return typeof value === "function" && coreKey in value;
|
|
36
|
+
}
|
|
37
|
+
function isOverlayFactory(value) {
|
|
38
|
+
return typeof value === "function" && value.overlay === true;
|
|
39
|
+
}
|
|
40
|
+
var isParameterized = (value) => typeof value === "function" && value.parameterized === true;
|
|
41
|
+
function definer(definition, factoryOf) {
|
|
42
|
+
if (typeof definition !== "function") {
|
|
43
|
+
return factoryOf(definition);
|
|
44
|
+
}
|
|
45
|
+
const build = definition;
|
|
46
|
+
return Object.assign(
|
|
47
|
+
(...params) => factoryOf(build(...params)),
|
|
48
|
+
{ parameterized: true }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
var IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/;
|
|
52
|
+
function paramsSourceOf(source) {
|
|
53
|
+
const paren = /^\s*(?:async\s+)?\(([^)]*)\)\s*=>/.exec(source);
|
|
54
|
+
if (paren !== null) {
|
|
55
|
+
return paren[1] ?? "";
|
|
56
|
+
}
|
|
57
|
+
const bare = /^\s*(?:async\s+)?([A-Za-z_$][\w$]*)\s*=>/.exec(source);
|
|
58
|
+
if (bare !== null) {
|
|
59
|
+
return bare[1] ?? "";
|
|
60
|
+
}
|
|
61
|
+
const keyword = /^\s*(?:async\s+)?function\s*[A-Za-z_$][\w$]*?\s*\(([^)]*)\)/.exec(source);
|
|
62
|
+
return keyword === null ? void 0 : keyword[1] ?? "";
|
|
63
|
+
}
|
|
64
|
+
function paramNamesFrom(source, arity) {
|
|
65
|
+
const fallback = Array.from({ length: arity }, (_, index) => `p${index}`);
|
|
66
|
+
const params = paramsSourceOf(source);
|
|
67
|
+
if (params === void 0) {
|
|
68
|
+
return fallback;
|
|
69
|
+
}
|
|
70
|
+
const pieces = params.trim() === "" ? [] : params.split(",").map((piece) => piece.trim());
|
|
71
|
+
if (pieces.length < arity) {
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
const sliced = pieces.slice(0, arity);
|
|
75
|
+
return sliced.every((piece) => IDENTIFIER_RE.test(piece)) ? sliced : fallback;
|
|
76
|
+
}
|
|
77
|
+
function sentinelFor(index, tag) {
|
|
78
|
+
return ` walkwright-param-${index}-${tag} `;
|
|
79
|
+
}
|
|
80
|
+
function tracedText(text, sentinelToName) {
|
|
81
|
+
if (text === void 0 || typeof text !== "string") {
|
|
82
|
+
return text;
|
|
83
|
+
}
|
|
84
|
+
const exact = sentinelToName.get(text);
|
|
85
|
+
if (exact !== void 0) {
|
|
86
|
+
return { param: exact };
|
|
87
|
+
}
|
|
88
|
+
const parts = [];
|
|
89
|
+
let remainder = text;
|
|
90
|
+
for (; ; ) {
|
|
91
|
+
let earliestIndex = -1;
|
|
92
|
+
let earliestSentinel = "";
|
|
93
|
+
let earliestName = "";
|
|
94
|
+
for (const [sentinel, name] of sentinelToName) {
|
|
95
|
+
const index = remainder.indexOf(sentinel);
|
|
96
|
+
if (index !== -1 && (earliestIndex === -1 || index < earliestIndex)) {
|
|
97
|
+
earliestIndex = index;
|
|
98
|
+
earliestSentinel = sentinel;
|
|
99
|
+
earliestName = name;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (earliestIndex === -1) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
const literal = remainder.slice(0, earliestIndex);
|
|
106
|
+
if (literal !== "") {
|
|
107
|
+
parts.push(literal);
|
|
108
|
+
}
|
|
109
|
+
parts.push({ param: earliestName });
|
|
110
|
+
remainder = remainder.slice(earliestIndex + earliestSentinel.length);
|
|
111
|
+
}
|
|
112
|
+
if (remainder !== "") {
|
|
113
|
+
parts.push(remainder);
|
|
114
|
+
}
|
|
115
|
+
if (parts.length === 0) {
|
|
116
|
+
return text;
|
|
117
|
+
}
|
|
118
|
+
return parts.length === 1 && typeof parts[0] === "string" ? text : { template: parts };
|
|
119
|
+
}
|
|
120
|
+
function tracedFilter(filter, sentinelToName) {
|
|
121
|
+
const hasText = tracedText(filter.hasText, sentinelToName);
|
|
122
|
+
let has;
|
|
123
|
+
if (filter.has !== void 0) {
|
|
124
|
+
const traced = tracedDescriptor(filter.has, sentinelToName);
|
|
125
|
+
if (traced === void 0) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
has = traced;
|
|
129
|
+
}
|
|
130
|
+
let hasNot;
|
|
131
|
+
if (filter.hasNot !== void 0) {
|
|
132
|
+
const traced = tracedDescriptor(filter.hasNot, sentinelToName);
|
|
133
|
+
if (traced === void 0) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
hasNot = traced;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
...hasText === void 0 ? {} : { hasText },
|
|
140
|
+
...has === void 0 ? {} : { has },
|
|
141
|
+
...hasNot === void 0 ? {} : { hasNot }
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function tracedNode(node, sentinelToName) {
|
|
145
|
+
if ("or" in node) {
|
|
146
|
+
const branches = tracedBranches(node.or, sentinelToName);
|
|
147
|
+
return branches === void 0 ? void 0 : { or: branches };
|
|
148
|
+
}
|
|
149
|
+
if ("and" in node) {
|
|
150
|
+
const branches = tracedBranches(node.and, sentinelToName);
|
|
151
|
+
return branches === void 0 ? void 0 : { and: branches };
|
|
152
|
+
}
|
|
153
|
+
const name = "name" in node ? tracedText(node.name, sentinelToName) : void 0;
|
|
154
|
+
const text = "text" in node ? tracedText(node.text, sentinelToName) : void 0;
|
|
155
|
+
const refine = tracedRefine(node.refine, sentinelToName);
|
|
156
|
+
if (refine === null) {
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
...node,
|
|
161
|
+
...name === void 0 ? {} : { name },
|
|
162
|
+
...text === void 0 ? {} : { text },
|
|
163
|
+
...refine === void 0 ? {} : { refine }
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function tracedRefine(refine, sentinelToName) {
|
|
167
|
+
if (refine === void 0) {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
const traced = [];
|
|
171
|
+
for (const refinement of refine) {
|
|
172
|
+
if ("nth" in refinement) {
|
|
173
|
+
traced.push(refinement);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const filter = tracedFilter(refinement.filter, sentinelToName);
|
|
177
|
+
if (filter === null) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
traced.push({ filter });
|
|
181
|
+
}
|
|
182
|
+
return traced;
|
|
183
|
+
}
|
|
184
|
+
function tracedBranches(branches, sentinelToName) {
|
|
185
|
+
const traced = [];
|
|
186
|
+
for (const branch of branches) {
|
|
187
|
+
const next = tracedDescriptor(branch, sentinelToName);
|
|
188
|
+
if (next === void 0) {
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
traced.push(next);
|
|
192
|
+
}
|
|
193
|
+
return traced;
|
|
194
|
+
}
|
|
195
|
+
function tracedDescriptor(descriptor, sentinelToName) {
|
|
196
|
+
const traced = [];
|
|
197
|
+
for (const node of descriptor) {
|
|
198
|
+
const next = tracedNode(node, sentinelToName);
|
|
199
|
+
if (next === void 0) {
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
traced.push(next);
|
|
203
|
+
}
|
|
204
|
+
return traced;
|
|
205
|
+
}
|
|
206
|
+
function probeParametric(value) {
|
|
207
|
+
if (typeof value !== "function") {
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
const fn = value;
|
|
211
|
+
const names = paramNamesFrom(fn.toString(), fn.length);
|
|
212
|
+
const tag = Math.random().toString(36).slice(2, 10);
|
|
213
|
+
const sentinelToName = new Map(
|
|
214
|
+
names.map((name, index) => [sentinelFor(index, tag), name])
|
|
215
|
+
);
|
|
216
|
+
let result;
|
|
217
|
+
try {
|
|
218
|
+
result = fn(...sentinelToName.keys());
|
|
219
|
+
} catch {
|
|
220
|
+
return void 0;
|
|
221
|
+
}
|
|
222
|
+
if (isLazyLocator(result)) {
|
|
223
|
+
const locator = tracedDescriptor(result.chain, sentinelToName);
|
|
224
|
+
if (locator === void 0) {
|
|
225
|
+
return { parametricOpaque: { build: fn } };
|
|
226
|
+
}
|
|
227
|
+
const used = new Set(paramNamesOf(locator));
|
|
228
|
+
if (!names.every((name) => used.has(name))) {
|
|
229
|
+
return { parametricOpaque: { build: fn } };
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
parametric: {
|
|
233
|
+
build: fn,
|
|
234
|
+
locator,
|
|
235
|
+
params: names.filter((name) => used.has(name)).map((name) => ({ name }))
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
if (typeof result === "function") {
|
|
240
|
+
return { parametricOpaque: { build: fn } };
|
|
241
|
+
}
|
|
242
|
+
return void 0;
|
|
243
|
+
}
|
|
244
|
+
function isOverlayEmbed(value) {
|
|
245
|
+
return typeof value === "object" && value !== null && isOverlayFactory(value.overlay);
|
|
246
|
+
}
|
|
247
|
+
function embedOf(factory, open) {
|
|
248
|
+
return {
|
|
249
|
+
factory,
|
|
250
|
+
descriptor: {
|
|
251
|
+
overlay: factory.descriptor,
|
|
252
|
+
...open === void 0 ? {} : { open: { opaque: true, provenance: "hand" } }
|
|
253
|
+
},
|
|
254
|
+
...open === void 0 ? {} : { open }
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function bindElement(value) {
|
|
258
|
+
if (isLazyLocator(value)) {
|
|
259
|
+
return value.params.length === 0 ? { locator: value.chain } : {
|
|
260
|
+
locator: value.chain,
|
|
261
|
+
params: value.params.map((name) => ({ name }))
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (isOverlayFactory(value)) {
|
|
265
|
+
return { overlay: embedOf(value, void 0) };
|
|
266
|
+
}
|
|
267
|
+
if (isOverlayEmbed(value)) {
|
|
268
|
+
return { overlay: embedOf(value.overlay, value.open) };
|
|
269
|
+
}
|
|
270
|
+
if (isSurface(value)) {
|
|
271
|
+
return { surface: { factory: value, descriptor: value.descriptor } };
|
|
272
|
+
}
|
|
273
|
+
if (isParameterized(value)) {
|
|
274
|
+
return { opaque: value, provenance: "hand" };
|
|
275
|
+
}
|
|
276
|
+
const parametric = probeParametric(value);
|
|
277
|
+
if (parametric !== void 0) {
|
|
278
|
+
return parametric;
|
|
279
|
+
}
|
|
280
|
+
return { opaque: value, provenance: "hand" };
|
|
281
|
+
}
|
|
282
|
+
function projectElement(bound) {
|
|
283
|
+
if ("locator" in bound) {
|
|
284
|
+
return bound.params === void 0 ? { locator: bound.locator } : { locator: bound.locator, params: bound.params };
|
|
285
|
+
}
|
|
286
|
+
if ("parametric" in bound) {
|
|
287
|
+
return bound.parametric.params.length === 0 ? { locator: bound.parametric.locator } : {
|
|
288
|
+
locator: bound.parametric.locator,
|
|
289
|
+
params: bound.parametric.params
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
if ("parametricOpaque" in bound) {
|
|
293
|
+
return { opaque: true, provenance: "hand" };
|
|
294
|
+
}
|
|
295
|
+
if ("surface" in bound) {
|
|
296
|
+
return { surface: bound.surface.descriptor };
|
|
297
|
+
}
|
|
298
|
+
if ("overlay" in bound) {
|
|
299
|
+
return bound.overlay.descriptor;
|
|
300
|
+
}
|
|
301
|
+
return { opaque: true, provenance: "hand" };
|
|
302
|
+
}
|
|
303
|
+
function projectElements(elements) {
|
|
304
|
+
return Object.fromEntries(
|
|
305
|
+
Object.entries(elements).map(([key, bound]) => [
|
|
306
|
+
key,
|
|
307
|
+
projectElement(bound)
|
|
308
|
+
])
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
function classifyActions(actions) {
|
|
312
|
+
const names = Object.keys(actions);
|
|
313
|
+
return names.length === 0 ? void 0 : names.map((name) => ({
|
|
314
|
+
name,
|
|
315
|
+
impl: { opaque: true, provenance: "hand" }
|
|
316
|
+
}));
|
|
317
|
+
}
|
|
318
|
+
function regexDescriptorOf(re) {
|
|
319
|
+
return {
|
|
320
|
+
pattern: re.source,
|
|
321
|
+
...re.flags === "" ? {} : { flags: re.flags }
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function captureRuleDescriptorOf(rule) {
|
|
325
|
+
if ("element" in rule) {
|
|
326
|
+
return {
|
|
327
|
+
element: rule.element.split("."),
|
|
328
|
+
text: rule.text,
|
|
329
|
+
...rule.match === void 0 ? {} : { match: regexDescriptorOf(rule.match) }
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
return { match: regexDescriptorOf(rule.match), text: rule.text };
|
|
333
|
+
}
|
|
334
|
+
function captureLookupOf(descriptor) {
|
|
335
|
+
if ("opaque" in descriptor) {
|
|
336
|
+
return { kind: "opaque" };
|
|
337
|
+
}
|
|
338
|
+
if ("locator" in descriptor) {
|
|
339
|
+
return { kind: "locator" };
|
|
340
|
+
}
|
|
341
|
+
if ("surface" in descriptor) {
|
|
342
|
+
return { kind: "nested", elements: descriptor.surface.elements };
|
|
343
|
+
}
|
|
344
|
+
return { kind: "nested", elements: descriptor.overlay.elements };
|
|
345
|
+
}
|
|
346
|
+
function validateCaptureElement(path, owner, elements, hasMatch) {
|
|
347
|
+
const segments = path.split(".");
|
|
348
|
+
let current = elements;
|
|
349
|
+
let consumed = [];
|
|
350
|
+
segments.forEach((segment, index) => {
|
|
351
|
+
consumed = [...consumed, segment];
|
|
352
|
+
const descriptor = current[segment];
|
|
353
|
+
if (descriptor === void 0) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
`capture rule "${path}" of ${owner} references unknown element "${consumed.join(".")}"`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
const isFinal = index === segments.length - 1;
|
|
359
|
+
const lookup = captureLookupOf(descriptor);
|
|
360
|
+
if (lookup.kind === "opaque") {
|
|
361
|
+
throw new Error(
|
|
362
|
+
`capture rule "${path}" of ${owner} targets "${consumed.join(".")}", which has no locator to resolve`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
if (lookup.kind === "locator") {
|
|
366
|
+
if (!isFinal) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
`capture rule "${path}" of ${owner} walks through "${consumed.join(".")}", which is not a surface`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (isFinal && !hasMatch) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`capture rule "${path}" of ${owner} targets "${consumed.join(".")}", a surface \u2014 give it match to scope a replace over its subtree`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
current = lookup.elements;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
function captureRulesOf(rules, owner, elements) {
|
|
382
|
+
return (rules ?? []).map((rule) => {
|
|
383
|
+
if ("element" in rule) {
|
|
384
|
+
validateCaptureElement(
|
|
385
|
+
rule.element,
|
|
386
|
+
owner,
|
|
387
|
+
elements,
|
|
388
|
+
rule.match !== void 0
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
return captureRuleDescriptorOf(rule);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
function captureDescriptorOf(rules) {
|
|
395
|
+
return rules.length === 0 ? void 0 : { rules };
|
|
396
|
+
}
|
|
397
|
+
function projectSurface(root, elements, actions, captureRules) {
|
|
398
|
+
const projectedActions = classifyActions(actions);
|
|
399
|
+
const capture = captureDescriptorOf(captureRules);
|
|
400
|
+
return {
|
|
401
|
+
...root.length === 0 ? {} : { root },
|
|
402
|
+
elements: projectElements(elements),
|
|
403
|
+
...projectedActions === void 0 ? {} : { actions: projectedActions },
|
|
404
|
+
...capture === void 0 ? {} : { capture }
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function assemble(def, owner, reserved) {
|
|
408
|
+
const elements = {};
|
|
409
|
+
const actions = {};
|
|
410
|
+
const capture = [];
|
|
411
|
+
const elementSources = /* @__PURE__ */ new Map();
|
|
412
|
+
const actionSources = /* @__PURE__ */ new Map();
|
|
413
|
+
const claim = (sources, key, source, what) => {
|
|
414
|
+
const holder = sources.get(key);
|
|
415
|
+
if (holder !== void 0) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`${holder} and ${source} of ${owner} both define ${what} "${key}"`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
sources.set(key, source);
|
|
421
|
+
};
|
|
422
|
+
for (const [index, included] of (def.include ?? []).entries()) {
|
|
423
|
+
const source = `include[${String(index)}]`;
|
|
424
|
+
if (!isSurface(included)) {
|
|
425
|
+
throw new Error(`${source} of ${owner} is not a surface`);
|
|
426
|
+
}
|
|
427
|
+
const core = included[coreKey];
|
|
428
|
+
if (core.root.length > 0) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
`${source} of ${owner} has a root \u2014 embed it as an element to keep its boundary; only rootless surfaces can be merged`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
for (const [key, bound] of Object.entries(core.elements)) {
|
|
434
|
+
claim(elementSources, key, source, "element");
|
|
435
|
+
elements[key] = bound;
|
|
436
|
+
}
|
|
437
|
+
for (const [key, action] of Object.entries(core.actions)) {
|
|
438
|
+
claim(actionSources, key, source, "action");
|
|
439
|
+
if (reserved.includes(key)) {
|
|
440
|
+
throw new Error(
|
|
441
|
+
`action "${key}" of ${source} of ${owner} shadows a built-in field of its instances`
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
actions[key] = action;
|
|
445
|
+
}
|
|
446
|
+
capture.push(...core.descriptor.capture?.rules ?? []);
|
|
447
|
+
}
|
|
448
|
+
for (const [key, value] of Object.entries(def.elements ?? {})) {
|
|
449
|
+
claim(elementSources, key, "the definition", "element");
|
|
450
|
+
elements[key] = bindElement(value);
|
|
451
|
+
}
|
|
452
|
+
for (const [key, action] of Object.entries(def.actions ?? {})) {
|
|
453
|
+
claim(actionSources, key, "the definition", "action");
|
|
454
|
+
if (reserved.includes(key)) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`action "${key}" of ${owner} shadows a built-in field of its instances`
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
actions[key] = action;
|
|
460
|
+
}
|
|
461
|
+
return { elements, actions, capture };
|
|
462
|
+
}
|
|
463
|
+
function mountNested(factory, context) {
|
|
464
|
+
return context.chain.length === 0 ? factory(context.page) : factory(lazyLocatorOf(context.chain))(context.page);
|
|
465
|
+
}
|
|
466
|
+
function mountBuilt(built, context) {
|
|
467
|
+
if (isSurface(built)) {
|
|
468
|
+
return mountNested(built, context);
|
|
469
|
+
}
|
|
470
|
+
if (typeof built === "function") {
|
|
471
|
+
return built.call(
|
|
472
|
+
context.instance(),
|
|
473
|
+
context.mount
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
return built;
|
|
477
|
+
}
|
|
478
|
+
function instantiateBoundElement(bound, context) {
|
|
479
|
+
if ("locator" in bound) {
|
|
480
|
+
const { locator, params } = bound;
|
|
481
|
+
return params === void 0 ? resolveLocator(context.mount, locator) : (...values) => resolveLocator(
|
|
482
|
+
context.mount,
|
|
483
|
+
locator,
|
|
484
|
+
Object.fromEntries(
|
|
485
|
+
params.flatMap((param, index) => {
|
|
486
|
+
const value = values[index];
|
|
487
|
+
return value === void 0 ? [] : [[param.name, value]];
|
|
488
|
+
})
|
|
489
|
+
)
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if ("parametric" in bound) {
|
|
493
|
+
const { build } = bound.parametric;
|
|
494
|
+
return (...values) => {
|
|
495
|
+
const built = build(...values);
|
|
496
|
+
if (!isLazyLocator(built)) {
|
|
497
|
+
throw new Error("parametric element did not build a lazy locator");
|
|
498
|
+
}
|
|
499
|
+
return resolveLocator(context.mount, built.chain);
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
if ("parametricOpaque" in bound) {
|
|
503
|
+
const { build } = bound.parametricOpaque;
|
|
504
|
+
return (...values) => {
|
|
505
|
+
const built = build(...values);
|
|
506
|
+
if (typeof built !== "function") {
|
|
507
|
+
throw new Error("parametric element did not build a function");
|
|
508
|
+
}
|
|
509
|
+
return mountBuilt(built, context);
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
if ("surface" in bound) {
|
|
513
|
+
return mountNested(bound.surface.factory, context);
|
|
514
|
+
}
|
|
515
|
+
if ("overlay" in bound) {
|
|
516
|
+
const mounted = mountNested(bound.overlay.factory, context);
|
|
517
|
+
const { open } = bound.overlay;
|
|
518
|
+
return open === void 0 ? mounted : Object.assign(mounted, {
|
|
519
|
+
open: async () => {
|
|
520
|
+
await open.call(context.instance());
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
const factory = bound.opaque;
|
|
525
|
+
return isParameterized(factory) ? (...params) => mountBuilt(factory(...params), context) : mountBuilt(factory, context);
|
|
526
|
+
}
|
|
527
|
+
function instantiateBound(elements, context) {
|
|
528
|
+
const instance = {};
|
|
529
|
+
const settled = /* @__PURE__ */ new Map();
|
|
530
|
+
const resolving = /* @__PURE__ */ new Set();
|
|
531
|
+
for (const [key, bound] of Object.entries(elements)) {
|
|
532
|
+
Object.defineProperty(instance, key, {
|
|
533
|
+
enumerable: true,
|
|
534
|
+
configurable: true,
|
|
535
|
+
get() {
|
|
536
|
+
if (settled.has(key)) {
|
|
537
|
+
return settled.get(key);
|
|
538
|
+
}
|
|
539
|
+
if (resolving.has(key)) {
|
|
540
|
+
throw new Error(`element "${key}" is part of a reference cycle`);
|
|
541
|
+
}
|
|
542
|
+
resolving.add(key);
|
|
543
|
+
try {
|
|
544
|
+
const value = instantiateBoundElement(bound, context);
|
|
545
|
+
settled.set(key, value);
|
|
546
|
+
return value;
|
|
547
|
+
} finally {
|
|
548
|
+
resolving.delete(key);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
return instance;
|
|
554
|
+
}
|
|
555
|
+
function instantiateSurface(core, page, prefix, decorate, mountOnly = {}) {
|
|
556
|
+
const chain = [...prefix, ...core.root];
|
|
557
|
+
const self = chain.length === 0 ? void 0 : lazyLocatorOf(chain);
|
|
558
|
+
const root = self === void 0 ? void 0 : resolveLocator(page, chain);
|
|
559
|
+
const instance = { page, elements: {} };
|
|
560
|
+
const grounded = {
|
|
561
|
+
page,
|
|
562
|
+
chain,
|
|
563
|
+
...self === void 0 ? {} : { self, root }
|
|
564
|
+
};
|
|
565
|
+
instance.elements = instantiateBound(
|
|
566
|
+
{ ...core.elements, ...mountOnly },
|
|
567
|
+
{ page, chain, mount: root ?? page, instance: () => instance }
|
|
568
|
+
);
|
|
569
|
+
const decorated = Object.assign(
|
|
570
|
+
instance,
|
|
571
|
+
self === void 0 ? {} : { self, root },
|
|
572
|
+
decorate(grounded)
|
|
573
|
+
);
|
|
574
|
+
for (const [key, action] of Object.entries(core.actions)) {
|
|
575
|
+
instance[key] = (...params) => action.call(instance, ...params);
|
|
576
|
+
}
|
|
577
|
+
return decorated;
|
|
578
|
+
}
|
|
579
|
+
function surfaceOf(core, factory, extra) {
|
|
580
|
+
return Object.assign(factory, extra, {
|
|
581
|
+
descriptor: core.descriptor,
|
|
582
|
+
[coreKey]: core
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
function rootedOf(grounded) {
|
|
586
|
+
if (grounded.self === void 0 || grounded.root === void 0) {
|
|
587
|
+
throw new Error("this surface is not mounted at a chain");
|
|
588
|
+
}
|
|
589
|
+
return { self: grounded.self, root: grounded.root };
|
|
590
|
+
}
|
|
591
|
+
function composedCore(core, scope) {
|
|
592
|
+
const root = [...scope.chain, ...core.root];
|
|
593
|
+
return { ...core, root, descriptor: { ...core.descriptor, root } };
|
|
594
|
+
}
|
|
595
|
+
function pointedFactoryOf(core) {
|
|
596
|
+
function factory(scope) {
|
|
597
|
+
return isLazyLocator(scope) ? pointedFactoryOf(composedCore(core, scope)) : instantiateSurface(core, scope, [], rootedOf);
|
|
598
|
+
}
|
|
599
|
+
return surfaceOf(core, factory, {});
|
|
600
|
+
}
|
|
601
|
+
function componentFactoryOf(core) {
|
|
602
|
+
function factory(scope) {
|
|
603
|
+
return isLazyLocator(scope) ? pointedFactoryOf(composedCore(core, scope)) : instantiateSurface(core, scope, [], () => ({}));
|
|
604
|
+
}
|
|
605
|
+
return surfaceOf(core, factory, {});
|
|
606
|
+
}
|
|
607
|
+
var componentReserved = ["elements", "page", "self", "root", "pages"];
|
|
608
|
+
function defineComponent(definition) {
|
|
609
|
+
return definer(definition, (def) => {
|
|
610
|
+
const root = def.root?.chain ?? [];
|
|
611
|
+
const owner = "the component";
|
|
612
|
+
const { elements, actions, capture } = assemble(
|
|
613
|
+
def,
|
|
614
|
+
owner,
|
|
615
|
+
componentReserved
|
|
616
|
+
);
|
|
617
|
+
const captureRules = [
|
|
618
|
+
...capture,
|
|
619
|
+
...captureRulesOf(def.capture?.rules, owner, projectElements(elements))
|
|
620
|
+
];
|
|
621
|
+
return componentFactoryOf({
|
|
622
|
+
root,
|
|
623
|
+
elements,
|
|
624
|
+
actions,
|
|
625
|
+
descriptor: projectSurface(root, elements, actions, captureRules)
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
var overlayReserved = [
|
|
630
|
+
...componentReserved,
|
|
631
|
+
"label",
|
|
632
|
+
"name",
|
|
633
|
+
"waitFor",
|
|
634
|
+
"open",
|
|
635
|
+
"close"
|
|
636
|
+
];
|
|
637
|
+
function overlayDescriptorOf(root, name, label, close, elements, actions, captureRules) {
|
|
638
|
+
const projectedActions = classifyActions(actions);
|
|
639
|
+
const capture = captureDescriptorOf(captureRules);
|
|
640
|
+
return {
|
|
641
|
+
root,
|
|
642
|
+
...name === void 0 ? {} : { name },
|
|
643
|
+
recorder: { label },
|
|
644
|
+
close: {
|
|
645
|
+
esc: close?.esc ?? true,
|
|
646
|
+
...close?.button === void 0 ? {} : { button: close.button.chain }
|
|
647
|
+
},
|
|
648
|
+
elements: projectElements(elements),
|
|
649
|
+
...projectedActions === void 0 ? {} : { actions: projectedActions },
|
|
650
|
+
...capture === void 0 ? {} : { capture }
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function overlayFactoryOf(core) {
|
|
654
|
+
const name = core.descriptor.name === void 0 ? void 0 : plainTextOf(core.descriptor.name, "overlay name");
|
|
655
|
+
const { label } = core.descriptor.recorder;
|
|
656
|
+
function factory(scope) {
|
|
657
|
+
if (isLazyLocator(scope)) {
|
|
658
|
+
return overlay;
|
|
659
|
+
}
|
|
660
|
+
const instance = instantiateSurface(core, scope, [], (grounded) => {
|
|
661
|
+
const { root, self } = grounded;
|
|
662
|
+
if (root === void 0 || self === void 0) {
|
|
663
|
+
throw new Error("an overlay needs a root");
|
|
664
|
+
}
|
|
665
|
+
const waitFor = async (state = "visible") => {
|
|
666
|
+
await root.waitFor({ state });
|
|
667
|
+
};
|
|
668
|
+
const open = async () => {
|
|
669
|
+
await Promise.resolve();
|
|
670
|
+
throw new Error(
|
|
671
|
+
`the "${label}" overlay is evoked elsewhere \u2014 await waitFor() instead of open()`
|
|
672
|
+
);
|
|
673
|
+
};
|
|
674
|
+
const close = async () => {
|
|
675
|
+
if (core.close.button !== void 0) {
|
|
676
|
+
await core.close.button(root).click();
|
|
677
|
+
} else if (core.close.esc) {
|
|
678
|
+
await grounded.page.keyboard.press("Escape");
|
|
679
|
+
} else {
|
|
680
|
+
throw new Error(
|
|
681
|
+
`declare how the "${label}" overlay closes: a close button or esc`
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
await root.waitFor({ state: "hidden" });
|
|
685
|
+
};
|
|
686
|
+
return {
|
|
687
|
+
...name === void 0 ? {} : { name },
|
|
688
|
+
label,
|
|
689
|
+
self,
|
|
690
|
+
root,
|
|
691
|
+
waitFor,
|
|
692
|
+
open,
|
|
693
|
+
close
|
|
694
|
+
};
|
|
695
|
+
});
|
|
696
|
+
Object.defineProperty(instance, "descriptor", {
|
|
697
|
+
value: core.descriptor,
|
|
698
|
+
enumerable: false
|
|
699
|
+
});
|
|
700
|
+
return instance;
|
|
701
|
+
}
|
|
702
|
+
const overlay = surfaceOf(core, factory, {
|
|
703
|
+
overlay: true
|
|
704
|
+
});
|
|
705
|
+
return overlay;
|
|
706
|
+
}
|
|
707
|
+
function closeOf(close) {
|
|
708
|
+
const button = close?.button;
|
|
709
|
+
return {
|
|
710
|
+
esc: close?.esc ?? true,
|
|
711
|
+
...button === void 0 ? {} : {
|
|
712
|
+
button: (root) => resolveLocator(root, button.chain)
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
function overlayOf(def, root, name, label, owner) {
|
|
717
|
+
const { elements, actions, capture } = assemble(def, owner, overlayReserved);
|
|
718
|
+
const captureRules = [
|
|
719
|
+
...capture,
|
|
720
|
+
...captureRulesOf(def.capture?.rules, owner, projectElements(elements))
|
|
721
|
+
];
|
|
722
|
+
return overlayFactoryOf({
|
|
723
|
+
root,
|
|
724
|
+
elements,
|
|
725
|
+
actions,
|
|
726
|
+
close: closeOf(def.close),
|
|
727
|
+
descriptor: overlayDescriptorOf(
|
|
728
|
+
root,
|
|
729
|
+
name,
|
|
730
|
+
label,
|
|
731
|
+
def.close,
|
|
732
|
+
elements,
|
|
733
|
+
actions,
|
|
734
|
+
captureRules
|
|
735
|
+
)
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function defineOverlay(definition) {
|
|
739
|
+
return definer(
|
|
740
|
+
definition,
|
|
741
|
+
(def) => overlayOf(
|
|
742
|
+
def,
|
|
743
|
+
def.root.chain,
|
|
744
|
+
void 0,
|
|
745
|
+
def.recorder.label,
|
|
746
|
+
`the "${def.recorder.label}" overlay`
|
|
747
|
+
)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
function defineDialog(definition) {
|
|
751
|
+
return definer(definition, (def) => {
|
|
752
|
+
const name = textFrom(def.name);
|
|
753
|
+
const root = [
|
|
754
|
+
{ by: "role", role: def.role ?? "dialog", name }
|
|
755
|
+
];
|
|
756
|
+
const owner = `the ${typeof def.name === "string" ? `"${def.name}"` : def.name.source} dialog`;
|
|
757
|
+
const label = def.recorder?.label ?? def.name;
|
|
758
|
+
if (typeof label !== "string") {
|
|
759
|
+
throw new Error(`${owner} needs recorder.label: its name is a pattern`);
|
|
760
|
+
}
|
|
761
|
+
return overlayOf(def, root, name, label, owner);
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
function readyOf(ready) {
|
|
765
|
+
if (ready === void 0) {
|
|
766
|
+
return waitForHydration;
|
|
767
|
+
}
|
|
768
|
+
if (typeof ready === "function") {
|
|
769
|
+
return ready;
|
|
770
|
+
}
|
|
771
|
+
return async (page) => {
|
|
772
|
+
await page.locator(ready.selector).waitFor({ state: "attached" });
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function readyDescriptorOf(ready) {
|
|
776
|
+
if (ready === void 0) {
|
|
777
|
+
return void 0;
|
|
778
|
+
}
|
|
779
|
+
return typeof ready === "function" ? { opaque: true } : ready;
|
|
780
|
+
}
|
|
781
|
+
function classifyAnchor(pageName, anchor, elements, hasHeader) {
|
|
782
|
+
if (anchor === void 0) {
|
|
783
|
+
return hasHeader ? { anchor: { key: "header" } } : {};
|
|
784
|
+
}
|
|
785
|
+
if (isLazyLocator(anchor)) {
|
|
786
|
+
return { descriptor: anchor.chain, anchor: { chain: anchor.chain } };
|
|
787
|
+
}
|
|
788
|
+
const bound = elements[anchor];
|
|
789
|
+
if (bound === void 0) {
|
|
790
|
+
throw new Error(
|
|
791
|
+
`the "${pageName}" page's anchor references unknown element "${anchor}"`
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
if ("locator" in bound && bound.params === void 0) {
|
|
795
|
+
return { descriptor: bound.locator, anchor: { key: anchor } };
|
|
796
|
+
}
|
|
797
|
+
if ("opaque" in bound) {
|
|
798
|
+
return { descriptor: { opaque: true }, anchor: { key: anchor } };
|
|
799
|
+
}
|
|
800
|
+
throw new Error(
|
|
801
|
+
`the "${pageName}" page's anchor "${anchor}" must be a plain locator element`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
function anchorElement(pageName, key, elements) {
|
|
805
|
+
const candidate = elements[key];
|
|
806
|
+
if (candidate === void 0 || typeof candidate.waitFor !== "function") {
|
|
807
|
+
throw new Error(
|
|
808
|
+
`the "${pageName}" page's anchor "${key}" did not produce a locator`
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
return candidate;
|
|
812
|
+
}
|
|
813
|
+
var pageReserved = [
|
|
814
|
+
...componentReserved,
|
|
815
|
+
"path",
|
|
816
|
+
"name",
|
|
817
|
+
"goto",
|
|
818
|
+
"url",
|
|
819
|
+
"params",
|
|
820
|
+
"waitFor"
|
|
821
|
+
];
|
|
822
|
+
var bindKey = /* @__PURE__ */ Symbol("walkwright.bind");
|
|
823
|
+
function isBindable(value) {
|
|
824
|
+
return typeof value === "function" && bindKey in value;
|
|
825
|
+
}
|
|
826
|
+
function pageFactoryOf(core, binding) {
|
|
827
|
+
const { path, name } = core.descriptor;
|
|
828
|
+
const ready = readyOf(core.ready ?? binding?.ready);
|
|
829
|
+
const anchorOf = (page, elements) => {
|
|
830
|
+
const anchor = core.anchor;
|
|
831
|
+
if (anchor === void 0) {
|
|
832
|
+
return void 0;
|
|
833
|
+
}
|
|
834
|
+
return "chain" in anchor ? resolveLocator(page, anchor.chain) : anchorElement(name, anchor.key, elements);
|
|
835
|
+
};
|
|
836
|
+
const factory = (page) => {
|
|
837
|
+
const url = (params) => fillPath(path, params);
|
|
838
|
+
const instance = instantiateSurface(
|
|
839
|
+
core,
|
|
840
|
+
page,
|
|
841
|
+
[],
|
|
842
|
+
() => ({
|
|
843
|
+
path,
|
|
844
|
+
name,
|
|
845
|
+
async goto(params) {
|
|
846
|
+
await page.goto(url(params));
|
|
847
|
+
await ready(page);
|
|
848
|
+
await anchorOf(page, this.elements)?.waitFor();
|
|
849
|
+
},
|
|
850
|
+
url,
|
|
851
|
+
params: () => matchPath(path, page.url()),
|
|
852
|
+
async waitFor() {
|
|
853
|
+
await anchorOf(page, this.elements)?.waitFor();
|
|
854
|
+
}
|
|
855
|
+
})
|
|
856
|
+
);
|
|
857
|
+
let pages;
|
|
858
|
+
Object.defineProperty(instance, "pages", {
|
|
859
|
+
enumerable: true,
|
|
860
|
+
get() {
|
|
861
|
+
if (binding === void 0) {
|
|
862
|
+
throw new Error(
|
|
863
|
+
`the "${name}" page is not app-bound \u2014 access this page through the app`
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
pages ??= binding.pages(page);
|
|
867
|
+
return pages;
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
return instance;
|
|
871
|
+
};
|
|
872
|
+
return Object.assign(factory, {
|
|
873
|
+
descriptor: core.descriptor,
|
|
874
|
+
[bindKey]: (next) => pageFactoryOf(core, next)
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
function recorderOf(recorder) {
|
|
878
|
+
if (recorder === void 0) {
|
|
879
|
+
return void 0;
|
|
880
|
+
}
|
|
881
|
+
const projected = {
|
|
882
|
+
...recorder.group === void 0 ? {} : { group: recorder.group },
|
|
883
|
+
...recorder.label === void 0 ? {} : { label: recorder.label }
|
|
884
|
+
};
|
|
885
|
+
return Object.keys(projected).length === 0 ? void 0 : projected;
|
|
886
|
+
}
|
|
887
|
+
function pageFactoryFrom(def) {
|
|
888
|
+
const owner = `the "${def.name}" page`;
|
|
889
|
+
const assembled = assemble(def, owner, pageReserved);
|
|
890
|
+
if (def.header !== void 0 && "header" in assembled.elements) {
|
|
891
|
+
throw new Error(`${owner} declares both header and elements.header`);
|
|
892
|
+
}
|
|
893
|
+
const elements = {
|
|
894
|
+
...def.header === void 0 ? {} : {
|
|
895
|
+
header: {
|
|
896
|
+
locator: [
|
|
897
|
+
{ by: "role", role: "heading", name: textFrom(def.header) }
|
|
898
|
+
]
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
...assembled.elements
|
|
902
|
+
};
|
|
903
|
+
const { actions } = assembled;
|
|
904
|
+
const anchor = classifyAnchor(
|
|
905
|
+
def.name,
|
|
906
|
+
def.anchor,
|
|
907
|
+
elements,
|
|
908
|
+
def.header !== void 0
|
|
909
|
+
);
|
|
910
|
+
const recorder = recorderOf(def.recorder);
|
|
911
|
+
const ready = readyDescriptorOf(def.ready);
|
|
912
|
+
const projectedActions = classifyActions(actions);
|
|
913
|
+
const captureRules = [
|
|
914
|
+
...assembled.capture,
|
|
915
|
+
...captureRulesOf(def.capture?.rules, owner, projectElements(elements))
|
|
916
|
+
];
|
|
917
|
+
const capture = captureDescriptorOf(captureRules);
|
|
918
|
+
const descriptor = {
|
|
919
|
+
path: def.path,
|
|
920
|
+
name: def.name,
|
|
921
|
+
...recorder === void 0 ? {} : { recorder },
|
|
922
|
+
...ready === void 0 ? {} : { ready },
|
|
923
|
+
...def.header === void 0 ? {} : { header: textFrom(def.header) },
|
|
924
|
+
...anchor.descriptor === void 0 ? {} : { anchor: anchor.descriptor },
|
|
925
|
+
elements: projectElements(elements),
|
|
926
|
+
...projectedActions === void 0 ? {} : { actions: projectedActions },
|
|
927
|
+
...capture === void 0 ? {} : { capture }
|
|
928
|
+
};
|
|
929
|
+
return pageFactoryOf({
|
|
930
|
+
root: [],
|
|
931
|
+
...anchor.anchor === void 0 ? {} : { anchor: anchor.anchor },
|
|
932
|
+
...def.ready === void 0 ? {} : { ready: def.ready },
|
|
933
|
+
elements,
|
|
934
|
+
actions,
|
|
935
|
+
descriptor
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
function presetBaseOf(outer, base) {
|
|
939
|
+
const ready = base.ready ?? outer?.ready;
|
|
940
|
+
return {
|
|
941
|
+
path: `${outer?.path ?? ""}${base.path}`,
|
|
942
|
+
include: [...outer?.include ?? [], ...base.include ?? []],
|
|
943
|
+
group: [...outer?.group ?? [], ...base.recorder?.group ?? []],
|
|
944
|
+
capture: [...base.capture?.rules ?? [], ...outer?.capture ?? []],
|
|
945
|
+
...ready === void 0 ? {} : { ready }
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
function presetOf(base) {
|
|
949
|
+
const apply = (def) => {
|
|
950
|
+
const group = [...base.group, ...def.recorder?.group ?? []];
|
|
951
|
+
const recorder = {
|
|
952
|
+
...def.recorder,
|
|
953
|
+
...group.length === 0 ? {} : { group }
|
|
954
|
+
};
|
|
955
|
+
const ready = def.ready ?? base.ready;
|
|
956
|
+
const capture = [...def.capture?.rules ?? [], ...base.capture];
|
|
957
|
+
return {
|
|
958
|
+
...def,
|
|
959
|
+
path: `${base.path}${def.path}`,
|
|
960
|
+
include: [...base.include, ...def.include ?? []],
|
|
961
|
+
recorder,
|
|
962
|
+
...capture.length === 0 ? {} : { capture: { rules: capture } },
|
|
963
|
+
...ready === void 0 ? {} : { ready }
|
|
964
|
+
};
|
|
965
|
+
};
|
|
966
|
+
const preset = (definition) => definer(definition, (def) => pageFactoryFrom(apply(def)));
|
|
967
|
+
return Object.assign(preset, {
|
|
968
|
+
path: base.path,
|
|
969
|
+
include: base.include,
|
|
970
|
+
preset: (sub) => presetOf(presetBaseOf(base, sub))
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
var definePage = Object.assign(
|
|
974
|
+
(definition) => definer(definition, pageFactoryFrom),
|
|
975
|
+
{
|
|
976
|
+
preset: (base) => presetOf(presetBaseOf(void 0, base))
|
|
977
|
+
}
|
|
978
|
+
);
|
|
979
|
+
function boundPagesOf(pages, page) {
|
|
980
|
+
const bound = {};
|
|
981
|
+
const settled = /* @__PURE__ */ new Map();
|
|
982
|
+
for (const [key, factory] of Object.entries(pages)) {
|
|
983
|
+
Object.defineProperty(bound, key, {
|
|
984
|
+
enumerable: true,
|
|
985
|
+
get() {
|
|
986
|
+
if (!settled.has(key)) {
|
|
987
|
+
settled.set(key, factory(page));
|
|
988
|
+
}
|
|
989
|
+
return settled.get(key);
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
return bound;
|
|
994
|
+
}
|
|
995
|
+
function appOf(def, descriptor) {
|
|
996
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
997
|
+
for (const page of descriptor.pages) {
|
|
998
|
+
const holder = byPath.get(page.path);
|
|
999
|
+
if (holder !== void 0) {
|
|
1000
|
+
throw new Error(
|
|
1001
|
+
`pages "${holder}" and "${page.name}" both claim the path ${page.path}`
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
byPath.set(page.path, page.name);
|
|
1005
|
+
}
|
|
1006
|
+
const bound = {};
|
|
1007
|
+
const binding = {
|
|
1008
|
+
...def.ready === void 0 ? {} : { ready: def.ready },
|
|
1009
|
+
pages: (page) => boundPagesOf(bound, page)
|
|
1010
|
+
};
|
|
1011
|
+
for (const [key, factory] of Object.entries(def.pages)) {
|
|
1012
|
+
if (!isBindable(factory)) {
|
|
1013
|
+
throw new Error(`"${key}" is not a page defined with definePage`);
|
|
1014
|
+
}
|
|
1015
|
+
bound[key] = factory[bindKey](binding);
|
|
1016
|
+
}
|
|
1017
|
+
const app = {
|
|
1018
|
+
pages: bound,
|
|
1019
|
+
descriptor,
|
|
1020
|
+
fixture: ((provisions, options) => {
|
|
1021
|
+
const test = (options?.base ?? playwrightTest).extend({
|
|
1022
|
+
...createRecorderFixtures(app),
|
|
1023
|
+
pages: async ({ page }, use) => {
|
|
1024
|
+
await use(boundPagesOf(bound, page));
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
return provisions === void 0 ? test : test.extend({
|
|
1028
|
+
provision: async ({ page }, use) => {
|
|
1029
|
+
var _stack = [];
|
|
1030
|
+
try {
|
|
1031
|
+
const provisioner = __using(_stack, createProvisioner(page, provisions), true);
|
|
1032
|
+
await use(provisioner.provision);
|
|
1033
|
+
} catch (_) {
|
|
1034
|
+
var _error = _, _hasError = true;
|
|
1035
|
+
} finally {
|
|
1036
|
+
var _promise = __callDispose(_stack, _error, _hasError);
|
|
1037
|
+
_promise && await _promise;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
})
|
|
1042
|
+
};
|
|
1043
|
+
return app;
|
|
1044
|
+
}
|
|
1045
|
+
function defineApp(def) {
|
|
1046
|
+
const ready = readyDescriptorOf(def.ready);
|
|
1047
|
+
const captureRules = (def.capture?.rules ?? []).map(
|
|
1048
|
+
(rule) => ({
|
|
1049
|
+
match: regexDescriptorOf(rule.match),
|
|
1050
|
+
text: rule.text
|
|
1051
|
+
})
|
|
1052
|
+
);
|
|
1053
|
+
const capture = captureDescriptorOf(captureRules);
|
|
1054
|
+
return appOf(def, {
|
|
1055
|
+
...ready === void 0 ? {} : { ready },
|
|
1056
|
+
pages: Object.values(def.pages).map((page) => page.descriptor).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0),
|
|
1057
|
+
...capture === void 0 ? {} : { capture }
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
function loadElement(descriptor) {
|
|
1061
|
+
if ("locator" in descriptor) {
|
|
1062
|
+
return descriptor.params === void 0 ? { locator: descriptor.locator } : { locator: descriptor.locator, params: descriptor.params };
|
|
1063
|
+
}
|
|
1064
|
+
if ("surface" in descriptor) {
|
|
1065
|
+
return { surface: loadSurface(descriptor.surface) };
|
|
1066
|
+
}
|
|
1067
|
+
if ("overlay" in descriptor) {
|
|
1068
|
+
return { overlay: loadOverlay(descriptor) };
|
|
1069
|
+
}
|
|
1070
|
+
return void 0;
|
|
1071
|
+
}
|
|
1072
|
+
function loadElements(elements) {
|
|
1073
|
+
const bound = {};
|
|
1074
|
+
for (const [key, descriptor] of Object.entries(elements)) {
|
|
1075
|
+
const loaded = loadElement(descriptor);
|
|
1076
|
+
if (loaded !== void 0) {
|
|
1077
|
+
bound[key] = loaded;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return bound;
|
|
1081
|
+
}
|
|
1082
|
+
function loadSurface(descriptor) {
|
|
1083
|
+
return {
|
|
1084
|
+
factory: componentFactoryOf({
|
|
1085
|
+
root: descriptor.root ?? [],
|
|
1086
|
+
elements: loadElements(descriptor.elements),
|
|
1087
|
+
actions: {},
|
|
1088
|
+
descriptor
|
|
1089
|
+
}),
|
|
1090
|
+
descriptor
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
function loadOverlay(descriptor) {
|
|
1094
|
+
const { overlay } = descriptor;
|
|
1095
|
+
const button = overlay.close.button;
|
|
1096
|
+
return {
|
|
1097
|
+
factory: overlayFactoryOf({
|
|
1098
|
+
root: overlay.root,
|
|
1099
|
+
elements: loadElements(overlay.elements),
|
|
1100
|
+
actions: {},
|
|
1101
|
+
close: {
|
|
1102
|
+
esc: overlay.close.esc,
|
|
1103
|
+
...button === void 0 ? {} : {
|
|
1104
|
+
button: (root) => resolveLocator(root, button)
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
descriptor: overlay
|
|
1108
|
+
}),
|
|
1109
|
+
descriptor
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
function loadPage(descriptor) {
|
|
1113
|
+
const ready = descriptor.ready !== void 0 && "selector" in descriptor.ready ? descriptor.ready : void 0;
|
|
1114
|
+
const anchor = Array.isArray(descriptor.anchor) ? { chain: descriptor.anchor } : descriptor.header === void 0 ? void 0 : { key: "header" };
|
|
1115
|
+
return pageFactoryOf({
|
|
1116
|
+
root: [],
|
|
1117
|
+
...anchor === void 0 ? {} : { anchor },
|
|
1118
|
+
...ready === void 0 ? {} : { ready },
|
|
1119
|
+
elements: loadElements(descriptor.elements),
|
|
1120
|
+
actions: {},
|
|
1121
|
+
descriptor
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
function loadApp(raw) {
|
|
1125
|
+
const parsed = appDescriptorSchema.parse(raw);
|
|
1126
|
+
const pages = Object.fromEntries(
|
|
1127
|
+
parsed.pages.map((page) => [page.name, loadPage(page)])
|
|
1128
|
+
);
|
|
1129
|
+
const ready = parsed.ready !== void 0 && "selector" in parsed.ready ? parsed.ready : void 0;
|
|
1130
|
+
return appOf(
|
|
1131
|
+
{
|
|
1132
|
+
pages,
|
|
1133
|
+
...ready === void 0 ? {} : { ready }
|
|
1134
|
+
},
|
|
1135
|
+
parsed
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
async function arriveAt(destination, page) {
|
|
1139
|
+
const arrived = destination(page);
|
|
1140
|
+
await arrived.waitFor();
|
|
1141
|
+
return arrived;
|
|
1142
|
+
}
|
|
1143
|
+
async function waitForHydration(page, timeoutMs = 6e4) {
|
|
1144
|
+
const marker = page.locator('html[data-hydrated="true"]');
|
|
1145
|
+
const attached = await marker.waitFor({ state: "attached", timeout: timeoutMs }).then(
|
|
1146
|
+
() => true,
|
|
1147
|
+
() => false
|
|
1148
|
+
);
|
|
1149
|
+
if (!attached) {
|
|
1150
|
+
await page.reload();
|
|
1151
|
+
await marker.waitFor({ state: "attached", timeout: timeoutMs });
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
function pathParams(path, url) {
|
|
1155
|
+
return matchPath(path, url);
|
|
1156
|
+
}
|
|
1157
|
+
function matchPath(path, url) {
|
|
1158
|
+
const pattern = new RegExp(
|
|
1159
|
+
`^${path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/:[A-Za-z]+/g, "([^/]+)")}(?:/|$)`
|
|
1160
|
+
);
|
|
1161
|
+
const names = [...path.matchAll(/:([A-Za-z]+)/g)].map(([, name]) => name);
|
|
1162
|
+
const pathname = new URL(url).pathname;
|
|
1163
|
+
const match = pattern.exec(pathname);
|
|
1164
|
+
if (match === null) {
|
|
1165
|
+
throw new Error(`${pathname} does not match ${path}`);
|
|
1166
|
+
}
|
|
1167
|
+
return Object.fromEntries(
|
|
1168
|
+
names.map((name, index) => [
|
|
1169
|
+
name,
|
|
1170
|
+
decodeURIComponent(match[index + 1] ?? "")
|
|
1171
|
+
])
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
function fillPath(path, params) {
|
|
1175
|
+
return path.replace(/:([A-Za-z]+)/g, (match, name) => {
|
|
1176
|
+
const value = params[name];
|
|
1177
|
+
if (value === void 0) {
|
|
1178
|
+
throw new Error(`missing path param ${match}`);
|
|
1179
|
+
}
|
|
1180
|
+
return encodeURIComponent(value);
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
export {
|
|
1185
|
+
rootOf,
|
|
1186
|
+
defineComponent,
|
|
1187
|
+
defineOverlay,
|
|
1188
|
+
defineDialog,
|
|
1189
|
+
definePage,
|
|
1190
|
+
defineApp,
|
|
1191
|
+
loadPage,
|
|
1192
|
+
loadApp,
|
|
1193
|
+
arriveAt,
|
|
1194
|
+
waitForHydration,
|
|
1195
|
+
pathParams
|
|
1196
|
+
};
|
|
1197
|
+
//# sourceMappingURL=chunk-6YRKPGUO.js.map
|