diff --git a/packages/js-core/src/lib/survey/no-code-action.ts b/packages/js-core/src/lib/survey/no-code-action.ts index e2b38f9b36..ac19745e7c 100644 --- a/packages/js-core/src/lib/survey/no-code-action.ts +++ b/packages/js-core/src/lib/survey/no-code-action.ts @@ -175,6 +175,16 @@ export const addPageUrlEventListeners = (): void => { window.dispatchEvent(event); }; + // eslint-disable-next-line @typescript-eslint/unbound-method -- We need to access the original method + const originalReplaceState = history.replaceState; + + // eslint-disable-next-line func-names -- We need an anonymous function here + history.replaceState = function (...args) { + originalReplaceState.apply(this, args); + const event = new Event("replacestate"); + window.dispatchEvent(event); + }; + isHistoryPatched = true; } diff --git a/packages/js-core/src/lib/survey/tests/no-code-action.test.ts b/packages/js-core/src/lib/survey/tests/no-code-action.test.ts index 96158746c6..f3fba162e9 100644 --- a/packages/js-core/src/lib/survey/tests/no-code-action.test.ts +++ b/packages/js-core/src/lib/survey/tests/no-code-action.test.ts @@ -17,6 +17,7 @@ import { removeExitIntentListener, removePageUrlEventListeners, removeScrollDepthListener, + setIsHistoryPatched, } from "@/lib/survey/no-code-action"; import { setIsSurveyRunning } from "@/lib/survey/widget"; import { type TActionClassNoCodeConfig } from "@/types/survey"; @@ -638,6 +639,32 @@ describe("addPageUrlEventListeners additional cases", () => { (window.addEventListener as Mock).mockRestore(); dispatchEventSpy.mockRestore(); }); + + test("patched history.replaceState dispatches a 'replacestate' event", () => { + const addEventListenerMock = vi.fn(); + const removeEventListenerMock = vi.fn(); + const originalReplaceState = vi.fn(); + const dispatchEventMock = vi.fn(); + vi.stubGlobal("window", { + addEventListener: addEventListenerMock, + removeEventListener: removeEventListenerMock, + dispatchEvent: dispatchEventMock, + }); + vi.stubGlobal("history", { pushState: vi.fn(), replaceState: originalReplaceState }); + + // Reset patching state so addPageUrlEventListeners patches history fresh + setIsHistoryPatched(false); + removePageUrlEventListeners(); + addPageUrlEventListeners(); + + // Call the patched replaceState + history.replaceState({}, "", "/replaced-url"); + + expect(originalReplaceState).toHaveBeenCalledWith({}, "", "/replaced-url"); + expect(dispatchEventMock).toHaveBeenCalledWith(expect.objectContaining({ type: "replacestate" })); + + (window.addEventListener as Mock).mockRestore(); + }); }); describe("removePageUrlEventListeners additional cases", () => {