Files
hermes-webui/tests/test_issue5545_three_panel_layout.py
T

135 lines
5.1 KiB
Python

"""
Issue #5545 three-panel desktop layout regression checks.
These tests stay source-level so they can verify the declared CSS contract
without needing a browser or rendered layout.
"""
import pathlib
import re
REPO = pathlib.Path(__file__).parent.parent
CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8")
BOOT_JS = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
def _media_blocks(kind):
pattern = re.compile(rf"@media\s*\(\s*{kind}\s*:\s*(\d+)px\s*\)\s*\{{")
blocks = []
for match in pattern.finditer(CSS):
width_px = int(match.group(1))
open_brace = match.end() - 1
depth = 0
for idx in range(open_brace, len(CSS)):
if CSS[idx] == "{":
depth += 1
elif CSS[idx] == "}":
depth -= 1
if depth == 0:
blocks.append((width_px, CSS[open_brace + 1:idx]))
break
return blocks
def _normalize_css(css):
return re.sub(r"\s+", "", css)
def _media_block(kind, width_px, needle):
normalized_needle = _normalize_css(needle)
for current_width, block in _media_blocks(kind):
if current_width == width_px and normalized_needle in _normalize_css(block):
return block
raise AssertionError(f"Missing @media({kind}:{width_px}px) block containing {needle!r}")
def _strip_css_comments(css):
return re.sub(r"/\*.*?\*/", "", css, flags=re.DOTALL)
def _rule_body(css, selector):
for match in re.finditer(r"([^{}]+)\{([^{}]*)\}", _strip_css_comments(css)):
selectors = {part.strip() for part in match.group(1).split(",")}
if selector in selectors:
return match.group(2)
raise AssertionError(f"Missing CSS rule for {selector}")
def _declarations(rule_body):
declarations = {}
for item in rule_body.split(";"):
if ":" not in item:
continue
prop, value = item.split(":", 1)
declarations[prop.strip()] = re.sub(r"\s+", " ", value.strip())
return declarations
def test_desktop_three_panel_contract_gives_main_readable_floor():
desktop = _media_block("min-width", 901, ".main{flex:1 1 420px;min-width:420px;}")
main = _declarations(_rule_body(desktop, ".main"))
assert main.get("flex") == "1 1 420px"
assert main.get("min-width") == "420px"
def test_desktop_side_rails_can_shrink_to_resize_minima():
desktop = _media_block("min-width", 901, ".sidebar{flex-shrink:1;min-width:180px;}")
sidebar = _declarations(_rule_body(desktop, ".sidebar"))
rightpanel = _declarations(_rule_body(desktop, ".rightpanel"))
closed_rightpanel = _declarations(
_rule_body(desktop, 'html[data-workspace-panel="closed"] .rightpanel')
)
collapsed_rightpanel = _declarations(
_rule_body(desktop, ".layout.workspace-panel-collapsed .rightpanel")
)
assert re.search(r"\bSIDEBAR_MIN\s*=\s*180\b", BOOT_JS)
assert re.search(r"\bPANEL_MIN\s*=\s*180\b", BOOT_JS)
assert sidebar.get("flex-shrink") == "1"
assert sidebar.get("min-width") == "180px"
assert rightpanel.get("flex-shrink") == "1"
assert rightpanel.get("min-width") == "180px"
assert closed_rightpanel.get("width") == "0 !important"
assert closed_rightpanel.get("min-width") == "0 !important"
assert collapsed_rightpanel.get("width") == "0 !important"
assert collapsed_rightpanel.get("min-width") == "0 !important"
def test_compact_breakpoint_900px_remains_hidden_right_panel_boundary():
assert "@media(max-width:900px)" in CSS or "@media (max-width: 900px)" in CSS
compact = _media_block("max-width", 900, ".rightpanel{display:none}")
rightpanel = _declarations(_rule_body(compact, ".rightpanel"))
workspace_toggle = _declarations(_rule_body(compact, ".workspace-toggle-btn"))
mobile_files = _declarations(_rule_body(compact, ".mobile-files-btn"))
assert rightpanel.get("display") == "none"
assert workspace_toggle.get("display") == "inline-flex!important"
assert mobile_files.get("display") == "inline-flex!important"
def test_mobile_slide_over_breakpoint_640px_remains_intact():
assert "@media(max-width:640px)" in CSS or "@media (max-width: 640px)" in CSS
mobile = _media_block("max-width", 640, ".rightpanel.mobile-open")
rightpanel = _declarations(_rule_body(mobile, ".rightpanel"))
rightpanel_open = _declarations(_rule_body(mobile, ".rightpanel.mobile-open"))
assert rightpanel.get("display") == "flex!important"
assert rightpanel.get("position") == "fixed"
assert rightpanel.get("right") == "calc(-1 * var(--mobile-rightpanel-width))!important"
assert rightpanel.get("width") == "var(--mobile-rightpanel-width)!important"
assert rightpanel.get("box-shadow") == "none!important"
assert rightpanel_open.get("right") == "0!important"
def test_no_unmatched_desktop_hide_breakpoint_above_900():
hidden_widths = []
for width_px, block in _media_blocks("max-width"):
if re.search(r"\.rightpanel\s*\{\s*display\s*:\s*none", block):
hidden_widths.append(width_px)
assert 900 in hidden_widths
assert all(width_px <= 900 for width_px in hidden_widths)