sseSource.test.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { describe, test, it, expect, beforeEach, afterEach, vi } from 'vitest'
  2. import { EventSourcePolyfill } from 'event-source-polyfill'
  3. import SseSource from '~/services/sse/sseSource'
  4. class TestableSseSource extends SseSource {
  5. public getUrl() {
  6. return this.url
  7. }
  8. public getOnOpen() {
  9. return this.onOpen
  10. }
  11. public getOnMessage() {
  12. return this.onMessage
  13. }
  14. public getOnClose() {
  15. return this.onClose
  16. }
  17. public getWithCredentials() {
  18. return this.withCredentials
  19. }
  20. public createEventSource(
  21. url: string,
  22. withCredentials: boolean,
  23. ): EventSourcePolyfill {
  24. return super.createEventSource(url, withCredentials)
  25. }
  26. public setEventSource(eventSource: EventSourcePolyfill) {
  27. this.eventSource = eventSource
  28. }
  29. }
  30. let mercureUrl: string
  31. let topic: string
  32. let onOpen: () => any
  33. let onMessage: (data: Array<any>) => any
  34. let onClose: () => any
  35. let sseSource: TestableSseSource
  36. const initConsoleLog = console.log
  37. beforeEach(() => {
  38. mercureUrl = 'https://my.mercure.com'
  39. topic = 'mytopic'
  40. onOpen = () => 'opened'
  41. onMessage = (data: Array<any>) => 'message'
  42. onClose = () => 'closed'
  43. sseSource = new TestableSseSource(
  44. mercureUrl,
  45. topic,
  46. onOpen,
  47. onMessage,
  48. onClose,
  49. false,
  50. )
  51. })
  52. afterEach(() => {
  53. console.log = initConsoleLog
  54. vi.restoreAllMocks()
  55. })
  56. describe('test constructor', () => {
  57. test('with all params', () => {
  58. expect(sseSource.getUrl().toString()).toEqual(
  59. 'https://my.mercure.com/?topic=mytopic',
  60. )
  61. expect(sseSource.getOnOpen()).toEqual(onOpen)
  62. expect(sseSource.getOnMessage()).toEqual(onMessage)
  63. expect(sseSource.getOnClose()).toEqual(onClose)
  64. expect(sseSource.getWithCredentials()).toEqual(false)
  65. })
  66. })
  67. describe('createEventSource', () => {
  68. test('simple call', () => {
  69. const eventSource = sseSource.createEventSource(mercureUrl, false)
  70. expect(eventSource.readyState).toEqual(0)
  71. expect(eventSource.url).toEqual(mercureUrl)
  72. expect(eventSource.withCredentials).toEqual(false)
  73. })
  74. })
  75. describe('isConnected', () => {
  76. test('no event source', () => {
  77. expect(sseSource.isConnected()).toEqual(false)
  78. })
  79. test('got an event source, but it is not open', () => {
  80. const eventSource = sseSource.createEventSource(mercureUrl, true)
  81. sseSource.setEventSource(eventSource)
  82. expect(sseSource.isConnected()).toEqual(false)
  83. })
  84. test('got an open event source', () => {
  85. // @ts-ignore
  86. const eventSource = vi.fn() as EventSourcePolyfill
  87. // @ts-ignore
  88. // noinspection JSConstantReassignment
  89. eventSource.readyState = EventSourcePolyfill.OPEN
  90. sseSource.setEventSource(eventSource)
  91. expect(sseSource.isConnected()).toEqual(true)
  92. })
  93. })
  94. describe('subscribe', () => {
  95. test('already connected', () => {
  96. sseSource.isConnected = vi.fn(() => true)
  97. expect(() => sseSource.subscribe()).toThrowError(
  98. 'SSE - Already subscribed to this event source',
  99. )
  100. })
  101. test('is server side', () => {
  102. // Mock the SseSource.prototype.subscribe method to simulate server-side behavior
  103. const originalSubscribe = SseSource.prototype.subscribe
  104. SseSource.prototype.subscribe = function () {
  105. // Simulate the server-side check
  106. throw new Error('SSE - Cannot subscribe on server side')
  107. }
  108. // Verify that the subscribe method throws an error
  109. expect(() => sseSource.subscribe()).toThrowError(
  110. 'SSE - Cannot subscribe on server side',
  111. )
  112. // Restore the original method
  113. SseSource.prototype.subscribe = originalSubscribe
  114. })
  115. test('successful subscription', () => {
  116. onOpen = vi.fn(() => null)
  117. onMessage = vi.fn((data: Array<any>) => null)
  118. sseSource = new TestableSseSource(
  119. mercureUrl,
  120. topic,
  121. onOpen,
  122. onMessage,
  123. onClose,
  124. )
  125. const dummyEventSource = new EventSourcePolyfill('https://my.mercure.com', {
  126. withCredentials: true,
  127. })
  128. sseSource.createEventSource = vi.fn(
  129. (url: string, withCredentials: boolean) => {
  130. return dummyEventSource
  131. },
  132. )
  133. console.log = vi.fn()
  134. console.error = vi.fn()
  135. sseSource.subscribe()
  136. expect(sseSource.createEventSource).toHaveBeenCalled()
  137. // @ts-ignore
  138. dummyEventSource.onopen()
  139. expect(onOpen).toHaveBeenCalled()
  140. expect(console.log).toHaveBeenCalledWith('SSE - Listening for events...')
  141. // @ts-ignore
  142. dummyEventSource.onmessage({ data: '1' })
  143. expect(onMessage).toHaveBeenCalledWith(1)
  144. // @ts-ignore
  145. const event = { target: { readyState: EventSourcePolyfill.CLOSED } }
  146. dummyEventSource.close = vi.fn()
  147. // @ts-ignore
  148. dummyEventSource.onerror(event)
  149. expect(console.log).toHaveBeenCalledWith('SSE closed')
  150. expect(dummyEventSource.close).toHaveBeenCalled()
  151. })
  152. })
  153. describe('unsubscribe', () => {
  154. test('if no event source, does nothing', () => {
  155. onClose = vi.fn(() => null)
  156. sseSource = new TestableSseSource(
  157. mercureUrl,
  158. topic,
  159. onOpen,
  160. onMessage,
  161. onClose,
  162. )
  163. sseSource.unsubscribe()
  164. expect(onClose).toHaveBeenCalledTimes(0)
  165. })
  166. test('if event source is not opened, does nothing', () => {
  167. onClose = vi.fn(() => null)
  168. sseSource = new TestableSseSource(
  169. mercureUrl,
  170. topic,
  171. onOpen,
  172. onMessage,
  173. onClose,
  174. )
  175. // @ts-ignore
  176. const eventSource = vi.fn() as EventSourcePolyfill
  177. // @ts-ignore
  178. // noinspection JSConstantReassignment
  179. eventSource.readyState = EventSourcePolyfill.CLOSED
  180. sseSource.setEventSource(eventSource)
  181. sseSource.unsubscribe()
  182. expect(onClose).toHaveBeenCalledTimes(0)
  183. })
  184. test('has an open subscription', () => {
  185. onClose = vi.fn(() => null)
  186. sseSource = new TestableSseSource(
  187. mercureUrl,
  188. topic,
  189. onOpen,
  190. onMessage,
  191. onClose,
  192. )
  193. // @ts-ignore
  194. const eventSource = vi.fn() as EventSourcePolyfill
  195. // @ts-ignore
  196. // noinspection JSConstantReassignment
  197. eventSource.readyState = EventSourcePolyfill.OPEN
  198. eventSource.close = vi.fn(() => null)
  199. sseSource.setEventSource(eventSource)
  200. console.log = vi.fn()
  201. sseSource.unsubscribe()
  202. expect(eventSource.close).toHaveBeenCalled()
  203. expect(onClose).toHaveBeenCalled()
  204. expect(console.log).toHaveBeenCalledWith('SSE - Subscription closed')
  205. })
  206. })