v0.33.3
This commit is contained in:
1 parent
5a36bb0c4b
commit
78ad28ef4d
27 files changed
+1448
-234
No files matched your search
+109
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Real-browser regression for Overview chart content when card width changes.
|
||||
|
||||
Runs the production HTML/CSS/assembled admin JS against a simulated DOM and
|
||||
fake network-interface samples. This does not exercise Rust HTTP/SSE.
|
||||
"""
|
||||
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()
|
||||
CSS = (ROOT / 'src/admin/web/style.css').read_text()
|
||||
ROUTER = (ROOT / 'src/admin/router.rs').read_text()
|
||||
|
||||
|
||||
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()]
|
||||
scripts = []
|
||||
for line in body.splitlines():
|
||||
line = line.strip()
|
||||
include = re.search(r'include_str!\("([^"]+)"\)', line)
|
||||
if include:
|
||||
scripts.append((ROOT / 'src/admin' / include.group(1)).read_text())
|
||||
elif line == 'admin_vhosts_javascript(),':
|
||||
scripts.append('(() => {\n' + '\n'.join(
|
||||
(ROOT / 'src/admin/web/vhosts' / (name + '.js')).read_text()
|
||||
for name in ('core', 'protocol', 'health', 'rates', 'editor', 'render')) + '\n})();')
|
||||
elif line.startswith('"') and line.endswith('",'):
|
||||
scripts.append(json.loads(line[:-1]))
|
||||
return '\n'.join(scripts)
|
||||
|
||||
|
||||
def inspect(page):
|
||||
return page.evaluate('''() => {
|
||||
const selectors = {
|
||||
'websocket_protocol':'[data-dashboard-widget="websocket_protocol"] svg',
|
||||
'wide_transform_activity':'[data-dashboard-widget="wide_transform_activity"] svg',
|
||||
'www_requests':'[data-dashboard-widget="www_requests"] svg',
|
||||
'traffic_chart':'[data-dashboard-widget="traffic_chart"] svg',
|
||||
'system_chart':'[data-dashboard-widget="system_chart"] svg',
|
||||
'cache_diagnostics':'[data-dashboard-widget="cache_diagnostics"] .mini-live-chart svg',
|
||||
'network_diagnostics':'[data-dashboard-widget="network_diagnostics"] .mini-live-chart svg',
|
||||
'network_interface':'[data-dashboard-widget="network_diagnostics"] .network-interface-row svg',
|
||||
};
|
||||
return Object.fromEntries(Object.entries(selectors).map(([name, selector]) => {
|
||||
const svg = document.querySelector(selector);
|
||||
if (!svg) throw new Error('Missing SVG: ' + name);
|
||||
const box = svg.getBoundingClientRect();
|
||||
const matrix = svg.getScreenCTM();
|
||||
const viewBox = svg.viewBox.baseVal;
|
||||
const contentWidth = Math.hypot(matrix.a, matrix.b) * viewBox.width;
|
||||
const series = svg.querySelector('.overview-wide-series');
|
||||
return [name, {width:box.width, contentWidth,
|
||||
preservation:svg.getAttribute('preserveAspectRatio'),
|
||||
lineStroke: series ? getComputedStyle(series).vectorEffect : null}];
|
||||
}));
|
||||
}''')
|
||||
|
||||
|
||||
with sync_playwright() as browser_api:
|
||||
browser = browser_api.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
||||
for screen_width in (320, 375, 800, 1440, 1920):
|
||||
page = browser.new_page(viewport={'width': screen_width, 'height': 920})
|
||||
errors = []
|
||||
page.on('pageerror', lambda error: errors.append(str(error)))
|
||||
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=app_bundle())
|
||||
page.evaluate('''() => {
|
||||
document.documentElement.classList.remove('app-loading', 'app-startup-failed');
|
||||
document.getElementById('workspace').hidden = false;
|
||||
document.getElementById('panel-overview').hidden = false;
|
||||
currentPanel = 'overview';
|
||||
renderNetworkInterfaceCharts([{name:'eth0',rx_bytes_per_second:12500,tx_bytes_per_second:5200}], true);
|
||||
}''')
|
||||
ids = ['websocket_protocol', 'wide_transform_activity', 'www_requests', 'traffic_chart',
|
||||
'system_chart', 'cache_diagnostics', 'network_diagnostics']
|
||||
results = []
|
||||
for span in (3, 12):
|
||||
page.evaluate('''({ids,span}) => {
|
||||
const hidden = ADMIN_DASHBOARD_WIDGETS.filter(id => !ids.includes(id));
|
||||
const sizes = Object.fromEntries(ids.map(id => [id, span]));
|
||||
applyDashboardLayout([...ids, ...hidden], hidden, sizes);
|
||||
}''', {'ids': ids, 'span': span})
|
||||
measured = inspect(page)
|
||||
results.append(measured)
|
||||
for chart, value in measured.items():
|
||||
assert value['width'] > 50, (screen_width, span, chart, value)
|
||||
assert abs(value['width'] - value['contentWidth']) <= 2, (
|
||||
'chart not filling its own SVG viewport', screen_width, span, chart, value)
|
||||
assert value['preservation'] == 'none', (screen_width, chart, value)
|
||||
if value['lineStroke'] is not None:
|
||||
assert value['lineStroke'] == 'non-scaling-stroke', (chart, value)
|
||||
if screen_width >= 1440:
|
||||
for name in results[0]:
|
||||
assert results[1][name]['width'] > results[0][name]['width'] * 1.4, (
|
||||
'chart does not grow when widening card', screen_width, name, results)
|
||||
assert page.evaluate('document.documentElement.scrollWidth - innerWidth') <= 2, screen_width
|
||||
assert not errors, (screen_width, errors)
|
||||
print('PASS SVG chart uses full width at spans 3 and 12:', screen_width)
|
||||
page.close()
|
||||
browser.close()
|
||||
print('PASS resizable Overview chart content regression')
|
||||
Reference in new issue
Block a user