83 lines
4.5 KiB
Python
Executable File
83 lines
4.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Browser regression: compact, matching Overview gutters in both directions.
|
|
|
|
Fixture uses live CSS and the real grid grouping script, but mocked content.
|
|
"""
|
|
from pathlib import Path
|
|
from bs4 import BeautifulSoup
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
root = Path(__file__).resolve().parents[2] / "src/admin/web"
|
|
soup = BeautifulSoup((root / "index.html").read_text(), "html.parser")
|
|
widgets = soup.select("#dashboard-overview-grid > [data-dashboard-widget]")
|
|
assert len(widgets) == 62, len(widgets)
|
|
css = (root / "style.css").read_text()
|
|
html = ("<!DOCTYPE html><html data-theme='dark' data-dashboard-density='normal'>"
|
|
"<head><meta charset='utf-8'><style>" + css + "</style></head>"
|
|
"<body><section id='panel-overview' style='width:1680px'>"
|
|
"<div id='dashboard-overview-grid' class='stats-grid dashboard-overview-grid'>"
|
|
+ "".join(map(str, widgets)) + "</div></section></body></html>")
|
|
|
|
snapshot = """() => {
|
|
const grid = document.getElementById('dashboard-overview-grid');
|
|
const style = getComputedStyle(grid);
|
|
const parentGap = parseFloat(getComputedStyle(document.body).getPropertyValue('--ui-gap'));
|
|
const rows = [];
|
|
for (const node of grid.children) {
|
|
if (!node.dataset.dashboardWidget || node.hidden) continue;
|
|
const rect = node.getBoundingClientRect();
|
|
const group = window.rustProxyMasonry.groupFor(node.dataset.dashboardWidget);
|
|
let row = rows.find(r => Math.abs(r.top - rect.top) < 0.75);
|
|
if (!row) {
|
|
row = {top: rect.top, bottom: rect.bottom, group, cards: []};
|
|
rows.push(row);
|
|
}
|
|
row.bottom = Math.max(row.bottom, rect.bottom);
|
|
row.cards.push({left: rect.left, right: rect.right, bottom: rect.bottom});
|
|
}
|
|
return {
|
|
rowGap: parseFloat(style.rowGap), columnGap: parseFloat(style.columnGap),
|
|
parentGap, margin: style.marginBottom,
|
|
rows: rows.sort((a, b) => a.top - b.top)
|
|
};
|
|
}"""
|
|
|
|
with sync_playwright() as playwright:
|
|
browser = playwright.chromium.launch(
|
|
headless=True, executable_path="/usr/bin/chromium", args=["--no-sandbox"])
|
|
page = browser.new_page(viewport={"width": 1800, "height": 1100})
|
|
page.set_content(html)
|
|
page.add_script_tag(content=(root / "app/overview-masonry.js").read_text())
|
|
checks = 0
|
|
for width in (900, 1220, 1680):
|
|
page.evaluate("w => document.getElementById('panel-overview').style.width = w + 'px'", width)
|
|
for ui_density, old_gap in (("normal", 5), ("dense", 4), ("comfortable", 13)):
|
|
page.evaluate("d => {document.body.dataset.density = d === 'normal' ? '' : d;}", ui_density)
|
|
for card_density in ("normal", "compact", "ultra"):
|
|
page.evaluate("d => document.documentElement.dataset.dashboardDensity = d", card_density)
|
|
data = page.evaluate(snapshot)
|
|
expected = max(3, old_gap - 1)
|
|
assert abs(data["parentGap"] - old_gap) < 0.01, data
|
|
assert abs(data["rowGap"] - expected) < 0.01, data
|
|
assert abs(data["columnGap"] - expected) < 0.01, data
|
|
assert data["margin"] in ("0px", "0"), data
|
|
rows = data["rows"]
|
|
assert len(rows) >= 4, (width, ui_density, card_density)
|
|
for before, after in zip(rows[:10], rows[1:11]):
|
|
distance = after["top"] - before["bottom"]
|
|
assert abs(distance - expected) < 1.05, (width, ui_density, card_density, distance, expected)
|
|
for row in rows[:10]:
|
|
cards = sorted(row["cards"], key=lambda card: card["left"])
|
|
assert max(c["bottom"] for c in cards) - min(c["bottom"] for c in cards) < 1.1
|
|
for a, b in zip(cards, cards[1:]):
|
|
assert abs(b["left"] - a["right"] - expected) < 1.1, (
|
|
width, ui_density, card_density, a, b, expected)
|
|
first_large = next((i for i, row in enumerate(rows) if row["group"] != 0), None)
|
|
assert first_large is not None and first_large > 0
|
|
transition = rows[first_large]["top"] - rows[first_large-1]["bottom"]
|
|
assert abs(transition - expected) < 1.05, (width, ui_density, card_density, transition)
|
|
checks += 1
|
|
print(f"PASS: {width}px/{ui_density}: uniform {expected}px gutters, all Dashboard densities")
|
|
browser.close()
|
|
print(f"PASS: {checks} Chromium layout checks including small-to-large transitions")
|