/** * @file MarkdownContent.test.tsx * @description Tests for the lightweight markdown renderer used by the conversation viewer. * Focuses on the block parser since the inline parser is well-exercised by snapshot-style * DOM assertions. * @author Nguyễn Ngọc Trí Vĩ */ import { describe, it, expect } from "vitest"; import { render, screen } from "@testing-library/react"; import { MarkdownContent } from "../MarkdownContent"; describe("", () => { it("renders fenced code blocks with the language label", () => { render(); // The CodeBlock header shows the language expect(screen.getByText(/javascript/i)).toBeInTheDocument(); // The code text is present (split across syntax-highlighted spans, so use a substring) expect(screen.getByText(/const/)).toBeInTheDocument(); }); it("renders headings as semantic-looking elements", () => { render(); expect(screen.getByText("Title")).toBeInTheDocument(); expect(screen.getByText("body")).toBeInTheDocument(); }); it("renders unordered and ordered lists", () => { const { container } = render(); expect(container.querySelectorAll("ul li")).toHaveLength(2); expect(container.querySelectorAll("ol li")).toHaveLength(2); }); it("renders blockquotes", () => { const { container } = render( a quote"} />); expect(container.querySelector("blockquote")).not.toBeNull(); expect(screen.getByText("a quote")).toBeInTheDocument(); }); it("renders inline code, bold, and italic", () => { const { container } = render( ); expect(container.querySelector("code")).not.toBeNull(); expect(container.querySelector("strong")).not.toBeNull(); expect(container.querySelector("em")).not.toBeNull(); }); it("auto-links bare URLs and renders explicit markdown links", () => { const { container } = render( ); const links = container.querySelectorAll("a"); expect(links.length).toBe(2); expect(links[0]!.getAttribute("href")).toBe("https://example.com"); expect(links[1]!.getAttribute("href")).toBe("https://example.com/docs"); // Both should open in a new tab safely for (const a of links) { expect(a.getAttribute("target")).toBe("_blank"); expect(a.getAttribute("rel")).toContain("noopener"); } }); it("renders plain text without any markdown features", () => { render(); expect(screen.getByText("just a normal sentence.")).toBeInTheDocument(); }); it("highlights standalone numbers, never ones inside a word, sha or issue ref", () => { const { container } = render( ); const nums = [...container.querySelectorAll("span.tabular-nums")].map((n) => n.textContent); expect(nums).toEqual(["3.5s", "87%", "$1.20", "v2.7.1", "2026-10-12"]); }); it("highlights an ISO date-time as one number", () => { const { container } = render( ); const nums = [...container.querySelectorAll("span.tabular-nums")].map((n) => n.textContent); expect(nums).toEqual([ "2026-10-12T08:00", "2026-10-12T08:00:00Z", "2026-10-12T08:00:00.123+07:00", ]); }); it("does not highlight numbers inside inline code", () => { const { container } = render(); expect(container.querySelector("span.tabular-nums")).toBeNull(); }); it("colours a flag label by how serious it is", () => { render(); expect(screen.getByText("Lưu ý:").className).toContain("text-accent"); expect(screen.getByText("Warning:").className).toContain("text-status-warning"); expect(screen.getByText("Lỗi:").className).toContain("text-status-danger"); expect(screen.queryByText("Note")).toBeNull(); }); it("handles empty input safely", () => { const { container } = render(); // Wrapper exists but no block elements expect(container.firstChild).not.toBeNull(); expect(container.querySelectorAll("p, ul, ol, blockquote, pre, hr").length).toBe(0); }); });