1import * as NodeFS from "node:fs";
2import * as NodePath from "node:path";
3import { describe, expect, it } from "vite-plus/test";
4
5import {
6 customThemeNames,
7 getGeneratedUniwindThemeOutputs,
8 renderDefaultThemeVariablesJSON,
9 renderUniwindThemesCSS,
10} from "./generate-uniwind-themes.mts";
11import { readDefaultMobileThemeVariables } from "../src/lib/mobileTheme.test-support";
12
13describe("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});