import { describe, expect, it } from "vitest"; import * as themeParamForm from "./themeParamForm"; import { COLOR_RE, effectiveValue, invalidParamKeys, isValidParamValue, paramValuesOf, } from "./themeParamForm"; import { ThemeVo } from "@/types/cms"; /** 与后端 default/t-demo-red schema 对齐的夹具(§4.2) */ function theme(): ThemeVo { return { name: "t-demo-red", title: "测试红(夹具)", screenshotUrl: "/api/theme/t-demo-red/asset/screenshot.png", params: [ { key: "primary", cssVar: "--primary", label: "主色", type: "color", defaultValue: "#b01a2e" }, { key: "bg", cssVar: "--bg", label: "页面底色", type: "color", defaultValue: "#f7f5f2" }, { key: "headerStyle", label: "头部样式", type: "select", defaultValue: "banner", options: [{ value: "banner", label: "横幅头图" }, { value: "plain", label: "简洁顶栏" }], }, { key: "logoImage", label: "头图", type: "image", defaultValue: "" }, ], }; } describe("themeParamForm(方案 §6.3/§8.4 + 工作项 T 包级参数)", () => { it("T-T-08: paramValuesOf 以包 schema + paramValues 组装,缺项回落 defaultValue", () => { const t = theme(); t.paramValues = { primary: "#123456" }; const model = paramValuesOf(t); expect(model.primary).toBe("#123456"); // 包级微调值 expect(model.bg).toBe("#f7f5f2"); // 未微调 → theme.json 默认值 expect(model.headerStyle).toBe("banner"); expect(model.logoImage).toBe(""); }); it("T-T-08: 覆盖层里的非法值回退默认、未声明 key 不入模型(与后端 filterValidValues 同语义)", () => { const t = theme(); t.paramValues = { primary: "red;}", bg: "#000000", zzhack: "zzvalue" }; const model = paramValuesOf(t); expect(model.primary).toBe("#b01a2e"); // 非法显式值 → 默认 expect(model.bg).toBe("#000000"); expect(model.zzhack).toBeUndefined(); }); it("T-T-08: 无 paramValues(新包/未微调)全量回落默认", () => { expect(paramValuesOf(theme())).toEqual({ primary: "#b01a2e", bg: "#f7f5f2", headerStyle: "banner", logoImage: "", }); }); it("R5-1 根治:paramValuesOf 只有「一个包」一个入参(无跨包 existing 通道),换包即取新包自身值", () => { expect(paramValuesOf.length).toBe(1); const nextPack = theme(); nextPack.name = "another-pack"; nextPack.params = nextPack.params.map(p => ({ ...p, defaultValue: p.type === "color" ? "#0a5c36" : p.defaultValue })); // 上一个包微调成 #123456 后切到未微调的新包:只能是新包默认值,旧包值无处带入 expect(paramValuesOf(nextPack).primary).toBe("#0a5c36"); expect(paramValuesOf(nextPack).primary).not.toBe("#123456"); }); it("T-T-07: 站点级装拆随参数迁包退役(formModelFromTheme / merge…IntoConfigJson / …FromConfigJson 不再导出)", () => { const mod = themeParamForm as unknown as Record; expect(mod.formModelFromTheme).toBeUndefined(); expect(mod.mergeThemeParamsIntoConfigJson).toBeUndefined(); expect(mod.themeParamsFromConfigJson).toBeUndefined(); // 模块出口收敛为「纯 schema + 值校验」:不再感知 configJson expect(Object.keys(mod).sort()).toEqual( [ "COLOR_RE", "KEY_RE", "effectiveValue", "hasParamType", "invalidParamKeys", "isValidParamValue", "paramValuesOf", ].sort() ); }); it("T28: 生效值兜底 —— 合法显式值优先,非法/缺失回退默认", () => { const primary = theme().params.find(p => p.key === "primary")!; expect(effectiveValue(primary, { primary: "#123456" })).toBe("#123456"); expect(effectiveValue(primary, { primary: "red;}" })).toBe("#b01a2e"); expect(effectiveValue(primary, {})).toBe("#b01a2e"); }); it("T29: 值校验 —— color 正则通过/拒绝", () => { expect(COLOR_RE.test("#b01a2e")).toBe(true); expect(COLOR_RE.test("#fff")).toBe(true); expect(COLOR_RE.test("#b01a2eff")).toBe(true); expect(COLOR_RE.test("red")).toBe(false); expect(COLOR_RE.test("red;}")).toBe(false); expect(COLOR_RE.test("#gggggg")).toBe(false); expect(COLOR_RE.test("javascript:alert(1)")).toBe(false); }); it("T29: select 枚举外拒绝;image 仅放行 /cms|/yschcms + /upload/** 前缀;空值一律不合法", () => { const t = theme(); const select = t.params.find(p => p.key === "headerStyle")!; const image = t.params.find(p => p.key === "logoImage")!; const color = t.params.find(p => p.key === "primary")!; expect(isValidParamValue(select, "plain")).toBe(true); expect(isValidParamValue(select, "hacker")).toBe(false); // B-7:与应用真实路径对齐,旧 /upload/** 前缀不再放行; // 兼容历史 /cms/upload/**(本地/旧数据)与现网 /yschcms/upload/**(projman 部署 context-path) expect(isValidParamValue(image, "/cms/upload/cms/logo.png")).toBe(true); expect(isValidParamValue(image, "/yschcms/upload/cms/logo.png")).toBe(true); expect(isValidParamValue(image, "/upload/cms/logo.png")).toBe(false); expect(isValidParamValue(image, "/yschcmsupload/x.png")).toBe(false); expect(isValidParamValue(image, "https://evil/x.png")).toBe(false); expect(isValidParamValue(color, "")).toBe(false); expect(isValidParamValue(color, undefined)).toBe(false); }); it("T29: invalidParamKeys 收集非法值 key", () => { const bad = invalidParamKeys(theme(), { primary: "#123456", headerStyle: "not-in-enum", }); expect(bad).toContain("headerStyle"); expect(bad).not.toContain("primary"); }); });