Skip to content

Commit

Permalink
Merge pull request #1099 from brunoprietog/debounce-stream-refreshes
Browse files Browse the repository at this point in the history
Debounce page refreshes triggered via Turbo streams
  • Loading branch information
jorgemanrubia committed Jan 7, 2024
2 parents f92bfa5 + 2147a14 commit 6fc2f38
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 11 deletions.
14 changes: 13 additions & 1 deletion src/core/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ScrollObserver } from "../observers/scroll_observer"
import { StreamMessage } from "./streams/stream_message"
import { StreamMessageRenderer } from "./streams/stream_message_renderer"
import { StreamObserver } from "../observers/stream_observer"
import { clearBusyState, dispatch, findClosestRecursively, getVisitAction, markAsBusy } from "../util"
import { clearBusyState, dispatch, findClosestRecursively, getVisitAction, markAsBusy, debounce } from "../util"
import { PageView } from "./drive/page_view"
import { FrameElement } from "../elements/frame_element"
import { Preloader } from "./drive/preloader"
Expand Down Expand Up @@ -40,10 +40,13 @@ export class Session {
progressBarDelay = 500
started = false
formMode = "on"
#pageRefreshDebouncePeriod = 150

constructor(recentRequests) {
this.recentRequests = recentRequests
this.preloader = new Preloader(this, this.view.snapshotCache)
this.debouncedRefresh = this.refresh
this.pageRefreshDebouncePeriod = this.pageRefreshDebouncePeriod
}

start() {
Expand Down Expand Up @@ -138,6 +141,15 @@ export class Session {
return this.history.restorationIdentifier
}

get pageRefreshDebouncePeriod() {
return this.#pageRefreshDebouncePeriod
}

set pageRefreshDebouncePeriod(value) {
this.refresh = debounce(this.debouncedRefresh.bind(this), value)
this.#pageRefreshDebouncePeriod = value
}

// Preloader delegate

shouldPreloadLink(element) {
Expand Down
26 changes: 19 additions & 7 deletions src/tests/functional/page_refresh_stream_action_tests.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test } from "@playwright/test"
import { assert } from "chai"
import { nextBeat } from "../helpers/page"
import { nextPageRefresh, readEventLogs } from "../helpers/page"

test.beforeEach(async ({ page }) => {
await page.goto("/src/tests/fixtures/page_refresh_stream_action.html")
Expand All @@ -9,24 +9,24 @@ test.beforeEach(async ({ page }) => {
test("test refreshing the page", async ({ page }) => {
assert.match(await textContent(page), /Hello/)

await page.locator("#content").evaluate((content)=>content.innerHTML = "")
await page.locator("#content").evaluate((content) => content.innerHTML = "")
assert.notMatch(await textContent(page), /Hello/)

await page.click("#refresh button")
await nextBeat()
await nextPageRefresh(page)

assert.match(await textContent(page), /Hello/)
})

test("don't refresh the page on self-originated request ids", async ({ page }) => {
assert.match(await textContent(page), /Hello/)

await page.locator("#content").evaluate((content)=>content.innerHTML = "")
page.evaluate(()=> { window.Turbo.session.recentRequests.add("123") })
await page.locator("#content").evaluate((content) => content.innerHTML = "")
page.evaluate(() => { window.Turbo.session.recentRequests.add("123") })

await page.locator("#request-id").evaluate((input)=>input.value = "123")
await page.locator("#request-id").evaluate((input) => input.value = "123")
await page.click("#refresh button")
await nextBeat()
await nextPageRefresh(page)

assert.notMatch(await textContent(page), /Hello/)
})
Expand All @@ -42,6 +42,18 @@ test("fetch injects a Turbo-Request-Id with a UID generated automatically", asyn
}
})

test("debounce stream page refreshes", async ({ page }) => {
await page.click("#refresh button")
await page.click("#refresh button")
await nextPageRefresh(page)
await page.click("#refresh button")
await nextPageRefresh(page)

const eventLogs = await readEventLogs(page)
const requestLogs = eventLogs.filter(([name]) => name == "turbo:visit")
assert.equal(requestLogs.length, 2)
})

async function textContent(page) {
const messages = await page.locator("#content")
return await messages.textContent()
Expand Down
6 changes: 6 additions & 0 deletions src/tests/helpers/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export function nextBody(_page, timeout = 500) {
return sleep(timeout)
}

export async function nextPageRefresh(page, timeout = 500) {
const pageRefreshDebouncePeriod = await page.evaluate(() => window.Turbo.session.pageRefreshDebouncePeriod)

return sleep(pageRefreshDebouncePeriod + timeout)
}

export async function nextEventNamed(page, eventName, expectedDetail = {}) {
let record
while (!record) {
Expand Down
6 changes: 3 additions & 3 deletions src/tests/unit/stream_element_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { StreamElement } from "../../elements"
import { nextAnimationFrame } from "../../util"
import { DOMTestCase } from "../helpers/dom_test_case"
import { assert } from "@open-wc/testing"
import { nextBeat } from "../helpers/page"
import { sleep } from "../helpers/page"
import * as Turbo from "../../index"

function createStreamElement(action, target, templateElement) {
Expand Down Expand Up @@ -177,7 +177,7 @@ test("test action=refresh", async () => {
const element = createStreamElement("refresh")
subject.append(element)

await nextBeat()
await sleep(250)

assert.notOk(document.body.hasAttribute("data-modified"))
})
Expand All @@ -192,7 +192,7 @@ test("test action=refresh discarded when matching request id", async () => {
element.setAttribute("request-id", "123")
subject.append(element)

await nextBeat()
await sleep(250)

assert.ok(document.body.hasAttribute("data-modified"))
})
10 changes: 10 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,13 @@ export async function around(callback, reader) {

return [before, after]
}

export function debounce(fn, delay) {
let timeoutId = null

return (...args) => {
const callback = () => fn.apply(this, args)
clearTimeout(timeoutId)
timeoutId = setTimeout(callback, delay)
}
}

0 comments on commit 6fc2f38

Please sign in to comment.