From ea3163e234725a36ab57e4b61a881781860decf4 Mon Sep 17 00:00:00 2001 From: Rod Boev Date: Sat, 8 Aug 2026 15:10:57 -0400 Subject: [PATCH] fix(#5932): harden board workspace modal proof --- static/index.html | 2 +- ..._issue5932_kanban_board_default_workdir.py | 5 ++ ...932_kanban_board_default_workdir_layout.py | 90 +++++++++++-------- 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/static/index.html b/static/index.html index 1381a6c8..73d9a7a3 100644 --- a/static/index.html +++ b/static/index.html @@ -1798,7 +1798,7 @@
- +
diff --git a/tests/test_issue5932_kanban_board_default_workdir.py b/tests/test_issue5932_kanban_board_default_workdir.py index a7b9d4e3..70d3e4e6 100644 --- a/tests/test_issue5932_kanban_board_default_workdir.py +++ b/tests/test_issue5932_kanban_board_default_workdir.py @@ -75,6 +75,11 @@ def test_board_payload_validates_default_workdir_and_preserves_omission(monkeypa def test_board_modal_submit_includes_and_clears_default_workdir(): assert 'id="kanbanBoardModalDefaultWorkdir"' in INDEX + default_workdir_input = re.search( + r']*\bid="kanbanBoardModalDefaultWorkdir"[^>]*>', INDEX + ) + assert default_workdir_input + assert 'maxlength="255"' in default_workdir_input.group(0) assert 'id="kanbanBoardModalOriginalDefaultWorkdir"' in INDEX assert 'list="kanbanBoardModalWorkdirs"' in INDEX assert "if (defaultWorkdir) payload.default_workdir = defaultWorkdir;" in PANELS diff --git a/tests/test_issue5932_kanban_board_default_workdir_layout.py b/tests/test_issue5932_kanban_board_default_workdir_layout.py index 028d75b2..0ab83e7d 100644 --- a/tests/test_issue5932_kanban_board_default_workdir_layout.py +++ b/tests/test_issue5932_kanban_board_default_workdir_layout.py @@ -20,40 +20,56 @@ def test_board_modal_default_workdir_layout(locale, width, height): 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}) - page.goto(BASE + "/", wait_until="domcontentloaded") - page.wait_for_selector("#kanbanBoardModalDefaultWorkdir", state="attached", timeout=10000) - page.wait_for_function( - "typeof setLocale === 'function' && typeof applyLocaleToDOM === 'function' && typeof openKanbanCreateBoard === 'function'", - timeout=10000, - ) - page.evaluate("""([lang]) => { - setLocale(lang); - applyLocaleToDOM(); - openKanbanCreateBoard(); - const row = document.getElementById('kanbanBoardModalDefaultWorkdir')?.closest('.kanban-modal-row'); - if (row) row.id = 'kanbanBoardModalDefaultWorkdirRow'; - }""", [locale]) - page.wait_for_function("""([expected]) => { - const modal = document.getElementById('kanbanBoardModal'); - const label = document.querySelector("label[for='kanbanBoardModalDefaultWorkdir']"); - const row = document.getElementById('kanbanBoardModalDefaultWorkdirRow'); - return !!modal && !modal.hidden && !!label && !!row && label.textContent.trim() === expected; - }""", arg=[EXPECTED_LABELS[locale]]) - modal = page.locator("#kanbanBoardModal") - row = page.locator("#kanbanBoardModalDefaultWorkdirRow") - field = page.locator("#kanbanBoardModalDefaultWorkdir") - assert row.is_visible() - assert field.is_visible() - label = page.locator("label[for='kanbanBoardModalDefaultWorkdir']") - assert label.is_visible() - assert label.text_content().strip() == EXPECTED_LABELS[locale] - modal_box = modal.bounding_box() - field_box = field.bounding_box() - assert modal_box and field_box - assert field_box["x"] >= modal_box["x"] - assert field_box["x"] + field_box["width"] <= modal_box["x"] + modal_box["width"] - assert field_box["y"] + field_box["height"] <= modal_box["y"] + modal_box["height"] - assert_no_raw_i18n_keys(page, "#kanbanBoardModalDefaultWorkdirRow") - assert_layout_sane(page, "#kanbanBoardModalDefaultWorkdirRow") - browser.close() + page = None + try: + page = browser.new_page(viewport={"width": width, "height": height}) + try: + 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' && typeof openKanbanCreateBoard === 'function'", + timeout=10000, + ) + page.evaluate("""([lang]) => { + setLocale(lang); + openKanbanCreateBoard(); + const modal = document.getElementById('kanbanBoardModal'); + if (!modal || modal.hidden) throw new Error('Kanban board modal did not open'); + applyLocaleToDOM(); + const row = document.getElementById('kanbanBoardModalDefaultWorkdir')?.closest('.kanban-modal-row'); + if (row) row.id = 'kanbanBoardModalDefaultWorkdirRow'; + }""", [locale]) + page.wait_for_function("""([expected]) => { + const modal = document.getElementById('kanbanBoardModal'); + const label = document.querySelector("label[for='kanbanBoardModalDefaultWorkdir']"); + const row = document.getElementById('kanbanBoardModalDefaultWorkdirRow'); + return !!modal && !modal.hidden && !!label && !!row && label.textContent.trim() === expected; + }""", arg=[EXPECTED_LABELS[locale]]) + modal = page.locator("#kanbanBoardModal") + row = page.locator("#kanbanBoardModalDefaultWorkdirRow") + field = page.locator("#kanbanBoardModalDefaultWorkdir") + assert row.is_visible() + assert field.is_visible() + label = page.locator("label[for='kanbanBoardModalDefaultWorkdir']") + assert label.is_visible() + assert label.text_content().strip() == EXPECTED_LABELS[locale] + modal_box = modal.bounding_box() + field_box = field.bounding_box() + assert modal_box and field_box + assert field_box["x"] >= modal_box["x"] + assert field_box["x"] + field_box["width"] <= modal_box["x"] + modal_box["width"] + assert field_box["y"] + field_box["height"] <= modal_box["y"] + modal_box["height"] + assert_no_raw_i18n_keys(page, "#kanbanBoardModalDefaultWorkdirRow") + assert_layout_sane(page, "#kanbanBoardModalDefaultWorkdirRow") + finally: + try: + page.evaluate("""() => { + if (typeof closeKanbanBoardModal === 'function') closeKanbanBoardModal(); + }""") + finally: + page.close() + finally: + browser.close()