From e237023a2293b9cc405e6fddb83850d4fb43f0c5 Mon Sep 17 00:00:00 2001 From: Bruno Prieto Date: Mon, 4 Dec 2023 05:10:29 -0300 Subject: [PATCH] Debounce page refreshes triggered via Turbo streams Fix Turbo 8 refresh debounce in frontend #1093 --- src/core/session.js | 3 ++- .../page_refresh_stream_action_tests.js | 24 +++++++++++++------ src/tests/unit/stream_element_tests.js | 6 ++--- src/util.js | 10 ++++++++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/core/session.js b/src/core/session.js index be28a8287..7507891f3 100644 --- a/src/core/session.js +++ b/src/core/session.js @@ -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" @@ -44,6 +44,7 @@ export class Session { constructor(recentRequests) { this.recentRequests = recentRequests + this.refresh = debounce(this.refresh.bind(this), 150) } start() { diff --git a/src/tests/functional/page_refresh_stream_action_tests.js b/src/tests/functional/page_refresh_stream_action_tests.js index cbdeb272a..892a2c9b3 100644 --- a/src/tests/functional/page_refresh_stream_action_tests.js +++ b/src/tests/functional/page_refresh_stream_action_tests.js @@ -1,6 +1,6 @@ import { test } from "@playwright/test" import { assert } from "chai" -import { nextBeat } from "../helpers/page" +import { readEventLogs, sleep } from "../helpers/page" test.beforeEach(async ({ page }) => { await page.goto("/src/tests/fixtures/page_refresh_stream_action.html") @@ -9,11 +9,11 @@ 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 sleep(250) assert.match(await textContent(page), /Hello/) }) @@ -21,12 +21,12 @@ test("test refreshing the page", async ({ page }) => { 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 sleep(250) assert.notMatch(await textContent(page), /Hello/) }) @@ -42,6 +42,16 @@ 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 sleep(250) + + const eventLogs = await readEventLogs(page) + const requestLogs = eventLogs.filter(([name]) => name == "turbo:visit") + assert.equal(requestLogs.length, 1) +}) + async function textContent(page) { const messages = await page.locator("#content") return await messages.textContent() diff --git a/src/tests/unit/stream_element_tests.js b/src/tests/unit/stream_element_tests.js index 8ca8c48f3..21a9ca8aa 100644 --- a/src/tests/unit/stream_element_tests.js +++ b/src/tests/unit/stream_element_tests.js @@ -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) { @@ -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")) }) @@ -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")) }) diff --git a/src/util.js b/src/util.js index 1dcf943fb..772e9896c 100644 --- a/src/util.js +++ b/src/util.js @@ -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) + } +}