- Vitest multi-project setup (unit / integration / component) - Real-Postgres integration harness via in-process PGlite (TEST_DATABASE_URL override), migrations applied per worker; production code untouched - Unit: routing, locale, validation/Zod schemas, metadata, mail, site-theme, marquee, media, portfolio helpers, plus architecture-rule tests - Integration: Prisma data layer, API routes, and all server actions - Component: UI primitives and form components (jsdom + Testing Library) - 371 tests passing
54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { buildSiteThemeStyleText, buildSiteThemeTokens } from "@/lib/site-theme";
|
|
|
|
const CHANNEL = /^\d+ \d+% \d+%$/;
|
|
|
|
describe("buildSiteThemeTokens", () => {
|
|
it("converts pure red to the expected HSL channels", () => {
|
|
const tokens = buildSiteThemeTokens("#ff0000");
|
|
expect(tokens.light.primary).toBe("0 100% 50%");
|
|
// dark primary lightens by 6 and clamps saturation into [40,95]
|
|
expect(tokens.dark.primary).toBe("0 95% 56%");
|
|
});
|
|
|
|
it("produces zero saturation for a neutral gray", () => {
|
|
const tokens = buildSiteThemeTokens("#808080");
|
|
expect(tokens.light.primary.startsWith("0 0%")).toBe(true);
|
|
});
|
|
|
|
it("emits well-formed channel strings for every token", () => {
|
|
const tokens = buildSiteThemeTokens("#dc5a35");
|
|
for (const value of [
|
|
tokens.light.primary,
|
|
tokens.light.secondary,
|
|
tokens.dark.primary,
|
|
tokens.dark.secondary,
|
|
]) {
|
|
expect(value).toMatch(CHANNEL);
|
|
}
|
|
});
|
|
|
|
it("derives distinct dark and secondary variants", () => {
|
|
const tokens = buildSiteThemeTokens("#dc5a35");
|
|
expect(tokens.dark.primary).not.toBe(tokens.light.primary);
|
|
expect(tokens.light.secondary).not.toBe(tokens.light.primary);
|
|
});
|
|
|
|
it("falls back to the default brand color for invalid input", () => {
|
|
expect(buildSiteThemeTokens("not-a-color")).toEqual(buildSiteThemeTokens("#dc5a35"));
|
|
expect(buildSiteThemeTokens("")).toEqual(buildSiteThemeTokens("#dc5a35"));
|
|
});
|
|
});
|
|
|
|
describe("buildSiteThemeStyleText", () => {
|
|
it("emits :root and .dark blocks with the derived channels", () => {
|
|
const css = buildSiteThemeStyleText("#ff0000");
|
|
expect(css).toContain(":root {");
|
|
expect(css).toContain(".dark {");
|
|
expect(css).toContain("--primary: 0 100% 50%;");
|
|
expect(css).toContain("--brand-secondary:");
|
|
expect(css).toContain("--sidebar-ring:");
|
|
});
|
|
});
|