From f1bd6f1557950a9322887eabeeacfbd034611dda Mon Sep 17 00:00:00 2001 From: giles Date: Mon, 29 Jun 2026 17:07:07 +0000 Subject: [PATCH] engine: boosted forms now submit (bind-boost-form was discarding method/action) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes "the remove button does nothing — no network, no console". A plain form on a boosted (sx-boost) page has no sx-get/sx-post, so the SPA engine boosts it and binds submit -> execute-request. But bind-boost-form called `(execute-request form nil nil)` — discarding the method+action it was handed — and execute-request then asks get-verb-info for a verb, gets nil, and no-ops. So EVERY plain boosted form silently did nothing: the related-posts "remove" button, the editor Save button, the is-a-tag toggle. Fix: pass the form's own method+action as the verbInfo `(dict "method" method "url" action)`, so the request actually fires (body built from the form fields). A latent web-engine bug surfaced by the host's edit page — the first page with plain boosted POST forms. Test: relate-picker.spec.js gains a remove-button case (relate, reload, click remove, assert the relation is gone) — 7/7. WASM rebuilt (boot-helpers.sxbc). Co-Authored-By: Claude Opus 4.8 --- lib/host/playwright/relate-picker.spec.js | 19 +++++++++++++++++++ shared/static/wasm/sx/boot-helpers.sx | 8 +++++++- shared/static/wasm/sx/boot-helpers.sxbc | 4 ++-- web/lib/boot-helpers.sx | 8 +++++++- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/host/playwright/relate-picker.spec.js b/lib/host/playwright/relate-picker.spec.js index a53799ea..d00ce1b1 100644 --- a/lib/host/playwright/relate-picker.spec.js +++ b/lib/host/playwright/relate-picker.spec.js @@ -97,6 +97,25 @@ test.describe('relate picker', () => { await expect(page.locator('body')).toContainText('Picker Item 07'); }); + test('the remove button on a current relation actually unrelates it', async ({ page }) => { + test.setTimeout(75000); + await loginTo(page, `/${HOST}/edit`); + await waitReady(page); + // relate Item 13 via the picker, then reload so it shows in the current list + await page.fill(RELF, 'Item 13'); + await expect.poll(() => page.locator(RELROWS).count(), { timeout: 10000 }).toBe(1); + await page.locator(`${RELROWS} button`).first().click(); + await expect.poll(() => page.locator(RELROWS).count(), { timeout: 10000 }).toBe(0); + await page.reload(); + await waitReady(page); + const relLink = page.locator('a[href="/picker-item-13/"]'); + await expect(relLink).toHaveCount(1); // current relation present + // click its remove button — a plain boosted form (regression: this did nothing + // because bind-boost-form discarded the form's method/action) + await page.locator('li:has(a[href="/picker-item-13/"]) button').click(); + await expect(relLink).toHaveCount(0, { timeout: 12000 }); // relation removed + }); + test('picker populates after a boosted SPA nav to the edit page', async ({ page }) => { // Reach the edit page by CLICKING its link (a boosted SPA nav), not page.goto. // The old inline