@marimo-team/islands 0.21.2-dev6 → 0.21.2-dev62
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/{ConnectedDataExplorerComponent-D0GoOd_c.js → ConnectedDataExplorerComponent-DrWDbHRV.js} +1 -1
- package/dist/{any-language-editor-DlsjUw_l.js → any-language-editor-BRpxklRq.js} +1 -1
- package/dist/{copy-DIK6DiIA.js → copy-BjkXCUxP.js} +12 -2
- package/dist/{esm-BLobyqMs.js → esm-No_6eSQS.js} +1 -1
- package/dist/{glide-data-editor-pZyd9UJ_.js → glide-data-editor-858wsVkd.js} +1 -1
- package/dist/main.js +821 -417
- package/dist/{spec-Bfvf9Hre.js → spec-oVDndBz4.js} +25 -16
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/__mocks__/notebook.ts +9 -9
- package/src/__mocks__/requests.ts +1 -0
- package/src/__tests__/branded.ts +20 -0
- package/src/components/app-config/user-config-form.tsx +5 -4
- package/src/components/data-table/__tests__/utils.test.ts +138 -1
- package/src/components/data-table/charts/__tests__/storage.test.ts +7 -7
- package/src/components/data-table/context-menu.tsx +9 -5
- package/src/components/data-table/data-table.tsx +3 -0
- package/src/components/data-table/range-focus/__tests__/atoms.test.ts +8 -2
- package/src/components/data-table/range-focus/__tests__/test-utils.ts +2 -0
- package/src/components/data-table/range-focus/__tests__/utils.test.ts +82 -8
- package/src/components/data-table/range-focus/atoms.ts +2 -2
- package/src/components/data-table/range-focus/utils.ts +50 -12
- package/src/components/data-table/types.ts +7 -0
- package/src/components/data-table/utils.ts +87 -0
- package/src/components/editor/__tests__/data-attributes.test.tsx +8 -8
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +15 -15
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +2 -2
- package/src/components/editor/connections/storage/as-code.ts +2 -2
- package/src/components/editor/file-tree/file-explorer.tsx +16 -2
- package/src/components/editor/file-tree/file-viewer.tsx +17 -3
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +2 -2
- package/src/components/editor/navigation/__tests__/selection.test.ts +7 -6
- package/src/components/editor/navigation/__tests__/state.test.ts +8 -7
- package/src/components/editor/output/MarimoErrorOutput.tsx +7 -7
- package/src/components/editor/output/__tests__/traceback.test.tsx +4 -4
- package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +4 -4
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +8 -1
- package/src/components/storage/storage-file-viewer.tsx +35 -1
- package/src/components/storage/storage-inspector.tsx +9 -4
- package/src/components/storage/storage-snippets.ts +3 -3
- package/src/components/tracing/tracing.tsx +3 -1
- package/src/components/ui/range-slider.tsx +108 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +9 -8
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +31 -31
- package/src/core/ai/context/providers/__tests__/datasource.test.ts +3 -3
- package/src/core/ai/context/providers/__tests__/tables.test.ts +3 -2
- package/src/core/ai/context/providers/__tests__/variable.test.ts +84 -63
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +10 -9
- package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +6 -6
- package/src/core/ai/tools/edit-notebook-tool.ts +3 -3
- package/src/core/cells/__tests__/add-missing-import.test.ts +3 -3
- package/src/core/cells/__tests__/apply-transaction.test.ts +279 -0
- package/src/core/cells/__tests__/cells.test.ts +198 -135
- package/src/core/cells/__tests__/document-changes.test.ts +575 -0
- package/src/core/cells/__tests__/document-roundtrip.test.ts +376 -0
- package/src/core/cells/__tests__/focus.test.ts +5 -4
- package/src/core/cells/__tests__/logs.test.ts +13 -12
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +3 -3
- package/src/core/cells/__tests__/runs.test.ts +22 -21
- package/src/core/cells/__tests__/scrollCellIntoView.test.ts +8 -7
- package/src/core/cells/__tests__/session.test.ts +23 -22
- package/src/core/cells/cells.ts +29 -4
- package/src/core/cells/document-changes.ts +644 -0
- package/src/core/cells/ids.ts +5 -5
- package/src/core/cells/logs.ts +2 -2
- package/src/core/cells/runs.ts +6 -8
- package/src/core/codemirror/__tests__/format.test.ts +34 -36
- package/src/core/codemirror/__tests__/setup.test.ts +2 -2
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +114 -0
- package/src/core/codemirror/cells/__tests__/traceback-decorations.test.ts +33 -32
- package/src/core/codemirror/cells/extensions.ts +66 -23
- package/src/core/codemirror/completion/__tests__/keymap.test.ts +15 -35
- package/src/core/codemirror/completion/keymap.ts +14 -4
- package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +12 -13
- package/src/core/codemirror/language/__tests__/utils.test.ts +3 -3
- package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +7 -8
- package/src/core/codemirror/language/languages/python.ts +4 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +4 -3
- package/src/core/codemirror/lsp/notebook-lsp.ts +28 -2
- package/src/core/codemirror/reactive-references/__tests__/analyzer.test.ts +7 -6
- package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
- package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +52 -0
- package/src/core/codemirror/rtc/loro/sync.ts +1 -0
- package/src/core/datasets/__tests__/data-source.test.ts +5 -6
- package/src/core/datasets/state.ts +1 -1
- package/src/core/errors/__tests__/errors.test.ts +2 -1
- package/src/core/export/__tests__/hooks.test.ts +37 -36
- package/src/core/islands/bridge.ts +1 -0
- package/src/core/islands/main.ts +4 -7
- package/src/core/kernel/__tests__/handlers.test.ts +5 -4
- package/src/core/kernel/handlers.ts +7 -4
- package/src/core/network/DeferredRequestRegistry.ts +2 -2
- package/src/core/network/__tests__/CachingRequestRegistry.test.ts +9 -10
- package/src/core/network/__tests__/DeferredRequestRegistry.test.ts +4 -6
- package/src/core/network/requests-lazy.ts +1 -0
- package/src/core/network/requests-network.ts +9 -0
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.tsx +1 -0
- package/src/core/network/types.ts +5 -0
- package/src/core/static/__tests__/virtual-file-tracker.test.ts +8 -8
- package/src/core/static/virtual-file-tracker.ts +1 -1
- package/src/core/storage/__tests__/state.test.ts +31 -21
- package/src/core/storage/state.ts +1 -1
- package/src/core/variables/__tests__/state.test.ts +6 -6
- package/src/core/variables/types.ts +2 -2
- package/src/core/wasm/__tests__/state.test.ts +8 -8
- package/src/core/wasm/bridge.ts +1 -0
- package/src/core/websocket/useMarimoKernelConnection.tsx +31 -16
- package/src/css/app/fonts.css +6 -6
- package/src/css/md-tooltip.css +4 -39
- package/src/css/md.css +7 -0
- package/src/fonts/Fira_Mono/FiraMono-Bold.woff2 +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Medium.woff2 +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Regular.woff2 +0 -0
- package/src/fonts/Lora/Lora-VariableFont_wght.woff2 +0 -0
- package/src/fonts/PT_Sans/PTSans-Bold.woff2 +0 -0
- package/src/fonts/PT_Sans/PTSans-Regular.woff2 +0 -0
- package/src/plugins/core/RenderHTML.tsx +17 -0
- package/src/plugins/core/__test__/RenderHTML.test.ts +45 -0
- package/src/plugins/core/sanitize-html.ts +25 -18
- package/src/plugins/impl/DataTablePlugin.tsx +23 -2
- package/src/plugins/impl/SliderPlugin.tsx +1 -3
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +120 -0
- package/src/plugins/impl/anywidget/model.ts +1 -2
- package/src/stories/cell.stories.tsx +8 -8
- package/src/stories/layout/vertical/one-column.stories.tsx +9 -8
- package/src/stories/log-viewer.stories.tsx +8 -8
- package/src/stories/variables.stories.tsx +2 -2
- package/src/utils/__tests__/download.test.tsx +21 -20
- package/src/utils/copy.ts +18 -5
- package/src/utils/createReducer.ts +26 -11
- package/src/utils/download.ts +4 -3
- package/src/utils/html-to-image.ts +6 -0
- package/src/utils/json/base64.ts +3 -3
- package/src/utils/traceback.ts +5 -3
- package/src/fonts/Fira_Mono/FiraMono-Bold.ttf +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Medium.ttf +0 -0
- package/src/fonts/Fira_Mono/FiraMono-Regular.ttf +0 -0
- package/src/fonts/Lora/Lora-Italic-VariableFont_wght.ttf +0 -0
- package/src/fonts/Lora/Lora-VariableFont_wght.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Bold.ttf +0 -0
- package/src/fonts/Lora/static/Lora-BoldItalic.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Italic.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Medium.ttf +0 -0
- package/src/fonts/Lora/static/Lora-MediumItalic.ttf +0 -0
- package/src/fonts/Lora/static/Lora-Regular.ttf +0 -0
- package/src/fonts/Lora/static/Lora-SemiBold.ttf +0 -0
- package/src/fonts/Lora/static/Lora-SemiBoldItalic.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-Bold.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-BoldItalic.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-Italic.ttf +0 -0
- package/src/fonts/PT_Sans/PTSans-Regular.ttf +0 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Round-trip tests: perform actions on a primary notebook (producing changes
|
|
5
|
+
* via the middleware), then apply those changes to a replica notebook via
|
|
6
|
+
* applyTransactionChanges. The two should converge to identical document state.
|
|
7
|
+
*
|
|
8
|
+
* This catches drift between what the middleware emits and what
|
|
9
|
+
* apply-transaction consumes.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { python } from "@codemirror/lang-python";
|
|
13
|
+
import { EditorState } from "@codemirror/state";
|
|
14
|
+
import { EditorView } from "@codemirror/view";
|
|
15
|
+
import {
|
|
16
|
+
afterAll,
|
|
17
|
+
afterEach,
|
|
18
|
+
beforeAll,
|
|
19
|
+
beforeEach,
|
|
20
|
+
describe,
|
|
21
|
+
expect,
|
|
22
|
+
it,
|
|
23
|
+
} from "vitest";
|
|
24
|
+
import { cellId } from "@/__tests__/branded";
|
|
25
|
+
import type { CellHandle } from "@/components/editor/notebook-cell";
|
|
26
|
+
import { adaptiveLanguageConfiguration } from "@/core/codemirror/language/extension";
|
|
27
|
+
import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
28
|
+
import { MultiColumn } from "@/utils/id-tree";
|
|
29
|
+
import { exportedForTesting, type NotebookState } from "../cells";
|
|
30
|
+
import {
|
|
31
|
+
applyTransactionChanges,
|
|
32
|
+
exportedForTesting as middlewareExports,
|
|
33
|
+
} from "../document-changes";
|
|
34
|
+
import { CellId } from "../ids";
|
|
35
|
+
|
|
36
|
+
const { initialNotebookState, reducer, createActions } = exportedForTesting;
|
|
37
|
+
const { drainChanges } = middlewareExports;
|
|
38
|
+
|
|
39
|
+
function createEditor(code: string) {
|
|
40
|
+
const state = EditorState.create({
|
|
41
|
+
doc: code,
|
|
42
|
+
extensions: [
|
|
43
|
+
python(),
|
|
44
|
+
adaptiveLanguageConfiguration({
|
|
45
|
+
cellId: cellId("cell1"),
|
|
46
|
+
completionConfig: {
|
|
47
|
+
activate_on_typing: true,
|
|
48
|
+
signature_hint_on_typing: false,
|
|
49
|
+
copilot: false,
|
|
50
|
+
codeium_api_key: null,
|
|
51
|
+
},
|
|
52
|
+
hotkeys: new OverridingHotkeyProvider({}),
|
|
53
|
+
placeholderType: "marimo-import",
|
|
54
|
+
lspConfig: {},
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
return new EditorView({ state, parent: document.body });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// --- Primary notebook: performs actions, middleware produces changes ---
|
|
62
|
+
|
|
63
|
+
let primary: NotebookState;
|
|
64
|
+
|
|
65
|
+
const primaryActions = createActions((action) => {
|
|
66
|
+
primary = reducer(primary, action);
|
|
67
|
+
for (const [cellIdString, handle] of Object.entries(primary.cellHandles)) {
|
|
68
|
+
// @ts-expect-error - Object.entries doesn't know keys are CellId
|
|
69
|
+
const cid: CellId = cellIdString;
|
|
70
|
+
if (!handle.current) {
|
|
71
|
+
const view = createEditor(primary.cellData[cid].code);
|
|
72
|
+
const h: CellHandle = { editorView: view, editorViewOrNull: view };
|
|
73
|
+
primary.cellHandles[cid] = { current: h };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// --- Replica notebook: receives changes via applyTransactionChanges ---
|
|
79
|
+
|
|
80
|
+
let replica: NotebookState;
|
|
81
|
+
|
|
82
|
+
const replicaActions = createActions((action) => {
|
|
83
|
+
replica = reducer(replica, action);
|
|
84
|
+
for (const [cellIdString, handle] of Object.entries(replica.cellHandles)) {
|
|
85
|
+
// @ts-expect-error - Object.entries doesn't know keys are CellId
|
|
86
|
+
const cid: CellId = cellIdString;
|
|
87
|
+
if (!handle.current) {
|
|
88
|
+
const view = createEditor(replica.cellData[cid].code);
|
|
89
|
+
const h: CellHandle = { editorView: view, editorViewOrNull: view };
|
|
90
|
+
replica.cellHandles[cid] = { current: h };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
let i = 0;
|
|
96
|
+
const originalCreate = CellId.create.bind(CellId);
|
|
97
|
+
|
|
98
|
+
beforeAll(() => {
|
|
99
|
+
CellId.create = () => cellId(`${i++}`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
i = 0;
|
|
104
|
+
primary = initialNotebookState();
|
|
105
|
+
primary.cellIds = MultiColumn.from([]);
|
|
106
|
+
drainChanges();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
afterEach(() => {
|
|
110
|
+
middlewareExports.cancelPendingChanges();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
afterAll(() => {
|
|
114
|
+
CellId.create = originalCreate;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/** Set up both notebooks with the same initial cells. */
|
|
118
|
+
function setup(...codes: string[]) {
|
|
119
|
+
for (const code of codes) {
|
|
120
|
+
primaryActions.createNewCell({
|
|
121
|
+
cellId: "__end__",
|
|
122
|
+
before: false,
|
|
123
|
+
code,
|
|
124
|
+
newCellId: CellId.create(),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Apply the setup changes to the replica so both start identical
|
|
129
|
+
const setupChanges = drainChanges();
|
|
130
|
+
replica = initialNotebookState();
|
|
131
|
+
replica.cellIds = MultiColumn.from([]);
|
|
132
|
+
applyTransactionChanges(
|
|
133
|
+
setupChanges,
|
|
134
|
+
replicaActions,
|
|
135
|
+
() => replica.cellIds.inOrderIds,
|
|
136
|
+
);
|
|
137
|
+
// Drain any changes the replica's middleware produced
|
|
138
|
+
drainChanges();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Drain changes from the primary's middleware and apply them to the replica.
|
|
143
|
+
*/
|
|
144
|
+
function sync() {
|
|
145
|
+
const changes = drainChanges();
|
|
146
|
+
applyTransactionChanges(
|
|
147
|
+
changes,
|
|
148
|
+
replicaActions,
|
|
149
|
+
() => replica.cellIds.inOrderIds,
|
|
150
|
+
);
|
|
151
|
+
// Drain any changes the replica's middleware produced (we don't want those)
|
|
152
|
+
drainChanges();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Extract the document-relevant state: cell ordering, code, name, config.
|
|
157
|
+
* This is the "NotebookDocument" equivalent — what the Python side tracks.
|
|
158
|
+
*
|
|
159
|
+
* TODO(column-config): config.column is excluded because the column
|
|
160
|
+
* reducers (addColumnBreakpoint, dropOverNewColumn, moveColumn, etc.)
|
|
161
|
+
* update cellIds (MultiColumn structure) but don't sync config.column
|
|
162
|
+
* on affected cells. The middleware correctly emits set-config changes with
|
|
163
|
+
* the new column index, but the primary's config.column stays stale,
|
|
164
|
+
* causing a mismatch with the replica. Fix: have the column reducers
|
|
165
|
+
* update config.column as part of their state transition, then remove
|
|
166
|
+
* the { column: _, ...config } exclusion here.
|
|
167
|
+
*/
|
|
168
|
+
function documentSnapshot(state: NotebookState) {
|
|
169
|
+
return state.cellIds.inOrderIds.map((id) => {
|
|
170
|
+
const { column: _, ...config } = state.cellData[id].config;
|
|
171
|
+
return {
|
|
172
|
+
id,
|
|
173
|
+
code: state.cellData[id].code,
|
|
174
|
+
name: state.cellData[id].name,
|
|
175
|
+
config,
|
|
176
|
+
};
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
describe("document round-trip", () => {
|
|
181
|
+
it("initial setup converges", () => {
|
|
182
|
+
setup("a", "b", "c");
|
|
183
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("createNewCell at end", () => {
|
|
187
|
+
setup("a", "b");
|
|
188
|
+
primaryActions.createNewCell({
|
|
189
|
+
cellId: "__end__",
|
|
190
|
+
before: false,
|
|
191
|
+
code: "c",
|
|
192
|
+
newCellId: CellId.create(),
|
|
193
|
+
});
|
|
194
|
+
sync();
|
|
195
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("createNewCell before first cell", () => {
|
|
199
|
+
setup("a", "b");
|
|
200
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
201
|
+
primaryActions.createNewCell({
|
|
202
|
+
cellId: a,
|
|
203
|
+
before: true,
|
|
204
|
+
code: "before-a",
|
|
205
|
+
newCellId: CellId.create(),
|
|
206
|
+
});
|
|
207
|
+
sync();
|
|
208
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("createNewCell between cells", () => {
|
|
212
|
+
setup("a", "b", "c");
|
|
213
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
214
|
+
primaryActions.createNewCell({
|
|
215
|
+
cellId: a,
|
|
216
|
+
before: false,
|
|
217
|
+
code: "between",
|
|
218
|
+
newCellId: CellId.create(),
|
|
219
|
+
});
|
|
220
|
+
sync();
|
|
221
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("createNewCell with hideCode config", () => {
|
|
225
|
+
setup("a");
|
|
226
|
+
primaryActions.createNewCell({
|
|
227
|
+
cellId: "__end__",
|
|
228
|
+
before: false,
|
|
229
|
+
code: "hidden",
|
|
230
|
+
newCellId: CellId.create(),
|
|
231
|
+
hideCode: true,
|
|
232
|
+
});
|
|
233
|
+
sync();
|
|
234
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("deleteCell", () => {
|
|
238
|
+
setup("a", "b", "c");
|
|
239
|
+
const [, b] = primary.cellIds.inOrderIds;
|
|
240
|
+
primaryActions.deleteCell({ cellId: b });
|
|
241
|
+
sync();
|
|
242
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("updateCellCode", () => {
|
|
246
|
+
setup("a", "b");
|
|
247
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
248
|
+
primaryActions.updateCellCode({
|
|
249
|
+
cellId: a,
|
|
250
|
+
code: "updated",
|
|
251
|
+
formattingChange: false,
|
|
252
|
+
});
|
|
253
|
+
sync();
|
|
254
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("updateCellName", () => {
|
|
258
|
+
setup("a");
|
|
259
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
260
|
+
primaryActions.updateCellName({ cellId: a, name: "my_var" });
|
|
261
|
+
sync();
|
|
262
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("updateCellConfig", () => {
|
|
266
|
+
setup("a");
|
|
267
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
268
|
+
primaryActions.updateCellConfig({
|
|
269
|
+
cellId: a,
|
|
270
|
+
config: { hide_code: true, disabled: true },
|
|
271
|
+
});
|
|
272
|
+
sync();
|
|
273
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("moveCell down", () => {
|
|
277
|
+
setup("a", "b", "c");
|
|
278
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
279
|
+
primaryActions.moveCell({ cellId: a, before: false });
|
|
280
|
+
sync();
|
|
281
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it("sendToTop", () => {
|
|
285
|
+
setup("a", "b", "c");
|
|
286
|
+
const c = primary.cellIds.inOrderIds[2];
|
|
287
|
+
primaryActions.sendToTop({ cellId: c });
|
|
288
|
+
sync();
|
|
289
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it("sendToBottom", () => {
|
|
293
|
+
setup("a", "b", "c");
|
|
294
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
295
|
+
primaryActions.sendToBottom({ cellId: a });
|
|
296
|
+
sync();
|
|
297
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("dropCellOverCell", () => {
|
|
301
|
+
setup("a", "b", "c");
|
|
302
|
+
const [a, , c] = primary.cellIds.inOrderIds;
|
|
303
|
+
primaryActions.dropCellOverCell({ cellId: c, overCellId: a });
|
|
304
|
+
sync();
|
|
305
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("multiple operations in sequence", () => {
|
|
309
|
+
setup("a", "b", "c");
|
|
310
|
+
|
|
311
|
+
// Add a cell
|
|
312
|
+
const [a] = primary.cellIds.inOrderIds;
|
|
313
|
+
primaryActions.createNewCell({
|
|
314
|
+
cellId: a,
|
|
315
|
+
before: false,
|
|
316
|
+
code: "new",
|
|
317
|
+
newCellId: CellId.create(),
|
|
318
|
+
});
|
|
319
|
+
sync();
|
|
320
|
+
|
|
321
|
+
// Rename it
|
|
322
|
+
const newId = primary.cellIds.inOrderIds[1];
|
|
323
|
+
primaryActions.updateCellName({ cellId: newId, name: "inserted" });
|
|
324
|
+
sync();
|
|
325
|
+
|
|
326
|
+
// Move it to top
|
|
327
|
+
primaryActions.sendToTop({ cellId: newId });
|
|
328
|
+
sync();
|
|
329
|
+
|
|
330
|
+
// Update code on another cell
|
|
331
|
+
const last =
|
|
332
|
+
primary.cellIds.inOrderIds[primary.cellIds.inOrderIds.length - 1];
|
|
333
|
+
primaryActions.updateCellCode({
|
|
334
|
+
cellId: last,
|
|
335
|
+
code: "modified",
|
|
336
|
+
formattingChange: false,
|
|
337
|
+
});
|
|
338
|
+
sync();
|
|
339
|
+
|
|
340
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("create then delete", () => {
|
|
344
|
+
setup("a", "b");
|
|
345
|
+
primaryActions.createNewCell({
|
|
346
|
+
cellId: "__end__",
|
|
347
|
+
before: false,
|
|
348
|
+
code: "temporary",
|
|
349
|
+
newCellId: CellId.create(),
|
|
350
|
+
});
|
|
351
|
+
sync();
|
|
352
|
+
|
|
353
|
+
const newId =
|
|
354
|
+
primary.cellIds.inOrderIds[primary.cellIds.inOrderIds.length - 1];
|
|
355
|
+
primaryActions.deleteCell({ cellId: newId });
|
|
356
|
+
sync();
|
|
357
|
+
|
|
358
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("addColumnBreakpoint", () => {
|
|
362
|
+
setup("a", "b", "c");
|
|
363
|
+
const [, b] = primary.cellIds.inOrderIds;
|
|
364
|
+
primaryActions.addColumnBreakpoint({ cellId: b });
|
|
365
|
+
sync();
|
|
366
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it("dropOverNewColumn", () => {
|
|
370
|
+
setup("a", "b", "c");
|
|
371
|
+
const [, b] = primary.cellIds.inOrderIds;
|
|
372
|
+
primaryActions.dropOverNewColumn({ cellId: b });
|
|
373
|
+
sync();
|
|
374
|
+
expect(documentSnapshot(primary)).toEqual(documentSnapshot(replica));
|
|
375
|
+
});
|
|
376
|
+
});
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
2
3
|
import { beforeEach, describe, expect, it } from "vitest";
|
|
3
|
-
import
|
|
4
|
+
import { cellId } from "@/__tests__/branded";
|
|
4
5
|
import type { CellFocusState } from "../focus";
|
|
5
6
|
import { exportedForTesting } from "../focus";
|
|
6
7
|
|
|
7
8
|
const { initialState, reducer, createActions } = exportedForTesting;
|
|
8
9
|
|
|
9
10
|
const CellIds = {
|
|
10
|
-
a: "a"
|
|
11
|
-
b: "b"
|
|
12
|
-
c: "c"
|
|
11
|
+
a: cellId("a"),
|
|
12
|
+
b: cellId("b"),
|
|
13
|
+
c: cellId("c"),
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
describe("cell focus reducer", () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
4
|
+
import { cellId } from "@/__tests__/branded";
|
|
4
5
|
import type { CellMessage } from "../../kernel/messages";
|
|
5
6
|
import { formatLogTimestamp, getCellLogsForMessage } from "../logs";
|
|
6
7
|
|
|
@@ -18,7 +19,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
18
19
|
|
|
19
20
|
test("handles text/plain MIME type on stdout", () => {
|
|
20
21
|
const cellMessage: CellMessage = {
|
|
21
|
-
cell_id: "cell-1",
|
|
22
|
+
cell_id: cellId("cell-1"),
|
|
22
23
|
console: [
|
|
23
24
|
{
|
|
24
25
|
mimetype: "text/plain",
|
|
@@ -46,7 +47,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
46
47
|
|
|
47
48
|
test("handles text/plain MIME type on stderr", () => {
|
|
48
49
|
const cellMessage: CellMessage = {
|
|
49
|
-
cell_id: "cell-2",
|
|
50
|
+
cell_id: cellId("cell-2"),
|
|
50
51
|
console: [
|
|
51
52
|
{
|
|
52
53
|
mimetype: "text/plain",
|
|
@@ -74,7 +75,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
74
75
|
|
|
75
76
|
test("handles text/html MIME type and strips HTML tags", () => {
|
|
76
77
|
const cellMessage: CellMessage = {
|
|
77
|
-
cell_id: "cell-3",
|
|
78
|
+
cell_id: cellId("cell-3"),
|
|
78
79
|
console: [
|
|
79
80
|
{
|
|
80
81
|
mimetype: "text/html",
|
|
@@ -102,7 +103,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
102
103
|
|
|
103
104
|
test("handles text/html MIME type on stderr", () => {
|
|
104
105
|
const cellMessage: CellMessage = {
|
|
105
|
-
cell_id: "cell-4",
|
|
106
|
+
cell_id: cellId("cell-4"),
|
|
106
107
|
console: [
|
|
107
108
|
{
|
|
108
109
|
mimetype: "text/html",
|
|
@@ -130,7 +131,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
130
131
|
|
|
131
132
|
test("handles application/vnd.marimo+traceback MIME type and strips HTML", () => {
|
|
132
133
|
const cellMessage: CellMessage = {
|
|
133
|
-
cell_id: "cell-5",
|
|
134
|
+
cell_id: cellId("cell-5"),
|
|
134
135
|
console: [
|
|
135
136
|
{
|
|
136
137
|
mimetype: "application/vnd.marimo+traceback",
|
|
@@ -156,7 +157,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
156
157
|
|
|
157
158
|
test("handles multiple console outputs with different MIME types", () => {
|
|
158
159
|
const cellMessage: CellMessage = {
|
|
159
|
-
cell_id: "cell-7",
|
|
160
|
+
cell_id: cellId("cell-7"),
|
|
160
161
|
console: [
|
|
161
162
|
{
|
|
162
163
|
mimetype: "text/plain",
|
|
@@ -196,7 +197,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
196
197
|
vi.spyOn(Date, "now").mockReturnValue(now);
|
|
197
198
|
|
|
198
199
|
const cellMessage: CellMessage = {
|
|
199
|
-
cell_id: "cell-8",
|
|
200
|
+
cell_id: cellId("cell-8"),
|
|
200
201
|
console: [
|
|
201
202
|
{
|
|
202
203
|
mimetype: "text/plain",
|
|
@@ -219,7 +220,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
219
220
|
|
|
220
221
|
test("ignores unsupported MIME types", () => {
|
|
221
222
|
const cellMessage: CellMessage = {
|
|
222
|
-
cell_id: "cell-9",
|
|
223
|
+
cell_id: cellId("cell-9"),
|
|
223
224
|
console: [
|
|
224
225
|
{
|
|
225
226
|
mimetype: "application/json",
|
|
@@ -241,7 +242,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
241
242
|
|
|
242
243
|
test("ignores non-logging channels", () => {
|
|
243
244
|
const cellMessage: CellMessage = {
|
|
244
|
-
cell_id: "cell-10",
|
|
245
|
+
cell_id: cellId("cell-10"),
|
|
245
246
|
console: [
|
|
246
247
|
{
|
|
247
248
|
mimetype: "text/plain",
|
|
@@ -263,7 +264,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
263
264
|
|
|
264
265
|
test("returns empty array when console is null", () => {
|
|
265
266
|
const cellMessage: CellMessage = {
|
|
266
|
-
cell_id: "cell-11",
|
|
267
|
+
cell_id: cellId("cell-11"),
|
|
267
268
|
console: null as unknown as CellMessage["console"],
|
|
268
269
|
output: null,
|
|
269
270
|
status: "idle",
|
|
@@ -278,7 +279,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
278
279
|
|
|
279
280
|
test("handles complex HTML with nested elements in text/html", () => {
|
|
280
281
|
const cellMessage: CellMessage = {
|
|
281
|
-
cell_id: "cell-12",
|
|
282
|
+
cell_id: cellId("cell-12"),
|
|
282
283
|
console: [
|
|
283
284
|
{
|
|
284
285
|
mimetype: "text/html",
|
|
@@ -301,7 +302,7 @@ describe("getCellLogsForMessage", () => {
|
|
|
301
302
|
|
|
302
303
|
test("handles marimo-error channel as stderr level", () => {
|
|
303
304
|
const cellMessage: CellMessage = {
|
|
304
|
-
cell_id: "cell-13",
|
|
305
|
+
cell_id: cellId("cell-13"),
|
|
305
306
|
console: [
|
|
306
307
|
{
|
|
307
308
|
mimetype: "text/plain",
|
|
@@ -4,11 +4,11 @@ import { act, renderHook, waitFor } from "@testing-library/react";
|
|
|
4
4
|
import { createStore, Provider } from "jotai";
|
|
5
5
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
6
|
import { MockNotebook } from "@/__mocks__/notebook";
|
|
7
|
+
import { variableName } from "@/__tests__/branded";
|
|
7
8
|
import { notebookAtom } from "@/core/cells/cells";
|
|
8
9
|
import { CellId } from "@/core/cells/ids";
|
|
9
10
|
import { createCellRuntimeState } from "@/core/cells/types";
|
|
10
11
|
import { variablesAtom } from "@/core/variables/state";
|
|
11
|
-
import type { VariableName } from "@/core/variables/types";
|
|
12
12
|
import type { Milliseconds } from "@/utils/time";
|
|
13
13
|
import {
|
|
14
14
|
usePendingDelete,
|
|
@@ -138,8 +138,8 @@ describe("pending-delete-service", () => {
|
|
|
138
138
|
|
|
139
139
|
store.set(notebookAtom, notebook);
|
|
140
140
|
store.set(variablesAtom, {
|
|
141
|
-
["x"
|
|
142
|
-
name: "x"
|
|
141
|
+
[variableName("x")]: {
|
|
142
|
+
name: variableName("x"),
|
|
143
143
|
declaredBy: [cell1Id],
|
|
144
144
|
usedBy: [cell2Id],
|
|
145
145
|
},
|