From 7d10519875834e1447fc8f3b0bf33b78f6860654 Mon Sep 17 00:00:00 2001 From: rumblefrog Date: Sun, 10 May 2026 13:20:28 -0400 Subject: [PATCH] fix(admin/groups): drop dangling applyApiResponse refs (#1310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three `then(function (r) { applyApiResponse(r); })` callbacks in `page_admin_groups_list.tpl` referenced a global helper that was deleted along with `web/scripts/sourcebans.js` at #1123 D1. The server-side `groups.remove` / `groups.edit` calls still fired and completed, but the `.then(...)` callback threw an unhandled `ReferenceError` before `window.SBPP.showToast` ever ran — so clicking Delete on a group hung the page with a red console error and no UI confirmation. Replace the three callbacks with the canonical inline response handler already used by the sibling `page_admin_groups_add.tpl`: - success/error toast via `window.SBPP.showToast`, - `sb.message.*` fallback for legacy themes that never wired SBPP, - honours `r.data.reload` (set by `groups.edit` after a save), and - — for handlers like `groups.remove` whose envelope only carries `message.redir` (no `data.reload`) — navigates to that URL after the toast so the master-detail editor stops pointing at the row that just got deleted. The handler is factored into a small `SbppGroupsApplyResponse` helper so all three callers share one definition. Regression test: `web/tests/e2e/specs/flows/admin-groups-delete.spec.ts` seeds a web admin group via `Actions.GroupsAdd`, deletes it, asserts (a) no `pageerror` (the pre-fix `ReferenceError`), (b) a `success` toast titled "Group Deleted" surfaces, and (c) the row is gone after the post-delete redirect. Also tightens the `applyApiResponse` comment in the existing #1272 bitmask spec — the toast contract is now its own spec, this one stays focused on the wire-level round-trip. The defensive `typeof window.applyApiResponse === 'function'` guard in `page_admin_bans_email.tpl:123` is a separate dead-code cleanup (it never throws because of the guard, just no-ops a fallback path) and is intentionally left out of scope here. --- .../specs/flows/admin-groups-bitmask.spec.ts | 14 +- .../specs/flows/admin-groups-delete.spec.ts | 178 ++++++++++++++++++ web/themes/default/page_admin_groups_list.tpl | 58 +++++- 3 files changed, 239 insertions(+), 11 deletions(-) create mode 100644 web/tests/e2e/specs/flows/admin-groups-delete.spec.ts diff --git a/web/tests/e2e/specs/flows/admin-groups-bitmask.spec.ts b/web/tests/e2e/specs/flows/admin-groups-bitmask.spec.ts index 1c48bd313..bafcede89 100644 --- a/web/tests/e2e/specs/flows/admin-groups-bitmask.spec.ts +++ b/web/tests/e2e/specs/flows/admin-groups-bitmask.spec.ts @@ -142,14 +142,12 @@ test.describe('flow: admin groups bitmask round-trip (#1272)', () => { // The Save button posts `Actions.GroupsEdit` with // `web_flags: `. We wait on the // network response (the deterministic terminal state for - // "save succeeded") rather than a UI toast — the inline - // success-toast wiring on this template is a separate concern - // outside #1272's scope (`SbppGroupsSave` calls a now- - // undefined `applyApiResponse`, a pre-existing dangling - // reference from the sourcebans.js removal at #1123 D1; the - // server-side save still completes, just without UI - // confirmation). The wire-level signal is what locks in the - // round-trip contract. + // "save succeeded") rather than a UI toast — the toast + // contract is locked in separately by + // `admin-groups-delete.spec.ts` (#1310 — replacement for the + // pre-#1310 dangling `applyApiResponse` reference); this + // spec stays focused on the wire-level round-trip that + // #1272 fixes. const saveButton = detail.locator('[data-testid="group-save"]'); await expect(saveButton).toBeVisible(); diff --git a/web/tests/e2e/specs/flows/admin-groups-delete.spec.ts b/web/tests/e2e/specs/flows/admin-groups-delete.spec.ts new file mode 100644 index 000000000..bfffe1a87 --- /dev/null +++ b/web/tests/e2e/specs/flows/admin-groups-delete.spec.ts @@ -0,0 +1,178 @@ +/** + * Flow spec — issue #1310: Delete-group on the admin groups list + * surfaces a success toast and navigates back to the list, with NO + * `ReferenceError: applyApiResponse is not defined` in the console. + * + * What this locks in + * ------------------ + * Pre-#1310, all three inline `then(function (r) { applyApiResponse(r); })` + * callbacks in `web/themes/default/page_admin_groups_list.tpl` referenced + * a global helper that was deleted wholesale at #1123 D1 along with + * `web/scripts/sourcebans.js`. The actual server-side `groups.remove` + * fired and completed, but the `.then(...)` callback threw an unhandled + * `ReferenceError` before `window.SBPP.showToast` ever ran — so from the + * operator's POV the page just hung with a red console error and no + * confirmation that the group was deleted (see issue #1310 repro). #1310 + * replaces the three callbacks with the canonical inline response handler + * already used by the sibling `page_admin_groups_add.tpl` (success/error + * toast via `window.SBPP.showToast`, fall back to `sb.message.*` for + * legacy themes, honour `data.reload` and — for handlers like + * `groups.remove` whose envelope only carries `message.redir` — navigate + * there after the toast so the master-detail editor stops pointing at + * the row that just got deleted). + * + * The two acceptance criteria asserted below: + * 1. Clicking Delete must NOT emit a `pageerror` (no `ReferenceError`). + * 2. A `success` toast titled "Group Deleted" must surface, and the + * group row must be gone from the list after the post-delete redirect. + * + * Selectors + * --------- + * Per AGENTS.md "Selectors must use #1123's testability hooks": + * - `[data-testid="web-groups-section"]` — page mount signal. + * - `[data-testid="group-list"]` — left-rail row container. + * - `[data-testid="group-row"]` — per-group anchor row. + * - `[data-testid="group-detail"]` — right-pane editor form. + * - `[data-testid="group-delete"]` — the Delete button. + * - `.toast[data-kind="success"]` — toast wrapper (theme.js). + * + * Project gating + * -------------- + * Pin to chromium (desktop). The reference-error contract holds at + * every viewport, but a flow spec mutating the shared `sourcebans_e2e` + * DB races with itself across projects (`workers: 1` is the suite-wide + * mitigation per AGENTS.md). The mobile chrome is structurally the same + * `