/* Copyright 2026 Marimo. All rights reserved. */ import { fireEvent, render, screen } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import { parseContent } from "@/utils/url-parser"; import { isMarkdown, UrlDetector } from "../url-detector"; describe("UrlDetector", () => { it("renders plain text without URLs", () => { render(); expect(screen.getByText("Hello world")).toBeInTheDocument(); }); it("renders regular URLs as clickable links", () => { render( , ); const link = screen.getByRole("link"); expect(link).toHaveAttribute("href", "https://marimo.io"); expect(link).toHaveAttribute("target", "_blank"); expect(link).toHaveAttribute("rel", "noopener noreferrer"); }); it("renders multiple URLs in text", () => { render( , ); const links = screen.getAllByRole("link"); expect(links).toHaveLength(2); expect(links[0]).toHaveAttribute("href", "https://marimo.io"); expect(links[1]).toHaveAttribute("href", "https://github.com/marimo-team"); }); it("renders image URLs as images", () => { render( , ); const img = screen.getByRole("img"); expect(img).toHaveAttribute("src", "https://example.com/image.png"); expect(img).toHaveAttribute("alt", "URL preview"); }); it("renders known image domains as images", () => { render( , ); const img = screen.getByRole("img"); expect(img).toHaveAttribute( "src", "https://avatars.githubusercontent.com/u/123", ); }); it("falls back to link when image fails to load", () => { render( , ); const img = screen.getByRole("img"); // Simulate image load error fireEvent.error(img); // Should now be a link const link = screen.getByRole("link"); expect(link).toHaveAttribute("href", "https://example.com/broken.png"); expect(img).not.toBeInTheDocument(); }); it("handles various image extensions", () => { const extensions = ["png", "jpg", "jpeg", "gif", "webp", "svg", "ico"]; extensions.forEach((ext) => { const { container } = render( , ); const img = container.querySelector("img"); expect(img).toHaveAttribute("src", `https://example.com/image.${ext}`); }); }); it("renders data URIs as images", () => { const dataUri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="; render(); const img = screen.getByRole("img"); expect(img).toHaveAttribute("src", dataUri); }); it.skip("prevents event propagation on link clicks", () => { const mockStopPropagation = vi.fn(); render(); const link = screen.getByRole("link"); fireEvent.click(link, { stopPropagation: mockStopPropagation, }); expect(mockStopPropagation).toHaveBeenCalled(); }); }); describe("isMarkdown", () => { it("returns true for headings", () => { expect(isMarkdown("# Heading 1")).toBe(true); expect(isMarkdown("## Heading 2")).toBe(true); expect(isMarkdown("### Heading 3")).toBe(true); expect(isMarkdown("Heading\n===")).toBe(true); expect(isMarkdown("Heading\n---")).toBe(true); }); it.fails("returns true for bold text", () => { expect(isMarkdown("**bold**")).toBe(true); expect(isMarkdown("__bold__")).toBe(true); }); it.fails("returns true for italic text", () => { expect(isMarkdown("*italic*")).toBe(true); expect(isMarkdown("_italic_")).toBe(true); }); it("returns false for inline code", () => { expect(isMarkdown("`code`")).toBe(false); expect(isMarkdown("Text with `inline code` in it")).toBe(false); }); it("returns true for code blocks", () => { expect(isMarkdown("```\ncode block\n```")).toBe(true); expect(isMarkdown("```python\ndef hello():\n pass\n```")).toBe(true); }); it("returns true for lists", () => { expect(isMarkdown("- item 1\n- item 2")).toBe(true); expect(isMarkdown("* item 1\n* item 2")).toBe(true); expect(isMarkdown("1. item 1\n2. item 2")).toBe(true); }); it("returns true for blockquotes", () => { expect(isMarkdown("> This is a quote")).toBe(true); expect(isMarkdown("> Quote line 1\n> Quote line 2")).toBe(true); }); it("returns true for horizontal rules", () => { expect(isMarkdown("---")).toBe(true); expect(isMarkdown("***")).toBe(true); expect(isMarkdown("___")).toBe(true); }); it("returns true for tables", () => { expect( isMarkdown("| col1 | col2 |\n|------|------|\n| val1 | val2 |"), ).toBe(true); }); it("returns true for HTML tags", () => { expect(isMarkdown("
content
")).toBe(true); expect(isMarkdown("
")).toBe(true); }); it.fails("returns true for escaped characters", () => { expect(isMarkdown("\\*not bold\\*")).toBe(true); expect(isMarkdown("\\# not a heading")).toBe(true); }); it("returns false for plain text", () => { expect(isMarkdown("Just plain text")).toBe(false); expect(isMarkdown("No markdown here")).toBe(false); }); it("returns false for empty string", () => { expect(isMarkdown("")).toBe(false); }); it("returns false for plain URLs without markdown syntax", () => { expect(isMarkdown("https://example.com")).toBe(false); expect(isMarkdown("Visit https://marimo.io for more")).toBe(false); }); it("returns false for plain text with numbers", () => { expect(isMarkdown("123 456 789")).toBe(false); }); it.fails("returns true for mixed markdown and plain text", () => { expect(isMarkdown("Plain text with **bold** in it")).toBe(true); expect(isMarkdown("Start with text\n\n# Then a heading")).toBe(true); }); it.fails("returns true for markdown with URLs", () => { expect(isMarkdown("[Link](https://example.com)")).toBe(true); expect(isMarkdown("Visit [marimo](https://marimo.io)")).toBe(true); }); });