| 1 | import * as NodeFS from "node:fs"; |
| 2 | import * as NodePath from "node:path"; |
| 3 | import { describe, expect, it } from "vite-plus/test"; |
| 4 | |
| 5 | import { |
| 6 | customThemeNames, |
| 7 | getGeneratedUniwindThemeOutputs, |
| 8 | renderDefaultThemeVariablesJSON, |
| 9 | renderUniwindThemesCSS, |
| 10 | } from "./generate-uniwind-themes.mts"; |
| 11 | import { readDefaultMobileThemeVariables } from "../src/lib/mobileTheme.test-support"; |
| 12 | |
| 13 | describe("generate mobile Uniwind themes", () => { |
| 14 | it("keeps the committed outputs current", () => { |
| 15 | const staleOutputs = getGeneratedUniwindThemeOutputs() |
| 16 | .filter( |
| 17 | ([filename, contents]) => |
| 18 | !NodeFS.existsSync(filename) || NodeFS.readFileSync(filename, "utf8") !== contents, |
| 19 | ) |
| 20 | .map(([filename]) => NodePath.relative(import.meta.dirname, filename)); |
| 21 | |
| 22 | expect( |
| 23 | staleOutputs, |
| 24 | "Run `vp run --filter @t3tools/mobile generate` and commit the generated outputs.", |
| 25 | ).toEqual([]); |
| 26 | }); |
| 27 | |
| 28 | it("registers every custom palette for both appearances", () => { |
| 29 | expect(customThemeNames).toEqual([ |
| 30 | "t3-chat-light", |
| 31 | "t3-chat-dark", |
| 32 | "grove-light", |
| 33 | "grove-dark", |
| 34 | "ocean-light", |
| 35 | "ocean-dark", |
| 36 | "ember-light", |
| 37 | "ember-dark", |
| 38 | "iris-light", |
| 39 | "iris-dark", |
| 40 | ]); |
| 41 | |
| 42 | const stylesheet = renderUniwindThemesCSS(); |
| 43 | for (const themeName of customThemeNames) { |
| 44 | expect(stylesheet.match(new RegExp(`@variant ${themeName} \\{`, "gu"))).toHaveLength(1); |
| 45 | } |
| 46 | }); |
| 47 | |
| 48 | it("keeps the default runtime bridge and generated CSS on the same palette", () => { |
| 49 | const variables = JSON.parse(renderDefaultThemeVariablesJSON()); |
| 50 | |
| 51 | expect(variables.light).toEqual(readDefaultMobileThemeVariables("light")); |
| 52 | expect(variables.dark).toEqual(readDefaultMobileThemeVariables("dark")); |
| 53 | expect(variables.light["--color-screen"]).toBe("#fcfcfc"); |
| 54 | expect(variables.light["--color-drawer"]).toBe("#fafafa"); |
| 55 | expect(variables.dark["--color-screen"]).toBe("#0a0a0a"); |
| 56 | expect(variables.dark["--color-drawer"]).toBe("#000000"); |
| 57 | expect(Object.keys(variables.light)).toEqual(Object.keys(variables.dark)); |
| 58 | }); |
| 59 | |
| 60 | it("gives every theme the same variables and a fixed Clerk palette for its appearance", () => { |
| 61 | const css = |
| 62 | NodeFS.readFileSync(NodePath.resolve(import.meta.dirname, "../global.css"), "utf8") + |
| 63 | renderUniwindThemesCSS(); |
| 64 | const themes = new Map<string, Map<string, string>>( |
| 65 | ["light", "dark", ...customThemeNames].map((name) => [name, new Map()]), |
| 66 | ); |
| 67 | for (const [, name, body] of css.matchAll(/@variant ([\w-]+) \{([^}]+)\}/gu)) { |
| 68 | const variables = themes.get(name!); |
| 69 | for (const [, variable, value] of body!.matchAll(/(--[\w-]+):\s*([^;]+);/gu)) { |
| 70 | variables?.set(variable!, value!.trim().toLowerCase()); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | const lightVariables = themes.get("light")!; |
| 75 | for (const [name, variables] of themes) { |
| 76 | expect([...variables.keys()].sort(), name).toEqual([...lightVariables.keys()].sort()); |
| 77 | const isDark = name === "dark" || name.endsWith("-dark"); |
| 78 | expect( |
| 79 | Object.fromEntries( |
| 80 | [...variables].filter(([variable]) => variable.startsWith("--color-clerk-")), |
| 81 | ), |
| 82 | name, |
| 83 | ).toEqual({ |
| 84 | "--color-clerk-page": isDark ? "#0a0a0a" : "#fcfcfc", |
| 85 | "--color-clerk-foreground": isDark ? "#f5f5f5" : "#27272a", |
| 86 | "--color-clerk-foreground-muted": isDark ? "#818181" : "#71717b", |
| 87 | "--color-clerk-border": isDark ? "#191919" : "#e4e4e7", |
| 88 | "--color-clerk-danger": isDark ? "#ff6467" : "#c10007", |
| 89 | }); |
| 90 | } |
| 91 | }); |
| 92 | }); |