v0.33.2
This commit is contained in:
1 parent
b757b34a31
commit
5a36bb0c4b
20 files changed
+356
-186
No files matched your search
+106
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Progressbar Appearance regression using the production CSS in Chromium.
|
||||
|
||||
Browser-only visual/CSS verification. Does not execute RustProxy or authenticate.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
from PIL import Image
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
CSS = (ROOT / 'src/admin/web/style.css').read_text(encoding='utf-8')
|
||||
APP_HTML = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8')
|
||||
assert re.search(r'<div class="overview-meter system-memory-meter" id="memory-meter"', APP_HTML)
|
||||
assert 'id="memory-meter-fill"' in APP_HTML
|
||||
CORNERS = {'default': '3px', 'square': '0px', 'subtle': '2px', 'rounded': '4px', 'extra': '999px'}
|
||||
STYLES = ('default', 'soft', 'technical', 'high_contrast', 'neon', 'monochrome', 'warm', 'cool')
|
||||
|
||||
HTML = '''<!doctype html><html><body>
|
||||
<section id="panel-cache"><div class="stats-grid cache-stats">
|
||||
<article class="stat"><progress id="cp-hot-entries-meter" value="55" max="100"></progress></article>
|
||||
<article class="stat"><progress id="cp-memory" value="55" max="100"></progress></article>
|
||||
<article class="stat"><progress id="cp-cold-entries-meter" value="55" max="100"></progress></article>
|
||||
<article class="stat"><progress id="cp-cold-disk" value="55" max="100"></progress></article>
|
||||
</div></section>
|
||||
<main id="panel-overview">
|
||||
<div class="stat stat-meter"><div class="overview-meter" id="overview-progress"><span class="overview-meter-fill" id="overview-fill" style="width:55%"></span></div></div>
|
||||
<article class="card system-card"><div class="overview-meter system-memory-meter" id="memory-meter" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="55"><span class="overview-meter-fill" id="memory-meter-fill" style="width:55%"></span></div></article>
|
||||
<div class="www-segmented" id="www-segmented"><span style="width:55%;background:var(--chart-accent)"></span></div>
|
||||
<div class="www-top-meter" id="www-top"><span id="www-top-fill" style="width:55%"></span></div>
|
||||
</main>
|
||||
<div class="traffic-bar-track" id="traffic-track"><span class="traffic-bar-fill" id="traffic-fill" style="width:55%"></span></div>
|
||||
<div id="chart-swatch" style="background:var(--chart-accent);width:4px;height:4px"></div>
|
||||
</body></html>'''
|
||||
|
||||
|
||||
def color_at_progress(page, progress_id: str) -> tuple[int, int, int]:
|
||||
rect = page.locator(f'#{progress_id}').bounding_box()
|
||||
assert rect and rect['width'] > 100
|
||||
screenshot = Image.open(BytesIO(page.screenshot())).convert('RGB')
|
||||
x = round(rect['x'] + rect['width'] * .22)
|
||||
y = round(rect['y'] + rect['height'] / 2)
|
||||
return screenshot.getpixel((x, y))
|
||||
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
browser = playwright.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
||||
for width in (375, 1440):
|
||||
page = browser.new_page(viewport={'width': width, 'height': 900})
|
||||
errors = []
|
||||
page.on('pageerror', lambda error: errors.append(str(error)))
|
||||
page.set_content(HTML)
|
||||
page.add_style_tag(content=CSS)
|
||||
for corner, radius in CORNERS.items():
|
||||
page.evaluate('corner => document.documentElement.dataset.cornerStyle = corner', corner)
|
||||
actual = page.evaluate('''() => {
|
||||
const ids = ['cp-hot-entries-meter','cp-memory','cp-cold-entries-meter','cp-cold-disk',
|
||||
'overview-progress','overview-fill','memory-meter','memory-meter-fill','www-segmented','www-top','www-top-fill',
|
||||
'traffic-track','traffic-fill'];
|
||||
return Object.fromEntries(ids.map(id => [id, getComputedStyle(document.getElementById(id)).borderRadius]));
|
||||
}''')
|
||||
assert all(value == radius for value in actual.values()), (corner, radius, actual)
|
||||
actual_pseudo = page.evaluate('''() => {
|
||||
const p = document.getElementById('cp-memory');
|
||||
return ['::-webkit-progress-bar', '::-webkit-progress-value'].map(pseudo => getComputedStyle(p, pseudo).borderRadius);
|
||||
}''')
|
||||
assert actual_pseudo == [radius, radius], (corner, actual_pseudo)
|
||||
|
||||
for theme in ('dark', 'light'):
|
||||
page.evaluate('theme => document.documentElement.dataset.theme = theme', theme)
|
||||
for chart_style in STYLES:
|
||||
page.evaluate('style => document.documentElement.dataset.chartStyle = style', chart_style)
|
||||
observed = page.evaluate('''() => {
|
||||
const color = id => getComputedStyle(document.getElementById(id)).backgroundColor;
|
||||
const progress = document.getElementById('cp-memory');
|
||||
return {
|
||||
chart:color('chart-swatch'), native:getComputedStyle(progress).accentColor,
|
||||
overview:color('overview-fill'), systemRam:color('memory-meter-fill'), www:color('www-top-fill'), traffic:color('traffic-fill'),
|
||||
track:getComputedStyle(progress).backgroundColor,
|
||||
overviewTrack:color('overview-progress'), systemTrack:color('memory-meter'), wwwTrack:color('www-top'),
|
||||
};
|
||||
}''')
|
||||
assert len({observed[key] for key in ('chart', 'native', 'overview', 'systemRam', 'www', 'traffic')}) == 1, (theme, chart_style, observed)
|
||||
assert len({observed[key] for key in ('track', 'overviewTrack', 'systemTrack', 'wwwTrack')}) == 1, (theme, chart_style, observed)
|
||||
# Native progress on Chromium is painted by the WebKit pseudo-element;
|
||||
# a pixel check confirms the color, not just the accent-color property.
|
||||
if chart_style in ('default', 'neon', 'monochrome'):
|
||||
color = color_at_progress(page, 'cp-memory')
|
||||
chart_rgb = tuple(page.evaluate("""() => {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = canvas.height = 1;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = getComputedStyle(document.getElementById('chart-swatch')).backgroundColor;
|
||||
ctx.fillRect(0, 0, 1, 1);
|
||||
return [...ctx.getImageData(0, 0, 1, 1).data].slice(0, 3);
|
||||
}"""))
|
||||
assert color == chart_rgb, (theme, chart_style, color, chart_rgb)
|
||||
assert not errors, errors
|
||||
page.close()
|
||||
browser.close()
|
||||
|
||||
print('PASS: native Cache, System RAM, Overview, WWW and Traffic bars respond to 5 corners and 8 palettes (dark/light, 375/1440px).')
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""System Load RAM meter updates using the actual combined panel JavaScript.
|
||||
|
||||
This is a simulated authenticated DOM/status test, not a Rust runtime integration test.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import json
|
||||
import re
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
router = (ROOT / 'src/admin/router.rs').read_text()
|
||||
|
||||
def bundle_for(section):
|
||||
match = re.search(r'fn ' + section + r'\([^)]*\)[^{]*\{', router)
|
||||
assert match, section
|
||||
text = router[match.end():]
|
||||
text = text[:re.search(r'\]\s*\.join\("\\n"\)', text).start()]
|
||||
result = []
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if 'include_str!("' in line:
|
||||
path = re.search(r'include_str!\("([^"]+)"\)', line).group(1)
|
||||
result.append((ROOT / 'src/admin' / path).read_text())
|
||||
elif line == 'admin_vhosts_javascript(),':
|
||||
result.append('(() => {\n' + ''.join((ROOT / 'src/admin/web/vhosts' / f'{part}.js').read_text() + '\n'
|
||||
for part in ['core', 'protocol', 'health', 'rates', 'editor', 'render']) + '})();')
|
||||
elif line.startswith('"') and line.endswith('",'):
|
||||
result.append(json.loads(line[:-1]))
|
||||
return '\n'.join(result)
|
||||
|
||||
html = (ROOT / 'src/admin/web/index.html').read_text()
|
||||
css = (ROOT / 'src/admin/web/style.css').read_text()
|
||||
js = bundle_for('admin_app_javascript')
|
||||
with sync_playwright() as playwright:
|
||||
browser = playwright.chromium.launch(headless=True, executable_path='/usr/bin/chromium', args=['--no-sandbox'])
|
||||
for width in (375, 1440):
|
||||
page = browser.new_page(viewport={'width': width, 'height': 900})
|
||||
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=js)
|
||||
page.wait_for_timeout(100)
|
||||
report = page.evaluate('''() => {
|
||||
currentPanel = "overview";
|
||||
document.getElementById('workspace').hidden = false;
|
||||
document.getElementById('panel-overview').hidden = false;
|
||||
const meter = document.getElementById('memory-meter');
|
||||
const fill = document.getElementById('memory-meter-fill');
|
||||
const snapshot = (used, total) => ({system: {source:'measured',cpu_percent:2.1,
|
||||
memory_used_bytes:used, memory_total_bytes:total},
|
||||
live_system:{cpu_percent:2.1}, cache:{}, metrics:{}, listeners:{}});
|
||||
const values = [];
|
||||
for (const [used, total] of [[250,1000],[800,1000],[0,1000],[null,null]]) {
|
||||
renderDiagnostics(snapshot(used,total), false, true);
|
||||
values.push({fill:fill.style.width, now:meter.getAttribute('aria-valuenow'),
|
||||
hidden:meter.hidden, text:meter.getAttribute('aria-valuetext')});
|
||||
}
|
||||
return { values, role:meter.getAttribute('role'), cssHeight:getComputedStyle(meter).height,
|
||||
scrollbar:document.documentElement.scrollWidth>window.innerWidth+2 };
|
||||
}''')
|
||||
assert report['role'] == 'progressbar' and report['cssHeight'] == '5px', report
|
||||
assert report['values'][0]['fill'] == '25%' and report['values'][0]['now'] == '25', report
|
||||
assert report['values'][1]['fill'] == '80%' and report['values'][1]['now'] == '80', report
|
||||
assert report['values'][2]['fill'] == '0%' and report['values'][2]['now'] == '0', report
|
||||
assert report['values'][3]['fill'] == '0%' and report['values'][3]['now'] is None and report['values'][3]['hidden'], report
|
||||
assert not report['scrollbar'] and not errors, (width, errors, report)
|
||||
page.close()
|
||||
browser.close()
|
||||
print('PASS: System RAM meter live percentage and unknown/zero handling on 375/1440px in bundled panel.')
|
||||
Reference in new issue
Block a user