mock-graphql-api.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright (C) 2012-2023 Zammad Foundation, https://zammad-foundation.org/
  2. /* eslint-disable no-use-before-define */
  3. import { NetworkStatus } from '@apollo/client/core'
  4. import type { UserError } from '@shared/graphql/types'
  5. import type { GraphQLErrorReport } from '@shared/types/error'
  6. import type { DocumentNode } from 'graphql'
  7. import {
  8. createMockSubscription,
  9. type IMockSubscription,
  10. type RequestHandlerResponse,
  11. } from 'mock-apollo-client'
  12. import type { SpyInstance } from 'vitest'
  13. import createMockClient from './mock-apollo-client'
  14. import { waitForNextTick } from './utils'
  15. interface Result {
  16. [key: string]: unknown
  17. }
  18. interface ResultWithUserError extends Result {
  19. errors: UserError[]
  20. }
  21. type OperationResultWithUserError = Record<string, ResultWithUserError>
  22. export interface MockGraphQLInstance {
  23. willBehave<T>(handler: (variables: any) => T): MockGraphQLInstance
  24. willResolve<T>(result: T): MockGraphQLInstance
  25. willFailWithError(
  26. errors: GraphQLErrorReport[],
  27. networkStatus?: NetworkStatus,
  28. ): MockGraphQLInstance
  29. willFailWithUserError(
  30. result: OperationResultWithUserError,
  31. ): MockGraphQLInstance
  32. willFailWithNetworkError(error: Error): MockGraphQLInstance
  33. spies: {
  34. behave: SpyInstance
  35. resolve: SpyInstance
  36. error: SpyInstance
  37. userError: SpyInstance
  38. networkError: SpyInstance
  39. }
  40. calls: {
  41. behave: number
  42. resolve: number
  43. error: number
  44. userError: number
  45. networkError: number
  46. }
  47. }
  48. export const mockGraphQLApi = (
  49. operationDocument: DocumentNode,
  50. ): MockGraphQLInstance => {
  51. const resolveSpy = vi.fn()
  52. const errorSpy = vi.fn()
  53. const userErrorSpy = vi.fn()
  54. const networkErrorSpy = vi.fn()
  55. const behaveSpy = vi.fn()
  56. const willBehave = (fn: (variables: any) => unknown) => {
  57. behaveSpy.mockImplementation(async (variables: any) => fn(variables))
  58. createMockClient([
  59. {
  60. operationDocument,
  61. handler: behaveSpy,
  62. },
  63. ])
  64. return instance
  65. }
  66. const willResolve = <T>(result: T | T[]) => {
  67. if (Array.isArray(result)) {
  68. result.forEach((singleResult) => {
  69. resolveSpy.mockResolvedValueOnce({ data: singleResult })
  70. })
  71. } else {
  72. resolveSpy.mockResolvedValue({ data: result })
  73. }
  74. createMockClient([
  75. {
  76. operationDocument,
  77. handler: resolveSpy,
  78. },
  79. ])
  80. return instance
  81. }
  82. const willFailWithError = (
  83. errors: GraphQLErrorReport[],
  84. networkStatus?: NetworkStatus,
  85. ) => {
  86. errorSpy.mockResolvedValue({
  87. networkStatus: networkStatus || NetworkStatus.error,
  88. errors,
  89. })
  90. createMockClient([
  91. {
  92. operationDocument,
  93. handler: errorSpy,
  94. },
  95. ])
  96. return instance
  97. }
  98. const willFailWithUserError = (result: OperationResultWithUserError) => {
  99. userErrorSpy.mockResolvedValue({ data: result })
  100. createMockClient([
  101. {
  102. operationDocument,
  103. handler: userErrorSpy,
  104. },
  105. ])
  106. return instance
  107. }
  108. const willFailWithNetworkError = (error: Error) => {
  109. networkErrorSpy.mockRejectedValue(error)
  110. createMockClient([
  111. {
  112. operationDocument,
  113. handler: networkErrorSpy,
  114. },
  115. ])
  116. return instance
  117. }
  118. const instance = {
  119. willFailWithError,
  120. willFailWithUserError,
  121. willFailWithNetworkError,
  122. willResolve,
  123. willBehave,
  124. spies: {
  125. behave: behaveSpy,
  126. resolve: resolveSpy,
  127. error: errorSpy,
  128. userError: userErrorSpy,
  129. networkError: networkErrorSpy,
  130. },
  131. calls: {
  132. get behave() {
  133. return behaveSpy.mock.calls.length
  134. },
  135. get resolve() {
  136. return resolveSpy.mock.calls.length
  137. },
  138. get error() {
  139. return errorSpy.mock.calls.length
  140. },
  141. get userError() {
  142. return userErrorSpy.mock.calls.length
  143. },
  144. get networkError() {
  145. return networkErrorSpy.mock.calls.length
  146. },
  147. },
  148. }
  149. return instance
  150. }
  151. export interface ExtendedIMockSubscription<T = unknown>
  152. extends Omit<IMockSubscription, 'next' | 'closed'> {
  153. closed: () => boolean
  154. next: (result: RequestHandlerResponse<T>) => Promise<void>
  155. }
  156. export const mockGraphQLSubscription = <T>(
  157. operationDocument: DocumentNode,
  158. ): ExtendedIMockSubscription<T> => {
  159. const mockSubscription = createMockSubscription({ disableLogging: true })
  160. createMockClient([
  161. {
  162. operationDocument,
  163. handler: () => mockSubscription,
  164. },
  165. ])
  166. return {
  167. next: async (
  168. value: Parameters<typeof mockSubscription.next>[0],
  169. ): Promise<void> => {
  170. mockSubscription.next(value)
  171. await waitForNextTick(true)
  172. },
  173. error: mockSubscription.error.bind(mockSubscription),
  174. complete: mockSubscription.complete.bind(mockSubscription),
  175. closed: () => mockSubscription.closed,
  176. }
  177. }