diff --git a/src/core/drive/form_submission.js b/src/core/drive/form_submission.js index e2fa643b2..4d6a9ef49 100644 --- a/src/core/drive/form_submission.js +++ b/src/core/drive/form_submission.js @@ -1,6 +1,6 @@ import { FetchRequest, FetchMethod, fetchMethodFromString, fetchEnctypeFromString, isSafe } from "../../http/fetch_request" import { expandURL } from "../url" -import { dispatch, getAttribute, getMetaContent, hasAttribute } from "../../util" +import { dispatch, getAttribute, getMetaContent, getVisitAction, hasAttribute } from "../../util" import { StreamMessage } from "../streams/stream_message" export const FormSubmissionState = { @@ -111,6 +111,12 @@ export class FormSubmission { if (this.requestAcceptsTurboStreamResponse(request)) { request.acceptResponseType(StreamMessage.contentType) } + + const turboAction = getVisitAction(this.submitter, this.formElement) + + if (turboAction) { + request.headers["Turbo-Action"] = turboAction + } } requestStarted(_request) { diff --git a/src/core/drive/visit.js b/src/core/drive/visit.js index 4e7796302..567d658ad 100644 --- a/src/core/drive/visit.js +++ b/src/core/drive/visit.js @@ -286,6 +286,8 @@ export class Visit { if (this.acceptsStreamResponse) { request.acceptResponseType(StreamMessage.contentType) } + + request.headers["Turbo-Action"] = this.action } requestStarted() { diff --git a/src/core/frames/frame_controller.js b/src/core/frames/frame_controller.js index 5940ba761..fabdef7ff 100644 --- a/src/core/frames/frame_controller.js +++ b/src/core/frames/frame_controller.js @@ -197,6 +197,10 @@ export class FrameController { if (this.currentNavigationElement?.hasAttribute("data-turbo-stream")) { request.acceptResponseType(StreamMessage.contentType) } + + if (this.action) { + request.headers["Turbo-Action"] = this.action + } } requestStarted(_request) { diff --git a/src/tests/fixtures/form.html b/src/tests/fixtures/form.html index 707640393..b132178a6 100644 --- a/src/tests/fixtures/form.html +++ b/src/tests/fixtures/form.html @@ -101,6 +101,11 @@

Form

+
+ + + +

diff --git a/src/tests/fixtures/visit.html b/src/tests/fixtures/visit.html index f4cef2457..1b8fc68ae 100644 --- a/src/tests/fixtures/visit.html +++ b/src/tests/fixtures/visit.html @@ -13,6 +13,7 @@

Visit

Same-origin link

+

Same-origin link replace

Same-origin link with ?key=value

Sample response

Same page link

diff --git a/src/tests/functional/form_submission_tests.js b/src/tests/functional/form_submission_tests.js index 987038448..2d75e679d 100644 --- a/src/tests/functional/form_submission_tests.js +++ b/src/tests/functional/form_submission_tests.js @@ -197,6 +197,14 @@ test("supports modifying the submission in a turbo:before-fetch-request listener assert.equal(getSearchParam(page.url(), "greeting"), "Hello from a redirect") }) +test("standard POST encodes [data-turbo-action] into Turbo-Action", async ({ page }) => { + await page.click("#standard-post-form-replace-submit") + + const { fetchOptions } = await nextEventNamed(page, "turbo:before-fetch-request") + + assert.equal(fetchOptions.headers["Turbo-Action"], "replace", "encodes the action to the Turbo-Action header") +}) + test("standard POST form submission merges values from both searchParams and body", async ({ page }) => { await page.click("#form-action-post-redirect-self-q-b") await nextBody(page) diff --git a/src/tests/functional/frame_tests.js b/src/tests/functional/frame_tests.js index b090aff8a..363a8465a 100644 --- a/src/tests/functional/frame_tests.js +++ b/src/tests/functional/frame_tests.js @@ -642,13 +642,13 @@ test("navigating a frame with a form[method=get] that does not redirect still up test("navigating turbo-frame[data-turbo-action=advance] from within pushes URL state", async ({ page }) => { await page.click("#add-turbo-action-to-frame") await page.click("#link-frame") + const { fetchOptions } = await nextEventOnTarget(page, "frame", "turbo:before-fetch-request") await nextEventNamed(page, "turbo:load") - const title = await page.textContent("h1") - const frameTitle = await page.textContent("#frame h2") - - assert.equal(title, "Frames") - assert.equal(frameTitle, "Frame: Loaded") + assert.equal(fetchOptions.headers["Turbo-Frame"], "frame", "encodes Turbo-Frame header") + assert.equal(fetchOptions.headers["Turbo-Action"], "advance", "encodes Turbo-Action header") + assert.equal(await page.textContent("h1"), "Frames") + assert.equal(await page.textContent("#frame h2"), "Frame: Loaded") assert.equal(pathname(page.url()), "/src/tests/fixtures/frames/frame.html") }) @@ -736,15 +736,16 @@ test("navigating frame with a[data-turbo-action=advance] pushes URL state", asyn test("navigating frame with form[method=get][data-turbo-action=advance] pushes URL state", async ({ page }) => { await page.click("#form-get-frame-action-advance button") + const { fetchOptions } = await nextEventOnTarget(page, "form-get-frame-action-advance", "turbo:before-fetch-request") await nextEventNamed(page, "turbo:load") - const title = await page.textContent("h1") - const frameTitle = await page.textContent("#frame h2") - const src = (await attributeForSelector(page, "#frame", "src")) ?? "" + const src = (await attributeForSelector(page, "#frame", "src")) || "" + assert.equal(fetchOptions.headers["Turbo-Frame"], "frame", "encodes the frame [id] to the Turbo-Frame header") + assert.equal(fetchOptions.headers["Turbo-Action"], "advance", "encodes the action to the Turbo-Action header") assert.ok(src.includes("/src/tests/fixtures/frames/frame.html"), "updates src attribute") - assert.equal(title, "Frames") - assert.equal(frameTitle, "Frame: Loaded") + assert.equal(await page.textContent("h1"), "Frames") + assert.equal(await page.textContent("#frame h2"), "Frame: Loaded") assert.equal(pathname(page.url()), "/src/tests/fixtures/frames/frame.html") assert.ok(await hasSelector(page, "#frame[complete]"), "marks the frame as [complete]") }) diff --git a/src/tests/functional/visit_tests.js b/src/tests/functional/visit_tests.js index 24aa89295..9fff3d493 100644 --- a/src/tests/functional/visit_tests.js +++ b/src/tests/functional/visit_tests.js @@ -114,6 +114,13 @@ test("turbo:before-fetch-request event.detail encodes searchParams", async ({ pa assert.ok(url.includes("/src/tests/fixtures/one.html?key=value")) }) +test("turbo:before-fetch-request encodes the action", async ({ page }) => { + await page.click("#same-origin-link-replace") + const { fetchOptions } = await nextEventNamed(page, "turbo:before-fetch-request") + + assert.equal(fetchOptions.headers["Turbo-Action"], "replace", "encodes the action into Turbo-Action") +}) + test("turbo:before-fetch-response open new site", async ({ page }) => { page.evaluate(() => addEventListener(