fix(ux): confirm modal's Cancel/Confirm were dead on the promise-API path
Release / build-and-push (push) Successful in 3m29s
Release / build-and-push (push) Successful in 3m29s
User report: the Purge Archived dialog ('Permanently delete all
archived items?...') opened, and Cancel did nothing — and Confirm
would have been equally dead, leaving the page stuck behind the
overlay until a reload.
Root cause: confirm.ts wired the modal's buttons ONLY inside the
htmx:confirm interceptor. confirmDialog() — the promise API used by
purgeArchivedItems, remove-account, and other TS call sites — never
wired them. So whenever a page's first confirm interaction came from
the promise path (no hx-confirm element clicked first), both buttons
had no listeners. The 2026-10-30 confirm_probe spec only exercised the
hx-confirm path, which is why it survived.
Fix: wireButtons() now runs at module init (the header renders before
deferred modules) and inside confirmDialog() — both idempotent via the
existing data-wired guard.
Pinned by tests/ui/promise_confirm_probe.spec.ts (no env gating): the
purge POST is route-intercepted, so the probe asserts Cancel closes
the modal and Confirm issues the guarded request without deleting any
dev data. hx-confirm path re-pinned green (confirm_probe with BH_FEED).
Verified manually at 390x844: open -> cancelWired=true -> Cancel
closes -> Confirm fires the intercepted request.
This commit is contained in:
1 parent
04e855e691
commit
2165829d0e
3 files changed
+74
-1
No files matched your search
@@ -40,6 +40,15 @@ Gate: every line below executes for BOTH personas unless marked
|
||||
- Row 23 chains by page navigation — assert the URL becomes the next
|
||||
episode's reader and progress carries over.
|
||||
|
||||
## 2026-11-02 — promise-API confirm probe
|
||||
|
||||
`promise_confirm_probe.spec.ts` (no env gating): the confirmDialog()
|
||||
promise path (purge archived, remove account, …) must wire the global
|
||||
modal's buttons even when no hx-confirm element fired first — the
|
||||
Purge All Archived dialog opened with dead Cancel/Confirm otherwise
|
||||
(user report; confirm.ts now wires at init + on every confirmDialog).
|
||||
The probe intercepts the purge POST, so nothing is deleted.
|
||||
|
||||
## 2026-10-30 — round-3 regression probes (live-server, env-gated)
|
||||
|
||||
Two specs added against a RUNNING dev server (skip unless
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Regression probe (2026-11-02): the CONFIRM PROMISE API path must wire
|
||||
// the global modal's buttons too. purgeArchivedItems() (admin archived
|
||||
// page) opens confirmDialog() directly — no hx-confirm element fires —
|
||||
// and both buttons were dead whenever no hx-confirm had been clicked
|
||||
// first: the modal opened and Cancel/Confirm did nothing (user report).
|
||||
// The purge request itself is INTERCEPTED here — nothing is deleted.
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
const BASE = process.env.BH_BASE ?? "http://localhost:8765";
|
||||
|
||||
test("confirmDialog promise path wires Cancel/Confirm", async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
const t = new URLSearchParams(window.location.search).get("token");
|
||||
if (t) localStorage.setItem("token", t);
|
||||
});
|
||||
// testuser login via endpoint, then seed cookie+localStorage like the
|
||||
// other probes (rls_sweep pattern, kept inline to stay one file).
|
||||
const res = await page.request.post(`${BASE}/api/auth/login`, {
|
||||
data: { login: "testuser", password: "Test@Pass123!" },
|
||||
});
|
||||
const { access_token: token } = (await res.json()) as { access_token?: string };
|
||||
expect(token).toBeTruthy();
|
||||
await page.context().addCookies([{ name: "token", value: token!, url: BASE }]);
|
||||
await page.addInitScript((t) => localStorage.setItem("token", t), token!);
|
||||
|
||||
// The purge POST is intercepted: Cancel/Confirm behavior is asserted
|
||||
// without ever deleting the dev data.
|
||||
let purgeRequested = false;
|
||||
await page.route("**/api/media-items/purge-archived", (route) => {
|
||||
purgeRequested = true;
|
||||
route.fulfill({ status: 200, contentType: "application/json", body: '{"purged":0}' });
|
||||
});
|
||||
|
||||
await page.goto(`${BASE}/admin/archived`);
|
||||
await expect(page.locator("#confirm-modal")).toBeAttached();
|
||||
|
||||
// Fresh page: no hx-confirm has fired, so pre-fix the buttons were
|
||||
// unwired (data-wired absent) and Cancel did nothing.
|
||||
await page.getByRole("button", { name: "Purge All Archived" }).click();
|
||||
const modal = page.locator("#confirm-modal");
|
||||
await expect(modal).toBeVisible();
|
||||
await expect(page.locator("#confirm-modal-cancel")).toHaveAttribute("data-wired", "1");
|
||||
|
||||
await page.locator("#confirm-modal-cancel").click();
|
||||
await expect(modal).toBeHidden();
|
||||
|
||||
// Confirm path: modal closes AND the guarded request issues.
|
||||
await page.getByRole("button", { name: "Purge All Archived" }).click();
|
||||
await expect(modal).toBeVisible();
|
||||
await page.locator("#confirm-modal-accept").click();
|
||||
await expect(modal).toBeHidden();
|
||||
expect(purgeRequested).toBe(true);
|
||||
});
|
||||
Reference in new issue
Block a user