66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
import { readdirSync, readFileSync, statSync } from "node:fs";
|
||
import { join, relative, resolve } from "node:path";
|
||
import { describe, expect, it } from "vitest";
|
||
|
||
const SRC = resolve(__dirname, "..");
|
||
const TOKENS = resolve(__dirname, "tokens.css");
|
||
|
||
function cssFiles(dir: string): string[] {
|
||
return readdirSync(dir).flatMap((name) => {
|
||
const p = join(dir, name);
|
||
if (statSync(p).isDirectory()) return cssFiles(p);
|
||
return p.endsWith(".css") ? [p] : [];
|
||
});
|
||
}
|
||
|
||
const stripComments = (s: string) => s.replace(/\/\*[\s\S]*?\*\//g, "");
|
||
const LITERAL =
|
||
/#[0-9a-fA-F]{3,8}\b|\b(?:rgb|rgba|hsl|hsla)\(|:\s*(?:white|black|red|green|blue|gray|grey|orange|yellow|purple)\b/g;
|
||
|
||
/** Тело блока, начинающегося с селектора `head` (по балансу скобок). */
|
||
function block(css: string, head: string): string {
|
||
const at = css.indexOf(head);
|
||
if (at < 0) throw new Error(`нет блока ${head}`);
|
||
let i = css.indexOf("{", at);
|
||
const start = i + 1;
|
||
let depth = 0;
|
||
for (; i < css.length; i++) {
|
||
if (css[i] === "{") depth++;
|
||
if (css[i] === "}" && --depth === 0) return css.slice(start, i);
|
||
}
|
||
throw new Error(`блок ${head} не закрыт`);
|
||
}
|
||
|
||
const names = (body: string) =>
|
||
[...body.matchAll(/(--color-[\w-]+)\s*:/g)].map((m) => m[1]).sort();
|
||
|
||
describe("токены AURA", () => {
|
||
it("вне tokens.css нет литералов цвета", () => {
|
||
const found: string[] = [];
|
||
for (const f of cssFiles(SRC)) {
|
||
if (f === TOKENS) continue;
|
||
for (const m of stripComments(readFileSync(f, "utf8")).matchAll(LITERAL)) {
|
||
found.push(`${relative(SRC, f)}: ${m[0]}`);
|
||
}
|
||
}
|
||
expect(found).toEqual([]);
|
||
});
|
||
|
||
const css = stripComments(readFileSync(TOKENS, "utf8"));
|
||
|
||
it("обе темы задают одни имена", () => {
|
||
const l = names(block(css, ":root {"));
|
||
const media = names(block(css, ':root:not([data-theme="light"])'));
|
||
const dark = names(block(css, ':root[data-theme="dark"]'));
|
||
expect(l.length).toBeGreaterThan(20);
|
||
expect(media).toEqual(l);
|
||
expect(dark).toEqual(l);
|
||
});
|
||
|
||
it("тёмные блоки и светлый :root задают color-scheme", () => {
|
||
expect(block(css, ":root {")).toContain("color-scheme: light");
|
||
expect(block(css, ':root:not([data-theme="light"])')).toContain("color-scheme: dark");
|
||
expect(block(css, ':root[data-theme="dark"]')).toContain("color-scheme: dark");
|
||
});
|
||
});
|