import { fireEvent, render, screen } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import { ProjectIconPicker } from "./ProjectIconPicker"; import { PROJECT_ICON_CATALOG, ProjectIcon, findProjectIcon, resolveProjectIcon, toEmojiIconRef, } from "./projectIcons"; describe("project emoji catalogue", () => { it("contains only stable Unicode emoji references and no bundled artwork metadata", () => { expect(PROJECT_ICON_CATALOG.length).toBeGreaterThan(90); for (const entry of PROJECT_ICON_CATALOG) { expect(entry.set).toBe("emoji"); expect(entry.name).toMatch(/^[a-z0-9]+(?:-[a-z0-9]+)*$/); expect(entry.glyph).toBeTruthy(); expect(entry).not.toHaveProperty("assetSrc"); expect(entry).not.toHaveProperty("legal"); } }); it("resolves known legacy references to emoji and unknown references to an initial", () => { expect(resolveProjectIcon({ set: "devicon", name: "python" })?.glyph).toBe("🐍"); expect(toEmojiIconRef({ set: "lucide", name: "star" })).toEqual({ set: "emoji", name: "star" }); expect(findProjectIcon({ set: "devicon", name: "python" })).toBeNull(); const { getByRole } = render(); expect(getByRole("img", { name: "U" }).textContent).toBe("U"); expect(getByRole("img").dataset.resolvedProjectIcon).toBe("initial"); }); it("keeps legacy alphabet initials readable", () => { const { getByRole } = render(); expect(getByRole("img", { name: "Z" }).textContent).toBe("Z"); }); it("uses the project initial when no icon is configured", () => { const { getByRole } = render(); expect(getByRole("img", { name: "A" }).textContent).toBe("A"); expect(getByRole("img").dataset.projectIcon).toBe("none"); }); it("keeps the full emoji artwork available for grayscale watermark rendering", () => { const { getByTestId } = render( <> , ); expect(getByTestId("product-icon").textContent).toBe("⭐"); expect(getByTestId("watermark-icon").textContent).toBe("⭐"); expect((getByTestId("watermark-icon") as HTMLElement).style.fontFamily).toContain("Color Emoji"); }); }); describe("ProjectIconPicker", () => { function renderPicker(value = null as Parameters[0]["value"]) { render(); } it("shows phone-like emoji categories only", () => { renderPicker(); for (const label of ["Smileys", "Animaux et nature", "Nourriture et boissons", "Activités", "Voyages et lieux", "Objets", "Symboles"]) { expect(screen.getByText(label)).toBeTruthy(); } expect(screen.queryByText("Technologies / Frameworks")).toBeNull(); expect(screen.queryByText("Lettres")).toBeNull(); }); it.each([ ["python", "Snake"], ["docker", "Package"], ["space launch", "Rocket"], ["database", "Floppy disk"], ])("finds %s using the English search metadata", (query, label) => { renderPicker(); fireEvent.change(screen.getByLabelText("Search icons in English"), { target: { value: query } }); expect(screen.getByRole("option", { name: label })).toBeTruthy(); }); it("selects and persists an emoji reference", () => { const onApply = vi.fn(); render(); fireEvent.change(screen.getByLabelText("Search icons in English"), { target: { value: "rocket" } }); fireEvent.click(screen.getByRole("option", { name: "Rocket" })); fireEvent.click(screen.getByRole("button", { name: "Appliquer" })); expect(onApply).toHaveBeenCalledWith({ set: "emoji", name: "rocket" }); }); it("normalizes a known legacy selection before applying it", () => { const onApply = vi.fn(); render(); fireEvent.click(screen.getByRole("button", { name: "Appliquer" })); expect(onApply).toHaveBeenCalledWith({ set: "emoji", name: "snake" }); }); });