diff --git a/package.json b/package.json index 69fbe9b..700f59b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "EventSource implementation for React Native. Server-Sent Events (SSE) for iOS and Android.", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node --test" }, "repository": { "type": "git", @@ -27,4 +27,4 @@ "url": "https://github.com/binaryminds/react-native-sse/issues" }, "homepage": "https://github.com/binaryminds/react-native-sse#readme" -} \ No newline at end of file +} diff --git a/src/EventSource.js b/src/EventSource.js index 8b2991d..081f982 100644 --- a/src/EventSource.js +++ b/src/EventSource.js @@ -247,10 +247,10 @@ class EventSource { }; this.dispatch(eventType, event); - - data = []; - type = undefined; } + + data = []; + type = undefined; } } } diff --git a/test/EventSource.test.mjs b/test/EventSource.test.mjs new file mode 100644 index 0000000..81af987 --- /dev/null +++ b/test/EventSource.test.mjs @@ -0,0 +1,87 @@ +import assert from 'node:assert/strict'; +import { readFile } from 'node:fs/promises'; +import test from 'node:test'; + +class MockXMLHttpRequest { + static LOADING = 3; + static DONE = 4; + + static latest = null; + + constructor() { + MockXMLHttpRequest.latest = this; + this.readyState = 0; + this.status = 0; + this.responseText = ''; + } + + open() {} + + setRequestHeader() {} + + send() {} + + abort() {} +} + +globalThis.XMLHttpRequest = MockXMLHttpRequest; + +// The package entry point is CommonJS while the source file is an ES module. +// Loading the source through a data URL lets Node test the real implementation +// without adding a transpiler or another test dependency. +const eventSourceCode = await readFile(new URL('../src/EventSource.js', import.meta.url), 'utf8'); +const eventSourceModuleUrl = `data:text/javascript;base64,${Buffer.from(eventSourceCode).toString('base64')}`; +const { default: EventSource } = await import(eventSourceModuleUrl); + +function createEventSource() { + const eventSource = new EventSource('https://example.test/events', { + pollingInterval: 0, + timeoutBeforeConnection: 60_000, + }); + + eventSource.open(); + + return { + eventSource, + xhr: MockXMLHttpRequest.latest, + }; +} + +function completeResponse(xhr, responseText) { + xhr.status = 200; + xhr.readyState = MockXMLHttpRequest.DONE; + xhr.responseText = responseText; + xhr.onreadystatechange(); +} + +test('dispatches a named event to its named listener', (t) => { + const { eventSource, xhr } = createEventSource(); + t.after(() => eventSource.close()); + + const messages = []; + const customEvents = []; + + eventSource.addEventListener('message', (event) => messages.push(event.data)); + eventSource.addEventListener('custom', (event) => customEvents.push(event.data)); + + completeResponse(xhr, 'event: custom\ndata: hello\n\n'); + + assert.deepEqual(messages, []); + assert.deepEqual(customEvents, ['hello']); +}); + +test('clears an event type when an empty block is dispatched', (t) => { + const { eventSource, xhr } = createEventSource(); + t.after(() => eventSource.close()); + + const messages = []; + const customEvents = []; + + eventSource.addEventListener('message', (event) => messages.push(event.data)); + eventSource.addEventListener('custom', (event) => customEvents.push(event.data)); + + completeResponse(xhr, 'event: custom\n\ndata: hello\n\n'); + + assert.deepEqual(messages, ['hello']); + assert.deepEqual(customEvents, []); +});