@truedat/dd 8.13.8 → 8.14.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.
@@ -0,0 +1,375 @@
1
+ {
2
+ "capturedAt": "2026-10-06",
3
+ "sourcePath": "/graphs/30148",
4
+ "groupLabel": "Manufacturing Business MySQL > db_man_bl_dtmep",
5
+ "counts": {
6
+ "visibleNodes": 362,
7
+ "visibleEdges": 410,
8
+ "children": 19,
9
+ "internalEdges": 38,
10
+ "externalEdges": 20
11
+ },
12
+ "nodes": [
13
+ {
14
+ "id": "sappm_activity_class",
15
+ "label": "sappm_activity_class",
16
+ "parentId": "dtmep",
17
+ "width": 360,
18
+ "height": 36,
19
+ "variant": "lineage-field"
20
+ },
21
+ {
22
+ "id": "sappm_activity_notification",
23
+ "label": "sappm_activity_notification",
24
+ "parentId": "dtmep",
25
+ "width": 360,
26
+ "height": 36,
27
+ "variant": "lineage-field"
28
+ },
29
+ {
30
+ "id": "sappm_costtype",
31
+ "label": "sappm_costtype",
32
+ "parentId": "dtmep",
33
+ "width": 360,
34
+ "height": 36,
35
+ "variant": "lineage-field"
36
+ },
37
+ {
38
+ "id": "sappm_equipment",
39
+ "label": "sappm_equipment",
40
+ "parentId": "dtmep",
41
+ "width": 360,
42
+ "height": 36,
43
+ "variant": "lineage-field"
44
+ },
45
+ {
46
+ "id": "sappm_equipment_object_class",
47
+ "label": "sappm_equipment_object_class",
48
+ "parentId": "dtmep",
49
+ "width": 360,
50
+ "height": 36,
51
+ "variant": "lineage-field"
52
+ },
53
+ {
54
+ "id": "sappm_gpa",
55
+ "label": "sappm_gpa",
56
+ "parentId": "dtmep",
57
+ "width": 360,
58
+ "height": 36,
59
+ "variant": "lineage-field"
60
+ },
61
+ {
62
+ "id": "sappm_kpi_objective",
63
+ "label": "sappm_kpi_objective",
64
+ "parentId": "dtmep",
65
+ "width": 360,
66
+ "height": 36,
67
+ "variant": "lineage-field"
68
+ },
69
+ {
70
+ "id": "sappm_manufacturing_calendar",
71
+ "label": "sappm_manufacturing_calendar",
72
+ "parentId": "dtmep",
73
+ "width": 360,
74
+ "height": 36,
75
+ "variant": "lineage-field"
76
+ },
77
+ {
78
+ "id": "sappm_md_functional_location",
79
+ "label": "sappm_md_functional_location",
80
+ "parentId": "dtmep",
81
+ "width": 360,
82
+ "height": 36,
83
+ "variant": "lineage-field"
84
+ },
85
+ {
86
+ "id": "sappm_md_pep",
87
+ "label": "sappm_md_pep",
88
+ "parentId": "dtmep",
89
+ "width": 360,
90
+ "height": 36,
91
+ "variant": "lineage-field"
92
+ },
93
+ {
94
+ "id": "sappm_md_work_center",
95
+ "label": "sappm_md_work_center",
96
+ "parentId": "dtmep",
97
+ "width": 360,
98
+ "height": 36,
99
+ "variant": "lineage-field"
100
+ },
101
+ {
102
+ "id": "sappm_notification",
103
+ "label": "sappm_notification",
104
+ "parentId": "dtmep",
105
+ "width": 360,
106
+ "height": 36,
107
+ "variant": "lineage-field"
108
+ },
109
+ {
110
+ "id": "sappm_notification_object_class",
111
+ "label": "sappm_notification_object_class",
112
+ "parentId": "dtmep",
113
+ "width": 360,
114
+ "height": 36,
115
+ "variant": "lineage-field"
116
+ },
117
+ {
118
+ "id": "sappm_operation",
119
+ "label": "sappm_operation",
120
+ "parentId": "dtmep",
121
+ "width": 360,
122
+ "height": 36,
123
+ "variant": "lineage-field"
124
+ },
125
+ {
126
+ "id": "sappm_order",
127
+ "label": "sappm_order",
128
+ "parentId": "dtmep",
129
+ "width": 360,
130
+ "height": 36,
131
+ "variant": "lineage-field"
132
+ },
133
+ {
134
+ "id": "sappm_order_notification",
135
+ "label": "sappm_order_notification",
136
+ "parentId": "dtmep",
137
+ "width": 360,
138
+ "height": 36,
139
+ "variant": "lineage-field"
140
+ },
141
+ {
142
+ "id": "sappm_order_pep_costtype",
143
+ "label": "sappm_order_pep_costtype",
144
+ "parentId": "dtmep",
145
+ "width": 360,
146
+ "height": 36,
147
+ "variant": "lineage-field"
148
+ },
149
+ {
150
+ "id": "sappm_work_center",
151
+ "label": "sappm_work_center",
152
+ "parentId": "dtmep",
153
+ "width": 360,
154
+ "height": 36,
155
+ "variant": "lineage-field"
156
+ },
157
+ {
158
+ "id": "sappm_workstation_calendar",
159
+ "label": "sappm_workstation_calendar",
160
+ "parentId": "dtmep",
161
+ "width": 360,
162
+ "height": 36,
163
+ "variant": "lineage-field"
164
+ }
165
+ ],
166
+ "edges": [
167
+ {
168
+ "id": "legacy-path-106-0",
169
+ "source": "sappm_activity_class",
170
+ "target": "sappm_order_notification"
171
+ },
172
+ {
173
+ "id": "legacy-path-593-0",
174
+ "source": "sappm_activity_notification",
175
+ "target": "sappm_notification"
176
+ },
177
+ {
178
+ "id": "legacy-path-409-0",
179
+ "source": "sappm_costtype",
180
+ "target": "sappm_order"
181
+ },
182
+ {
183
+ "id": "legacy-path-428-0",
184
+ "source": "sappm_costtype",
185
+ "target": "sappm_order_pep_costtype"
186
+ },
187
+ {
188
+ "id": "legacy-path-794-0",
189
+ "source": "sappm_equipment",
190
+ "target": "sappm_order"
191
+ },
192
+ {
193
+ "id": "legacy-path-335-0",
194
+ "source": "sappm_equipment_object_class",
195
+ "target": "sappm_equipment"
196
+ },
197
+ {
198
+ "id": "legacy-path-121-0",
199
+ "source": "sappm_equipment_object_class",
200
+ "target": "sappm_notification"
201
+ },
202
+ {
203
+ "id": "legacy-path-712-0",
204
+ "source": "sappm_equipment_object_class",
205
+ "target": "sappm_order"
206
+ },
207
+ {
208
+ "id": "legacy-path-384-0",
209
+ "source": "sappm_gpa",
210
+ "target": "sappm_kpi_objective"
211
+ },
212
+ {
213
+ "id": "legacy-path-765-0",
214
+ "source": "sappm_gpa",
215
+ "target": "sappm_md_functional_location"
216
+ },
217
+ {
218
+ "id": "legacy-path-141-0",
219
+ "source": "sappm_kpi_objective",
220
+ "target": "sappm_operation"
221
+ },
222
+ {
223
+ "id": "legacy-path-73-0",
224
+ "source": "sappm_manufacturing_calendar",
225
+ "target": "sappm_equipment"
226
+ },
227
+ {
228
+ "id": "legacy-path-761-0",
229
+ "source": "sappm_manufacturing_calendar",
230
+ "target": "sappm_kpi_objective"
231
+ },
232
+ {
233
+ "id": "legacy-path-764-0",
234
+ "source": "sappm_manufacturing_calendar",
235
+ "target": "sappm_notification"
236
+ },
237
+ {
238
+ "id": "legacy-path-48-0",
239
+ "source": "sappm_manufacturing_calendar",
240
+ "target": "sappm_operation"
241
+ },
242
+ {
243
+ "id": "legacy-path-350-0",
244
+ "source": "sappm_manufacturing_calendar",
245
+ "target": "sappm_order"
246
+ },
247
+ {
248
+ "id": "legacy-path-814-0",
249
+ "source": "sappm_manufacturing_calendar",
250
+ "target": "sappm_order_notification"
251
+ },
252
+ {
253
+ "id": "legacy-path-678-0",
254
+ "source": "sappm_manufacturing_calendar",
255
+ "target": "sappm_order_pep_costtype"
256
+ },
257
+ {
258
+ "id": "legacy-path-309-0",
259
+ "source": "sappm_md_functional_location",
260
+ "target": "sappm_equipment"
261
+ },
262
+ {
263
+ "id": "legacy-path-467-0",
264
+ "source": "sappm_md_functional_location",
265
+ "target": "sappm_notification"
266
+ },
267
+ {
268
+ "id": "legacy-path-576-0",
269
+ "source": "sappm_md_functional_location",
270
+ "target": "sappm_order"
271
+ },
272
+ {
273
+ "id": "legacy-path-233-0",
274
+ "source": "sappm_md_functional_location",
275
+ "target": "sappm_order_pep_costtype"
276
+ },
277
+ {
278
+ "id": "legacy-path-455-0",
279
+ "source": "sappm_md_pep",
280
+ "target": "sappm_order_pep_costtype"
281
+ },
282
+ {
283
+ "id": "legacy-path-685-0",
284
+ "source": "sappm_md_work_center",
285
+ "target": "sappm_operation"
286
+ },
287
+ {
288
+ "id": "legacy-path-444-0",
289
+ "source": "sappm_md_work_center",
290
+ "target": "sappm_order"
291
+ },
292
+ {
293
+ "id": "legacy-path-372-0",
294
+ "source": "sappm_notification",
295
+ "target": "sappm_equipment"
296
+ },
297
+ {
298
+ "id": "legacy-path-404-0",
299
+ "source": "sappm_notification",
300
+ "target": "sappm_operation"
301
+ },
302
+ {
303
+ "id": "legacy-path-294-0",
304
+ "source": "sappm_notification",
305
+ "target": "sappm_order"
306
+ },
307
+ {
308
+ "id": "legacy-path-390-0",
309
+ "source": "sappm_notification",
310
+ "target": "sappm_order_pep_costtype"
311
+ },
312
+ {
313
+ "id": "legacy-path-705-0",
314
+ "source": "sappm_notification_object_class",
315
+ "target": "sappm_notification"
316
+ },
317
+ {
318
+ "id": "legacy-path-667-0",
319
+ "source": "sappm_operation",
320
+ "target": "sappm_order"
321
+ },
322
+ {
323
+ "id": "legacy-path-686-0",
324
+ "source": "sappm_order",
325
+ "target": "sappm_order_pep_costtype"
326
+ },
327
+ {
328
+ "id": "legacy-path-214-0",
329
+ "source": "sappm_order_notification",
330
+ "target": "sappm_operation"
331
+ },
332
+ {
333
+ "id": "legacy-path-682-0",
334
+ "source": "sappm_order_notification",
335
+ "target": "sappm_order"
336
+ },
337
+ {
338
+ "id": "legacy-path-327-0",
339
+ "source": "sappm_work_center",
340
+ "target": "sappm_notification"
341
+ },
342
+ {
343
+ "id": "legacy-path-27-0",
344
+ "source": "sappm_work_center",
345
+ "target": "sappm_operation"
346
+ },
347
+ {
348
+ "id": "legacy-path-574-0",
349
+ "source": "sappm_work_center",
350
+ "target": "sappm_order"
351
+ },
352
+ {
353
+ "id": "legacy-path-807-0",
354
+ "source": "sappm_workstation_calendar",
355
+ "target": "sappm_order_notification"
356
+ }
357
+ ],
358
+ "baseline": {
359
+ "groupWidth": 392,
360
+ "groupHeight": 826,
361
+ "columns": 1
362
+ },
363
+ "rankOnlySimulation": {
364
+ "columns": 6,
365
+ "columnCounts": [11, 3, 1, 2, 1, 1],
366
+ "groupWidth": 2952,
367
+ "groupHeight": 906,
368
+ "columnX": [16, 528, 1040, 1552, 2064, 2576]
369
+ },
370
+ "notes": [
371
+ "IDs de recursos sustituidos por nombres; sin hosts, credenciales ni payload completo.",
372
+ "Las 20 conexiones externas solo se registran como conteo; no forman parte del fixture de rutas.",
373
+ "La simulación cambia solo el ranking: no incluye reservas de canales ni validación visual final."
374
+ ]
375
+ }
@@ -0,0 +1,202 @@
1
+ import { layoutVisibleLineageGraph } from "../lineageLayout";
2
+ import {
3
+ applyInternalEdgeRoutes,
4
+ applyInternalNodeLayers,
5
+ } from "../lineageInternalRoutes";
6
+ import fixture from "./fixtures/lineageInternalDependencies.json";
7
+
8
+ const input = () => [
9
+ {
10
+ id: "dtmep",
11
+ type: "lineageGroup",
12
+ position: { x: 0, y: 0 },
13
+ data: { label: "dtmep", resourceContainer: true },
14
+ },
15
+ ...fixture.nodes.map(({ variant, label, ...node }) => ({
16
+ ...node,
17
+ position: { x: 0, y: 0 },
18
+ data: { label, variant },
19
+ })),
20
+ ];
21
+ const snapshot = (nodes) =>
22
+ Object.fromEntries(nodes.map((n) => [n.id, n.data.absoluteGeometry]));
23
+
24
+ it.each([
25
+ ["compact", [19]],
26
+ ["expanded", [11, 3, 1, 2, 1, 1]],
27
+ ])("keeps all resources and routes in %s mode", (dependencyLayout, counts) => {
28
+ const nodes = layoutVisibleLineageGraph({
29
+ nodes: input(),
30
+ edges: fixture.edges,
31
+ dependencyLayout,
32
+ });
33
+ const children = nodes.filter((n) => n.parentId === "dtmep");
34
+ const xs = [...new Set(children.map((n) => n.position.x))].sort(
35
+ (a, b) => a - b,
36
+ );
37
+ expect(
38
+ xs.map((x) => children.filter((n) => n.position.x === x).length),
39
+ ).toEqual(counts);
40
+ const parent = nodes.find((n) => n.id === "dtmep");
41
+ expect(Object.keys(parent.data.internalEdgeGuides)).toHaveLength(38);
42
+ expect(parent.data.internalColumns).toHaveLength(
43
+ dependencyLayout === "compact" ? 3 : xs.length,
44
+ );
45
+ parent.data.internalColumns.forEach((column) => {
46
+ if (dependencyLayout === "expanded") expect(column.y).toBe(66);
47
+ if (dependencyLayout === "compact") {
48
+ children.forEach((node) => {
49
+ const above = node.position.y + node.height <= column.y - 12;
50
+ const below = node.position.y >= column.y + 24 + 8;
51
+ expect(above || below).toBe(true);
52
+ });
53
+ }
54
+ expect(column.count).toBeGreaterThan(0);
55
+ if (dependencyLayout === "expanded") {
56
+ expect(column.roles.length).toBeGreaterThan(0);
57
+ expect(column.roles).not.toContain(undefined);
58
+ }
59
+ });
60
+ if (dependencyLayout === "expanded") {
61
+ xs.forEach((x) => {
62
+ const column = children
63
+ .filter((n) => n.position.x === x)
64
+ .sort((a, b) => a.position.y - b.position.y);
65
+ column.slice(1).forEach((node, index) => {
66
+ expect(
67
+ node.position.y - column[index].position.y - column[index].height,
68
+ ).toBe(8);
69
+ });
70
+ });
71
+ }
72
+ children.forEach((n) => {
73
+ expect(n.position.x).toBeGreaterThanOrEqual(16);
74
+ expect(n.position.y).toBeGreaterThanOrEqual(58);
75
+ expect(n.position.x + n.width).toBeLessThanOrEqual(parent.width);
76
+ expect(n.position.y + n.height).toBeLessThanOrEqual(parent.height);
77
+ });
78
+ const reversed = layoutVisibleLineageGraph({
79
+ nodes: input().reverse(),
80
+ edges: [...fixture.edges].reverse(),
81
+ dependencyLayout,
82
+ });
83
+ expect(snapshot(reversed)).toEqual(snapshot(nodes));
84
+ const layered = applyInternalNodeLayers(nodes);
85
+ const result = applyInternalEdgeRoutes(fixture.edges, layered);
86
+ expect(result.map((e) => e.id)).toEqual(fixture.edges.map((e) => e.id));
87
+ const owner = layered.find((n) => n.id === "dtmep");
88
+ result.forEach((e) => {
89
+ expect(e.data.edgePath).toBe("guided-bezier");
90
+ expect(e.zIndex).toBe(150);
91
+ expect(e.zIndex).toBeGreaterThan(owner.zIndex);
92
+ expect(e.data.selectionBaseZIndex).toBe(owner.zIndex + 2);
93
+ expect(e.zIndex).toBeLessThan(
94
+ layered.find((n) => n.id === e.source).zIndex,
95
+ );
96
+ });
97
+ });
98
+
99
+ it("keeps plain lists compact and removes obsolete guides", () => {
100
+ const related = layoutVisibleLineageGraph({
101
+ nodes: input(),
102
+ edges: fixture.edges,
103
+ });
104
+ const plain = layoutVisibleLineageGraph({ nodes: related, edges: [] });
105
+ const children = plain
106
+ .filter((n) => n.parentId === "dtmep")
107
+ .sort((a, b) => a.position.y - b.position.y);
108
+ expect(new Set(children.map((n) => n.position.x)).size).toBe(1);
109
+ expect(children[1].position.y - children[0].position.y).toBe(40);
110
+ expect(
111
+ plain.find((n) => n.id === "dtmep").data.internalEdgeGuides,
112
+ ).toBeUndefined();
113
+ });
114
+
115
+ it.each(["compact", "expanded"])(
116
+ "keeps cyclic, isolated and differently sized children inside nested %s groups",
117
+ (dependencyLayout) => {
118
+ const nodes = [
119
+ { id: "outer", type: "lineageGroup", data: {} },
120
+ {
121
+ id: "inner",
122
+ parentId: "outer",
123
+ type: "lineageGroup",
124
+ data: { resourceContainer: true },
125
+ },
126
+ ...["a", "b", "c", "isolated"].map((id, index) => ({
127
+ id,
128
+ parentId: "inner",
129
+ width: 240 + index * 80,
130
+ height: 36,
131
+ data: { label: id, variant: "lineage-field" },
132
+ })),
133
+ ].map((node) => ({ position: { x: 0, y: 0 }, ...node }));
134
+ const edges = [
135
+ ["a", "b"],
136
+ ["b", "c"],
137
+ ["c", "a"],
138
+ ].map(([source, target]) => ({
139
+ id: `${source}-${target}`,
140
+ source,
141
+ target,
142
+ }));
143
+ const result = applyInternalNodeLayers(
144
+ layoutVisibleLineageGraph({ nodes, edges, dependencyLayout }),
145
+ );
146
+ const owner = result.find((node) => node.id === "inner");
147
+ expect(Object.keys(owner.data.internalEdgeGuides)).toHaveLength(3);
148
+ result
149
+ .filter((node) => node.parentId === "inner")
150
+ .forEach((node) => {
151
+ expect(node.position.x + node.width).toBeLessThanOrEqual(owner.width);
152
+ expect(node.position.y + node.height).toBeLessThanOrEqual(owner.height);
153
+ });
154
+ const routed = applyInternalEdgeRoutes(edges, result);
155
+ routed.forEach((edge) => {
156
+ expect(edge.targetHandle).toBe("target-right-node");
157
+ expect(edge.zIndex).toBe(150);
158
+ expect(edge.zIndex).toBeGreaterThan(owner.zIndex);
159
+ expect(edge.data.selectionBaseZIndex).toBe(owner.zIndex + 2);
160
+ expect(edge.zIndex).toBeLessThan(
161
+ result.find((node) => node.id === edge.source).zIndex,
162
+ );
163
+ expect(Number.isFinite(edge.data.routeGuide.laneX)).toBe(true);
164
+ });
165
+ },
166
+ );
167
+
168
+ it("drops an obsolete internal layer when its owner has no route guide", () => {
169
+ const [edge] = applyInternalEdgeRoutes(
170
+ [
171
+ {
172
+ id: "obsolete",
173
+ zIndex: 9,
174
+ data: { internalOwnerId: "group", selectionBaseZIndex: 206 },
175
+ },
176
+ ],
177
+ [],
178
+ );
179
+ expect(edge.zIndex).toBe(150);
180
+ expect(edge.data.selectionBaseZIndex).toBe(100);
181
+ expect(edge.data.internalOwnerId).toBeUndefined();
182
+ });
183
+
184
+ it("keeps compact routing within a narrow right gutter", () => {
185
+ const nodes = layoutVisibleLineageGraph({
186
+ nodes: input(),
187
+ edges: fixture.edges,
188
+ dependencyLayout: "compact",
189
+ });
190
+ const parent = nodes.find((node) => node.id === "dtmep");
191
+ const right = Math.max(
192
+ ...nodes
193
+ .filter((node) => node.parentId === "dtmep")
194
+ .map((node) => node.position.x + node.width),
195
+ );
196
+ const guides = Object.values(parent.data.internalEdgeGuides);
197
+ expect(parent.width - right).toBeLessThanOrEqual(48 + guides.length * 4);
198
+ guides.forEach((guide) => {
199
+ expect(guide.laneX).toBeGreaterThan(right);
200
+ expect(guide.laneX).toBeLessThan(parent.width);
201
+ });
202
+ });
@@ -162,9 +162,9 @@ describe("compound root columns", () => {
162
162
  ];
163
163
  const positions = geometry(freshLayout()({ nodes, edges }));
164
164
  expect(positions.a.x).toBe(positions.b.x);
165
- expect(positions.b.y).toBe(positions.a.y + positions.a.h + 44);
166
- expect(positions.c.x).toBe(positions.a.x + positions.a.w + 152);
167
- expect(positions.d.x).toBe(positions.c.x + positions.c.w + 152);
165
+ expect(positions.b.y).toBe(positions.a.y + positions.a.h + 48);
166
+ expect(positions.c.x).toBe(positions.a.x + positions.a.w + 160);
167
+ expect(positions.d.x).toBe(positions.c.x + positions.c.w + 160);
168
168
  expect(positions.c.y).toBe(positions.a.y);
169
169
  expect(
170
170
  geometry(
@@ -186,7 +186,7 @@ describe("compound root columns", () => {
186
186
  expect(new Set(positions.map(({ x }) => x)).size).toBe(1);
187
187
  positions.slice(1).forEach((current, i) => {
188
188
  expect(current.y).toBeGreaterThanOrEqual(
189
- positions[i].y + positions[i].h + 44,
189
+ positions[i].y + positions[i].h + 48,
190
190
  );
191
191
  });
192
192
  });
@@ -220,7 +220,7 @@ describe("compound root columns", () => {
220
220
  expect(expanded.peer.x).toBe(geometry(collapsed).peer.x);
221
221
  expect(expanded.peer.y).toBe(geometry(collapsed).peer.y);
222
222
  expect(expanded.peer.y).toBeGreaterThanOrEqual(
223
- expanded.global.y + expanded.global.h + 44,
223
+ expanded.global.y + expanded.global.h + 48,
224
224
  );
225
225
  expect(new Set(fields.map(({ id }) => expanded[id].x)).size).toBe(1);
226
226
  fields.forEach(({ id }) => {
@@ -229,7 +229,7 @@ describe("compound root columns", () => {
229
229
  expanded.global.y + expanded.global.h,
230
230
  );
231
231
  });
232
- expect(expanded.sink.x).toBe(expanded.global.x + expanded.global.w + 152);
232
+ expect(expanded.sink.x).toBe(expanded.global.x + expanded.global.w + 160);
233
233
  });
234
234
  });
235
235
 
@@ -0,0 +1,27 @@
1
+ import {
2
+ readDependencyLayout,
3
+ writeDependencyLayout,
4
+ } from "../lineageViewPreferences";
5
+ const KEY = "td.lineage.dependencyLayout.v1";
6
+ afterEach(() => {
7
+ localStorage.clear();
8
+ jest.restoreAllMocks();
9
+ });
10
+ it("defaults to compact and restores an explicit valid choice", () => {
11
+ expect(readDependencyLayout()).toBe("compact");
12
+ expect(writeDependencyLayout("expanded")).toBe(true);
13
+ expect(localStorage.getItem(KEY)).toBe("expanded");
14
+ expect(readDependencyLayout()).toBe("expanded");
15
+ localStorage.setItem(KEY, "invalid");
16
+ expect(readDependencyLayout()).toBe("compact");
17
+ });
18
+ it("works when browser storage is blocked", () => {
19
+ jest.spyOn(Storage.prototype, "getItem").mockImplementation(() => {
20
+ throw new Error("blocked");
21
+ });
22
+ jest.spyOn(Storage.prototype, "setItem").mockImplementation(() => {
23
+ throw new Error("blocked");
24
+ });
25
+ expect(readDependencyLayout()).toBe("compact");
26
+ expect(writeDependencyLayout("expanded")).toBe(false);
27
+ });