-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathrender_gui.py
More file actions
96 lines (82 loc) · 4.2 KB
/
Copy pathrender_gui.py
File metadata and controls
96 lines (82 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
"""Visual check for the web GUI: render index.html in headless Chromium.
python3 render_gui.py OUT_DIR [width height]...
WebView2 is Chromium, so a Chromium screenshot is what the Windows app shows --
same layout engine, same compositor, same font rasteriser. Runs the page's own
MockApi, so the render path under test is the one that ships.
"""
from __future__ import annotations
import pathlib
import sys
from playwright.sync_api import sync_playwright
HERE = pathlib.Path(__file__).parent
PAGE = (HERE / "web" / "index.html").resolve()
def shoot(out_dir: pathlib.Path, sizes: list[tuple[int, int]]) -> int:
out_dir.mkdir(parents=True, exist_ok=True)
problems: list[str] = []
with sync_playwright() as p:
browser = p.chromium.launch(args=["--force-color-profile=srgb",
"--font-render-hinting=none"])
for width, height in sizes:
page = browser.new_page(viewport={"width": width, "height": height},
device_scale_factor=1)
errors: list[str] = []
page.on("pageerror", lambda e: errors.append(str(e)))
page.on("console", lambda m: errors.append(m.text) if m.type == "error" else None)
page.goto(PAGE.as_uri())
page.wait_for_selector(".frow", timeout=8000)
page.wait_for_timeout(2600) # let the mock engine fill the spark
shot = out_dir / f"web_{width}x{height}_files.png"
page.screenshot(path=str(shot))
page.click('button[data-tab="log"]')
page.wait_for_timeout(400)
page.screenshot(path=str(out_dir / f"web_{width}x{height}_log.png"))
page.click('button[data-tab="files"]')
# Overflow audit: anything wider than the viewport means a layout bug.
overflow = page.evaluate(
"""() => {
const bad = [];
for (const el of document.querySelectorAll('*')) {
const r = el.getBoundingClientRect();
if (r.width && (r.right > window.innerWidth + 1 || r.left < -1))
bad.push(el.tagName + '.' + (el.className || '') + ' right=' + Math.round(r.right));
}
return bad.slice(0, 8);
}"""
)
metrics = page.evaluate(
"""() => ({
rows: document.querySelectorAll('.frow').length,
speed: document.querySelector('#vSpeed').textContent,
spark: (document.querySelector('#sparkLine').getAttribute('points') || '').length,
scrollW: document.documentElement.scrollWidth,
innerW: window.innerWidth,
fontPx: getComputedStyle(document.documentElement).fontSize,
label: getComputedStyle(document.querySelector('.s-label')).fontSize,
})"""
)
print(f"{width}x{height}: rows={metrics['rows']} speed={metrics['speed']} "
f"sparkpts={metrics['spark']} root={metrics['fontPx']} "
f"slider-label={metrics['label']} scrollW={metrics['scrollW']}/{metrics['innerW']}")
if errors:
problems.append(f"{width}x{height} js errors: {errors[:3]}")
if overflow:
problems.append(f"{width}x{height} overflow: {overflow}")
if metrics["scrollW"] > metrics["innerW"] + 1:
problems.append(f"{width}x{height} horizontal scroll")
if metrics["rows"] < 8:
problems.append(f"{width}x{height} only {metrics['rows']} rows rendered")
page.close()
browser.close()
for line in problems:
print("[FAIL]", line)
if not problems:
print("[OK] no js errors, no overflow, no horizontal scroll")
return 1 if problems else 0
def main() -> int:
out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else HERE / "shots")
raw = sys.argv[2:]
sizes = [(2554, 1400), (1440, 900)] if not raw else [
(int(raw[i]), int(raw[i + 1])) for i in range(0, len(raw) - 1, 2)]
return shoot(out, sizes)
if __name__ == "__main__":
raise SystemExit(main())