100 lines
5.1 KiB
Python
Executable File
100 lines
5.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Render real Vhost cards with mock config/status to verify badge placement/color.
|
|
|
|
Headless Chromium fixture only: does not verify authenticated Rust runtime/ALPN.
|
|
"""
|
|
from pathlib import Path
|
|
import shutil
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
WEB = ROOT / 'src/admin/web'
|
|
source = (WEB / 'vhosts/render.js').read_text()
|
|
start = source.index('const VHOST_PROTOCOL_FRESH_MS = ')
|
|
end = source.index('function backendTrafficRate(', start)
|
|
renderer = source[start:end]
|
|
css = (WEB / 'style.css').read_text()
|
|
html = (f'<!doctype html><html data-theme="ultra_dark" data-corner-style="rounded">'
|
|
f'<head><meta name="viewport" content="width=device-width,initial-scale=1">'
|
|
f'<style>{css}</style></head><body><section id="panel-vhosts">'
|
|
f'<div id="backend-diagnostics-card"></div><input id="vhost-search">'
|
|
f'<div id="vhost-list"></div><span id="vhost-editing"></span>'
|
|
f'</section></body></html>')
|
|
stubs = r'''
|
|
const $ = id => document.getElementById(id);
|
|
const t = key => key;
|
|
const locale = () => 'en-US';
|
|
const el = (tag, value, cls) => {
|
|
const node=document.createElement(tag);
|
|
if (value !== undefined) node.textContent=value;
|
|
if (cls) node.className=cls;
|
|
return node;
|
|
};
|
|
const text = (tag, key, cls) => el(tag, t(key), cls);
|
|
const action = (name, fn) => {
|
|
const btn=el('button', name, 'quiet');
|
|
btn.addEventListener('click', fn);
|
|
return btn;
|
|
};
|
|
const button = (name, fn, cls) => {
|
|
const btn=action(name, fn);
|
|
if (cls) btn.className=cls;
|
|
return btn;
|
|
};
|
|
const initialized=true;
|
|
let editing=null, latestStatus=null;
|
|
const vhostListPrefs={sort:'id',descending:false};
|
|
const envelope={config:{listeners:{endpoints:[]},vhosts:[
|
|
{id:'proxy-test',enabled:true,frontend:{hosts:['proxy.example'],listener_ids:[]},
|
|
source:{type:'reverse_proxy'},force_ssl:true,websockets:true,
|
|
backends:[{id:'origin1',protocol:'auto'}, {id:'origin2',protocol:'http1'}]},
|
|
{id:'static-test',enabled:true,frontend:{hosts:['static.example'],listener_ids:[]},
|
|
source:{type:'web_server',web_service_id:'public'},
|
|
backends:[{id:'unused',protocol:'auto'}]},
|
|
]}};
|
|
'''
|
|
with sync_playwright() as playwright:
|
|
browser = playwright.chromium.launch(headless=True, executable_path=shutil.which('chromium'), args=['--no-sandbox'])
|
|
for width in (393, 1024):
|
|
page = browser.new_page(viewport={'width': width, 'height': 844})
|
|
page.set_content(html, wait_until='domcontentloaded')
|
|
page.add_script_tag(content=stubs + renderer + '\nrender();')
|
|
initial = page.evaluate('''() => {
|
|
const card=document.querySelector('[data-vhost-card="proxy-test"]');
|
|
const badge=card.querySelector('.vhost-backend-protocol-badge');
|
|
const group=card.querySelector('.vhost-policy-pills');
|
|
const staticCard=document.querySelector('[data-vhost-card="static-test"]');
|
|
return {badges:group.querySelectorAll('.vhost-backend-protocol-badge').length,
|
|
otherPills:group.querySelectorAll('.pill').length,
|
|
parent:badge.parentElement===group,
|
|
lastChild:card.lastElementChild===group,
|
|
separateRows:card.querySelectorAll('.vhost-backend-protocols').length,
|
|
staticBadges:staticCard.querySelectorAll('.vhost-backend-protocol-badge').length,
|
|
initialText:badge.textContent,
|
|
scroll:document.documentElement.scrollWidth,screen:innerWidth};
|
|
}''')
|
|
assert initial['badges'] == 2 and initial['otherPills'] == 2, initial
|
|
assert initial['parent'] and initial['lastChild'], initial
|
|
assert initial['separateRows'] == 0 and initial['staticBadges'] == 0, initial
|
|
assert initial['initialText'] == 'origin1 · AUTO → ?', initial
|
|
assert initial['scroll'] <= initial['screen'] + 2, initial
|
|
appearance = page.evaluate('''() => {
|
|
const badge=document.querySelector('[data-upstream-protocol-backend="origin1"]');
|
|
const expected=token=>{const n=document.createElement('span');n.style.color=`var(${token})`;
|
|
document.body.append(n);const c=getComputedStyle(n).color;n.remove();return c;};
|
|
const unknown=getComputedStyle(badge).color;
|
|
renderUpstreamProtocolBadges({backend_pools:[{id:'proxy-test',members:[
|
|
{id:'origin1',upstream_protocol_observed:'h2',upstream_protocol_last_seen_ms:Date.now()-1000},
|
|
{id:'origin2',upstream_protocol_observed:'h1',upstream_protocol_last_seen_ms:Date.now()-1000}
|
|
]}]});
|
|
const h2={text:badge.textContent,observed:badge.dataset.observed,
|
|
color:getComputedStyle(badge).color,expected:expected('--green')};
|
|
return {unknown, expectedUnknown:expected('--muted'), h2};
|
|
}''')
|
|
assert appearance['unknown'] == appearance['expectedUnknown'], (width, appearance)
|
|
assert appearance['h2']['color'] == appearance['h2']['expected'], (width, appearance)
|
|
assert appearance['h2']['observed'] == 'true' and appearance['h2']['text'] == 'origin1 · AUTO → H2', appearance
|
|
print(f'PASS: {width}px Vhost protocol badges share policy group and preserve green/neutral states')
|
|
page.close()
|
|
browser.close()
|