/** * Tests for ArsMarkdown * @vi-environment jsdom */ import { beforeEach, describe, expect, it } from "vitest"; import { ArsMarkdown } from "./ars-markdown.js"; describe("ArsMarkdown", () => { let element: ArsMarkdown; beforeEach(() => { document.body.innerHTML = ""; element = new ArsMarkdown(); }); it("registers the ars-markdown custom element", () => { expect(customElements.get("ars-markdown")).toBe(ArsMarkdown); }); it("creates a shadow root on construction", () => { expect(element.shadowRoot).toBeTruthy(); }); it("renders headings", () => { element.source = "# Hello\n## World"; document.body.appendChild(element); const h1 = element.shadowRoot?.querySelector("h1"); const h2 = element.shadowRoot?.querySelector("h2"); expect(h1?.textContent).toBe("Hello"); expect(h2?.textContent).toBe("World"); }); it("renders bold and italic", () => { element.source = "**bold** *italic*"; document.body.appendChild(element); const strong = element.shadowRoot?.querySelector("strong"); const em = element.shadowRoot?.querySelector("em"); expect(strong?.textContent).toBe("bold"); expect(em?.textContent).toBe("italic"); }); it("renders code inline", () => { element.source = "Use `code` here"; document.body.appendChild(element); const code = element.shadowRoot?.querySelector("code"); expect(code?.textContent).toBe("code"); }); it("renders links", () => { element.source = "[link](https://example.com)"; document.body.appendChild(element); const a = element.shadowRoot?.querySelector("a"); expect(a?.getAttribute("href")).toBe("https://example.com"); expect(a?.textContent).toBe("link"); }); it("updates when source changes", () => { element.source = "First"; document.body.appendChild(element); let p = element.shadowRoot?.querySelector("p"); expect(p?.textContent).toBe("First"); element.source = "Second"; p = element.shadowRoot?.querySelector("p"); expect(p?.textContent).toBe("Second"); }); // ── View/Edit mode tests ──────────────────────────────────────── it("defaults to view mode", () => { expect(element.mode).toBe("view"); }); it("renders markdown in view mode", () => { element.source = "**bold**"; document.body.appendChild(element); const strong = element.shadowRoot?.querySelector("strong"); expect(strong?.textContent).toBe("bold"); const textarea = element.shadowRoot?.querySelector("textarea"); expect(textarea).toBeNull(); }); it("renders textarea in edit mode", () => { element.source = "raw text"; element.mode = "edit"; document.body.appendChild(element); const textarea = element.shadowRoot?.querySelector("textarea"); expect(textarea).toBeTruthy(); expect(textarea?.value).toBe("raw text"); const body = element.shadowRoot?.querySelector(".markdown-body"); expect(body).toBeNull(); }); it("preserves raw HTML content in edit mode textarea", () => { element.source = ""; element.mode = "edit"; document.body.appendChild(element); const textarea = element.shadowRoot?.querySelector("textarea") as HTMLTextAreaElement; expect(textarea.value).toBe(""); }); it("emits ars-markdown:change on textarea input", () => { element.mode = "edit"; document.body.appendChild(element); const textarea = element.shadowRoot?.querySelector("textarea") as HTMLTextAreaElement; let received: string | null = null; element.addEventListener("ars-markdown:change", ((e: CustomEvent) => { received = e.detail.source; }) as EventListener); textarea.value = "new content"; textarea.dispatchEvent(new Event("input")); expect(received).toBe("new content"); }); it("updates source when textarea changes", () => { element.mode = "edit"; document.body.appendChild(element); const textarea = element.shadowRoot?.querySelector("textarea") as HTMLTextAreaElement; textarea.value = "updated"; textarea.dispatchEvent(new Event("input")); expect(element.source).toBe("updated"); }); it("toggling from edit to view renders markdown from edited source", () => { element.source = "initial"; element.mode = "edit"; document.body.appendChild(element); const textarea = element.shadowRoot?.querySelector("textarea") as HTMLTextAreaElement; textarea.value = "**bold**"; textarea.dispatchEvent(new Event("input")); element.mode = "view"; const strong = element.shadowRoot?.querySelector("strong"); expect(strong?.textContent).toBe("bold"); }); it("toggling from view to edit preserves source", () => { element.source = "my text"; document.body.appendChild(element); element.mode = "edit"; const textarea = element.shadowRoot?.querySelector("textarea") as HTMLTextAreaElement; expect(textarea.value).toBe("my text"); }); it("toggling mode does not fire change event", () => { element.source = "text"; document.body.appendChild(element); let changeCount = 0; element.addEventListener("ars-markdown:change", () => { changeCount++; }); element.mode = "edit"; element.mode = "view"; expect(changeCount).toBe(0); }); it("sets mode via attribute", () => { element.source = "content"; document.body.appendChild(element); element.setAttribute("mode", "edit"); const textarea = element.shadowRoot?.querySelector("textarea"); expect(textarea).toBeTruthy(); expect(element.mode).toBe("edit"); }); it("invalid mode attribute defaults to view", () => { element.source = "content"; document.body.appendChild(element); element.setAttribute("mode", "invalid"); expect(element.mode).toBe("view"); const body = element.shadowRoot?.querySelector(".markdown-body"); expect(body).toBeTruthy(); }); });