// Regression contract: Appearance list is badge-free, corners are account-scoped, // and Settings opens Appearance/last active tab unless a valid URL tab wins. import assert from 'node:assert/strict'; import fs from 'node:fs'; import vm from 'node:vm'; const prefs = fs.readFileSync('src/admin/web/app/preferences.js', 'utf8'); const settings = fs.readFileSync('src/admin/web/settings.js', 'utf8'); const css = fs.readFileSync('src/admin/web/style.css', 'utf8'); const html = fs.readFileSync('src/admin/web/index.html', 'utf8'); const login = fs.readFileSync('src/admin/web/login.js', 'utf8'); const rust = fs.readFileSync('src/admin/preferences.rs', 'utf8'); const tests = fs.readFileSync('src/admin/preferences_tests.rs', 'utf8'); const schema = JSON.parse(fs.readFileSync('src/admin/openapi.json', 'utf8')); const en = JSON.parse(fs.readFileSync('langs/en_US.json', 'utf8')).translations; const pl = JSON.parse(fs.readFileSync('langs/pl_pl.json', 'utf8')).translations; const styles = ['default', 'square', 'subtle', 'rounded', 'extra']; const appearance = prefs.slice(prefs.indexOf('function renderDashboardWidgetSettings()'), prefs.indexOf('function refreshChartStylePalette')); assert(appearance.includes('dashboard-widget-setting-actions')); assert(!appearance.includes('dashboardWidgetTypeBadge('), 'Appearance rows must not render type badges'); assert(prefs.includes('function dashboardWidgetTypeBadge('), 'visual editors retain type labels'); assert(html.includes('id="ui-corner-style"')); assert(html.includes('id="dashboard-widget-list"')); const contract = schema.components.schemas.AdminPreferences.properties.corner_style; assert.deepEqual(contract.enum, styles); assert.equal(contract.default, 'default'); assert(rust.includes('default_admin_corner_style')); assert(rust.includes('"default" | "square" | "subtle" | "rounded" | "extra"')); assert(tests.includes('fn corner_style_defaults_and_rejects_unknown_presets()')); for (const key of ['cornerStyle', 'cornerStyleHelp', 'cornerStyleDefault', 'cornerStyleSquare', 'cornerStyleSubtle', 'cornerStyleRounded', 'cornerStyleExtra']) { assert(en[key] && pl[key], `missing translation ${key}`); } assert(!/html:is\(\[data-theme="ultra_dark"\], \[data-theme="cream"\]\) :is\(\.card, \.stat, dialog\) \{/.test(css), 'theme may not override corner geometry'); assert(css.includes('--logo-accent: #f69b74;')); assert(css.includes('--logo-secondary: #cfe0ac;')); assert(css.includes('var(--logo-accent, var(--accent))')); assert(login.includes('rp-corner-style'), 'cached corners must work on login too'); const decl = prefs.match(/^const ADMIN_CORNER_STYLES = .*;$/m)?.[0]; const normalize = prefs.match(/^function normalizeAdminCornerStyle\(value\) \{[^\n]*\}$/m)?.[0]; const body = prefs.split('function applyAdminCornerStyle(value, cache = true) {')[1]?.split('function applyAdminTableDensity(')[0]; assert(decl && normalize && body); const storage = new Map(); const root = { dataset: {} }, input = { value: '' }; const ctx = { document: { documentElement: root }, $: id => id === 'ui-corner-style' ? input : null, localStorage: { setItem: (key, value) => storage.set(key, value) } }; vm.createContext(ctx); vm.runInContext(`${decl}\n${normalize}\nfunction applyAdminCornerStyle(value, cache = true) {${body}`, ctx); for (const style of styles) { assert.equal(vm.runInContext(`applyAdminCornerStyle('${style}')`, ctx), style); assert.equal(root.dataset.cornerStyle, style); assert.equal(storage.get('rp-corner-style'), style); assert.equal(input.value, style); } vm.runInContext("applyAdminCornerStyle('invalid')", ctx); assert.equal(root.dataset.cornerStyle, 'default'); assert(prefs.includes('corner_style: "default"'), 'old account defaults preserve original geometry'); assert(prefs.includes('corner_style: next'), 'choice persisted via API'); assert(prefs.includes('applyAdminCornerStyle(previous)'), 'failed update rolls back'); const setup = settings.slice(settings.indexOf(' const buttons ='), settings.indexOf(' const policyTabs =')); assert(setup.length > 1000); function boot(path, cached, query = '') { const cache = new Map(cached ? [['rp-settings-last-tab', cached]] : []); const names = ['appearance', 'listeners', 'cache', 'security', 'manual-config', 'logging']; const buttons = names.map(name => ({ dataset: { settingsTab: name }, hidden: false, textContent: name, addEventListener() {}, setAttribute() {}, focus() {} })); const pages = names.map(name => ({ dataset: { settingsPage: name }, hidden: true })); const url = new URL(`https://dashboard.example${path}${query}`); const location = { href: url.href, pathname: url.pathname, search: url.search, hash: '', protocol: url.protocol }; const context = { location, URL, URLSearchParams, CSS: { escape: value => value }, localStorage: { getItem: key => cache.get(key) || null, setItem: (key, value) => cache.set(key, value) }, document: { querySelectorAll: selector => selector === '[data-settings-tab]' ? buttons : pages, querySelector: () => null }, $: () => null, history: { state: {}, replaceState(_state, _title, href) { location.href = 'https://dashboard.example' + href; } }, window: { rustProxyRefresh: { setSettingsTab() {} } }, t: key => key, }; const vmctx = vm.createContext(context); const result = vm.runInContext(`(() => { ${setup}\nreturn { current: () => active, select }; })()`, vmctx); return { ...result, cache }; } assert.equal(boot('/settings').current(), 'appearance', 'first Settings visit opens Appearance'); assert.equal(boot('/settings', 'cache').current(), 'cache', 'last Settings tab is restored'); assert.equal(boot('/settings', 'unknown').current(), 'appearance', 'invalid cached tab is ignored'); assert.equal(boot('/settings', 'cache', '?tab=security').current(), 'security', 'URL tab takes precedence'); assert.equal(boot('/configuration', 'cache').current(), 'manual-config', 'legacy /configuration keeps intent'); assert.equal(boot('/logging', 'cache').current(), 'logging', 'legacy /logging keeps intent'); const active = boot('/settings'); active.select('cache'); assert.equal(active.cache.get('rp-settings-last-tab'), 'cache'); active.select('security'); assert.equal(active.cache.get('rp-settings-last-tab'), 'security'); active.select('appearance', false, false); assert.equal(active.cache.get('rp-settings-last-tab'), 'security', 'temporary selection does not override remembered tab'); console.log('PASS: badge-free Appearance, 5 global corner presets, logo colors, account/API defaults, and last Settings tab navigation');