feat: consolidate shared UI and harden browser authority for release
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import vm from "node:vm";
|
||||
import ts from "typescript";
|
||||
|
||||
const webuiRoot = resolve(import.meta.dirname, "..");
|
||||
const repositoryRoot = resolve(webuiRoot, "..", "..");
|
||||
@@ -9,6 +11,7 @@ const app = readFileSync(resolve(webuiRoot, "src/App.tsx"), "utf8");
|
||||
const settings = readFileSync(resolve(webuiRoot, "src/features/settings/SettingsPage.tsx"), "utf8");
|
||||
const paletteControl = readFileSync(resolve(webuiRoot, "src/components/AppearancePaletteControl.tsx"), "utf8");
|
||||
const overridesEditor = readFileSync(resolve(webuiRoot, "src/components/AppearanceOverridesEditor.tsx"), "utf8");
|
||||
const overridesRuntime = readFileSync(resolve(webuiRoot, "src/components/appearanceOverrides.ts"), "utf8");
|
||||
|
||||
assert.match(tokens, /:root\[data-theme="dark"\]/, "dark token overrides are required");
|
||||
assert.match(tokens, /color-scheme:\s*dark/, "native controls must receive the dark color scheme");
|
||||
@@ -24,11 +27,38 @@ for (const palette of ["default", "civic_blue", "forest", "plum"]) {
|
||||
assert.match(settings, /AppearancePaletteSelect/, "personal settings must use the shared palette control");
|
||||
assert.match(settings, /AppearanceOverridesEditor/, "personal settings must use the shared override editor");
|
||||
assert.match(app, /applyAppearanceOverrides/, "the shell must apply validated overrides centrally");
|
||||
assert.match(overridesEditor, /schema_version:\s*"1"/, "override exchange must use an explicit versioned schema");
|
||||
assert.match(overridesEditor, /contrastRatio[\s\S]*?<\s*4\.5/, "custom pairs must enforce WCAG AA contrast");
|
||||
assert.match(overridesEditor, /rgbDistance[\s\S]*?<\s*12/, "custom status colors must enforce differentiation");
|
||||
assert.match(app, /import \{ applyAppearanceOverrides \} from "\.\/components\/appearanceOverrides"/, "startup applies themes synchronously without importing settings editor controls");
|
||||
assert.doesNotMatch(overridesRuntime, /import .*from ["']react["']|\.tsx|ColorPickerField|ContentGrid/, "the startup theme runtime must stay independent of editor UI");
|
||||
assert.match(overridesEditor, /from "\.\/appearanceOverrides"/, "editor and runtime share the same validation implementation");
|
||||
assert.match(overridesRuntime, /schema_version:\s*"1"/, "override exchange must use an explicit versioned schema");
|
||||
assert.match(overridesRuntime, /contrastRatio[\s\S]*?<\s*4\.5/, "custom pairs must enforce WCAG AA contrast");
|
||||
assert.match(overridesRuntime, /rgbDistance[\s\S]*?<\s*12/, "custom status colors must enforce differentiation");
|
||||
const runtimeExports = {};
|
||||
vm.runInNewContext(ts.transpileModule(overridesRuntime, {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 }
|
||||
}).outputText, { exports: runtimeExports });
|
||||
const validOverrides = runtimeExports.cloneDefaultAppearanceOverrides();
|
||||
assert.equal(runtimeExports.validateAppearanceOverrides(validOverrides), validOverrides);
|
||||
const colorProperties = new Map();
|
||||
const root = { style: {
|
||||
setProperty: (property, value) => colorProperties.set(property, value),
|
||||
removeProperty: (property) => colorProperties.delete(property)
|
||||
} };
|
||||
runtimeExports.applyAppearanceOverrides(root, validOverrides, "dark");
|
||||
assert.equal(colorProperties.get("--accent"), validOverrides.dark.accent);
|
||||
assert.equal(colorProperties.get("--danger-text"), validOverrides.dark.danger_foreground);
|
||||
runtimeExports.applyAppearanceOverrides(root, validOverrides, "light");
|
||||
assert.equal(colorProperties.get("--accent"), validOverrides.light.accent);
|
||||
const invalidOverrides = runtimeExports.cloneDefaultAppearanceOverrides();
|
||||
invalidOverrides.light.accent_foreground = invalidOverrides.light.accent;
|
||||
assert.throws(() => runtimeExports.validateAppearanceOverrides(invalidOverrides));
|
||||
runtimeExports.applyAppearanceOverrides(root, invalidOverrides, "dark");
|
||||
assert.equal(colorProperties.size, 0, "invalid documents clear previous custom tokens and never partially apply");
|
||||
runtimeExports.applyAppearanceOverrides(root, validOverrides, "light");
|
||||
runtimeExports.applyAppearanceOverrides(root, null, "light");
|
||||
assert.equal(colorProperties.size, 0, "reset restores inherited CSS values synchronously");
|
||||
for (const token of ["accent", "surface", "success", "info", "warning", "danger"]) {
|
||||
assert.match(overridesEditor, new RegExp(`"--${token}`), `runtime overrides must map ${token} into shared tokens`);
|
||||
assert.match(overridesRuntime, new RegExp(`"--${token}`), `runtime overrides must map ${token} into shared tokens`);
|
||||
}
|
||||
for (const relativePath of [
|
||||
"govoplan-admin/webui/src/features/admin/SystemSettingsPanel.tsx",
|
||||
|
||||
Reference in New Issue
Block a user