122 lines
4.6 KiB
TypeScript
122 lines
4.6 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
applyThemePalette,
|
|
defaultThemeBackgroundId,
|
|
defaultThemePaletteId,
|
|
getThemeBackgroundPreset,
|
|
getThemePalette,
|
|
themeBackgroundPresets,
|
|
themePaletteChangeEvent,
|
|
themePalettes
|
|
} from "./tokens";
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe("platform web theme palettes", () => {
|
|
it("offers black mecha and magical-girl palettes with usable swatches", () => {
|
|
expect(themePalettes.map((palette) => palette.label)).toEqual(["黑色机甲", "魔法少女"]);
|
|
|
|
for (const palette of themePalettes) {
|
|
expect(palette.swatches.length).toBeGreaterThanOrEqual(5);
|
|
expect(palette.variables["--accent"]).toMatch(/^#[0-9a-f]{6}$/i);
|
|
expect(palette.variables["--surface"]).toContain("rgba");
|
|
expect(palette.variables["--rim-light"]).toContain("rgba");
|
|
expect(palette.variables["--crystal-rim"]).toContain("rgba");
|
|
expect(palette.variables["--sparkle-gold"]).toContain("rgba");
|
|
expect(palette.variables["--panel-material"]).toContain("var");
|
|
expect(palette.variables["--menu-item-bg"]).toContain("gradient");
|
|
expect(palette.variables["--frame-accessory-top"]).toContain("data:image/svg+xml");
|
|
expect(palette.variables["--frame-accessory-bottom"]).toContain("data:image/svg+xml");
|
|
expect(palette.variables["--frame-accessory-opacity"]).toMatch(/^0\.\d{2}$/);
|
|
expect(accessorySizeNumbers(palette.variables["--frame-accessory-size"])).toEqual(
|
|
expect.arrayContaining([expect.any(Number)])
|
|
);
|
|
expect(palette.variables["--custom-background-overlay"]).toBe("transparent");
|
|
|
|
const accessoryOpacity = Number(palette.variables["--frame-accessory-opacity"]);
|
|
const largestAccessoryDimension = Math.max(...accessorySizeNumbers(palette.variables["--frame-accessory-size"]));
|
|
|
|
if (palette.id === "magical-girl") {
|
|
expect(accessoryOpacity).toBeGreaterThanOrEqual(0.65);
|
|
expect(largestAccessoryDimension).toBeGreaterThanOrEqual(56);
|
|
expect(largestAccessoryDimension).toBeLessThanOrEqual(80);
|
|
expect(
|
|
new Set([
|
|
palette.variables["--frame-accessory-heart"],
|
|
palette.variables["--frame-accessory-wand"],
|
|
palette.variables["--frame-accessory-moon"],
|
|
palette.variables["--frame-accessory-ribbon"],
|
|
palette.variables["--frame-accessory-crystal"],
|
|
palette.variables["--frame-accessory-circle"]
|
|
]).size
|
|
).toBe(6);
|
|
} else {
|
|
expect(accessoryOpacity).toBeLessThanOrEqual(0.4);
|
|
expect(largestAccessoryDimension).toBeLessThanOrEqual(60);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("falls back to the default black mecha palette", () => {
|
|
expect(defaultThemePaletteId).toBe("mecha-black");
|
|
expect(getThemePalette("missing").id).toBe("mecha-black");
|
|
});
|
|
|
|
it("clears stale palette variables and publishes the active palette", () => {
|
|
const removeProperty = vi.fn();
|
|
const setProperty = vi.fn();
|
|
const dispatchEvent = vi.fn(() => true);
|
|
|
|
class TestCustomEvent<T> {
|
|
readonly type: string;
|
|
readonly detail: T | null;
|
|
|
|
constructor(type: string, init?: CustomEventInit<T>) {
|
|
this.type = type;
|
|
this.detail = init?.detail ?? null;
|
|
}
|
|
}
|
|
|
|
vi.stubGlobal("document", {
|
|
documentElement: {
|
|
dataset: {},
|
|
style: { removeProperty, setProperty }
|
|
}
|
|
});
|
|
vi.stubGlobal("window", { dispatchEvent });
|
|
vi.stubGlobal("CustomEvent", TestCustomEvent);
|
|
|
|
applyThemePalette("mecha-black");
|
|
|
|
expect(removeProperty).toHaveBeenCalledWith("--frame-accessory-heart");
|
|
expect(setProperty).toHaveBeenCalledWith("--accent", "#48e6ff");
|
|
expect(dispatchEvent).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
type: themePaletteChangeEvent,
|
|
detail: { paletteId: "mecha-black" }
|
|
})
|
|
);
|
|
});
|
|
|
|
it("offers mecha and magical desktop presets with a mecha default", () => {
|
|
expect(defaultThemeBackgroundId).toBe("mecha-grid");
|
|
expect(themeBackgroundPresets.map((preset) => preset.label)).toEqual(["机甲格纳库", "粉月魔法阵"]);
|
|
|
|
for (const preset of themeBackgroundPresets) {
|
|
expect(preset.preview).toContain("gradient");
|
|
expect(preset.variables["--workspace-background-pattern-image"]).toContain("gradient");
|
|
expect(preset.variables["--workspace-background-pattern-opacity"]).toMatch(/^0\./);
|
|
}
|
|
|
|
expect(getThemeBackgroundPreset("missing").id).toBe("mecha-grid");
|
|
});
|
|
|
|
});
|
|
|
|
function accessorySizeNumbers(sizeValue: string): number[] {
|
|
return Array.from(sizeValue.matchAll(/(\d+)px/g), (match) => Number(match[1]));
|
|
}
|