* docs: prepare Vendoo 1.41.2 git-ignore boundary hotfix * fix: track native source modules with root-anchored runtime ignores
77 lines
5.7 KiB
JavaScript
77 lines
5.7 KiB
JavaScript
import { readFileSync } from 'fs';
|
|
import { dirname, join, resolve } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { spawnSync } from 'child_process';
|
|
import {
|
|
evaluateThemeAccessibility, getThemeContract, validateThemeDefinition, validateThemePreference,
|
|
} from '../app/core/themes/theme-contract.mjs';
|
|
|
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
const read = rel => readFileSync(join(root, rel), 'utf8');
|
|
const failures = [];
|
|
const assert = (condition, message) => { if (!condition) failures.push(message); };
|
|
const expectThrow = (fn, code, message) => {
|
|
try { fn(); failures.push(message); }
|
|
catch (error) { if (error.code !== code) failures.push(`${message} (erhalten ${error.code || error.message})`); }
|
|
};
|
|
|
|
const pkg = JSON.parse(read('package.json'));
|
|
assert(pkg.version === '1.40.2', `Paketversion ist ${pkg.version}`);
|
|
const source = JSON.parse(read('public/design-system/tokens/source.json'));
|
|
assert(source.contractVersion === '1.1.0', `Tokenvertrag ist ${source.contractVersion}`);
|
|
assert(Object.keys(source.tokens).length >= 42, `Zu wenige Tokens: ${Object.keys(source.tokens).length}`);
|
|
for (const token of ['component.buttonHeight','component.inputHeight','component.cardRadius','component.focusWidth','component.touchTarget']) {
|
|
assert(Boolean(source.tokens[token]), `Komponenten-Token fehlt: ${token}`);
|
|
}
|
|
|
|
const contract = getThemeContract();
|
|
assert(contract.tokens['color.brand'].label === 'Akzentfarbe', 'Token-Label fehlt');
|
|
assert(contract.tokens['component.buttonHeight'].group === 'Komponenten', 'Token-Gruppe fehlt');
|
|
const valid = validateThemeDefinition({ id: 'custom-markus-pro', name: 'Markus Pro', base_theme: 'dark', values: { 'color.brand': '#ff6600', 'component.buttonHeight': '44px' } });
|
|
assert(valid.id === 'custom-markus-pro', 'Gültiges Theme wurde nicht akzeptiert');
|
|
expectThrow(() => validateThemeDefinition({ id: 'unsafe', name: 'Unsafe', values: {} }), 'THEME_ID_PREFIX', 'Theme ohne custom-Präfix wurde akzeptiert');
|
|
expectThrow(() => validateThemeDefinition({ id: 'custom-unsafe', name: 'Unsafe', values: { 'color.brand': 'url(javascript:alert(1))' } }), 'THEME_VALUE_INVALID', 'CSS-Injektion wurde akzeptiert');
|
|
expectThrow(() => validateThemeDefinition({ id: 'custom-unsafe', name: 'Unsafe', values: { 'shadow.float': '0 0 1px red' } }), 'THEME_TOKEN_FORBIDDEN', 'Nicht anpassbarer Token wurde akzeptiert');
|
|
expectThrow(() => validateThemePreference({ theme_id: 'missing', density: 'comfortable', reduced_motion: 'system' }), 'THEME_NOT_FOUND', 'Unbekanntes Theme wurde akzeptiert');
|
|
const poorContrast = evaluateThemeAccessibility({ baseTheme: 'light', values: { 'color.ink': '#ffffff', 'color.canvas': '#ffffff', 'color.surface': '#ffffff' } });
|
|
assert(!poorContrast.ok && poorContrast.failures.length >= 2, 'Kontrastfehler wurde nicht erkannt');
|
|
|
|
const before = read('public/design-system/tokens.css');
|
|
const generated = spawnSync(process.execPath, [join(root, 'tools/generate-design-tokens.mjs')], { cwd: root, encoding: 'utf8' });
|
|
assert(generated.status === 0, `Token-Generator fehlgeschlagen: ${generated.stderr || generated.stdout}`);
|
|
assert(before === read('public/design-system/tokens.css'), 'Token-Generator ist nicht deterministisch');
|
|
|
|
const manifest = JSON.parse(read('app/modules/themes/module.json'));
|
|
assert(manifest.status === 'native', 'Theme-Modul ist nicht nativ');
|
|
assert(manifest.provides.includes('themes.accessibility'), 'Accessibility-Capability fehlt');
|
|
for (const file of [
|
|
'app/modules/themes/index.mjs','app/modules/themes/service.mjs','app/modules/themes/repository.mjs',
|
|
'app/modules/themes/migrations/001_theme_profiles.sql','public/modules/theme-manager/theme-manager.js',
|
|
'public/modules/theme-manager/theme-manager.css',
|
|
]) assert(read(file).length > 50, `Theme-Pflichtdatei fehlt oder ist leer: ${file}`);
|
|
|
|
const server = read('server.mjs');
|
|
for (const marker of [
|
|
"id: 'themes.catalog.read'", "id: 'themes.preference.update'", "id: 'themes.profile.create'",
|
|
"id: 'themes.profile.update'", "id: 'themes.profile.delete'", "id: 'themes.system-default.update'",
|
|
"policy: 'themes.manage'", "policy: 'themes.use'",
|
|
]) assert(server.includes(marker), `Theme-Route fehlt: ${marker}`);
|
|
assert(server.indexOf("path: '/api/themes/system-default'") < server.indexOf("path: '/api/themes/:id'"), 'Systemstandard-Route wird von :id überschattet');
|
|
|
|
const html = read('public/index.html');
|
|
for (const marker of ['id="theme-manager-root"','id="theme-choice-grid"','id="theme-token-groups"','id="theme-contrast-list"','theme-manager.js?v=1.40.2']) assert(html.includes(marker), `Theme-Manager UI fehlt: ${marker}`);
|
|
const manager = read('public/modules/theme-manager/theme-manager.js');
|
|
assert(!manager.includes('.innerHTML ='), 'Theme Manager verwendet unsicheres innerHTML');
|
|
for (const marker of ['/api/themes/validate','/api/themes/preference','/api/themes/system-default','window.VendooTheme?.previewTheme']) assert(manager.includes(marker), `Theme Manager Logik fehlt: ${marker}`);
|
|
const runtime = read('public/design-system/theme-runtime.js');
|
|
for (const marker of ['vendoo-theme-state-v2','dataset.reducedMotion','clearCustomProperties','previewTheme']) assert(runtime.includes(marker), `Theme Runtime fehlt: ${marker}`);
|
|
const schema = read('db/schema.sql');
|
|
for (const marker of ['CREATE TABLE IF NOT EXISTS theme_profiles','CREATE TABLE IF NOT EXISTS user_theme_preferences']) assert(schema.includes(marker), `Theme-Schema fehlt: ${marker}`);
|
|
|
|
if (failures.length) {
|
|
console.error(`Theme-Manager-Gate 1.40.2 fehlgeschlagen (${failures.length}):`);
|
|
failures.forEach(item => console.error(`- ${item}`));
|
|
process.exit(1);
|
|
}
|
|
console.log(`Theme-Manager-Gate 1.40.2 erfolgreich: ${Object.keys(source.tokens).length} Tokens, natives Theme-Modul, sichere Profile, Benutzerpräferenzen, Kontrastprüfung und tokenisierte Komponenten geprüft.`);
|