libretto 0.6.10 → 0.6.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/README.template.md +4 -0
- package/dist/cli/cli.js +4 -3
- package/dist/cli/commands/ai.js +3 -2
- package/dist/cli/commands/browser.js +17 -17
- package/dist/cli/commands/execution.js +254 -234
- package/dist/cli/commands/experiments.js +100 -0
- package/dist/cli/commands/setup.js +20 -34
- package/dist/cli/commands/shared.js +10 -0
- package/dist/cli/commands/snapshot.js +81 -9
- package/dist/cli/commands/status.js +5 -4
- package/dist/cli/core/ai-model.js +6 -3
- package/dist/cli/core/browser.js +300 -121
- package/dist/cli/core/config.js +4 -2
- package/dist/cli/core/context.js +4 -0
- package/dist/cli/core/daemon/config.js +0 -6
- package/dist/cli/core/daemon/daemon.js +535 -89
- package/dist/cli/core/daemon/ipc.js +170 -129
- package/dist/cli/core/daemon/snapshot.js +72 -6
- package/dist/cli/core/experiments.js +66 -0
- package/dist/cli/core/session.js +5 -4
- package/dist/cli/core/skill-version.js +2 -1
- package/dist/cli/core/snapshot-analyzer.js +4 -3
- package/dist/cli/core/workflow-runner/runner.js +147 -0
- package/dist/cli/core/workflow-runtime.js +60 -0
- package/dist/cli/router.js +4 -1
- package/dist/shared/debug/pause-handler.d.ts +9 -0
- package/dist/shared/debug/pause-handler.js +15 -0
- package/dist/shared/debug/pause.d.ts +1 -2
- package/dist/shared/debug/pause.js +13 -36
- package/dist/shared/ipc/child-process-transport.d.ts +7 -0
- package/dist/shared/ipc/child-process-transport.js +60 -0
- package/dist/shared/ipc/child-process-transport.spec.d.ts +2 -0
- package/dist/shared/ipc/child-process-transport.spec.js +68 -0
- package/dist/shared/ipc/ipc.d.ts +46 -0
- package/dist/shared/ipc/ipc.js +165 -0
- package/dist/shared/ipc/ipc.spec.d.ts +2 -0
- package/dist/shared/ipc/ipc.spec.js +114 -0
- package/dist/shared/ipc/socket-transport.d.ts +9 -0
- package/dist/shared/ipc/socket-transport.js +143 -0
- package/dist/shared/ipc/socket-transport.spec.d.ts +2 -0
- package/dist/shared/ipc/socket-transport.spec.js +117 -0
- package/dist/shared/package-manager.d.ts +7 -0
- package/dist/shared/package-manager.js +60 -0
- package/dist/shared/paths/paths.d.ts +1 -8
- package/dist/shared/paths/paths.js +1 -49
- package/dist/shared/snapshot/capture-snapshot.d.ts +9 -0
- package/dist/shared/snapshot/capture-snapshot.js +463 -0
- package/dist/shared/snapshot/diff-snapshots.d.ts +72 -0
- package/dist/shared/snapshot/diff-snapshots.js +358 -0
- package/dist/shared/snapshot/render-snapshot.d.ts +39 -0
- package/dist/shared/snapshot/render-snapshot.js +651 -0
- package/dist/shared/snapshot/snapshot.spec.d.ts +2 -0
- package/dist/shared/snapshot/snapshot.spec.js +333 -0
- package/dist/shared/snapshot/types.d.ts +40 -0
- package/dist/shared/snapshot/types.js +0 -0
- package/dist/shared/snapshot/wait-for-page-stable.d.ts +17 -0
- package/dist/shared/snapshot/wait-for-page-stable.js +281 -0
- package/dist/shared/state/session-state.d.ts +1 -0
- package/dist/shared/state/session-state.js +1 -0
- package/docs/experiments.md +67 -0
- package/package.json +4 -2
- package/skills/libretto/SKILL.md +3 -1
- package/skills/libretto-readonly/SKILL.md +1 -1
- package/src/cli/AGENTS.md +7 -0
- package/src/cli/cli.ts +4 -3
- package/src/cli/commands/ai.ts +3 -2
- package/src/cli/commands/browser.ts +13 -11
- package/src/cli/commands/execution.ts +303 -271
- package/src/cli/commands/experiments.ts +120 -0
- package/src/cli/commands/setup.ts +18 -36
- package/src/cli/commands/shared.ts +20 -0
- package/src/cli/commands/snapshot.ts +99 -11
- package/src/cli/commands/status.ts +5 -4
- package/src/cli/core/ai-model.ts +6 -3
- package/src/cli/core/browser.ts +369 -147
- package/src/cli/core/config.ts +3 -1
- package/src/cli/core/context.ts +4 -0
- package/src/cli/core/daemon/config.ts +35 -19
- package/src/cli/core/daemon/daemon.ts +686 -106
- package/src/cli/core/daemon/ipc.ts +330 -214
- package/src/cli/core/daemon/snapshot.ts +106 -8
- package/src/cli/core/experiments.ts +85 -0
- package/src/cli/core/session.ts +5 -4
- package/src/cli/core/skill-version.ts +2 -1
- package/src/cli/core/snapshot-analyzer.ts +4 -3
- package/src/cli/core/workflow-runner/runner.ts +237 -0
- package/src/cli/core/workflow-runtime.ts +85 -0
- package/src/cli/router.ts +4 -1
- package/src/shared/debug/pause-handler.ts +20 -0
- package/src/shared/debug/pause.ts +14 -48
- package/src/shared/ipc/AGENTS.md +24 -0
- package/src/shared/ipc/child-process-transport.spec.ts +86 -0
- package/src/shared/ipc/child-process-transport.ts +96 -0
- package/src/shared/ipc/ipc.spec.ts +161 -0
- package/src/shared/ipc/ipc.ts +288 -0
- package/src/shared/ipc/socket-transport.spec.ts +141 -0
- package/src/shared/ipc/socket-transport.ts +189 -0
- package/src/shared/package-manager.ts +76 -0
- package/src/shared/paths/paths.ts +0 -72
- package/src/shared/snapshot/capture-snapshot.ts +615 -0
- package/src/shared/snapshot/diff-snapshots.ts +579 -0
- package/src/shared/snapshot/render-snapshot.ts +962 -0
- package/src/shared/snapshot/snapshot.spec.ts +388 -0
- package/src/shared/snapshot/types.ts +43 -0
- package/src/shared/snapshot/wait-for-page-stable.ts +425 -0
- package/src/shared/state/session-state.ts +1 -0
- package/dist/cli/core/daemon/index.js +0 -16
- package/dist/cli/core/daemon/spawn.js +0 -90
- package/dist/cli/core/pause-signals.js +0 -29
- package/dist/cli/workers/run-integration-runtime.js +0 -235
- package/dist/cli/workers/run-integration-worker-protocol.js +0 -17
- package/dist/cli/workers/run-integration-worker.js +0 -64
- package/src/cli/core/daemon/index.ts +0 -24
- package/src/cli/core/daemon/spawn.ts +0 -171
- package/src/cli/core/pause-signals.ts +0 -35
- package/src/cli/workers/run-integration-runtime.ts +0 -326
- package/src/cli/workers/run-integration-worker-protocol.ts +0 -19
- package/src/cli/workers/run-integration-worker.ts +0 -72
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { chromium, type Page } from "playwright";
|
|
2
|
+
import outdent from "outdent";
|
|
3
|
+
import { describe, expect, test as base } from "vitest";
|
|
4
|
+
import { snapshot } from "./capture-snapshot.js";
|
|
5
|
+
import {
|
|
6
|
+
diffSnapshots,
|
|
7
|
+
renderSnapshotDiff,
|
|
8
|
+
} from "./diff-snapshots.js";
|
|
9
|
+
import { renderSnapshot } from "./render-snapshot.js";
|
|
10
|
+
import type {
|
|
11
|
+
Snapshot,
|
|
12
|
+
SnapshotNode,
|
|
13
|
+
SnapshotPrimitive,
|
|
14
|
+
} from "./types.js";
|
|
15
|
+
|
|
16
|
+
type SnapshotRenderFixtures = {
|
|
17
|
+
page: Page;
|
|
18
|
+
expectSnapshot: (html: string, expected: string) => Promise<void>;
|
|
19
|
+
expectScopedSnapshot: (
|
|
20
|
+
html: string,
|
|
21
|
+
refId: string,
|
|
22
|
+
expected: string,
|
|
23
|
+
) => Promise<void>;
|
|
24
|
+
expectSnapshotDiff: (
|
|
25
|
+
beforeHtml: string,
|
|
26
|
+
afterHtml: string,
|
|
27
|
+
expected: string,
|
|
28
|
+
) => Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const test = base.extend<SnapshotRenderFixtures>({
|
|
32
|
+
page: async ({}, use) => {
|
|
33
|
+
const browser = await chromium.launch({ headless: true });
|
|
34
|
+
const page = await browser.newPage();
|
|
35
|
+
await use(page);
|
|
36
|
+
await page.close();
|
|
37
|
+
await browser.close();
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
expectSnapshot: async ({ page }, use) => {
|
|
41
|
+
await use(async (html: string, expected: string) => {
|
|
42
|
+
await page.setContent(outdent.string(html));
|
|
43
|
+
const raw = await snapshot(page);
|
|
44
|
+
expect(normalizeIncidentalUrls(renderSnapshot(raw))).toBe(
|
|
45
|
+
outdent.string(expected),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
expectScopedSnapshot: async ({ page }, use) => {
|
|
51
|
+
await use(async (html: string, refId: string, expected: string) => {
|
|
52
|
+
await page.setContent(outdent.string(html));
|
|
53
|
+
const raw = await snapshot(page);
|
|
54
|
+
expect(normalizeIncidentalUrls(renderSnapshot(raw, refId))).toBe(
|
|
55
|
+
outdent.string(expected),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
expectSnapshotDiff: async ({ page }, use) => {
|
|
61
|
+
await use(async (beforeHtml: string, afterHtml: string, expected: string) => {
|
|
62
|
+
await page.setContent(outdent.string(beforeHtml));
|
|
63
|
+
const before = await snapshot(page);
|
|
64
|
+
await page.setContent(outdent.string(afterHtml));
|
|
65
|
+
const after = await snapshot(page);
|
|
66
|
+
expect(
|
|
67
|
+
normalizeIncidentalUrls(
|
|
68
|
+
renderSnapshotDiff(diffSnapshots(before, after)),
|
|
69
|
+
),
|
|
70
|
+
).toBe(outdent.string(expected));
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
function normalizeIncidentalUrls(rendered: string): string {
|
|
76
|
+
return rendered.replaceAll(/url="[^"]*"/g, `url="<page-url>"`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type SnapshotNodeInput = {
|
|
80
|
+
nodeId: string;
|
|
81
|
+
role: string;
|
|
82
|
+
name?: string | null;
|
|
83
|
+
ref?: string | null;
|
|
84
|
+
value?: SnapshotPrimitive;
|
|
85
|
+
description?: string | null;
|
|
86
|
+
properties?: Record<string, SnapshotPrimitive>;
|
|
87
|
+
attributes?: Record<string, string>;
|
|
88
|
+
children?: SnapshotNode[];
|
|
89
|
+
ignored?: boolean;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function makeNode(input: SnapshotNodeInput): SnapshotNode {
|
|
93
|
+
const node: SnapshotNode = {
|
|
94
|
+
nodeId: input.nodeId,
|
|
95
|
+
ignored: input.ignored ?? false,
|
|
96
|
+
role: input.role,
|
|
97
|
+
name: input.name ?? null,
|
|
98
|
+
value: input.value ?? null,
|
|
99
|
+
description: input.description ?? null,
|
|
100
|
+
properties: input.properties ?? {},
|
|
101
|
+
attributes: input.attributes ?? {},
|
|
102
|
+
children: input.children ?? [],
|
|
103
|
+
ref: input.ref ?? null,
|
|
104
|
+
subtreeSize: 1,
|
|
105
|
+
};
|
|
106
|
+
node.subtreeSize = countSubtree(node);
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function countSubtree(node: SnapshotNode): number {
|
|
111
|
+
return 1 + node.children.reduce((sum, child) => sum + countSubtree(child), 0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function makeSnapshot(
|
|
115
|
+
roots: SnapshotNode[],
|
|
116
|
+
options: { title?: string; url?: string } = {},
|
|
117
|
+
): Snapshot {
|
|
118
|
+
const url = options.url ?? "about:blank";
|
|
119
|
+
return {
|
|
120
|
+
title: options.title ?? "Demo Page",
|
|
121
|
+
url,
|
|
122
|
+
frames: [
|
|
123
|
+
{
|
|
124
|
+
status: "ok",
|
|
125
|
+
id: "main",
|
|
126
|
+
index: 0,
|
|
127
|
+
url,
|
|
128
|
+
name: null,
|
|
129
|
+
parentId: null,
|
|
130
|
+
roots,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
describe("renderSnapshot", () => {
|
|
137
|
+
test("renders page, frame, semantic roles, heading text, refs, and no command hint", async ({
|
|
138
|
+
expectSnapshot,
|
|
139
|
+
}) => {
|
|
140
|
+
await expectSnapshot(
|
|
141
|
+
`
|
|
142
|
+
<!doctype html>
|
|
143
|
+
<html>
|
|
144
|
+
<head><title>Product Docs</title></head>
|
|
145
|
+
<body>
|
|
146
|
+
<header>
|
|
147
|
+
<nav aria-label="Primary">
|
|
148
|
+
<a href="/docs">Docs</a>
|
|
149
|
+
</nav>
|
|
150
|
+
</header>
|
|
151
|
+
<main>
|
|
152
|
+
<h1>Welcome</h1>
|
|
153
|
+
<button>Save</button>
|
|
154
|
+
<input placeholder="Search docs" value="query" />
|
|
155
|
+
</main>
|
|
156
|
+
</body>
|
|
157
|
+
</html>
|
|
158
|
+
`,
|
|
159
|
+
`
|
|
160
|
+
<page title="Product Docs" url="<page-url>">
|
|
161
|
+
<frame index="0" url="<page-url>">
|
|
162
|
+
<document ref="l1">
|
|
163
|
+
Product Docs
|
|
164
|
+
<banner ref="l2">
|
|
165
|
+
...
|
|
166
|
+
Primary
|
|
167
|
+
<link ref="l4" href="/docs">Docs</link>
|
|
168
|
+
</banner>
|
|
169
|
+
<main ref="l5">
|
|
170
|
+
# Welcome
|
|
171
|
+
<button ref="l7">Save</button>
|
|
172
|
+
<textbox ref="l8" value="query" placeholder="Search docs">
|
|
173
|
+
Search docs
|
|
174
|
+
query
|
|
175
|
+
</textbox>
|
|
176
|
+
</main>
|
|
177
|
+
</document>
|
|
178
|
+
</frame>
|
|
179
|
+
</page>
|
|
180
|
+
`,
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("scopes an already-captured tree by ref with numeric-suffix fallback", async ({
|
|
185
|
+
expectScopedSnapshot,
|
|
186
|
+
}) => {
|
|
187
|
+
await expectScopedSnapshot(
|
|
188
|
+
`
|
|
189
|
+
<!doctype html>
|
|
190
|
+
<title>Scoped Snapshot</title>
|
|
191
|
+
<main>
|
|
192
|
+
<button>Sibling</button>
|
|
193
|
+
<button>Target</button>
|
|
194
|
+
</main>
|
|
195
|
+
`,
|
|
196
|
+
"e4",
|
|
197
|
+
`
|
|
198
|
+
<page title="Scoped Snapshot" url="<page-url>">
|
|
199
|
+
<frame index="0" url="<page-url>">
|
|
200
|
+
<button ref="l4">Target</button>
|
|
201
|
+
</frame>
|
|
202
|
+
</page>
|
|
203
|
+
`,
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("compacts low-value wrappers, clickable generics, single-child chains, and long child lists", async ({
|
|
208
|
+
expectSnapshot,
|
|
209
|
+
}) => {
|
|
210
|
+
await expectSnapshot(
|
|
211
|
+
`
|
|
212
|
+
<!doctype html>
|
|
213
|
+
<title>Compact Demo</title>
|
|
214
|
+
<style>.card { cursor: pointer; }</style>
|
|
215
|
+
<main>
|
|
216
|
+
<div><section><p>Flattened wrapper text</p></section></div>
|
|
217
|
+
<div class="card" onclick="void 0">Open card</div>
|
|
218
|
+
<main aria-label="Outer">
|
|
219
|
+
<nav aria-label="Navigation">
|
|
220
|
+
<form aria-label="Lookup"><button>Submit chain</button></form>
|
|
221
|
+
</nav>
|
|
222
|
+
</main>
|
|
223
|
+
<ul>
|
|
224
|
+
<li><button>One</button></li>
|
|
225
|
+
<li><button>Two</button></li>
|
|
226
|
+
<li><button>Three</button></li>
|
|
227
|
+
<li><button>Four</button></li>
|
|
228
|
+
<li><button>Five</button></li>
|
|
229
|
+
<li><button>Six</button></li>
|
|
230
|
+
</ul>
|
|
231
|
+
</main>
|
|
232
|
+
`,
|
|
233
|
+
`
|
|
234
|
+
<page title="Compact Demo" url="<page-url>">
|
|
235
|
+
<frame index="0" url="<page-url>">
|
|
236
|
+
<document ref="l1">
|
|
237
|
+
Compact Demo
|
|
238
|
+
<main ref="l2">
|
|
239
|
+
Flattened wrapper text
|
|
240
|
+
<button ref="l3">Open card</button>
|
|
241
|
+
<main ref="l4">
|
|
242
|
+
Outer
|
|
243
|
+
...
|
|
244
|
+
Lookup
|
|
245
|
+
<button ref="l7">Submit chain</button>
|
|
246
|
+
</main>
|
|
247
|
+
<list>
|
|
248
|
+
<listitem>
|
|
249
|
+
<button ref="l9">One</button>
|
|
250
|
+
</listitem>
|
|
251
|
+
<listitem>
|
|
252
|
+
<button ref="l11">Two</button>
|
|
253
|
+
</listitem>
|
|
254
|
+
<listitem>
|
|
255
|
+
<button ref="l13">Three</button>
|
|
256
|
+
</listitem>
|
|
257
|
+
<listitem>
|
|
258
|
+
<button ref="l15">Four</button>
|
|
259
|
+
</listitem>
|
|
260
|
+
[Truncated 2 more elements. Interactive elements: <button ref="l17">Five</button>, <button ref="l19">Six</button>]
|
|
261
|
+
</list>
|
|
262
|
+
</main>
|
|
263
|
+
</document>
|
|
264
|
+
</frame>
|
|
265
|
+
</page>
|
|
266
|
+
`,
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe("diffSnapshots", () => {
|
|
272
|
+
test("returns no rendered diff for unchanged browser-rendered snapshots", async ({
|
|
273
|
+
expectSnapshotDiff,
|
|
274
|
+
}) => {
|
|
275
|
+
const html = `
|
|
276
|
+
<!-- BEFORE -->
|
|
277
|
+
<!doctype html>
|
|
278
|
+
<title>Stable Demo</title>
|
|
279
|
+
<main><button>Stable</button></main>
|
|
280
|
+
`;
|
|
281
|
+
|
|
282
|
+
// AFTER: unchanged from BEFORE.
|
|
283
|
+
await expectSnapshotDiff(html, html, "");
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("tracks added, removed, and modified nodes under context ancestors", async ({
|
|
287
|
+
expectSnapshotDiff,
|
|
288
|
+
}) => {
|
|
289
|
+
await expectSnapshotDiff(
|
|
290
|
+
`
|
|
291
|
+
<!-- BEFORE -->
|
|
292
|
+
<!doctype html>
|
|
293
|
+
<title>Diff Demo</title>
|
|
294
|
+
<main>
|
|
295
|
+
<h1>Tasks</h1>
|
|
296
|
+
<button id="stable">Stable</button>
|
|
297
|
+
<button id="save">Save</button>
|
|
298
|
+
<button id="delete">Delete</button>
|
|
299
|
+
</main>
|
|
300
|
+
`,
|
|
301
|
+
`
|
|
302
|
+
<!-- AFTER -->
|
|
303
|
+
<!doctype html>
|
|
304
|
+
<title>Diff Demo</title>
|
|
305
|
+
<main>
|
|
306
|
+
<h1>Tasks</h1>
|
|
307
|
+
<button id="stable">Stable</button>
|
|
308
|
+
<button id="save">Saved</button>
|
|
309
|
+
<a id="docs" href="https://example.test/docs">Docs</a>
|
|
310
|
+
</main>
|
|
311
|
+
`,
|
|
312
|
+
`
|
|
313
|
+
<page title="Diff Demo" url="<page-url>">
|
|
314
|
+
<frame index="0" url="<page-url>">
|
|
315
|
+
<document ref="l1">
|
|
316
|
+
...
|
|
317
|
+
<main ref="l2">
|
|
318
|
+
...
|
|
319
|
+
~ <button ref="l5">Saved</button>
|
|
320
|
+
+ <link ref="l6" href="https://example.test/docs">Docs</link>
|
|
321
|
+
- <button ref="l6">...</button>
|
|
322
|
+
</main>
|
|
323
|
+
</document>
|
|
324
|
+
</frame>
|
|
325
|
+
</page>
|
|
326
|
+
`,
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test("suppresses href query and hash changes from browser-rendered links", async ({
|
|
331
|
+
expectSnapshotDiff,
|
|
332
|
+
}) => {
|
|
333
|
+
await expectSnapshotDiff(
|
|
334
|
+
`
|
|
335
|
+
<!-- BEFORE -->
|
|
336
|
+
<!doctype html>
|
|
337
|
+
<title>Href Demo</title>
|
|
338
|
+
<main><a id="docs" href="https://example.test/docs?utm=old#intro">Docs</a></main>
|
|
339
|
+
`,
|
|
340
|
+
`
|
|
341
|
+
<!-- AFTER -->
|
|
342
|
+
<!doctype html>
|
|
343
|
+
<title>Href Demo</title>
|
|
344
|
+
<main><a id="docs" href="https://example.test/docs?utm=new#api">Docs</a></main>
|
|
345
|
+
`,
|
|
346
|
+
"",
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test("suppresses ref-only changes that browser HTML cannot express directly", () => {
|
|
351
|
+
const before = makeSnapshot([
|
|
352
|
+
makeNode({
|
|
353
|
+
nodeId: "root",
|
|
354
|
+
role: "RootWebArea",
|
|
355
|
+
ref: "l1",
|
|
356
|
+
children: [
|
|
357
|
+
makeNode({
|
|
358
|
+
nodeId: "docs",
|
|
359
|
+
role: "link",
|
|
360
|
+
name: "Docs",
|
|
361
|
+
ref: "l2",
|
|
362
|
+
attributes: { href: "https://example.test/docs" },
|
|
363
|
+
}),
|
|
364
|
+
],
|
|
365
|
+
}),
|
|
366
|
+
]);
|
|
367
|
+
const after = makeSnapshot([
|
|
368
|
+
makeNode({
|
|
369
|
+
nodeId: "root",
|
|
370
|
+
role: "RootWebArea",
|
|
371
|
+
ref: "l10",
|
|
372
|
+
children: [
|
|
373
|
+
makeNode({
|
|
374
|
+
nodeId: "docs",
|
|
375
|
+
role: "link",
|
|
376
|
+
name: "Docs",
|
|
377
|
+
ref: "l20",
|
|
378
|
+
attributes: { href: "https://example.test/docs" },
|
|
379
|
+
}),
|
|
380
|
+
],
|
|
381
|
+
}),
|
|
382
|
+
]);
|
|
383
|
+
|
|
384
|
+
const diff = diffSnapshots(before, after);
|
|
385
|
+
|
|
386
|
+
expect(renderSnapshotDiff(diff)).toBe("");
|
|
387
|
+
});
|
|
388
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type SnapshotPrimitive = string | number | boolean | null;
|
|
2
|
+
|
|
3
|
+
export type Snapshot = {
|
|
4
|
+
title: string;
|
|
5
|
+
url: string;
|
|
6
|
+
frames: SnapshotFrame[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type SnapshotFrame = SnapshotAvailableFrame | SnapshotUnavailableFrame;
|
|
10
|
+
|
|
11
|
+
export type SnapshotAvailableFrame = {
|
|
12
|
+
status: "ok";
|
|
13
|
+
id: string;
|
|
14
|
+
index: number;
|
|
15
|
+
url: string;
|
|
16
|
+
name: string | null;
|
|
17
|
+
parentId: string | null;
|
|
18
|
+
roots: SnapshotNode[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type SnapshotUnavailableFrame = {
|
|
22
|
+
status: "unavailable";
|
|
23
|
+
id: string;
|
|
24
|
+
index: number;
|
|
25
|
+
url: string;
|
|
26
|
+
name: string | null;
|
|
27
|
+
parentId: string | null;
|
|
28
|
+
error: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type SnapshotNode = {
|
|
32
|
+
nodeId: string;
|
|
33
|
+
ignored: boolean;
|
|
34
|
+
role: string;
|
|
35
|
+
name: string | null;
|
|
36
|
+
value: SnapshotPrimitive;
|
|
37
|
+
description: string | null;
|
|
38
|
+
properties: Record<string, SnapshotPrimitive>;
|
|
39
|
+
attributes: Record<string, string>;
|
|
40
|
+
children: SnapshotNode[];
|
|
41
|
+
ref: string | null;
|
|
42
|
+
subtreeSize: number;
|
|
43
|
+
};
|