executable-stories-showcase 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +17 -2
- package/dist/adapters/cookies.d.ts +11 -0
- package/dist/adapters/cookies.js +27 -0
- package/dist/adapters/cookies.js.map +1 -0
- package/dist/adapters/redux.d.ts +23 -0
- package/dist/adapters/redux.js +28 -0
- package/dist/adapters/redux.js.map +1 -0
- package/dist/adapters/tanstack-query.d.ts +17 -0
- package/dist/adapters/tanstack-query.js +43 -0
- package/dist/adapters/tanstack-query.js.map +1 -0
- package/dist/adapters/xstate.d.ts +33 -0
- package/dist/adapters/xstate.js +36 -0
- package/dist/adapters/xstate.js.map +1 -0
- package/dist/adapters/zustand.d.ts +17 -0
- package/dist/adapters/zustand.js +16 -0
- package/dist/adapters/zustand.js.map +1 -0
- package/dist/capture-CIKR46QE.js +10 -0
- package/dist/capture-CIKR46QE.js.map +1 -0
- package/dist/capture.d.ts +26 -0
- package/dist/catalog.d.ts +27 -0
- package/dist/chunk-BK4EEQXV.js +98 -0
- package/dist/chunk-BK4EEQXV.js.map +1 -0
- package/dist/chunk-C755RCRL.js +125 -0
- package/dist/chunk-C755RCRL.js.map +1 -0
- package/dist/chunk-COLHEOSE.js +148 -0
- package/dist/chunk-COLHEOSE.js.map +1 -0
- package/dist/chunk-DETES767.js +43 -0
- package/dist/chunk-DETES767.js.map +1 -0
- package/dist/chunk-ESIS3D6R.js +47 -0
- package/dist/chunk-ESIS3D6R.js.map +1 -0
- package/dist/chunk-MCC3CAWQ.js +208 -0
- package/dist/chunk-MCC3CAWQ.js.map +1 -0
- package/dist/chunk-XLNYICOI.js +234 -0
- package/dist/chunk-XLNYICOI.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +531 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +7 -0
- package/dist/link.d.ts +33 -0
- package/dist/network-I5SB4F6T.js +7 -0
- package/dist/network-I5SB4F6T.js.map +1 -0
- package/dist/network.d.ts +3 -0
- package/dist/next.d.ts +21 -0
- package/dist/next.js +30 -0
- package/dist/next.js.map +1 -0
- package/dist/no-msw-browser.d.ts +2 -0
- package/dist/no-msw-browser.js +8 -0
- package/dist/no-msw-browser.js.map +1 -0
- package/dist/panel.d.ts +30 -0
- package/dist/panel.js +607 -0
- package/dist/panel.js.map +1 -0
- package/dist/playwright.d.ts +31 -0
- package/dist/playwright.js +92 -0
- package/dist/playwright.js.map +1 -0
- package/dist/ready-4MENAMNI.js +13 -0
- package/dist/ready-4MENAMNI.js.map +1 -0
- package/dist/ready.d.ts +39 -0
- package/dist/record.d.ts +49 -0
- package/dist/redact.d.ts +18 -0
- package/dist/runtime.d.ts +191 -0
- package/dist/save.d.ts +27 -0
- package/dist/schema.d.ts +161 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +26 -0
- package/dist/vite.js.map +1 -0
- package/package.json +118 -3
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseScenario
|
|
3
|
+
} from "./chunk-BK4EEQXV.js";
|
|
4
|
+
|
|
5
|
+
// src/catalog.ts
|
|
6
|
+
var defineScenarioRules = (rules) => rules;
|
|
7
|
+
function createCatalog(raw, { rules = [] } = {}) {
|
|
8
|
+
const byName = /* @__PURE__ */ new Map();
|
|
9
|
+
for (const [i, data] of raw.entries()) {
|
|
10
|
+
const s = parseScenario(data, `#${i}`);
|
|
11
|
+
if (s.name.includes(",")) throw new Error(`Scenario name "${s.name}" can't contain a comma`);
|
|
12
|
+
if (byName.has(s.name)) throw new Error(`Duplicate scenario name "${s.name}"`);
|
|
13
|
+
byName.set(s.name, s);
|
|
14
|
+
}
|
|
15
|
+
const flatten = (s, seen) => {
|
|
16
|
+
if (seen.includes(s.name)) {
|
|
17
|
+
throw new Error(`Circular extends: ${[...seen, s.name].join(" -> ")}`);
|
|
18
|
+
}
|
|
19
|
+
const parents = [s.extends ?? []].flat().map((name) => {
|
|
20
|
+
const parent = byName.get(name);
|
|
21
|
+
if (!parent) throw new Error(`Scenario "${s.name}" extends unknown "${name}"`);
|
|
22
|
+
return flatten(parent, [...seen, s.name]);
|
|
23
|
+
});
|
|
24
|
+
const { extends: _extends, $schema: _schema, ...own } = s;
|
|
25
|
+
return [...parents, own].reduce(merge, { name: own.name });
|
|
26
|
+
};
|
|
27
|
+
const resolve = (s) => {
|
|
28
|
+
const flat = flatten(s, []);
|
|
29
|
+
const violations = rules.flatMap((rule) => rule(flat) ?? []);
|
|
30
|
+
if (violations.length) {
|
|
31
|
+
throw new Error(`Scenario "${s.name}" breaks rules:
|
|
32
|
+
${violations.map((m) => `- ${m}`).join("\n")}`);
|
|
33
|
+
}
|
|
34
|
+
return flat;
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
list: [...byName.values()],
|
|
38
|
+
get: (name) => byName.get(name),
|
|
39
|
+
resolve,
|
|
40
|
+
check: () => [...byName.values()].flatMap((s) => {
|
|
41
|
+
try {
|
|
42
|
+
resolve(s);
|
|
43
|
+
return [];
|
|
44
|
+
} catch (err) {
|
|
45
|
+
return [err instanceof Error ? err.message : String(err)];
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
var byKey = ([a], [b]) => a.localeCompare(b);
|
|
51
|
+
var routeKey = (e) => `${e.method} ${e.path} ${JSON.stringify(Object.entries(e.query ?? {}).toSorted(byKey))}`;
|
|
52
|
+
function merge(base, over) {
|
|
53
|
+
const overKeys = new Set((over.network ?? []).map(routeKey));
|
|
54
|
+
return {
|
|
55
|
+
...base,
|
|
56
|
+
...over,
|
|
57
|
+
// Own-only fields: a child describes and finishes rendering differently.
|
|
58
|
+
description: over.description,
|
|
59
|
+
tags: over.tags,
|
|
60
|
+
ready: over.ready,
|
|
61
|
+
// A null param removes the inherited one.
|
|
62
|
+
url: Object.fromEntries(Object.entries({ ...base.url, ...over.url }).filter(([, v]) => v !== null)),
|
|
63
|
+
network: [
|
|
64
|
+
...(over.network ?? []).filter((e) => !e.remove),
|
|
65
|
+
...(base.network ?? []).filter((e) => !overKeys.has(routeKey(e)))
|
|
66
|
+
],
|
|
67
|
+
handlers: union(over.handlers, base.handlers),
|
|
68
|
+
state: mergeState(base.state ?? {}, over.state ?? {})
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function mergeState(base, over) {
|
|
72
|
+
const out = mergeObjects(base, over);
|
|
73
|
+
for (const [k, v] of Object.entries(over)) {
|
|
74
|
+
if (v === null) out[k] = null;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
var union = (a = [], b = []) => {
|
|
79
|
+
const all = [.../* @__PURE__ */ new Set([...a, ...b])];
|
|
80
|
+
return all.length ? all : void 0;
|
|
81
|
+
};
|
|
82
|
+
var isJsonObject = (value) => value instanceof Object && !Array.isArray(value);
|
|
83
|
+
function mergeObjects(base, over) {
|
|
84
|
+
const out = { ...base };
|
|
85
|
+
for (const [k, v] of Object.entries(over)) {
|
|
86
|
+
if (v === null) delete out[k];
|
|
87
|
+
else out[k] = deepMerge(base[k], v);
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
function deepMerge(base, over) {
|
|
92
|
+
return isJsonObject(base) && isJsonObject(over) ? mergeObjects(base, over) : over;
|
|
93
|
+
}
|
|
94
|
+
function diffScenario(captured, base) {
|
|
95
|
+
if (!base) {
|
|
96
|
+
return compactScenario({
|
|
97
|
+
name: captured.name,
|
|
98
|
+
description: captured.description,
|
|
99
|
+
tags: captured.tags,
|
|
100
|
+
path: captured.path,
|
|
101
|
+
url: captured.url,
|
|
102
|
+
network: captured.network,
|
|
103
|
+
handlers: captured.handlers,
|
|
104
|
+
state: captured.state,
|
|
105
|
+
ready: captured.ready,
|
|
106
|
+
viewport: captured.viewport
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const url = diffUrl(base.url, captured.url);
|
|
110
|
+
const network = diffNetwork(base.network ?? [], captured.network ?? []);
|
|
111
|
+
const state = diffObjects(
|
|
112
|
+
Object.fromEntries(Object.entries(base.state ?? {}).filter(([k]) => k in (captured.state ?? {}))),
|
|
113
|
+
captured.state ?? {}
|
|
114
|
+
);
|
|
115
|
+
const handlers = diffHandlers(base.handlers ?? [], captured.handlers ?? []);
|
|
116
|
+
return compactScenario({
|
|
117
|
+
name: captured.name,
|
|
118
|
+
extends: base.name,
|
|
119
|
+
description: captured.description,
|
|
120
|
+
tags: captured.tags,
|
|
121
|
+
path: captured.path !== base.path ? captured.path : void 0,
|
|
122
|
+
url,
|
|
123
|
+
network: network.length ? network : void 0,
|
|
124
|
+
handlers,
|
|
125
|
+
state,
|
|
126
|
+
ready: captured.ready,
|
|
127
|
+
viewport: jsonEqual(captured.viewport ?? null, base.viewport ?? null) ? void 0 : captured.viewport
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function diffHandlers(base, captured) {
|
|
131
|
+
for (let k = 0; k <= captured.length; k++) {
|
|
132
|
+
const own = captured.slice(0, k);
|
|
133
|
+
if (JSON.stringify(union(own, base)) === JSON.stringify(captured)) return own.length ? own : void 0;
|
|
134
|
+
}
|
|
135
|
+
return captured.length ? captured : void 0;
|
|
136
|
+
}
|
|
137
|
+
function diffUrl(base, captured) {
|
|
138
|
+
const baseUrl = base ?? {};
|
|
139
|
+
const capturedUrl = captured ?? {};
|
|
140
|
+
const out = {};
|
|
141
|
+
for (const [k, v] of Object.entries(capturedUrl)) {
|
|
142
|
+
if (v === null) continue;
|
|
143
|
+
if (baseUrl[k] !== v) out[k] = v;
|
|
144
|
+
}
|
|
145
|
+
for (const [k, v] of Object.entries(baseUrl)) {
|
|
146
|
+
if (v === null) continue;
|
|
147
|
+
if (!(k in capturedUrl) || capturedUrl[k] === null) out[k] = null;
|
|
148
|
+
}
|
|
149
|
+
return Object.keys(out).length ? out : void 0;
|
|
150
|
+
}
|
|
151
|
+
function diffNetwork(base, captured) {
|
|
152
|
+
const baseByKey = new Map(base.map((e) => [routeKey(e), e]));
|
|
153
|
+
const capByKey = /* @__PURE__ */ new Map();
|
|
154
|
+
for (const e of captured) if (!capByKey.has(routeKey(e))) capByKey.set(routeKey(e), e);
|
|
155
|
+
const out = [];
|
|
156
|
+
for (const [key, e] of capByKey) {
|
|
157
|
+
const prior = baseByKey.get(key);
|
|
158
|
+
if (!prior || !jsonEqual(prior, e)) out.push(e);
|
|
159
|
+
}
|
|
160
|
+
for (const [key, e] of baseByKey) {
|
|
161
|
+
if (!capByKey.has(key)) {
|
|
162
|
+
const removed = { method: e.method, path: e.path, remove: true };
|
|
163
|
+
if (e.query) removed.query = e.query;
|
|
164
|
+
out.push(removed);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
function diffObjects(base, captured) {
|
|
170
|
+
const out = {};
|
|
171
|
+
for (const [k, v] of Object.entries(captured)) {
|
|
172
|
+
if (!(k in base)) out[k] = v;
|
|
173
|
+
else if (isJsonObject(base[k]) && isJsonObject(v)) {
|
|
174
|
+
const nested = diffObjects(base[k], v);
|
|
175
|
+
if (nested) out[k] = nested;
|
|
176
|
+
} else if (!jsonEqual(base[k], v)) out[k] = v;
|
|
177
|
+
}
|
|
178
|
+
for (const k of Object.keys(base)) {
|
|
179
|
+
if (!(k in captured)) out[k] = null;
|
|
180
|
+
}
|
|
181
|
+
return Object.keys(out).length ? out : void 0;
|
|
182
|
+
}
|
|
183
|
+
function jsonEqual(a, b) {
|
|
184
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
185
|
+
}
|
|
186
|
+
function compactScenario(s) {
|
|
187
|
+
const out = { name: s.name };
|
|
188
|
+
if (s.$schema !== void 0) out.$schema = s.$schema;
|
|
189
|
+
if (s.description !== void 0) out.description = s.description;
|
|
190
|
+
if (s.tags !== void 0) out.tags = s.tags;
|
|
191
|
+
if (s.extends !== void 0) out.extends = s.extends;
|
|
192
|
+
if (s.path !== void 0) out.path = s.path;
|
|
193
|
+
if (s.url !== void 0 && Object.keys(s.url).length) out.url = s.url;
|
|
194
|
+
if (s.network !== void 0 && s.network.length) out.network = s.network;
|
|
195
|
+
if (s.handlers !== void 0 && s.handlers.length) out.handlers = s.handlers;
|
|
196
|
+
if (s.state !== void 0 && Object.keys(s.state).length) out.state = s.state;
|
|
197
|
+
if (s.ready !== void 0) out.ready = s.ready;
|
|
198
|
+
if (s.viewport !== void 0) out.viewport = s.viewport;
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export {
|
|
203
|
+
defineScenarioRules,
|
|
204
|
+
createCatalog,
|
|
205
|
+
deepMerge,
|
|
206
|
+
diffScenario
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=chunk-MCC3CAWQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/catalog.ts"],"sourcesContent":["import {\n type Json,\n type JsonObject,\n type NetworkEntry,\n parseScenario,\n type Scenario,\n type ScenarioInput,\n} from './schema.js';\n\n/**\n * A business rule a resolved scenario must satisfy, beyond schema shape.\n * Return a message (or several) for each violation.\n */\nexport type ScenarioRule = (scenario: Scenario) => string | string[] | void | undefined;\n\n/** Identity helper for typing rule lists. */\nexport const defineScenarioRules = (rules: ScenarioRule[]) => rules;\n\nexport interface Catalog {\n list: Scenario[];\n get(name: string): Scenario | undefined;\n /** Flatten `extends` into one self-contained scenario, then check the rules. */\n resolve(scenario: Scenario): Scenario;\n /** Resolve every scenario; returns one message per problem. */\n check(): string[];\n}\n\nexport function createCatalog(\n raw: ScenarioInput[],\n { rules = [] }: { rules?: ScenarioRule[]; } = {},\n): Catalog {\n const byName = new Map<string, Scenario>();\n\n for (const [i, data] of raw.entries()) {\n const s = parseScenario(data, `#${i}`);\n\n // Commas join names in ?scenario=a,b, so a catalog name can't contain one.\n if (s.name.includes(',')) throw new Error(`Scenario name \"${s.name}\" can't contain a comma`);\n\n if (byName.has(s.name)) throw new Error(`Duplicate scenario name \"${s.name}\"`);\n byName.set(s.name, s);\n }\n\n const flatten = (s: Scenario, seen: string[]): Scenario => {\n if (seen.includes(s.name)) {\n throw new Error(`Circular extends: ${[...seen, s.name].join(' -> ')}`);\n }\n\n const parents = [s.extends ?? []].flat().map((name) => {\n const parent = byName.get(name);\n\n if (!parent) throw new Error(`Scenario \"${s.name}\" extends unknown \"${name}\"`);\n\n return flatten(parent, [...seen, s.name]);\n });\n\n const { extends: _extends, $schema: _schema, ...own } = s;\n\n // Start from an empty base so standalone scenarios are normalised too (remove entries, nulls).\n return [...parents, own].reduce(merge, { name: own.name });\n };\n\n const resolve = (s: Scenario) => {\n const flat = flatten(s, []);\n const violations = rules.flatMap((rule) => rule(flat) ?? []);\n\n if (violations.length) {\n throw new Error(`Scenario \"${s.name}\" breaks rules:\\n${violations.map((m) => `- ${m}`).join('\\n')}`);\n }\n\n return flat;\n };\n\n return {\n list: [...byName.values()],\n get: (name) => byName.get(name),\n resolve,\n check: () =>\n [...byName.values()].flatMap((s) => {\n try {\n resolve(s);\n\n return [];\n } catch (err) {\n return [err instanceof Error ? err.message : String(err)];\n }\n }),\n };\n}\n\nconst byKey = ([a]: [string, unknown], [b]: [string, unknown]) => a.localeCompare(b);\n\nconst routeKey = (e: NetworkEntry) =>\n `${e.method} ${e.path} ${JSON.stringify(Object.entries(e.query ?? {}).toSorted(byKey))}`;\n\nfunction merge(base: Scenario, over: Scenario): Scenario {\n const overKeys = new Set((over.network ?? []).map(routeKey));\n\n return {\n ...base,\n ...over,\n // Own-only fields: a child describes and finishes rendering differently.\n description: over.description,\n tags: over.tags,\n ready: over.ready,\n // A null param removes the inherited one.\n url: Object.fromEntries(Object.entries({ ...base.url, ...over.url }).filter(([, v]) => v !== null)),\n network: [\n ...(over.network ?? []).filter((e) => !e.remove),\n ...(base.network ?? []).filter((e) => !overKeys.has(routeKey(e))),\n ],\n handlers: union(over.handlers, base.handlers),\n state: mergeState(base.state ?? {}, over.state ?? {}),\n };\n}\n\n/**\n * Like `mergeObjects`, except a top-level `null` sets that slice to null (a signed-out `user`)\n * rather than removing it, so capture can record one and replay restores it.\n */\nfunction mergeState(base: JsonObject, over: JsonObject): JsonObject {\n const out = mergeObjects(base, over);\n\n for (const [k, v] of Object.entries(over)) {\n if (v === null) out[k] = null;\n }\n\n return out;\n}\n\nconst union = (a: string[] = [], b: string[] = []) => {\n const all = [...new Set([...a, ...b])];\n\n return all.length ? all : undefined;\n};\n\nconst isJsonObject = (value: Json | undefined): value is JsonObject =>\n value instanceof Object && !Array.isArray(value);\n\nfunction mergeObjects(base: JsonObject, over: JsonObject): JsonObject {\n const out = { ...base };\n\n for (const [k, v] of Object.entries(over)) {\n if (v === null) delete out[k];\n else out[k] = deepMerge(base[k], v);\n }\n\n return out;\n}\n\n/** Objects merge recursively, `null` deletes a key, anything else replaces. */\nexport function deepMerge(base: Json | undefined, over: Json): Json {\n return isJsonObject(base) && isJsonObject(over) ? mergeObjects(base, over) : over;\n}\n\n/**\n * Inverse of `extends` merge: overrides that turn a resolved `base` into `captured`.\n * Pass the flattened base (from `catalog.resolve`). Copy-link keeps `captured` as-is;\n * Download JSON uses this so the file can `extends` the base instead of repeating it.\n */\nexport function diffScenario(captured: Scenario, base?: Scenario): ScenarioInput {\n if (!base) {\n return compactScenario({\n name: captured.name,\n description: captured.description,\n tags: captured.tags,\n path: captured.path,\n url: captured.url,\n network: captured.network,\n handlers: captured.handlers,\n state: captured.state,\n ready: captured.ready,\n viewport: captured.viewport,\n });\n }\n\n const url = diffUrl(base.url, captured.url);\n const network = diffNetwork(base.network ?? [], captured.network ?? []);\n // A slice missing from the capture had no adapter connected: inherit it, since a top-level null\n // would set it to null.\n const state = diffObjects(\n Object.fromEntries(Object.entries(base.state ?? {}).filter(([k]) => k in (captured.state ?? {}))),\n captured.state ?? {},\n );\n const handlers = diffHandlers(base.handlers ?? [], captured.handlers ?? []);\n\n return compactScenario({\n name: captured.name,\n extends: base.name,\n description: captured.description,\n tags: captured.tags,\n path: captured.path !== base.path ? captured.path : undefined,\n url,\n network: network.length ? network : undefined,\n handlers,\n state,\n ready: captured.ready,\n viewport: jsonEqual(captured.viewport ?? null, base.viewport ?? null) ? undefined : captured.viewport,\n });\n}\n\n/**\n * The shortest own list that `extends` merges back into `captured`, order included: merge puts\n * the child's handlers first, then the base's it didn't name. Handler order is precedence.\n */\nfunction diffHandlers(base: string[], captured: string[]): string[] | undefined {\n for (let k = 0; k <= captured.length; k++) {\n const own = captured.slice(0, k);\n\n if (JSON.stringify(union(own, base)) === JSON.stringify(captured)) return own.length ? own : undefined;\n }\n\n // An inherited handler can't be removed: keep the captured order and let the base's extras follow.\n return captured.length ? captured : undefined;\n}\n\nfunction diffUrl(\n base: Scenario['url'],\n captured: Scenario['url'],\n): ScenarioInput['url'] {\n const baseUrl = base ?? {};\n const capturedUrl = captured ?? {};\n const out: Record<string, string | null> = {};\n\n for (const [k, v] of Object.entries(capturedUrl)) {\n if (v === null) continue;\n\n if (baseUrl[k] !== v) out[k] = v;\n }\n\n for (const [k, v] of Object.entries(baseUrl)) {\n if (v === null) continue;\n\n if (!(k in capturedUrl) || capturedUrl[k] === null) out[k] = null;\n }\n\n return Object.keys(out).length ? out : undefined;\n}\n\nfunction diffNetwork(base: NetworkEntry[], captured: NetworkEntry[]): NetworkEntry[] {\n const baseByKey = new Map(base.map((e) => [routeKey(e), e]));\n const capByKey = new Map<string, NetworkEntry>();\n\n // First match wins in MSW, so a later duplicate route is unreachable; keep the first.\n for (const e of captured) if (!capByKey.has(routeKey(e))) capByKey.set(routeKey(e), e);\n\n const out: NetworkEntry[] = [];\n\n for (const [key, e] of capByKey) {\n const prior = baseByKey.get(key);\n\n if (!prior || !jsonEqual(prior, e)) out.push(e);\n }\n\n for (const [key, e] of baseByKey) {\n if (!capByKey.has(key)) {\n const removed: NetworkEntry = { method: e.method, path: e.path, remove: true };\n\n if (e.query) removed.query = e.query;\n\n out.push(removed);\n }\n }\n\n return out;\n}\n\nfunction diffObjects(base: JsonObject, captured: JsonObject): JsonObject | undefined {\n const out: JsonObject = {};\n\n for (const [k, v] of Object.entries(captured)) {\n if (!(k in base)) out[k] = v;\n else if (isJsonObject(base[k]) && isJsonObject(v)) {\n const nested = diffObjects(base[k], v);\n\n if (nested) out[k] = nested;\n } else if (!jsonEqual(base[k], v)) out[k] = v;\n }\n\n for (const k of Object.keys(base)) {\n if (!(k in captured)) out[k] = null;\n }\n\n return Object.keys(out).length ? out : undefined;\n}\n\nfunction jsonEqual(a: Json | NetworkEntry, b: Json | NetworkEntry): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\nfunction compactScenario(s: ScenarioInput): ScenarioInput {\n const out: ScenarioInput = { name: s.name };\n\n if (s.$schema !== undefined) out.$schema = s.$schema;\n\n if (s.description !== undefined) out.description = s.description;\n\n if (s.tags !== undefined) out.tags = s.tags;\n\n if (s.extends !== undefined) out.extends = s.extends;\n\n if (s.path !== undefined) out.path = s.path;\n\n if (s.url !== undefined && Object.keys(s.url).length) out.url = s.url;\n\n if (s.network !== undefined && s.network.length) out.network = s.network;\n\n if (s.handlers !== undefined && s.handlers.length) out.handlers = s.handlers;\n\n if (s.state !== undefined && Object.keys(s.state).length) out.state = s.state;\n\n if (s.ready !== undefined) out.ready = s.ready;\n\n if (s.viewport !== undefined) out.viewport = s.viewport;\n\n return out;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// src/ready.ts
|
|
2
|
+
var SKIP = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "NOSCRIPT", "META", "LINK", "HEAD", "HTML"]);
|
|
3
|
+
var MAX_TEXT = 60;
|
|
4
|
+
var CHROME = "header, nav, aside, footer, [role=banner], [role=navigation], [role=complementary], [role=contentinfo]";
|
|
5
|
+
var NAME_FROM_CONTENT = /* @__PURE__ */ new Set([
|
|
6
|
+
"button",
|
|
7
|
+
"cell",
|
|
8
|
+
"checkbox",
|
|
9
|
+
"columnheader",
|
|
10
|
+
"gridcell",
|
|
11
|
+
"heading",
|
|
12
|
+
"link",
|
|
13
|
+
"menuitem",
|
|
14
|
+
"menuitemcheckbox",
|
|
15
|
+
"menuitemradio",
|
|
16
|
+
"option",
|
|
17
|
+
"radio",
|
|
18
|
+
"row",
|
|
19
|
+
"rowheader",
|
|
20
|
+
"switch",
|
|
21
|
+
"tab",
|
|
22
|
+
"tooltip",
|
|
23
|
+
"treeitem"
|
|
24
|
+
]);
|
|
25
|
+
var IMPLICIT_ROLES = /* @__PURE__ */ new Set([
|
|
26
|
+
"button",
|
|
27
|
+
"link",
|
|
28
|
+
"heading",
|
|
29
|
+
"img",
|
|
30
|
+
"textbox",
|
|
31
|
+
"searchbox",
|
|
32
|
+
"checkbox",
|
|
33
|
+
"radio",
|
|
34
|
+
"combobox"
|
|
35
|
+
]);
|
|
36
|
+
function selectorForElement(el) {
|
|
37
|
+
if (!(el instanceof Element) || SKIP.has(el.tagName) || el.closest("scenario-panel")) {
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
for (const attr of ["data-testid", "data-test-id"]) {
|
|
41
|
+
const testId = el.getAttribute(attr);
|
|
42
|
+
if (testId) return `[${attr}=${JSON.stringify(testId)}]`;
|
|
43
|
+
}
|
|
44
|
+
const role = roleOf(el);
|
|
45
|
+
const name = role && accessibleName(el, role);
|
|
46
|
+
if (role && name && name.length <= MAX_TEXT) {
|
|
47
|
+
return `role=${role}[name=${JSON.stringify(name)}]`;
|
|
48
|
+
}
|
|
49
|
+
const text = ownText(el);
|
|
50
|
+
if (text && text.length <= MAX_TEXT) {
|
|
51
|
+
const selector = `text=${JSON.stringify(text)}`;
|
|
52
|
+
const check = checkReadySelector(selector, el.ownerDocument);
|
|
53
|
+
if (check.status === "match" && check.count === 1) return selector;
|
|
54
|
+
}
|
|
55
|
+
if (role) return `role=${role}`;
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
function closestReadySelector(el) {
|
|
59
|
+
for (let node = el; node && node !== node.ownerDocument.body; node = node.parentElement) {
|
|
60
|
+
const selector = selectorForElement(node);
|
|
61
|
+
if (selector) return { element: node, selector };
|
|
62
|
+
}
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
function suggestReadySelectors(root, { limit = 3 } = {}) {
|
|
66
|
+
const target = root ?? globalThis.document?.body;
|
|
67
|
+
if (!target) return [];
|
|
68
|
+
const ranked = [];
|
|
69
|
+
const seen = /* @__PURE__ */ new Set();
|
|
70
|
+
const walk = (node) => {
|
|
71
|
+
if (SKIP.has(node.tagName) || node.localName === "scenario-panel" || !isVisible(node)) return;
|
|
72
|
+
const selector = selectorForElement(node);
|
|
73
|
+
const tooShort = selector?.startsWith("text=") && selector.length < 'text=""'.length + 3;
|
|
74
|
+
if (selector && !tooShort && !seen.has(selector)) {
|
|
75
|
+
seen.add(selector);
|
|
76
|
+
ranked.push({ score: scoreSelector(selector, node), selector });
|
|
77
|
+
}
|
|
78
|
+
for (const child of node.children) walk(child);
|
|
79
|
+
};
|
|
80
|
+
if (target instanceof Element) walk(target);
|
|
81
|
+
else for (const child of target.children) walk(child);
|
|
82
|
+
return ranked.toSorted((a, b) => b.score - a.score).slice(0, limit).map((r) => r.selector);
|
|
83
|
+
}
|
|
84
|
+
function checkReadySelector(selector, doc = document) {
|
|
85
|
+
const s = selector.trim();
|
|
86
|
+
if (!s || s.includes(">>")) return { status: "unchecked" };
|
|
87
|
+
const found = s.startsWith("text=") ? matchText(s.slice(5), doc) : s.startsWith("role=") ? matchRole(s.slice(5), doc) : s.startsWith("css=") || !/^[a-z-]+=/i.test(s) ? matchCss(s.replace(/^css=/, ""), doc) : void 0;
|
|
88
|
+
if (!found) return { status: "unchecked" };
|
|
89
|
+
if ("error" in found) return { status: "invalid", error: found.error };
|
|
90
|
+
const [first] = found;
|
|
91
|
+
return first ? { status: "match", count: found.length, first, visible: isShown(first) } : { status: "none" };
|
|
92
|
+
}
|
|
93
|
+
function matchText(body, doc) {
|
|
94
|
+
const quoted = unquote(body);
|
|
95
|
+
if (quoted !== void 0) {
|
|
96
|
+
return appElements(doc).filter(
|
|
97
|
+
(el) => [...el.childNodes].some(
|
|
98
|
+
(n) => n.nodeType === Node.TEXT_NODE && collapse(n.textContent ?? "") === quoted
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (body.startsWith("/")) return void 0;
|
|
103
|
+
const needle = collapse(body).toLowerCase();
|
|
104
|
+
if (!needle) return void 0;
|
|
105
|
+
const has = (el) => collapse(el.textContent ?? "").toLowerCase().includes(needle);
|
|
106
|
+
return appElements(doc).filter((el) => has(el) && ![...el.children].some(has));
|
|
107
|
+
}
|
|
108
|
+
function matchRole(body, doc) {
|
|
109
|
+
const m = /^([a-z]+)(?:\[name=(".*")\])?$/.exec(body);
|
|
110
|
+
if (!m) return void 0;
|
|
111
|
+
const [, role, rawName] = m;
|
|
112
|
+
const name = rawName === void 0 ? void 0 : unquote(rawName);
|
|
113
|
+
if (rawName !== void 0 && name === void 0) return void 0;
|
|
114
|
+
const found = appElements(doc).filter(
|
|
115
|
+
(el) => roleOf(el) === role && isShown(el) && !el.closest("[aria-hidden=true]") && (name === void 0 || accessibleName(el, role) === name)
|
|
116
|
+
);
|
|
117
|
+
if (!found.length && !IMPLICIT_ROLES.has(role)) return void 0;
|
|
118
|
+
return found;
|
|
119
|
+
}
|
|
120
|
+
function matchCss(css, doc) {
|
|
121
|
+
try {
|
|
122
|
+
return [...doc.querySelectorAll(css)].filter((el) => !el.closest("scenario-panel"));
|
|
123
|
+
} catch {
|
|
124
|
+
return { error: "Not a valid CSS selector" };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function unquote(s) {
|
|
128
|
+
if (!s.startsWith('"') || !s.endsWith('"') || s.length < 2) return void 0;
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(s);
|
|
131
|
+
} catch {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function appElements(doc) {
|
|
136
|
+
const out = [];
|
|
137
|
+
const walk = (el) => {
|
|
138
|
+
if (SKIP.has(el.tagName) || el.localName === "scenario-panel") return;
|
|
139
|
+
out.push(el);
|
|
140
|
+
for (const child of el.children) walk(child);
|
|
141
|
+
};
|
|
142
|
+
if (doc.body) walk(doc.body);
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
function scoreSelector(selector, el) {
|
|
146
|
+
let score = 0;
|
|
147
|
+
if (selector.startsWith("[data-test")) score += 100;
|
|
148
|
+
else if (selector.startsWith("role=") && selector.includes("[name=")) score += 80;
|
|
149
|
+
else if (selector.startsWith("text=")) score += 60;
|
|
150
|
+
else if (selector.startsWith("role=")) score += 40;
|
|
151
|
+
if (el.children.length === 0) score += 10;
|
|
152
|
+
if (el.tagName === "H1" || el.tagName === "H2" || el.tagName === "H3") score += 15;
|
|
153
|
+
const role = el.getAttribute("role");
|
|
154
|
+
if (role === "alert" || role === "status") score += 20;
|
|
155
|
+
if (el.closest(CHROME)) score -= 50;
|
|
156
|
+
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT") score -= 50;
|
|
157
|
+
return score;
|
|
158
|
+
}
|
|
159
|
+
function roleOf(el) {
|
|
160
|
+
return el.getAttribute("role")?.trim().split(/\s+/)[0] || implicitRole(el);
|
|
161
|
+
}
|
|
162
|
+
function implicitRole(el) {
|
|
163
|
+
const tag = el.tagName;
|
|
164
|
+
if (tag === "BUTTON") return "button";
|
|
165
|
+
if (tag === "A" && el.hasAttribute("href")) return "link";
|
|
166
|
+
if (tag === "H1" || tag === "H2" || tag === "H3" || tag === "H4" || tag === "H5" || tag === "H6") {
|
|
167
|
+
return "heading";
|
|
168
|
+
}
|
|
169
|
+
if (tag === "IMG") return "img";
|
|
170
|
+
if (tag === "INPUT") {
|
|
171
|
+
const type = (el.getAttribute("type") ?? "text").toLowerCase();
|
|
172
|
+
if (type === "submit" || type === "button") return "button";
|
|
173
|
+
if (type === "checkbox") return "checkbox";
|
|
174
|
+
if (type === "radio") return "radio";
|
|
175
|
+
if (type === "search") return "searchbox";
|
|
176
|
+
return "textbox";
|
|
177
|
+
}
|
|
178
|
+
if (tag === "TEXTAREA") return "textbox";
|
|
179
|
+
if (tag === "SELECT") return "combobox";
|
|
180
|
+
return void 0;
|
|
181
|
+
}
|
|
182
|
+
function accessibleName(el, role) {
|
|
183
|
+
const labelledBy = el.getAttribute("aria-labelledby");
|
|
184
|
+
if (labelledBy) {
|
|
185
|
+
const text = collapse(
|
|
186
|
+
labelledBy.split(/\s+/).map((id) => el.ownerDocument.getElementById(id)?.textContent ?? "").join(" ")
|
|
187
|
+
);
|
|
188
|
+
if (text) return text;
|
|
189
|
+
}
|
|
190
|
+
const label = collapse(el.getAttribute("aria-label") ?? "");
|
|
191
|
+
if (label) return label;
|
|
192
|
+
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
|
|
193
|
+
const fromLabel = collapse([...el.labels ?? []].map((l) => l.textContent ?? "").join(" "));
|
|
194
|
+
if (fromLabel) return fromLabel;
|
|
195
|
+
if (!(el instanceof HTMLSelectElement) && el.placeholder) return collapse(el.placeholder);
|
|
196
|
+
}
|
|
197
|
+
if (el instanceof HTMLImageElement && el.alt) return collapse(el.alt);
|
|
198
|
+
if (NAME_FROM_CONTENT.has(role)) return collapse(el.textContent ?? "") || void 0;
|
|
199
|
+
return collapse(el.getAttribute("title") ?? "") || void 0;
|
|
200
|
+
}
|
|
201
|
+
function ownText(el) {
|
|
202
|
+
for (const n of el.childNodes) {
|
|
203
|
+
const text = n.nodeType === Node.TEXT_NODE ? collapse(n.textContent ?? "") : "";
|
|
204
|
+
if (text) return text;
|
|
205
|
+
}
|
|
206
|
+
return el.children.length === 1 ? ownText(el.children[0]) : "";
|
|
207
|
+
}
|
|
208
|
+
function collapse(s) {
|
|
209
|
+
return s.replace(/\s+/g, " ").trim();
|
|
210
|
+
}
|
|
211
|
+
function isVisible(el) {
|
|
212
|
+
if (!(el instanceof HTMLElement)) return true;
|
|
213
|
+
const style = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
214
|
+
if (style && (style.display === "none" || style.visibility === "hidden")) return false;
|
|
215
|
+
if (el.hidden || el.getAttribute("aria-hidden") === "true") return false;
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
function isShown(el) {
|
|
219
|
+
for (let node = el; node; node = node.parentElement) {
|
|
220
|
+
if (!(node instanceof HTMLElement)) continue;
|
|
221
|
+
if (node.hidden) return false;
|
|
222
|
+
const style = node.ownerDocument.defaultView?.getComputedStyle(node);
|
|
223
|
+
if (style && (style.display === "none" || style.visibility === "hidden")) return false;
|
|
224
|
+
}
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export {
|
|
229
|
+
selectorForElement,
|
|
230
|
+
closestReadySelector,
|
|
231
|
+
suggestReadySelectors,
|
|
232
|
+
checkReadySelector
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=chunk-XLNYICOI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ready.ts"],"sourcesContent":["const SKIP = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'META', 'LINK', 'HEAD', 'HTML']);\n\nconst MAX_TEXT = 60;\n\nconst CHROME =\n 'header, nav, aside, footer, [role=banner], [role=navigation], [role=complementary], [role=contentinfo]';\n\n// Roles whose accessible name comes from their text. Others (alert, status, region…) are\n// named only by aria-label/labelledby, so Playwright's [name=…] never matches their text.\nconst NAME_FROM_CONTENT = new Set([\n 'button',\n 'cell',\n 'checkbox',\n 'columnheader',\n 'gridcell',\n 'heading',\n 'link',\n 'menuitem',\n 'menuitemcheckbox',\n 'menuitemradio',\n 'option',\n 'radio',\n 'row',\n 'rowheader',\n 'switch',\n 'tab',\n 'tooltip',\n 'treeitem',\n]);\n\n// Implicit roles implicitRole knows; for any other role only an explicit role=\"…\" is checkable.\nconst IMPLICIT_ROLES = new Set([\n 'button',\n 'link',\n 'heading',\n 'img',\n 'textbox',\n 'searchbox',\n 'checkbox',\n 'radio',\n 'combobox',\n]);\n\n/** Playwright selector for an element the user marked as the scenario's ready signal. */\nexport function selectorForElement(el: Element): string | undefined {\n if (!(el instanceof Element) || SKIP.has(el.tagName) || el.closest('scenario-panel')) {\n return undefined;\n }\n\n for (const attr of ['data-testid', 'data-test-id']) {\n const testId = el.getAttribute(attr);\n\n if (testId) return `[${attr}=${JSON.stringify(testId)}]`;\n }\n\n const role = roleOf(el);\n const name = role && accessibleName(el, role);\n\n if (role && name && name.length <= MAX_TEXT) {\n return `role=${role}[name=${JSON.stringify(name)}]`;\n }\n\n const text = ownText(el);\n\n if (text && text.length <= MAX_TEXT) {\n const selector = `text=${JSON.stringify(text)}`;\n const check = checkReadySelector(selector, el.ownerDocument);\n\n if (check.status === 'match' && check.count === 1) return selector;\n }\n\n if (role) return `role=${role}`;\n\n return undefined;\n}\n\n/**\n * The nearest element at or above `el` that has a ready selector, so clicking an icon or a\n * wrapper still marks something.\n */\nexport function closestReadySelector(el: Element): { element: Element; selector: string; } | undefined {\n for (let node: Element | null = el; node && node !== node.ownerDocument.body; node = node.parentElement) {\n const selector = selectorForElement(node);\n\n if (selector) return { element: node, selector };\n }\n\n return undefined;\n}\n\n/**\n * Ranked Playwright selectors for visible, distinctive UI, offered when the user\n * hasn't marked ready. Prefers test ids, then named roles, then unique short text.\n */\nexport function suggestReadySelectors(\n root?: ParentNode | null,\n { limit = 3 }: { limit?: number; } = {},\n): string[] {\n const target = root ?? globalThis.document?.body;\n\n if (!target) return [];\n\n const ranked: { score: number; selector: string; }[] = [];\n const seen = new Set<string>();\n\n const walk = (node: Element) => {\n if (SKIP.has(node.tagName) || node.localName === 'scenario-panel' || !isVisible(node)) return;\n\n const selector = selectorForElement(node);\n // A count or a single character (\"0\" in \"0 countries\") is too generic to suggest.\n const tooShort = selector?.startsWith('text=') && selector.length < 'text=\"\"'.length + 3;\n\n if (selector && !tooShort && !seen.has(selector)) {\n seen.add(selector);\n ranked.push({ score: scoreSelector(selector, node), selector });\n }\n\n for (const child of node.children) walk(child);\n };\n\n if (target instanceof Element) walk(target);\n else for (const child of target.children) walk(child);\n\n return ranked.toSorted((a, b) => b.score - a.score).slice(0, limit).map((r) => r.selector);\n}\n\nexport type ReadyCheck =\n /** `first` is what `openScenario` waits on; `visible` says whether it's showing now. */\n | { status: 'match'; count: number; first: Element; visible: boolean; }\n | { status: 'none'; }\n /** A selector form this page can't evaluate (xpath, chains, regex…); Playwright still can. */\n | { status: 'unchecked'; }\n | { status: 'invalid'; error: string; };\n\n/**\n * Evaluate a ready selector against the live page the way Playwright would, for the forms\n * the panel generates and plain CSS. Anything else comes back `unchecked`, never a guess.\n */\nexport function checkReadySelector(selector: string, doc: Document = document): ReadyCheck {\n const s = selector.trim();\n\n if (!s || s.includes('>>')) return { status: 'unchecked' };\n\n const found = s.startsWith('text=')\n ? matchText(s.slice(5), doc)\n : s.startsWith('role=')\n ? matchRole(s.slice(5), doc)\n : s.startsWith('css=') || !/^[a-z-]+=/i.test(s)\n ? matchCss(s.replace(/^css=/, ''), doc)\n : undefined;\n\n if (!found) return { status: 'unchecked' };\n\n if ('error' in found) return { status: 'invalid', error: found.error };\n\n const [first] = found;\n\n return first\n ? { status: 'match', count: found.length, first, visible: isShown(first) }\n : { status: 'none' };\n}\n\ntype Found = Element[] | { error: string; } | undefined;\n\nfunction matchText(body: string, doc: Document): Found {\n const quoted = unquote(body);\n\n // Quoted: an element with a text node equal to it, case-sensitive.\n if (quoted !== undefined) {\n return appElements(doc).filter((el) =>\n [...el.childNodes].some((n) =>\n n.nodeType === Node.TEXT_NODE && collapse(n.textContent ?? '') === quoted\n )\n );\n }\n\n if (body.startsWith('/')) return undefined;\n\n // Unquoted: the smallest elements whose text contains it, ignoring case.\n const needle = collapse(body).toLowerCase();\n\n if (!needle) return undefined;\n\n const has = (el: Element) => collapse(el.textContent ?? '').toLowerCase().includes(needle);\n\n return appElements(doc).filter((el) => has(el) && ![...el.children].some(has));\n}\n\nfunction matchRole(body: string, doc: Document): Found {\n const m = /^([a-z]+)(?:\\[name=(\".*\")\\])?$/.exec(body);\n\n if (!m) return undefined;\n\n const [, role, rawName] = m;\n const name = rawName === undefined ? undefined : unquote(rawName);\n\n if (rawName !== undefined && name === undefined) return undefined;\n\n // Like Playwright's role engine, hidden elements don't count.\n const found = appElements(doc).filter((el) =>\n roleOf(el) === role && isShown(el) && !el.closest('[aria-hidden=true]')\n && (name === undefined || accessibleName(el, role) === name)\n );\n\n // We only know some implicit roles (<main> is \"main\", <p> is \"paragraph\"…), so a miss\n // on any other role isn't proof of absence.\n if (!found.length && !IMPLICIT_ROLES.has(role)) return undefined;\n\n return found;\n}\n\nfunction matchCss(css: string, doc: Document): Found {\n try {\n return [...doc.querySelectorAll(css)].filter((el) => !el.closest('scenario-panel'));\n } catch {\n return { error: 'Not a valid CSS selector' };\n }\n}\n\n/** A JSON-style \"quoted\" string, or undefined when `s` isn't one. */\nfunction unquote(s: string): string | undefined {\n if (!s.startsWith('\"') || !s.endsWith('\"') || s.length < 2) return undefined;\n\n try {\n return JSON.parse(s);\n } catch {\n return undefined;\n }\n}\n\nfunction appElements(doc: Document): Element[] {\n const out: Element[] = [];\n\n const walk = (el: Element) => {\n if (SKIP.has(el.tagName) || el.localName === 'scenario-panel') return;\n out.push(el);\n\n for (const child of el.children) walk(child);\n };\n\n if (doc.body) walk(doc.body);\n\n return out;\n}\n\nfunction scoreSelector(selector: string, el: Element): number {\n let score = 0;\n\n if (selector.startsWith('[data-test')) score += 100;\n else if (selector.startsWith('role=') && selector.includes('[name=')) score += 80;\n else if (selector.startsWith('text=')) score += 60;\n else if (selector.startsWith('role=')) score += 40;\n\n // Prefer leaf-ish content over giant containers.\n if (el.children.length === 0) score += 10;\n\n if (el.tagName === 'H1' || el.tagName === 'H2' || el.tagName === 'H3') score += 15;\n\n const role = el.getAttribute('role');\n\n if (role === 'alert' || role === 'status') score += 20;\n\n // Headers, navs, sidebars and inputs render in every state, so they make weak ready signals.\n if (el.closest(CHROME)) score -= 50;\n\n if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') score -= 50;\n\n return score;\n}\n\nfunction roleOf(el: Element): string | undefined {\n return el.getAttribute('role')?.trim().split(/\\s+/)[0] || implicitRole(el);\n}\n\nfunction implicitRole(el: Element): string | undefined {\n const tag = el.tagName;\n\n if (tag === 'BUTTON') return 'button';\n\n if (tag === 'A' && el.hasAttribute('href')) return 'link';\n\n if (tag === 'H1' || tag === 'H2' || tag === 'H3' || tag === 'H4' || tag === 'H5' || tag === 'H6') {\n return 'heading';\n }\n\n if (tag === 'IMG') return 'img';\n\n if (tag === 'INPUT') {\n const type = (el.getAttribute('type') ?? 'text').toLowerCase();\n\n if (type === 'submit' || type === 'button') return 'button';\n\n if (type === 'checkbox') return 'checkbox';\n\n if (type === 'radio') return 'radio';\n\n if (type === 'search') return 'searchbox';\n\n return 'textbox';\n }\n\n if (tag === 'TEXTAREA') return 'textbox';\n\n if (tag === 'SELECT') return 'combobox';\n\n return undefined;\n}\n\n/** The accessible name Playwright's `[name=…]` compares against, for the common cases. */\nfunction accessibleName(el: Element, role: string): string | undefined {\n const labelledBy = el.getAttribute('aria-labelledby');\n\n if (labelledBy) {\n const text = collapse(\n labelledBy.split(/\\s+/).map((id) => el.ownerDocument.getElementById(id)?.textContent ?? '').join(' '),\n );\n\n if (text) return text;\n }\n\n const label = collapse(el.getAttribute('aria-label') ?? '');\n\n if (label) return label;\n\n if (\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement\n ) {\n const fromLabel = collapse([...(el.labels ?? [])].map((l) => l.textContent ?? '').join(' '));\n\n if (fromLabel) return fromLabel;\n\n if (!(el instanceof HTMLSelectElement) && el.placeholder) return collapse(el.placeholder);\n }\n\n if (el instanceof HTMLImageElement && el.alt) return collapse(el.alt);\n\n if (NAME_FROM_CONTENT.has(role)) return collapse(el.textContent ?? '') || undefined;\n\n return collapse(el.getAttribute('title') ?? '') || undefined;\n}\n\n/** The element's first own text node, which is what a quoted `text=` selector matches. */\nfunction ownText(el: Element): string {\n for (const n of el.childNodes) {\n const text = n.nodeType === Node.TEXT_NODE ? collapse(n.textContent ?? '') : '';\n\n if (text) return text;\n }\n\n // No own text: a lone descendant text still identifies this element's content.\n return el.children.length === 1 ? ownText(el.children[0]) : '';\n}\n\nfunction collapse(s: string): string {\n return s.replace(/\\s+/g, ' ').trim();\n}\n\nfunction isVisible(el: Element): boolean {\n if (!(el instanceof HTMLElement)) return true;\n const style = el.ownerDocument.defaultView?.getComputedStyle(el);\n\n if (style && (style.display === 'none' || style.visibility === 'hidden')) return false;\n\n if (el.hidden || el.getAttribute('aria-hidden') === 'true') return false;\n\n return true;\n}\n\n/** Rendered including ancestors: what Playwright's waitFor needs. */\nfunction isShown(el: Element): boolean {\n for (let node: Element | null = el; node; node = node.parentElement) {\n if (!(node instanceof HTMLElement)) continue;\n\n if (node.hidden) return false;\n const style = node.ownerDocument.defaultView?.getComputedStyle(node);\n\n if (style && (style.display === 'none' || style.visibility === 'hidden')) return false;\n }\n\n return true;\n}\n"],"mappings":";AAAA,IAAM,OAAO,oBAAI,IAAI,CAAC,UAAU,SAAS,YAAY,QAAQ,QAAQ,QAAQ,MAAM,CAAC;AAEpF,IAAM,WAAW;AAEjB,IAAM,SACJ;AAIF,IAAM,oBAAoB,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGD,IAAM,iBAAiB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,SAAS,mBAAmB,IAAiC;AAClE,MAAI,EAAE,cAAc,YAAY,KAAK,IAAI,GAAG,OAAO,KAAK,GAAG,QAAQ,gBAAgB,GAAG;AACpF,WAAO;AAAA,EACT;AAEA,aAAW,QAAQ,CAAC,eAAe,cAAc,GAAG;AAClD,UAAM,SAAS,GAAG,aAAa,IAAI;AAEnC,QAAI,OAAQ,QAAO,IAAI,IAAI,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,EACvD;AAEA,QAAM,OAAO,OAAO,EAAE;AACtB,QAAM,OAAO,QAAQ,eAAe,IAAI,IAAI;AAE5C,MAAI,QAAQ,QAAQ,KAAK,UAAU,UAAU;AAC3C,WAAO,QAAQ,IAAI,SAAS,KAAK,UAAU,IAAI,CAAC;AAAA,EAClD;AAEA,QAAM,OAAO,QAAQ,EAAE;AAEvB,MAAI,QAAQ,KAAK,UAAU,UAAU;AACnC,UAAM,WAAW,QAAQ,KAAK,UAAU,IAAI,CAAC;AAC7C,UAAM,QAAQ,mBAAmB,UAAU,GAAG,aAAa;AAE3D,QAAI,MAAM,WAAW,WAAW,MAAM,UAAU,EAAG,QAAO;AAAA,EAC5D;AAEA,MAAI,KAAM,QAAO,QAAQ,IAAI;AAE7B,SAAO;AACT;AAMO,SAAS,qBAAqB,IAAkE;AACrG,WAAS,OAAuB,IAAI,QAAQ,SAAS,KAAK,cAAc,MAAM,OAAO,KAAK,eAAe;AACvG,UAAM,WAAW,mBAAmB,IAAI;AAExC,QAAI,SAAU,QAAO,EAAE,SAAS,MAAM,SAAS;AAAA,EACjD;AAEA,SAAO;AACT;AAMO,SAAS,sBACd,MACA,EAAE,QAAQ,EAAE,IAAyB,CAAC,GAC5B;AACV,QAAM,SAAS,QAAQ,WAAW,UAAU;AAE5C,MAAI,CAAC,OAAQ,QAAO,CAAC;AAErB,QAAM,SAAiD,CAAC;AACxD,QAAM,OAAO,oBAAI,IAAY;AAE7B,QAAM,OAAO,CAAC,SAAkB;AAC9B,QAAI,KAAK,IAAI,KAAK,OAAO,KAAK,KAAK,cAAc,oBAAoB,CAAC,UAAU,IAAI,EAAG;AAEvF,UAAM,WAAW,mBAAmB,IAAI;AAExC,UAAM,WAAW,UAAU,WAAW,OAAO,KAAK,SAAS,SAAS,UAAU,SAAS;AAEvF,QAAI,YAAY,CAAC,YAAY,CAAC,KAAK,IAAI,QAAQ,GAAG;AAChD,WAAK,IAAI,QAAQ;AACjB,aAAO,KAAK,EAAE,OAAO,cAAc,UAAU,IAAI,GAAG,SAAS,CAAC;AAAA,IAChE;AAEA,eAAW,SAAS,KAAK,SAAU,MAAK,KAAK;AAAA,EAC/C;AAEA,MAAI,kBAAkB,QAAS,MAAK,MAAM;AAAA,MACrC,YAAW,SAAS,OAAO,SAAU,MAAK,KAAK;AAEpD,SAAO,OAAO,SAAS,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ;AAC3F;AAcO,SAAS,mBAAmB,UAAkB,MAAgB,UAAsB;AACzF,QAAM,IAAI,SAAS,KAAK;AAExB,MAAI,CAAC,KAAK,EAAE,SAAS,IAAI,EAAG,QAAO,EAAE,QAAQ,YAAY;AAEzD,QAAM,QAAQ,EAAE,WAAW,OAAO,IAC9B,UAAU,EAAE,MAAM,CAAC,GAAG,GAAG,IACzB,EAAE,WAAW,OAAO,IACpB,UAAU,EAAE,MAAM,CAAC,GAAG,GAAG,IACzB,EAAE,WAAW,MAAM,KAAK,CAAC,aAAa,KAAK,CAAC,IAC5C,SAAS,EAAE,QAAQ,SAAS,EAAE,GAAG,GAAG,IACpC;AAEJ,MAAI,CAAC,MAAO,QAAO,EAAE,QAAQ,YAAY;AAEzC,MAAI,WAAW,MAAO,QAAO,EAAE,QAAQ,WAAW,OAAO,MAAM,MAAM;AAErE,QAAM,CAAC,KAAK,IAAI;AAEhB,SAAO,QACH,EAAE,QAAQ,SAAS,OAAO,MAAM,QAAQ,OAAO,SAAS,QAAQ,KAAK,EAAE,IACvE,EAAE,QAAQ,OAAO;AACvB;AAIA,SAAS,UAAU,MAAc,KAAsB;AACrD,QAAM,SAAS,QAAQ,IAAI;AAG3B,MAAI,WAAW,QAAW;AACxB,WAAO,YAAY,GAAG,EAAE;AAAA,MAAO,CAAC,OAC9B,CAAC,GAAG,GAAG,UAAU,EAAE;AAAA,QAAK,CAAC,MACvB,EAAE,aAAa,KAAK,aAAa,SAAS,EAAE,eAAe,EAAE,MAAM;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AAEA,MAAI,KAAK,WAAW,GAAG,EAAG,QAAO;AAGjC,QAAM,SAAS,SAAS,IAAI,EAAE,YAAY;AAE1C,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,MAAM,CAAC,OAAgB,SAAS,GAAG,eAAe,EAAE,EAAE,YAAY,EAAE,SAAS,MAAM;AAEzF,SAAO,YAAY,GAAG,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,GAAG,QAAQ,EAAE,KAAK,GAAG,CAAC;AAC/E;AAEA,SAAS,UAAU,MAAc,KAAsB;AACrD,QAAM,IAAI,iCAAiC,KAAK,IAAI;AAEpD,MAAI,CAAC,EAAG,QAAO;AAEf,QAAM,CAAC,EAAE,MAAM,OAAO,IAAI;AAC1B,QAAM,OAAO,YAAY,SAAY,SAAY,QAAQ,OAAO;AAEhE,MAAI,YAAY,UAAa,SAAS,OAAW,QAAO;AAGxD,QAAM,QAAQ,YAAY,GAAG,EAAE;AAAA,IAAO,CAAC,OACrC,OAAO,EAAE,MAAM,QAAQ,QAAQ,EAAE,KAAK,CAAC,GAAG,QAAQ,oBAAoB,MAClE,SAAS,UAAa,eAAe,IAAI,IAAI,MAAM;AAAA,EACzD;AAIA,MAAI,CAAC,MAAM,UAAU,CAAC,eAAe,IAAI,IAAI,EAAG,QAAO;AAEvD,SAAO;AACT;AAEA,SAAS,SAAS,KAAa,KAAsB;AACnD,MAAI;AACF,WAAO,CAAC,GAAG,IAAI,iBAAiB,GAAG,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,QAAQ,gBAAgB,CAAC;AAAA,EACpF,QAAQ;AACN,WAAO,EAAE,OAAO,2BAA2B;AAAA,EAC7C;AACF;AAGA,SAAS,QAAQ,GAA+B;AAC9C,MAAI,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,EAAG,QAAO;AAEnE,MAAI;AACF,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,KAA0B;AAC7C,QAAM,MAAiB,CAAC;AAExB,QAAM,OAAO,CAAC,OAAgB;AAC5B,QAAI,KAAK,IAAI,GAAG,OAAO,KAAK,GAAG,cAAc,iBAAkB;AAC/D,QAAI,KAAK,EAAE;AAEX,eAAW,SAAS,GAAG,SAAU,MAAK,KAAK;AAAA,EAC7C;AAEA,MAAI,IAAI,KAAM,MAAK,IAAI,IAAI;AAE3B,SAAO;AACT;AAEA,SAAS,cAAc,UAAkB,IAAqB;AAC5D,MAAI,QAAQ;AAEZ,MAAI,SAAS,WAAW,YAAY,EAAG,UAAS;AAAA,WACvC,SAAS,WAAW,OAAO,KAAK,SAAS,SAAS,QAAQ,EAAG,UAAS;AAAA,WACtE,SAAS,WAAW,OAAO,EAAG,UAAS;AAAA,WACvC,SAAS,WAAW,OAAO,EAAG,UAAS;AAGhD,MAAI,GAAG,SAAS,WAAW,EAAG,UAAS;AAEvC,MAAI,GAAG,YAAY,QAAQ,GAAG,YAAY,QAAQ,GAAG,YAAY,KAAM,UAAS;AAEhF,QAAM,OAAO,GAAG,aAAa,MAAM;AAEnC,MAAI,SAAS,WAAW,SAAS,SAAU,UAAS;AAGpD,MAAI,GAAG,QAAQ,MAAM,EAAG,UAAS;AAEjC,MAAI,GAAG,YAAY,WAAW,GAAG,YAAY,cAAc,GAAG,YAAY,SAAU,UAAS;AAE7F,SAAO;AACT;AAEA,SAAS,OAAO,IAAiC;AAC/C,SAAO,GAAG,aAAa,MAAM,GAAG,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,KAAK,aAAa,EAAE;AAC3E;AAEA,SAAS,aAAa,IAAiC;AACrD,QAAM,MAAM,GAAG;AAEf,MAAI,QAAQ,SAAU,QAAO;AAE7B,MAAI,QAAQ,OAAO,GAAG,aAAa,MAAM,EAAG,QAAO;AAEnD,MAAI,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,MAAM;AAChG,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,MAAO,QAAO;AAE1B,MAAI,QAAQ,SAAS;AACnB,UAAM,QAAQ,GAAG,aAAa,MAAM,KAAK,QAAQ,YAAY;AAE7D,QAAI,SAAS,YAAY,SAAS,SAAU,QAAO;AAEnD,QAAI,SAAS,WAAY,QAAO;AAEhC,QAAI,SAAS,QAAS,QAAO;AAE7B,QAAI,SAAS,SAAU,QAAO;AAE9B,WAAO;AAAA,EACT;AAEA,MAAI,QAAQ,WAAY,QAAO;AAE/B,MAAI,QAAQ,SAAU,QAAO;AAE7B,SAAO;AACT;AAGA,SAAS,eAAe,IAAa,MAAkC;AACrE,QAAM,aAAa,GAAG,aAAa,iBAAiB;AAEpD,MAAI,YAAY;AACd,UAAM,OAAO;AAAA,MACX,WAAW,MAAM,KAAK,EAAE,IAAI,CAAC,OAAO,GAAG,cAAc,eAAe,EAAE,GAAG,eAAe,EAAE,EAAE,KAAK,GAAG;AAAA,IACtG;AAEA,QAAI,KAAM,QAAO;AAAA,EACnB;AAEA,QAAM,QAAQ,SAAS,GAAG,aAAa,YAAY,KAAK,EAAE;AAE1D,MAAI,MAAO,QAAO;AAElB,MACE,cAAc,oBAAoB,cAAc,uBAAuB,cAAc,mBACrF;AACA,UAAM,YAAY,SAAS,CAAC,GAAI,GAAG,UAAU,CAAC,CAAE,EAAE,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,KAAK,GAAG,CAAC;AAE3F,QAAI,UAAW,QAAO;AAEtB,QAAI,EAAE,cAAc,sBAAsB,GAAG,YAAa,QAAO,SAAS,GAAG,WAAW;AAAA,EAC1F;AAEA,MAAI,cAAc,oBAAoB,GAAG,IAAK,QAAO,SAAS,GAAG,GAAG;AAEpE,MAAI,kBAAkB,IAAI,IAAI,EAAG,QAAO,SAAS,GAAG,eAAe,EAAE,KAAK;AAE1E,SAAO,SAAS,GAAG,aAAa,OAAO,KAAK,EAAE,KAAK;AACrD;AAGA,SAAS,QAAQ,IAAqB;AACpC,aAAW,KAAK,GAAG,YAAY;AAC7B,UAAM,OAAO,EAAE,aAAa,KAAK,YAAY,SAAS,EAAE,eAAe,EAAE,IAAI;AAE7E,QAAI,KAAM,QAAO;AAAA,EACnB;AAGA,SAAO,GAAG,SAAS,WAAW,IAAI,QAAQ,GAAG,SAAS,CAAC,CAAC,IAAI;AAC9D;AAEA,SAAS,SAAS,GAAmB;AACnC,SAAO,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACrC;AAEA,SAAS,UAAU,IAAsB;AACvC,MAAI,EAAE,cAAc,aAAc,QAAO;AACzC,QAAM,QAAQ,GAAG,cAAc,aAAa,iBAAiB,EAAE;AAE/D,MAAI,UAAU,MAAM,YAAY,UAAU,MAAM,eAAe,UAAW,QAAO;AAEjF,MAAI,GAAG,UAAU,GAAG,aAAa,aAAa,MAAM,OAAQ,QAAO;AAEnE,SAAO;AACT;AAGA,SAAS,QAAQ,IAAsB;AACrC,WAAS,OAAuB,IAAI,MAAM,OAAO,KAAK,eAAe;AACnE,QAAI,EAAE,gBAAgB,aAAc;AAEpC,QAAI,KAAK,OAAQ,QAAO;AACxB,UAAM,QAAQ,KAAK,cAAc,aAAa,iBAAiB,IAAI;AAEnE,QAAI,UAAU,MAAM,YAAY,UAAU,MAAM,eAAe,UAAW,QAAO;AAAA,EACnF;AAEA,SAAO;AACT;","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { type CapturedScenario, type CaptureOptions, captureScenario } from './capture.js';
|
|
2
|
+
export { type Catalog, createCatalog, deepMerge, defineScenarioRules, diffScenario, type ScenarioRule, } from './catalog.js';
|
|
3
|
+
export { unsignedJwt } from './jwt.js';
|
|
4
|
+
export { redactSecrets } from './redact.js';
|
|
5
|
+
export { DATA_KEY, PARAM, readInline, RECORD_PARAM, scenarioHref, STRICT_PARAM } from './link.js';
|
|
6
|
+
export { toHandlers } from './network.js';
|
|
7
|
+
export { checkReadySelector, type ReadyCheck, selectorForElement, suggestReadySelectors } from './ready.js';
|
|
8
|
+
export { type Adapter, type Connection, currentSession, type JsonAdapter, type MswWorker, navigate, type PageHooks, type ReadyReport, type ScenarioSession, type StartOptions, startScenarios, type TypedAdapter, } from './runtime.js';
|
|
9
|
+
export { type Json, type JsonObject, JsonSchema, type NetworkEntry, parseJson, parseScenario, type Scenario, type ScenarioInput, scenarioJsonSchema, ScenarioSchema, type Serializable, } from './schema.js';
|