960696745f
- Migration 20261008180000: NextcloudFilesConfig (Organisation) und NextcloudFilesAccount (Mandant UND Benutzer per Zeilenschutz, nur verschlüsseltes App-Passwort) - Transportschicht ncRequest: feste Pfadanfänge, Segmentcodierung, keine Weiterleitungen, keine Cookies, Zeitgrenzen; Aufrufsperre pausiert den Ursprung nach jedem 429 und sperrt Zugangsschlüssel nach dem ersten 401 - Einstellungen: Adresse speichern/prüfen, Adresswechsel läuft alle Konten ab (mit Bestätigung) - Controller mit Verwalten nur auf Einstellungen, Seed, Modul, Registrierung in Web (Ladefunktion, Symbol folder, Navigation, Layout) und Reiter Einstellungen - RLS-Inventar fortgeschrieben, Testaufbau (tessera-nc-test) und E2E-Skripte Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
77 lines
3.2 KiB
TypeScript
77 lines
3.2 KiB
TypeScript
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 DomainsLayout from './domains/layout';
|
|
import HandelswareDatevLayout from './handelsware-datev/layout';
|
|
import KantineDatevLayout from './kantine-datev/layout';
|
|
import NextcloudFilesLayout from './nextcloud-files/layout';
|
|
import NextcloudStatusLayout from './nextcloud-status/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 = <div data-testid="placeholder-child">child</div>;
|
|
|
|
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],
|
|
['kantine-datev', KantineDatevLayout],
|
|
['handelsware-datev', HandelswareDatevLayout],
|
|
['nextcloud-status', NextcloudStatusLayout],
|
|
['domains', DomainsLayout],
|
|
['nextcloud-files', NextcloudFilesLayout],
|
|
] 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);
|
|
}
|
|
});
|
|
});
|