From 759b42121ef4db46e8301e82c17509d338bb3f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Fern=C3=A1ndez-Capel?= Date: Tue, 23 Jan 2024 18:28:37 +0000 Subject: [PATCH 1/2] Ensure that the turbo-frame header is not sent when the turbo-frame has a target of _top Given an HTML structure like this: ```html Hover to prefetch me ``` When the user hovers over the link, the turbo-frame header should not be sent in a prefetch request because the wrapping turbo-frame has a target of `_top`. If we don't respect this turbo-rails sees the turbo-frame header and renders a turbo-frame response with a minimal layout that doesn't include any assets in the head. When turbo processes the response it may find a missmatch in tracked assets and cause a reload. --- src/observers/link_prefetch_observer.js | 11 +++----- src/tests/fixtures/hover_to_prefetch.html | 8 ++++++ .../link_prefetch_observer_tests.js | 28 +++++++++++++++++-- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/observers/link_prefetch_observer.js b/src/observers/link_prefetch_observer.js index de08e05da..60794b2ae 100644 --- a/src/observers/link_prefetch_observer.js +++ b/src/observers/link_prefetch_observer.js @@ -100,14 +100,11 @@ export class LinkPrefetchObserver { request.headers["Sec-Purpose"] = "prefetch" - if (link.dataset.turboFrame && link.dataset.turboFrame !== "_top") { - request.headers["Turbo-Frame"] = link.dataset.turboFrame - } else if (link.dataset.turboFrame !== "_top") { - const turboFrame = link.closest("turbo-frame") + const turboFrame = link.closest("turbo-frame") + const turboFrameTarget = link.dataset.turboFrame || turboFrame?.getAttribute("target") || turboFrame?.id - if (turboFrame) { - request.headers["Turbo-Frame"] = turboFrame.id - } + if (turboFrameTarget && turboFrameTarget !== "_top") { + request.headers["Turbo-Frame"] = turboFrameTarget } if (link.hasAttribute("data-turbo-stream")) { diff --git a/src/tests/fixtures/hover_to_prefetch.html b/src/tests/fixtures/hover_to_prefetch.html index e0748fe0e..89b94f1bb 100644 --- a/src/tests/fixtures/hover_to_prefetch.html +++ b/src/tests/fixtures/hover_to_prefetch.html @@ -41,5 +41,13 @@ Won't prefetch when hovering me + + + Hover to prefetch me + + + + Hover to prefetch me + diff --git a/src/tests/functional/link_prefetch_observer_tests.js b/src/tests/functional/link_prefetch_observer_tests.js index 1d0139986..584d673ef 100644 --- a/src/tests/functional/link_prefetch_observer_tests.js +++ b/src/tests/functional/link_prefetch_observer_tests.js @@ -153,6 +153,25 @@ test("it prefetches links with inner elements", async ({ page }) => { await assertPrefetchedOnHover({ page, selector: "#anchor_with_inner_elements" }) }) +test("it prefetches links inside a turbo frame", async ({ page }) => { + await goTo({ page, path: "/hover_to_prefetch.html" }) + + await assertPrefetchedOnHover({ page, selector: "#anchor_for_prefetch_in_frame", callback: (request) => { + const turboFrameHeader = request.headers()["turbo-frame"] + assert.equal(turboFrameHeader, "frame_for_prefetch") + }}) +}) + + +test("doesn't include a turbo-frame header when the link is inside a turbo frame with a target=_top", async ({ page}) => { + await goTo({ page, path: "/hover_to_prefetch.html" }) + + await assertPrefetchedOnHover({ page, selector: "#anchor_for_prefetch_in_frame_target_top", callback: (request) => { + const turboFrameHeader = request.headers()["turbo-frame"] + assert.equal(undefined, turboFrameHeader) + }}) +}) + test("it prefetches links with a delay", async ({ page }) => { await goTo({ page, path: "/hover_to_prefetch.html" }) @@ -253,15 +272,18 @@ const assertPrefetchedOnHover = async ({ page, selector, callback }) => { let requestMade = false page.on("request", (request) => { - callback && callback(request) - requestMade = true + requestMade = request }) await hoverSelector({ page, selector }) await sleep(100) - assertRequestMade(requestMade) + if (callback) { + await callback(requestMade) + } + + assertRequestMade(!!requestMade) } const assertNotPrefetchedOnHover = async ({ page, selector, callback }) => { From 894027d680b8a96abf07bcce440105abb3f76b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Fern=C3=A1ndez-Capel?= Date: Wed, 24 Jan 2024 09:53:31 +0000 Subject: [PATCH 2/2] Prefer `getAttribute` over `dataset` for `data-turbo-frame` attribute --- src/observers/link_prefetch_observer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/observers/link_prefetch_observer.js b/src/observers/link_prefetch_observer.js index 60794b2ae..d204cb2a5 100644 --- a/src/observers/link_prefetch_observer.js +++ b/src/observers/link_prefetch_observer.js @@ -101,7 +101,7 @@ export class LinkPrefetchObserver { request.headers["Sec-Purpose"] = "prefetch" const turboFrame = link.closest("turbo-frame") - const turboFrameTarget = link.dataset.turboFrame || turboFrame?.getAttribute("target") || turboFrame?.id + const turboFrameTarget = link.getAttribute("data-turbo-frame") || turboFrame?.getAttribute("target") || turboFrame?.id if (turboFrameTarget && turboFrameTarget !== "_top") { request.headers["Turbo-Frame"] = turboFrameTarget