From a6be29c6d289bebfddee7bfe1ddaf529f3493802 Mon Sep 17 00:00:00 2001 From: mwiegand Date: Sun, 17 May 2026 19:17:21 +0200 Subject: [PATCH] test(files): cover Escape closes editor with no stale state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opens server.cfg, drives the CM6 controller to a dirty buffer, presses Escape to close without saving, then reopens the same file and asserts the editor shows the original disk content — not the discarded buffer. Pins two invariants: native 's cancel→close path stays intact (no JS shortcut around Escape), and the reopened editor fetches a FRESH fragment via htmx.ajax with CM6 re-mounting on the new textarea. A regression that cached buffer state to "feel snappy" would fail this test loudly. Per docs/superpowers/plans/2026-05-17-files-overlay-e2e-handoff.md (Tier 2 case B). Co-Authored-By: Claude Opus 4.7 (1M context) --- l4d2web/tests/e2e/test_files_overlay.py | 41 +++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/l4d2web/tests/e2e/test_files_overlay.py b/l4d2web/tests/e2e/test_files_overlay.py index a1ee693..2dbd3bc 100644 --- a/l4d2web/tests/e2e/test_files_overlay.py +++ b/l4d2web/tests/e2e/test_files_overlay.py @@ -412,3 +412,44 @@ def test_share_url_deep_link_reopens_editor(page: Page, files_overlay_server) -> _wait_for_routed_editor(page) assert page.locator('textarea[data-rel-path="server.cfg"]').count() == 1 + + +def test_modal_close_on_escape_preserves_no_state(page: Page, files_overlay_server) -> None: + """Open server.cfg, drive the CM6 controller to a dirty buffer, + press Escape to close the modal without saving, then reopen the + same file and assert the editor shows the original disk content + — not the dirty buffer the user just discarded. + + Pins two invariants: + * modals.js's `cancel` → preventDefault → close() path on + `` actually closes the modal on + Escape (no JS shortcuts away from the native behavior). + * The reopened editor fetches a fresh fragment via htmx.ajax; + the CM6 controller re-mounts on the FRESH textarea (seeded + from disk content), not a stale cache of the prior session. + + A regression that survived the close event — e.g., a future commit + that cached buffer state in a module-scope variable to "feel + snappy" — would surface here as the dirty content reappearing. + """ + base = files_overlay_server["base_url"] + overlay_id = files_overlay_server["overlay_id"] + overlay_root = files_overlay_server["overlay_root"] + original_content = (overlay_root / "server.cfg").read_text() + _open_overlay(page, base, overlay_id) + + # --- First open: type something but don't save ---------------------- + page.click('button.file-tree-name-button[data-target-path="server.cfg"]') + _wait_for_routed_editor(page) + dirty = "DIRTY UNSAVED BUFFER " + "x" * 40 + page.evaluate("(t) => window.__filesEditor.setContent(t)", dirty) + page.keyboard.press("Escape") + expect(page.locator("#modal-container")).to_be_hidden(timeout=5000) + # Disk must still hold the original — never saved. + assert (overlay_root / "server.cfg").read_text() == original_content + + # --- Reopen and confirm CM6 starts fresh from disk ------------------ + page.click('button.file-tree-name-button[data-target-path="server.cfg"]') + _wait_for_routed_editor(page) + reopened = page.evaluate("() => window.__filesEditor.getValue()") + assert reopened == original_content