/** * Unit tests for useTelegramTheme, getFallbackScheme, and applyTheme. * Ported from webapp/js/theme.test.js (getTheme, applyTheme). */ import { describe, it, expect, vi, afterEach } from "vitest"; import { renderHook } from "@testing-library/react"; import { useTelegramTheme, getFallbackScheme, applyTheme, } from "./use-telegram-theme"; vi.mock("@telegram-apps/sdk-react", () => ({ useSignal: vi.fn(() => undefined), isThemeParamsDark: vi.fn(), setMiniAppBackgroundColor: { isAvailable: vi.fn(() => false) }, setMiniAppHeaderColor: { isAvailable: vi.fn(() => false) }, setMiniAppBottomBarColor: { isAvailable: vi.fn(() => false) }, })); describe("getFallbackScheme", () => { const originalMatchMedia = window.matchMedia; const originalGetComputedStyle = window.getComputedStyle; afterEach(() => { window.matchMedia = originalMatchMedia; window.getComputedStyle = originalGetComputedStyle; document.documentElement.removeAttribute("data-theme"); vi.clearAllMocks(); }); it("returns dark when prefers-color-scheme is dark", () => { window.matchMedia = vi.fn((query: string) => ({ matches: query === "(prefers-color-scheme: dark)", media: query, addEventListener: vi.fn(), removeEventListener: vi.fn(), addListener: vi.fn(), removeListener: vi.fn(), dispatchEvent: vi.fn(), onchange: null, })) as unknown as typeof window.matchMedia; expect(getFallbackScheme()).toBe("dark"); }); it("returns light when prefers-color-scheme is light", () => { window.matchMedia = vi.fn((query: string) => ({ matches: query === "(prefers-color-scheme: light)", media: query, addEventListener: vi.fn(), removeEventListener: vi.fn(), addListener: vi.fn(), removeListener: vi.fn(), dispatchEvent: vi.fn(), onchange: null, })) as unknown as typeof window.matchMedia; expect(getFallbackScheme()).toBe("light"); }); it("uses --tg-color-scheme when set on document", () => { window.getComputedStyle = vi.fn(() => Object.assign( {}, { getPropertyValue: (prop: string) => prop === "--tg-color-scheme" ? " light " : "", } ) ) as unknown as typeof window.getComputedStyle; expect(getFallbackScheme()).toBe("light"); }); it("uses --tg-color-scheme dark when set", () => { window.getComputedStyle = vi.fn(() => Object.assign( {}, { getPropertyValue: (prop: string) => prop === "--tg-color-scheme" ? "dark" : "", } ) ) as unknown as typeof window.getComputedStyle; expect(getFallbackScheme()).toBe("dark"); }); }); describe("applyTheme", () => { afterEach(() => { document.documentElement.removeAttribute("data-theme"); vi.clearAllMocks(); }); it("sets data-theme to given scheme", () => { applyTheme("light"); expect(document.documentElement.getAttribute("data-theme")).toBe("light"); applyTheme("dark"); expect(document.documentElement.getAttribute("data-theme")).toBe("dark"); }); it("resolves scheme via getFallbackScheme when no argument", () => { window.matchMedia = vi.fn((query: string) => ({ matches: query === "(prefers-color-scheme: dark)", media: query, addEventListener: vi.fn(), removeEventListener: vi.fn(), addListener: vi.fn(), removeListener: vi.fn(), dispatchEvent: vi.fn(), onchange: null, })) as unknown as typeof window.matchMedia; applyTheme(); expect(document.documentElement.getAttribute("data-theme")).toBe("dark"); }); }); describe("useTelegramTheme", () => { const originalMatchMedia = window.matchMedia; const originalGetComputedStyle = window.getComputedStyle; afterEach(() => { window.matchMedia = originalMatchMedia; window.getComputedStyle = originalGetComputedStyle; document.documentElement.removeAttribute("data-theme"); vi.clearAllMocks(); }); it("sets data-theme to dark when useSignal returns true", async () => { const { useSignal } = await import("@telegram-apps/sdk-react"); vi.mocked(useSignal).mockReturnValue(true); renderHook(() => useTelegramTheme()); expect(document.documentElement.getAttribute("data-theme")).toBe("dark"); }); it("sets data-theme to light when useSignal returns false", async () => { const { useSignal } = await import("@telegram-apps/sdk-react"); vi.mocked(useSignal).mockReturnValue(false); renderHook(() => useTelegramTheme()); expect(document.documentElement.getAttribute("data-theme")).toBe("light"); }); });