#!/usr/bin/env python3 """0.33.7 Dashboard preset manager and chart-title font regression. Runs the bundled administration DOM/CSS/JavaScript with mocked account preferences. This is simulated-browser UI coverage, not a live RustProxy server test. """ import json import re from pathlib import Path from playwright.sync_api import sync_playwright ROOT = Path(__file__).resolve().parents[2] HTML = (ROOT / "src/admin/web/index.html").read_text(encoding="utf-8") CSS = (ROOT / "src/admin/web/style.css").read_text(encoding="utf-8") ROUTER = (ROOT / "src/admin/router.rs").read_text(encoding="utf-8") PACKS = { name: {"id": name, **json.loads((ROOT / "langs" / f"{name}.json").read_text(encoding="utf-8"))} for name in ("en_US", "pl_pl") } def app_bundle(): match = re.search(r"fn admin_app_javascript\([^)]*\)[^{]*\{", ROUTER) assert match body = ROUTER[match.end():] body = body[:re.search(r'\]\s*\.join\("\\n"\)', body).start()] result = [] for line in body.splitlines(): line = line.strip() include = re.search(r'include_str!\("([^"]+)"\)', line) if include: result.append((ROOT / "src/admin" / include.group(1)).read_text(encoding="utf-8")) elif line == "admin_vhosts_javascript(),": result.append("(() => {\n" + "\n".join( (ROOT / "src/admin/web/vhosts" / f"{name}.js").read_text(encoding="utf-8") for name in ("core", "protocol", "health", "rates", "editor", "render")) + "\n})();") elif line.startswith('"') and line.endswith('",'): result.append(json.loads(line[:-1])) return "\n".join(result) BUNDLE = app_bundle() def load(page): page.set_content(HTML.replace("/assets/app___RUSTPROXY_ASSET_ID__.js", "") .replace("/assets/style___RUSTPROXY_ASSET_ID__.css", "")) page.add_style_tag(content=CSS) page.add_script_tag(content=BUNDLE) page.wait_for_function("window.rustProxyPreferences && window.rustProxyDashboardLayout") page.evaluate("""packs => { languagePacks = new Map(Object.entries(packs)); defaultLanguage = 'en_US'; language = 'pl_pl'; document.documentElement.lang = 'pl-PL'; document.documentElement.classList.remove('app-loading', 'app-startup-failed'); document.getElementById('workspace').hidden = false; document.getElementById('panel-overview').hidden = false; document.getElementById('overview-layout-open').hidden = false; currentPanel = 'overview'; const visible = ['connection_chart', 'traffic_chart']; const hidden = ADMIN_DASHBOARD_WIDGETS.filter(id => !visible.includes(id)); window.__savedDashboard = { ...structuredClone(ADMIN_APPEARANCE_DEFAULTS), language: 'pl_pl', dashboard_density: 'ultra', dashboard_widgets: [...visible, ...hidden], dashboard_hidden_widgets: hidden, dashboard_widget_sizes: {connection_chart: 6, traffic_chart: 3}, dashboard_presets: [], }; window.rustProxyPreferences.load = async () => window.__savedDashboard; window.rustProxyPreferences.current = () => window.__savedDashboard; window.rustProxyPreferences.update = async patch => { window.__savedDashboard = {...window.__savedDashboard, ...structuredClone(patch)}; applyAdminPreferences(window.__savedDashboard); return window.__savedDashboard; }; applyAdminPreferences(window.__savedDashboard); language = 'pl_pl'; languagePreference = 'pl_pl'; for (const node of document.querySelectorAll('#overview-layout-dialog [data-i18n]')) node.textContent = t(node.dataset.i18n); for (const node of document.querySelectorAll('#overview-layout-dialog [data-i18n-placeholder]')) node.placeholder = t(node.dataset.i18nPlaceholder); }""", PACKS) with sync_playwright() as p: browser = p.chromium.launch(headless=True, executable_path="/usr/bin/chromium", args=["--no-sandbox"]) for width in (320, 375, 768, 1440): page = browser.new_page(viewport={"width": width, "height": 900}) errors = [] page.on("pageerror", lambda error: errors.append(str(error))) load(page) page.locator("#overview-layout-open").click() page.wait_for_function("document.getElementById('overview-layout-dialog').open") assert page.locator("#overview-layout-dialog-title").inner_text() == "Presety Dashboardu" assert page.locator("#overview-layout-visible").count() == 0 assert page.locator("#overview-layout-hidden").count() == 0 geom = page.locator("#overview-layout-dialog").evaluate("""node => ({ left: node.getBoundingClientRect().left, right: node.getBoundingClientRect().right, client: node.clientWidth, scroll: node.scrollWidth, })""") assert geom["left"] >= -1 and geom["right"] <= width + 1, (width, geom) assert geom["scroll"] <= geom["client"] + 2, (width, geom) page.locator("#overview-preset-name").fill("NOC") page.locator("#overview-preset-save").click() page.wait_for_function("window.__savedDashboard.dashboard_presets.length === 1") saved = page.evaluate("window.__savedDashboard.dashboard_presets[0]") assert saved["name"] == "NOC" assert saved["dashboard_density"] == "ultra" assert saved["dashboard_widgets"][:2] == ["connection_chart", "traffic_chart"] assert len(saved["dashboard_hidden_widgets"]) == len(saved["dashboard_widgets"]) - 2 assert saved["dashboard_widget_sizes"] == {"connection_chart": 6, "traffic_chart": 3} assert page.locator("#overview-preset-list .dashboard-preset-row").count() == 1 assert "2 widocznych kart" in page.locator(".dashboard-preset-info small").inner_text() page.locator("#overview-preset-name").fill("NOC") page.locator("#overview-preset-save").click() page.wait_for_function("document.getElementById('overview-layout-message').textContent.includes('Zaktualizowano')") assert page.evaluate("window.__savedDashboard.dashboard_presets.length") == 1 page.locator("#overview-layout-cancel").click() page.evaluate("""async () => { await window.rustProxyPreferences.update({ dashboard_density: 'normal', dashboard_widgets: [...ADMIN_DASHBOARD_WIDGETS], dashboard_hidden_widgets: [], dashboard_widget_sizes: null, }); }""") page.locator("#overview-layout-open").click() page.locator("#overview-preset-list .outline").click() page.wait_for_function("!document.getElementById('overview-layout-dialog').open") restored = page.evaluate("""() => ({ density: window.__savedDashboard.dashboard_density, hidden: window.__savedDashboard.dashboard_hidden_widgets.length, sizes: window.__savedDashboard.dashboard_widget_sizes, })""") assert restored == { "density": "ultra", "hidden": len(saved["dashboard_hidden_widgets"]), "sizes": {"connection_chart": 6, "traffic_chart": 3}, }, (width, restored) fonts = page.evaluate("""() => { const family = selector => getComputedStyle(document.querySelector(selector)).fontFamily; return { connection: family('[data-dashboard-widget="connection_chart"] .small-label'), traffic: family('[data-dashboard-widget="traffic_chart"] .card-heading h2'), wide: family('[data-dashboard-widget="wide_cpu"] .card-heading h2'), compact: family('[data-dashboard-widget="http_protocol"] > span:first-child'), }; }""") assert len(set(fonts.values())) == 1, (width, fonts) page.locator("#overview-layout-open").click() page.once("dialog", lambda prompt: prompt.accept()) page.locator("#overview-preset-list .quiet").click() page.wait_for_function("window.__savedDashboard.dashboard_presets.length === 0") assert page.locator("#overview-preset-list .dashboard-preset-row").count() == 0 assert page.locator("#overview-preset-empty").is_visible() # The browser enforces the same 20-preset limit as the preferences API. page.evaluate("""() => { const prefs = window.__savedDashboard; prefs.dashboard_presets = Array.from({length: 20}, (_, index) => ({ name: `Preset ${index + 1}`, dashboard_density: prefs.dashboard_density, dashboard_widgets: [...prefs.dashboard_widgets], dashboard_hidden_widgets: [...prefs.dashboard_hidden_widgets], dashboard_widget_sizes: prefs.dashboard_widget_sizes === null ? null : {...prefs.dashboard_widget_sizes}, })); applyAdminPreferences(prefs); window.rustProxyDashboardLayout.refresh(); }""") assert page.locator("#overview-preset-list .dashboard-preset-row").count() == 20 page.locator("#overview-preset-name").fill("Preset 21") page.locator("#overview-preset-save").click() assert page.evaluate("window.__savedDashboard.dashboard_presets.length") == 20 assert "Osiągnięto limit presetów" in page.locator("#overview-layout-message").inner_text() assert not errors, (width, errors) page.close() print(f"PASS Dashboard presets + chart title font at {width}px") browser.close() print("PASS 0.33.7 Dashboard preset manager regression (simulated API)")