@marimo-team/frontend 0.23.1-dev8 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{JsonOutput-BY31ccA7.js → JsonOutput-CavtrueA.js} +1 -1
- package/dist/assets/{MarimoErrorOutput--Yd2Aw0J.js → MarimoErrorOutput-Bmp8DLLo.js} +1 -1
- package/dist/assets/RenderHTML-CM3WMmA8.js +1 -0
- package/dist/assets/{add-connection-dialog-CjvNOKgb.js → add-connection-dialog-BGZvJkor.js} +1 -1
- package/dist/assets/{agent-panel-C24uwabG.js → agent-panel-BvL9Lu9c.js} +1 -1
- package/dist/assets/{cell-editor-zW0u82sK.js → cell-editor-B40o_zx_.js} +1 -1
- package/dist/assets/{chat-display-DsHMZa9F.js → chat-display-M_nvYuHH.js} +1 -1
- package/dist/assets/{chat-panel-o9D3upnX.js → chat-panel-BMOW93uQ.js} +1 -1
- package/dist/assets/{chat-ui-BYS03y86.js → chat-ui-DyeimpVh.js} +1 -1
- package/dist/assets/{column-preview-Dwv5a_zE.js → column-preview-AfcgbFG_.js} +1 -1
- package/dist/assets/{command-palette-BYbKGSF3.js → command-palette-BgvdyU3B.js} +1 -1
- package/dist/assets/{documentation-panel-CA2pWMgB.js → documentation-panel-DUPcsi8P.js} +1 -1
- package/dist/assets/{edit-page-CMUN3ESy.js → edit-page-DD4uEDmX.js} +4 -4
- package/dist/assets/{error-panel-CbqfK1HJ.js → error-panel-DQOeSv5-.js} +1 -1
- package/dist/assets/{file-explorer-panel-CbS8z-JR.js → file-explorer-panel-B67zjs2X.js} +1 -1
- package/dist/assets/{form-DLyXacSF.js → form-BJ6VFU8l.js} +1 -1
- package/dist/assets/{hooks-kZJc1iBf.js → hooks-DvwShzDb.js} +1 -1
- package/dist/assets/index-y6osgSWB.js +42 -0
- package/dist/assets/{layout-tmN-U1zs.js → layout-erv8pLIP.js} +1 -1
- package/dist/assets/{panels-CLfdzLPR.js → panels-1u-RE72f.js} +1 -1
- package/dist/assets/{run-page-DPuH6QY4.js → run-page-DfWH_1mz.js} +1 -1
- package/dist/assets/{scratchpad-panel-BsMm0GQP.js → scratchpad-panel-CnaiXtoJ.js} +1 -1
- package/dist/assets/{session-panel-CTDzGShO.js → session-panel-C68GBFwH.js} +1 -1
- package/dist/assets/{snippets-panel-CWof0wHk.js → snippets-panel-BmIdR0lc.js} +1 -1
- package/dist/assets/state-D1n-olwf.js +3 -0
- package/dist/assets/{useNotebookActions-DHBEqrc_.js → useNotebookActions-Ch1o32Jw.js} +1 -1
- package/dist/index.html +7 -7
- package/package.json +4 -4
- package/src/core/islands/__tests__/bridge.test.ts +2 -12
- package/src/core/islands/__tests__/islands-harness.test.ts +348 -0
- package/src/core/islands/__tests__/parse.test.ts +466 -24
- package/src/core/islands/__tests__/test-utils.tsx +263 -0
- package/src/core/islands/bootstrap.ts +265 -0
- package/src/core/islands/bridge.ts +154 -75
- package/src/core/islands/components/IslandControls.tsx +103 -0
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +185 -0
- package/src/core/islands/components/__tests__/useIslandControls.test.ts +208 -0
- package/src/core/islands/components/output-wrapper.tsx +76 -93
- package/src/core/islands/components/useIslandControls.ts +60 -0
- package/src/core/islands/components/web-components.tsx +168 -40
- package/src/core/islands/constants.ts +28 -0
- package/src/core/islands/main.ts +7 -205
- package/src/core/islands/parse.ts +73 -26
- package/src/core/islands/worker-factory.ts +86 -0
- package/src/plugins/core/RenderHTML.tsx +9 -0
- package/src/plugins/core/__test__/RenderHTML.test.ts +27 -0
- package/src/plugins/core/__test__/trusted-url.test.ts +48 -0
- package/src/plugins/core/registerReactComponent.tsx +11 -8
- package/src/plugins/core/trusted-url.ts +20 -0
- package/src/plugins/impl/ButtonPlugin.tsx +4 -6
- package/src/plugins/impl/CodeEditorPlugin.tsx +15 -18
- package/src/plugins/impl/DataEditorPlugin.tsx +8 -14
- package/src/plugins/impl/DataTablePlugin.tsx +8 -9
- package/src/plugins/impl/FileUploadPlugin.tsx +39 -43
- package/src/plugins/impl/FormPlugin.tsx +2 -6
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +27 -1
- package/src/plugins/impl/anywidget/widget-binding.ts +13 -0
- package/src/plugins/impl/chat/ChatPlugin.tsx +17 -20
- package/src/plugins/impl/data-explorer/DataExplorerPlugin.tsx +5 -8
- package/src/plugins/impl/matplotlib/matplotlib-renderer.ts +38 -14
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +21 -0
- package/src/plugins/impl/mpl-interactive/__tests__/MplInteractivePlugin.test.tsx +119 -0
- package/src/plugins/impl/panel/PanelPlugin.tsx +31 -10
- package/src/plugins/impl/panel/__tests__/PanelPlugin.test.ts +60 -0
- package/src/plugins/impl/vega/VegaPlugin.tsx +5 -8
- package/src/plugins/layout/NavigationMenuPlugin.tsx +2 -6
- package/dist/assets/RenderHTML-CbuarQqA.js +0 -1
- package/dist/assets/index-bjxpaV0V.js +0 -42
- package/dist/assets/state-BvnlMKdT.js +0 -3
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import {
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
ISLAND_DATA_ATTRIBUTES,
|
|
5
|
+
ISLAND_TAG_NAMES,
|
|
6
|
+
} from "@/core/islands/constants";
|
|
7
|
+
import {
|
|
8
|
+
createMarimoFile,
|
|
9
|
+
extractIslandCodeFromEmbed,
|
|
10
|
+
parseIslandCode,
|
|
11
|
+
parseIslandEditor,
|
|
12
|
+
parseIslandElement,
|
|
13
|
+
parseIslandElementsIntoApps,
|
|
14
|
+
parseMarimoIslandApps,
|
|
15
|
+
} from "../parse";
|
|
16
|
+
import { createMockIslandElement, createMockIslands } from "./test-utils.tsx";
|
|
4
17
|
|
|
5
18
|
describe("createMarimoFile", () => {
|
|
6
19
|
it("should return a string", () => {
|
|
@@ -24,15 +37,14 @@ describe("createMarimoFile", () => {
|
|
|
24
37
|
],
|
|
25
38
|
};
|
|
26
39
|
const result = createMarimoFile(app);
|
|
27
|
-
|
|
28
|
-
"import marimo
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
expect(result).toBe(expected);
|
|
40
|
+
expect(result).toMatchInlineSnapshot(`
|
|
41
|
+
"import marimo
|
|
42
|
+
app = marimo.App()
|
|
43
|
+
@app.cell
|
|
44
|
+
def __():
|
|
45
|
+
print("Hello, World!")
|
|
46
|
+
return"
|
|
47
|
+
`);
|
|
36
48
|
});
|
|
37
49
|
|
|
38
50
|
it("should correctly format multiple cells", () => {
|
|
@@ -47,19 +59,54 @@ describe("createMarimoFile", () => {
|
|
|
47
59
|
],
|
|
48
60
|
};
|
|
49
61
|
const result = createMarimoFile(app);
|
|
50
|
-
|
|
51
|
-
"import marimo
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
expect(result).toMatchInlineSnapshot(`
|
|
63
|
+
"import marimo
|
|
64
|
+
app = marimo.App()
|
|
65
|
+
@app.cell
|
|
66
|
+
def __():
|
|
67
|
+
print("Hello, World!")
|
|
68
|
+
return
|
|
69
|
+
@app.cell
|
|
70
|
+
def __():
|
|
71
|
+
print("Goodbye, World!")
|
|
72
|
+
return"
|
|
73
|
+
`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should create an async marimo file from cells", () => {
|
|
77
|
+
const app = {
|
|
78
|
+
cells: [{ code: "await asyncio.sleep(1)" }],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const result = createMarimoFile(app);
|
|
82
|
+
|
|
83
|
+
expect(result).toMatchInlineSnapshot(`
|
|
84
|
+
"import marimo
|
|
85
|
+
app = marimo.App()
|
|
86
|
+
@app.cell
|
|
87
|
+
async def __():
|
|
88
|
+
await asyncio.sleep(1)
|
|
89
|
+
return"
|
|
90
|
+
`);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should properly indent multi-line code", () => {
|
|
94
|
+
const app = {
|
|
95
|
+
cells: [{ code: "if True:\n print('hello')\n print('world')" }],
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const result = createMarimoFile(app);
|
|
99
|
+
|
|
100
|
+
expect(result).toMatchInlineSnapshot(`
|
|
101
|
+
"import marimo
|
|
102
|
+
app = marimo.App()
|
|
103
|
+
@app.cell
|
|
104
|
+
def __():
|
|
105
|
+
if True:
|
|
106
|
+
print('hello')
|
|
107
|
+
print('world')
|
|
108
|
+
return"
|
|
109
|
+
`);
|
|
63
110
|
});
|
|
64
111
|
});
|
|
65
112
|
|
|
@@ -86,4 +133,399 @@ describe("parseIslandCode", () => {
|
|
|
86
133
|
expect(result).toBe(expected);
|
|
87
134
|
},
|
|
88
135
|
);
|
|
136
|
+
|
|
137
|
+
it("should parse URI-encoded code with special characters", () => {
|
|
138
|
+
const code = "print(%22Hello%2C%20world!%22)";
|
|
139
|
+
|
|
140
|
+
const result = parseIslandCode(code);
|
|
141
|
+
|
|
142
|
+
expect(result).toBe('print("Hello, world!")');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should handle null and undefined", () => {
|
|
146
|
+
expect(parseIslandCode(null)).toBe("");
|
|
147
|
+
expect(parseIslandCode(undefined)).toBe("");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("parseIslandEditor", () => {
|
|
152
|
+
it("should parse JSON-encoded code", () => {
|
|
153
|
+
const code = '"print(\\"Hello\\")"';
|
|
154
|
+
|
|
155
|
+
const result = parseIslandEditor(code);
|
|
156
|
+
|
|
157
|
+
expect(result).toBe('print("Hello")');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should return raw code if JSON parsing fails", () => {
|
|
161
|
+
const code = 'print("Hello")';
|
|
162
|
+
|
|
163
|
+
const result = parseIslandEditor(code);
|
|
164
|
+
|
|
165
|
+
expect(result).toBe('print("Hello")');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should handle null and undefined", () => {
|
|
169
|
+
expect(parseIslandEditor(null)).toBe("");
|
|
170
|
+
expect(parseIslandEditor(undefined)).toBe("");
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe("extractIslandCodeFromEmbed", () => {
|
|
175
|
+
it("should extract code from marimo-cell-code element", () => {
|
|
176
|
+
const element = createMockIslandElement({
|
|
177
|
+
code: 'print("test")',
|
|
178
|
+
});
|
|
179
|
+
element.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
180
|
+
|
|
181
|
+
const result = extractIslandCodeFromEmbed(element);
|
|
182
|
+
|
|
183
|
+
expect(result).toBe('print("test")');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("should return empty string for non-reactive cells", () => {
|
|
187
|
+
const element = createMockIslandElement({
|
|
188
|
+
code: 'print("test")',
|
|
189
|
+
});
|
|
190
|
+
element.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "false");
|
|
191
|
+
|
|
192
|
+
const result = extractIslandCodeFromEmbed(element);
|
|
193
|
+
|
|
194
|
+
expect(result).toBe("");
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should extract code from editor element if code element not found", () => {
|
|
198
|
+
const element = document.createElement(ISLAND_TAG_NAMES.ISLAND);
|
|
199
|
+
element.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
200
|
+
const editor = document.createElement(ISLAND_TAG_NAMES.CODE_EDITOR);
|
|
201
|
+
editor.setAttribute("data-initial-value", '"print(\\"hello\\")"');
|
|
202
|
+
element.appendChild(editor);
|
|
203
|
+
|
|
204
|
+
const result = extractIslandCodeFromEmbed(element);
|
|
205
|
+
|
|
206
|
+
expect(result).toBe('print("hello")');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should return empty string if no code elements found", () => {
|
|
210
|
+
const element = document.createElement(ISLAND_TAG_NAMES.ISLAND);
|
|
211
|
+
element.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
212
|
+
|
|
213
|
+
const result = extractIslandCodeFromEmbed(element);
|
|
214
|
+
|
|
215
|
+
expect(result).toBe("");
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe("parseIslandElement", () => {
|
|
220
|
+
it("should parse a valid island element", () => {
|
|
221
|
+
const element = createMockIslandElement({
|
|
222
|
+
code: 'print("test")',
|
|
223
|
+
innerHTML: "<div>output</div>",
|
|
224
|
+
});
|
|
225
|
+
element.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
226
|
+
|
|
227
|
+
const result = parseIslandElement(element);
|
|
228
|
+
|
|
229
|
+
expect(result).toMatchInlineSnapshot(`
|
|
230
|
+
{
|
|
231
|
+
"code": "print("test")",
|
|
232
|
+
"output": "<div>output</div>",
|
|
233
|
+
}
|
|
234
|
+
`);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("should return null if output is missing", () => {
|
|
238
|
+
const element = document.createElement(ISLAND_TAG_NAMES.ISLAND);
|
|
239
|
+
element.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
240
|
+
const codeElement = document.createElement(ISLAND_TAG_NAMES.CELL_CODE);
|
|
241
|
+
codeElement.textContent = encodeURIComponent('print("test")');
|
|
242
|
+
element.appendChild(codeElement);
|
|
243
|
+
|
|
244
|
+
const result = parseIslandElement(element);
|
|
245
|
+
|
|
246
|
+
expect(result).toBeNull();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("should return null if code is missing", () => {
|
|
250
|
+
const element = document.createElement(ISLAND_TAG_NAMES.ISLAND);
|
|
251
|
+
const outputElement = document.createElement(ISLAND_TAG_NAMES.CELL_OUTPUT);
|
|
252
|
+
outputElement.innerHTML = "<div>output</div>";
|
|
253
|
+
element.appendChild(outputElement);
|
|
254
|
+
|
|
255
|
+
const result = parseIslandElement(element);
|
|
256
|
+
|
|
257
|
+
expect(result).toBeNull();
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe("parseIslandElementsIntoApps", () => {
|
|
262
|
+
it("should parse single app with single cell", () => {
|
|
263
|
+
const elements = [
|
|
264
|
+
createMockIslandElement({
|
|
265
|
+
appId: "app1",
|
|
266
|
+
cellIdx: "0",
|
|
267
|
+
code: 'print("hello")',
|
|
268
|
+
innerHTML: "<div>output1</div>",
|
|
269
|
+
}),
|
|
270
|
+
];
|
|
271
|
+
elements[0].setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
272
|
+
|
|
273
|
+
const result = parseIslandElementsIntoApps(elements);
|
|
274
|
+
|
|
275
|
+
expect(result).toHaveLength(1);
|
|
276
|
+
expect(result).toMatchInlineSnapshot(`
|
|
277
|
+
[
|
|
278
|
+
{
|
|
279
|
+
"cells": [
|
|
280
|
+
{
|
|
281
|
+
"code": "print("hello")",
|
|
282
|
+
"idx": 0,
|
|
283
|
+
"output": "<div>output1</div>",
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
"id": "app1",
|
|
287
|
+
},
|
|
288
|
+
]
|
|
289
|
+
`);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("should parse single app with multiple cells", () => {
|
|
293
|
+
const elements = createMockIslands(3, "app1").map((el) => {
|
|
294
|
+
el.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
295
|
+
return el;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const result = parseIslandElementsIntoApps(elements);
|
|
299
|
+
|
|
300
|
+
expect(result).toHaveLength(1);
|
|
301
|
+
expect(result).toMatchInlineSnapshot(`
|
|
302
|
+
[
|
|
303
|
+
{
|
|
304
|
+
"cells": [
|
|
305
|
+
{
|
|
306
|
+
"code": "cell_0 = 0",
|
|
307
|
+
"idx": 0,
|
|
308
|
+
"output": "<div>output 0</div>",
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"code": "cell_1 = 1",
|
|
312
|
+
"idx": 1,
|
|
313
|
+
"output": "<div>output 1</div>",
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"code": "cell_2 = 2",
|
|
317
|
+
"idx": 2,
|
|
318
|
+
"output": "<div>output 2</div>",
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
"id": "app1",
|
|
322
|
+
},
|
|
323
|
+
]
|
|
324
|
+
`);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("should parse multiple apps", () => {
|
|
328
|
+
const elements = [
|
|
329
|
+
...createMockIslands(2, "app1"),
|
|
330
|
+
...createMockIslands(2, "app2"),
|
|
331
|
+
].map((el) => {
|
|
332
|
+
el.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
333
|
+
return el;
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const result = parseIslandElementsIntoApps(elements);
|
|
337
|
+
|
|
338
|
+
expect(result).toHaveLength(2);
|
|
339
|
+
expect(result).toMatchInlineSnapshot(`
|
|
340
|
+
[
|
|
341
|
+
{
|
|
342
|
+
"cells": [
|
|
343
|
+
{
|
|
344
|
+
"code": "cell_0 = 0",
|
|
345
|
+
"idx": 0,
|
|
346
|
+
"output": "<div>output 0</div>",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
"code": "cell_1 = 1",
|
|
350
|
+
"idx": 1,
|
|
351
|
+
"output": "<div>output 1</div>",
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
"id": "app1",
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
"cells": [
|
|
358
|
+
{
|
|
359
|
+
"code": "cell_0 = 0",
|
|
360
|
+
"idx": 0,
|
|
361
|
+
"output": "<div>output 0</div>",
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
"code": "cell_1 = 1",
|
|
365
|
+
"idx": 1,
|
|
366
|
+
"output": "<div>output 1</div>",
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
"id": "app2",
|
|
370
|
+
},
|
|
371
|
+
]
|
|
372
|
+
`);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("should skip elements without app-id", () => {
|
|
376
|
+
const validElement = createMockIslandElement({
|
|
377
|
+
appId: "app1",
|
|
378
|
+
code: 'print("test")',
|
|
379
|
+
innerHTML: "<div>output</div>",
|
|
380
|
+
});
|
|
381
|
+
validElement.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
382
|
+
|
|
383
|
+
const invalidElement = document.createElement(ISLAND_TAG_NAMES.ISLAND);
|
|
384
|
+
invalidElement.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
385
|
+
|
|
386
|
+
const result = parseIslandElementsIntoApps([validElement, invalidElement]);
|
|
387
|
+
|
|
388
|
+
expect(result).toHaveLength(1);
|
|
389
|
+
expect(result).toMatchInlineSnapshot(`
|
|
390
|
+
[
|
|
391
|
+
{
|
|
392
|
+
"cells": [
|
|
393
|
+
{
|
|
394
|
+
"code": "print("test")",
|
|
395
|
+
"idx": 0,
|
|
396
|
+
"output": "<div>output</div>",
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
"id": "app1",
|
|
400
|
+
},
|
|
401
|
+
]
|
|
402
|
+
`);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("should assign correct cell indices", () => {
|
|
406
|
+
const elements = createMockIslands(3, "app1").map((el) => {
|
|
407
|
+
el.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
408
|
+
return el;
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const result = parseIslandElementsIntoApps(elements);
|
|
412
|
+
|
|
413
|
+
expect(result).toMatchInlineSnapshot(`
|
|
414
|
+
[
|
|
415
|
+
{
|
|
416
|
+
"cells": [
|
|
417
|
+
{
|
|
418
|
+
"code": "cell_0 = 0",
|
|
419
|
+
"idx": 0,
|
|
420
|
+
"output": "<div>output 0</div>",
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"code": "cell_1 = 1",
|
|
424
|
+
"idx": 1,
|
|
425
|
+
"output": "<div>output 1</div>",
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
"code": "cell_2 = 2",
|
|
429
|
+
"idx": 2,
|
|
430
|
+
"output": "<div>output 2</div>",
|
|
431
|
+
},
|
|
432
|
+
],
|
|
433
|
+
"id": "app1",
|
|
434
|
+
},
|
|
435
|
+
]
|
|
436
|
+
`);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("should set data-cell-idx attribute on elements", () => {
|
|
440
|
+
const elements = createMockIslands(2, "app1").map((el) => {
|
|
441
|
+
el.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
442
|
+
return el;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
parseIslandElementsIntoApps(elements);
|
|
446
|
+
|
|
447
|
+
expect(elements[0].getAttribute(ISLAND_DATA_ATTRIBUTES.CELL_IDX)).toBe("0");
|
|
448
|
+
expect(elements[1].getAttribute(ISLAND_DATA_ATTRIBUTES.CELL_IDX)).toBe("1");
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe("parseMarimoIslandApps", () => {
|
|
453
|
+
let container: HTMLDivElement;
|
|
454
|
+
|
|
455
|
+
beforeEach(() => {
|
|
456
|
+
container = document.createElement("div");
|
|
457
|
+
document.body.appendChild(container);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
afterEach(() => {
|
|
461
|
+
document.body.removeChild(container);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it("should parse islands from document", () => {
|
|
465
|
+
const elements = createMockIslands(2, "app1").map((el) => {
|
|
466
|
+
el.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
467
|
+
return el;
|
|
468
|
+
});
|
|
469
|
+
for (const el of elements) {
|
|
470
|
+
container.appendChild(el);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const result = parseMarimoIslandApps(container);
|
|
474
|
+
|
|
475
|
+
expect(result).toHaveLength(1);
|
|
476
|
+
expect(result).toMatchInlineSnapshot(`
|
|
477
|
+
[
|
|
478
|
+
{
|
|
479
|
+
"cells": [
|
|
480
|
+
{
|
|
481
|
+
"code": "cell_0 = 0",
|
|
482
|
+
"idx": 0,
|
|
483
|
+
"output": "<div>output 0</div>",
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"code": "cell_1 = 1",
|
|
487
|
+
"idx": 1,
|
|
488
|
+
"output": "<div>output 1</div>",
|
|
489
|
+
},
|
|
490
|
+
],
|
|
491
|
+
"id": "app1",
|
|
492
|
+
},
|
|
493
|
+
]
|
|
494
|
+
`);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it("should return empty array if no islands found", () => {
|
|
498
|
+
const result = parseMarimoIslandApps(container);
|
|
499
|
+
|
|
500
|
+
expect(result).toEqual([]);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it("should accept custom root element", () => {
|
|
504
|
+
const customRoot = document.createElement("div");
|
|
505
|
+
const elements = createMockIslands(1, "app1").map((el) => {
|
|
506
|
+
el.setAttribute(ISLAND_DATA_ATTRIBUTES.REACTIVE, "true");
|
|
507
|
+
return el;
|
|
508
|
+
});
|
|
509
|
+
for (const el of elements) {
|
|
510
|
+
customRoot.appendChild(el);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const result = parseMarimoIslandApps(customRoot);
|
|
514
|
+
|
|
515
|
+
expect(result).toHaveLength(1);
|
|
516
|
+
expect(result).toMatchInlineSnapshot(`
|
|
517
|
+
[
|
|
518
|
+
{
|
|
519
|
+
"cells": [
|
|
520
|
+
{
|
|
521
|
+
"code": "cell_0 = 0",
|
|
522
|
+
"idx": 0,
|
|
523
|
+
"output": "<div>output 0</div>",
|
|
524
|
+
},
|
|
525
|
+
],
|
|
526
|
+
"id": "app1",
|
|
527
|
+
},
|
|
528
|
+
]
|
|
529
|
+
`);
|
|
530
|
+
});
|
|
89
531
|
});
|