/* Copyright 2026 Marimo. All rights reserved. */ import { act, fireEvent, render, screen } from "@testing-library/react"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { cellId } from "@/__tests__/branded"; import { TooltipProvider } from "@/components/ui/tooltip"; import { OutputArea, OutputRenderer } from "../Output"; describe("OutputRenderer renderFallback prop", () => { it("should use renderFallback for unsupported mimetypes", () => { const renderFallback = (mimetype: string) => (
Custom fallback for {mimetype}
); render( , ); expect(screen.getByTestId("custom-fallback")).toBeInTheDocument(); expect(screen.getByText(/Custom fallback for/)).toHaveTextContent( "Custom fallback for application/unsupported", ); }); it("should not use renderFallback for supported mimetypes", () => { const renderFallback = () => (
Should not appear
); render( , ); expect(screen.queryByTestId("custom-fallback")).not.toBeInTheDocument(); expect(screen.getByText("Hello World")).toBeInTheDocument(); }); it("should show default error when renderFallback is not provided", () => { render( , ); expect( screen.getByText(/Unsupported mimetype: application\/unknown/), ).toBeInTheDocument(); }); }); describe("OutputArea null/undefined handling", () => { it("should render null when output is null", () => { const { container } = render( , ); expect(container.innerHTML).toBe(""); }); it("should render null when output is undefined", () => { const { container } = render( , ); expect(container.innerHTML).toBe(""); }); }); describe("OutputRenderer image and SVG rendering", () => { const plainSvgString = ''; const base64SvgDataUrl = "data:image/svg+xml;base64,PHN2Zz48cmVjdCB4PSIwIiB5PSIw"; const base64PngDataUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"; it("should render plain SVG string via renderHTML", () => { const { container } = render( , ); const svgElement = container.querySelector("svg"); expect(svgElement).not.toBeNull(); const rectElement = svgElement!.querySelector("rect"); expect(rectElement).not.toBeNull(); const imgElement = container.querySelector("img"); expect(imgElement).toBeNull(); }); it("should render Base64 SVG data URL via ImageOutput", () => { const { container } = render( , ); const imgElement = container.querySelector("img"); expect(imgElement).not.toBeNull(); expect(imgElement).toHaveAttribute("src", base64SvgDataUrl); const svgElement = container.querySelector("svg"); expect(svgElement).toBeNull(); }); it("should render Base64 PNG data URL via ImageOutput", () => { const { container } = render( , ); const imgElement = container.querySelector("img"); expect(imgElement).not.toBeNull(); expect(imgElement).toHaveAttribute("src", base64PngDataUrl); }); }); describe("OutputArea fullscreen exit control", () => { let fullscreenElement: Element | null = null; const exitFullscreen = vi.fn(() => Promise.resolve()); beforeEach(() => { fullscreenElement = null; exitFullscreen.mockClear(); Object.defineProperty(document, "fullscreenElement", { configurable: true, get: () => fullscreenElement, }); Object.defineProperty(document, "exitFullscreen", { configurable: true, value: exitFullscreen, }); }); // Drop the stubs so that the real document properties come back. afterEach(() => { Reflect.deleteProperty(document, "fullscreenElement"); Reflect.deleteProperty(document, "exitFullscreen"); }); const renderOutput = () => render( , ); const enterFullscreen = (container: HTMLElement) => { fullscreenElement = container.querySelector('[data-cell-role="output"]'); act(() => { document.dispatchEvent(new Event("fullscreenchange")); }); }; it("hides the exit button outside fullscreen", () => { renderOutput(); expect( screen.queryByTestId("exit-fullscreen-output-button"), ).not.toBeInTheDocument(); }); it("shows the exit button when the output is the fullscreen element", () => { const { container } = renderOutput(); enterFullscreen(container); expect( screen.getByTestId("exit-fullscreen-output-button"), ).toBeInTheDocument(); }); it("exits fullscreen on click", () => { const { container } = renderOutput(); enterFullscreen(container); fireEvent.click(screen.getByTestId("exit-fullscreen-output-button")); expect(exitFullscreen).toHaveBeenCalledTimes(1); }); it("exits fullscreen on Escape", () => { const { container } = renderOutput(); enterFullscreen(container); fireEvent.keyDown(document, { key: "Escape" }); expect(exitFullscreen).toHaveBeenCalledTimes(1); }); it("ignores Escape outside fullscreen", () => { renderOutput(); fireEvent.keyDown(document, { key: "Escape" }); expect(exitFullscreen).not.toHaveBeenCalled(); }); });