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:
parent
d92f71f691
commit
aad8356613
1 changed files with 59 additions and 0 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue