// Stream S4 (PLAN C5): the organization file's `appearance: { theme, mode }`. import test, { after } from "node:test"; import assert from "node:assert/strict"; import { readFileSync, rmSync } from "node:fs"; import { join } from "node:path"; import { businessDoc, rolesCopy, tmp as makeRoot, writeJson } from "./helpers.mjs"; import { validateBusinessDocument } from "../src/organization-config.mjs"; import { APPEARANCE_MODES, checkAppearance } from "../src/appearance.mjs"; import { pluginSettings } from "../src/plugin-settings.mjs"; import { BusinessError } from "../src/errors.mjs"; const roots = []; after(() => roots.forEach(root => rmSync(root, { recursive: true, force: true }))); function fixture() { const root = makeRoot(); roots.push(root); const rolesDir = rolesCopy(root); const doc = businessDoc(root, "acme"); const file = writeJson(join(root, "businesses", "acme.json"), doc); return { root, rolesDir, doc, file, check: d => validateBusinessDocument(d, file, { rolesDir }) }; } const withAppearance = (doc, appearance) => ({ ...structuredClone(doc), appearance }); test("S4 appearance is optional: a file without it is valid and the validated business has no appearance key", () => { const f = fixture(); const business = f.check(f.doc); assert.equal(Object.hasOwn(business, "appearance"), false); assert.equal(checkAppearance(undefined, "x"), undefined); }); test("S4 appearance accepts theme and mode, either or both, and the result is frozen", () => { const f = fixture(); for (const appearance of [{}, { theme: "harbor" }, { mode: "dim" }, { theme: "grove", mode: "system" }]) { const business = f.check(withAppearance(f.doc, appearance)); assert.deepEqual({ ...business.appearance }, appearance); assert.ok(Object.isFrozen(business.appearance)); } for (const mode of APPEARANCE_MODES) assert.equal(f.check(withAppearance(f.doc, { mode })).appearance.mode, mode); assert.deepEqual([...APPEARANCE_MODES], ["system", "light", "dim", "dark"]); }); test("S4 appearance refuses a bad shape, an unknown key, a bad mode and a theme name that isn't a plugin name", () => { const f = fixture(); const refused = [ null, [], "harbor", 3, { colour: "red" }, { theme: "harbor", extra: 1 }, { mode: "night" }, { mode: "Dark" }, { mode: "" }, { mode: null }, { mode: 1 }, { theme: "Harbor" }, { theme: "har bor" }, { theme: "../harbor" }, { theme: "a/b" }, { theme: "" }, { theme: 7 }, { theme: null }, { theme: `a${"b".repeat(64)}` }, { theme: "