@marimo-team/islands 0.25.3-dev28 → 0.25.3-dev281
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-7EaP9VWb.js → ConnectedDataExplorerComponent-CErm132k.js} +116 -113
- package/dist/{ErrorBoundary-CUdSBv06.js → ErrorBoundary-fSq1YI-S.js} +76 -82
- package/dist/_baseSet-BfI5FjBM.js +328 -0
- package/dist/{abnfDiagram-VCTEODGH-z_OSynK8.js → abnfDiagram-VCTEODGH-d-Qn2fwb.js} +2 -2
- package/dist/any-language-editor-6k6F12Z3.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-Cs8J8BZr.js → architectureDiagram-5GKGNRK7-DxragHYv.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-DoQ-AILx.js} +15 -27
- package/dist/assertNever-B-YzWzbL.js +507 -0
- package/dist/assets/__vite-browser-external-BKBpimif.js +1 -0
- package/dist/assets/worker-CgoMtD-_.js +113 -0
- package/dist/{blockDiagram-I7D4REHJ-Cg25UrxT.js → blockDiagram-I7D4REHJ-Dkhe6_PP.js} +1 -1
- package/dist/{button-CvjkwdSk.js → button-DZuNnIZO.js} +56 -48
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-D9FskwsK.js} +2 -2
- package/dist/{chat-ui-BzjAsTFN.js → chat-ui-ByGe2rP1.js} +2100 -1810
- package/dist/{check-BMKBBrdc.js → check-XFzZeH-y.js} +1 -1
- package/dist/{chunk-IMKFNOWR-aoSFasqF.js → chunk-IMKFNOWR-C0x0feFg.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CMaybO3O.js → chunk-SHT3W25Y-D0ZdASqB.js} +1 -1
- package/dist/{chunk-SVP7TREG-BNBrTAw1.js → chunk-SVP7TREG-BHjL96zx.js} +1 -1
- package/dist/{chunk-TICWLB2K-DXNxvUJn.js → chunk-TICWLB2K-CzcrwGfp.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-DXdCCDOg.js → classDiagram-ZZMXUADV-BTqFAZTP.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DyZqrtLP.js → classDiagram-v2-VYDZK3BY-EPXb0Elt.js} +2 -2
- package/dist/{code-visibility-BuxqA3v5.js → code-visibility-BhtW7mrV.js} +43 -50
- package/dist/{common-xtZHxsM2.js → common-BIZJLLSc.js} +10605 -10045
- package/dist/config-C8MFHQ2E.js +637 -0
- package/dist/{copy-icon-BgUO8jsi.js → copy-icon-CtllOdmg.js} +3 -3
- package/dist/createReducer-DRqwmaTc.js +67 -0
- package/dist/{cynefinDiagram-5FMLGOSQ-DWHuKJs8.js → cynefinDiagram-5FMLGOSQ-CBSD344q.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-DYLu-OCU.js → dagre-GXQ25YYZ-JItjh3Ge.js} +3 -3
- package/dist/{dagre-BGSfXNEb.js → dagre-mqiMAsFY.js} +531 -680
- package/dist/{data-grid-overlay-editor-qnbf5J94.js → data-grid-overlay-editor-B8b_mHmv.js} +1 -1
- package/dist/{debounce-C3jyATpc.js → debounce-DrhhziJv.js} +2 -2
- package/dist/{diagram-S7CK7UJ4-PwwcUOHP.js → diagram-S7CK7UJ4-CTD9xTVb.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-CvJbL_IM.js → diagram-UQ7AKVKN-DTfnFai8.js} +1 -1
- package/dist/{diagram-VX7I27RA-B5RkqnzN.js → diagram-VX7I27RA-BcSnucV-.js} +1 -1
- package/dist/{diagram-Z3DM3KII-CyijwTR2.js → diagram-Z3DM3KII-C2ATIE8A.js} +1 -1
- package/dist/{dialog-Cc479ZEH.js → dialog-CaYm5imE.js} +32 -32
- package/dist/{dist-Bvs24R_E.js → dist-11T5wq3_.js} +31 -30
- package/dist/{dist-F8Ep36rk.js → dist-17ugwGZc.js} +59 -58
- package/dist/{dist-BMQf5zIS.js → dist-7cRjf--y.js} +123 -122
- package/dist/{dist-diIx5qJW.js → dist-B8pkfkzc.js} +31 -30
- package/dist/{dist-CN_BeG2I.js → dist-B9UoM_wq.js} +58 -57
- package/dist/{dist-CLdpGQVr.js → dist-BGuMTs1i.js} +118 -117
- package/dist/{dist-BK4Oj99o.js → dist-BHW11fni.js} +18 -17
- package/dist/dist-BLY4m1Wi.js +850 -0
- package/dist/{dist-PXYI_yuP.js → dist-BNlcVoxs.js} +24 -23
- package/dist/{dist-D6d86eNJ.js → dist-BQYe5Bju.js} +47 -46
- package/dist/{dist-CkTTIdBe.js → dist-BX1mn8na.js} +337 -337
- package/dist/{dist-DiDibOYr.js → dist-Bh0PTKUW.js} +151 -150
- package/dist/{dist-CSJ0MDRw.js → dist-CKaXK92p.js} +56 -55
- package/dist/{dist-C8fAKSH1.js → dist-CLhwfhpJ.js} +53 -52
- package/dist/{dist-DoE4XJBC.js → dist-CYeqK4Xw.js} +9801 -10649
- package/dist/{dist-BLfKmR-l.js → dist-Cxlp3WH6.js} +16 -15
- package/dist/{dist-D-wwqlzJ.js → dist-DSJPGj42.js} +158 -157
- package/dist/{dist-BorMD-uX.js → dist-DU9ICsWZ.js} +75 -74
- package/dist/{dist-DBtSl3sf.js → dist-DWv0ALBi.js} +47 -46
- package/dist/{dist-OR1OnN-y.js → dist-DiBpWEsB.js} +31 -30
- package/dist/{dist-CrSP_H1M.js → dist-DiLFTlnl.js} +242 -242
- package/dist/{dist-CN8zI-XC.js → dist-X9CsotJl.js} +239 -239
- package/dist/{dist-CSbh1PNP.js → dist-cVDV5IuI.js} +23 -22
- package/dist/{dist-BuaRHxOc.js → dist-fRfaOoq8.js} +44 -43
- package/dist/{dist-Cg7gzw-a.js → dist-gTTI-9Px.js} +78 -77
- package/dist/{dropdown-menu-BuE2b8Yb.js → dropdown-menu-Bb0bFOIk.js} +2904 -1493
- package/dist/{ebnfDiagram-PWID7BFC-DG90ELFp.js → ebnfDiagram-PWID7BFC-B7B9JPxc.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-FMo_SpyX.js → erDiagram-RLTQ6QDP-v6hDno1O.js} +1 -1
- package/dist/{error-banner-CFPHFw4h.js → error-banner-CHCdHVFB.js} +87 -87
- package/dist/error-entries-9U4BFHjf.js +2586 -0
- package/dist/{esm-AMFssoNp.js → esm-BDqqT86j.js} +329 -329
- package/dist/{esm-D2gTsXEV.js → esm-BgKjMvWw.js} +530 -529
- package/dist/feedback-button-CY92zeC6.js +374 -0
- package/dist/{flowDiagram-HODETNUW-DQ4JuadE.js → flowDiagram-HODETNUW-aIFlAtOy.js} +2 -2
- package/dist/{formats-Bv_5N2Ff.js → formats-PzBQMUnr.js} +6 -6
- package/dist/{glide-data-editor-DQ_cEi05.js → glide-data-editor-BdgfR8uu.js} +6630 -2846
- package/dist/{glide-utils-pisbYVjH.js → glide-utils-BSQr4d90.js} +107 -76
- package/dist/{globals-D7vITxEX.js → globals-CjnqsurJ.js} +3 -3
- package/dist/{graphlib-BmY_CCV9.js → graphlib-nlwT3Wvy.js} +177 -220
- package/dist/hasIn-C20JsQ2m.js +28 -0
- package/dist/{error-entries-j85LP88u.js → html-to-image-BYh_MorV.js} +26294 -26451
- package/dist/{infoDiagram-27XIBGKW-CRSDtRrd.js → infoDiagram-27XIBGKW-D6YHLeCP.js} +1 -1
- package/dist/isArrayLikeObject-B-HXCsex.js +626 -0
- package/dist/isEmpty-CdX7MTE1.js +14 -0
- package/dist/{ishikawaDiagram-5VMMS53U-DxLEUldB.js → ishikawaDiagram-5VMMS53U-D1ZvprOX.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-BLhvu8o4.js → kanban-definition-UXKFOSKX-Bna_3BMr.js} +1 -1
- package/dist/{loader-BPdQhmlq.js → loader-DZvtzQoD.js} +3 -3
- package/dist/main.js +10276 -8171
- package/dist/{mermaid-Bjm6C1qN.js → mermaid-A36unKXJ.js} +62 -62
- package/dist/{mindmap-definition-YA3MSWOX-g2LwxnYQ.js → mindmap-definition-YA3MSWOX-CyVCs__G.js} +1 -1
- package/dist/{now-jM8-M7IR.js → now-BKZCLE5P.js} +12 -13
- package/dist/{number-overlay-editor-DJHB-CGK.js → number-overlay-editor-BQBhWG2O.js} +1 -1
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-NnpX_HnX.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-CpMAt3Qs.js → pegDiagram-XKGWAZYB-9hNBD2wZ.js} +2 -2
- package/dist/pencil-DYUlSKxZ.js +10 -0
- package/dist/pick-tCszMUbm.js +35 -0
- package/dist/{pieDiagram-E7YTZNPT-CVTthoZo.js → pieDiagram-E7YTZNPT-8tr-NYo7.js} +1 -1
- package/dist/{process-output-DGzGHArA.js → process-output-bS3MGJ3f.js} +4 -4
- package/dist/{railroadDiagram-O6MQD6OU-ao8cqqFB.js → railroadDiagram-O6MQD6OU-Dzg1dyyp.js} +2 -2
- package/dist/{react-vega-BDpbvBCb.js → react-vega-C4T9Rb_w.js} +1 -1
- package/dist/{requirementDiagram-BXWQKSXE--J8vyfm-.js → requirementDiagram-BXWQKSXE-B7sNqwGG.js} +1 -1
- package/dist/{reveal-component-BxYUpAPg.js → reveal-component-BqyL-_xE.js} +1151 -1128
- package/dist/select-DsoP1qe8.js +1200 -0
- package/dist/{sequenceDiagram-WJ2MYXX4-CiQbn62x.js → sequenceDiagram-WJ2MYXX4-oP9shbaG.js} +1 -1
- package/dist/{spec-D_u2ipzF.js → spec-BZMDxAu7.js} +41 -11
- package/dist/{stateDiagram-D77RDMKH-DVEeGs5b.js → stateDiagram-D77RDMKH-CjCv4ogv.js} +4 -4
- package/dist/{stateDiagram-v2-MP3YSRHH-1OQRe6iA.js → stateDiagram-v2-MP3YSRHH-BW9X162a.js} +2 -2
- package/dist/static-state-D-Q-Ajaa.js +72 -0
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-CqkGoGqn.js → swimlanes-42K2YHIH-Ckfs9Wrn.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-CszjkrKo.js → swimlanesDiagram-VR7AAH4N-DpTNG9YP.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-DQbK_ZWm.js} +7 -7
- package/dist/{timeline-definition-24CTP7MA-DLRUNVAx.js → timeline-definition-24CTP7MA-BXaBlV6U.js} +1 -1
- package/dist/toast-BhjIy3Tq.js +491 -0
- package/dist/{tooltip-CtBZ5q8p.js → tooltip-DMMiOn2f.js} +321 -297
- package/dist/use-restore-focus-CosX7-gx.js +7568 -0
- package/dist/{useAsyncData-BneN349z.js → useAsyncData-Ci4AvJff.js} +18 -17
- package/dist/{useDateFormatter-DkajGwwd.js → useDateFormatter-CPlava52.js} +2 -2
- package/dist/{useDeepCompareMemoize-Z_7R8wTt.js → useDeepCompareMemoize-NaaWoFlX.js} +1 -1
- package/dist/useEvent-BRH-aCqi.js +19 -0
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BXhDDoCG.js} +1 -1
- package/dist/useLifecycle-SniivYmC.js +112 -0
- package/dist/{vega-component-DPd52jvr.js → vega-component-DIxaDQNs.js} +161 -160
- package/dist/{vega-embed-container-BgKnsDbB.js → vega-embed-container--LYZqguO.js} +2 -2
- package/dist/{vega-loader.browser-CpbZAXub.js → vega-loader.browser-B9Pz0EGP.js} +1 -1
- package/dist/{vennDiagram-4TSXK5OY-CypgoBVZ.js → vennDiagram-4TSXK5OY-C-hQ4iNJ.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-B6xjiQ_g.js → wardleyDiagram-VM6X3IG4-C2MQ_Umv.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-e0PefDVs.js → xychartDiagram-S5SC5T6Z-BtTTBn7Q.js} +1 -1
- package/package.json +16 -20
- package/src/__mocks__/document.tsx +63 -0
- package/src/__mocks__/executions.ts +197 -0
- package/src/__mocks__/notebook.ts +3 -7
- package/src/__mocks__/partial.ts +26 -0
- package/src/__mocks__/requests.ts +1 -4
- package/src/__tests__/CellStatus.test.tsx +25 -26
- package/src/__tests__/fixtures/pair-config.ts +1 -1
- package/src/__tests__/main.test.tsx +24 -1
- package/src/__tests__/module-entry.test.ts +44 -0
- package/src/__tests__/mount.test.ts +1 -21
- package/src/__tests__/{host-imports.test.ts → replica-imports.test.ts} +12 -11
- package/src/components/ai/__tests__/ai-model-dropdown.test.tsx +63 -0
- package/src/components/ai/ai-model-dropdown.tsx +11 -6
- package/src/components/app-config/__tests__/state.test.tsx +50 -0
- package/src/components/app-config/ai-config.tsx +156 -150
- package/src/components/app-config/app-config-button.tsx +6 -5
- package/src/components/app-config/app-config-form.tsx +5 -9
- package/src/components/app-config/common.tsx +7 -9
- package/src/components/app-config/data-form.tsx +112 -113
- package/src/components/app-config/mcp-config.tsx +30 -53
- package/src/components/app-config/user-config-form.css +91 -0
- package/src/components/app-config/user-config-form.tsx +266 -349
- package/src/components/chat/__tests__/capabilities-popover.test.tsx +0 -1
- package/src/components/chat/__tests__/chat-display.test.tsx +196 -0
- package/src/components/chat/__tests__/chat-markdown.test.tsx +43 -0
- package/src/components/chat/__tests__/chat-utils.test.ts +93 -1
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/__tests__/notebook-access-selector.test.tsx +64 -0
- package/src/components/chat/__tests__/turn-timing.test.ts +84 -0
- package/src/components/chat/__tests__/use-assistant-turn.test.ts +95 -0
- package/src/components/chat/__tests__/user-message.test.tsx +27 -0
- package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +1 -1
- package/src/components/chat/acp/__tests__/agent-panel.test.tsx +22 -17
- package/src/components/chat/acp/__tests__/session-tabs.test.tsx +43 -0
- package/src/components/chat/acp/__tests__/state.test.ts +2 -2
- package/src/components/chat/acp/agent-panel.tsx +6 -3
- package/src/components/chat/acp/blocks.tsx +13 -12
- package/src/components/chat/acp/common.tsx +4 -5
- package/src/components/chat/acp/model-selector.tsx +1 -1
- package/src/components/chat/acp/session-tabs.tsx +8 -3
- package/src/components/chat/agent-activity.tsx +132 -0
- package/src/components/chat/capabilities-popover.tsx +2 -11
- package/src/components/chat/chat-components.tsx +9 -4
- package/src/components/chat/chat-display.tsx +131 -57
- package/src/components/chat/chat-history-popover.tsx +6 -1
- package/src/components/chat/chat-markdown.tsx +15 -0
- package/src/components/chat/chat-panel.tsx +216 -294
- package/src/components/chat/chat-typography.css +108 -0
- package/src/components/chat/chat-utils.ts +24 -35
- package/src/components/chat/notebook-access-selector.stories.tsx +22 -0
- package/src/components/chat/notebook-access-selector.tsx +100 -0
- package/src/components/chat/tool-call/__tests__/tool-labels.test.ts +76 -0
- package/src/components/chat/tool-call/__tests__/tool-step.test.tsx +57 -0
- package/src/components/chat/tool-call/shared.ts +6 -0
- package/src/components/chat/tool-call/tool-call-view.tsx +22 -76
- package/src/components/chat/tool-call/tool-labels.ts +172 -0
- package/src/components/chat/tool-call/tool-result.tsx +1 -1
- package/src/components/chat/tool-call/tool-step.tsx +145 -0
- package/src/components/chat/turn-timing.ts +73 -0
- package/src/components/chat/use-assistant-turn.ts +65 -0
- package/src/components/chat/use-chat-generation.ts +20 -0
- package/src/components/chat/user-message.tsx +145 -0
- package/src/components/data-table/TableBottomBar.tsx +4 -1
- package/src/components/data-table/TableTopBar.tsx +2 -0
- package/src/components/data-table/__tests__/chart-spec-model.test.ts +195 -195
- package/src/components/data-table/__tests__/column-explorer.test.tsx +27 -1
- package/src/components/data-table/__tests__/columns.test.tsx +105 -0
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/data-table.test.tsx +1 -0
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +105 -0
- package/src/components/data-table/__tests__/keyboard-selection.test.tsx +188 -0
- package/src/components/data-table/__tests__/pagination.test.tsx +28 -1
- package/src/components/data-table/__tests__/search-highlight.test.tsx +109 -0
- package/src/components/data-table/__tests__/url-detector.test.tsx +74 -0
- package/src/components/data-table/cell-selection/feature.ts +2 -2
- package/src/components/data-table/cell-selection/types.ts +2 -2
- package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +74 -1
- package/src/components/data-table/charts/__tests__/spec-snapshot.test.ts +20 -20
- package/src/components/data-table/charts/__tests__/spec.test.ts +78 -52
- package/src/components/data-table/charts/__tests__/storage.test.ts +4 -1
- package/src/components/data-table/charts/chart-spec/spec.ts +68 -43
- package/src/components/data-table/charts/charts.tsx +40 -30
- package/src/components/data-table/charts/storage.ts +16 -5
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
- package/src/components/data-table/column-formatting/types.ts +1 -2
- package/src/components/data-table/column-summary/chart-spec-model.tsx +23 -16
- package/src/components/data-table/column-summary/column-summary.tsx +2 -4
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +45 -46
- package/src/components/data-table/context-menu.tsx +36 -20
- package/src/components/data-table/data-table.tsx +17 -1
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +76 -48
- package/src/components/data-table/pagination.tsx +44 -33
- package/src/components/data-table/range-focus/atoms.ts +4 -6
- package/src/components/data-table/range-focus/use-cell-range-selection.ts +65 -38
- package/src/components/data-table/range-focus/use-scroll-into-view.ts +21 -2
- package/src/components/data-table/renderers.tsx +16 -7
- package/src/components/data-table/row-viewer-panel/__tests__/row-viewer.test.tsx +24 -1
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +4 -4
- package/src/components/data-table/search-highlight.tsx +79 -0
- package/src/components/data-table/types.ts +1 -0
- package/src/components/data-table/url-detector.tsx +49 -9
- package/src/components/datasources/__tests__/column-actions.test.tsx +97 -0
- package/src/components/datasources/__tests__/column-preview.test.tsx +96 -5
- package/src/components/datasources/__tests__/datasources-interactions.test.tsx +181 -0
- package/src/components/datasources/__tests__/table-list.test.tsx +118 -0
- package/src/components/datasources/__tests__/utils.test.ts +57 -6
- package/src/components/datasources/column-preview.tsx +17 -12
- package/src/components/datasources/datasources.tsx +56 -43
- package/src/components/datasources/utils.ts +32 -23
- package/src/components/debugger/debugger-code.tsx +5 -0
- package/src/components/dependency-graph/custom-node.tsx +1 -1
- package/src/components/dependency-graph/dependency-graph-tree.tsx +8 -8
- package/src/components/dependency-graph/elements.ts +24 -21
- package/src/components/dependency-graph/minimap-content.tsx +15 -20
- package/src/components/dependency-graph/panels.tsx +4 -2
- package/src/components/dependency-graph/utils/__tests__/layout.test.ts +18 -11
- package/src/components/editor/SortableCell.tsx +1 -3
- package/src/components/editor/__tests__/Output.test.tsx +50 -32
- package/src/{core/attachments/__tests__/toast.test.tsx → components/editor/__tests__/agent-toast.test.tsx} +1 -1
- package/src/components/editor/__tests__/data-attributes.test.tsx +28 -23
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/__tests__/output-persistence.test.tsx +28 -9
- package/src/components/editor/__tests__/package-alert.test.tsx +22 -1
- package/src/components/editor/actions/__tests__/name-cell-input.test.tsx +69 -0
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +21 -40
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +41 -14
- package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +6 -24
- package/src/components/editor/actions/__tests__/useCellActionButton.test.tsx +131 -0
- package/src/components/editor/actions/name-cell-input.tsx +4 -3
- package/src/components/editor/actions/pair-with-agent-commands.ts +10 -7
- package/src/components/editor/actions/pair-with-agent-modal.tsx +29 -30
- package/src/components/editor/actions/pair-with-agent.tsx +16 -39
- package/src/components/editor/actions/useCellActionButton.tsx +47 -35
- package/src/components/editor/actions/useConfigActions.tsx +3 -4
- package/src/components/editor/actions/useNotebookActions.tsx +25 -29
- package/src/components/editor/actions/useRestartKernel.tsx +1 -1
- package/src/components/editor/actions/useSetCodeVisibility.ts +11 -12
- package/src/components/editor/actions/useSetOutputsExpanded.ts +14 -19
- package/src/components/editor/ai/ai-completion-editor.tsx +2 -1
- package/src/components/editor/ai/completion-handlers.tsx +0 -37
- package/src/components/editor/ai/prompt-input.tsx +189 -0
- package/src/components/editor/alerts/__tests__/connection-notice.test.tsx +1 -1
- package/src/components/editor/alerts/connection-notice.tsx +1 -1
- package/src/components/editor/alerts/startup-logs-alert.tsx +1 -0
- package/src/components/editor/alerts/stdin-blocking-alert.tsx +4 -2
- package/src/components/editor/app-container.tsx +1 -1
- package/src/components/editor/boundary/__tests__/ErrorBoundary.test.tsx +1 -1
- package/src/components/editor/cell/CellStatus.tsx +28 -23
- package/src/components/editor/cell/CreateCellButton.tsx +15 -21
- package/src/components/editor/cell/DeleteButton.tsx +3 -2
- package/src/components/editor/cell/RunButton.tsx +18 -8
- package/src/components/editor/cell/StagedAICell.tsx +52 -137
- package/src/components/editor/cell/StopButton.tsx +2 -2
- package/src/components/editor/cell/__tests__/useRunCells.test.tsx +169 -0
- package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/cell-actions.tsx +12 -1
- package/src/components/editor/cell/cell-context-menu.tsx +4 -1
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +65 -169
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/collapse.tsx +16 -6
- package/src/components/editor/cell/useAddCell.ts +4 -8
- package/src/components/editor/cell/useDeleteCell.tsx +10 -36
- package/src/components/editor/cell/useRunCells.ts +49 -8
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/cell/useSplitCell.tsx +10 -6
- package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +1 -1
- package/src/components/editor/chrome/components/feedback-button.tsx +2 -8
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +31 -2
- package/src/components/editor/chrome/panels/__tests__/runtime-details.test.tsx +1 -1
- package/src/components/editor/chrome/panels/__tests__/sandbox-recovery.test.tsx +1 -1
- package/src/components/editor/chrome/panels/cache-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/dependency-graph-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/logs-panel.tsx +2 -1
- package/src/components/editor/chrome/panels/outline/__tests__/outline-keyboard.test.tsx +84 -0
- package/src/components/editor/chrome/panels/outline/floating-outline.tsx +39 -14
- package/src/components/editor/chrome/panels/outline/useActiveOutline.tsx +3 -2
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/panels/panel-header.tsx +13 -0
- package/src/components/editor/chrome/panels/secrets-panel.tsx +1 -0
- package/src/components/editor/chrome/panels/session-panel.tsx +1 -1
- package/src/components/editor/chrome/panels/snippets-panel.tsx +10 -12
- package/src/components/editor/chrome/panels/variable-panel.tsx +1 -5
- package/src/components/editor/chrome/types.ts +1 -1
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +7 -9
- package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +92 -0
- package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +265 -37
- package/src/components/editor/chrome/wrapper/__tests__/useAskAiAboutCell.test.tsx +58 -0
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +91 -74
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +4 -4
- package/src/components/editor/chrome/wrapper/footer-items/backend-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/document-sync-status.tsx +54 -0
- package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +1 -1
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +1 -7
- package/src/components/editor/chrome/wrapper/footer.tsx +2 -2
- package/src/components/editor/chrome/wrapper/minimap-state.ts +1 -1
- package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +143 -72
- package/src/components/editor/chrome/wrapper/pending-ai-changes-badge.tsx +25 -0
- package/src/components/editor/chrome/wrapper/sidebar.tsx +8 -4
- package/src/components/editor/chrome/wrapper/storage.ts +1 -1
- package/src/components/editor/chrome/wrapper/useAskAiAboutCell.ts +36 -0
- package/src/components/editor/columns/__tests__/cell-column.test.tsx +1 -1
- package/src/components/editor/columns/cell-column.tsx +1 -1
- package/src/components/editor/columns/sortable-column.tsx +2 -2
- package/src/components/editor/connections/components.tsx +5 -10
- package/src/components/editor/controls/Controls.tsx +4 -4
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/__tests__/keyboard-shortcuts.test.tsx +61 -0
- package/src/components/editor/controls/__tests__/notebook-menu-dropdown.test.tsx +10 -4
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/controls/keyboard-shortcuts.tsx +14 -5
- package/src/components/editor/documentation.css +34 -11
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +26 -22
- package/src/components/editor/errors/auto-fix.tsx +4 -8
- package/src/components/editor/errors/send-error-report-button.tsx +3 -6
- package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
- package/src/components/editor/file-tree/__tests__/file-explorer-navigation.test.tsx +47 -0
- package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
- package/src/components/editor/file-tree/file-explorer.tsx +48 -15
- package/src/components/editor/file-tree/file-name-input.tsx +1 -0
- package/src/components/editor/file-tree/file-viewer.tsx +4 -2
- package/src/components/editor/file-tree/use-file-search.ts +35 -21
- package/src/components/editor/header/__tests__/filename-form.test.tsx +1 -4
- package/src/components/editor/header/__tests__/status.test.tsx +5 -3
- package/src/components/editor/header/app-header.tsx +1 -1
- package/src/components/editor/header/filename-form.tsx +1 -1
- package/src/components/editor/header/status.tsx +44 -22
- package/src/components/editor/links/__tests__/cell-link.test.tsx +148 -0
- package/src/components/editor/links/cell-link-list.tsx +1 -1
- package/src/components/editor/links/cell-link.tsx +21 -11
- package/src/components/editor/navigation/__tests__/clipboard.test.ts +60 -38
- package/src/components/editor/navigation/__tests__/navigation.test.ts +108 -85
- package/src/components/editor/navigation/__tests__/selection.test.ts +43 -21
- package/src/components/editor/navigation/clipboard.ts +17 -14
- package/src/components/editor/navigation/focus-utils.ts +23 -4
- package/src/components/editor/navigation/multi-cell-action-toolbar.tsx +17 -33
- package/src/components/editor/navigation/navigation.ts +74 -72
- package/src/components/editor/navigation/selection.ts +30 -29
- package/src/components/editor/notebook-banner.tsx +1 -0
- package/src/components/editor/notebook-cell.tsx +126 -152
- package/src/components/editor/output/MarimoErrorOutput.tsx +2 -1
- package/src/components/editor/output/MarimoTracebackOutput.tsx +19 -17
- package/src/components/editor/output/Outputs.css +8 -0
- package/src/components/editor/output/VideoOutput.tsx +6 -0
- package/src/components/editor/output/__tests__/VideoOutput.test.tsx +131 -0
- package/src/components/editor/output/__tests__/traceback.test.tsx +27 -1
- package/src/components/editor/output/ansi-reduce.ts +33 -20
- package/src/components/editor/output/console/ConsoleOutput.tsx +19 -25
- package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +125 -69
- package/src/components/editor/package-alert.tsx +14 -7
- package/src/components/editor/renderers/__tests__/add-cell-buttons.test.tsx +18 -0
- package/src/components/editor/renderers/cell-array.tsx +32 -100
- package/src/components/editor/renderers/grid-layout/__tests__/grid-layout.test.tsx +75 -0
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +28 -22
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -4
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +1 -1
- package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +4 -4
- package/src/components/editor/renderers/vertical-layout/sidebar/toggle.tsx +7 -1
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +1 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +19 -37
- package/src/components/editor/stdin-blocking-alert.tsx +4 -2
- package/src/components/home/components.tsx +17 -31
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/icons/spinner.tsx +1 -1
- package/src/components/markdown/markdown-renderer.tsx +39 -28
- package/src/components/mcp/mcp-status-indicator.tsx +18 -14
- package/src/components/pages/__tests__/home-page.test.tsx +116 -0
- package/src/components/pages/edit-page.tsx +19 -17
- package/src/components/pages/home-page.tsx +164 -91
- package/src/components/scratchpad/scratchpad.test.tsx +89 -0
- package/src/components/scratchpad/scratchpad.tsx +47 -33
- package/src/components/scratchpad/state.ts +7 -0
- package/src/components/slides/__tests__/minimap-actions.test.tsx +22 -29
- package/src/components/slides/__tests__/minimap.test.ts +38 -19
- package/src/components/slides/__tests__/slide-scroll-hint.test.tsx +37 -0
- package/src/components/slides/minimap.tsx +22 -14
- package/src/components/slides/reveal-component.tsx +73 -36
- package/src/components/slides/reveal-slides.css +5 -4
- package/src/components/slides/slide-cell-view.tsx +37 -34
- package/src/components/slides/slide-form.tsx +1 -0
- package/src/components/slides/slide-scroll-hint.tsx +3 -1
- package/src/components/slides/slide.tsx +3 -4
- package/src/components/sort/SortableCellsProvider.tsx +8 -25
- package/src/components/tracing/tracing-keyboard.test.tsx +85 -0
- package/src/components/tracing/tracing.tsx +46 -42
- package/src/components/ui/__tests__/draggable-popover.test.tsx +24 -0
- package/src/components/ui/__tests__/fullscreen.test.tsx +44 -2
- package/src/components/ui/__tests__/range-slider.test.tsx +165 -0
- package/src/components/ui/alert-dialog.tsx +1 -1
- package/src/components/ui/draggable-popover.tsx +24 -2
- package/src/components/ui/fullscreen.tsx +25 -1
- package/src/components/ui/range-slider.tsx +22 -9
- package/src/components/ui/toast.tsx +1 -1
- package/src/core/MarimoApp.tsx +3 -1
- package/src/core/ai/__tests__/staged-cells.test.ts +89 -608
- package/src/core/ai/context/context.ts +2 -0
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +8 -12
- package/src/core/ai/context/providers/__tests__/cell.test.ts +49 -0
- package/src/core/ai/context/providers/cell-output.ts +2 -2
- package/src/core/ai/context/providers/cell.ts +54 -0
- package/src/core/ai/context/providers/datasource.ts +6 -2
- package/src/core/ai/staged-cells.ts +56 -323
- package/src/core/ai/state.ts +3 -0
- package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +40 -88
- package/src/core/ai/tools/__tests__/registry.test.ts +17 -0
- package/src/core/ai/tools/base.ts +18 -15
- package/src/core/ai/tools/edit-notebook-tool.ts +86 -80
- package/src/core/ai/tools/registry.ts +13 -10
- package/src/core/ai/tools/sample-tool.ts +6 -7
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +4 -527
- package/src/core/cells/__tests__/actions.test.ts +623 -35
- package/src/core/cells/__tests__/add-missing-import.test.ts +7 -8
- package/src/core/cells/__tests__/arrange.test.ts +432 -0
- package/src/core/cells/__tests__/cell.test.ts +148 -150
- package/src/core/cells/__tests__/cells.test.ts +225 -2723
- package/src/core/cells/__tests__/collapseConsoleOutputs.test.ts +54 -0
- package/src/core/cells/__tests__/document.test.ts +158 -0
- package/src/core/cells/__tests__/editor.test.ts +57 -0
- package/src/core/cells/__tests__/flush-document.test.ts +40 -0
- package/src/core/cells/__tests__/harness.ts +106 -0
- package/src/core/cells/__tests__/layout.test.ts +177 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +122 -0
- package/src/core/cells/__tests__/outputs.test.tsx +71 -53
- package/src/core/cells/__tests__/pending-delete-service.test.tsx +11 -20
- package/src/core/cells/__tests__/session.test.ts +445 -211
- package/src/core/cells/__tests__/utils.test.ts +135 -337
- package/src/core/cells/actions.ts +677 -6
- package/src/core/cells/add-missing-import.ts +47 -43
- package/src/core/cells/arrange.ts +529 -0
- package/src/core/cells/cell.ts +78 -146
- package/src/core/cells/cells.ts +116 -1457
- package/src/core/cells/columns.ts +38 -0
- package/src/core/cells/document.ts +167 -0
- package/src/core/cells/edit.ts +30 -0
- package/src/core/cells/editor.ts +82 -0
- package/src/core/cells/focus.ts +27 -6
- package/src/core/cells/layout.ts +221 -0
- package/src/core/cells/outputs.ts +12 -28
- package/src/core/cells/pending-delete-service.ts +8 -14
- package/src/core/cells/scrollCellIntoView.ts +6 -0
- package/src/core/cells/session.ts +76 -53
- package/src/core/cells/types.ts +21 -51
- package/src/core/cells/utils.ts +85 -87
- package/src/core/codemirror/__tests__/format.test.ts +0 -34
- package/src/core/codemirror/__tests__/setup.test.ts +61 -29
- package/src/core/codemirror/ai/__tests__/resources.test.ts +117 -0
- package/src/core/codemirror/ai/resources.ts +1 -1
- package/src/core/codemirror/cells/__tests__/extensions.test.ts +2 -6
- package/src/core/codemirror/cells/extensions.ts +18 -30
- package/src/core/codemirror/cells/state.ts +2 -6
- package/src/core/codemirror/cm.ts +5 -1
- package/src/core/codemirror/config/types.ts +1 -1
- package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +6 -7
- package/src/core/codemirror/copilot/getCodes.ts +2 -2
- package/src/core/codemirror/format.ts +6 -23
- package/src/core/codemirror/keymaps/vim.ts +4 -8
- package/src/core/codemirror/language/__tests__/sql.test.ts +22 -20
- package/src/core/codemirror/language/__tests__/utils.test.ts +187 -15
- package/src/core/codemirror/language/cell-code.ts +114 -0
- package/src/core/codemirror/language/extension.ts +52 -35
- package/src/core/codemirror/language/languages/markdown.ts +8 -6
- package/src/core/codemirror/language/languages/python.ts +19 -13
- package/src/core/codemirror/language/languages/sql/sql.ts +16 -24
- package/src/core/codemirror/language/panel/panel.tsx +1 -0
- package/src/core/codemirror/language/types.ts +7 -7
- package/src/core/codemirror/language/utils.ts +9 -39
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -2
- package/src/core/codemirror/lsp/notebook-lsp.ts +6 -5
- package/src/core/codemirror/lsp/utils.ts +1 -1
- package/src/core/codemirror/misc/__tests__/paste.test.ts +3 -1
- package/src/core/codemirror/misc/paste.ts +1 -1
- package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +75 -0
- package/src/core/codemirror/placeholder/extensions.ts +80 -19
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/readonly/extension.ts +2 -2
- package/src/core/codemirror/utils.ts +1 -0
- package/src/core/config/__tests__/app-config.test.ts +64 -0
- package/src/core/config/__tests__/config-schema.test.ts +56 -6
- package/src/core/config/config-schema.ts +17 -4
- package/src/core/config/config.ts +65 -20
- package/src/core/config/feature-flag.tsx +0 -2
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +5 -5
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/documentation/doc-lookup.ts +1 -1
- package/src/core/edit-app.tsx +22 -28
- package/src/core/errors/__tests__/error-entries.test.ts +40 -1
- package/src/core/errors/error-entries.ts +6 -4
- package/src/core/executions/__tests__/atoms.test.ts +99 -0
- package/src/core/executions/__tests__/freshness.test.ts +482 -0
- package/src/core/executions/__tests__/hooks.test.tsx +106 -0
- package/src/core/executions/__tests__/select.test.ts +268 -0
- package/src/core/executions/atoms.ts +228 -0
- package/src/core/executions/freshness.ts +171 -0
- package/src/core/executions/hooks.ts +47 -0
- package/src/core/executions/index.ts +33 -0
- package/src/core/executions/select.ts +175 -0
- package/src/core/export/__tests__/hooks.test.ts +0 -5
- package/src/core/export/hooks.ts +1 -1
- package/src/core/export/layout.ts +2 -2
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/hotkeys/hotkeys.ts +2 -1
- package/src/core/islands/__tests__/bootstrap.test.ts +69 -17
- package/src/core/islands/__tests__/bridge.test.ts +85 -27
- package/src/core/islands/bootstrap.ts +49 -54
- package/src/core/islands/bridge.ts +84 -25
- package/src/core/islands/components/__tests__/IslandControls.test.tsx +7 -5
- package/src/core/islands/components/__tests__/web-components.test.tsx +63 -0
- package/src/core/islands/components/output-wrapper.tsx +5 -5
- package/src/core/islands/components/web-components.tsx +12 -1
- package/src/core/islands/worker/__tests__/controller.test.ts +62 -4
- package/src/core/islands/worker/controller.ts +4 -10
- package/src/core/islands/worker/worker.tsx +33 -33
- package/src/core/kernel/__tests__/RuntimeState.test.ts +1 -1
- package/src/core/kernel/__tests__/handlers.test.ts +7 -153
- package/src/core/kernel/handlers.ts +13 -79
- package/src/core/kernel/state.ts +0 -15
- package/src/core/lifecycle/render-policy.ts +12 -26
- package/src/core/meta/__tests__/code-visibility.test.tsx +78 -70
- package/src/core/meta/code-visibility.ts +18 -9
- package/src/core/network/__tests__/connection.test.ts +1 -1
- package/src/core/network/__tests__/requests-lazy.test.ts +6 -8
- package/src/core/network/connection-notice.ts +1 -1
- package/src/core/network/connection.ts +1 -1
- package/src/core/network/requests-lazy.ts +5 -8
- package/src/core/network/requests-network.ts +5 -31
- package/src/core/network/requests-static.ts +1 -4
- package/src/core/network/requests-toasting.tsx +1 -4
- package/src/core/network/types.ts +6 -14
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/notebook/__tests__/operations.test.ts +51 -0
- package/src/core/notebook/attachments.ts +38 -0
- package/src/core/notebook/document/__tests__/codemirror.test.ts +759 -0
- package/src/core/notebook/document/__tests__/draft.test.ts +363 -0
- package/src/core/notebook/document/__tests__/fixtures.ts +92 -0
- package/src/core/notebook/document/__tests__/hooks.test.tsx +391 -0
- package/src/core/notebook/document/__tests__/lifecycle-scenarios.test.ts +61 -0
- package/src/core/notebook/document/__tests__/model.test.ts +247 -0
- package/src/core/notebook/document/__tests__/protocol-fixtures.ts +38 -0
- package/src/core/notebook/document/__tests__/recovery-scenarios.test.ts +230 -0
- package/src/core/notebook/document/__tests__/scenario-fixture.ts +43 -0
- package/src/core/notebook/document/__tests__/scenarios.test.ts +205 -0
- package/src/core/notebook/document/__tests__/state.test.ts +459 -0
- package/src/core/notebook/document/__tests__/submission.test.ts +203 -0
- package/src/core/notebook/document/__tests__/test-host.test.ts +92 -0
- package/src/core/notebook/document/__tests__/test-host.ts +247 -0
- package/src/core/notebook/document/__tests__/test-notebook.ts +149 -0
- package/src/core/notebook/document/__tests__/user.ts +61 -0
- package/src/core/notebook/document/codemirror.ts +216 -0
- package/src/core/notebook/document/draft.ts +502 -0
- package/src/core/notebook/document/hooks.tsx +118 -0
- package/src/core/notebook/document/index.ts +24 -0
- package/src/core/notebook/document/model.ts +102 -0
- package/src/core/notebook/document/select.ts +41 -0
- package/src/core/notebook/document/state.ts +378 -0
- package/src/core/notebook/document/sync.ts +369 -0
- package/src/core/notebook/index.ts +19 -0
- package/src/core/notebook/operations.ts +30 -0
- package/src/core/notebook/replica/AGENTS.md +11 -0
- package/src/core/{host → notebook/replica}/__tests__/attachments.test.ts +9 -10
- package/src/core/{host → notebook/replica}/__tests__/document.test.ts +18 -16
- package/src/core/notebook/replica/__tests__/executions.test.ts +200 -0
- package/src/core/notebook/replica/__tests__/fixtures.ts +79 -0
- package/src/core/notebook/replica/__tests__/replica.test.ts +559 -0
- package/src/core/notebook/replica/__tests__/results.test.ts +193 -0
- package/src/core/notebook/replica/__tests__/runtime.test.ts +78 -0
- package/src/core/notebook/replica/backend.ts +19 -0
- package/src/core/notebook/replica/connection.ts +42 -0
- package/src/core/notebook/replica/index.ts +20 -0
- package/src/core/notebook/replica/replica.ts +336 -0
- package/src/core/{host → notebook/replica}/resources/attachments.ts +6 -3
- package/src/core/{host → notebook/replica}/resources/document.ts +3 -3
- package/src/core/notebook/replica/resources/executions.ts +157 -0
- package/src/core/notebook/replica/resources/results.ts +48 -0
- package/src/core/notebook/replica/resources/runtime.ts +14 -0
- package/src/core/packages/__tests__/useSandboxController.test.tsx +1 -1
- package/src/core/packages/useSandboxController.ts +1 -1
- package/src/core/run-app.tsx +49 -51
- package/src/core/runtime/__tests__/adapter.test.ts +4 -4
- package/src/core/runtime/__tests__/createWsUrl.test.ts +77 -7
- package/src/core/runtime/__tests__/runtime.test.ts +32 -74
- package/src/core/runtime/adapter.ts +5 -5
- package/src/core/runtime/config.ts +2 -2
- package/src/core/runtime/runtime.ts +33 -46
- package/src/core/runtime/types.ts +0 -2
- package/src/core/saving/__tests__/document.test.tsx +123 -0
- package/src/core/saving/__tests__/state.test.ts +111 -0
- package/src/core/saving/__tests__/useAutoSave.test.tsx +67 -0
- package/src/core/saving/document.ts +33 -0
- package/src/core/saving/filename.ts +1 -1
- package/src/core/saving/save-component.tsx +39 -60
- package/src/core/saving/state.ts +38 -39
- package/src/core/saving/useAutoSave.ts +17 -53
- package/src/core/session/__tests__/backend.test.ts +236 -0
- package/src/core/session/__tests__/useNotebookReplica.hook.test.tsx +186 -0
- package/src/core/{websocket → session}/__tests__/useNotebookSession.hook.test.tsx +247 -262
- package/src/core/session/active-notebook.ts +28 -0
- package/src/core/session/backend.ts +123 -0
- package/src/core/session/embedded.ts +7 -0
- package/src/core/{websocket → session}/types.ts +2 -2
- package/src/core/session/useNotebookReplica.ts +74 -0
- package/src/core/{websocket → session}/useNotebookSession.tsx +80 -67
- package/src/core/state/__tests__/jotai.test.tsx +108 -0
- package/src/core/state/jotai.ts +1 -1
- package/src/core/static/__tests__/files.test.ts +38 -10
- package/src/core/static/files.ts +2 -0
- package/src/core/{websocket → transports}/__tests__/connection-state.test.ts +1 -13
- package/src/core/transports/__tests__/server.test.ts +408 -0
- package/src/core/transports/__tests__/static.test.ts +306 -0
- package/src/core/transports/__tests__/wasm.test.ts +166 -0
- package/src/core/{websocket → transports}/connection-state.ts +9 -16
- package/src/core/transports/server.ts +152 -0
- package/src/core/transports/static.ts +166 -0
- package/src/core/transports/wasm.ts +97 -0
- package/src/core/vscode/__tests__/vscode-bindings.test.ts +125 -0
- package/src/core/vscode/vscode-bindings.ts +14 -2
- package/src/core/wasm/__tests__/bridge.test.ts +210 -64
- package/src/core/wasm/__tests__/worker.test.ts +188 -0
- package/src/core/wasm/bridge.ts +120 -73
- package/src/core/wasm/rpc.ts +15 -3
- package/src/core/wasm/store.ts +3 -3
- package/src/core/wasm/worker/bootstrap.ts +55 -51
- package/src/core/wasm/worker/getMarimoWheel.ts +1 -1
- package/src/core/wasm/worker/save-worker.ts +9 -10
- package/src/core/wasm/worker/types.ts +30 -18
- package/src/core/wasm/worker/worker.ts +118 -72
- package/src/css/app/Cell.css +7 -36
- package/src/css/app/codemirror.css +1 -1
- package/src/css/common.css +1 -1
- package/src/css/globals.css +72 -24
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
- package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
- package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
- package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
- package/src/hooks/__tests__/useScript.test.tsx +77 -0
- package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useDebounce.ts +6 -10
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useEventListener.ts +1 -2
- package/src/hooks/useInputHistory.ts +4 -7
- package/src/hooks/useInternalStateWithSync.ts +6 -6
- package/src/hooks/useResizeObserver.ts +4 -7
- package/src/hooks/useScript.ts +67 -70
- package/src/hooks/useStateForKey.ts +93 -0
- package/src/mount.tsx +20 -15
- package/src/plugins/__tests__/plugin-object-schemas.test.ts +104 -0
- package/src/plugins/core/RenderHTML.tsx +3 -5
- package/src/plugins/core/__test__/RenderHTML-copy.test.tsx +60 -0
- package/src/plugins/core/registerReactComponent.tsx +5 -14
- package/src/plugins/impl/CodeEditorPlugin.tsx +1 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +34 -13
- package/src/plugins/impl/DataTablePlugin.tsx +69 -37
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/RangeSliderPlugin.tsx +16 -2
- package/src/plugins/impl/RefreshPlugin.tsx +1 -0
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +93 -4
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +387 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/RangeSliderPlugin.test.tsx +109 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +153 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/model.ts +3 -1
- package/src/plugins/impl/anywidget/registry.ts +18 -14
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
- package/src/plugins/impl/chat/chat-ui.tsx +17 -15
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/common/error-banner.tsx +5 -3
- package/src/plugins/impl/data-editor/__tests__/column-types.test.ts +42 -0
- package/src/plugins/impl/data-editor/__tests__/editor-state.test.ts +52 -1
- package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +120 -4
- package/src/plugins/impl/data-editor/__tests__/glide-utils.test.ts +116 -0
- package/src/plugins/impl/data-editor/column-types.ts +55 -0
- package/src/plugins/impl/data-editor/editor-state.ts +18 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +74 -12
- package/src/plugins/impl/data-editor/glide-utils.ts +39 -38
- package/src/plugins/impl/data-editor/types.ts +5 -0
- package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +1 -1
- package/src/plugins/impl/data-explorer/components/column-summary.tsx +4 -2
- package/src/plugins/impl/data-explorer/state/reducer.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -2
- package/src/plugins/impl/data-frames/__tests__/sidebar.test.tsx +160 -0
- package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +66 -66
- package/src/plugins/impl/data-frames/panel.tsx +44 -16
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +16 -17
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +2 -5
- package/src/plugins/impl/plotly/selection.ts +25 -19
- package/src/plugins/impl/vega/VegaPlugin.tsx +2 -3
- package/src/plugins/impl/vega/make-selectable.ts +4 -7
- package/src/plugins/layout/DownloadPlugin.tsx +7 -11
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/plugins/layout/__test__/DownloadPlugin.test.tsx +205 -0
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/button.stories.tsx +1 -0
- package/src/stories/cell-status.stories.tsx +29 -24
- package/src/stories/cell.stories.tsx +46 -46
- package/src/stories/data-table.stories.tsx +31 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/stories/form-wrapper.stories.tsx +2 -2
- package/src/stories/layout/vertical/one-column.stories.tsx +28 -42
- package/src/stories/runtime-story.tsx +23 -17
- package/src/stories/select.stories.tsx +2 -2
- package/src/stories/switchable-multi-select.stories.tsx +1 -1
- package/src/theme/__tests__/islands-theme.test.tsx +276 -0
- package/src/theme/namespace.tsx +4 -3
- package/src/theme/useTheme.ts +71 -41
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/batch-requests.test.ts +3 -3
- package/src/utils/__tests__/{createReducer.test.ts → createReducer.test.tsx} +127 -1
- package/src/utils/__tests__/download.test.tsx +183 -23
- package/src/utils/__tests__/numbers.test.ts +105 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +11 -2
- package/src/utils/blob.ts +1 -1
- package/src/utils/createReducer.ts +115 -79
- package/src/utils/download.ts +67 -6
- package/src/utils/maps.ts +0 -3
- package/src/utils/numbers.ts +79 -18
- package/src/utils/objects.ts +4 -9
- package/src/utils/time.ts +10 -0
- package/src/utils/tracer.ts +9 -5
- package/src/utils/zod-utils.ts +1 -3
- package/dist/_baseMerge-BiGRwuqv.js +0 -165
- package/dist/any-language-editor-D4q-IFcM.js +0 -76
- package/dist/assets/__vite-browser-external-BXYkiFsk.js +0 -1
- package/dist/assets/worker-HPY9fbhz.js +0 -114
- package/dist/feedback-button-g9eH5l7Q.js +0 -379
- package/dist/hasIn-o2Us0uVx.js +0 -132
- package/dist/html-to-image-B5H-xKRH.js +0 -3251
- package/dist/isArrayLikeObject-BZl-15ir.js +0 -450
- package/dist/isEmpty-BSZ1LaUo.js +0 -35
- package/dist/isSymbol-kQcfYbNX.js +0 -8
- package/dist/label-DoysmLMW.js +0 -281
- package/dist/pick-CJeoxYOS.js +0 -51
- package/dist/select-DemoKT4G.js +0 -1344
- package/dist/toDate-BvdkR0aH.js +0 -1185
- package/dist/toast-PkdZraM8.js +0 -1830
- package/dist/use-restore-focus-CKjfFjLM.js +0 -6369
- package/dist/useLifecycle-Dfq8UD-b.js +0 -169
- package/dist/useTheme-Bu5bBgrh.js +0 -904
- package/src/components/chat/reasoning-accordion.tsx +0 -61
- package/src/components/chat/tool-call/tool-error-card.tsx +0 -81
- package/src/components/chat/tool-call/tool-history-row.tsx +0 -174
- package/src/components/editor/ai/__tests__/staged-cell-submission.test.ts +0 -106
- package/src/components/editor/ai/add-cell-with-ai.tsx +0 -506
- package/src/components/editor/ai/staged-cell-submission.ts +0 -46
- package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +0 -64
- package/src/components/editor/cell/code/__tests__/rtc-editor-config.test.ts +0 -50
- package/src/components/editor/chrome/wrapper/footer-items/rtc-status.tsx +0 -65
- package/src/components/editor/navigation/__tests__/state.test.ts +0 -81
- package/src/components/editor/navigation/state.ts +0 -65
- package/src/components/icons/loading-ellipsis.tsx +0 -46
- package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +0 -656
- package/src/core/ai/tools/run-cells-tool.ts +0 -303
- package/src/core/attachments/state.ts +0 -18
- package/src/core/cells/__tests__/apply-transaction.test.ts +0 -991
- package/src/core/cells/__tests__/document-changes.test.ts +0 -1140
- package/src/core/cells/__tests__/document-roundtrip.test.ts +0 -392
- package/src/core/cells/document-changes.ts +0 -944
- package/src/core/codemirror/rtc/extension.ts +0 -453
- package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +0 -52
- package/src/core/codemirror/rtc/loro/awareness.ts +0 -531
- package/src/core/codemirror/rtc/loro/colors.ts +0 -38
- package/src/core/codemirror/rtc/loro/sync.ts +0 -168
- package/src/core/host/AGENTS.md +0 -11
- package/src/core/host/__tests__/events.test.ts +0 -46
- package/src/core/host/__tests__/fixtures.ts +0 -39
- package/src/core/host/__tests__/notebook-client.test.ts +0 -433
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/__tests__/stream.test.ts +0 -209
- package/src/core/host/connection.ts +0 -81
- package/src/core/host/events.ts +0 -61
- package/src/core/host/notebook-client.ts +0 -290
- package/src/core/host/protocol.ts +0 -1945
- package/src/core/host/resources/resource.ts +0 -47
- package/src/core/host/stream.ts +0 -211
- package/src/core/rtc/state.ts +0 -34
- package/src/core/state/__tests__/jotai.test.ts +0 -25
- package/src/core/wasm/worker/message-buffer.ts +0 -33
- package/src/core/websocket/__tests__/createConnectionTransport.test.ts +0 -49
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +0 -170
- package/src/core/websocket/state.ts +0 -7
- package/src/core/websocket/transports/__tests__/basic.test.ts +0 -275
- package/src/core/websocket/transports/__tests__/sse.test.ts +0 -335
- package/src/core/websocket/transports/__tests__/transport.test.ts +0 -308
- package/src/core/websocket/transports/__tests__/ws.test.ts +0 -125
- package/src/core/websocket/transports/basic.ts +0 -102
- package/src/core/websocket/transports/create.ts +0 -31
- package/src/core/websocket/transports/sse.ts +0 -308
- package/src/core/websocket/transports/transport.ts +0 -50
- package/src/core/websocket/transports/ws.ts +0 -98
- package/src/core/websocket/useNotebookClient.ts +0 -88
- package/src/utils/__tests__/id-tree.test.ts +0 -2145
- package/src/utils/id-tree.tsx +0 -1134
- /package/src/{core/attachments/toast.ts → components/editor/agent-toast.ts} +0 -0
- /package/src/core/{websocket → session}/connection-utils.ts +0 -0
|
@@ -4,29 +4,32 @@ import { python } from "@codemirror/lang-python";
|
|
|
4
4
|
import { EditorState } from "@codemirror/state";
|
|
5
5
|
import { EditorView } from "@codemirror/view";
|
|
6
6
|
import { createStore } from "jotai";
|
|
7
|
+
import { createRef } from "react";
|
|
7
8
|
import {
|
|
8
9
|
afterAll,
|
|
9
|
-
afterEach,
|
|
10
10
|
beforeAll,
|
|
11
|
+
afterEach,
|
|
11
12
|
beforeEach,
|
|
12
13
|
describe,
|
|
13
14
|
expect,
|
|
14
15
|
it,
|
|
15
16
|
vi,
|
|
16
17
|
} from "vitest";
|
|
18
|
+
import { MockExecutions } from "@/__mocks__/executions";
|
|
19
|
+
import {
|
|
20
|
+
notebookQueuedOrRunningCountAtom,
|
|
21
|
+
submitCells,
|
|
22
|
+
} from "@/core/executions";
|
|
23
|
+
import { store } from "@/core/state/jotai";
|
|
17
24
|
import { MockNotebook } from "@/__mocks__/notebook";
|
|
18
25
|
import { cellId } from "@/__tests__/branded";
|
|
19
26
|
import type { CellHandle } from "@/components/editor/notebook-cell";
|
|
20
|
-
import { CellId, SCRATCH_CELL_ID
|
|
27
|
+
import { CellId, SCRATCH_CELL_ID } from "@/core/cells/ids";
|
|
21
28
|
import { foldAllBulk, unfoldAllBulk } from "@/core/codemirror/editing/commands";
|
|
22
29
|
import { adaptiveLanguageConfiguration } from "@/core/codemirror/language/extension";
|
|
23
30
|
import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
|
|
24
31
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
25
|
-
import {
|
|
26
|
-
type CollapsibleTree,
|
|
27
|
-
MultiColumn,
|
|
28
|
-
type CellColumnId,
|
|
29
|
-
} from "@/utils/id-tree";
|
|
32
|
+
import type { CellConfig } from "@/core/network/types";
|
|
30
33
|
import type { Seconds } from "@/utils/time";
|
|
31
34
|
import {
|
|
32
35
|
exportedForTesting,
|
|
@@ -34,13 +37,12 @@ import {
|
|
|
34
37
|
type NotebookState,
|
|
35
38
|
notebookAtom,
|
|
36
39
|
} from "../cells";
|
|
37
|
-
import { exportedForTesting as documentTransactionTestExports } from "../document-changes";
|
|
38
40
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
type CellData,
|
|
42
|
+
createCell,
|
|
43
|
+
createCellConfig,
|
|
44
|
+
createCellRuntimeState,
|
|
45
|
+
} from "../types";
|
|
44
46
|
|
|
45
47
|
vi.mock("@/core/codemirror/editing/commands", () => ({
|
|
46
48
|
foldAllBulk: vi.fn(),
|
|
@@ -60,9 +62,6 @@ vi.mock("../scrollCellIntoView", async (importOriginal) => {
|
|
|
60
62
|
};
|
|
61
63
|
});
|
|
62
64
|
|
|
63
|
-
const FIRST_COLUMN = 0;
|
|
64
|
-
const SECOND_COLUMN = 1;
|
|
65
|
-
|
|
66
65
|
const { initialNotebookState, reducer, createActions } = exportedForTesting;
|
|
67
66
|
|
|
68
67
|
function formatCells(notebook: NotebookState) {
|
|
@@ -78,14 +77,7 @@ function formatCells(notebook: NotebookState) {
|
|
|
78
77
|
const cells = cellsId.map((cellId) => cellData[cellId]);
|
|
79
78
|
return cells.map((cell) => printCell(cell)).join("\n\n");
|
|
80
79
|
};
|
|
81
|
-
|
|
82
|
-
return `> col ${columnIdx}\n${printCells(column.inOrderIds)}`;
|
|
83
|
-
};
|
|
84
|
-
const columns = cellIds.getColumns();
|
|
85
|
-
if (columns.length > 1) {
|
|
86
|
-
return wrap(columns.map(printColumn).join("\n\n"));
|
|
87
|
-
}
|
|
88
|
-
return wrap(printCells(cellIds.inOrderIds));
|
|
80
|
+
return wrap(printCells(cellIds));
|
|
89
81
|
}
|
|
90
82
|
|
|
91
83
|
function createEditor(content: string) {
|
|
@@ -121,8 +113,7 @@ describe("cell reducer", () => {
|
|
|
121
113
|
let cells: ReturnType<typeof flattenTopLevelNotebookCells>;
|
|
122
114
|
let firstCellId: CellId;
|
|
123
115
|
|
|
124
|
-
const
|
|
125
|
-
state = reducer(state, action);
|
|
116
|
+
const mountEditors = () => {
|
|
126
117
|
for (const [cellIdString, handle] of Object.entries(state.cellHandles)) {
|
|
127
118
|
// @ts-expect-error - Typescript Object.entries doesn't know that keys are CellId
|
|
128
119
|
const cid: CellId = cellIdString;
|
|
@@ -136,7 +127,83 @@ describe("cell reducer", () => {
|
|
|
136
127
|
}
|
|
137
128
|
}
|
|
138
129
|
cells = flattenTopLevelNotebookCells(state);
|
|
139
|
-
}
|
|
130
|
+
};
|
|
131
|
+
const actions = createActions((action) => {
|
|
132
|
+
state = reducer(state, action);
|
|
133
|
+
mountEditors();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
/** Put a cell into the notebook the way the document would. */
|
|
137
|
+
function seedCell({
|
|
138
|
+
cellId,
|
|
139
|
+
before,
|
|
140
|
+
code = "",
|
|
141
|
+
name,
|
|
142
|
+
config,
|
|
143
|
+
newCellId,
|
|
144
|
+
hideCode,
|
|
145
|
+
autoFocus = true,
|
|
146
|
+
}: {
|
|
147
|
+
cellId: CellId | "__end__";
|
|
148
|
+
before: boolean;
|
|
149
|
+
code?: string;
|
|
150
|
+
name?: string;
|
|
151
|
+
config?: CellConfig;
|
|
152
|
+
newCellId?: CellId;
|
|
153
|
+
hideCode?: boolean;
|
|
154
|
+
autoFocus?: boolean;
|
|
155
|
+
}): CellId {
|
|
156
|
+
const cellIndex =
|
|
157
|
+
cellId === "__end__"
|
|
158
|
+
? state.cellIds.length
|
|
159
|
+
: state.cellIds.indexOf(cellId);
|
|
160
|
+
const id = newCellId ?? CellId.create();
|
|
161
|
+
const cellIds = [...state.cellIds];
|
|
162
|
+
cellIds.splice(before ? cellIndex : cellIndex + 1, 0, id);
|
|
163
|
+
state = {
|
|
164
|
+
...state,
|
|
165
|
+
cellIds,
|
|
166
|
+
cellData: {
|
|
167
|
+
...state.cellData,
|
|
168
|
+
[id]: createCell({
|
|
169
|
+
id,
|
|
170
|
+
code,
|
|
171
|
+
name,
|
|
172
|
+
config: createCellConfig({
|
|
173
|
+
...config,
|
|
174
|
+
...(hideCode != null && { hide_code: hideCode }),
|
|
175
|
+
}),
|
|
176
|
+
}),
|
|
177
|
+
},
|
|
178
|
+
cellRuntime: { ...state.cellRuntime, [id]: createCellRuntimeState() },
|
|
179
|
+
cellHandles: { ...state.cellHandles, [id]: createRef() },
|
|
180
|
+
scrollKey: autoFocus ? id : null,
|
|
181
|
+
};
|
|
182
|
+
mountEditors();
|
|
183
|
+
return id;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Edit a cell's code as the document delivers it. */
|
|
187
|
+
function editCode(cellId: CellId, code: string) {
|
|
188
|
+
const cells = state.cellIds.map((id) => {
|
|
189
|
+
const cell = state.cellData[id];
|
|
190
|
+
return {
|
|
191
|
+
id,
|
|
192
|
+
code: id === cellId ? code : cell.code,
|
|
193
|
+
name: cell.name,
|
|
194
|
+
config: {
|
|
195
|
+
column: cell.config.column ?? null,
|
|
196
|
+
disabled: cell.config.disabled ?? false,
|
|
197
|
+
hide_code: cell.config.hide_code ?? false,
|
|
198
|
+
expand_output: cell.config.expand_output ?? false,
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
actions.reconcileDocument({
|
|
203
|
+
order: state.cellIds,
|
|
204
|
+
cells: new Map(cells.map((cell) => [cell.id, cell])),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
140
207
|
|
|
141
208
|
let i = 0;
|
|
142
209
|
const originalCreate = CellId.create.bind(CellId);
|
|
@@ -151,200 +218,16 @@ describe("cell reducer", () => {
|
|
|
151
218
|
i = 0;
|
|
152
219
|
|
|
153
220
|
state = initialNotebookState();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
firstCellId = state.cellIds.inOrderIds[0];
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
afterEach(() => {
|
|
160
|
-
documentTransactionTestExports.cancelPendingChanges();
|
|
221
|
+
seedCell({ cellId: "__end__", before: false });
|
|
222
|
+
firstCellId = state.cellIds[0];
|
|
161
223
|
});
|
|
162
224
|
|
|
163
225
|
afterAll(() => {
|
|
164
226
|
CellId.create = originalCreate;
|
|
165
227
|
});
|
|
166
228
|
|
|
167
|
-
it("can add a cell after another cell", () => {
|
|
168
|
-
actions.createNewCell({
|
|
169
|
-
cellId: firstCellId,
|
|
170
|
-
before: false,
|
|
171
|
-
});
|
|
172
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
173
|
-
"
|
|
174
|
-
[0] ''
|
|
175
|
-
|
|
176
|
-
[1] ''
|
|
177
|
-
"
|
|
178
|
-
`);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it("can add a cell to the end with code", () => {
|
|
182
|
-
actions.createNewCell({
|
|
183
|
-
cellId: "__end__",
|
|
184
|
-
code: "import numpy as np",
|
|
185
|
-
before: false,
|
|
186
|
-
});
|
|
187
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
188
|
-
"
|
|
189
|
-
[0] ''
|
|
190
|
-
|
|
191
|
-
[1] 'import numpy as np'
|
|
192
|
-
"
|
|
193
|
-
`);
|
|
194
|
-
|
|
195
|
-
// Cell should be added to the end and edited
|
|
196
|
-
expect(cells[1].code).toBe("import numpy as np");
|
|
197
|
-
expect(cells[1].edited).toBe(true);
|
|
198
|
-
expect(cells[1].lastCodeRun).toBe(null);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("can add a cell before another cell", () => {
|
|
202
|
-
actions.createNewCell({
|
|
203
|
-
cellId: firstCellId,
|
|
204
|
-
before: true,
|
|
205
|
-
});
|
|
206
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
207
|
-
"
|
|
208
|
-
[1] ''
|
|
209
|
-
|
|
210
|
-
[0] ''
|
|
211
|
-
"
|
|
212
|
-
`);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("can add a cell with name and config", () => {
|
|
216
|
-
actions.createNewCell({
|
|
217
|
-
cellId: firstCellId,
|
|
218
|
-
before: false,
|
|
219
|
-
code: "x = 1",
|
|
220
|
-
name: "My Cell",
|
|
221
|
-
config: { hide_code: true, disabled: false },
|
|
222
|
-
});
|
|
223
|
-
const newCellId = state.cellIds.inOrderIds[1];
|
|
224
|
-
expect(state.cellData[newCellId].name).toBe("My Cell");
|
|
225
|
-
expect(state.cellData[newCellId].config.hide_code).toBe(true);
|
|
226
|
-
expect(state.cellData[newCellId].config.disabled).toBe(false);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it("can delete a Python cell and undo delete", () => {
|
|
230
|
-
actions.createNewCell({
|
|
231
|
-
cellId: firstCellId,
|
|
232
|
-
before: false,
|
|
233
|
-
});
|
|
234
|
-
actions.deleteCell({
|
|
235
|
-
cellId: firstCellId,
|
|
236
|
-
});
|
|
237
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
238
|
-
"
|
|
239
|
-
[1] ''
|
|
240
|
-
"
|
|
241
|
-
`);
|
|
242
|
-
|
|
243
|
-
// undo
|
|
244
|
-
actions.undoDeleteCell();
|
|
245
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
246
|
-
"
|
|
247
|
-
[2] ''
|
|
248
|
-
|
|
249
|
-
[1] ''
|
|
250
|
-
"
|
|
251
|
-
`);
|
|
252
|
-
|
|
253
|
-
// Verify scrollKey is set to the restored cell
|
|
254
|
-
expect(state.scrollKey).toBe("2");
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
it("can delete a SQL cell and undo delete", () => {
|
|
258
|
-
actions.createNewCell({
|
|
259
|
-
cellId: firstCellId,
|
|
260
|
-
before: false,
|
|
261
|
-
code: `df = mo.sql("""SELECT * FROM table""")`,
|
|
262
|
-
});
|
|
263
|
-
const newCellId = state.cellIds.getColumns()[0].atOrThrow(1);
|
|
264
|
-
actions.deleteCell({
|
|
265
|
-
cellId: newCellId,
|
|
266
|
-
});
|
|
267
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
268
|
-
"
|
|
269
|
-
[0] ''
|
|
270
|
-
"
|
|
271
|
-
`);
|
|
272
|
-
|
|
273
|
-
// undo
|
|
274
|
-
actions.undoDeleteCell();
|
|
275
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
276
|
-
"
|
|
277
|
-
[0] ''
|
|
278
|
-
|
|
279
|
-
[2] 'df = mo.sql("""SELECT * FROM table""")'
|
|
280
|
-
"
|
|
281
|
-
`);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it("can delete a Markdown cell and undo delete", () => {
|
|
285
|
-
const text = "The quick brown fox jumps over the lazy dog.";
|
|
286
|
-
actions.createNewCell({
|
|
287
|
-
cellId: firstCellId,
|
|
288
|
-
before: false,
|
|
289
|
-
code: `mo.md(r"""${text}""")`,
|
|
290
|
-
});
|
|
291
|
-
const newCellId = state.cellIds.getColumns()[0].atOrThrow(1);
|
|
292
|
-
actions.deleteCell({
|
|
293
|
-
cellId: newCellId,
|
|
294
|
-
});
|
|
295
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
296
|
-
"
|
|
297
|
-
[0] ''
|
|
298
|
-
"
|
|
299
|
-
`);
|
|
300
|
-
|
|
301
|
-
// undo
|
|
302
|
-
actions.undoDeleteCell();
|
|
303
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
304
|
-
"
|
|
305
|
-
[0] ''
|
|
306
|
-
|
|
307
|
-
[2] 'mo.md(r"""The quick brown fox jumps over the lazy dog.""")'
|
|
308
|
-
"
|
|
309
|
-
`);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it("undo delete restores cell config (hide_code, disabled)", () => {
|
|
313
|
-
actions.createNewCell({
|
|
314
|
-
cellId: firstCellId,
|
|
315
|
-
before: false,
|
|
316
|
-
code: "x = 1",
|
|
317
|
-
});
|
|
318
|
-
const newCellId = state.cellIds.getColumns()[0].atOrThrow(1);
|
|
319
|
-
|
|
320
|
-
// Set config to hidden and disabled
|
|
321
|
-
actions.updateCellConfig({
|
|
322
|
-
cellId: newCellId,
|
|
323
|
-
config: { hide_code: true, disabled: true },
|
|
324
|
-
});
|
|
325
|
-
expect(state.cellData[newCellId].config.hide_code).toBe(true);
|
|
326
|
-
expect(state.cellData[newCellId].config.disabled).toBe(true);
|
|
327
|
-
|
|
328
|
-
// Delete the cell
|
|
329
|
-
actions.deleteCell({ cellId: newCellId });
|
|
330
|
-
expect(state.cellIds.inOrderIds).not.toContain(newCellId);
|
|
331
|
-
|
|
332
|
-
// Undo delete
|
|
333
|
-
actions.undoDeleteCell();
|
|
334
|
-
const restoredCellId =
|
|
335
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
336
|
-
|
|
337
|
-
// Config should be preserved
|
|
338
|
-
expect(state.cellData[restoredCellId].config.hide_code).toBe(true);
|
|
339
|
-
expect(state.cellData[restoredCellId].config.disabled).toBe(true);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
229
|
it("can update a cell", () => {
|
|
343
|
-
|
|
344
|
-
cellId: firstCellId,
|
|
345
|
-
code: "import numpy as np",
|
|
346
|
-
formattingChange: false,
|
|
347
|
-
});
|
|
230
|
+
editCode(firstCellId, "import numpy as np");
|
|
348
231
|
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
349
232
|
"
|
|
350
233
|
[0] 'import numpy as np'
|
|
@@ -352,443 +235,6 @@ describe("cell reducer", () => {
|
|
|
352
235
|
`);
|
|
353
236
|
});
|
|
354
237
|
|
|
355
|
-
it("can move a cell", () => {
|
|
356
|
-
actions.createNewCell({
|
|
357
|
-
cellId: firstCellId,
|
|
358
|
-
before: false,
|
|
359
|
-
});
|
|
360
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
361
|
-
"
|
|
362
|
-
[0] ''
|
|
363
|
-
|
|
364
|
-
[1] ''
|
|
365
|
-
"
|
|
366
|
-
`);
|
|
367
|
-
|
|
368
|
-
// move first cell to the end
|
|
369
|
-
actions.moveCell({
|
|
370
|
-
cellId: firstCellId,
|
|
371
|
-
before: false,
|
|
372
|
-
});
|
|
373
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
374
|
-
"
|
|
375
|
-
[1] ''
|
|
376
|
-
|
|
377
|
-
[0] ''
|
|
378
|
-
"
|
|
379
|
-
`);
|
|
380
|
-
|
|
381
|
-
// move it back
|
|
382
|
-
actions.moveCell({
|
|
383
|
-
cellId: firstCellId,
|
|
384
|
-
before: true,
|
|
385
|
-
});
|
|
386
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
387
|
-
"
|
|
388
|
-
[0] ''
|
|
389
|
-
|
|
390
|
-
[1] ''
|
|
391
|
-
"
|
|
392
|
-
`);
|
|
393
|
-
|
|
394
|
-
// Add a column breakpoint to test left/right movement
|
|
395
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
396
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
397
|
-
"
|
|
398
|
-
> col 0
|
|
399
|
-
[0] ''
|
|
400
|
-
|
|
401
|
-
> col 1
|
|
402
|
-
[1] ''
|
|
403
|
-
"
|
|
404
|
-
`);
|
|
405
|
-
|
|
406
|
-
// Move cell right
|
|
407
|
-
actions.moveCell({
|
|
408
|
-
cellId: firstCellId,
|
|
409
|
-
direction: "right",
|
|
410
|
-
});
|
|
411
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
412
|
-
"
|
|
413
|
-
> col 0
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
> col 1
|
|
417
|
-
[0] ''
|
|
418
|
-
|
|
419
|
-
[1] ''
|
|
420
|
-
"
|
|
421
|
-
`);
|
|
422
|
-
|
|
423
|
-
// Move cell left
|
|
424
|
-
actions.moveCell({
|
|
425
|
-
cellId: firstCellId,
|
|
426
|
-
direction: "left",
|
|
427
|
-
});
|
|
428
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
429
|
-
"
|
|
430
|
-
> col 0
|
|
431
|
-
[0] ''
|
|
432
|
-
|
|
433
|
-
> col 1
|
|
434
|
-
[1] ''
|
|
435
|
-
"
|
|
436
|
-
`);
|
|
437
|
-
|
|
438
|
-
// Try to move cell left when it's already in leftmost column (should noop)
|
|
439
|
-
actions.moveCell({
|
|
440
|
-
cellId: firstCellId,
|
|
441
|
-
direction: "left",
|
|
442
|
-
});
|
|
443
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
444
|
-
"
|
|
445
|
-
> col 0
|
|
446
|
-
[0] ''
|
|
447
|
-
|
|
448
|
-
> col 1
|
|
449
|
-
[1] ''
|
|
450
|
-
"
|
|
451
|
-
`);
|
|
452
|
-
|
|
453
|
-
// Try to move cell right when it's already in rightmost column (should noop)
|
|
454
|
-
actions.moveCell({
|
|
455
|
-
cellId: cellId("1"),
|
|
456
|
-
direction: "right",
|
|
457
|
-
});
|
|
458
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
459
|
-
"
|
|
460
|
-
> col 0
|
|
461
|
-
[0] ''
|
|
462
|
-
|
|
463
|
-
> col 1
|
|
464
|
-
[1] ''
|
|
465
|
-
"
|
|
466
|
-
`);
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
it("can drag and drop a cell", () => {
|
|
470
|
-
actions.createNewCell({
|
|
471
|
-
cellId: firstCellId,
|
|
472
|
-
before: false,
|
|
473
|
-
});
|
|
474
|
-
actions.createNewCell({
|
|
475
|
-
cellId: cellId("1"),
|
|
476
|
-
before: false,
|
|
477
|
-
});
|
|
478
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
479
|
-
"
|
|
480
|
-
[0] ''
|
|
481
|
-
|
|
482
|
-
[1] ''
|
|
483
|
-
|
|
484
|
-
[2] ''
|
|
485
|
-
"
|
|
486
|
-
`);
|
|
487
|
-
|
|
488
|
-
// drag first cell to the end
|
|
489
|
-
actions.dropCellOverCell({
|
|
490
|
-
cellId: firstCellId,
|
|
491
|
-
overCellId: cellId("2"),
|
|
492
|
-
});
|
|
493
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
494
|
-
"
|
|
495
|
-
[1] ''
|
|
496
|
-
|
|
497
|
-
[2] ''
|
|
498
|
-
|
|
499
|
-
[0] ''
|
|
500
|
-
"
|
|
501
|
-
`);
|
|
502
|
-
|
|
503
|
-
// drag it back to the middle
|
|
504
|
-
actions.dropCellOverCell({
|
|
505
|
-
cellId: firstCellId,
|
|
506
|
-
overCellId: cellId("2"),
|
|
507
|
-
});
|
|
508
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
509
|
-
"
|
|
510
|
-
[1] ''
|
|
511
|
-
|
|
512
|
-
[0] ''
|
|
513
|
-
|
|
514
|
-
[2] ''
|
|
515
|
-
"
|
|
516
|
-
`);
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
it("can move a cell to an exact index within a column", () => {
|
|
520
|
-
actions.createNewCell({
|
|
521
|
-
cellId: firstCellId,
|
|
522
|
-
before: false,
|
|
523
|
-
});
|
|
524
|
-
actions.createNewCell({
|
|
525
|
-
cellId: cellId("1"),
|
|
526
|
-
before: false,
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
const columnId = state.cellIds.atOrThrow(0).id;
|
|
530
|
-
actions.moveCellToIndex({
|
|
531
|
-
cellId: firstCellId,
|
|
532
|
-
columnId,
|
|
533
|
-
index: 3,
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
537
|
-
"
|
|
538
|
-
[1] ''
|
|
539
|
-
|
|
540
|
-
[2] ''
|
|
541
|
-
|
|
542
|
-
[0] ''
|
|
543
|
-
"
|
|
544
|
-
`);
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
it("can move a cell to an exact index across columns", () => {
|
|
548
|
-
actions.createNewCell({
|
|
549
|
-
cellId: firstCellId,
|
|
550
|
-
before: false,
|
|
551
|
-
});
|
|
552
|
-
actions.createNewCell({
|
|
553
|
-
cellId: cellId("1"),
|
|
554
|
-
before: false,
|
|
555
|
-
});
|
|
556
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
557
|
-
|
|
558
|
-
const secondColumnId = state.cellIds.atOrThrow(1).id;
|
|
559
|
-
actions.moveCellToIndex({
|
|
560
|
-
cellId: firstCellId,
|
|
561
|
-
columnId: secondColumnId,
|
|
562
|
-
index: 1,
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
566
|
-
"
|
|
567
|
-
> col 0
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
> col 1
|
|
571
|
-
[1] ''
|
|
572
|
-
|
|
573
|
-
[0] ''
|
|
574
|
-
|
|
575
|
-
[2] ''
|
|
576
|
-
"
|
|
577
|
-
`);
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
it("moveCellToIndex is a no-op when moving to the same position", () => {
|
|
581
|
-
actions.createNewCell({
|
|
582
|
-
cellId: firstCellId,
|
|
583
|
-
before: false,
|
|
584
|
-
});
|
|
585
|
-
actions.createNewCell({
|
|
586
|
-
cellId: cellId("1"),
|
|
587
|
-
before: false,
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
const columnId = state.cellIds.atOrThrow(0).id;
|
|
591
|
-
const before = formatCells(state);
|
|
592
|
-
|
|
593
|
-
actions.moveCellToIndex({
|
|
594
|
-
cellId: firstCellId,
|
|
595
|
-
columnId,
|
|
596
|
-
index: 0,
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
expect(formatCells(state)).toBe(before);
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
it("moveCellToIndex is a no-op for an invalid columnId", () => {
|
|
603
|
-
actions.createNewCell({
|
|
604
|
-
cellId: firstCellId,
|
|
605
|
-
before: false,
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
const before = formatCells(state);
|
|
609
|
-
|
|
610
|
-
actions.moveCellToIndex({
|
|
611
|
-
cellId: firstCellId,
|
|
612
|
-
columnId: "nonexistent-column" as CellColumnId,
|
|
613
|
-
index: 0,
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
expect(formatCells(state)).toBe(before);
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
it("can move multiple cells relative to target", () => {
|
|
620
|
-
actions.createNewCell({
|
|
621
|
-
cellId: firstCellId,
|
|
622
|
-
before: false,
|
|
623
|
-
});
|
|
624
|
-
actions.createNewCell({
|
|
625
|
-
cellId: cellId("1"),
|
|
626
|
-
before: false,
|
|
627
|
-
});
|
|
628
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
629
|
-
"
|
|
630
|
-
[0] ''
|
|
631
|
-
|
|
632
|
-
[1] ''
|
|
633
|
-
|
|
634
|
-
[2] ''
|
|
635
|
-
"
|
|
636
|
-
`);
|
|
637
|
-
|
|
638
|
-
// Move first two cells after the third
|
|
639
|
-
actions.moveCellsRelativeTo({
|
|
640
|
-
cellIds: [firstCellId, cellId("1")],
|
|
641
|
-
targetCellId: cellId("2"),
|
|
642
|
-
position: "after",
|
|
643
|
-
});
|
|
644
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
645
|
-
"
|
|
646
|
-
[2] ''
|
|
647
|
-
|
|
648
|
-
[0] ''
|
|
649
|
-
|
|
650
|
-
[1] ''
|
|
651
|
-
"
|
|
652
|
-
`);
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
it("can undo cut-paste (move with previousPlacements)", () => {
|
|
656
|
-
actions.createNewCell({
|
|
657
|
-
cellId: firstCellId,
|
|
658
|
-
before: false,
|
|
659
|
-
});
|
|
660
|
-
actions.createNewCell({
|
|
661
|
-
cellId: cellId("1"),
|
|
662
|
-
before: false,
|
|
663
|
-
});
|
|
664
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
665
|
-
"
|
|
666
|
-
[0] ''
|
|
667
|
-
|
|
668
|
-
[1] ''
|
|
669
|
-
|
|
670
|
-
[2] ''
|
|
671
|
-
"
|
|
672
|
-
`);
|
|
673
|
-
|
|
674
|
-
const col = state.cellIds.findWithId(firstCellId);
|
|
675
|
-
const previousPlacements = [
|
|
676
|
-
{
|
|
677
|
-
columnId: col.id,
|
|
678
|
-
index: col.indexOfOrThrow(
|
|
679
|
-
firstCellId,
|
|
680
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
681
|
-
},
|
|
682
|
-
{
|
|
683
|
-
columnId: col.id,
|
|
684
|
-
index: col.indexOfOrThrow(
|
|
685
|
-
cellId("1"),
|
|
686
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
687
|
-
},
|
|
688
|
-
];
|
|
689
|
-
|
|
690
|
-
actions.moveCellsRelativeTo({
|
|
691
|
-
cellIds: [firstCellId, cellId("1")],
|
|
692
|
-
targetCellId: cellId("2"),
|
|
693
|
-
position: "after",
|
|
694
|
-
previousPlacements,
|
|
695
|
-
});
|
|
696
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
697
|
-
"
|
|
698
|
-
[2] ''
|
|
699
|
-
|
|
700
|
-
[0] ''
|
|
701
|
-
|
|
702
|
-
[1] ''
|
|
703
|
-
"
|
|
704
|
-
`);
|
|
705
|
-
|
|
706
|
-
actions.undoDeleteCell();
|
|
707
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
708
|
-
"
|
|
709
|
-
[0] ''
|
|
710
|
-
|
|
711
|
-
[1] ''
|
|
712
|
-
|
|
713
|
-
[2] ''
|
|
714
|
-
"
|
|
715
|
-
`);
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
it("undo order: cut-paste then delete — first undo restores delete, second undo undoes move", () => {
|
|
719
|
-
actions.createNewCell({
|
|
720
|
-
cellId: firstCellId,
|
|
721
|
-
before: false,
|
|
722
|
-
});
|
|
723
|
-
actions.createNewCell({
|
|
724
|
-
cellId: cellId("1"),
|
|
725
|
-
before: false,
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
const col = state.cellIds.findWithId(firstCellId);
|
|
729
|
-
const previousPlacements = [
|
|
730
|
-
{
|
|
731
|
-
columnId: col.id,
|
|
732
|
-
index: col.indexOfOrThrow(
|
|
733
|
-
firstCellId,
|
|
734
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
735
|
-
},
|
|
736
|
-
{
|
|
737
|
-
columnId: col.id,
|
|
738
|
-
index: col.indexOfOrThrow(
|
|
739
|
-
cellId("1"),
|
|
740
|
-
) as import("@/utils/id-tree").CellIndex,
|
|
741
|
-
},
|
|
742
|
-
];
|
|
743
|
-
|
|
744
|
-
actions.moveCellsRelativeTo({
|
|
745
|
-
cellIds: [firstCellId, cellId("1")],
|
|
746
|
-
targetCellId: cellId("2"),
|
|
747
|
-
position: "after",
|
|
748
|
-
previousPlacements,
|
|
749
|
-
});
|
|
750
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
751
|
-
"
|
|
752
|
-
[2] ''
|
|
753
|
-
|
|
754
|
-
[0] ''
|
|
755
|
-
|
|
756
|
-
[1] ''
|
|
757
|
-
"
|
|
758
|
-
`);
|
|
759
|
-
|
|
760
|
-
actions.deleteCell({ cellId: cellId("2") });
|
|
761
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
762
|
-
"
|
|
763
|
-
[0] ''
|
|
764
|
-
|
|
765
|
-
[1] ''
|
|
766
|
-
"
|
|
767
|
-
`);
|
|
768
|
-
|
|
769
|
-
actions.undoDeleteCell();
|
|
770
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
771
|
-
"
|
|
772
|
-
[3] ''
|
|
773
|
-
|
|
774
|
-
[0] ''
|
|
775
|
-
|
|
776
|
-
[1] ''
|
|
777
|
-
"
|
|
778
|
-
`);
|
|
779
|
-
|
|
780
|
-
actions.undoDeleteCell();
|
|
781
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
782
|
-
"
|
|
783
|
-
[0] ''
|
|
784
|
-
|
|
785
|
-
[1] ''
|
|
786
|
-
|
|
787
|
-
[3] ''
|
|
788
|
-
"
|
|
789
|
-
`);
|
|
790
|
-
});
|
|
791
|
-
|
|
792
238
|
it("keeps error evidence after an edit and a new queued run", () => {
|
|
793
239
|
const code = "raise ValueError('old failure')";
|
|
794
240
|
const output: OutputMessage = {
|
|
@@ -820,11 +266,7 @@ describe("cell reducer", () => {
|
|
|
820
266
|
stale_inputs: null,
|
|
821
267
|
timestamp: 1,
|
|
822
268
|
});
|
|
823
|
-
|
|
824
|
-
cellId: firstCellId,
|
|
825
|
-
code: "print('new code')",
|
|
826
|
-
formattingChange: false,
|
|
827
|
-
});
|
|
269
|
+
editCode(firstCellId, "print('new code')");
|
|
828
270
|
actions.prepareForRun({ cellId: firstCellId });
|
|
829
271
|
actions.handleCellMessage({
|
|
830
272
|
cell_id: firstCellId,
|
|
@@ -835,7 +277,6 @@ describe("cell reducer", () => {
|
|
|
835
277
|
timestamp: 2,
|
|
836
278
|
});
|
|
837
279
|
const runtime = state.cellRuntime[firstCellId];
|
|
838
|
-
expect(state.cellData[firstCellId].lastCodeRun).toBe("print('new code')");
|
|
839
280
|
expect(runtime.output).toEqual(output);
|
|
840
281
|
expect(runtime.consoleOutputs[0].code).toBe(code);
|
|
841
282
|
expect(runtime.consoleOutputs[0].data).toContain("old failure");
|
|
@@ -846,21 +287,11 @@ describe("cell reducer", () => {
|
|
|
846
287
|
/////////////////
|
|
847
288
|
// Initial state
|
|
848
289
|
let cell = cells[0];
|
|
849
|
-
expect(cell.status).toBe("idle");
|
|
850
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
851
|
-
expect(cell.edited).toBe(false);
|
|
852
290
|
expect(cell).toMatchSnapshot("initial state");
|
|
853
291
|
|
|
854
292
|
// Update code
|
|
855
|
-
|
|
856
|
-
cellId: firstCellId,
|
|
857
|
-
code: "import marimo as mo",
|
|
858
|
-
formattingChange: false,
|
|
859
|
-
});
|
|
293
|
+
editCode(firstCellId, "import marimo as mo");
|
|
860
294
|
cell = cells[0];
|
|
861
|
-
expect(cell.status).toBe("idle");
|
|
862
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
863
|
-
expect(cell.edited).toBe(true);
|
|
864
295
|
expect(cell).toMatchSnapshot("code edited");
|
|
865
296
|
|
|
866
297
|
// Prepare for run
|
|
@@ -868,9 +299,6 @@ describe("cell reducer", () => {
|
|
|
868
299
|
cellId: firstCellId,
|
|
869
300
|
});
|
|
870
301
|
cell = cells[0];
|
|
871
|
-
expect(cell.status).toBe("queued");
|
|
872
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
873
|
-
expect(cell.edited).toBe(false);
|
|
874
302
|
expect(cell).toMatchSnapshot("prepared for run");
|
|
875
303
|
|
|
876
304
|
// Receive queued messages
|
|
@@ -883,12 +311,6 @@ describe("cell reducer", () => {
|
|
|
883
311
|
timestamp: new Date(10).getTime() as Seconds,
|
|
884
312
|
});
|
|
885
313
|
cell = cells[0];
|
|
886
|
-
expect(cell.status).toBe("queued");
|
|
887
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
888
|
-
expect(cell.edited).toBe(false);
|
|
889
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
890
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
891
|
-
expect(cell.lastRunStartTimestamp).toBe(null);
|
|
892
314
|
expect(cell).toMatchSnapshot("queued message received");
|
|
893
315
|
|
|
894
316
|
// Receive running messages
|
|
@@ -901,12 +323,6 @@ describe("cell reducer", () => {
|
|
|
901
323
|
timestamp: new Date(20).getTime() as Seconds,
|
|
902
324
|
});
|
|
903
325
|
cell = cells[0];
|
|
904
|
-
expect(cell.status).toBe("running");
|
|
905
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
906
|
-
expect(cell.edited).toBe(false);
|
|
907
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
908
|
-
expect(cell.runStartTimestamp).toBe(20);
|
|
909
|
-
expect(cell.lastRunStartTimestamp).toBe(20);
|
|
910
326
|
expect(cell).toMatchSnapshot("running message received");
|
|
911
327
|
|
|
912
328
|
// Console messages shouldn't transition status
|
|
@@ -924,11 +340,6 @@ describe("cell reducer", () => {
|
|
|
924
340
|
timestamp: new Date(22).getTime() as Seconds,
|
|
925
341
|
});
|
|
926
342
|
cell = cells[0];
|
|
927
|
-
expect(cell.status).toBe("running");
|
|
928
|
-
expect(cell.edited).toBe(false);
|
|
929
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
930
|
-
expect(cell.runStartTimestamp).toBe(20);
|
|
931
|
-
expect(cell.lastRunStartTimestamp).toBe(20);
|
|
932
343
|
expect(cell).toMatchSnapshot("console message received");
|
|
933
344
|
|
|
934
345
|
// Receive output messages
|
|
@@ -951,46 +362,23 @@ describe("cell reducer", () => {
|
|
|
951
362
|
timestamp: new Date(33).getTime() as Seconds,
|
|
952
363
|
});
|
|
953
364
|
cell = cells[0];
|
|
954
|
-
expect(cell.status).toBe("idle");
|
|
955
|
-
expect(cell.edited).toBe(false);
|
|
956
|
-
expect(cell.runElapsedTimeMs).toBe(13_000);
|
|
957
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
958
|
-
expect(cell.lastRunStartTimestamp).toBe(20);
|
|
959
365
|
expect(cell).toMatchSnapshot("output received");
|
|
960
366
|
|
|
961
367
|
// EDITING BACK AND FORTH
|
|
962
368
|
/////////////////
|
|
963
369
|
// Update code again
|
|
964
|
-
|
|
965
|
-
cellId: firstCellId,
|
|
966
|
-
code: "import marimo as mo\nimport numpy",
|
|
967
|
-
formattingChange: false,
|
|
968
|
-
});
|
|
370
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
969
371
|
cell = cells[0];
|
|
970
|
-
expect(cell.status).toBe("idle");
|
|
971
|
-
expect(cell.edited).toBe(true);
|
|
972
372
|
expect(cell).toMatchSnapshot("code edited again");
|
|
973
373
|
|
|
974
374
|
// Update code should be unedited
|
|
975
|
-
|
|
976
|
-
cellId: firstCellId,
|
|
977
|
-
code: "import marimo as mo",
|
|
978
|
-
formattingChange: false,
|
|
979
|
-
});
|
|
375
|
+
editCode(firstCellId, "import marimo as mo");
|
|
980
376
|
cell = cells[0];
|
|
981
|
-
expect(cell.edited).toBe(false);
|
|
982
377
|
expect(cell).toMatchSnapshot("code reverted");
|
|
983
378
|
|
|
984
379
|
// Update code should be edited again
|
|
985
|
-
|
|
986
|
-
cellId: firstCellId,
|
|
987
|
-
code: "import marimo as mo\nimport numpy",
|
|
988
|
-
formattingChange: false,
|
|
989
|
-
});
|
|
380
|
+
editCode(firstCellId, "import marimo as mo\nimport numpy");
|
|
990
381
|
cell = cells[0];
|
|
991
|
-
expect(cell.status).toBe("idle");
|
|
992
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
993
|
-
expect(cell.edited).toBe(true);
|
|
994
382
|
expect(cell).toMatchSnapshot("code changed after revert");
|
|
995
383
|
|
|
996
384
|
// ERROR RESPONSE
|
|
@@ -1041,11 +429,6 @@ describe("cell reducer", () => {
|
|
|
1041
429
|
});
|
|
1042
430
|
cell = cells[0];
|
|
1043
431
|
expect(cell.output).not.toBe(null); // keep old output
|
|
1044
|
-
expect(cell.status).toBe("idle");
|
|
1045
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo\nimport numpy");
|
|
1046
|
-
expect(cell.edited).toBe(false);
|
|
1047
|
-
expect(cell.runElapsedTimeMs).toBe(11_000);
|
|
1048
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1049
432
|
expect(cell).toMatchSnapshot("error response received");
|
|
1050
433
|
|
|
1051
434
|
// INTERRUPT RESPONSE
|
|
@@ -1093,96 +476,20 @@ describe("cell reducer", () => {
|
|
|
1093
476
|
timestamp: new Date(61).getTime() as Seconds,
|
|
1094
477
|
});
|
|
1095
478
|
cell = cells[0];
|
|
1096
|
-
expect(cell.status).toBe("idle");
|
|
1097
|
-
expect(cell.lastCodeRun).toBe(cell.code);
|
|
1098
|
-
expect(cell.edited).toBe(false);
|
|
1099
|
-
expect(cell.runElapsedTimeMs).toBe(11_000);
|
|
1100
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1101
479
|
expect(cell).toMatchSnapshot("interruption response received");
|
|
1102
480
|
});
|
|
1103
481
|
|
|
1104
|
-
it("errors reset status to idle", () => {
|
|
1105
|
-
// Initial state
|
|
1106
|
-
let cell = cells[0];
|
|
1107
|
-
expect(cell.status).toBe("idle");
|
|
1108
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
1109
|
-
expect(cell.edited).toBe(false);
|
|
1110
|
-
expect(cell).toMatchSnapshot("initial state");
|
|
1111
|
-
|
|
1112
|
-
// Update code
|
|
1113
|
-
actions.updateCellCode({
|
|
1114
|
-
cellId: firstCellId,
|
|
1115
|
-
code: "import marimo as mo",
|
|
1116
|
-
formattingChange: false,
|
|
1117
|
-
});
|
|
1118
|
-
cell = cells[0];
|
|
1119
|
-
expect(cell.status).toBe("idle");
|
|
1120
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
1121
|
-
expect(cell.edited).toBe(true);
|
|
1122
|
-
expect(cell).toMatchSnapshot("code edited");
|
|
1123
|
-
|
|
1124
|
-
// Prepare for run
|
|
1125
|
-
actions.prepareForRun({
|
|
1126
|
-
cellId: firstCellId,
|
|
1127
|
-
});
|
|
1128
|
-
cell = cells[0];
|
|
1129
|
-
expect(cell.status).toBe("queued");
|
|
1130
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1131
|
-
expect(cell.edited).toBe(false);
|
|
1132
|
-
expect(cell).toMatchSnapshot("prepared for run");
|
|
1133
|
-
|
|
1134
|
-
// ERROR RESPONSE
|
|
1135
|
-
//
|
|
1136
|
-
// should reset status to idle
|
|
1137
|
-
/////////////////
|
|
1138
|
-
// Prepare for run
|
|
1139
|
-
actions.prepareForRun({
|
|
1140
|
-
cellId: firstCellId,
|
|
1141
|
-
});
|
|
1142
|
-
cell = cells[0];
|
|
1143
|
-
// Receive error
|
|
1144
|
-
actions.handleCellMessage({
|
|
1145
|
-
cell_id: firstCellId,
|
|
1146
|
-
output: {
|
|
1147
|
-
channel: "marimo-error",
|
|
1148
|
-
mimetype: "application/vnd.marimo+error",
|
|
1149
|
-
data: [
|
|
1150
|
-
{ type: "exception", exception_type: "SyntaxError", msg: "Oh no!" },
|
|
1151
|
-
],
|
|
1152
|
-
timestamp: 0,
|
|
1153
|
-
},
|
|
1154
|
-
console: null,
|
|
1155
|
-
stale_inputs: null,
|
|
1156
|
-
timestamp: new Date(61).getTime() as Seconds,
|
|
1157
|
-
});
|
|
1158
|
-
cell = cells[0];
|
|
1159
|
-
expect(cell.status).toBe("idle");
|
|
1160
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1161
|
-
expect(cell.edited).toBe(false);
|
|
1162
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
1163
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1164
|
-
expect(cell).toMatchSnapshot("error response received");
|
|
1165
|
-
});
|
|
1166
|
-
|
|
1167
482
|
it("can run a stale cell", () => {
|
|
1168
|
-
// Update code of first cell
|
|
1169
|
-
|
|
1170
|
-
cellId: firstCellId,
|
|
1171
|
-
code: "import marimo as mo",
|
|
1172
|
-
formattingChange: false,
|
|
1173
|
-
});
|
|
483
|
+
// Update code of first cell
|
|
484
|
+
editCode(firstCellId, "import marimo as mo");
|
|
1174
485
|
// Add cell
|
|
1175
|
-
|
|
486
|
+
seedCell({
|
|
1176
487
|
cellId: firstCellId,
|
|
1177
488
|
before: false,
|
|
1178
489
|
});
|
|
1179
490
|
const secondCell = cells[1].id;
|
|
1180
491
|
// Update code
|
|
1181
|
-
|
|
1182
|
-
code: "mo.slider()",
|
|
1183
|
-
cellId: secondCell,
|
|
1184
|
-
formattingChange: false,
|
|
1185
|
-
});
|
|
492
|
+
editCode(secondCell, "mo.slider()");
|
|
1186
493
|
|
|
1187
494
|
// Prepare for run
|
|
1188
495
|
actions.prepareForRun({
|
|
@@ -1199,11 +506,6 @@ describe("cell reducer", () => {
|
|
|
1199
506
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1200
507
|
});
|
|
1201
508
|
let cell = cells[1];
|
|
1202
|
-
expect(cell.status).toBe("queued");
|
|
1203
|
-
expect(cell.lastCodeRun).toBe("mo.slider()");
|
|
1204
|
-
expect(cell.edited).toBe(false);
|
|
1205
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
1206
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1207
509
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1208
510
|
|
|
1209
511
|
// Receive stale message
|
|
@@ -1216,101 +518,12 @@ describe("cell reducer", () => {
|
|
|
1216
518
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1217
519
|
});
|
|
1218
520
|
cell = cells[1];
|
|
1219
|
-
expect(cell.status).toBe("queued");
|
|
1220
|
-
expect(cell.lastCodeRun).toBe("mo.slider()");
|
|
1221
|
-
expect(cell.edited).toBe(false);
|
|
1222
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
1223
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1224
521
|
expect(cell).toMatchSnapshot("stale inputs received");
|
|
1225
522
|
});
|
|
1226
523
|
|
|
1227
|
-
it("can format code and update cell", () => {
|
|
1228
|
-
const firstCellId = cells[0].id;
|
|
1229
|
-
actions.updateCellCode({
|
|
1230
|
-
cellId: firstCellId,
|
|
1231
|
-
code: "import marimo as mo",
|
|
1232
|
-
formattingChange: false,
|
|
1233
|
-
});
|
|
1234
|
-
let cell = cells[0];
|
|
1235
|
-
expect(cell.status).toBe("idle");
|
|
1236
|
-
expect(cell.lastCodeRun).toBe(null);
|
|
1237
|
-
expect(cell.edited).toBe(true);
|
|
1238
|
-
|
|
1239
|
-
// Run
|
|
1240
|
-
actions.prepareForRun({
|
|
1241
|
-
cellId: firstCellId,
|
|
1242
|
-
});
|
|
1243
|
-
actions.handleCellMessage({
|
|
1244
|
-
cell_id: firstCellId,
|
|
1245
|
-
output: {
|
|
1246
|
-
channel: "output",
|
|
1247
|
-
mimetype: "text/plain",
|
|
1248
|
-
data: "ok",
|
|
1249
|
-
timestamp: 0,
|
|
1250
|
-
},
|
|
1251
|
-
console: {
|
|
1252
|
-
channel: "stdout",
|
|
1253
|
-
mimetype: "text/plain",
|
|
1254
|
-
data: "hello!",
|
|
1255
|
-
timestamp: 0,
|
|
1256
|
-
},
|
|
1257
|
-
status: "idle",
|
|
1258
|
-
stale_inputs: null,
|
|
1259
|
-
timestamp: new Date(33).getTime() as Seconds,
|
|
1260
|
-
});
|
|
1261
|
-
|
|
1262
|
-
// Check steady state
|
|
1263
|
-
cell = cells[0];
|
|
1264
|
-
expect(cell.status).toBe("idle");
|
|
1265
|
-
expect(cell.edited).toBe(false);
|
|
1266
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1267
|
-
|
|
1268
|
-
// Format code
|
|
1269
|
-
actions.updateCellCode({
|
|
1270
|
-
cellId: firstCellId,
|
|
1271
|
-
code: "import marimo as mo",
|
|
1272
|
-
formattingChange: true,
|
|
1273
|
-
});
|
|
1274
|
-
cell = cells[0];
|
|
1275
|
-
expect(cell.status).toBe("idle");
|
|
1276
|
-
expect(cell.lastCodeRun).toBe("import marimo as mo");
|
|
1277
|
-
expect(cell.edited).toBe(false);
|
|
1278
|
-
});
|
|
1279
|
-
|
|
1280
|
-
it("can update a cells config", () => {
|
|
1281
|
-
const firstCellId = cells[0].id;
|
|
1282
|
-
let cell = cells[0];
|
|
1283
|
-
// Starts empty
|
|
1284
|
-
expect(cell.config).toEqual({
|
|
1285
|
-
disabled: false,
|
|
1286
|
-
hide_code: false,
|
|
1287
|
-
column: null,
|
|
1288
|
-
expand_output: false,
|
|
1289
|
-
});
|
|
1290
|
-
|
|
1291
|
-
actions.updateCellConfig({
|
|
1292
|
-
cellId: firstCellId,
|
|
1293
|
-
config: { disabled: true },
|
|
1294
|
-
});
|
|
1295
|
-
cell = cells[0];
|
|
1296
|
-
expect(cell.config.disabled).toBe(true);
|
|
1297
|
-
|
|
1298
|
-
// Revert
|
|
1299
|
-
actions.updateCellConfig({
|
|
1300
|
-
cellId: firstCellId,
|
|
1301
|
-
config: { disabled: false },
|
|
1302
|
-
});
|
|
1303
|
-
cell = cells[0];
|
|
1304
|
-
expect(cell.config.disabled).toBe(false);
|
|
1305
|
-
});
|
|
1306
|
-
|
|
1307
524
|
it("can run a stopped cell", () => {
|
|
1308
525
|
// Update code of first cell
|
|
1309
|
-
|
|
1310
|
-
cellId: firstCellId,
|
|
1311
|
-
code: "mo.md('This has an ancestor that was stopped')",
|
|
1312
|
-
formattingChange: false,
|
|
1313
|
-
});
|
|
526
|
+
editCode(firstCellId, "mo.md('This has an ancestor that was stopped')");
|
|
1314
527
|
|
|
1315
528
|
// Prepare for run
|
|
1316
529
|
actions.prepareForRun({
|
|
@@ -1327,13 +540,6 @@ describe("cell reducer", () => {
|
|
|
1327
540
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1328
541
|
});
|
|
1329
542
|
let cell = cells[0];
|
|
1330
|
-
expect(cell.status).toBe("queued");
|
|
1331
|
-
expect(cell.lastCodeRun).toBe(
|
|
1332
|
-
"mo.md('This has an ancestor that was stopped')",
|
|
1333
|
-
);
|
|
1334
|
-
expect(cell.edited).toBe(false);
|
|
1335
|
-
expect(cell.runElapsedTimeMs).toBe(null);
|
|
1336
|
-
expect(cell.runStartTimestamp).toBe(null);
|
|
1337
543
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1338
544
|
|
|
1339
545
|
// Receive idle message
|
|
@@ -1369,12 +575,10 @@ describe("cell reducer", () => {
|
|
|
1369
575
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1370
576
|
});
|
|
1371
577
|
cell = cells[0];
|
|
1372
|
-
expect(cell.status).toBe("idle");
|
|
1373
578
|
// oxlint-disable-next-line typescript/no-explicit-any no-unsafe-optional-chaining
|
|
1374
579
|
expect((cell.output?.data as any)[0].msg).toBe(
|
|
1375
580
|
"This cell wasn't run because an ancestor was stopped with `mo.stop`: ",
|
|
1376
581
|
);
|
|
1377
|
-
expect(cell.stopped).toBe(true);
|
|
1378
582
|
expect(cell).toMatchSnapshot("ancestor stopped");
|
|
1379
583
|
|
|
1380
584
|
// Receive queued message
|
|
@@ -1387,8 +591,6 @@ describe("cell reducer", () => {
|
|
|
1387
591
|
timestamp: new Date(30).getTime() as Seconds,
|
|
1388
592
|
});
|
|
1389
593
|
cell = cells[0];
|
|
1390
|
-
expect(cell.status).toBe("queued");
|
|
1391
|
-
expect(cell.stopped).toBe(true);
|
|
1392
594
|
expect(cell).toMatchSnapshot("queued after stop");
|
|
1393
595
|
|
|
1394
596
|
// Receive running message
|
|
@@ -1401,8 +603,6 @@ describe("cell reducer", () => {
|
|
|
1401
603
|
timestamp: new Date(40).getTime() as Seconds,
|
|
1402
604
|
});
|
|
1403
605
|
cell = cells[0];
|
|
1404
|
-
expect(cell.status).toBe("running");
|
|
1405
|
-
expect(cell.stopped).toBe(false);
|
|
1406
606
|
expect(cell.output).toBe(null);
|
|
1407
607
|
expect(cell).toMatchSnapshot("running after stop");
|
|
1408
608
|
});
|
|
@@ -1429,7 +629,6 @@ describe("cell reducer", () => {
|
|
|
1429
629
|
|
|
1430
630
|
// Initial state
|
|
1431
631
|
let cell = cells[0];
|
|
1432
|
-
expect(cell.status).toBe("idle");
|
|
1433
632
|
expect(cell.consoleOutputs).toEqual([]);
|
|
1434
633
|
expect(cell).toMatchSnapshot("initial state");
|
|
1435
634
|
|
|
@@ -1438,7 +637,6 @@ describe("cell reducer", () => {
|
|
|
1438
637
|
cellId: firstCellId,
|
|
1439
638
|
});
|
|
1440
639
|
cell = cells[0];
|
|
1441
|
-
expect(cell.status).toBe("queued");
|
|
1442
640
|
expect(cell.consoleOutputs).toEqual([]);
|
|
1443
641
|
expect(cell).toMatchSnapshot("prepared for run");
|
|
1444
642
|
|
|
@@ -1452,7 +650,6 @@ describe("cell reducer", () => {
|
|
|
1452
650
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1453
651
|
});
|
|
1454
652
|
cell = cells[0];
|
|
1455
|
-
expect(cell.status).toBe("queued");
|
|
1456
653
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1457
654
|
|
|
1458
655
|
// Receive running messages
|
|
@@ -1465,7 +662,6 @@ describe("cell reducer", () => {
|
|
|
1465
662
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1466
663
|
});
|
|
1467
664
|
cell = cells[0];
|
|
1468
|
-
expect(cell.status).toBe("running");
|
|
1469
665
|
expect(cell).toMatchSnapshot("running message received");
|
|
1470
666
|
|
|
1471
667
|
// Add console
|
|
@@ -1478,12 +674,10 @@ describe("cell reducer", () => {
|
|
|
1478
674
|
timestamp: new Date(22).getTime() as Seconds,
|
|
1479
675
|
});
|
|
1480
676
|
cell = cells[0];
|
|
1481
|
-
expect(cell.status).toBe("running");
|
|
1482
677
|
expect(cell).toMatchSnapshot("stdout received");
|
|
1483
678
|
|
|
1484
679
|
cell = cells[0];
|
|
1485
680
|
expect(cell.consoleOutputs[0]).toMatchObject(STDOUT);
|
|
1486
|
-
expect(cell.status).toBe("running");
|
|
1487
681
|
expect(cell).toMatchSnapshot("stdout verified");
|
|
1488
682
|
|
|
1489
683
|
// Ask via stdin
|
|
@@ -1498,7 +692,6 @@ describe("cell reducer", () => {
|
|
|
1498
692
|
cell = cells[0];
|
|
1499
693
|
expect(cell.consoleOutputs[0]).toMatchObject(STDOUT);
|
|
1500
694
|
expect(cell.consoleOutputs[1]).toMatchObject(STD_IN_1);
|
|
1501
|
-
expect(cell.status).toBe("running");
|
|
1502
695
|
expect(cell).toMatchSnapshot("stdin requested");
|
|
1503
696
|
|
|
1504
697
|
// Response to stdin
|
|
@@ -1512,7 +705,6 @@ describe("cell reducer", () => {
|
|
|
1512
705
|
...STD_IN_1,
|
|
1513
706
|
response: "Marimo!",
|
|
1514
707
|
});
|
|
1515
|
-
expect(cell.status).toBe("running");
|
|
1516
708
|
expect(cell).toMatchSnapshot("stdin answered");
|
|
1517
709
|
|
|
1518
710
|
// Ask via stdin, again
|
|
@@ -1530,7 +722,6 @@ describe("cell reducer", () => {
|
|
|
1530
722
|
{ ...STD_IN_1, response: "Marimo!" },
|
|
1531
723
|
STD_IN_2,
|
|
1532
724
|
]);
|
|
1533
|
-
expect(cell.status).toBe("running");
|
|
1534
725
|
expect(cell).toMatchSnapshot("second stdin requested");
|
|
1535
726
|
|
|
1536
727
|
// Interrupt, so we respond with ""
|
|
@@ -1548,7 +739,6 @@ describe("cell reducer", () => {
|
|
|
1548
739
|
timestamp: new Date(61).getTime() as Seconds,
|
|
1549
740
|
});
|
|
1550
741
|
cell = cells[0];
|
|
1551
|
-
expect(cell.status).toBe("idle");
|
|
1552
742
|
expect(cell.consoleOutputs).toMatchObject([
|
|
1553
743
|
STDOUT,
|
|
1554
744
|
{ ...STD_IN_1, response: "Marimo!" },
|
|
@@ -1579,7 +769,9 @@ describe("cell reducer", () => {
|
|
|
1579
769
|
response: secret,
|
|
1580
770
|
});
|
|
1581
771
|
|
|
1582
|
-
expect(cells[0].consoleOutputs).toEqual([
|
|
772
|
+
expect(cells[0].consoleOutputs).toEqual([
|
|
773
|
+
{ ...prompt, id: 1, response: "" },
|
|
774
|
+
]);
|
|
1583
775
|
expect(JSON.stringify(cells[0].consoleOutputs)).not.toContain(secret);
|
|
1584
776
|
});
|
|
1585
777
|
|
|
@@ -1640,7 +832,6 @@ describe("cell reducer", () => {
|
|
|
1640
832
|
|
|
1641
833
|
// Initial state
|
|
1642
834
|
let cell = cells[0];
|
|
1643
|
-
expect(cell.status).toBe("idle");
|
|
1644
835
|
expect(cell.consoleOutputs).toEqual([]);
|
|
1645
836
|
expect(cell).toMatchSnapshot("initial state");
|
|
1646
837
|
|
|
@@ -1659,7 +850,6 @@ describe("cell reducer", () => {
|
|
|
1659
850
|
cellId: firstCellId,
|
|
1660
851
|
});
|
|
1661
852
|
cell = cells[0];
|
|
1662
|
-
expect(cell.status).toBe("queued");
|
|
1663
853
|
expect(cell.consoleOutputs).toMatchObject([OLD_STDOUT]); // Old stays there until it starts running
|
|
1664
854
|
expect(cell).toMatchSnapshot("prepared for run");
|
|
1665
855
|
|
|
@@ -1673,7 +863,6 @@ describe("cell reducer", () => {
|
|
|
1673
863
|
timestamp: new Date(10).getTime() as Seconds,
|
|
1674
864
|
});
|
|
1675
865
|
cell = cells[0];
|
|
1676
|
-
expect(cell.status).toBe("queued");
|
|
1677
866
|
expect(cell.consoleOutputs).toMatchObject([OLD_STDOUT]); // Old stays there until it starts running
|
|
1678
867
|
expect(cell).toMatchSnapshot("queued message received");
|
|
1679
868
|
|
|
@@ -1687,7 +876,6 @@ describe("cell reducer", () => {
|
|
|
1687
876
|
timestamp: new Date(20).getTime() as Seconds,
|
|
1688
877
|
});
|
|
1689
878
|
cell = cells[0];
|
|
1690
|
-
expect(cell.status).toBe("running");
|
|
1691
879
|
expect(cell.consoleOutputs).toMatchObject([]);
|
|
1692
880
|
expect(cell).toMatchSnapshot("console cleared while running");
|
|
1693
881
|
|
|
@@ -1697,610 +885,39 @@ describe("cell reducer", () => {
|
|
|
1697
885
|
output: undefined,
|
|
1698
886
|
console: [STDOUT],
|
|
1699
887
|
status: "idle",
|
|
1700
|
-
stale_inputs: null,
|
|
1701
|
-
timestamp: new Date(22).getTime() as Seconds,
|
|
1702
|
-
});
|
|
1703
|
-
cell = cells[0];
|
|
1704
|
-
expect(cell.consoleOutputs).toMatchObject([STDOUT]);
|
|
1705
|
-
expect(cell
|
|
1706
|
-
expect(cell).toMatchSnapshot("console received while idle");
|
|
1707
|
-
});
|
|
1708
|
-
|
|
1709
|
-
it("can send a cell to the top", () => {
|
|
1710
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1711
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1712
|
-
actions.sendToTop({ cellId: cellId("2") });
|
|
1713
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
1714
|
-
"
|
|
1715
|
-
[2] ''
|
|
1716
|
-
|
|
1717
|
-
[0] ''
|
|
1718
|
-
|
|
1719
|
-
[1] ''
|
|
1720
|
-
"
|
|
1721
|
-
`);
|
|
1722
|
-
});
|
|
1723
|
-
|
|
1724
|
-
it("can send a cell to the bottom", () => {
|
|
1725
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1726
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1727
|
-
actions.sendToBottom({ cellId: firstCellId });
|
|
1728
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
1729
|
-
"
|
|
1730
|
-
[1] ''
|
|
1731
|
-
|
|
1732
|
-
[2] ''
|
|
1733
|
-
|
|
1734
|
-
[0] ''
|
|
1735
|
-
"
|
|
1736
|
-
`);
|
|
1737
|
-
});
|
|
1738
|
-
|
|
1739
|
-
it("can focus cells", () => {
|
|
1740
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1741
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1742
|
-
|
|
1743
|
-
actions.focusCell({ cellId: cellId("1"), where: "before" });
|
|
1744
|
-
expect(focusAndScrollCellIntoView).toHaveBeenCalledWith(
|
|
1745
|
-
expect.objectContaining({
|
|
1746
|
-
cellId: cellId("0"),
|
|
1747
|
-
}),
|
|
1748
|
-
);
|
|
1749
|
-
|
|
1750
|
-
actions.focusTopCell();
|
|
1751
|
-
expect(scrollToTop).toHaveBeenCalled();
|
|
1752
|
-
|
|
1753
|
-
actions.focusBottomCell();
|
|
1754
|
-
expect(scrollToBottom).toHaveBeenCalled();
|
|
1755
|
-
});
|
|
1756
|
-
|
|
1757
|
-
it("can update cell name", () => {
|
|
1758
|
-
actions.updateCellName({ cellId: firstCellId, name: "Test Cell" });
|
|
1759
|
-
expect(state.cellData[firstCellId].name).toBe("Test Cell");
|
|
1760
|
-
});
|
|
1761
|
-
|
|
1762
|
-
it("can set cell IDs and codes", () => {
|
|
1763
|
-
const newIds = [cellId("3"), cellId("4"), cellId("5")];
|
|
1764
|
-
const newCodes = ["code1", "code2", "code3"];
|
|
1765
|
-
|
|
1766
|
-
actions.setCellIds({ cellIds: newIds });
|
|
1767
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual(newIds);
|
|
1768
|
-
|
|
1769
|
-
// When codeIsStale is false, lastCodeRun should match code
|
|
1770
|
-
actions.setCellCodes({
|
|
1771
|
-
codes: newCodes,
|
|
1772
|
-
ids: newIds,
|
|
1773
|
-
codeIsStale: false,
|
|
1774
|
-
});
|
|
1775
|
-
newIds.forEach((id, index) => {
|
|
1776
|
-
expect(state.cellData[id].code).toBe(newCodes[index]);
|
|
1777
|
-
expect(state.cellData[id].lastCodeRun).toBe(newCodes[index]);
|
|
1778
|
-
expect(state.cellData[id].edited).toBe(false);
|
|
1779
|
-
});
|
|
1780
|
-
|
|
1781
|
-
// When codeIsStale is true, lastCodeRun should not change
|
|
1782
|
-
const staleCodes = ["stale1", "stale2", "stale3"];
|
|
1783
|
-
actions.setCellCodes({
|
|
1784
|
-
codes: staleCodes,
|
|
1785
|
-
ids: newIds,
|
|
1786
|
-
codeIsStale: true,
|
|
1787
|
-
});
|
|
1788
|
-
newIds.forEach((id, index) => {
|
|
1789
|
-
expect(state.cellData[id].code).toBe(staleCodes[index]);
|
|
1790
|
-
expect(state.cellData[id].lastCodeRun).toBe(newCodes[index]);
|
|
1791
|
-
expect(state.cellData[id].edited).toBe(true);
|
|
1792
|
-
});
|
|
1793
|
-
});
|
|
1794
|
-
|
|
1795
|
-
it("can can add a new cell with/without stale code", () => {
|
|
1796
|
-
actions.setCellCodes({
|
|
1797
|
-
codes: ["new code"],
|
|
1798
|
-
ids: [cellId("2")],
|
|
1799
|
-
codeIsStale: false,
|
|
1800
|
-
});
|
|
1801
|
-
|
|
1802
|
-
expect(state.cellData[cellId("2")].code).toBe("new code");
|
|
1803
|
-
expect(state.cellData[cellId("2")].edited).toBe(false);
|
|
1804
|
-
expect(state.cellData[cellId("2")].lastCodeRun).toBe("new code");
|
|
1805
|
-
|
|
1806
|
-
actions.setCellCodes({
|
|
1807
|
-
codes: ["new code 2"],
|
|
1808
|
-
ids: [cellId("9")],
|
|
1809
|
-
codeIsStale: true,
|
|
1810
|
-
});
|
|
1811
|
-
|
|
1812
|
-
expect(state.cellData[cellId("9")].code).toBe("new code 2");
|
|
1813
|
-
expect(state.cellData[cellId("9")].edited).toBe(true);
|
|
1814
|
-
expect(state.cellData[cellId("9")].lastCodeRun).toBe(null);
|
|
1815
|
-
});
|
|
1816
|
-
|
|
1817
|
-
it("can partial update cell codes", () => {
|
|
1818
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1819
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
1820
|
-
|
|
1821
|
-
expect(state.cellIds.inOrderIds).toEqual([
|
|
1822
|
-
cellId("0"),
|
|
1823
|
-
cellId("1"),
|
|
1824
|
-
cellId("2"),
|
|
1825
|
-
]);
|
|
1826
|
-
expect(state.cellData[cellId("0")].code).toBe("");
|
|
1827
|
-
expect(state.cellData[cellId("1")].code).toBe("");
|
|
1828
|
-
expect(state.cellData[cellId("2")].code).toBe("");
|
|
1829
|
-
|
|
1830
|
-
// Update cell 1
|
|
1831
|
-
actions.setCellCodes({
|
|
1832
|
-
codes: ["new code 2"],
|
|
1833
|
-
ids: [cellId("1")],
|
|
1834
|
-
codeIsStale: false,
|
|
1835
|
-
});
|
|
1836
|
-
|
|
1837
|
-
expect(state.cellIds.inOrderIds).toEqual([
|
|
1838
|
-
cellId("0"),
|
|
1839
|
-
cellId("1"),
|
|
1840
|
-
cellId("2"),
|
|
1841
|
-
]);
|
|
1842
|
-
expect(state.cellData[cellId("0")].code).toBe("");
|
|
1843
|
-
expect(state.cellData[cellId("1")].code).toBe("new code 2");
|
|
1844
|
-
expect(state.cellData[cellId("1")].edited).toBe(false);
|
|
1845
|
-
expect(state.cellData[cellId("2")].code).toBe("");
|
|
1846
|
-
});
|
|
1847
|
-
|
|
1848
|
-
it("can set cell codes with new cell ids, while preserving the old cell data", () => {
|
|
1849
|
-
actions.setCellCodes({
|
|
1850
|
-
codes: ["code1", "code2", "code3"],
|
|
1851
|
-
ids: [cellId("3"), cellId("4"), cellId("5")],
|
|
1852
|
-
codeIsStale: false,
|
|
1853
|
-
});
|
|
1854
|
-
expect(state.cellData[cellId("3")].code).toBe("code1");
|
|
1855
|
-
expect(state.cellData[cellId("4")].code).toBe("code2");
|
|
1856
|
-
expect(state.cellData[cellId("5")].code).toBe("code3");
|
|
1857
|
-
|
|
1858
|
-
// Update with some new cell ids and some old cell ids
|
|
1859
|
-
actions.setCellIds({
|
|
1860
|
-
cellIds: [cellId("1"), cellId("2"), cellId("3"), cellId("4")],
|
|
1861
|
-
});
|
|
1862
|
-
actions.setCellCodes({
|
|
1863
|
-
codes: ["new1", "new2", "code1", "code2"],
|
|
1864
|
-
ids: [cellId("1"), cellId("2"), cellId("3"), cellId("4")],
|
|
1865
|
-
codeIsStale: false,
|
|
1866
|
-
});
|
|
1867
|
-
expect(state.cellData[cellId("1")].code).toBe("new1");
|
|
1868
|
-
expect(state.cellData[cellId("2")].code).toBe("new2");
|
|
1869
|
-
expect(state.cellData[cellId("3")].code).toBe("code1");
|
|
1870
|
-
expect(state.cellData[cellId("4")].code).toBe("code2");
|
|
1871
|
-
expect(state.cellIds.inOrderIds).toEqual([
|
|
1872
|
-
cellId("1"),
|
|
1873
|
-
cellId("2"),
|
|
1874
|
-
cellId("3"),
|
|
1875
|
-
cellId("4"),
|
|
1876
|
-
]);
|
|
1877
|
-
// Cell 5 data is preserved (possibly used for tracing), but it's not in the cellIds
|
|
1878
|
-
expect(state.cellData[cellId("5")]).not.toBeUndefined();
|
|
1879
|
-
});
|
|
1880
|
-
|
|
1881
|
-
it("can set cell codes with names and configs", () => {
|
|
1882
|
-
const newIds = [cellId("3"), cellId("4")];
|
|
1883
|
-
actions.setCellIds({ cellIds: newIds });
|
|
1884
|
-
actions.setCellCodes({
|
|
1885
|
-
codes: ["code1", "code2"],
|
|
1886
|
-
ids: newIds,
|
|
1887
|
-
codeIsStale: false,
|
|
1888
|
-
names: ["setup_cell", "analysis"],
|
|
1889
|
-
configs: [
|
|
1890
|
-
{ hide_code: true, disabled: false, column: null },
|
|
1891
|
-
{ hide_code: false, disabled: true, column: null },
|
|
1892
|
-
],
|
|
1893
|
-
});
|
|
1894
|
-
|
|
1895
|
-
expect(state.cellData[cellId("3")].name).toBe("setup_cell");
|
|
1896
|
-
expect(state.cellData[cellId("3")].config.hide_code).toBe(true);
|
|
1897
|
-
expect(state.cellData[cellId("3")].config.disabled).toBe(false);
|
|
1898
|
-
|
|
1899
|
-
expect(state.cellData[cellId("4")].name).toBe("analysis");
|
|
1900
|
-
expect(state.cellData[cellId("4")].config.hide_code).toBe(false);
|
|
1901
|
-
expect(state.cellData[cellId("4")].config.disabled).toBe(true);
|
|
1902
|
-
});
|
|
1903
|
-
|
|
1904
|
-
it("can set cell codes without names/configs (backward compat)", () => {
|
|
1905
|
-
const newIds = [cellId("3")];
|
|
1906
|
-
actions.setCellIds({ cellIds: newIds });
|
|
1907
|
-
actions.setCellCodes({
|
|
1908
|
-
codes: ["code1"],
|
|
1909
|
-
ids: newIds,
|
|
1910
|
-
codeIsStale: false,
|
|
1911
|
-
});
|
|
1912
|
-
|
|
1913
|
-
// Should use defaults when names/configs not provided
|
|
1914
|
-
expect(state.cellData[cellId("3")].code).toBe("code1");
|
|
1915
|
-
expect(state.cellData[cellId("3")].config.hide_code).toBe(false);
|
|
1916
|
-
expect(state.cellData[cellId("3")].config.disabled).toBe(false);
|
|
1917
|
-
});
|
|
1918
|
-
|
|
1919
|
-
it("can update names and configs on existing cells via setCellCodes", () => {
|
|
1920
|
-
// Set initial state
|
|
1921
|
-
actions.setCellCodes({
|
|
1922
|
-
codes: ["x = 1"],
|
|
1923
|
-
ids: [firstCellId],
|
|
1924
|
-
codeIsStale: false,
|
|
1925
|
-
names: ["old_name"],
|
|
1926
|
-
configs: [{ hide_code: false, disabled: false, column: null }],
|
|
1927
|
-
});
|
|
1928
|
-
expect(state.cellData[firstCellId].name).toBe("old_name");
|
|
1929
|
-
expect(state.cellData[firstCellId].config.hide_code).toBe(false);
|
|
1930
|
-
|
|
1931
|
-
// Update with new name and config (same code)
|
|
1932
|
-
actions.setCellCodes({
|
|
1933
|
-
codes: ["x = 1"],
|
|
1934
|
-
ids: [firstCellId],
|
|
1935
|
-
codeIsStale: true,
|
|
1936
|
-
names: ["new_name"],
|
|
1937
|
-
configs: [{ hide_code: true, disabled: false, column: null }],
|
|
1938
|
-
});
|
|
1939
|
-
expect(state.cellData[firstCellId].name).toBe("new_name");
|
|
1940
|
-
expect(state.cellData[firstCellId].config.hide_code).toBe(true);
|
|
1941
|
-
});
|
|
1942
|
-
|
|
1943
|
-
it("can fold and unfold all cells", () => {
|
|
1944
|
-
actions.foldAll();
|
|
1945
|
-
expect(foldAllBulk).toHaveBeenCalled();
|
|
1946
|
-
|
|
1947
|
-
actions.unfoldAll();
|
|
1948
|
-
expect(unfoldAllBulk).toHaveBeenCalled();
|
|
1949
|
-
});
|
|
1950
|
-
|
|
1951
|
-
it("can clear logs", () => {
|
|
1952
|
-
state.cellLogs = [
|
|
1953
|
-
{
|
|
1954
|
-
level: "stderr",
|
|
1955
|
-
message: "log1",
|
|
1956
|
-
timestamp: 0,
|
|
1957
|
-
source: { type: "cell", cellId: firstCellId },
|
|
1958
|
-
},
|
|
1959
|
-
{
|
|
1960
|
-
level: "stderr",
|
|
1961
|
-
message: "log1",
|
|
1962
|
-
timestamp: 0,
|
|
1963
|
-
source: { type: "cell", cellId: firstCellId },
|
|
1964
|
-
},
|
|
1965
|
-
];
|
|
1966
|
-
actions.clearLogs();
|
|
1967
|
-
expect(state.cellLogs).toEqual([]);
|
|
1968
|
-
});
|
|
1969
|
-
|
|
1970
|
-
it("can collapse and expand cells", () => {
|
|
1971
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
1972
|
-
actions.createNewCell({
|
|
1973
|
-
cellId: cellId("1"),
|
|
1974
|
-
before: false,
|
|
1975
|
-
code: "# Header",
|
|
1976
|
-
});
|
|
1977
|
-
actions.createNewCell({
|
|
1978
|
-
cellId: cellId("2"),
|
|
1979
|
-
before: false,
|
|
1980
|
-
code: "## Subheader",
|
|
1981
|
-
});
|
|
1982
|
-
|
|
1983
|
-
const id = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
1984
|
-
state.cellRuntime[id] = {
|
|
1985
|
-
...state.cellRuntime[id],
|
|
1986
|
-
outline: {
|
|
1987
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
1988
|
-
},
|
|
1989
|
-
};
|
|
1990
|
-
|
|
1991
|
-
actions.collapseCell({ cellId: id });
|
|
1992
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(id)).toBe(true);
|
|
1993
|
-
|
|
1994
|
-
actions.expandCell({ cellId: id });
|
|
1995
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(id)).toBe(false);
|
|
1996
|
-
});
|
|
1997
|
-
|
|
1998
|
-
it("can collapse and expand all cells in multiple columns", () => {
|
|
1999
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2000
|
-
actions.createNewCell({
|
|
2001
|
-
cellId: cellId("1"),
|
|
2002
|
-
before: false,
|
|
2003
|
-
code: "# First Column Header",
|
|
2004
|
-
});
|
|
2005
|
-
actions.createNewCell({
|
|
2006
|
-
cellId: cellId("2"),
|
|
2007
|
-
before: false,
|
|
2008
|
-
code: "## First Column Subheader",
|
|
2009
|
-
});
|
|
2010
|
-
|
|
2011
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2012
|
-
|
|
2013
|
-
actions.createNewCell({
|
|
2014
|
-
cellId: cellId("3"),
|
|
2015
|
-
before: false,
|
|
2016
|
-
code: "# Second Column Header",
|
|
2017
|
-
});
|
|
2018
|
-
actions.createNewCell({
|
|
2019
|
-
cellId: cellId("4"),
|
|
2020
|
-
before: false,
|
|
2021
|
-
code: "## Second Column Subheader",
|
|
2022
|
-
});
|
|
2023
|
-
|
|
2024
|
-
const firstColumnHeaderId = state.cellIds
|
|
2025
|
-
.atOrThrow(FIRST_COLUMN)
|
|
2026
|
-
.atOrThrow(1);
|
|
2027
|
-
state.cellRuntime[firstColumnHeaderId] = {
|
|
2028
|
-
...state.cellRuntime[firstColumnHeaderId],
|
|
2029
|
-
outline: {
|
|
2030
|
-
items: [
|
|
2031
|
-
{ name: "First Column Header", level: 1, by: { id: "header" } },
|
|
2032
|
-
],
|
|
2033
|
-
},
|
|
2034
|
-
};
|
|
2035
|
-
|
|
2036
|
-
const secondColumnHeaderId = state.cellIds
|
|
2037
|
-
.atOrThrow(SECOND_COLUMN)
|
|
2038
|
-
.atOrThrow(0);
|
|
2039
|
-
state.cellRuntime[secondColumnHeaderId] = {
|
|
2040
|
-
...state.cellRuntime[secondColumnHeaderId],
|
|
2041
|
-
outline: {
|
|
2042
|
-
items: [
|
|
2043
|
-
{ name: "Second Column Header", level: 1, by: { id: "header" } },
|
|
2044
|
-
],
|
|
2045
|
-
},
|
|
2046
|
-
};
|
|
2047
|
-
|
|
2048
|
-
actions.collapseAllCells();
|
|
2049
|
-
expect(
|
|
2050
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(firstColumnHeaderId),
|
|
2051
|
-
).toBe(true);
|
|
2052
|
-
expect(
|
|
2053
|
-
state.cellIds.atOrThrow(SECOND_COLUMN).isCollapsed(secondColumnHeaderId),
|
|
2054
|
-
).toBe(true);
|
|
2055
|
-
|
|
2056
|
-
actions.expandAllCells();
|
|
2057
|
-
expect(
|
|
2058
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(firstColumnHeaderId),
|
|
2059
|
-
).toBe(false);
|
|
2060
|
-
expect(
|
|
2061
|
-
state.cellIds.atOrThrow(SECOND_COLUMN).isCollapsed(secondColumnHeaderId),
|
|
2062
|
-
).toBe(false);
|
|
2063
|
-
});
|
|
2064
|
-
|
|
2065
|
-
it("can collapse and expand nested cells in one call", () => {
|
|
2066
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2067
|
-
actions.createNewCell({
|
|
2068
|
-
cellId: cellId("1"),
|
|
2069
|
-
before: false,
|
|
2070
|
-
code: "# Header",
|
|
2071
|
-
});
|
|
2072
|
-
actions.createNewCell({
|
|
2073
|
-
cellId: cellId("2"),
|
|
2074
|
-
before: false,
|
|
2075
|
-
code: "## Subheader",
|
|
2076
|
-
});
|
|
2077
|
-
actions.createNewCell({
|
|
2078
|
-
cellId: cellId("3"),
|
|
2079
|
-
before: false,
|
|
2080
|
-
code: "### Subsubheader",
|
|
2081
|
-
});
|
|
2082
|
-
|
|
2083
|
-
const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
2084
|
-
state.cellRuntime[headerId] = {
|
|
2085
|
-
...state.cellRuntime[headerId],
|
|
2086
|
-
outline: {
|
|
2087
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
2088
|
-
},
|
|
2089
|
-
};
|
|
2090
|
-
|
|
2091
|
-
const subheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
|
|
2092
|
-
state.cellRuntime[subheaderId] = {
|
|
2093
|
-
...state.cellRuntime[subheaderId],
|
|
2094
|
-
outline: {
|
|
2095
|
-
items: [{ name: "Subheader", level: 2, by: { id: "subheader" } }],
|
|
2096
|
-
},
|
|
2097
|
-
};
|
|
2098
|
-
|
|
2099
|
-
// Check if both the parent and child are collapsed
|
|
2100
|
-
actions.collapseAllCells();
|
|
2101
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2102
|
-
true,
|
|
2103
|
-
);
|
|
2104
|
-
actions.expandCell({ cellId: headerId });
|
|
2105
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
|
|
2106
|
-
true,
|
|
2107
|
-
);
|
|
2108
|
-
actions.collapseCell({ cellId: headerId });
|
|
2109
|
-
|
|
2110
|
-
// Check if both the parent and child are expanded
|
|
2111
|
-
actions.expandAllCells();
|
|
2112
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2113
|
-
false,
|
|
2114
|
-
);
|
|
2115
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
|
|
2116
|
-
false,
|
|
2117
|
-
);
|
|
2118
|
-
});
|
|
2119
|
-
|
|
2120
|
-
it("can collapse all cells when nested heading ranges end at the same cell", () => {
|
|
2121
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2122
|
-
actions.createNewCell({
|
|
2123
|
-
cellId: cellId("1"),
|
|
2124
|
-
before: false,
|
|
2125
|
-
code: "# Header",
|
|
2126
|
-
});
|
|
2127
|
-
actions.createNewCell({
|
|
2128
|
-
cellId: cellId("2"),
|
|
2129
|
-
before: false,
|
|
2130
|
-
code: "## Subheader",
|
|
2131
|
-
});
|
|
2132
|
-
actions.createNewCell({
|
|
2133
|
-
cellId: cellId("3"),
|
|
2134
|
-
before: false,
|
|
2135
|
-
code: "### Subsubheader",
|
|
2136
|
-
});
|
|
2137
|
-
|
|
2138
|
-
const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
2139
|
-
state.cellRuntime[headerId] = {
|
|
2140
|
-
...state.cellRuntime[headerId],
|
|
2141
|
-
outline: {
|
|
2142
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
2143
|
-
},
|
|
2144
|
-
};
|
|
2145
|
-
|
|
2146
|
-
const subheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
|
|
2147
|
-
state.cellRuntime[subheaderId] = {
|
|
2148
|
-
...state.cellRuntime[subheaderId],
|
|
2149
|
-
outline: {
|
|
2150
|
-
items: [{ name: "Subheader", level: 2, by: { id: "subheader" } }],
|
|
2151
|
-
},
|
|
2152
|
-
};
|
|
2153
|
-
|
|
2154
|
-
const subsubheaderId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(3);
|
|
2155
|
-
state.cellRuntime[subsubheaderId] = {
|
|
2156
|
-
...state.cellRuntime[subsubheaderId],
|
|
2157
|
-
outline: {
|
|
2158
|
-
items: [{ name: "Subsubheader", level: 3, by: { id: "subsubheader" } }],
|
|
2159
|
-
},
|
|
2160
|
-
};
|
|
2161
|
-
|
|
2162
|
-
// The header, subheader and subsubheader ranges all end at the last
|
|
2163
|
-
// cell. Collapsing all used to throw "Node ... not found in tree" and
|
|
2164
|
-
// leave the whole notebook unchanged.
|
|
2165
|
-
actions.collapseAllCells();
|
|
2166
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2167
|
-
true,
|
|
2168
|
-
);
|
|
2169
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual([
|
|
2170
|
-
firstCellId,
|
|
2171
|
-
headerId,
|
|
2172
|
-
]);
|
|
2173
|
-
|
|
2174
|
-
// Each level is nested under its parent, collapsed
|
|
2175
|
-
actions.expandCell({ cellId: headerId });
|
|
2176
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subheaderId)).toBe(
|
|
2177
|
-
true,
|
|
2178
|
-
);
|
|
2179
|
-
actions.expandCell({ cellId: subheaderId });
|
|
2180
|
-
expect(
|
|
2181
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subsubheaderId),
|
|
2182
|
-
).toBe(true);
|
|
2183
|
-
});
|
|
2184
|
-
|
|
2185
|
-
it("can collapse all cells when a deep chain is followed by a later section", () => {
|
|
2186
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2187
|
-
actions.createNewCell({
|
|
2188
|
-
cellId: cellId("1"),
|
|
2189
|
-
before: false,
|
|
2190
|
-
code: "# Header",
|
|
2191
|
-
});
|
|
2192
|
-
actions.createNewCell({
|
|
2193
|
-
cellId: cellId("2"),
|
|
2194
|
-
before: false,
|
|
2195
|
-
code: "## Section A",
|
|
2196
|
-
});
|
|
2197
|
-
actions.createNewCell({
|
|
2198
|
-
cellId: cellId("3"),
|
|
2199
|
-
before: false,
|
|
2200
|
-
code: "### Sub of A",
|
|
2201
|
-
});
|
|
2202
|
-
actions.createNewCell({
|
|
2203
|
-
cellId: cellId("4"),
|
|
2204
|
-
before: false,
|
|
2205
|
-
code: "## Section B",
|
|
2206
|
-
});
|
|
2207
|
-
|
|
2208
|
-
const headerId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(1);
|
|
2209
|
-
state.cellRuntime[headerId] = {
|
|
2210
|
-
...state.cellRuntime[headerId],
|
|
2211
|
-
outline: {
|
|
2212
|
-
items: [{ name: "Header", level: 1, by: { id: "header" } }],
|
|
2213
|
-
},
|
|
2214
|
-
};
|
|
2215
|
-
|
|
2216
|
-
const sectionAId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2);
|
|
2217
|
-
state.cellRuntime[sectionAId] = {
|
|
2218
|
-
...state.cellRuntime[sectionAId],
|
|
2219
|
-
outline: {
|
|
2220
|
-
items: [{ name: "Section A", level: 2, by: { id: "section-a" } }],
|
|
2221
|
-
},
|
|
2222
|
-
};
|
|
2223
|
-
|
|
2224
|
-
const subsectionId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(3);
|
|
2225
|
-
state.cellRuntime[subsectionId] = {
|
|
2226
|
-
...state.cellRuntime[subsectionId],
|
|
2227
|
-
outline: {
|
|
2228
|
-
items: [{ name: "Sub of A", level: 3, by: { id: "sub-of-a" } }],
|
|
2229
|
-
},
|
|
2230
|
-
};
|
|
2231
|
-
|
|
2232
|
-
const sectionBId = state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(4);
|
|
2233
|
-
state.cellRuntime[sectionBId] = {
|
|
2234
|
-
...state.cellRuntime[sectionBId],
|
|
2235
|
-
outline: {
|
|
2236
|
-
items: [{ name: "Section B", level: 2, by: { id: "section-b" } }],
|
|
2237
|
-
},
|
|
2238
|
-
};
|
|
2239
|
-
|
|
2240
|
-
actions.collapseAllCells();
|
|
2241
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(headerId)).toBe(
|
|
2242
|
-
true,
|
|
2243
|
-
);
|
|
2244
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).topLevelIds).toEqual([
|
|
2245
|
-
firstCellId,
|
|
2246
|
-
headerId,
|
|
2247
|
-
]);
|
|
2248
|
-
|
|
2249
|
-
// Both sections sit under the header, collapsed, with the subsection
|
|
2250
|
-
// nested under section A
|
|
2251
|
-
actions.expandCell({ cellId: headerId });
|
|
2252
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(sectionAId)).toBe(
|
|
2253
|
-
true,
|
|
2254
|
-
);
|
|
2255
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(sectionBId)).toBe(
|
|
2256
|
-
true,
|
|
2257
|
-
);
|
|
2258
|
-
actions.expandCell({ cellId: sectionAId });
|
|
2259
|
-
expect(
|
|
2260
|
-
state.cellIds.atOrThrow(FIRST_COLUMN).isCollapsed(subsectionId),
|
|
2261
|
-
).toBe(true);
|
|
888
|
+
stale_inputs: null,
|
|
889
|
+
timestamp: new Date(22).getTime() as Seconds,
|
|
890
|
+
});
|
|
891
|
+
cell = cells[0];
|
|
892
|
+
expect(cell.consoleOutputs).toMatchObject([STDOUT]);
|
|
893
|
+
expect(cell).toMatchSnapshot("console received while idle");
|
|
2262
894
|
});
|
|
2263
895
|
|
|
2264
|
-
it("can
|
|
2265
|
-
actions.
|
|
2266
|
-
|
|
2267
|
-
actions.collapseCell({ cellId: firstCellId });
|
|
896
|
+
it("can fold and unfold all cells", () => {
|
|
897
|
+
actions.foldAll();
|
|
898
|
+
expect(foldAllBulk).toHaveBeenCalled();
|
|
2268
899
|
|
|
2269
|
-
actions.
|
|
2270
|
-
expect(
|
|
2271
|
-
false,
|
|
2272
|
-
);
|
|
900
|
+
actions.unfoldAll();
|
|
901
|
+
expect(unfoldAllBulk).toHaveBeenCalled();
|
|
2273
902
|
});
|
|
2274
903
|
|
|
2275
|
-
it("can
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
actions.
|
|
2291
|
-
expect(state.
|
|
2292
|
-
originalCellCount + 1,
|
|
2293
|
-
);
|
|
2294
|
-
expect(state.cellData[nextCellId].code).toBe("line1");
|
|
2295
|
-
expect(
|
|
2296
|
-
state.cellData[state.cellIds.atOrThrow(FIRST_COLUMN).atOrThrow(2)].code,
|
|
2297
|
-
).toBe("line2");
|
|
2298
|
-
|
|
2299
|
-
actions.undoSplitCell({ cellId: nextCellId, snapshot: "line1\nline2" });
|
|
2300
|
-
expect(state.cellIds.atOrThrow(FIRST_COLUMN).length).toBe(
|
|
2301
|
-
originalCellCount,
|
|
2302
|
-
);
|
|
2303
|
-
expect(state.cellData[nextCellId].code).toBe("line1\nline2");
|
|
904
|
+
it("can clear logs", () => {
|
|
905
|
+
state.cellLogs = [
|
|
906
|
+
{
|
|
907
|
+
level: "stderr",
|
|
908
|
+
message: "log1",
|
|
909
|
+
timestamp: 0,
|
|
910
|
+
source: { type: "cell", cellId: firstCellId },
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
level: "stderr",
|
|
914
|
+
message: "log1",
|
|
915
|
+
timestamp: 0,
|
|
916
|
+
source: { type: "cell", cellId: firstCellId },
|
|
917
|
+
},
|
|
918
|
+
];
|
|
919
|
+
actions.clearLogs();
|
|
920
|
+
expect(state.cellLogs).toEqual([]);
|
|
2304
921
|
});
|
|
2305
922
|
|
|
2306
923
|
it("can handle multiple console outputs", () => {
|
|
@@ -2339,407 +956,6 @@ describe("cell reducer", () => {
|
|
|
2339
956
|
`);
|
|
2340
957
|
});
|
|
2341
958
|
|
|
2342
|
-
it("can add a column breakpoint", () => {
|
|
2343
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2344
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2345
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2346
|
-
|
|
2347
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2348
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2349
|
-
"
|
|
2350
|
-
[0] ''
|
|
2351
|
-
|
|
2352
|
-
[1] ''
|
|
2353
|
-
|
|
2354
|
-
[2] ''
|
|
2355
|
-
|
|
2356
|
-
[3] ''
|
|
2357
|
-
"
|
|
2358
|
-
`);
|
|
2359
|
-
|
|
2360
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2361
|
-
|
|
2362
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2363
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2364
|
-
"
|
|
2365
|
-
> col 0
|
|
2366
|
-
[0] ''
|
|
2367
|
-
|
|
2368
|
-
[1] ''
|
|
2369
|
-
|
|
2370
|
-
> col 1
|
|
2371
|
-
[2] ''
|
|
2372
|
-
|
|
2373
|
-
[3] ''
|
|
2374
|
-
"
|
|
2375
|
-
`);
|
|
2376
|
-
|
|
2377
|
-
// Check that the cells are in the correct columns
|
|
2378
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2379
|
-
cellId("0"),
|
|
2380
|
-
cellId("1"),
|
|
2381
|
-
]);
|
|
2382
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2383
|
-
cellId("2"),
|
|
2384
|
-
cellId("3"),
|
|
2385
|
-
]);
|
|
2386
|
-
});
|
|
2387
|
-
|
|
2388
|
-
it("cannot add a column breakpoint before the first cell", () => {
|
|
2389
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2390
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2391
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2392
|
-
actions.addColumnBreakpoint({ cellId: firstCellId });
|
|
2393
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2394
|
-
});
|
|
2395
|
-
|
|
2396
|
-
it("can delete a column", () => {
|
|
2397
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2398
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2399
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2400
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2401
|
-
|
|
2402
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2403
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2404
|
-
"
|
|
2405
|
-
> col 0
|
|
2406
|
-
[0] ''
|
|
2407
|
-
|
|
2408
|
-
[1] ''
|
|
2409
|
-
|
|
2410
|
-
> col 1
|
|
2411
|
-
[2] ''
|
|
2412
|
-
|
|
2413
|
-
[3] ''
|
|
2414
|
-
"
|
|
2415
|
-
`);
|
|
2416
|
-
|
|
2417
|
-
const columnId = state.cellIds.atOrThrow(0).id;
|
|
2418
|
-
actions.deleteColumn({ columnId: columnId });
|
|
2419
|
-
|
|
2420
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2421
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2422
|
-
"
|
|
2423
|
-
[2] ''
|
|
2424
|
-
|
|
2425
|
-
[3] ''
|
|
2426
|
-
|
|
2427
|
-
[0] ''
|
|
2428
|
-
|
|
2429
|
-
[1] ''
|
|
2430
|
-
"
|
|
2431
|
-
`);
|
|
2432
|
-
|
|
2433
|
-
// Check that all cells are now in the remaining column
|
|
2434
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2435
|
-
"2",
|
|
2436
|
-
"3",
|
|
2437
|
-
"0",
|
|
2438
|
-
"1",
|
|
2439
|
-
]);
|
|
2440
|
-
});
|
|
2441
|
-
|
|
2442
|
-
it("deleting the last column does nothing", () => {
|
|
2443
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2444
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2445
|
-
|
|
2446
|
-
const initialState = { ...state };
|
|
2447
|
-
|
|
2448
|
-
actions.deleteColumn({ columnId: initialState.cellIds.atOrThrow(0).id });
|
|
2449
|
-
|
|
2450
|
-
// State should not change
|
|
2451
|
-
expect(state).toEqual(initialState);
|
|
2452
|
-
});
|
|
2453
|
-
|
|
2454
|
-
it("can drop a cell over another cell", () => {
|
|
2455
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2456
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2457
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2458
|
-
|
|
2459
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2460
|
-
"
|
|
2461
|
-
[0] ''
|
|
2462
|
-
|
|
2463
|
-
[1] ''
|
|
2464
|
-
|
|
2465
|
-
[2] ''
|
|
2466
|
-
|
|
2467
|
-
[3] ''
|
|
2468
|
-
"
|
|
2469
|
-
`);
|
|
2470
|
-
|
|
2471
|
-
actions.dropCellOverCell({
|
|
2472
|
-
cellId: cellId("0"),
|
|
2473
|
-
overCellId: cellId("3"),
|
|
2474
|
-
});
|
|
2475
|
-
|
|
2476
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2477
|
-
"
|
|
2478
|
-
[1] ''
|
|
2479
|
-
|
|
2480
|
-
[2] ''
|
|
2481
|
-
|
|
2482
|
-
[3] ''
|
|
2483
|
-
|
|
2484
|
-
[0] ''
|
|
2485
|
-
"
|
|
2486
|
-
`);
|
|
2487
|
-
});
|
|
2488
|
-
|
|
2489
|
-
it("can drop a cell over a new column", () => {
|
|
2490
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2491
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2492
|
-
|
|
2493
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2494
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2495
|
-
"
|
|
2496
|
-
[0] ''
|
|
2497
|
-
|
|
2498
|
-
[1] ''
|
|
2499
|
-
|
|
2500
|
-
[2] ''
|
|
2501
|
-
"
|
|
2502
|
-
`);
|
|
2503
|
-
|
|
2504
|
-
actions.dropOverNewColumn({ cellId: cellId("1") });
|
|
2505
|
-
|
|
2506
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2507
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2508
|
-
"
|
|
2509
|
-
> col 0
|
|
2510
|
-
[0] ''
|
|
2511
|
-
|
|
2512
|
-
[2] ''
|
|
2513
|
-
|
|
2514
|
-
> col 1
|
|
2515
|
-
[1] ''
|
|
2516
|
-
"
|
|
2517
|
-
`);
|
|
2518
|
-
|
|
2519
|
-
// Check that the cells are in the correct columns
|
|
2520
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2521
|
-
cellId("0"),
|
|
2522
|
-
cellId("2"),
|
|
2523
|
-
]);
|
|
2524
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([cellId("1")]);
|
|
2525
|
-
});
|
|
2526
|
-
|
|
2527
|
-
it("can drop a column over another column", () => {
|
|
2528
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2529
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2530
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2531
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2532
|
-
|
|
2533
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2534
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2535
|
-
"
|
|
2536
|
-
> col 0
|
|
2537
|
-
[0] ''
|
|
2538
|
-
|
|
2539
|
-
[1] ''
|
|
2540
|
-
|
|
2541
|
-
> col 1
|
|
2542
|
-
[2] ''
|
|
2543
|
-
|
|
2544
|
-
[3] ''
|
|
2545
|
-
"
|
|
2546
|
-
`);
|
|
2547
|
-
|
|
2548
|
-
const columnId0 = state.cellIds.atOrThrow(0).id;
|
|
2549
|
-
const columnId1 = state.cellIds.atOrThrow(1).id;
|
|
2550
|
-
expect(columnId0).not.toBe(columnId1);
|
|
2551
|
-
actions.moveColumn({ column: columnId1, overColumn: columnId0 });
|
|
2552
|
-
|
|
2553
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2554
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2555
|
-
"
|
|
2556
|
-
> col 0
|
|
2557
|
-
[2] ''
|
|
2558
|
-
|
|
2559
|
-
[3] ''
|
|
2560
|
-
|
|
2561
|
-
> col 1
|
|
2562
|
-
[0] ''
|
|
2563
|
-
|
|
2564
|
-
[1] ''
|
|
2565
|
-
"
|
|
2566
|
-
`);
|
|
2567
|
-
|
|
2568
|
-
// Check that the columns have swapped positions
|
|
2569
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2570
|
-
cellId("2"),
|
|
2571
|
-
cellId("3"),
|
|
2572
|
-
]);
|
|
2573
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2574
|
-
cellId("0"),
|
|
2575
|
-
cellId("1"),
|
|
2576
|
-
]);
|
|
2577
|
-
});
|
|
2578
|
-
|
|
2579
|
-
it("can compact columns", () => {
|
|
2580
|
-
// Create initial state with 3 columns, including an empty one
|
|
2581
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2582
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2583
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
2584
|
-
actions.addColumnBreakpoint({ cellId: cellId("2") });
|
|
2585
|
-
actions.dropOverNewColumn({ cellId: cellId("2") });
|
|
2586
|
-
|
|
2587
|
-
expect(state.cellIds.getColumns().length).toBe(4);
|
|
2588
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2589
|
-
"
|
|
2590
|
-
> col 0
|
|
2591
|
-
[0] ''
|
|
2592
|
-
|
|
2593
|
-
> col 1
|
|
2594
|
-
[1] ''
|
|
2595
|
-
|
|
2596
|
-
> col 2
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
> col 3
|
|
2600
|
-
[2] ''
|
|
2601
|
-
"
|
|
2602
|
-
`);
|
|
2603
|
-
|
|
2604
|
-
// Check initial column structure
|
|
2605
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([cellId("0")]);
|
|
2606
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([cellId("1")]);
|
|
2607
|
-
expect(state.cellIds.getColumns()[2].topLevelIds).toEqual([]);
|
|
2608
|
-
expect(state.cellIds.getColumns()[3].topLevelIds).toEqual([cellId("2")]);
|
|
2609
|
-
|
|
2610
|
-
// Compact columns
|
|
2611
|
-
actions.compactColumns();
|
|
2612
|
-
|
|
2613
|
-
expect(state.cellIds.getColumns().length).toBe(3);
|
|
2614
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2615
|
-
"
|
|
2616
|
-
> col 0
|
|
2617
|
-
[0] ''
|
|
2618
|
-
|
|
2619
|
-
> col 1
|
|
2620
|
-
[1] ''
|
|
2621
|
-
|
|
2622
|
-
> col 2
|
|
2623
|
-
[2] ''
|
|
2624
|
-
"
|
|
2625
|
-
`);
|
|
2626
|
-
|
|
2627
|
-
// Check compacted column structure
|
|
2628
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([cellId("0")]);
|
|
2629
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([cellId("1")]);
|
|
2630
|
-
expect(state.cellIds.getColumns()[2].topLevelIds).toEqual([cellId("2")]);
|
|
2631
|
-
});
|
|
2632
|
-
|
|
2633
|
-
it("rebuildCellColumns regroups cells by config.column", () => {
|
|
2634
|
-
// Create four cells in a single column.
|
|
2635
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2636
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2637
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2638
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2639
|
-
|
|
2640
|
-
// Explicitly set config.column on each cell. This ONLY updates metadata —
|
|
2641
|
-
// updateCellConfig does not touch the MultiColumn tree.
|
|
2642
|
-
actions.updateCellConfig({
|
|
2643
|
-
cellId: cellId("0"),
|
|
2644
|
-
config: { column: 0 },
|
|
2645
|
-
});
|
|
2646
|
-
actions.updateCellConfig({
|
|
2647
|
-
cellId: cellId("2"),
|
|
2648
|
-
config: { column: 1 },
|
|
2649
|
-
});
|
|
2650
|
-
|
|
2651
|
-
// Tree is still a single column at this point.
|
|
2652
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2653
|
-
|
|
2654
|
-
// Now rebuild the column tree from metadata.
|
|
2655
|
-
actions.rebuildCellColumns({
|
|
2656
|
-
cellIds: [cellId("0"), cellId("1"), cellId("2"), cellId("3")],
|
|
2657
|
-
});
|
|
2658
|
-
|
|
2659
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2660
|
-
// Cell 1 inherits from 0 (col 0), cell 3 inherits from 2 (col 1).
|
|
2661
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2662
|
-
cellId("0"),
|
|
2663
|
-
cellId("1"),
|
|
2664
|
-
]);
|
|
2665
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2666
|
-
cellId("2"),
|
|
2667
|
-
cellId("3"),
|
|
2668
|
-
]);
|
|
2669
|
-
});
|
|
2670
|
-
|
|
2671
|
-
it("rebuildCellColumns with explicit column on every cell", () => {
|
|
2672
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2673
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2674
|
-
actions.createNewCell({ cellId: cellId("2"), before: false });
|
|
2675
|
-
|
|
2676
|
-
for (const [id, col] of [
|
|
2677
|
-
["0", 1],
|
|
2678
|
-
["1", 0],
|
|
2679
|
-
["2", 1],
|
|
2680
|
-
["3", 0],
|
|
2681
|
-
] as const) {
|
|
2682
|
-
actions.updateCellConfig({ cellId: cellId(id), config: { column: col } });
|
|
2683
|
-
}
|
|
2684
|
-
|
|
2685
|
-
actions.rebuildCellColumns({
|
|
2686
|
-
cellIds: [cellId("0"), cellId("1"), cellId("2"), cellId("3")],
|
|
2687
|
-
});
|
|
2688
|
-
|
|
2689
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2690
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2691
|
-
cellId("1"),
|
|
2692
|
-
cellId("3"),
|
|
2693
|
-
]);
|
|
2694
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2695
|
-
cellId("0"),
|
|
2696
|
-
cellId("2"),
|
|
2697
|
-
]);
|
|
2698
|
-
});
|
|
2699
|
-
|
|
2700
|
-
it("rebuildCellColumns collapses to one column when all cells are col 0", () => {
|
|
2701
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2702
|
-
actions.addColumnBreakpoint({ cellId: cellId("1") });
|
|
2703
|
-
expect(state.cellIds.getColumns().length).toBe(2);
|
|
2704
|
-
|
|
2705
|
-
// Wipe column metadata back to 0 for both cells.
|
|
2706
|
-
actions.updateCellConfig({ cellId: cellId("0"), config: { column: 0 } });
|
|
2707
|
-
actions.updateCellConfig({ cellId: cellId("1"), config: { column: 0 } });
|
|
2708
|
-
|
|
2709
|
-
actions.rebuildCellColumns({
|
|
2710
|
-
cellIds: [cellId("0"), cellId("1")],
|
|
2711
|
-
});
|
|
2712
|
-
|
|
2713
|
-
expect(state.cellIds.getColumns().length).toBe(1);
|
|
2714
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([
|
|
2715
|
-
cellId("0"),
|
|
2716
|
-
cellId("1"),
|
|
2717
|
-
]);
|
|
2718
|
-
});
|
|
2719
|
-
|
|
2720
|
-
it("rebuildCellColumns follows the provided order, not current tree order", () => {
|
|
2721
|
-
// Start with a single column.
|
|
2722
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
2723
|
-
actions.createNewCell({ cellId: cellId("1"), before: false });
|
|
2724
|
-
actions.updateCellConfig({ cellId: cellId("0"), config: { column: 0 } });
|
|
2725
|
-
actions.updateCellConfig({ cellId: cellId("2"), config: { column: 1 } });
|
|
2726
|
-
|
|
2727
|
-
// Pass the reversed order. The rebuild should honor it.
|
|
2728
|
-
actions.rebuildCellColumns({
|
|
2729
|
-
cellIds: [cellId("2"), cellId("1"), cellId("0")],
|
|
2730
|
-
});
|
|
2731
|
-
|
|
2732
|
-
// cellIds iteration:
|
|
2733
|
-
// 2 → col=1, pushed to col1, prev=1
|
|
2734
|
-
// 1 → col=null, pushed to col[prev=1] = col1
|
|
2735
|
-
// 0 → col=0, pushed to col0
|
|
2736
|
-
expect(state.cellIds.getColumns()[0].topLevelIds).toEqual([cellId("0")]);
|
|
2737
|
-
expect(state.cellIds.getColumns()[1].topLevelIds).toEqual([
|
|
2738
|
-
cellId("2"),
|
|
2739
|
-
cellId("1"),
|
|
2740
|
-
]);
|
|
2741
|
-
});
|
|
2742
|
-
|
|
2743
959
|
it("can clear output of a single cell", () => {
|
|
2744
960
|
// Set up initial state with output
|
|
2745
961
|
actions.handleCellMessage({
|
|
@@ -2828,8 +1044,8 @@ describe("cell reducer", () => {
|
|
|
2828
1044
|
|
|
2829
1045
|
it("can clear output of all cells", () => {
|
|
2830
1046
|
// Create multiple cells with output
|
|
2831
|
-
|
|
2832
|
-
const secondCellId = state.cellIds
|
|
1047
|
+
seedCell({ cellId: firstCellId, before: false });
|
|
1048
|
+
const secondCellId = state.cellIds[1];
|
|
2833
1049
|
|
|
2834
1050
|
// Add output to both cells
|
|
2835
1051
|
const outputMessage = {
|
|
@@ -2854,142 +1070,30 @@ describe("cell reducer", () => {
|
|
|
2854
1070
|
...outputMessage,
|
|
2855
1071
|
cell_id: firstCellId,
|
|
2856
1072
|
});
|
|
2857
|
-
actions.handleCellMessage({
|
|
2858
|
-
...outputMessage,
|
|
2859
|
-
cell_id: secondCellId,
|
|
2860
|
-
});
|
|
2861
|
-
|
|
2862
|
-
// Verify initial state has output
|
|
2863
|
-
expect(state.cellRuntime[firstCellId].output).not.toBeNull();
|
|
2864
|
-
expect(state.cellRuntime[firstCellId].consoleOutputs.length).toBe(1);
|
|
2865
|
-
expect(state.cellRuntime[secondCellId].output).not.toBeNull();
|
|
2866
|
-
expect(state.cellRuntime[secondCellId].consoleOutputs.length).toBe(1);
|
|
2867
|
-
|
|
2868
|
-
// Clear all outputs
|
|
2869
|
-
actions.clearAllCellOutputs();
|
|
2870
|
-
|
|
2871
|
-
// Verify all outputs are cleared
|
|
2872
|
-
expect(state.cellRuntime[firstCellId].output).toBeNull();
|
|
2873
|
-
expect(state.cellRuntime[firstCellId].consoleOutputs).toEqual([]);
|
|
2874
|
-
expect(state.cellRuntime[secondCellId].output).toBeNull();
|
|
2875
|
-
expect(state.cellRuntime[secondCellId].consoleOutputs).toEqual([]);
|
|
2876
|
-
});
|
|
2877
|
-
|
|
2878
|
-
it("skips creating new cell if code exists and skipIfCodeExists is true", () => {
|
|
2879
|
-
// Add initial cell with code
|
|
2880
|
-
actions.updateCellCode({
|
|
2881
|
-
cellId: firstCellId,
|
|
2882
|
-
code: "import numpy as np",
|
|
2883
|
-
formattingChange: false,
|
|
2884
|
-
});
|
|
2885
|
-
|
|
2886
|
-
// Try to create new cell with same code and skipIfCodeExists
|
|
2887
|
-
actions.createNewCell({
|
|
2888
|
-
cellId: "__end__",
|
|
2889
|
-
code: "import numpy as np",
|
|
2890
|
-
before: false,
|
|
2891
|
-
skipIfCodeExists: true,
|
|
2892
|
-
});
|
|
2893
|
-
|
|
2894
|
-
// Should still only have one cell
|
|
2895
|
-
expect(state.cellIds.inOrderIds.length).toBe(1);
|
|
2896
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2897
|
-
"
|
|
2898
|
-
[0] 'import numpy as np'
|
|
2899
|
-
"
|
|
2900
|
-
`);
|
|
2901
|
-
|
|
2902
|
-
// Verify we can still add cell with different code
|
|
2903
|
-
actions.createNewCell({
|
|
2904
|
-
cellId: "__end__",
|
|
2905
|
-
code: "import pandas as pd",
|
|
2906
|
-
before: false,
|
|
2907
|
-
skipIfCodeExists: true,
|
|
2908
|
-
});
|
|
2909
|
-
|
|
2910
|
-
expect(state.cellIds.inOrderIds.length).toBe(2);
|
|
2911
|
-
expect(formatCells(state)).toMatchInlineSnapshot(`
|
|
2912
|
-
"
|
|
2913
|
-
[0] 'import numpy as np'
|
|
2914
|
-
|
|
2915
|
-
[1] 'import pandas as pd'
|
|
2916
|
-
"
|
|
2917
|
-
`);
|
|
2918
|
-
});
|
|
2919
|
-
|
|
2920
|
-
it("can create and noop-update a setup cell", () => {
|
|
2921
|
-
// Create the setup cell
|
|
2922
|
-
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
|
|
2923
|
-
|
|
2924
|
-
// Check that setup cell was created
|
|
2925
|
-
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
|
|
2926
|
-
expect(state.cellData[SETUP_CELL_ID].name).toBe("setup");
|
|
2927
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2928
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2929
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2930
|
-
|
|
2931
|
-
// Update the setup cell
|
|
2932
|
-
actions.addSetupCellIfDoesntExist({ code: "# Updated setup code" });
|
|
2933
|
-
|
|
2934
|
-
// Check that the setup cell did not change, since it already exists
|
|
2935
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2936
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2937
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2938
|
-
});
|
|
2939
|
-
|
|
2940
|
-
it("can delete and undelete the setup cell", () => {
|
|
2941
|
-
// Create the setup cell
|
|
2942
|
-
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
|
|
2943
|
-
|
|
2944
|
-
// Check that setup cell was created
|
|
2945
|
-
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
|
|
2946
|
-
expect(state.cellData[SETUP_CELL_ID].name).toBe("setup");
|
|
2947
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2948
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2949
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2950
|
-
|
|
2951
|
-
// Delete the setup cell
|
|
2952
|
-
actions.deleteCell({ cellId: SETUP_CELL_ID });
|
|
2953
|
-
|
|
2954
|
-
// Check that setup cell was deleted
|
|
2955
|
-
expect(state.cellData[SETUP_CELL_ID]).toBeDefined(); // we keep old state
|
|
2956
|
-
expect(state.cellIds.inOrderIds).not.toContain(SETUP_CELL_ID);
|
|
2957
|
-
|
|
2958
|
-
// Undo delete the setup cell
|
|
2959
|
-
actions.undoDeleteCell();
|
|
2960
|
-
|
|
2961
|
-
// Check that setup cell was restored
|
|
2962
|
-
expect(state.cellData[SETUP_CELL_ID].id).toBe(SETUP_CELL_ID);
|
|
2963
|
-
expect(state.cellData[SETUP_CELL_ID].name).toBe("setup");
|
|
2964
|
-
expect(state.cellData[SETUP_CELL_ID].code).toBe("# Setup code");
|
|
2965
|
-
expect(state.cellData[SETUP_CELL_ID].edited).toBe(true);
|
|
2966
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
2967
|
-
|
|
2968
|
-
// Verify scrollKey is set to the restored setup cell
|
|
2969
|
-
expect(state.scrollKey).toBe(SETUP_CELL_ID);
|
|
2970
|
-
});
|
|
2971
|
-
|
|
2972
|
-
it("can delete and then create a new setup cell", () => {
|
|
2973
|
-
// Create the setup cell
|
|
2974
|
-
actions.addSetupCellIfDoesntExist({ code: "# Setup code" });
|
|
1073
|
+
actions.handleCellMessage({
|
|
1074
|
+
...outputMessage,
|
|
1075
|
+
cell_id: secondCellId,
|
|
1076
|
+
});
|
|
2975
1077
|
|
|
2976
|
-
//
|
|
2977
|
-
|
|
1078
|
+
// Verify initial state has output
|
|
1079
|
+
expect(state.cellRuntime[firstCellId].output).not.toBeNull();
|
|
1080
|
+
expect(state.cellRuntime[firstCellId].consoleOutputs.length).toBe(1);
|
|
1081
|
+
expect(state.cellRuntime[secondCellId].output).not.toBeNull();
|
|
1082
|
+
expect(state.cellRuntime[secondCellId].consoleOutputs.length).toBe(1);
|
|
2978
1083
|
|
|
2979
|
-
//
|
|
2980
|
-
actions.
|
|
1084
|
+
// Clear all outputs
|
|
1085
|
+
actions.clearAllCellOutputs();
|
|
2981
1086
|
|
|
2982
|
-
//
|
|
2983
|
-
expect(state.
|
|
2984
|
-
expect(state.
|
|
2985
|
-
expect(state.
|
|
2986
|
-
expect(state.
|
|
2987
|
-
expect(state.cellIds.inOrderIds).toContain(SETUP_CELL_ID);
|
|
1087
|
+
// Verify all outputs are cleared
|
|
1088
|
+
expect(state.cellRuntime[firstCellId].output).toBeNull();
|
|
1089
|
+
expect(state.cellRuntime[firstCellId].consoleOutputs).toEqual([]);
|
|
1090
|
+
expect(state.cellRuntime[secondCellId].output).toBeNull();
|
|
1091
|
+
expect(state.cellRuntime[secondCellId].consoleOutputs).toEqual([]);
|
|
2988
1092
|
});
|
|
2989
1093
|
|
|
2990
1094
|
it("can clear all outputs", () => {
|
|
2991
1095
|
// Add a cell and give it output
|
|
2992
|
-
|
|
1096
|
+
seedCell({
|
|
2993
1097
|
cellId: firstCellId,
|
|
2994
1098
|
before: false,
|
|
2995
1099
|
});
|
|
@@ -3017,522 +1121,13 @@ describe("cell reducer", () => {
|
|
|
3017
1121
|
expect(state.cellRuntime[cell2Id].output).toBeNull();
|
|
3018
1122
|
});
|
|
3019
1123
|
|
|
3020
|
-
describe("moveToNextCell", () => {
|
|
3021
|
-
let cell1Id: CellId;
|
|
3022
|
-
let cell2Id: CellId;
|
|
3023
|
-
let cell3Id: CellId;
|
|
3024
|
-
|
|
3025
|
-
beforeEach(() => {
|
|
3026
|
-
// Create a few cells to work with
|
|
3027
|
-
actions.createNewCell({ cellId: "__end__", before: false });
|
|
3028
|
-
actions.createNewCell({ cellId: "__end__", before: false });
|
|
3029
|
-
|
|
3030
|
-
cell1Id = state.cellIds.inOrderIds[0];
|
|
3031
|
-
cell2Id = state.cellIds.inOrderIds[1];
|
|
3032
|
-
cell3Id = state.cellIds.inOrderIds[2];
|
|
3033
|
-
});
|
|
3034
|
-
|
|
3035
|
-
it("creates new cell when moving after last cell with noCreate=false", () => {
|
|
3036
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3037
|
-
|
|
3038
|
-
actions.moveToNextCell({
|
|
3039
|
-
cellId: cell3Id,
|
|
3040
|
-
before: false,
|
|
3041
|
-
noCreate: false,
|
|
3042
|
-
});
|
|
3043
|
-
|
|
3044
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount + 1);
|
|
3045
|
-
});
|
|
3046
|
-
|
|
3047
|
-
it("creates new cell when moving before first cell with noCreate=false", () => {
|
|
3048
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3049
|
-
|
|
3050
|
-
actions.moveToNextCell({
|
|
3051
|
-
cellId: cell1Id,
|
|
3052
|
-
before: true,
|
|
3053
|
-
noCreate: false,
|
|
3054
|
-
});
|
|
3055
|
-
|
|
3056
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount + 1);
|
|
3057
|
-
});
|
|
3058
|
-
|
|
3059
|
-
it("does not create new cell when moving after last cell with noCreate=true", () => {
|
|
3060
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3061
|
-
const initialState = { ...state };
|
|
3062
|
-
|
|
3063
|
-
actions.moveToNextCell({
|
|
3064
|
-
cellId: cell3Id,
|
|
3065
|
-
before: false,
|
|
3066
|
-
noCreate: true,
|
|
3067
|
-
});
|
|
3068
|
-
|
|
3069
|
-
// Should not create a new cell
|
|
3070
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount);
|
|
3071
|
-
// Should not crash or throw an error
|
|
3072
|
-
expect(state.cellIds.inOrderIds).toEqual(initialState.cellIds.inOrderIds);
|
|
3073
|
-
});
|
|
3074
|
-
|
|
3075
|
-
it("does not create new cell when moving before first cell with noCreate=true", () => {
|
|
3076
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3077
|
-
const initialState = { ...state };
|
|
3078
|
-
|
|
3079
|
-
actions.moveToNextCell({ cellId: cell1Id, before: true, noCreate: true });
|
|
3080
|
-
|
|
3081
|
-
// Should not create a new cell
|
|
3082
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount);
|
|
3083
|
-
// Should not crash or throw an error
|
|
3084
|
-
expect(state.cellIds.inOrderIds).toEqual(initialState.cellIds.inOrderIds);
|
|
3085
|
-
});
|
|
3086
|
-
|
|
3087
|
-
it("focuses next cell when moving within bounds", () => {
|
|
3088
|
-
const focusSpy = vi.mocked(focusAndScrollCellIntoView);
|
|
3089
|
-
focusSpy.mockClear();
|
|
3090
|
-
|
|
3091
|
-
actions.moveToNextCell({
|
|
3092
|
-
cellId: cell1Id,
|
|
3093
|
-
before: false,
|
|
3094
|
-
noCreate: true,
|
|
3095
|
-
});
|
|
3096
|
-
|
|
3097
|
-
expect(focusSpy).toHaveBeenCalledWith({
|
|
3098
|
-
cellId: cell2Id,
|
|
3099
|
-
cell: state.cellHandles[cell2Id],
|
|
3100
|
-
isCodeHidden: false,
|
|
3101
|
-
codeFocus: "top",
|
|
3102
|
-
variableName: undefined,
|
|
3103
|
-
});
|
|
3104
|
-
});
|
|
3105
|
-
|
|
3106
|
-
it("focuses previous cell when moving backward within bounds", () => {
|
|
3107
|
-
const focusSpy = vi.mocked(focusAndScrollCellIntoView);
|
|
3108
|
-
focusSpy.mockClear();
|
|
3109
|
-
|
|
3110
|
-
actions.moveToNextCell({ cellId: cell2Id, before: true, noCreate: true });
|
|
3111
|
-
|
|
3112
|
-
expect(focusSpy).toHaveBeenCalledWith({
|
|
3113
|
-
cellId: cell1Id,
|
|
3114
|
-
cell: state.cellHandles[cell1Id],
|
|
3115
|
-
isCodeHidden: false,
|
|
3116
|
-
codeFocus: "bottom",
|
|
3117
|
-
variableName: undefined,
|
|
3118
|
-
});
|
|
3119
|
-
});
|
|
3120
|
-
|
|
3121
|
-
it("does not move focus from scratch cell", () => {
|
|
3122
|
-
const focusSpy = vi.mocked(focusAndScrollCellIntoView);
|
|
3123
|
-
focusSpy.mockClear();
|
|
3124
|
-
const initialState = { ...state };
|
|
3125
|
-
|
|
3126
|
-
actions.moveToNextCell({
|
|
3127
|
-
cellId: SCRATCH_CELL_ID,
|
|
3128
|
-
before: false,
|
|
3129
|
-
noCreate: false,
|
|
3130
|
-
});
|
|
3131
|
-
|
|
3132
|
-
expect(focusSpy).not.toHaveBeenCalled();
|
|
3133
|
-
expect(state.cellIds.inOrderIds).toEqual(initialState.cellIds.inOrderIds);
|
|
3134
|
-
});
|
|
3135
|
-
});
|
|
3136
|
-
|
|
3137
|
-
describe("untouched cells functionality", () => {
|
|
3138
|
-
it("starts with empty untouchedNewCells set", () => {
|
|
3139
|
-
expect(state.untouchedNewCells).toEqual(new Set());
|
|
3140
|
-
});
|
|
3141
|
-
|
|
3142
|
-
it("can create a new cell with hideCode option", () => {
|
|
3143
|
-
const initialCellCount = state.cellIds.inOrderIds.length;
|
|
3144
|
-
|
|
3145
|
-
actions.createNewCell({
|
|
3146
|
-
cellId: "__end__",
|
|
3147
|
-
before: false,
|
|
3148
|
-
hideCode: true,
|
|
3149
|
-
});
|
|
3150
|
-
|
|
3151
|
-
expect(state.cellIds.inOrderIds.length).toBe(initialCellCount + 1);
|
|
3152
|
-
const newCellId =
|
|
3153
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3154
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3155
|
-
});
|
|
3156
|
-
|
|
3157
|
-
it("adds interactively-created hidden cell with boilerplate code to untouchedNewCells", () => {
|
|
3158
|
-
// Markdown cells are created with hideCode and non-empty default code
|
|
3159
|
-
// (e.g. `mo.md(r"""\n""")`). They are user-initiated, so their editor
|
|
3160
|
-
// should be shown until first blur.
|
|
3161
|
-
actions.createNewCell({
|
|
3162
|
-
cellId: "__end__",
|
|
3163
|
-
before: false,
|
|
3164
|
-
code: 'mo.md(r"""\n""")',
|
|
3165
|
-
hideCode: true,
|
|
3166
|
-
autoFocus: true,
|
|
3167
|
-
});
|
|
3168
|
-
|
|
3169
|
-
const newCellId =
|
|
3170
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3171
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3172
|
-
});
|
|
3173
|
-
|
|
3174
|
-
it("does not add programmatically-created hidden cell with code to untouchedNewCells", () => {
|
|
3175
|
-
// Cells created by the kernel (e.g. via code_mode) carry code and
|
|
3176
|
-
// autoFocus=false; their hide_code must take effect immediately.
|
|
3177
|
-
actions.createNewCell({
|
|
3178
|
-
cellId: "__end__",
|
|
3179
|
-
before: false,
|
|
3180
|
-
code: "x = 1",
|
|
3181
|
-
hideCode: true,
|
|
3182
|
-
autoFocus: false,
|
|
3183
|
-
});
|
|
3184
|
-
|
|
3185
|
-
const newCellId =
|
|
3186
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3187
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3188
|
-
});
|
|
3189
|
-
|
|
3190
|
-
it("does not add cell to untouchedNewCells when hideCode is false", () => {
|
|
3191
|
-
actions.createNewCell({
|
|
3192
|
-
cellId: "__end__",
|
|
3193
|
-
before: false,
|
|
3194
|
-
hideCode: false,
|
|
3195
|
-
});
|
|
3196
|
-
|
|
3197
|
-
const newCellId =
|
|
3198
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3199
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3200
|
-
});
|
|
3201
|
-
|
|
3202
|
-
it("does not add cell to untouchedNewCells when hideCode is undefined", () => {
|
|
3203
|
-
actions.createNewCell({
|
|
3204
|
-
cellId: "__end__",
|
|
3205
|
-
before: false,
|
|
3206
|
-
});
|
|
3207
|
-
|
|
3208
|
-
const newCellId =
|
|
3209
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3210
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3211
|
-
});
|
|
3212
|
-
|
|
3213
|
-
it("can mark a cell as touched", () => {
|
|
3214
|
-
// Create a cell with hideCode
|
|
3215
|
-
actions.createNewCell({
|
|
3216
|
-
cellId: "__end__",
|
|
3217
|
-
before: false,
|
|
3218
|
-
hideCode: true,
|
|
3219
|
-
});
|
|
3220
|
-
|
|
3221
|
-
const newCellId =
|
|
3222
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3223
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3224
|
-
|
|
3225
|
-
// Mark it as touched
|
|
3226
|
-
actions.markTouched({ cellId: newCellId });
|
|
3227
|
-
|
|
3228
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3229
|
-
});
|
|
3230
|
-
|
|
3231
|
-
it("markTouched is idempotent", () => {
|
|
3232
|
-
// Create a cell with hideCode
|
|
3233
|
-
actions.createNewCell({
|
|
3234
|
-
cellId: "__end__",
|
|
3235
|
-
before: false,
|
|
3236
|
-
hideCode: true,
|
|
3237
|
-
});
|
|
3238
|
-
|
|
3239
|
-
const newCellId =
|
|
3240
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3241
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3242
|
-
|
|
3243
|
-
// Mark it as touched multiple times
|
|
3244
|
-
actions.markTouched({ cellId: newCellId });
|
|
3245
|
-
actions.markTouched({ cellId: newCellId });
|
|
3246
|
-
actions.markTouched({ cellId: newCellId });
|
|
3247
|
-
|
|
3248
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3249
|
-
});
|
|
3250
|
-
|
|
3251
|
-
it("can mark a non-existent cell as touched without error", () => {
|
|
3252
|
-
const nonExistentCellId = cellId("non-existent");
|
|
3253
|
-
|
|
3254
|
-
expect(() => {
|
|
3255
|
-
actions.markTouched({ cellId: nonExistentCellId });
|
|
3256
|
-
}).not.toThrow();
|
|
3257
|
-
|
|
3258
|
-
expect(state.untouchedNewCells.has(nonExistentCellId)).toBe(false);
|
|
3259
|
-
});
|
|
3260
|
-
|
|
3261
|
-
it("can handle multiple untouched cells", () => {
|
|
3262
|
-
// Create multiple cells with hideCode
|
|
3263
|
-
actions.createNewCell({
|
|
3264
|
-
cellId: "__end__",
|
|
3265
|
-
before: false,
|
|
3266
|
-
hideCode: true,
|
|
3267
|
-
});
|
|
3268
|
-
actions.createNewCell({
|
|
3269
|
-
cellId: "__end__",
|
|
3270
|
-
before: false,
|
|
3271
|
-
hideCode: true,
|
|
3272
|
-
});
|
|
3273
|
-
actions.createNewCell({
|
|
3274
|
-
cellId: "__end__",
|
|
3275
|
-
before: false,
|
|
3276
|
-
hideCode: false,
|
|
3277
|
-
});
|
|
3278
|
-
|
|
3279
|
-
const cellIds = state.cellIds.inOrderIds;
|
|
3280
|
-
const cell1Id = cellIds[cellIds.length - 3];
|
|
3281
|
-
const cell2Id = cellIds[cellIds.length - 2];
|
|
3282
|
-
const cell3Id = cellIds[cellIds.length - 1];
|
|
3283
|
-
|
|
3284
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
|
|
3285
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3286
|
-
expect(state.untouchedNewCells.has(cell3Id)).toBe(false);
|
|
3287
|
-
|
|
3288
|
-
// Mark one as touched
|
|
3289
|
-
actions.markTouched({ cellId: cell1Id });
|
|
3290
|
-
|
|
3291
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(false);
|
|
3292
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3293
|
-
expect(state.untouchedNewCells.has(cell3Id)).toBe(false);
|
|
3294
|
-
});
|
|
3295
|
-
|
|
3296
|
-
it("preserves untouched state when cells are moved", () => {
|
|
3297
|
-
// Create cells with hideCode
|
|
3298
|
-
actions.createNewCell({
|
|
3299
|
-
cellId: "__end__",
|
|
3300
|
-
before: false,
|
|
3301
|
-
hideCode: true,
|
|
3302
|
-
});
|
|
3303
|
-
actions.createNewCell({
|
|
3304
|
-
cellId: "__end__",
|
|
3305
|
-
before: false,
|
|
3306
|
-
hideCode: true,
|
|
3307
|
-
});
|
|
3308
|
-
|
|
3309
|
-
const cellIds = state.cellIds.inOrderIds;
|
|
3310
|
-
const cell1Id = cellIds[cellIds.length - 2];
|
|
3311
|
-
const cell2Id = cellIds[cellIds.length - 1];
|
|
3312
|
-
|
|
3313
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
|
|
3314
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3315
|
-
|
|
3316
|
-
// Move cell1 to the end
|
|
3317
|
-
actions.moveCell({
|
|
3318
|
-
cellId: cell1Id,
|
|
3319
|
-
before: false,
|
|
3320
|
-
});
|
|
3321
|
-
|
|
3322
|
-
// Both cells should still be untouched
|
|
3323
|
-
expect(state.untouchedNewCells.has(cell1Id)).toBe(true);
|
|
3324
|
-
expect(state.untouchedNewCells.has(cell2Id)).toBe(true);
|
|
3325
|
-
});
|
|
3326
|
-
|
|
3327
|
-
it("does not remove untouched state when cell is deleted", () => {
|
|
3328
|
-
// We could implement this, but it doesn't actually affect downstream behavior.
|
|
3329
|
-
// It is easier to leave it, so `undo` works as expected.
|
|
3330
|
-
|
|
3331
|
-
// Create a cell with hideCode
|
|
3332
|
-
actions.createNewCell({
|
|
3333
|
-
cellId: "__end__",
|
|
3334
|
-
before: false,
|
|
3335
|
-
hideCode: true,
|
|
3336
|
-
});
|
|
3337
|
-
|
|
3338
|
-
const newCellId =
|
|
3339
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3340
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3341
|
-
|
|
3342
|
-
// Delete the cell
|
|
3343
|
-
actions.deleteCell({ cellId: newCellId });
|
|
3344
|
-
|
|
3345
|
-
// Does not actually remove untouched state
|
|
3346
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3347
|
-
});
|
|
3348
|
-
|
|
3349
|
-
it("restores untouched state when cell deletion is undone", () => {
|
|
3350
|
-
// Create a cell with hideCode
|
|
3351
|
-
actions.createNewCell({
|
|
3352
|
-
cellId: "__end__",
|
|
3353
|
-
before: false,
|
|
3354
|
-
hideCode: true,
|
|
3355
|
-
});
|
|
3356
|
-
|
|
3357
|
-
const newCellId =
|
|
3358
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3359
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3360
|
-
|
|
3361
|
-
// Delete the cell
|
|
3362
|
-
actions.deleteCell({ cellId: newCellId });
|
|
3363
|
-
// Still exists in untouchedNewCells
|
|
3364
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3365
|
-
|
|
3366
|
-
// Undo the deletion
|
|
3367
|
-
actions.undoDeleteCell();
|
|
3368
|
-
|
|
3369
|
-
// The cell should be restored but no longer untouched
|
|
3370
|
-
// (this is expected behavior - undoing doesn't restore untouched state)
|
|
3371
|
-
const restoredCellId =
|
|
3372
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3373
|
-
expect(state.untouchedNewCells.has(restoredCellId)).toBe(false);
|
|
3374
|
-
});
|
|
3375
|
-
|
|
3376
|
-
it("untouched cell behavior with hide_code config", () => {
|
|
3377
|
-
// Create a cell with hideCode and hide_code config
|
|
3378
|
-
actions.createNewCell({
|
|
3379
|
-
cellId: "__end__",
|
|
3380
|
-
before: false,
|
|
3381
|
-
hideCode: true,
|
|
3382
|
-
});
|
|
3383
|
-
|
|
3384
|
-
const newCellId =
|
|
3385
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3386
|
-
|
|
3387
|
-
// Update the cell config to hide code
|
|
3388
|
-
actions.updateCellConfig({
|
|
3389
|
-
cellId: newCellId,
|
|
3390
|
-
config: { hide_code: true },
|
|
3391
|
-
});
|
|
3392
|
-
|
|
3393
|
-
// Cell should be untouched and have hide_code config
|
|
3394
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3395
|
-
expect(state.cellData[newCellId].config.hide_code).toBe(true);
|
|
3396
|
-
|
|
3397
|
-
// Code should not be hidden because cell is untouched
|
|
3398
|
-
expect(exportedForTesting.isCellCodeHidden(state, newCellId)).toBe(false);
|
|
3399
|
-
|
|
3400
|
-
// Mark as touched
|
|
3401
|
-
actions.markTouched({ cellId: newCellId });
|
|
3402
|
-
|
|
3403
|
-
// Now code should be hidden
|
|
3404
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3405
|
-
expect(exportedForTesting.isCellCodeHidden(state, newCellId)).toBe(true);
|
|
3406
|
-
});
|
|
3407
|
-
|
|
3408
|
-
it("can mark an existing cell as untouched", () => {
|
|
3409
|
-
// Create a cell without hideCode (not in untouchedNewCells)
|
|
3410
|
-
actions.createNewCell({
|
|
3411
|
-
cellId: "__end__",
|
|
3412
|
-
before: false,
|
|
3413
|
-
hideCode: false,
|
|
3414
|
-
});
|
|
3415
|
-
|
|
3416
|
-
const newCellId =
|
|
3417
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3418
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3419
|
-
|
|
3420
|
-
// Mark it as untouched
|
|
3421
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3422
|
-
|
|
3423
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3424
|
-
});
|
|
3425
|
-
|
|
3426
|
-
it("markUntouched is idempotent", () => {
|
|
3427
|
-
// Create a cell without hideCode
|
|
3428
|
-
actions.createNewCell({
|
|
3429
|
-
cellId: "__end__",
|
|
3430
|
-
before: false,
|
|
3431
|
-
hideCode: false,
|
|
3432
|
-
});
|
|
3433
|
-
|
|
3434
|
-
const newCellId =
|
|
3435
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3436
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3437
|
-
|
|
3438
|
-
// Mark as untouched multiple times
|
|
3439
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3440
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3441
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3442
|
-
|
|
3443
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3444
|
-
});
|
|
3445
|
-
|
|
3446
|
-
it("markUntouched does not affect already untouched cells", () => {
|
|
3447
|
-
// Create a cell with hideCode (already in untouchedNewCells)
|
|
3448
|
-
actions.createNewCell({
|
|
3449
|
-
cellId: "__end__",
|
|
3450
|
-
before: false,
|
|
3451
|
-
hideCode: true,
|
|
3452
|
-
});
|
|
3453
|
-
|
|
3454
|
-
const newCellId =
|
|
3455
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3456
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3457
|
-
|
|
3458
|
-
// Calling markUntouched should not change anything
|
|
3459
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3460
|
-
|
|
3461
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3462
|
-
});
|
|
3463
|
-
|
|
3464
|
-
it("markTouched and markUntouched can toggle cell state", () => {
|
|
3465
|
-
// Create a cell without hideCode
|
|
3466
|
-
actions.createNewCell({
|
|
3467
|
-
cellId: "__end__",
|
|
3468
|
-
before: false,
|
|
3469
|
-
hideCode: false,
|
|
3470
|
-
});
|
|
3471
|
-
|
|
3472
|
-
const newCellId =
|
|
3473
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3474
|
-
|
|
3475
|
-
// Initially not untouched
|
|
3476
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3477
|
-
|
|
3478
|
-
// Mark as untouched
|
|
3479
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3480
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3481
|
-
|
|
3482
|
-
// Mark as touched
|
|
3483
|
-
actions.markTouched({ cellId: newCellId });
|
|
3484
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(false);
|
|
3485
|
-
|
|
3486
|
-
// Mark as untouched again
|
|
3487
|
-
actions.markUntouched({ cellId: newCellId });
|
|
3488
|
-
expect(state.untouchedNewCells.has(newCellId)).toBe(true);
|
|
3489
|
-
});
|
|
3490
|
-
|
|
3491
|
-
it("markUntouched works for markdown cell conversion scenario", () => {
|
|
3492
|
-
// Simulates converting a Python cell to Markdown
|
|
3493
|
-
// 1. Create a regular cell (no hideCode)
|
|
3494
|
-
actions.createNewCell({
|
|
3495
|
-
cellId: "__end__",
|
|
3496
|
-
before: false,
|
|
3497
|
-
hideCode: false,
|
|
3498
|
-
});
|
|
3499
|
-
|
|
3500
|
-
const cellId =
|
|
3501
|
-
state.cellIds.inOrderIds[state.cellIds.inOrderIds.length - 1];
|
|
3502
|
-
|
|
3503
|
-
// Cell starts without hide_code and not in untouchedNewCells
|
|
3504
|
-
expect(state.cellData[cellId].config.hide_code).toBe(false);
|
|
3505
|
-
expect(state.untouchedNewCells.has(cellId)).toBe(false);
|
|
3506
|
-
expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(false);
|
|
3507
|
-
|
|
3508
|
-
// 2. Convert to markdown: set hide_code and mark as untouched
|
|
3509
|
-
actions.updateCellConfig({
|
|
3510
|
-
cellId,
|
|
3511
|
-
config: { hide_code: true },
|
|
3512
|
-
});
|
|
3513
|
-
actions.markUntouched({ cellId });
|
|
3514
|
-
|
|
3515
|
-
// Code should NOT be hidden because cell is untouched (user can edit)
|
|
3516
|
-
expect(state.cellData[cellId].config.hide_code).toBe(true);
|
|
3517
|
-
expect(state.untouchedNewCells.has(cellId)).toBe(true);
|
|
3518
|
-
expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(false);
|
|
3519
|
-
|
|
3520
|
-
// 3. User blurs the cell (markTouched)
|
|
3521
|
-
actions.markTouched({ cellId });
|
|
3522
|
-
|
|
3523
|
-
// Now code should be hidden
|
|
3524
|
-
expect(state.untouchedNewCells.has(cellId)).toBe(false);
|
|
3525
|
-
expect(exportedForTesting.isCellCodeHidden(state, cellId)).toBe(true);
|
|
3526
|
-
});
|
|
3527
|
-
});
|
|
3528
|
-
|
|
3529
1124
|
describe("releaseCellAtoms", () => {
|
|
3530
1125
|
it("atom families cache atoms until explicitly removed", () => {
|
|
3531
1126
|
const { cellDataAtom, cellRuntimeAtom, cellHandleAtom } =
|
|
3532
1127
|
exportedForTesting;
|
|
3533
1128
|
|
|
3534
|
-
|
|
3535
|
-
const newCellId = state.cellIds
|
|
1129
|
+
seedCell({ cellId: firstCellId, before: false });
|
|
1130
|
+
const newCellId = state.cellIds[1];
|
|
3536
1131
|
|
|
3537
1132
|
const dataAtom1 = cellDataAtom(newCellId);
|
|
3538
1133
|
const runtimeAtom1 = cellRuntimeAtom(newCellId);
|
|
@@ -3547,116 +1142,15 @@ describe("cell reducer", () => {
|
|
|
3547
1142
|
expect(runtimeAtom2).toBe(runtimeAtom1);
|
|
3548
1143
|
expect(handleAtom2).toBe(handleAtom1);
|
|
3549
1144
|
});
|
|
3550
|
-
|
|
3551
|
-
it("cleans up atom family cache when cells are deleted", () => {
|
|
3552
|
-
const { cellDataAtom, cellRuntimeAtom, cellHandleAtom } =
|
|
3553
|
-
exportedForTesting;
|
|
3554
|
-
|
|
3555
|
-
actions.createNewCell({ cellId: firstCellId, before: false });
|
|
3556
|
-
const newCellId = state.cellIds.inOrderIds[1];
|
|
3557
|
-
|
|
3558
|
-
// Access to create cache entries
|
|
3559
|
-
const dataAtom1 = cellDataAtom(newCellId);
|
|
3560
|
-
const runtimeAtom1 = cellRuntimeAtom(newCellId);
|
|
3561
|
-
const handleAtom1 = cellHandleAtom(newCellId);
|
|
3562
|
-
|
|
3563
|
-
// Triggers purge
|
|
3564
|
-
actions.deleteCell({ cellId: newCellId });
|
|
3565
|
-
|
|
3566
|
-
// Access again (should be new instances after cleanup)
|
|
3567
|
-
const dataAtom2 = cellDataAtom(newCellId);
|
|
3568
|
-
const runtimeAtom2 = cellRuntimeAtom(newCellId);
|
|
3569
|
-
const handleAtom2 = cellHandleAtom(newCellId);
|
|
3570
|
-
|
|
3571
|
-
expect(dataAtom2).not.toBe(dataAtom1);
|
|
3572
|
-
expect(runtimeAtom2).not.toBe(runtimeAtom1);
|
|
3573
|
-
expect(handleAtom2).not.toBe(handleAtom1);
|
|
3574
|
-
});
|
|
3575
|
-
});
|
|
3576
|
-
});
|
|
3577
|
-
|
|
3578
|
-
describe("isCellCodeHidden", () => {
|
|
3579
|
-
const state = initialNotebookState();
|
|
3580
|
-
const firstCellId = state.cellIds.inOrderIds[0];
|
|
3581
|
-
|
|
3582
|
-
it("returns false when hide_code is false and cell is not untouched", () => {
|
|
3583
|
-
const testCellId = cellId("test-cell");
|
|
3584
|
-
const testState: NotebookState = {
|
|
3585
|
-
...state,
|
|
3586
|
-
cellData: {
|
|
3587
|
-
...state.cellData,
|
|
3588
|
-
[testCellId]: {
|
|
3589
|
-
...state.cellData[firstCellId],
|
|
3590
|
-
id: testCellId,
|
|
3591
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3592
|
-
},
|
|
3593
|
-
},
|
|
3594
|
-
untouchedNewCells: new Set(),
|
|
3595
|
-
};
|
|
3596
|
-
|
|
3597
|
-
expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
|
|
3598
|
-
false,
|
|
3599
|
-
);
|
|
3600
|
-
});
|
|
3601
|
-
|
|
3602
|
-
it("returns true when hide_code is true and cell is not untouched", () => {
|
|
3603
|
-
const testCellId = cellId("test-cell");
|
|
3604
|
-
const testState: NotebookState = {
|
|
3605
|
-
...state,
|
|
3606
|
-
cellData: {
|
|
3607
|
-
...state.cellData,
|
|
3608
|
-
[testCellId]: {
|
|
3609
|
-
...state.cellData[firstCellId],
|
|
3610
|
-
id: testCellId,
|
|
3611
|
-
config: { hide_code: true, disabled: false, column: null },
|
|
3612
|
-
},
|
|
3613
|
-
},
|
|
3614
|
-
untouchedNewCells: new Set(),
|
|
3615
|
-
};
|
|
3616
|
-
|
|
3617
|
-
expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
|
|
3618
|
-
true,
|
|
3619
|
-
);
|
|
3620
|
-
});
|
|
3621
|
-
|
|
3622
|
-
it("returns false when hide_code is true but cell is untouched", () => {
|
|
3623
|
-
const testCellId = cellId("test-cell");
|
|
3624
|
-
const testState: NotebookState = {
|
|
3625
|
-
...state,
|
|
3626
|
-
cellData: {
|
|
3627
|
-
...state.cellData,
|
|
3628
|
-
[testCellId]: {
|
|
3629
|
-
...state.cellData[firstCellId],
|
|
3630
|
-
id: testCellId,
|
|
3631
|
-
config: { hide_code: true, disabled: false, column: null },
|
|
3632
|
-
},
|
|
3633
|
-
},
|
|
3634
|
-
untouchedNewCells: new Set([testCellId]),
|
|
3635
|
-
};
|
|
3636
|
-
|
|
3637
|
-
expect(exportedForTesting.isCellCodeHidden(testState, testCellId)).toBe(
|
|
3638
|
-
false,
|
|
3639
|
-
);
|
|
3640
1145
|
});
|
|
1146
|
+
it("withdraws the submission of a cell that leaves the document", () => {
|
|
1147
|
+
MockExecutions.seed(store, []);
|
|
1148
|
+
submitCells(store, [firstCellId]);
|
|
1149
|
+
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(1);
|
|
3641
1150
|
|
|
3642
|
-
|
|
3643
|
-
const testCellId = cellId("test-cell");
|
|
3644
|
-
const testState: NotebookState = {
|
|
3645
|
-
...state,
|
|
3646
|
-
cellData: {
|
|
3647
|
-
...state.cellData,
|
|
3648
|
-
[testCellId]: {
|
|
3649
|
-
...state.cellData[firstCellId],
|
|
3650
|
-
id: testCellId,
|
|
3651
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3652
|
-
},
|
|
3653
|
-
},
|
|
3654
|
-
untouchedNewCells: new Set([testCellId]),
|
|
3655
|
-
};
|
|
1151
|
+
actions.reconcileDocument({ order: [], cells: new Map() });
|
|
3656
1152
|
|
|
3657
|
-
expect(
|
|
3658
|
-
false,
|
|
3659
|
-
);
|
|
1153
|
+
expect(store.get(notebookQueuedOrRunningCountAtom)).toBe(0);
|
|
3660
1154
|
});
|
|
3661
1155
|
});
|
|
3662
1156
|
|
|
@@ -3839,11 +1333,11 @@ describe("createTracebackInfoAtom", () => {
|
|
|
3839
1333
|
data: [{ type: "syntax", msg: "Syntax error", lineno: 1 }],
|
|
3840
1334
|
mimetype: "application/vnd.marimo+error",
|
|
3841
1335
|
},
|
|
3842
|
-
status: "queued",
|
|
3843
1336
|
},
|
|
3844
1337
|
},
|
|
3845
1338
|
}),
|
|
3846
1339
|
);
|
|
1340
|
+
MockExecutions.seed(store, [MockExecutions.queued(cellId("cell1"))]);
|
|
3847
1341
|
|
|
3848
1342
|
const tracebackAtom = exportedForTesting.createTracebackInfoAtom(
|
|
3849
1343
|
cellId("cell1"),
|
|
@@ -3871,11 +1365,11 @@ describe("createTracebackInfoAtom", () => {
|
|
|
3871
1365
|
data: [{ type: "syntax", msg: "Syntax error", lineno: 1 }],
|
|
3872
1366
|
mimetype: "application/vnd.marimo+error",
|
|
3873
1367
|
},
|
|
3874
|
-
status: "running",
|
|
3875
1368
|
},
|
|
3876
1369
|
},
|
|
3877
1370
|
}),
|
|
3878
1371
|
);
|
|
1372
|
+
MockExecutions.seed(store, [MockExecutions.running(cellId("cell1"))]);
|
|
3879
1373
|
|
|
3880
1374
|
const tracebackAtom = exportedForTesting.createTracebackInfoAtom(
|
|
3881
1375
|
cellId("cell1"),
|
|
@@ -3886,31 +1380,9 @@ describe("createTracebackInfoAtom", () => {
|
|
|
3886
1380
|
});
|
|
3887
1381
|
});
|
|
3888
1382
|
|
|
3889
|
-
describe("
|
|
1383
|
+
describe("kernel-ready", () => {
|
|
3890
1384
|
const CELL_A = cellId("A");
|
|
3891
1385
|
const CELL_B = cellId("B");
|
|
3892
|
-
const newCells: CellData[] = [
|
|
3893
|
-
{
|
|
3894
|
-
id: CELL_A,
|
|
3895
|
-
name: "a",
|
|
3896
|
-
code: "1",
|
|
3897
|
-
edited: false,
|
|
3898
|
-
lastCodeRun: null,
|
|
3899
|
-
lastExecutionTime: null,
|
|
3900
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3901
|
-
serializedEditorState: null,
|
|
3902
|
-
},
|
|
3903
|
-
{
|
|
3904
|
-
id: CELL_B,
|
|
3905
|
-
name: "b",
|
|
3906
|
-
code: "2",
|
|
3907
|
-
edited: false,
|
|
3908
|
-
lastCodeRun: null,
|
|
3909
|
-
lastExecutionTime: null,
|
|
3910
|
-
config: { hide_code: false, disabled: false, column: null },
|
|
3911
|
-
serializedEditorState: null,
|
|
3912
|
-
},
|
|
3913
|
-
];
|
|
3914
1386
|
|
|
3915
1387
|
const hydratedState = () =>
|
|
3916
1388
|
MockNotebook.notebookState({
|
|
@@ -3940,44 +1412,74 @@ describe("setCells snapshot preservation", () => {
|
|
|
3940
1412
|
},
|
|
3941
1413
|
});
|
|
3942
1414
|
|
|
3943
|
-
|
|
3944
|
-
const { isWasm } = await import("@/core/wasm/utils");
|
|
3945
|
-
vi.mocked(isWasm).mockReturnValue(true);
|
|
3946
|
-
});
|
|
3947
|
-
|
|
3948
|
-
it("preserves hydrated output in WASM", () => {
|
|
1415
|
+
it("resetCellRuntimes starts every cell's runtime over", () => {
|
|
3949
1416
|
const next = exportedForTesting.reducer(hydratedState(), {
|
|
3950
|
-
type: "
|
|
3951
|
-
payload:
|
|
1417
|
+
type: "resetCellRuntimes",
|
|
1418
|
+
payload: undefined,
|
|
3952
1419
|
});
|
|
3953
1420
|
|
|
3954
|
-
expect(next.cellRuntime[CELL_A].output).
|
|
3955
|
-
|
|
1421
|
+
expect(next.cellRuntime[CELL_A].output).toBeNull();
|
|
1422
|
+
expect(next.cellRuntime[CELL_B].consoleOutputs).toEqual([]);
|
|
1423
|
+
});
|
|
1424
|
+
|
|
1425
|
+
it("resetCellRuntimes starts the scratch cell over too", () => {
|
|
1426
|
+
const state = MockNotebook.notebookState({
|
|
1427
|
+
cellData: { [CELL_A]: { id: CELL_A, code: "1" } },
|
|
1428
|
+
cellRuntime: {
|
|
1429
|
+
[SCRATCH_CELL_ID]: {
|
|
1430
|
+
output: {
|
|
1431
|
+
channel: "output",
|
|
1432
|
+
mimetype: "text/plain",
|
|
1433
|
+
data: "scratch",
|
|
1434
|
+
timestamp: 0,
|
|
1435
|
+
},
|
|
1436
|
+
},
|
|
1437
|
+
},
|
|
3956
1438
|
});
|
|
1439
|
+
const next = exportedForTesting.reducer(state, {
|
|
1440
|
+
type: "resetCellRuntimes",
|
|
1441
|
+
payload: undefined,
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
expect(next.cellRuntime[SCRATCH_CELL_ID].output).toBeNull();
|
|
3957
1445
|
});
|
|
3958
1446
|
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
1447
|
+
describe("resetCellRuntimes in WASM", () => {
|
|
1448
|
+
beforeEach(async () => {
|
|
1449
|
+
const { isWasm } = await import("@/core/wasm/utils");
|
|
1450
|
+
vi.mocked(isWasm).mockReturnValue(true);
|
|
3963
1451
|
});
|
|
3964
1452
|
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
1453
|
+
afterEach(async () => {
|
|
1454
|
+
const { isWasm } = await import("@/core/wasm/utils");
|
|
1455
|
+
vi.mocked(isWasm).mockReturnValue(false);
|
|
3968
1456
|
});
|
|
3969
|
-
});
|
|
3970
1457
|
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
1458
|
+
it("keeps hydrated outputs and console", () => {
|
|
1459
|
+
const next = exportedForTesting.reducer(hydratedState(), {
|
|
1460
|
+
type: "resetCellRuntimes",
|
|
1461
|
+
payload: undefined,
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
expect(next.cellRuntime[CELL_A].output).toMatchObject({
|
|
1465
|
+
data: "hydrated-A",
|
|
1466
|
+
});
|
|
1467
|
+
expect(next.cellRuntime[CELL_B].consoleOutputs[0]).toMatchObject({
|
|
1468
|
+
data: "hydrated-B-stdout",
|
|
1469
|
+
});
|
|
3976
1470
|
});
|
|
3977
1471
|
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
1472
|
+
it("starts cells with no prior runtime over", () => {
|
|
1473
|
+
const state = MockNotebook.notebookState({
|
|
1474
|
+
cellData: { [CELL_A]: { id: CELL_A, code: "1" } },
|
|
1475
|
+
});
|
|
1476
|
+
const next = exportedForTesting.reducer(state, {
|
|
1477
|
+
type: "resetCellRuntimes",
|
|
1478
|
+
payload: undefined,
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1481
|
+
expect(next.cellRuntime[CELL_A].output).toBeNull();
|
|
1482
|
+
expect(next.cellRuntime[CELL_A].consoleOutputs).toEqual([]);
|
|
1483
|
+
});
|
|
3982
1484
|
});
|
|
3983
1485
|
});
|