mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-23 19:00:14 +00:00
d625bac6d4
* fix(projects): opaque context menu + auto-sizing rename/create input Two project chip UI bugs reported in project-ui-bugs.md: 1. Right-click context menu was transparent and the session list bled through it. Root cause: _showProjectContextMenu set background: var(--panel), but --panel is not defined anywhere in style.css, so the menu fell back to transparent. Fix: use var(--surface) -- the same opaque variable used by .session-action-menu and other floating popovers. 2. The rename and new-project input field was hard-coded to 100px regardless of the project name being edited (a 3-letter name got the same field size as a 20-letter name). Fix: drop width:100px from .project-create-input, replace with min-width:40px / max-width:180px / width:auto. Add a _resizeProjectInput() helper that measures the current value with a hidden span and sets pixel width inside those bounds. Wired into both _startProjectRename (called once on focus, again on every input event) and _startProjectCreate (same pattern). Tests: 9 new static-source tests in tests/test_project_chip_ui.py that pin (a) var(--panel) is undefined in style.css so the fallback trap doesn't return; (b) menu uses var(--surface); (c) the fixed width:100px is gone and min/max bounds are present; (d) the _resizeProjectInput helper is defined and called from both flows. Full suite: 2419 passed, 47 skipped, 0 PR-related failures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(projects): use getComputedStyle in _resizeProjectInput sizer span Switch the hidden sizer span from hardcoded font-size:10px / font-family:inherit to reading the live values from getComputedStyle(inp). This keeps the sizer calibrated if the CSS rule ever changes, rather than silently drifting. Also update test_resize_helper_uses_hidden_span to assert getComputedStyle is used rather than the old literal font-size check. Suggested by Opus independent review of #1086. * docs: v0.50.219 release notes, test count 2467, roadmap update --------- Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
169 lines
7.9 KiB
Python
169 lines
7.9 KiB
Python
"""Regression tests for the project chip UI fixes (issue #1085).
|
|
|
|
Two bugs:
|
|
|
|
1. The right-click context menu opened by `_showProjectContextMenu` was styled
|
|
with `background: var(--panel)`, but `--panel` is NOT defined anywhere in
|
|
style.css. CSS falls back to `transparent` for undefined variables, so the
|
|
menu appeared see-through and the session list bled through. The fix
|
|
replaces `var(--panel)` with `var(--surface)` — the same opaque variable
|
|
used by `.session-action-menu` and other floating popovers.
|
|
|
|
2. The `.project-create-input` (used for both rename and new-project creation)
|
|
had `width: 100px` hard-coded, so the field was always exactly 100px wide
|
|
regardless of the project name being edited. Fix: bound the field with
|
|
`min-width: 40px` / `max-width: 180px` and `width: auto`, plus a
|
|
`_resizeProjectInput()` JS helper that measures the current value with a
|
|
hidden span and sets the pixel width accordingly.
|
|
|
|
These are static-source tests — CSS/JS behaviour of a popover and an input
|
|
sizer can't be exercised faithfully without a browser, but the patterns
|
|
worth pinning are the variable names, the absence of the bad ones, and the
|
|
presence of the resize helper at both call sites.
|
|
"""
|
|
|
|
import pathlib
|
|
|
|
REPO = pathlib.Path(__file__).parent.parent
|
|
SESSIONS_JS = (REPO / "static" / "sessions.js").read_text(encoding="utf-8")
|
|
STYLE_CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8")
|
|
|
|
|
|
# ── Bug 1: context menu background ────────────────────────────────────────────
|
|
|
|
|
|
class TestContextMenuBackground:
|
|
|
|
def test_panel_variable_not_defined_in_stylesheet(self):
|
|
"""`--panel` is not defined as a CSS custom property anywhere — so
|
|
any rule using `var(--panel)` falls back to `transparent`, which is
|
|
the actual root cause of the menu bleed-through. This test
|
|
documents that fact: if `--panel` is ever defined, the test will
|
|
need updating but the fix is still safer using `--surface`."""
|
|
# Match either ":root --panel:" or "--panel:" assignments; absence
|
|
# confirms the fallback-to-transparent failure mode.
|
|
assert "--panel:" not in STYLE_CSS, (
|
|
"If --panel is now defined, update this test, but the menu "
|
|
"should still use --surface for consistency with other popovers."
|
|
)
|
|
|
|
def test_context_menu_uses_surface_not_panel(self):
|
|
"""`_showProjectContextMenu` must set the menu background to
|
|
`var(--surface)`, not `var(--panel)`."""
|
|
# Locate the menu construction
|
|
idx = SESSIONS_JS.find("project-ctx-menu")
|
|
assert idx >= 0, "project-ctx-menu className not found in sessions.js"
|
|
# Look at the surrounding 800 chars where the cssText is set
|
|
window = SESSIONS_JS[idx: idx + 1200]
|
|
assert "background:var(--surface)" in window, (
|
|
"Project context menu must use background:var(--surface) for an "
|
|
"opaque surface — var(--panel) is undefined and falls back to "
|
|
"transparent."
|
|
)
|
|
assert "background:var(--panel)" not in window, (
|
|
"Project context menu still uses background:var(--panel) — "
|
|
"this CSS variable is not defined and renders transparent."
|
|
)
|
|
|
|
def test_session_action_menu_also_uses_surface_for_consistency(self):
|
|
"""Sanity check: the existing .session-action-menu (the analogous
|
|
right-click menu for session items) uses `var(--surface)` — so the
|
|
fix is consistent with the rest of the codebase."""
|
|
assert "session-action-menu" in STYLE_CSS
|
|
# Find the rule and confirm it uses --surface
|
|
idx = STYLE_CSS.find(".session-action-menu")
|
|
assert idx >= 0
|
|
rule = STYLE_CSS[idx: idx + 400]
|
|
assert "var(--surface)" in rule, (
|
|
".session-action-menu should use var(--surface) — kept here as "
|
|
"the canonical reference for opaque popover surfaces."
|
|
)
|
|
|
|
|
|
# ── Bug 2: project-create-input width ─────────────────────────────────────────
|
|
|
|
|
|
class TestProjectCreateInputWidth:
|
|
|
|
def test_no_hardcoded_100px_width(self):
|
|
"""The fixed `width: 100px` on .project-create-input is gone."""
|
|
idx = STYLE_CSS.find(".project-create-input{")
|
|
assert idx >= 0, ".project-create-input rule not found in style.css"
|
|
rule = STYLE_CSS[idx: idx + 400]
|
|
assert "width:100px" not in rule and "width: 100px" not in rule, (
|
|
"Fixed 100px width must be replaced with min-width/max-width/"
|
|
"width:auto so the input grows with its content."
|
|
)
|
|
|
|
def test_min_and_max_width_present(self):
|
|
"""Both min-width and max-width must be set on .project-create-input."""
|
|
idx = STYLE_CSS.find(".project-create-input{")
|
|
rule = STYLE_CSS[idx: idx + 400]
|
|
assert "min-width:40px" in rule, (
|
|
f"min-width:40px not found in .project-create-input rule: {rule}"
|
|
)
|
|
assert "max-width:180px" in rule, (
|
|
f"max-width:180px not found in .project-create-input rule: {rule}"
|
|
)
|
|
assert "width:auto" in rule, (
|
|
f"width:auto not found in .project-create-input rule: {rule}"
|
|
)
|
|
|
|
|
|
class TestResizeProjectInputHelper:
|
|
"""The `_resizeProjectInput` helper must exist and be wired into both
|
|
rename and create call sites."""
|
|
|
|
def test_resize_helper_defined(self):
|
|
assert "function _resizeProjectInput(" in SESSIONS_JS, (
|
|
"_resizeProjectInput helper not found in sessions.js"
|
|
)
|
|
|
|
def test_resize_helper_uses_hidden_span(self):
|
|
"""The standard pattern is to measure with a hidden absolute span
|
|
sharing the same font/padding as the input. Font and family are read
|
|
via getComputedStyle so the sizer stays calibrated if CSS changes."""
|
|
idx = SESSIONS_JS.find("function _resizeProjectInput(")
|
|
assert idx >= 0
|
|
body = SESSIONS_JS[idx: idx + 900]
|
|
assert "position:absolute" in body and "visibility:hidden" in body, (
|
|
"_resizeProjectInput should use a hidden absolute span to "
|
|
"measure the value's rendered width."
|
|
)
|
|
assert "getComputedStyle(inp)" in body, (
|
|
"_resizeProjectInput should use getComputedStyle to read font " "properties so the sizer stays calibrated if CSS changes."
|
|
)
|
|
assert "Math.min(180" in body, (
|
|
"max bound (180) not applied in _resizeProjectInput"
|
|
)
|
|
assert "Math.max(40" in body, (
|
|
"min bound (40) not applied in _resizeProjectInput"
|
|
)
|
|
|
|
def test_rename_calls_resize_helper(self):
|
|
"""`_startProjectRename` must call `_resizeProjectInput` once on
|
|
creation and again on every input event."""
|
|
idx = SESSIONS_JS.find("function _startProjectRename(")
|
|
assert idx >= 0
|
|
body = SESSIONS_JS[idx: idx + 1200]
|
|
assert "_resizeProjectInput(inp)" in body, (
|
|
"_startProjectRename must call _resizeProjectInput so the "
|
|
"input width matches the existing project name."
|
|
)
|
|
# Wired into the input event so it grows as the user types
|
|
assert "addEventListener('input'" in body and "_resizeProjectInput" in body, (
|
|
"_startProjectRename must wire input events to _resizeProjectInput"
|
|
)
|
|
|
|
def test_create_calls_resize_helper(self):
|
|
"""Same for `_startProjectCreate` (new-project entry field)."""
|
|
idx = SESSIONS_JS.find("function _startProjectCreate(")
|
|
assert idx >= 0
|
|
body = SESSIONS_JS[idx: idx + 1200]
|
|
assert "_resizeProjectInput(inp)" in body, (
|
|
"_startProjectCreate must call _resizeProjectInput on focus"
|
|
)
|
|
assert "addEventListener('input'" in body, (
|
|
"_startProjectCreate must wire input events to _resizeProjectInput"
|
|
)
|