diff --git a/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.test.ts b/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.test.ts index 69c213e96f..2979e55425 100644 --- a/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.test.ts +++ b/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.test.ts @@ -6,6 +6,7 @@ import EventSource, { backoff, jitter } from './EventSource'; describe('EventSource', () => { const uri = 'https://mock.events.uri'; let eventSource: EventSource; + let mockXhr: any; beforeAll(() => { jest.useFakeTimers(); @@ -17,9 +18,17 @@ describe('EventSource', () => { .mockImplementationOnce(() => 0.888) .mockImplementationOnce(() => 0.999); + mockXhr = { + open: jest.fn(), + send: jest.fn(), + setRequestHeader: jest.fn(), + abort: jest.fn(), + }; + + jest.spyOn(window, 'XMLHttpRequest').mockImplementation(() => mockXhr as XMLHttpRequest); + eventSource = new EventSource(uri, { logger }); eventSource.onclose = jest.fn(); - eventSource.open = jest.fn(); eventSource.onretrying = jest.fn(); }); @@ -66,18 +75,17 @@ describe('EventSource', () => { expect(delay1).toEqual(1001); }); - test('tryConnect force no delay', () => { - // @ts-ignore - eventSource.tryConnect(true); + test('initial connection', () => { jest.runAllTimers(); - expect(logger.debug).toHaveBeenCalledWith(expect.stringMatching(/new connection in 0 ms/i)); - expect(eventSource.onretrying).toHaveBeenCalledWith({ type: 'retry', delayMillis: 0 }); - expect(eventSource.open).toHaveBeenCalledTimes(1); - expect(eventSource.onclose).toHaveBeenCalledTimes(1); + expect(logger.debug).toHaveBeenCalledWith(expect.stringMatching(/\[EventSource\] opening new connection./)); + expect(mockXhr.open).toHaveBeenCalledTimes(1); + expect(eventSource.onclose).toHaveBeenCalledTimes(0); }); test('tryConnect with delay', () => { + jest.runAllTimers(); + // This forces it to reconnect. // @ts-ignore eventSource.tryConnect(); jest.runAllTimers(); @@ -87,7 +95,8 @@ describe('EventSource', () => { expect.stringMatching(/new connection in 556 ms/i), ); expect(eventSource.onretrying).toHaveBeenCalledWith({ type: 'retry', delayMillis: 556 }); - expect(eventSource.open).toHaveBeenCalledTimes(1); + // Initial connection + forced reconnect. + expect(mockXhr.open).toHaveBeenCalledTimes(2); expect(eventSource.onclose).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.ts b/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.ts index 1cc7897706..2b22d3600b 100644 --- a/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.ts +++ b/packages/sdk/react-native/src/fromExternal/react-native-sse/EventSource.ts @@ -57,7 +57,7 @@ export default class EventSource { private body: any; private url: string; private xhr: XMLHttpRequest = new XMLHttpRequest(); - private pollTimer: any; + private connectTimer: any; private retryAndHandleError?: (err: any) => boolean; private initialRetryDelayMillis: number = 1000; private retryCount: number = 0; @@ -88,17 +88,25 @@ export default class EventSource { return delay; } - private tryConnect(forceNoDelay: boolean = false) { - let delay = forceNoDelay ? 0 : this.getNextRetryDelay(); - this.logger?.debug(`[EventSource] Will open new connection in ${delay} ms.`); - this.dispatch('retry', { type: 'retry', delayMillis: delay }); - this.pollTimer = setTimeout(() => { - this.close(); + private tryConnect(initialConnection: boolean = false) { + let delay = initialConnection ? 0 : this.getNextRetryDelay(); + if(initialConnection) { + this.logger?.debug(`[EventSource] opening new connection.`) + } else { + this.logger?.debug(`[EventSource] Will open new connection in ${delay} ms.`); + this.dispatch('retry', { type: 'retry', delayMillis: delay }); + } + + this.connectTimer = setTimeout(() => { + if(!initialConnection) { + this.close(); + } + this.open(); }, delay); } - open() { + private open() { try { this.lastIndexProcessed = 0; this.status = this.CONNECTING; @@ -329,7 +337,7 @@ export default class EventSource { close() { this.status = this.CLOSED; - clearTimeout(this.pollTimer); + clearTimeout(this.connectTimer); if (this.xhr) { this.xhr.abort(); }