Skip to content

Commit 903d552

Browse files
committed
Skip Snapshot Caching for redirect visits
Closes [hotwired#794][] Reverts the implementation change made in [hotwired#674][], and in its place passes `shouldCacheSnapshot: false` alongside `willRender: false` for the `action: "replace"` Visit proposed when following a redirect. In order to test this behavior, this commit introduces the `readBodyMutationLogs` test utility function, along with the `window.bodyMutationLogs` property and the `BodyMutationLog` type. `BodyMutationLog` instances are pushed onto the log `Array` whenever the `<body>` element is replaced. [hotwired#794]: hotwired#794 [hotwired#674]: hotwired#674
1 parent 00b287a commit 903d552

File tree

5 files changed

+43
-0
lines changed

5 files changed

+43
-0
lines changed

src/core/drive/visit.ts

+2
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ export class Visit implements FetchRequestDelegate {
317317
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
318318
action: "replace",
319319
response: this.response,
320+
shouldCacheSnapshot: false,
321+
willRender: false,
320322
})
321323
this.followedRedirect = true
322324
}

src/tests/fixtures/rendering.html

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ <h1>Rendering</h1>
4545
<p><a id="permanent-in-frame-element-link" href="/src/tests/fixtures/permanent_element.html" data-turbo-frame="frame">Permanent element in frame</a></p>
4646
<p><a id="permanent-in-frame-without-layout-element-link" href="/src/tests/fixtures/frames/without_layout.html" data-turbo-frame="frame">Permanent element in frame without layout</a></p>
4747
<p><a id="delayed-link" href="/__turbo/delayed_response">Delayed link</a></p>
48+
<p><a id="redirect-link" href="/__turbo/redirect">Redirect link</a></p>
4849
<form>
4950
<input type="text" id="text-input">
5051
<input type="radio" id="radio-input">

src/tests/fixtures/test.js

+11
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,17 @@
4646
}
4747
}
4848
}).observe(document, { subtree: true, childList: true, attributes: true })
49+
50+
window.bodyMutationLogs = []
51+
addEventListener("turbo:load", () => {
52+
new MutationObserver((mutations) => {
53+
for (const { addedNodes } of mutations) {
54+
for (const { localName, outerHTML } of addedNodes) {
55+
if (localName == "body") bodyMutationLogs.push([outerHTML])
56+
}
57+
}
58+
}).observe(document.documentElement, { childList: true })
59+
}, { once: true })
4960
})([
5061
"turbo:click",
5162
"turbo:before-stream-render",

src/tests/functional/rendering_tests.ts

+9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
isScrolledToTop,
77
nextBeat,
88
nextBody,
9+
nextBodyMutation,
910
nextEventNamed,
11+
noNextBodyMutation,
1012
pathname,
1113
propertyForSelector,
1214
readEventLogs,
@@ -538,6 +540,13 @@ test("test error pages", async ({ page }) => {
538540
assert.equal(await page.textContent("body"), "404 Not Found: /nonexistent\n")
539541
})
540542

543+
test("test rendering a redirect response replaces the body once and only once", async ({ page }) => {
544+
await page.click("#redirect-link")
545+
await nextBodyMutation(page)
546+
547+
assert.ok(await noNextBodyMutation(page), "replaces <body> element once")
548+
})
549+
541550
function deepElementsEqual(
542551
page: Page,
543552
left: JSHandle<SVGElement | HTMLElement>[],

src/tests/helpers/page.ts

+20
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ type MutationAttributeName = string
1010
type MutationAttributeValue = string | null
1111
type MutationLog = [MutationAttributeName, Target, MutationAttributeValue]
1212

13+
type BodyHTML = string
14+
type BodyMutationLog = [BodyHTML]
15+
1316
export function attributeForSelector(page: Page, selector: string, attributeName: string): Promise<string | null> {
1417
return page.locator(selector).getAttribute(attributeName)
1518
}
@@ -112,6 +115,19 @@ export async function listenForEventOnTarget(page: Page, elementId: string, even
112115
}, eventName)
113116
}
114117

118+
export async function nextBodyMutation(page: Page): Promise<string | null> {
119+
let record: BodyMutationLog | undefined
120+
while (!record) {
121+
;[record] = await readBodyMutationLogs(page, 1)
122+
}
123+
return record[0]
124+
}
125+
126+
export async function noNextBodyMutation(page: Page): Promise<boolean> {
127+
const records = await readBodyMutationLogs(page, 1)
128+
return !records.some((record) => !!record)
129+
}
130+
115131
export async function nextAttributeMutationNamed(
116132
page: Page,
117133
elementId: string,
@@ -174,6 +190,10 @@ async function readArray<T>(page: Page, identifier: string, length?: number): Pr
174190
)
175191
}
176192

193+
export function readBodyMutationLogs(page: Page, length?: number): Promise<BodyMutationLog[]> {
194+
return readArray<BodyMutationLog>(page, "bodyMutationLogs", length)
195+
}
196+
177197
export function readEventLogs(page: Page, length?: number): Promise<EventLog[]> {
178198
return readArray<EventLog>(page, "eventLogs", length)
179199
}

0 commit comments

Comments
 (0)