From c1ea5eb11e741f2c38c0278fb092ec63870ac67f Mon Sep 17 00:00:00 2001 From: mwiegand Date: Sun, 17 May 2026 18:41:37 +0200 Subject: [PATCH] test(files): cover binary editor UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opens icon.png and asserts the routed editor renders the binary branch of overlay_file_editor.html: replace-zone present, save button labelled "Replace" and disabled on open, download link pointing back at /files/download. The same /files/edit route serves both text and binary modes — the server picks the template branch from is_editable() + a magic-byte check. Without this test, a regression that flipped a binary file into the text branch would mean rendering raw PNG bytes inside an editable textarea (and a misleadingly working save button). Also asserts no textarea[data-rel-path] is in the DOM, so a future regression that left both branches enabled fails loudly. Per docs/superpowers/plans/2026-05-17-files-overlay-e2e-handoff.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- l4d2web/tests/e2e/test_files_overlay.py | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/l4d2web/tests/e2e/test_files_overlay.py b/l4d2web/tests/e2e/test_files_overlay.py index ad80b01..f7a42b7 100644 --- a/l4d2web/tests/e2e/test_files_overlay.py +++ b/l4d2web/tests/e2e/test_files_overlay.py @@ -175,3 +175,60 @@ def test_create_new_file_409_askConflict_keep_both(page: Page, files_overlay_ser expect( page.locator('.file-tree-row-file[data-target-path="cfg (1)"]') ).to_be_visible(timeout=5000) + + +def test_open_binary_file_renders_replace_ui(page: Page, files_overlay_server) -> None: + """Open icon.png and assert the routed editor renders the binary + branch of overlay_file_editor.html — replace-zone present, save + button labelled "Replace" and disabled until a file is queued or + the filename is edited. + + The same `/files/edit?path=...` route serves both text and binary + modes; the server picks the template branch based on is_editable + + a magic-byte check. This test pins the binary contract: the user + cannot save by accident on a fresh open, and the queue UI is wired + correctly. + + Pins: + * .files-editor-binary[data-rel-path="icon.png"] exists (the + data-rel-path stable hook for save logic). + * Save button has visible text "Replace" + the `disabled` + attribute is set on open. + * .files-editor-replace-zone + .files-editor-replace-browse + + the hidden file exist. + * Download link points back at /files/download?path=icon.png. + """ + base = files_overlay_server["base_url"] + overlay_id = files_overlay_server["overlay_id"] + _open_overlay(page, base, overlay_id) + + page.click('button.file-tree-name-button[data-target-path="icon.png"]') + + fragment = page.locator("#files-editor-fragment") + expect(fragment).to_be_visible(timeout=5000) + + binary_panel = page.locator('.files-editor-binary[data-rel-path="icon.png"]') + expect(binary_panel).to_be_visible(timeout=5000) + + save_btn = page.locator(".files-editor-save") + expect(save_btn).to_have_text("Replace") + expect(save_btn).to_be_disabled() + + expect(page.locator(".files-editor-replace-zone")).to_be_visible() + expect(page.locator(".files-editor-replace-browse")).to_be_visible() + # The file input is type=file hidden — Playwright's "visible" + # assertions treat hidden inputs as not visible, so check + # attached + the type instead. + expect(page.locator(".files-editor-replace-input")).to_have_attribute( + "type", "file" + ) + # Download link points back at the overlay's /files/download. + download = page.locator("a.files-editor-download") + expect(download).to_have_attribute( + "href", f"/overlays/{overlay_id}/files/download?path=icon.png" + ) + # In binary mode there's no