-
Notifications
You must be signed in to change notification settings - Fork 441
/
Copy pathpage_refresh_tests.js
350 lines (253 loc) · 13 KB
/
page_refresh_tests.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
import { test, expect } from "@playwright/test"
import { assert } from "chai"
import {
hasSelector,
nextBeat,
nextBody,
nextEventNamed,
nextEventOnTarget,
noNextEventOnTarget,
noNextEventNamed,
getSearchParam
} from "../helpers/page"
test("renders a page refresh with morphing", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#form-submit")
await nextEventNamed(page, "turbo:before-render", { renderMethod: "morph" })
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
})
test("async page refresh with turbo-stream", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await expect(page.locator("#title")).toHaveText("Page to be refreshed")
await page.evaluate(() => document.querySelector("#title").innerText = "Updated")
await expect(page.locator("#title")).toHaveText("Updated")
await page.evaluate(() => {
document.body.insertAdjacentHTML("beforeend", `<turbo-stream action="refresh"></turbo-stream>`)
})
await expect(page.locator("#title")).not.toHaveText("Updated")
await expect(page.locator("#title")).toHaveText("Page to be refreshed")
expect(await noNextEventNamed(page, "turbo:before-cache")).toBeTruthy()
})
test("dispatches a turbo:before-morph-element and turbo:morph-element event for each morphed element", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.fill("#form-text", "Morph me")
await page.click("#form-submit")
await nextEventOnTarget(page, "form-text", "turbo:before-morph-element")
await nextEventOnTarget(page, "form-text", "turbo:morph-element")
})
test("preventing a turbo:before-morph-element prevents the morph", async ({ page }) => {
const input = await page.locator("#form-text")
const submit = await page.locator("#form-submit")
await page.goto("/src/tests/fixtures/page_refresh.html")
await input.evaluate((input) => input.addEventListener("turbo:before-morph-element", (event) => event.preventDefault()))
await input.fill("Morph me")
await submit.click()
await nextEventOnTarget(page, "form-text", "turbo:before-morph-element")
await noNextEventOnTarget(page, "form-text", "turbo:morph-element")
await expect(input).toHaveValue("Morph me")
})
test("turbo:morph-element Stimulus listeners can handle morphing", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await expect(page.locator("#test-output")).toHaveText("connected")
await page.fill("#form-text", "Ignore me")
await page.click("#form-submit")
await expect(page.locator("#form-text")).toHaveValue("")
await expect(page.locator("#test-output")).toHaveText("connected")
})
test("turbo:before-morph-attribute Stimulus listeners can handle morphing attributes", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
const controller = page.locator("#stimulus-controller")
const input = controller.locator("input")
await expect(page.locator("#test-output")).toHaveText("connected")
await input.fill("controller state")
await page.fill("#form-text", "Ignore me")
await page.click("#form-submit")
const { mutationType } = await nextEventOnTarget(page, "stimulus-controller", "turbo:before-morph-attribute", { attributeName: "data-test-state-value" })
await expect(mutationType).toEqual("update")
await expect(controller).toHaveAttribute("data-test-state-value", "controller state")
await expect(page.locator("#form-text")).toHaveValue("")
await expect(page.locator("#test-output")).toHaveText("connected")
})
test("page refreshes cause a reload when assets change", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#add-new-assets")
await expect(page.locator("#new-stylesheet")).toHaveCount(1)
await page.click("#form-submit")
await nextEventNamed(page, "turbo:load")
await expect(page.locator("#new-stylesheet")).toHaveCount(0)
})
test("renders a page refresh with morphing when the paths are the same but search params are different", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#replace-link")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
})
test("renders a page refresh with morphing when the GET form paths are the same but search params are different", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
const input = page.locator("form[method=get] input[name=query]")
await input.fill("Search")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await expect(input).toBeFocused()
expect(getSearchParam(page.url(), "query")).toEqual("Search")
await input.press("?")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await expect(input).toBeFocused()
expect(getSearchParam(page.url(), "query")).toEqual("Search?")
})
test("doesn't morph when the turbo-refresh-method meta tag is not 'morph'", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh_replace.html")
await page.click("#form-submit")
expect(await noNextEventNamed(page, "turbo:render", { renderMethod: "morph" })).toBeTruthy()
})
test("doesn't morph when the navigation doesn't go to the same URL", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#link")
await expect(page.locator("h1")).toHaveText("One")
expect(await noNextEventNamed(page, "turbo:render", { renderMethod: "morph" })).toBeTruthy()
})
test("uses morphing to update remote frames marked with refresh='morph'", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#form-submit")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await nextBeat()
// Only the frame marked with refresh="morph" uses morphing
expect(await nextEventOnTarget(page, "refresh-morph", "turbo:before-frame-morph")).toBeTruthy()
expect(await noNextEventOnTarget(page, "refresh-reload", "turbo:before-frame-morph")).toBeTruthy()
await expect(page.locator("#refresh-morph")).toHaveText("Loaded morphed frame")
// Regular turbo-frames also gets reloaded since their complete attribute is removed
await expect(page.locator("#refresh-reload")).toHaveText("Loaded reloadable frame")
})
test("don't refresh frames contained in [data-turbo-permanent] elements", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#form-submit")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await nextBeat()
// Only the frame marked with refresh="morph" uses morphing
expect(await noNextEventOnTarget(page, "refresh-reload", "turbo:before-frame-morph")).toBeTruthy()
})
test("frames marked with refresh='morph' are excluded from full page morphing", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.evaluate(() => document.getElementById("refresh-morph").setAttribute("data-modified", "true"))
await page.click("#form-submit")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await nextBeat()
await expect(page.locator("#refresh-morph")).toHaveAttribute("data-modified", "true")
await expect(page.locator("#refresh-morph")).toHaveText("Loaded morphed frame")
})
test("navigated frames without refresh attribute are reset after morphing", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#refresh-after-navigation-link")
await nextBeat()
assert.ok(
await hasSelector(page, "#refresh-after-navigation-content"),
"navigates theframe"
)
await page.click("#form-submit")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await nextBeat()
assert.ok(
await hasSelector(page, "#refresh-after-navigation-link"),
"resets the frame"
)
assert.notOk(
await hasSelector(page, "#refresh-after-navigation-content"),
"does not reload the frame"
)
})
test("it preserves the scroll position when the turbo-refresh-scroll meta tag is 'preserve'", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.evaluate(() => window.scrollTo(10, 10))
await assertPageScroll(page, 10, 10)
// not using page.locator("#form-submit").click() because it can reset the scroll position
await page.evaluate(() => document.getElementById("form-submit")?.click())
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await assertPageScroll(page, 10, 10)
})
test("it does not preserve the scroll position on regular 'advance' navigations, despite of using a 'preserve' option", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.evaluate(() => window.scrollTo(10, 10))
await assertPageScroll(page, 10, 10)
await page.evaluate(() => document.getElementById("reload-link").click())
await nextEventNamed(page, "turbo:render", { renderMethod: "replace" })
await assertPageScroll(page, 0, 0)
})
test("it resets the scroll position when the turbo-refresh-scroll meta tag is 'reset'", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh_scroll_reset.html")
await page.evaluate(() => window.scrollTo(10, 10))
await assertPageScroll(page, 10, 10)
// not using page.locator("#form-submit").click() because it can reset the scroll position
await page.evaluate(() => document.getElementById("form-submit")?.click())
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await assertPageScroll(page, 0, 0)
})
test("it preserves focus across morphs", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
const input = await page.locator("#form input[type=text]")
await input.fill("Preserve me")
await input.press("Enter")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await expect(input).toBeFocused()
await expect(input).toHaveValue("Preserve me")
})
test("it preserves focus and the [data-turbo-permanent] element's value across morphs", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
const input = await page.locator("#form input[type=text]")
await input.evaluate((element) => element.setAttribute("data-turbo-permanent", ""))
await input.fill("Preserve me")
await input.press("Enter")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await expect(input).toBeFocused()
await expect(input).toHaveValue("Preserve me")
})
test("it preserves data-turbo-permanent elements", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.evaluate(() => {
const element = document.getElementById("preserve-me")
element.textContent = "Preserve me, I have a family!"
})
await expect(page.locator("#preserve-me")).toHaveText("Preserve me, I have a family!")
await page.click("#form-submit")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await expect(page.locator("#preserve-me")).toHaveText("Preserve me, I have a family!")
})
test("it preserves data-turbo-permanent elements that don't match when their ids do", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.evaluate(() => {
const element = document.getElementById("preserve-me")
element.textContent = "Preserve me, I have a family!"
document.getElementById("container").append(element)
})
await expect(page.locator("#preserve-me")).toHaveText("Preserve me, I have a family!")
await page.click("#form-submit")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await expect(page.locator("#preserve-me")).toHaveText("Preserve me, I have a family!")
})
test("renders unprocessable entity responses with morphing", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#reject form.unprocessable_entity input[type=submit]")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await nextBody(page)
const title = await page.locator("h1")
assert.equal(await title.textContent(), "Unprocessable Entity", "renders the response HTML")
assert.notOk(await hasSelector(page, "#frame form.reject"), "replaces entire page")
})
test("doesn't render previews when morphing", async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh.html")
await page.click("#link")
await page.click("#page-refresh-link")
await page.click("#refresh-link")
await nextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await noNextEventNamed(page, "turbo:render", { renderMethod: "morph" })
await nextBody(page)
const title = await page.locator("h1")
assert.equal(await title.textContent(), "Page to be refreshed")
})
async function assertPageScroll(page, top, left) {
const [scrollTop, scrollLeft] = await page.evaluate(() => {
return [
document.documentElement.scrollTop || document.body.scrollTop,
document.documentElement.scrollLeft || document.body.scrollLeft
]
})
expect(scrollTop).toEqual(top)
expect(scrollLeft).toEqual(left)
}