sokko-g/tests/static-styles.test.mjs
Shinuwa 00e73f0062
All checks were successful
Deploy Sokko G / deploy (push) Successful in 20s
Add responsive game filters and hybrid toolbox drawer
2026-09-27 18:00:49 +02:00

125 lines
6.3 KiB
JavaScript

// Rôle : protège les frontières Sass, les conventions publiques et le découpage des fichiers.
import { readFile, readdir } from "node:fs/promises";
import { test } from "node:test";
import assert from "node:assert/strict";
function countLines(source) {
return source.trimEnd().split("\n").length;
}
test("Sass layers retain ownership boundaries and exclude removed APIs", async () => {
const root = "website/src/styles";
const paths = (await readdir(root, { recursive: true })).filter((path) => path.endsWith(".scss"));
const sources = new Map(await Promise.all(paths.map(async (path) => [path, await readFile(`${root}/${path}`, "utf8")])));
assert.doesNotMatch(sources.get("_base.scss"), /\.(?:card-icon-button|drawer-action-button|import-icon-button)/);
assert.doesNotMatch(sources.get("_cards.scss"), /@use "design-system\/surfaces"|controls\.card-pattern|\.compact-data-card\s*\{/);
assert.doesNotMatch(sources.get("_mhwilds.scss"), /\.game-(home-card|layout|grid)/);
const home = [...sources].filter(([path]) => path === "_home.scss" || path.startsWith("home/")).map(([, source]) => source).join("\n");
assert.doesNotMatch(home, /\.nebula-panel\s*\{/);
assert.doesNotMatch(sources.get("toolboxes/_notepad.scss"), /lecture seule/);
assert.match(sources.get("design-system/_controls.scss"), /@forward "controls\/patterns"/);
assert.match(sources.get("design-system/_attributes.scss"), /@use "attributes\/patterns"/);
const branch = await readFile("website/src/features/toolboxes/modules/GoalTreeBranch.jsx", "utf8");
assert.doesNotMatch(
[...sources.values(), branch].join("\n"),
/(?:data-size="tiny"|icon-button(?:-icon)?\(tiny\)|\$size == tiny)/
);
});
test("product components keep the public NDS pattern vocabulary", async () => {
const paths = [
"website/src/pages/HomePage.jsx",
"website/src/pages/LibraryPage.jsx",
"website/src/pages/AboutPage.jsx",
"website/src/features/games/GamesPage.jsx",
"website/src/features/games/GameFiltersPanel.jsx",
"website/src/features/games/diablo4/Diablo4AffixCard.jsx",
"website/src/features/toolboxes/ToolboxCard.jsx",
"website/src/features/toolboxes/ToolboxPages.jsx",
"website/src/components/AppOverlays.jsx",
"website/src/components/StorageQuota.jsx",
"website/src/components/ToolboxModals.jsx",
"website/src/features/toolboxes/modules/AddToolControls.jsx",
"website/src/features/toolboxes/modules/TextImportModal.jsx"
];
const consumers = (await Promise.all(paths.map((path) => readFile(path, "utf8")))).join("\n");
for (const family of [
"page-hero", "panel", "card", "card-cover", "tool-panel", "filter-panel",
"chip", "badge", "empty-state", "inline-notice", "progress", "modal", "drawer-panel", "toast"
]) {
assert.ok(consumers.includes(`nds-${family}`), `public nds-${family} convention must remain in use`);
}
});
test("filter patterns expose vertical, compact and menu layouts", async () => {
const attributes = await readFile("website/src/styles/design-system/attributes/_patterns.scss", "utf8");
const catalog = await readFile("website/src/pages/DesignSystemPage.jsx", "utf8");
const documentation = await readFile("DESIGN_SYSTEM.md", "utf8");
for (const variant of ["vertical", "horizontal-compact", "horizontal-menu"]) {
assert.match(catalog, new RegExp(variant));
assert.match(documentation, new RegExp(variant));
}
assert.match(attributes, /nds-filter-menu/);
assert.match(attributes, /data-columns="2"/);
assert.match(attributes, /grid-template-columns: repeat\(2/);
assert.match(attributes, /nds-filter-option-label/);
const gameFilters = await readFile("website/src/styles/games/_filters.scss", "utf8");
assert.match(gameFilters, /nds-active-filters/);
assert.match(gameFilters, /nds-active-filter/);
assert.match(documentation, /composant commun `Tooltip`/);
});
test("large sass files stay split by stable responsibility", async () => {
const orchestrators = [
"website/src/styles/home/_library.scss",
"website/src/styles/home/_about.scss",
"website/src/styles/_overlays.scss",
"website/src/styles/toolboxes/goal-tree/_nodes.scss",
"website/src/styles/_icons.scss",
"website/src/styles/toolboxes/calendar/_form.scss"
];
const partials = [
"website/src/styles/home/library/_page.scss",
"website/src/styles/home/library/_summary.scss",
"website/src/styles/home/library/_toc.scss",
"website/src/styles/home/library/_tool-cards.scss",
"website/src/styles/home/library/_examples.scss",
"website/src/styles/home/library/_previews.scss",
"website/src/styles/home/about/_page.scss",
"website/src/styles/home/about/_story.scss",
"website/src/styles/home/about/_contribute.scss",
"website/src/styles/home/about/_tools.scss",
"website/src/styles/home/about/_reminders.scss",
"website/src/styles/overlays/_image-viewer.scss",
"website/src/styles/overlays/_drawers.scss",
"website/src/styles/overlays/_modals.scss",
"website/src/styles/overlays/_notifications.scss",
"website/src/styles/overlays/_tool-add.scss",
"website/src/styles/toolboxes/goal-tree/_node-card.scss",
"website/src/styles/toolboxes/goal-tree/_node-content.scss",
"website/src/styles/toolboxes/goal-tree/_node-actions.scss",
"website/src/styles/toolboxes/goal-tree/_node-drag.scss",
"website/src/styles/toolboxes/goal-tree/_node-responsive.scss",
"website/src/styles/icons/_base.scss",
"website/src/styles/icons/_navigation.scss",
"website/src/styles/icons/_toolbox.scss",
"website/src/styles/icons/_actions.scss",
"website/src/styles/icons/_equipment.scss",
"website/src/styles/toolboxes/calendar/_form-layout.scss",
"website/src/styles/toolboxes/calendar/_date-picker.scss",
"website/src/styles/toolboxes/calendar/_event-controls.scss",
"website/src/styles/toolboxes/calendar/_form-actions.scss"
];
for (const path of orchestrators) {
const source = await readFile(path, "utf8");
assert.ok(source.startsWith("// Rôle :"), `${path} must keep a role header`);
assert.ok(countLines(source) <= 120, `${path} should remain an orchestrator`);
}
for (const path of partials) {
const source = await readFile(path, "utf8");
assert.ok(source.startsWith("// Rôle :"), `${path} must keep a role header`);
assert.ok(countLines(source) <= 300, `${path} should stay scoped to one responsibility`);
}
});