QueryHandler.spec.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. import { useLazyQuery, useQuery } from '@vue/apollo-composable'
  3. import createMockClient from '@tests/support/mock-apollo-client'
  4. import type {
  5. SampleQuery,
  6. SampleQueryVariables,
  7. } from '@tests/fixtures/graphqlSampleTypes'
  8. import { SampleTypedQueryDocument } from '@tests/fixtures/graphqlSampleTypes'
  9. import { useNotifications } from '@shared/components/CommonNotifications'
  10. import { NetworkStatus } from '@apollo/client/core'
  11. import { GraphQLErrorTypes } from '@shared/types/error'
  12. import QueryHandler from '../QueryHandler'
  13. const queryFunctionCallSpy = vi.fn()
  14. const querySampleResult = {
  15. Sample: {
  16. __typename: 'Sample',
  17. id: 1,
  18. title: 'Test Title',
  19. text: 'Test Text',
  20. },
  21. }
  22. const querySampleErrorResult = {
  23. networkStatus: NetworkStatus.error,
  24. errors: [
  25. {
  26. message: 'GraphQL Error',
  27. extensions: { type: 'Exceptions::Unknown' },
  28. },
  29. ],
  30. }
  31. const querySampleNetworkErrorResult = new Error('GraphQL Network Error')
  32. const handlerCallSpy = vi.fn()
  33. const mockClient = (error = false, errorType = 'GraphQL') => {
  34. handlerCallSpy.mockImplementation(() => {
  35. if (error) {
  36. return errorType === 'GraphQL'
  37. ? Promise.resolve(querySampleErrorResult)
  38. : Promise.reject(querySampleNetworkErrorResult)
  39. }
  40. return Promise.resolve({
  41. data: querySampleResult,
  42. })
  43. })
  44. createMockClient([
  45. {
  46. operationDocument: SampleTypedQueryDocument,
  47. handler: handlerCallSpy,
  48. },
  49. ])
  50. handlerCallSpy.mockClear()
  51. queryFunctionCallSpy.mockClear()
  52. }
  53. describe('QueryHandler', () => {
  54. const sampleQuery = (variables: SampleQueryVariables, options = {}) => {
  55. queryFunctionCallSpy()
  56. return useQuery<SampleQuery, SampleQueryVariables>(
  57. SampleTypedQueryDocument,
  58. variables,
  59. options,
  60. )
  61. }
  62. describe('constructor', () => {
  63. beforeEach(() => {
  64. mockClient()
  65. })
  66. it('instance can be created', () => {
  67. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  68. expect(queryHandlerObject).toBeInstanceOf(QueryHandler)
  69. })
  70. it('default handler options can be changed', () => {
  71. const errorNotificationMessage = 'A test message.'
  72. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }), {
  73. errorNotificationMessage,
  74. })
  75. expect(queryHandlerObject.handlerOptions.errorNotificationMessage).toBe(
  76. errorNotificationMessage,
  77. )
  78. })
  79. it('given query function was executed', () => {
  80. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  81. expect(queryFunctionCallSpy).toBeCalled()
  82. expect(queryHandlerObject.operationResult).toBeTruthy()
  83. })
  84. })
  85. describe('loading', () => {
  86. beforeEach(() => {
  87. mockClient()
  88. })
  89. it('loading state will be updated', async () => {
  90. expect.assertions(2)
  91. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  92. expect(queryHandlerObject.loading().value).toBe(true)
  93. await queryHandlerObject.onLoaded()
  94. expect(queryHandlerObject.loading().value).toBe(false)
  95. })
  96. it('supports lazy queries', async () => {
  97. expect.assertions(3)
  98. const sampleLazyQuery = (
  99. variables: SampleQueryVariables,
  100. options = {},
  101. ) => {
  102. queryFunctionCallSpy()
  103. return useLazyQuery<SampleQuery, SampleQueryVariables>(
  104. SampleTypedQueryDocument,
  105. variables,
  106. options,
  107. )
  108. }
  109. const queryHandlerObject = new QueryHandler(sampleLazyQuery({ id: 1 }))
  110. expect(queryHandlerObject.loading().value).toBe(false)
  111. await queryHandlerObject.load()
  112. expect(queryHandlerObject.loading().value).toBe(true)
  113. await queryHandlerObject.onLoaded()
  114. expect(queryHandlerObject.loading().value).toBe(false)
  115. })
  116. })
  117. describe('result', () => {
  118. beforeEach(() => {
  119. mockClient()
  120. })
  121. it('result is available', async () => {
  122. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  123. await expect(queryHandlerObject.loadedResult()).resolves.toEqual(
  124. querySampleResult,
  125. )
  126. })
  127. it('loaded result is also resolved after additional result call with active trigger refetch', async () => {
  128. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  129. await expect(queryHandlerObject.loadedResult()).resolves.toEqual(
  130. querySampleResult,
  131. )
  132. await expect(queryHandlerObject.loadedResult(true)).resolves.toEqual(
  133. querySampleResult,
  134. )
  135. })
  136. it('watch on result change', async () => {
  137. expect.assertions(1)
  138. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  139. queryHandlerObject.watchOnResult((result) => {
  140. expect(result).toEqual(querySampleResult)
  141. })
  142. await queryHandlerObject.onLoaded()
  143. })
  144. it('on result trigger', async () => {
  145. expect.assertions(1)
  146. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  147. queryHandlerObject.onResult((result) => {
  148. expect(result.data).toEqual(querySampleResult)
  149. })
  150. await queryHandlerObject.onLoaded()
  151. })
  152. it('receive value immediately in non-reactive way', async () => {
  153. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  154. await expect(queryHandlerObject.trigger()).resolves.toEqual(
  155. querySampleResult,
  156. )
  157. expect(handlerCallSpy).toHaveBeenCalledOnce()
  158. await expect(queryHandlerObject.trigger()).resolves.toEqual(
  159. querySampleResult,
  160. )
  161. expect(handlerCallSpy).toHaveBeenCalledOnce()
  162. })
  163. })
  164. describe('error handling', () => {
  165. describe('GraphQL errors', () => {
  166. beforeEach(() => {
  167. mockClient(true)
  168. })
  169. it('notification is triggerd', async () => {
  170. expect.assertions(1)
  171. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  172. await queryHandlerObject.loadedResult()
  173. const { notifications } = useNotifications()
  174. expect(notifications.value.length).toBe(1)
  175. })
  176. it('use error callback', async () => {
  177. expect.assertions(1)
  178. const errorCallbackSpy = vi.fn()
  179. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }), {
  180. errorCallback: (error) => {
  181. errorCallbackSpy(error)
  182. },
  183. })
  184. await queryHandlerObject.loadedResult()
  185. expect(errorCallbackSpy).toHaveBeenCalledWith({
  186. type: 'Exceptions::Unknown',
  187. message: 'GraphQL Error',
  188. })
  189. })
  190. it('refetch with error', async () => {
  191. expect.assertions(1)
  192. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  193. const errorCallbackSpy = vi.fn()
  194. await queryHandlerObject.loadedResult()
  195. // Refetch after first load again.
  196. await queryHandlerObject.refetch().catch((error) => {
  197. errorCallbackSpy(error)
  198. })
  199. expect(errorCallbackSpy).toHaveBeenCalled()
  200. })
  201. })
  202. describe('Network errors', () => {
  203. beforeEach(() => {
  204. mockClient(true, 'NetworkError')
  205. })
  206. it('use error callback', async () => {
  207. expect.assertions(1)
  208. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }), {
  209. errorCallback: (error) => {
  210. expect(error).toEqual({
  211. type: GraphQLErrorTypes.NetworkError,
  212. })
  213. },
  214. })
  215. await queryHandlerObject.loadedResult()
  216. })
  217. })
  218. })
  219. describe('use operation result wrapper', () => {
  220. beforeEach(() => {
  221. mockClient()
  222. })
  223. it('use returned query options', () => {
  224. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  225. expect(queryHandlerObject.options()).toBeTruthy()
  226. })
  227. it('use fetchMore query function', async () => {
  228. const queryHandlerObject = new QueryHandler(sampleQuery({ id: 1 }))
  229. await expect(queryHandlerObject.fetchMore({})).resolves.toEqual(
  230. querySampleResult,
  231. )
  232. })
  233. })
  234. })