Files
hermes-webui/tests/test_issue6906_kanban_modal_height_cap.py
T
f1115355a6 fix(#6906): keep kanban actions reachable in short windows (#7065)
* fix(#6906): tighten Kanban modal viewport regression

* fix(#6906): preserve modal centering fallbacks

* docs(changelog): note the #6906 Kanban modal height-cap fix

---------

Co-authored-by: nesquena-hermes <nesquena+hermes@gmail.com>
Co-authored-by: n <a@n>
2026-08-16 23:45:51 +00:00

123 lines
4.7 KiB
Python

"""Regression coverage for reachable Kanban modal actions in short windows."""
import pytest
from tests._layout_helpers import assert_layout_sane, assert_no_raw_i18n_keys
from tests._pytest_port import BASE
_BROWSER_ARGS = ["--no-sandbox", "--disable-dev-shm-usage"]
_LOCALES = ["en", "ru", "de"]
def _open_modal(page, modal_id, locale):
page.goto(BASE + "/", wait_until="domcontentloaded")
page.wait_for_function("() => typeof S !== 'undefined' && S._bootReady === true", timeout=10000)
page.wait_for_function(
"() => typeof setLocale === 'function' && typeof applyLocaleToDOM === 'function'",
timeout=10000,
)
page.evaluate(
"""([lang, id]) => {
setLocale(lang);
if (id === 'kanbanTaskModal') openKanbanCreate();
else openKanbanCreateBoard();
applyLocaleToDOM();
const modal = document.getElementById(id);
if (!modal || modal.hidden) throw new Error(`${id} did not open`);
}""",
[locale, modal_id],
)
def _assert_reachable(page, modal_id, *, should_scroll, max_height_inset):
result = page.evaluate(
"""(id) => {
const overlay = document.getElementById(id);
const modal = overlay?.querySelector('.kanban-modal');
const actions = modal?.querySelector('.kanban-modal-actions');
if (!overlay || !modal || !actions) throw new Error('modal geometry is incomplete');
if (modal.scrollHeight > modal.clientHeight) modal.scrollTop = modal.scrollHeight;
const viewport = {width: innerWidth, height: innerHeight};
const overlayBox = overlay.getBoundingClientRect();
const modalBox = modal.getBoundingClientRect();
const actionsBox = actions.getBoundingClientRect();
return {
viewport,
overlay: overlayBox.toJSON(),
modal: modalBox.toJSON(),
actions: actionsBox.toJSON(),
clientHeight: modal.clientHeight,
scrollHeight: modal.scrollHeight,
maxHeight: getComputedStyle(modal).maxHeight,
maxHeightPx: parseFloat(getComputedStyle(modal).maxHeight),
};
}""",
modal_id,
)
assert result["overlay"]["x"] == pytest.approx(0)
assert result["overlay"]["y"] == pytest.approx(0)
assert result["modal"]["bottom"] <= result["viewport"]["height"] + 1
assert result["modal"]["top"] >= -1
assert result["actions"]["bottom"] <= result["modal"]["bottom"] + 1
assert result["actions"]["top"] >= result["modal"]["top"] - 1
assert result["maxHeightPx"] == pytest.approx(result["viewport"]["height"] - max_height_inset, abs=1)
if should_scroll:
assert result["scrollHeight"] > result["clientHeight"]
assert result["maxHeight"] != "none"
else:
assert result["scrollHeight"] == pytest.approx(result["clientHeight"], abs=1)
assert_no_raw_i18n_keys(page, f"#{modal_id}")
assert_layout_sane(page, f"#{modal_id}")
@pytest.mark.parametrize("locale", _LOCALES)
@pytest.mark.parametrize(
"width,height,should_scroll,max_height_inset",
[
(1280, 720, True, 48),
(1440, 800, True, 48),
(800, 450, True, 48),
(1920, 1080, False, 48),
(400, 800, True, 24),
(640, 480, True, 24),
],
)
def test_task_modal_actions_reachable_across_viewports(
locale, width, height, should_scroll, max_height_inset
):
pw = pytest.importorskip("playwright.sync_api")
with pw.sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=True, args=_BROWSER_ARGS)
page = browser.new_page(viewport={"width": width, "height": height})
try:
_open_modal(page, "kanbanTaskModal", locale)
_assert_reachable(
page,
"kanbanTaskModal",
should_scroll=should_scroll,
max_height_inset=max_height_inset,
)
finally:
page.close()
browser.close()
@pytest.mark.parametrize("locale", _LOCALES)
def test_board_modal_inherits_reachable_modal_geometry(locale):
pw = pytest.importorskip("playwright.sync_api")
with pw.sync_playwright() as playwright:
browser = playwright.chromium.launch(headless=True, args=_BROWSER_ARGS)
page = browser.new_page(viewport={"width": 800, "height": 450})
try:
_open_modal(page, "kanbanBoardModal", locale)
_assert_reachable(
page,
"kanbanBoardModal",
should_scroll=True,
max_height_inset=48,
)
finally:
page.close()
browser.close()