embedTheme.test.ts (3928B)
1 import { describe, expect, it } from "vitest"; 2 import { buildEmbedSearchParams, parseEmbedParams } from "./embedParams"; 3 import { 4 mergeThemeOverrides, 5 parseEmbedFont, 6 parseEmbedPreset, 7 parseEmbedRadius, 8 parseHexColor, 9 parseThemePayload, 10 resolveEmbedTheme, 11 } from "./embedTheme"; 12 13 describe("parseHexColor", () => { 14 it("accepts 6-digit hex with or without hash", () => { 15 expect(parseHexColor("fbc02d")).toBe("#FBC02D"); 16 expect(parseHexColor("#fbc02d")).toBe("#FBC02D"); 17 expect(parseHexColor("%23fbc02d")).toBe("#FBC02D"); 18 }); 19 20 it("expands 3-digit shorthand", () => { 21 expect(parseHexColor("f00")).toBe("#FF0000"); 22 expect(parseHexColor("#abc")).toBe("#AABBCC"); 23 }); 24 25 it("rejects invalid values", () => { 26 expect(parseHexColor("")).toBeNull(); 27 expect(parseHexColor("gggggg")).toBeNull(); 28 expect(parseHexColor("12345")).toBeNull(); 29 expect(parseHexColor("not-a-color")).toBeNull(); 30 }); 31 }); 32 33 describe("parseEmbedRadius", () => { 34 it("clamps radius between 0 and 24", () => { 35 expect(parseEmbedRadius("12")).toBe(12); 36 expect(parseEmbedRadius("0")).toBe(0); 37 expect(parseEmbedRadius("24")).toBe(24); 38 expect(parseEmbedRadius("99")).toBe(24); 39 expect(parseEmbedRadius("-5")).toBe(0); 40 }); 41 42 it("returns null for empty or invalid", () => { 43 expect(parseEmbedRadius("")).toBeNull(); 44 expect(parseEmbedRadius("abc")).toBeNull(); 45 }); 46 }); 47 48 describe("resolveEmbedTheme", () => { 49 it("merges preset with overrides", () => { 50 const tokens = resolveEmbedTheme({ preset: "light", accent: "#112233" }); 51 expect(tokens.bg).toBe("#f1f5f9"); 52 expect(tokens.accent).toBe("#112233"); 53 }); 54 55 it("applies custom radius in px", () => { 56 const tokens = resolveEmbedTheme({ preset: "minimal", radius: 16 }); 57 expect(tokens.radius).toBe("16px"); 58 }); 59 }); 60 61 describe("parseThemePayload", () => { 62 it("parses postMessage theme object", () => { 63 expect(parseThemePayload({ preset: "dark", accent: "e64a19" })).toEqual({ 64 preset: "dark", 65 accent: "#E64A19", 66 }); 67 }); 68 69 it("returns null when no valid keys", () => { 70 expect(parseThemePayload({})).toBeNull(); 71 expect(parseThemePayload(null)).toBeNull(); 72 expect(parseThemePayload({ preset: "invalid" })).toBeNull(); 73 }); 74 75 it("accepts fgMuted alias textMuted", () => { 76 expect(parseThemePayload({ fgMuted: "94a3b8" })?.fgMuted).toBe("#94A3B8"); 77 expect(parseThemePayload({ textMuted: "64748b" })?.fgMuted).toBe("#64748B"); 78 }); 79 }); 80 81 describe("mergeThemeOverrides", () => { 82 it("patch wins over base", () => { 83 expect( 84 mergeThemeOverrides({ preset: "light", accent: "#111111" }, { accent: "#222222" }), 85 ).toEqual({ preset: "light", accent: "#222222" }); 86 }); 87 }); 88 89 describe("parseEmbedPreset and parseEmbedFont", () => { 90 it("accepts known preset ids", () => { 91 expect(parseEmbedPreset("midnight")).toBe("midnight"); 92 expect(parseEmbedPreset("unknown")).toBeNull(); 93 }); 94 95 it("accepts known font ids", () => { 96 expect(parseEmbedFont("serif")).toBe("serif"); 97 expect(parseEmbedFont("comic")).toBeNull(); 98 }); 99 }); 100 101 describe("buildEmbedSearchParams round-trip", () => { 102 it("preserves theme params in parseEmbedParams", () => { 103 const qs = buildEmbedSearchParams({ 104 preset: "minimal", 105 accent: "#e64a19", 106 bg: "0f172a", 107 panel: "1e293b", 108 text: "f8fafc", 109 fgMuted: "94a3b8", 110 radius: 10, 111 font: "serif", 112 theme: "compact", 113 showBrand: false, 114 startMuted: true, 115 }); 116 const parsed = parseEmbedParams(qs); 117 expect(parsed.theme).toBe("compact"); 118 expect(parsed.showBrand).toBe(false); 119 expect(parsed.startMuted).toBe(true); 120 expect(parsed.themeOverrides).toMatchObject({ 121 preset: "minimal", 122 accent: "#E64A19", 123 bg: "#0F172A", 124 panel: "#1E293B", 125 text: "#F8FAFC", 126 fgMuted: "#94A3B8", 127 radius: 10, 128 font: "serif", 129 }); 130 }); 131 });