#!/usr/bin/env python3 """All Overview widget labels and small values in simulated Chromium. This is a DOM/CSS regression test, not a live RustProxy integration test. """ from pathlib import Path import json from panel_html import render_admin_index from playwright.sync_api import sync_playwright ROOT = Path(__file__).resolve().parents[2] HTML = render_admin_index(ROOT) CSS = (ROOT / 'src/admin/web/style.css').read_text(encoding='utf-8') PACKS = {name: json.loads((ROOT / f'langs/{name}.json').read_text(encoding='utf-8'))['translations'] for name in ('en_US', 'pl_pl')} CHECK = r'''({density, lang, translations}) => { document.documentElement.dataset.dashboardDensity = density; document.documentElement.lang = lang === 'pl_pl' ? 'pl-PL' : 'en-US'; const grid = document.getElementById('dashboard-overview-grid'); grid.querySelectorAll('[data-i18n]').forEach(node => { if (Object.hasOwn(translations, node.dataset.i18n)) node.textContent = translations[node.dataset.i18n]; }); const cards = [...grid.children].filter(node => node.hasAttribute('data-dashboard-widget')); const font = value => getComputedStyle(value).fontFamily; const probe = document.createElement('div'); grid.append(probe); probe.style.fontFamily = 'var(--sans)'; const sans = font(probe); probe.style.fontFamily = 'var(--mono)'; const mono = font(probe); probe.remove(); const titleErrors = []; const titles = []; const labelErrors = []; const numericErrors = []; for (const card of cards) { let title; if (card.classList.contains('stat')) title = card.querySelector(':scope > span:first-child'); else if (card.classList.contains('system-card') || card.classList.contains('connection-card')) title = card.querySelector('.overview-chart-heading'); else if (card.querySelector('.mini-heading > h3')) title = card.querySelector('.mini-heading > h3'); else title = card.querySelector('.card-heading h2'); if (!title) { titleErrors.push(`${card.dataset.dashboardWidget}: missing title`); continue; } const style = getComputedStyle(title); titles.push(title); if (style.fontFamily !== sans || style.fontSize !== '12px' || style.textTransform !== 'none') titleErrors.push(`${card.dataset.dashboardWidget}: ${style.fontFamily} ${style.fontSize} ${style.textTransform}`); if (title.textContent.trim() === title.textContent.trim().toLocaleUpperCase('pl') && /[a-ząćęłńóśżź]{4}/i.test(title.textContent) && !/^(?:HIT|MISS|HTTP|CPU|RAM|WWW|RX|TX)/.test(title.textContent.trim())) titleErrors.push(`${card.dataset.dashboardWidget}: all-uppercase title ${title.textContent.trim()}`); for (const node of card.querySelectorAll(':scope > small, .mini-metrics dt, .mini-metrics dd, .mini-live-chart-head, .memory-line, .chart-footer, .meta, .small-label')) { if (font(node) !== mono) labelErrors.push(`${card.dataset.dashboardWidget}: ${node.outerHTML.slice(0, 85)}`); } const readings = card.classList.contains('stat') ? [...card.querySelectorAll(':scope > strong'), ...card.querySelectorAll(':scope > small')] : [...card.querySelectorAll('.mini-metrics dd')]; for (const node of readings) if (font(node) !== mono) numericErrors.push(`${card.dataset.dashboardWidget}: ${node.outerHTML.slice(0, 85)}`); } const nested = [...grid.querySelectorAll('.mini-live-chart > .mini-live-chart-head > span:first-child')]; for (const node of nested) { const style = getComputedStyle(node); if (style.fontFamily !== mono || style.fontSize !== '10px' || style.textTransform !== 'none') labelErrors.push(`mini-live-chart: ${style.fontFamily} ${style.fontSize} ${style.textTransform}`); } const systemNumeric = getComputedStyle(document.querySelector('#system-cpu')); return { cardCount: cards.length, titleCount: titles.length, secondaryCount: nested.length, titleErrors, labelErrors, numericErrors, systemNumericMono: systemNumeric.fontFamily === mono, cpuLabel: document.querySelector('[data-dashboard-widget="system_chart"] .overview-chart-heading').textContent, connectionsLabel: document.querySelector('[data-dashboard-widget="connection_chart"] .overview-chart-heading').textContent, memoryLabel: document.querySelector('[data-dashboard-widget="memory"] > span:first-child').textContent, backendLabel: document.querySelector('[data-dashboard-widget="backend_health"] > span:first-child').textContent, diagnosticsTitle: document.querySelector('[data-dashboard-widget="process_diagnostics"] .mini-heading h3').textContent, }; }''' with sync_playwright() as playwright: browser = playwright.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox']) for width in (320, 375, 768, 1440, 1920): page = browser.new_page(viewport={'width': width, 'height': 900}) page.set_content(HTML.replace('/assets/app___RUSTPROXY_ASSET_ID__.js', '') .replace('/assets/style___RUSTPROXY_ASSET_ID__.css', '')) page.add_style_tag(content=CSS) for density in ('normal', 'compact', 'ultra'): for lang in ('en_US', 'pl_pl'): result = page.evaluate(CHECK, {'density': density, 'lang': lang, 'translations': PACKS[lang]}) descriptor = (width, density, lang) assert result['cardCount'] == result['titleCount'] >= 75, (descriptor, result) assert result['secondaryCount'] >= 6, descriptor assert not result['titleErrors'], (descriptor, result['titleErrors'][:10]) assert not result['labelErrors'], (descriptor, result['labelErrors'][:10]) assert not result['numericErrors'], (descriptor, result['numericErrors'][:10]) assert result['systemNumericMono'], descriptor expected = ('System load', 'Connection activity') if lang == 'en_US' else ('Obciążenie systemu', 'Aktywność połączeń') assert (result['cpuLabel'], result['connectionsLabel']) == expected, (descriptor, result) assert result['memoryLabel'] and result['backendLabel'] and result['diagnosticsTitle'], descriptor print(f'PASS all Overview titles and small readings at {width}px (3 densities, 2 languages)') page.close() browser.close()