import { render, screen } from "@testing-library/svelte"; import { tick } from "svelte"; import { user } from "../setup-tests"; import Theme from "./Theme.test.svelte"; import ThemeSelect from "./ThemeSelect.test.svelte"; import ThemeSelectCustom from "./ThemeSelectCustom.test.svelte"; import ThemeToggle from "./ThemeToggle.test.svelte"; import ThemeToggleCustom from "./ThemeToggleCustom.test.svelte"; describe("Theme", () => { let documentMock: { setAttribute: ReturnType; style: { setProperty: ReturnType }; }; let consoleLog: ReturnType; let localStorageMock: Record; let originalLocalStorage: Storage; beforeEach(() => { documentMock = { setAttribute: vi.spyOn(document.documentElement, "setAttribute"), style: { setProperty: vi.spyOn(document.documentElement.style, "setProperty"), }, }; consoleLog = vi.spyOn(console, "log"); originalLocalStorage = global.localStorage; localStorageMock = {}; global.localStorage = { getItem: vi.fn((key) => localStorageMock[key] || null), setItem: vi.fn((key, value) => { localStorageMock[key] = value; }), removeItem: vi.fn((key) => { delete localStorageMock[key]; }), clear: vi.fn(() => { localStorageMock = {}; }), length: 0, key: vi.fn(), }; }); afterEach(() => { vi.restoreAllMocks(); global.localStorage = originalLocalStorage; localStorage.clear(); localStorageMock = {}; }); it("should set default theme to white", () => { render(Theme); expect(documentMock.setAttribute).toHaveBeenCalledWith("theme", "white"); }); it("should update theme attribute when theme changes", async () => { const { component } = render(Theme); component.$set({ theme: "g100" }); await tick(); expect(documentMock.setAttribute).toHaveBeenCalledWith("theme", "g100"); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" }); }); it("should apply custom tokens", async () => { const tokens = { "interactive-01": "#ff0000", "ui-background": "#ffffff", }; render(Theme, { props: { tokens } }); await tick(); expect(documentMock.style.setProperty).toHaveBeenCalledWith( "--cds-interactive-01", "#ff0000", ); expect(documentMock.style.setProperty).toHaveBeenCalledWith( "--cds-ui-background", "#ffffff", ); }); it("should persist theme in localStorage when persist is true", async () => { render(Theme, { props: { persist: true } }); await tick(); expect(localStorage.setItem).toHaveBeenCalledWith("theme", "white"); }); it("should load persisted theme from localStorage", async () => { localStorageMock["theme"] = "g90"; render(Theme, { props: { persist: true } }); await tick(); expect(documentMock.setAttribute).toHaveBeenCalledWith("theme", "g90"); }); it("should warn on invalid theme", async () => { const consoleWarn = vi.spyOn(console, "warn"); const { component } = render(Theme); // @ts-ignore - Testing invalid theme component.$set({ theme: "invalid" }); await tick(); expect(consoleWarn).toHaveBeenCalledWith( expect.stringContaining('invalid theme "invalid"'), ); }); it("should render toggle when render prop is set to toggle", async () => { render(ThemeToggle); const toggle = screen.getByLabelText("Dark mode"); expect(toggle).toBeInTheDocument(); expect(toggle).toHaveAttribute("type", "checkbox"); await user.click(toggle); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" }); await user.click(toggle); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "white" }); }); it("should render custom toggle when render prop is set to toggle and custom toggle options are provided", async () => { render(ThemeToggleCustom); const toggle = screen.getAllByText("Enable dark mode")[0]; expect(toggle).toBeInTheDocument(); await user.click(toggle); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g80" }); await user.click(toggle); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g10" }); }); it("should render select when render prop is set to select", async () => { render(ThemeSelect); const select = screen.getByLabelText("Themes"); expect(select).toBeInTheDocument(); await user.selectOptions(select, "g100"); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" }); await user.selectOptions(select, "white"); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "white" }); }); it("should render custom select when render prop is set to select and custom select options are provided", async () => { render(ThemeSelectCustom); const select = screen.getByLabelText("Select a theme"); expect(select).toBeInTheDocument(); await user.selectOptions(select, "g100"); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "g100" }); await user.selectOptions(select, "white"); expect(consoleLog).toHaveBeenCalledWith("update", { theme: "white" }); }); });