sseSource.test.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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. import.meta.server = true
  103. expect(() => sseSource.subscribe()).toThrowError(
  104. 'SSE - Cannot subscribe on server side',
  105. )
  106. import.meta.server = false
  107. })
  108. test('successful subscription', () => {
  109. onOpen = vi.fn(() => null)
  110. onMessage = vi.fn((data: Array<any>) => null)
  111. sseSource = new TestableSseSource(
  112. mercureUrl,
  113. topic,
  114. onOpen,
  115. onMessage,
  116. onClose,
  117. )
  118. const dummyEventSource = new EventSourcePolyfill('https://my.mercure.com', {
  119. withCredentials: true,
  120. })
  121. sseSource.createEventSource = vi.fn(
  122. (url: string, withCredentials: boolean) => {
  123. return dummyEventSource
  124. },
  125. )
  126. console.log = vi.fn()
  127. console.error = vi.fn()
  128. sseSource.subscribe()
  129. expect(sseSource.createEventSource).toHaveBeenCalled()
  130. // @ts-ignore
  131. dummyEventSource.onopen()
  132. expect(onOpen).toHaveBeenCalled()
  133. expect(console.log).toHaveBeenCalledWith('SSE - Listening for events...')
  134. // @ts-ignore
  135. dummyEventSource.onmessage({ data: '1' })
  136. expect(onMessage).toHaveBeenCalledWith(1)
  137. // @ts-ignore
  138. const event = { target: { readyState: EventSourcePolyfill.CLOSED } }
  139. dummyEventSource.close = vi.fn()
  140. // @ts-ignore
  141. dummyEventSource.onerror(event)
  142. expect(console.log).toHaveBeenCalledWith('SSE closed')
  143. expect(dummyEventSource.close).toHaveBeenCalled()
  144. })
  145. })
  146. describe('unsubscribe', () => {
  147. test('if no event source, does nothing', () => {
  148. onClose = vi.fn(() => null)
  149. sseSource = new TestableSseSource(
  150. mercureUrl,
  151. topic,
  152. onOpen,
  153. onMessage,
  154. onClose,
  155. )
  156. sseSource.unsubscribe()
  157. expect(onClose).toHaveBeenCalledTimes(0)
  158. })
  159. test('if event source is not opened, does nothing', () => {
  160. onClose = vi.fn(() => null)
  161. sseSource = new TestableSseSource(
  162. mercureUrl,
  163. topic,
  164. onOpen,
  165. onMessage,
  166. onClose,
  167. )
  168. // @ts-ignore
  169. const eventSource = vi.fn() as EventSourcePolyfill
  170. // @ts-ignore
  171. // noinspection JSConstantReassignment
  172. eventSource.readyState = EventSourcePolyfill.CLOSED
  173. sseSource.setEventSource(eventSource)
  174. sseSource.unsubscribe()
  175. expect(onClose).toHaveBeenCalledTimes(0)
  176. })
  177. test('has an open subscription', () => {
  178. onClose = vi.fn(() => null)
  179. sseSource = new TestableSseSource(
  180. mercureUrl,
  181. topic,
  182. onOpen,
  183. onMessage,
  184. onClose,
  185. )
  186. // @ts-ignore
  187. const eventSource = vi.fn() as EventSourcePolyfill
  188. // @ts-ignore
  189. // noinspection JSConstantReassignment
  190. eventSource.readyState = EventSourcePolyfill.OPEN
  191. eventSource.close = vi.fn(() => null)
  192. sseSource.setEventSource(eventSource)
  193. console.log = vi.fn()
  194. sseSource.unsubscribe()
  195. expect(eventSource.close).toHaveBeenCalled()
  196. expect(onClose).toHaveBeenCalled()
  197. expect(console.log).toHaveBeenCalledWith('SSE - Subscription closed')
  198. })
  199. })