diff --git a/src/browser/setupWorker/start/createStartHandler.ts b/src/browser/setupWorker/start/createStartHandler.ts index dd9c35eb..6629590f 100644 --- a/src/browser/setupWorker/start/createStartHandler.ts +++ b/src/browser/setupWorker/start/createStartHandler.ts @@ -1,4 +1,5 @@ import { devUtils } from '~/core/utils/internal/devUtils' +import { MSW_WEBSOCKET_CLIENTS_KEY } from '~/core/ws/WebSocketClientManager' import { getWorkerInstance } from './utils/getWorkerInstance' import { enableMocking } from './utils/enableMocking' import { SetupWorkerInternalContext, StartHandler } from '../glossary' @@ -71,6 +72,11 @@ Please consider using a custom "serviceWorker.url" option to point to the actual // Make sure we're always clearing the interval - there are reports that not doing this can // cause memory leaks in headless browser environments. window.clearInterval(context.keepAliveInterval) + + // Purge persisted clients on page reload. + // WebSocket clients will get new IDs on reload so persisting them + // makes little sense. + localStorage.removeItem(MSW_WEBSOCKET_CLIENTS_KEY) }) // Check if the active Service Worker has been generated diff --git a/src/core/ws/WebSocketClientManager.ts b/src/core/ws/WebSocketClientManager.ts index e9d11d46..0a768073 100644 --- a/src/core/ws/WebSocketClientManager.ts +++ b/src/core/ws/WebSocketClientManager.ts @@ -43,9 +43,8 @@ export class WebSocketClientManager { ) { this.inMemoryClients = new Set() + // Purge in-memory clients when the worker stops. if (typeof localStorage !== 'undefined') { - // When the worker clears the local storage key in "worker.stop()", - // also clear the in-memory clients map. localStorage.removeItem = new Proxy(localStorage.removeItem, { apply: (target, thisArg, args) => { const [key] = args diff --git a/test/browser/ws-api/ws.clients.browser.test.ts b/test/browser/ws-api/ws.clients.browser.test.ts index 4d0a4a77..f811a383 100644 --- a/test/browser/ws-api/ws.clients.browser.test.ts +++ b/test/browser/ws-api/ws.clients.browser.test.ts @@ -183,23 +183,62 @@ test('clears the list of clients when the worker is stopped', async ({ await worker.start() }) + expect(await page.evaluate(() => window.link.clients.size)).toBe(0) + await page.evaluate(async () => { const ws = new WebSocket('wss://example.com') await new Promise((done) => (ws.onopen = done)) }) - // Must return 1 after a single client joined. - expect( - await page.evaluate(() => { - return window.link.clients.size - }), - ).toBe(1) + // Must return the number of joined clients. + expect(await page.evaluate(() => window.link.clients.size)).toBe(1) await page.evaluate(() => { window.worker.stop() }) - // Must return 0. - // The localStorage has been purged, and the in-memory manager clients too. + // Must purge the local storage on reload. + // The worker has been started as a part of the test, not runtime, + // so it will start with empty clients. + expect(await page.evaluate(() => window.link.clients.size)).toBe(0) +}) + +test('clears the list of clients when the page is reloaded', async ({ + loadExample, + page, +}) => { + await loadExample(require.resolve('./ws.runtime.js'), { + skipActivation: true, + }) + + const enableMocking = async () => { + await page.evaluate(async () => { + const { setupWorker, ws } = window.msw + const api = ws.link('wss://example.com') + const worker = setupWorker(api.on('connection', () => {})) + window.link = api + window.worker = worker + await worker.start() + }) + } + + await enableMocking(page) + + expect(await page.evaluate(() => window.link.clients.size)).toBe(0) + + await page.evaluate(async () => { + const ws = new WebSocket('wss://example.com') + await new Promise((done) => (ws.onopen = done)) + }) + + // Must return the number of joined clients. + expect(await page.evaluate(() => window.link.clients.size)).toBe(1) + + await page.reload() + await enableMocking() + + // Must purge the local storage on reload. + // The worker has been started as a part of the test, not runtime, + // so it will start with empty clients. expect(await page.evaluate(() => window.link.clients.size)).toBe(0) })