test(files): cover 409 askConflict keep-both path

Tries to create `cfg`, which collides with the seeded directory of the
same name. /files/save returns 409 "destination is not a file";
routedSaveClicked routes that through fo.askConflict, which opens the
inline #files-conflict-modal on top of the still-open routed editor.
Clicking keep-both triggers a second POST with the suffixed path
(`cfg (1)`), the routed modal closes, and the new row materialises in
the tree.

This is the F4 path from 8dc14f0 ("wire askConflict into the routed
new-file 409 path"). Before that commit, the routed code branch fell
through to a generic alert(). With this test in place, a missing
call site fails loudly instead of silently.

Pins three invariants:
  * The conflict dialog is INLINE, not routed — it appears without a
    URL change (the decision tree in AGENTS.md "Modals: inline vs
    routed" hinges on this).
  * .files-conflict-path echoes the original colliding path, not the
    computed suffix — the suffix is internal, the user sees the
    collision.
  * withCollisionSuffix("cfg") → "cfg (1)" (no dot after the last
    slash → trailing-suffix branch in uploads.js).

Per docs/superpowers/plans/2026-05-17-files-overlay-e2e-handoff.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mwiegand 2026-05-17 18:40:56 +02:00
parent d92f71f691
commit aad8356613
No known key found for this signature in database

View file

@ -116,3 +116,62 @@ def test_create_new_file_routed(page: Page, files_overlay_server) -> None:
expect( expect(
page.locator(f'.file-tree-row-file[data-target-path="{new_filename}"]') page.locator(f'.file-tree-row-file[data-target-path="{new_filename}"]')
).to_be_visible(timeout=5000) ).to_be_visible(timeout=5000)
def test_create_new_file_409_askConflict_keep_both(page: Page, files_overlay_server) -> None:
"""Try to create a file named `cfg`, which collides with the
seeded directory of the same name. /files/save returns 409 because
the target exists and is not a file; routedSaveClicked routes that
409 through fo.askConflict, which opens the INLINE
#files-conflict-modal on top of the still-open routed editor.
Clicking "keep-both" causes a second POST with the suffixed path
(cfg "cfg (1)"), the routed modal closes, and the new row
appears in the tree.
This is the F4 path from commit 8dc14f0 ("wire askConflict into
the routed new-file 409 path"). Without coverage it can regress
silently the legacy create-new flow went through a different code
path before the rewrite, and a missing call site would only
manifest as a confusing alert() instead of the conflict dialog.
Key invariants this asserts:
* The conflict dialog is inline, NOT routed it appears WITHOUT
a URL change (we don't navigate, we just wait for the dialog).
* .files-conflict-path shows the original colliding path, not
the suffixed one.
* withCollisionSuffix("cfg") returns "cfg (1)" (single-extension
path with no dot falls into the trailing-suffix branch).
"""
base = files_overlay_server["base_url"]
overlay_id = files_overlay_server["overlay_id"]
overlay_root = files_overlay_server["overlay_root"]
_open_overlay(page, base, overlay_id)
page.click('button[data-action="new-file"][data-target-path=""]')
_wait_for_routed_editor(page)
page.fill('input[data-editor-filename]', "cfg")
new_content = "STEAM_1:0:5\n"
page.evaluate("(text) => window.__filesEditor.setContent(text)", new_content)
page.click(".files-editor-save")
# The conflict dialog should pop up because `cfg` is a directory.
conflict = page.locator("#files-conflict-modal")
expect(conflict).to_be_visible(timeout=5000)
# The dialog must echo the ORIGINAL colliding path, not the suffix.
expect(conflict.locator(".files-conflict-path")).to_have_text("cfg")
page.click('[data-files-conflict-action="keep-both"]')
# Both modals should close (conflict dialog first, routed modal
# after the second /files/save resolves).
expect(page.locator("#modal-container")).to_be_hidden(timeout=5000)
expect(conflict).to_be_hidden(timeout=5000)
# The seeded `cfg/` directory must still be intact + the new
# suffixed file landed alongside it.
assert (overlay_root / "cfg").is_dir()
assert (overlay_root / "cfg (1)").read_text() == new_content
expect(
page.locator('.file-tree-row-file[data-target-path="cfg (1)"]')
).to_be_visible(timeout=5000)