import { existsSync, readdirSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import CertManagerLayout from './cert-manager/layout'; import DkvFleetLayout from './dkv-fleet/layout'; import DomaincheckLayout from './domaincheck/layout'; import TenderRadarLayout from './tender-radar/layout'; /** * Covers T-e8k-01, T-e8k-03, and T-e8k-04 (WINDOWS #10, PERM-04): * * - Each module-owned layout wraps children in ModuleAccessGate with the * slug that exactly matches its directory name — a copy-paste mistake * between the four near-identical files would flip the wrong module's * gate (T-e8k-03). * - Every non-dynamic module directory has a layout.tsx — a future module * directory added without one would run past the gate again exactly * like the four routes this plan fixes (T-e8k-04). */ const modulesDir = dirname(fileURLToPath(import.meta.url)); /** * Ordner ohne ModuleAccessGate, bewusst (quick-260929-9wc): `custom/` traegt * die Rahmen-Seiten der „Eigenen Module“. Das sind keine Zeilen der Tabelle * `Module` und haengen an keiner Aktivierung oder Freigabe — sie sind fuer * ALLE angemeldeten Benutzer sichtbar (D-01). Jeder weitere Eintrag hier ist * eine bewusste Ausnahme von T-e8k-04 und gehoert begruendet. */ const DIRS_WITHOUT_GATE: readonly string[] = ['custom']; const placeholderChild =
child
; describe('module layouts — ModuleAccessGate slug wiring (T-e8k-01, T-e8k-03)', () => { it.each([ ['cert-manager', CertManagerLayout], ['dkv-fleet', DkvFleetLayout], ['domaincheck', DomaincheckLayout], ['tender-radar', TenderRadarLayout], ] as const)('%s/layout.tsx passes moduleSlug="%s" and forwards children', (expectedSlug, Layout) => { const element = Layout({ children: placeholderChild }); expect(element.props.moduleSlug).toBe(expectedSlug); expect(element.props.children).toBe(placeholderChild); }); }); describe('module directory coverage — every module has a layout (T-e8k-04)', () => { it('requires a layout.tsx in every non-dynamic module directory', () => { const entries = readdirSync(modulesDir, { withFileTypes: true }) .filter((entry) => entry.isDirectory()) .filter((entry) => !entry.name.startsWith('[')) .filter((entry) => !DIRS_WITHOUT_GATE.includes(entry.name)) .map((entry) => entry.name); expect(entries.length).toBeGreaterThan(0); for (const dirName of entries) { const hasLayout = existsSync(join(modulesDir, dirName, 'layout.tsx')) || existsSync(join(modulesDir, dirName, 'layout.ts')); expect(hasLayout, `${dirName}/ is missing a layout.tsx`).toBe(true); } }); });