import ReconnectingWebSocket from "reconnecting-websocket"; import { MockWebSocket } from "./helpers/MockWebSocket"; import { advanceTimersAndFlush, flushMicrotasks } from "./helpers/testUtils"; describe("ReconnectingWebSocket integration", () => { beforeEach(() => { jest.useFakeTimers(); MockWebSocket.reset(); }); afterEach(() => { jest.clearAllTimers(); jest.useRealTimers(); }); it("retries when the connection times out", async () => { new ReconnectingWebSocket("ws://test", [], { WebSocket: MockWebSocket, connectionTimeout: 50, minReconnectionDelay: 10, maxReconnectionDelay: 10, maxRetries: 2, }); await advanceTimersAndFlush(0); expect(MockWebSocket.instances).toHaveLength(1); await advanceTimersAndFlush(50); await advanceTimersAndFlush(10); expect(MockWebSocket.instances).toHaveLength(2); await advanceTimersAndFlush(50); await advanceTimersAndFlush(10); expect(MockWebSocket.instances).toHaveLength(3); }); it("does not reset retry backoff for short-lived opens but resets it after a stable open", async () => { new ReconnectingWebSocket("ws://test", [], { WebSocket: MockWebSocket, connectionTimeout: 20, minReconnectionDelay: 100, maxReconnectionDelay: 1000, reconnectionDelayGrowFactor: 2, minUptime: 500, }); await advanceTimersAndFlush(0); expect(MockWebSocket.instances).toHaveLength(1); await advanceTimersAndFlush(20); await advanceTimersAndFlush(100); expect(MockWebSocket.instances).toHaveLength(2); const secondSocket = MockWebSocket.latest(); secondSocket.open(); await flushMicrotasks(); await advanceTimersAndFlush(100); secondSocket.fail(); await flushMicrotasks(); await advanceTimersAndFlush(199); expect(MockWebSocket.instances).toHaveLength(2); await advanceTimersAndFlush(1); expect(MockWebSocket.instances).toHaveLength(3); const thirdSocket = MockWebSocket.latest(); thirdSocket.open(); await flushMicrotasks(); await advanceTimersAndFlush(500); thirdSocket.fail(); await flushMicrotasks(); await advanceTimersAndFlush(99); expect(MockWebSocket.instances).toHaveLength(3); await advanceTimersAndFlush(1); expect(MockWebSocket.instances).toHaveLength(4); }); it("queues messages while disconnected and flushes them on open", async () => { const rws = new ReconnectingWebSocket("ws://test", [], { WebSocket: MockWebSocket, minReconnectionDelay: 10, maxReconnectionDelay: 10, }); await advanceTimersAndFlush(0); rws.send("first"); rws.send("second"); const firstSocket = MockWebSocket.latest(); expect(firstSocket.sentMessages).toEqual([]); firstSocket.open(); await flushMicrotasks(); expect(firstSocket.sentMessages).toEqual(["first", "second"]); }); it("stops retrying once maxRetries is reached", async () => { new ReconnectingWebSocket("ws://test", [], { WebSocket: MockWebSocket, connectionTimeout: 20, minReconnectionDelay: 10, maxReconnectionDelay: 10, maxRetries: 1, }); await advanceTimersAndFlush(0); expect(MockWebSocket.instances).toHaveLength(1); await advanceTimersAndFlush(20); await advanceTimersAndFlush(10); expect(MockWebSocket.instances).toHaveLength(2); await advanceTimersAndFlush(20); await advanceTimersAndFlush(100); expect(MockWebSocket.instances).toHaveLength(2); }); });