diff --git a/packages/web/src/lib/__tests__/client-logger.test.ts b/packages/web/src/lib/__tests__/client-logger.test.ts index ef0d0cbdf..2a4f98e55 100644 --- a/packages/web/src/lib/__tests__/client-logger.test.ts +++ b/packages/web/src/lib/__tests__/client-logger.test.ts @@ -562,6 +562,47 @@ describe("cleanup", () => { expect(pageRemoves.length).toBe(1); }); + it("removes visibilitychange event listener", () => { + const removeSpy = vi.spyOn(window, "removeEventListener"); + const cleanup = initClientLogger(); + + cleanup(); + + const visRemoves = removeSpy.mock.calls.filter( + ([type]) => type === "visibilitychange", + ); + expect(visRemoves.length).toBe(1); + }); + + it("no longer flushes on visibilitychange after cleanup", () => { + const cleanup = initClientLogger(); + cleanup(); + + // Reset spies after cleanup flush + fetchSpy.mockClear(); + sendBeaconSpy.mockClear(); + + // Add an entry directly via a new error (won't be captured since error + // listener was removed, but set up document state for the visibility test) + Object.defineProperty(document, "visibilityState", { + value: "hidden", + writable: true, + configurable: true, + }); + // Fire visibilitychange -- listener should have been removed + window.dispatchEvent(new Event("visibilitychange")); + + expect(sendBeaconSpy).not.toHaveBeenCalled(); + expect(fetchSpy).not.toHaveBeenCalled(); + + // Restore + Object.defineProperty(document, "visibilityState", { + value: "visible", + writable: true, + configurable: true, + }); + }); + it("clears the flush interval", () => { const clearIntervalSpy = vi.spyOn(globalThis, "clearInterval"); const cleanup = initClientLogger(); diff --git a/packages/web/src/lib/client-logger.ts b/packages/web/src/lib/client-logger.ts index 5df324ac6..de54437b8 100644 --- a/packages/web/src/lib/client-logger.ts +++ b/packages/web/src/lib/client-logger.ts @@ -117,11 +117,12 @@ export function initClientLogger(): () => void { // Flush periodically flushTimer = setInterval(flush, 10_000); - // Flush on page unload - const onUnload = (): void => flush(); - window.addEventListener("visibilitychange", () => { + // Flush on page hide / visibility change + const onVisibilityChange = (): void => { if (document.visibilityState === "hidden") flush(); - }); + }; + const onUnload = (): void => flush(); + window.addEventListener("visibilitychange", onVisibilityChange); window.addEventListener("pagehide", onUnload); // Return cleanup function @@ -129,6 +130,7 @@ export function initClientLogger(): () => void { flush(); window.removeEventListener("error", onError); window.removeEventListener("unhandledrejection", onRejection); + window.removeEventListener("visibilitychange", onVisibilityChange); window.removeEventListener("pagehide", onUnload); if (flushTimer) clearInterval(flushTimer); if (perfObserver) perfObserver.disconnect();