/* Copyright 2026 Marimo. All rights reserved. */
import { describe, expect, it } from "vitest";
import type { Outline } from "@/core/cells/outline";
import {
canCollapseOutline,
findCollapseRange,
mergeOutlines,
parseOutline,
} from "../outline";
describe("parseOutline", () => {
it("can parse html outline", () => {
const html = `
Welcome to marimo! 🌊🍃
What is marimo?
marimo is a Python library for creating reactive and interactive notebooks
How do I use marimo?
pip install marimo
`;
const outline = parseOutline({
mimetype: "text/html",
timestamp: 0,
channel: "output",
data: html,
});
expect(outline).toMatchInlineSnapshot(`
{
"items": [
{
"by": {
"id": "welcome-to-marimo",
},
"level": 1,
"name": "Welcome to marimo! 🌊🍃",
},
{
"by": {
"id": "what-is-marimo",
},
"html": "What is marimo?",
"level": 2,
"name": "What is marimo?",
},
{
"by": {
"id": "how-do-i-use-marimo",
},
"level": 2,
"name": "How do I use marimo?",
},
],
}
`);
});
it("can parse html outline with duplicate nested headings", () => {
const html = `
Experiment 1
Setup
Instructions
Experiment 2
Setup
Instructions
Acknowledgements
marimo
`;
const outline = parseOutline({
mimetype: "text/html",
timestamp: 0,
channel: "output",
data: html,
});
expect(outline).toMatchInlineSnapshot(`
{
"items": [
{
"by": {
"id": "experiment-1",
},
"level": 1,
"name": "Experiment 1",
},
{
"by": {
"id": "setup",
},
"level": 2,
"name": "Setup",
},
{
"by": {
"id": "instructions",
},
"level": 2,
"name": "Instructions",
},
{
"by": {
"id": "experiment-2",
},
"level": 1,
"name": "Experiment 2",
},
{
"by": {
"id": "setup",
},
"level": 2,
"name": "Setup",
},
{
"by": {
"id": "instructions",
},
"level": 2,
"name": "Instructions",
},
{
"by": {
"id": "ack",
},
"level": 1,
"name": "Acknowledgements",
},
{
"by": {
"id": "marimo",
},
"level": 3,
"name": "marimo",
},
],
}
`);
});
it("can parse markdown outline", () => {
const markdown = `
Introduction
Getting Started
Installation
`;
const outline = parseOutline({
mimetype: "text/markdown",
timestamp: 0,
channel: "output",
data: markdown,
});
expect(outline).toMatchInlineSnapshot(`
{
"items": [
{
"by": {
"id": "introduction",
},
"level": 1,
"name": "Introduction",
},
{
"by": {
"id": "getting-started",
},
"level": 2,
"name": "Getting Started",
},
{
"by": {
"id": "installation",
},
"level": 3,
"name": "Installation",
},
],
}
`);
});
it("can handle non-html outline", () => {
const html = "foo";
const outline = parseOutline({
mimetype: "text/plain",
timestamp: 0,
channel: "output",
data: html,
});
expect(outline).toEqual(null);
});
it("can handle empty/null outline", () => {
expect(parseOutline(null)).toEqual(null);
expect(parseOutline(undefined!)).toEqual(null);
const html = "";
expect(
parseOutline({
mimetype: "text/html",
timestamp: 0,
channel: "output",
data: html,
}),
).toEqual({ items: [] });
});
it("can handle invalid outline", () => {
const html = "