// Rôle : vérifie les conventions statiques des styles Sass. import { readFile } from "node:fs/promises"; import { test } from "node:test"; import assert from "node:assert/strict"; async function readJoinedStyles(paths) { return (await Promise.all(paths.map((path) => readFile(path, "utf8")))).join("\n"); } function countLines(source) { return source.trimEnd().split("\n").length; } test("style entrypoint and theme layers are wired", async () => { const styles = await readFile("website/src/styles/main.scss", "utf8"); const styleNebula = await readFile("website/src/styles/_nebula.scss", "utf8"); const styleTokens = await readFile("website/src/styles/_tokens.scss", "utf8"); const styleBase = await readFile("website/src/styles/_base.scss", "utf8"); const styleMixins = await readFile("website/src/styles/_mixins.scss", "utf8"); const styleShell = await readFile("website/src/styles/_shell.scss", "utf8"); const styleCards = await readFile("website/src/styles/_cards.scss", "utf8"); const styleHome = await readJoinedStyles([ "website/src/styles/_home.scss", "website/src/styles/home/_shared.scss", "website/src/styles/home/_onboarding.scss", "website/src/styles/home/_library.scss", "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/_story.scss", "website/src/styles/home/_about.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/home/_stats.scss" ]); const styleToolboxesEntry = await readFile("website/src/styles/_toolboxes.scss", "utf8"); const styleToolboxesCore = await readFile("website/src/styles/toolboxes/_core.scss", "utf8"); const styleToolboxLayout = await readJoinedStyles([ "website/src/styles/toolboxes/_layout.scss", "website/src/styles/toolboxes/layout/_header.scss", "website/src/styles/toolboxes/layout/_icon-picker.scss", "website/src/styles/toolboxes/layout/_tutorial.scss", "website/src/styles/toolboxes/layout/_add-tools.scss", "website/src/styles/toolboxes/layout/_preferences.scss", "website/src/styles/toolboxes/layout/_modules.scss" ]); const styleToolboxShell = await readFile("website/src/styles/toolboxes/_module-shell.scss", "utf8"); const styleToolboxTextExchange = await readFile("website/src/styles/toolboxes/_text-exchange.scss", "utf8"); const styleToolboxNotepad = await readFile("website/src/styles/toolboxes/_notepad.scss", "utf8"); const styleToolboxChecklist = await readFile("website/src/styles/toolboxes/_checklist.scss", "utf8"); const styleToolboxCalculator = await readFile("website/src/styles/toolboxes/_calculator.scss", "utf8"); const styleToolboxCalculatorResults = await readFile("website/src/styles/toolboxes/_calculator-results.scss", "utf8"); const styleToolboxTable = await readFile("website/src/styles/toolboxes/_table.scss", "utf8"); const styleToolboxCombos = await readJoinedStyles([ "website/src/styles/toolboxes/_combos.scss", "website/src/styles/toolboxes/combos/_editor.scss", "website/src/styles/toolboxes/combos/_palette.scss", "website/src/styles/toolboxes/combos/_list.scss", "website/src/styles/toolboxes/combos/_sequence.scss", "website/src/styles/toolboxes/combos/_tokens.scss", "website/src/styles/toolboxes/combos/_hold.scss" ]); const styleToolboxImages = await readFile("website/src/styles/toolboxes/_images.scss", "utf8"); const styleToolboxGoalTree = await readJoinedStyles([ "website/src/styles/toolboxes/_goal-tree.scss", "website/src/styles/toolboxes/goal-tree/_editor.scss", "website/src/styles/toolboxes/goal-tree/_view.scss", "website/src/styles/toolboxes/goal-tree/_nodes.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" ]); const styleToolboxCalendar = await readJoinedStyles([ "website/src/styles/toolboxes/_calendar.scss", "website/src/styles/toolboxes/calendar/_base.scss", "website/src/styles/toolboxes/calendar/_form.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", "website/src/styles/toolboxes/calendar/_week.scss", "website/src/styles/toolboxes/calendar/_month.scss", "website/src/styles/toolboxes/calendar/_responsive.scss" ]); const styleToolboxSharedControls = await readJoinedStyles([ "website/src/styles/toolboxes/_shared-controls.scss", "website/src/styles/toolboxes/shared-controls/_selects.scss", "website/src/styles/toolboxes/shared-controls/_split-controls.scss", "website/src/styles/toolboxes/shared-controls/_split-tree.scss", "website/src/styles/toolboxes/shared-controls/_mix-tags.scss", "website/src/styles/toolboxes/shared-controls/_entries.scss" ]); const styleOverlays = await readJoinedStyles([ "website/src/styles/_overlays.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" ]); const styleIcons = await readJoinedStyles([ "website/src/styles/_icons.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" ]); const styleGames = await readJoinedStyles([ "website/src/styles/_games.scss", "website/src/styles/games/_home-cards.scss", "website/src/styles/games/_heading.scss", "website/src/styles/games/_lists.scss", "website/src/styles/games/_filters.scss", "website/src/styles/games/_cards.scss" ]); const styleMhwilds = await readFile("website/src/styles/_mhwilds.scss", "utf8"); const styleResponsive = await readFile("website/src/styles/_responsive.scss", "utf8"); assert.match(styles, /@use "tokens"/); assert.match(styles, /@use "nebula"/); assert.match(styles, /@use "toolboxes"/); assert.match(styleToolboxesEntry, /@use "toolboxes\/core"/); assert.match(styleToolboxesCore, /@use "layout"/); assert.match(styleToolboxesCore, /@use "module-shell"/); assert.match(styleToolboxesCore, /@use "task-planner"/); assert.match(styleToolboxesCore, /@use "equipment-planner"/); assert.match(styleToolboxesCore, /@use "shared-controls"/); assert.match(styles, /@use "mhwilds"/); assert.match(styles, /@use "diablo4"/); assert.match(styleTokens, /--gradient-brand/); assert.match(styleBase, /:is\(/); assert.match(styleBase, /\.card-icon-button/); assert.match(styleBase, /\.drawer-action-button/); assert.match(styleBase, /\.import-icon-button/); assert.match(styleBase, /themed-scrollbar/); assert.match(styleBase, /body\.is-modal-open/); assert.match(styleNebula, /@function star-field/); assert.match(styleNebula, /body::after/); assert.match(styleHome, /\.dialogue-line i/); assert.match(styleHome, /font-style: italic/); assert.match(styleHome, /\.about-page/); assert.match(styleHome, /\.nebula-panel/); assert.match(styleHome, /\.home-onboarding-layout/); assert.match(styleHome, /\.home-onboarding-slider figcaption/); assert.match(styleHome, /\.home-onboarding-dots/); assert.match(styleHome, /\.about-story-section/); assert.match(styleHome, /\.about-tools-list/); assert.match(styleHome, /\.about-tool-item \.module-icon/); assert.match(styleMixins, /@mixin gold-panel-frame/); assert.match(styleShell, /\.sidebar-about-link/); assert.match(styleShell, /\.sidebar-mascot/); assert.match(styleToolboxShell, /\.module-icon-abacus/); assert.match(styleToolboxShell, /\.module-icon-calculator/); assert.match(styleToolboxShell, /\.module-icon-columns/); assert.match(styleToolboxShell, /\.module-icon-table/); assert.match(styleToolboxCalculator, /\.calculator-module/); assert.match(styleToolboxTable, /\.table-module/); assert.match(styleToolboxTable, /\.table-cell-input\.is-selected/); assert.match(styleToolboxTable, /\.table-cell-input\.is-move-target/); assert.match(styleToolboxGoalTree, /\.goal-tree-node-card/); assert.match(styleToolboxGoalTree, /\.goal-tree-node-content-row/); assert.match(styleToolboxGoalTree, /\.goal-tree-node.is-drop-target/); assert.match(styleToolboxCalendar, /\.calendar-event-form/); assert.match(styleToolboxCalendar, /\.calendar-month-date-picker/); assert.match(styleToolboxCalendar, /\.calendar-alert-mode-switch/); assert.match(styleToolboxSharedControls, /\.notepad-toolbar-button\.danger/); assert.match(styleToolboxLayout, /\.toolbox-icon-picker/); assert.match(styleToolboxLayout, /\.toolbox-game-icon/); assert.match(styleToolboxLayout, /\.toolbox-hero-link/); assert.match(styleToolboxLayout, /\.toolbox-actions-row/); assert.match(styleToolboxTextExchange, /\.text-import-actions/); assert.match(styleToolboxTextExchange, /\.text-import-modal-dialog/); assert.doesNotMatch(styleToolboxNotepad, /lecture seule/); assert.match(styleIcons, /ui-icon-drag/); assert.match(styleIcons, /ui-icon-dropdown/); assert.match(styleIcons, /ui-icon-enter/); assert.match(styleIcons, /ui-icon-calculator/); assert.match(styleIcons, /ui-icon-scrollable/); assert.match(styleIcons, /ui-icon-hide/); assert.match(styleIcons, /ui-icon-eye-open/); assert.match(styleIcons, /ui-icon-eye-closed/); assert.match(styleIcons, /ui-icon-chevron-down/); assert.match(styleIcons, /ui-icon-chevron-up/); assert.match(styleIcons, /ui-icon-flip/); assert.match(styleIcons, /ui-icon-add/); assert.match(styleIcons, /ui-icon-add-row/); assert.match(styleIcons, /ui-icon-add-column/); assert.match(styleIcons, /ui-icon-remove-row/); assert.match(styleIcons, /ui-icon-remove-column/); assert.match(styleIcons, /ui-icon-table/); assert.match(styleIcons, /ui-icon-map/); assert.match(styleIcons, /ui-icon-grab/); assert.match(styleIcons, /ui-icon-shield/); assert.match(styleIcons, /ui-icon-gun/); assert.match(styleToolboxShell, /module-icon-chest-armor/); assert.match(styleToolboxCombos, /\.combo-hold-controls/); assert.match(styleToolboxCombos, /\.combo-input-direction/); assert.match(styleToolboxCombos, /\.combo-device-n64\.combo-value-stick-up/); assert.match(styleToolboxCombos, /\.combo-device-n64\.combo-value-up/); assert.match(styleToolboxCombos, /\.combo-value-grab \.ui-icon/); assert.match(styleToolboxChecklist, /\.timer-alert-mode-button/); assert.match(styleToolboxSharedControls, /\.timer-alert-mode-switch\.is-blocked/); assert.match(styleToolboxCalculatorResults, /\.calculator-result/); assert.match(styleToolboxSharedControls, /\.tool-split-scroll-toggle/); assert.match(styleToolboxImages, /\.annotation-marker/); assert.match(styleGames, /\.game-home-card/); assert.match(styleGames, /\.game-layout/); assert.match(styleGames, /\.game-grid/); assert.match(styleGames, /\.filter-panel/); assert.match(styleMhwilds, /\.mhwilds-card/); assert.match(styleMhwilds, /\.mhwilds-flip-indicator/); assert.doesNotMatch(styleMhwilds, /\.game-(home-card|layout|grid)/); assert.match(styleCards, /compact-data-card/); assert.match(styleOverlays, /overscroll-behavior: contain/); assert.match(styleResponsive, /@media \(max-width: 760px\)/); }); 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`); } });