tests/themeTokens.test.ts
Rama main · réplica de lectura del repositorio original.
// The custom theme builder's token list (shared/customTheme.ts) against the
// two things it has to agree with: the surface layer in tokens.css, and the
// dictionary. A token the builder offers without a `:root, [data-theme]`
// derivation would read as "#000000 inherited" in every row; a token without
// a label in both languages would print its raw name to an Arabic reader —
// the half-translation check-i18n exists to catch, one level up.
// Both dictionaries, installed: under Node there is no chunk to fetch
// (client/i18n.ts loads one language at a time in a page).
import "../client/i18n/both.ts";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { describe, it } from "node:test";
import { fileURLToPath } from "node:url";
import { SURFACE_PAIRS } from "../shared/contrast.ts";
import { SURFACE_TOKENS, THEME_TOKENS, TOKEN_GROUPS, tokenSpec } from "../shared/customTheme.ts";
import { setLang, t } from "../client/i18n.ts";
const css = readFileSync(fileURLToPath(new URL("../client/styles/tokens.css", import.meta.url)), "utf8");
const surfaceBlock = /:root,\s*\[data-theme\]\s*\{([^}]*)\}/.exec(css);
describe("the theme builder's tokens", () => {
it("names every token once, in exactly one group the builder lays out", () => {
const names = THEME_TOKENS.map((spec) => spec.name);
assert.equal(new Set(names).size, names.length);
for (const spec of THEME_TOKENS) {
assert.ok(TOKEN_GROUPS.includes(spec.group), `${spec.name} sits in unknown group ${spec.group}`);
assert.equal(tokenSpec(spec.name), spec);
}
assert.ok(THEME_TOKENS.length >= 110, "the widest customisation the owner asked for is at least the surfaces");
});
it("gives every surface token a derivation in tokens.css, spelled as the spec says", () => {
assert.ok(surfaceBlock, "tokens.css carries the `:root, [data-theme]` surface layer");
const block = surfaceBlock![1];
for (const spec of SURFACE_TOKENS) {
const m = new RegExp(`${spec.name.replace(/[-]/g, "\\-")}\\s*:\\s*([^;]+);`).exec(block);
assert.ok(m, `${spec.name} has no default in the surface layer`);
assert.equal(m![1].trim(), spec.derivedFrom, `${spec.name} derives from what the spec says`);
}
// And nothing in the layer that the builder does not offer: a token the
// CSS reads but nobody can set is a setting that does not exist.
for (const m of block.matchAll(/(--[\w-]+)\s*:/g)) {
assert.ok(tokenSpec(m[1]), `${m[1]} is in the surface layer but not in THEME_TOKENS`);
}
});
it("labels every token in both languages, with real Arabic", () => {
for (const spec of THEME_TOKENS) {
setLang("en");
const en = t(spec.label as never);
setLang("ar");
const ar = t(spec.label as never);
assert.notEqual(en, spec.label, `${spec.name} has no English label`);
assert.ok(/[-ۿ]/.test(ar), `${spec.name}'s Arabic label carries no Arabic script: ${ar}`);
assert.notEqual(en, ar, `${spec.name} has the same label in both languages`);
}
setLang("en");
});
it("measures only pairs the builder can set, and every surface ground it pairs is a plain colour", () => {
for (const [token, against] of SURFACE_PAIRS) {
assert.ok(tokenSpec(token), `${token} is measured but not offered`);
assert.ok(tokenSpec(against), `${against} is measured but not offered`);
assert.equal(tokenSpec(against)!.kind, "color", `${against} is a wash; a ratio against it is not a promise`);
}
});
});