/* Copyright 2026 Marimo. All rights reserved. */
import { render } from "@testing-library/react";
import { beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import { Tracebacks } from "@/__mocks__/tracebacks";
import { cellId } from "@/__tests__/branded";
import { TooltipProvider } from "@/components/ui/tooltip";
import { initialModeAtom } from "@/core/mode";
import { store } from "@/core/state/jotai";
import { renderHTML } from "@/plugins/core/RenderHTML";
import {
MarimoTracebackOutput,
replaceTracebackFilenames,
replaceTracebackPrefix,
} from "../MarimoTracebackOutput";
const cid = cellId("1");
describe("traceback component", () => {
beforeEach(() => {
vi.resetAllMocks();
store.set(initialModeAtom, "edit");
});
test("extracts cell-link", () => {
const traceback = (
);
const { unmount, getAllByRole } = render(traceback);
// Has traceback links
expect(getAllByRole("link")).toHaveLength(2);
// Check that the traceback links are parsed
expect(getAllByRole("link")[0].textContent).toContain(
"marimo://untitled#cell=",
);
expect(getAllByRole("link")[1].textContent).toContain(
"marimo://untitled#cell=",
);
unmount();
});
test("renames File to Cell for relevant lines", () => {
const traceback = (
);
const { unmount, container } = render(traceback);
expect(container).not.toBeNull();
expect(Tracebacks.raw).not.toMatch(/Cell/);
expect(container.textContent).toMatch(/Cell/);
expect(Tracebacks.raw.match(/File/g)).toHaveLength(3);
expect(container?.textContent?.match(/File/g)).toHaveLength(1);
unmount();
});
});
describe("traceback replacement", () => {
beforeAll(() => {
store.set(initialModeAtom, "edit");
});
test("replaces File with Cell", () => {
const traceback = renderHTML({
html: Tracebacks.assertion,
additionalReplacements: [replaceTracebackPrefix],
});
const { unmount, container } = render(
{traceback},
);
expect(container).not.toBeNull();
expect(Tracebacks.assertion).not.toMatch(/Cell/);
expect(container.textContent).toMatch(/Cell/);
expect(Tracebacks.assertion.match(/File/g)).toHaveLength(3);
// Only replaces the relevant File to Cell
expect(container?.textContent?.match(/File/g)).toHaveLength(2);
unmount();
});
test("renames filenames", () => {
const traceback = renderHTML({
html: Tracebacks.assertion,
additionalReplacements: [replaceTracebackFilenames],
});
const { unmount, getAllByRole, container } = render(
{traceback},
);
expect(container).not.toBeNull();
// Has just traceback links
expect(getAllByRole("link")).toHaveLength(1);
// Check that the traceback links are parsed
expect(getAllByRole("link")[0].textContent).toContain(
"marimo://untitled#cell=",
);
expect(Tracebacks.assertion.match(/__marimo__cell_Hbol_/g)).toHaveLength(2);
// Still contains the string of the filename in the trace
expect(container?.textContent?.match(/__marimo__cell_Hbol_/g)).toHaveLength(
1,
);
unmount();
});
});