relay-runtime 20.1.1 → 21.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/experimental.d.ts +34 -0
  2. package/experimental.js +1 -1
  3. package/experimental.js.flow +11 -11
  4. package/handlers/RelayDefaultHandlerProvider.d.ts +12 -0
  5. package/handlers/connection/ConnectionHandler.d.ts +51 -0
  6. package/handlers/connection/ConnectionHandler.js.flow +5 -5
  7. package/handlers/connection/ConnectionInterface.d.ts +40 -0
  8. package/handlers/connection/ConnectionInterface.js.flow +1 -1
  9. package/handlers/connection/MutationHandlers.d.ts +17 -0
  10. package/index.d.ts +274 -0
  11. package/index.js +1 -1
  12. package/index.js.flow +125 -62
  13. package/lib/experimental.js +3 -3
  14. package/lib/index.js +105 -57
  15. package/lib/multi-actor-environment/ActorIdentifier.js +2 -2
  16. package/lib/multi-actor-environment/MultiActorEnvironment.js +3 -1
  17. package/lib/mutations/commitMutation.js +8 -8
  18. package/lib/mutations/validateMutation.js +4 -4
  19. package/lib/query/GraphQLTag.js +3 -3
  20. package/lib/query/fetchQuery.js +15 -3
  21. package/lib/store/DataChecker.js +38 -4
  22. package/lib/store/NormalizationEngine.js +373 -0
  23. package/lib/store/OperationExecutor.js +172 -113
  24. package/lib/store/RelayConcreteVariables.js +1 -1
  25. package/lib/store/RelayErrorTrie.js +2 -2
  26. package/lib/store/RelayExperimentalGraphResponseTransform.js +8 -8
  27. package/lib/store/RelayModernEnvironment.js +26 -19
  28. package/lib/store/RelayModernRecord.js +18 -8
  29. package/lib/store/RelayModernSelector.js +9 -9
  30. package/lib/store/RelayModernStore.js +152 -43
  31. package/lib/store/RelayPublishQueue.js +1 -1
  32. package/lib/store/RelayReader.js +76 -38
  33. package/lib/store/RelayRecordSource.js +6 -0
  34. package/lib/store/RelayReferenceMarker.js +2 -1
  35. package/lib/store/RelayResponseNormalizer.js +88 -55
  36. package/lib/store/RelayStoreSubscriptions.js +34 -10
  37. package/lib/store/RelayStoreUtils.js +8 -1
  38. package/lib/store/ResolverFragments.js +2 -2
  39. package/lib/store/live-resolvers/LiveResolverCache.js +25 -9
  40. package/lib/store/observeFragmentExperimental.js +17 -1
  41. package/lib/store/observeQueryExperimental.js +2 -2
  42. package/lib/subscription/requestSubscription.js +3 -3
  43. package/lib/util/RelayError.js +3 -0
  44. package/lib/util/RelayFeatureFlags.js +6 -2
  45. package/lib/util/RelayReplaySubject.js +4 -4
  46. package/lib/util/handlePotentialSnapshotErrors.js +2 -2
  47. package/lib/util/stableCopy.js +2 -2
  48. package/llm-docs/api-reference/entrypoint-apis/entrypoint-container.mdx +38 -0
  49. package/llm-docs/api-reference/entrypoint-apis/load-entrypoint.mdx +77 -0
  50. package/llm-docs/api-reference/entrypoint-apis/use-entrypoint-loader.mdx +99 -0
  51. package/llm-docs/api-reference/graphql/graphql-directives.mdx +378 -0
  52. package/llm-docs/api-reference/hooks/_use-lazy-load-query-extra.mdx +16 -0
  53. package/llm-docs/api-reference/hooks/load-query.mdx +84 -0
  54. package/llm-docs/api-reference/hooks/relay-environment-provider.mdx +78 -0
  55. package/llm-docs/api-reference/hooks/use-client-query.mdx +65 -0
  56. package/llm-docs/api-reference/hooks/use-fragment.mdx +69 -0
  57. package/llm-docs/api-reference/hooks/use-lazy-load-query.mdx +62 -0
  58. package/llm-docs/api-reference/hooks/use-mutation.mdx +94 -0
  59. package/llm-docs/api-reference/hooks/use-pagination-fragment.mdx +166 -0
  60. package/llm-docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.mdx +134 -0
  61. package/llm-docs/api-reference/hooks/use-preloaded-query.mdx +84 -0
  62. package/llm-docs/api-reference/hooks/use-query-loader.mdx +95 -0
  63. package/llm-docs/api-reference/hooks/use-refetchable-fragment.mdx +122 -0
  64. package/llm-docs/api-reference/hooks/use-relay-environment.mdx +37 -0
  65. package/llm-docs/api-reference/hooks/use-subscription.mdx +66 -0
  66. package/llm-docs/api-reference/relay-resolvers/docblock-format.mdx +321 -0
  67. package/llm-docs/api-reference/relay-resolvers/runtime-functions.mdx +94 -0
  68. package/llm-docs/api-reference/relay-runtime/commit-mutation.mdx +65 -0
  69. package/llm-docs/api-reference/relay-runtime/fetch-query.mdx +118 -0
  70. package/llm-docs/api-reference/relay-runtime/field-logger.mdx +170 -0
  71. package/llm-docs/api-reference/relay-runtime/observe-fragment.mdx +92 -0
  72. package/llm-docs/api-reference/relay-runtime/relay-environment.mdx +53 -0
  73. package/llm-docs/api-reference/relay-runtime/request-subscription.mdx +54 -0
  74. package/llm-docs/api-reference/relay-runtime/runtime-configuration.mdx +52 -0
  75. package/llm-docs/api-reference/relay-runtime/store.mdx +734 -0
  76. package/llm-docs/api-reference/relay-runtime/wait-for-fragment-data.mdx +89 -0
  77. package/llm-docs/api-reference/types/CacheConfig.mdx +8 -0
  78. package/llm-docs/api-reference/types/Disposable.mdx +4 -0
  79. package/llm-docs/api-reference/types/GraphQLSubscriptionConfig.mdx +17 -0
  80. package/llm-docs/api-reference/types/MutationConfig.mdx +31 -0
  81. package/llm-docs/api-reference/types/SelectorStoreUpdater.mdx +6 -0
  82. package/llm-docs/api-reference/types/UploadableMap.mdx +3 -0
  83. package/llm-docs/community/learning-resources.mdx +64 -0
  84. package/llm-docs/debugging/declarative-mutation-directives.mdx +34 -0
  85. package/llm-docs/debugging/disallowed-id-types-error.mdx +43 -0
  86. package/llm-docs/debugging/inconsistent-typename-error.mdx +47 -0
  87. package/llm-docs/debugging/relay-devtools.mdx +73 -0
  88. package/llm-docs/debugging/why-null.mdx +116 -0
  89. package/llm-docs/editor-support.mdx +55 -0
  90. package/llm-docs/error-reference/unknown-field.mdx +36 -0
  91. package/llm-docs/getting-started/babel-plugin.mdx +31 -0
  92. package/llm-docs/getting-started/compiler-config.mdx +25 -0
  93. package/llm-docs/getting-started/compiler.mdx +98 -0
  94. package/llm-docs/getting-started/lint-rules.mdx +87 -0
  95. package/llm-docs/getting-started/production.mdx +30 -0
  96. package/llm-docs/getting-started/quick-start.mdx +216 -0
  97. package/llm-docs/glossary/glossary.mdx +1040 -0
  98. package/llm-docs/guided-tour/list-data/advanced-pagination.mdx +157 -0
  99. package/llm-docs/guided-tour/list-data/connections.mdx +81 -0
  100. package/llm-docs/guided-tour/list-data/pagination.mdx +193 -0
  101. package/llm-docs/guided-tour/list-data/rendering-connections.mdx +112 -0
  102. package/llm-docs/guided-tour/list-data/streaming-pagination.mdx +87 -0
  103. package/llm-docs/guided-tour/managing-data-outside-react/retaining-queries.mdx +51 -0
  104. package/llm-docs/guided-tour/refetching/refetching-queries-with-different-data.mdx +337 -0
  105. package/llm-docs/guided-tour/refetching/refreshing-queries.mdx +350 -0
  106. package/llm-docs/guided-tour/rendering/environment.mdx +59 -0
  107. package/llm-docs/guided-tour/rendering/error-states.mdx +295 -0
  108. package/llm-docs/guided-tour/rendering/fragments.mdx +354 -0
  109. package/llm-docs/guided-tour/rendering/loading-states.mdx +245 -0
  110. package/llm-docs/guided-tour/rendering/queries.mdx +261 -0
  111. package/llm-docs/guided-tour/rendering/variables.mdx +233 -0
  112. package/llm-docs/guided-tour/reusing-cached-data/fetch-policies.mdx +56 -0
  113. package/llm-docs/guided-tour/reusing-cached-data/filling-in-missing-data.mdx +102 -0
  114. package/llm-docs/guided-tour/reusing-cached-data/introduction.mdx +22 -0
  115. package/llm-docs/guided-tour/reusing-cached-data/presence-of-data.mdx +93 -0
  116. package/llm-docs/guided-tour/reusing-cached-data/rendering-partially-cached-data.mdx +175 -0
  117. package/llm-docs/guided-tour/reusing-cached-data/staleness-of-data.mdx +116 -0
  118. package/llm-docs/guided-tour/updating-data/client-only-data.mdx +115 -0
  119. package/llm-docs/guided-tour/updating-data/graphql-mutations.mdx +334 -0
  120. package/llm-docs/guided-tour/updating-data/graphql-subscriptions.mdx +279 -0
  121. package/llm-docs/guided-tour/updating-data/imperatively-modifying-linked-fields.mdx +511 -0
  122. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data-legacy.mdx +142 -0
  123. package/llm-docs/guided-tour/updating-data/imperatively-modifying-store-data.mdx +275 -0
  124. package/llm-docs/guided-tour/updating-data/introduction.mdx +25 -0
  125. package/llm-docs/guided-tour/updating-data/local-data-updates.mdx +71 -0
  126. package/llm-docs/guided-tour/updating-data/typesafe-updaters-faq.mdx +83 -0
  127. package/llm-docs/guided-tour/updating-data/updating-connections.mdx +592 -0
  128. package/llm-docs/guides/alias-directive.mdx +160 -0
  129. package/llm-docs/guides/catch-directive.mdx +167 -0
  130. package/llm-docs/guides/client-schema-extensions.mdx +208 -0
  131. package/llm-docs/guides/codemods.mdx +79 -0
  132. package/llm-docs/guides/data-driven-dependencies/client-3d.mdx +255 -0
  133. package/llm-docs/guides/data-driven-dependencies/configuration.mdx +127 -0
  134. package/llm-docs/guides/data-driven-dependencies/introduction.mdx +39 -0
  135. package/llm-docs/guides/data-driven-dependencies/server-3d.mdx +664 -0
  136. package/llm-docs/guides/document-comparison.mdx +106 -0
  137. package/llm-docs/guides/graphql-server-specification.mdx +453 -0
  138. package/llm-docs/guides/network-layer.mdx +69 -0
  139. package/llm-docs/guides/persisted-queries.mdx +328 -0
  140. package/llm-docs/guides/relay-resolvers/context.mdx +99 -0
  141. package/llm-docs/guides/relay-resolvers/defining-fields.mdx +151 -0
  142. package/llm-docs/guides/relay-resolvers/defining-types.mdx +164 -0
  143. package/llm-docs/guides/relay-resolvers/deprecated.mdx +27 -0
  144. package/llm-docs/guides/relay-resolvers/derived-fields.mdx +127 -0
  145. package/llm-docs/guides/relay-resolvers/descriptions.mdx +44 -0
  146. package/llm-docs/guides/relay-resolvers/enabling.mdx +41 -0
  147. package/llm-docs/guides/relay-resolvers/errors.mdx +64 -0
  148. package/llm-docs/guides/relay-resolvers/field-arguments.mdx +63 -0
  149. package/llm-docs/guides/relay-resolvers/introduction.mdx +62 -0
  150. package/llm-docs/guides/relay-resolvers/limitations.mdx +30 -0
  151. package/llm-docs/guides/relay-resolvers/live-fields.mdx +164 -0
  152. package/llm-docs/guides/relay-resolvers/return-types.mdx +161 -0
  153. package/llm-docs/guides/relay-resolvers/suspense.mdx +41 -0
  154. package/llm-docs/guides/required-directive.mdx +240 -0
  155. package/llm-docs/guides/semantic-nullability.mdx +93 -0
  156. package/llm-docs/guides/testing-relay-components.mdx +642 -0
  157. package/llm-docs/guides/testing-relay-with-preloaded-queries.mdx +160 -0
  158. package/llm-docs/guides/throw-on-field-error-directive.mdx +58 -0
  159. package/llm-docs/guides/type-emission.mdx +414 -0
  160. package/llm-docs/home.mdx +32 -0
  161. package/llm-docs/principles-and-architecture/architecture-overview.mdx +24 -0
  162. package/llm-docs/principles-and-architecture/compiler-architecture.mdx +106 -0
  163. package/llm-docs/principles-and-architecture/runtime-architecture.mdx +249 -0
  164. package/llm-docs/principles-and-architecture/thinking-in-graphql.mdx +309 -0
  165. package/llm-docs/principles-and-architecture/thinking-in-relay.mdx +104 -0
  166. package/llm-docs/principles-and-architecture/videos.mdx +50 -0
  167. package/llm-docs/tutorial/arrays-lists.mdx +126 -0
  168. package/llm-docs/tutorial/fragments-1.mdx +487 -0
  169. package/llm-docs/tutorial/graphql.mdx +172 -0
  170. package/llm-docs/tutorial/interfaces-polymorphism.mdx +161 -0
  171. package/llm-docs/tutorial/intro.mdx +58 -0
  172. package/llm-docs/tutorial/mutations-updates.mdx +624 -0
  173. package/llm-docs/tutorial/organizing-mutations-queries-and-subscriptions.mdx +13 -0
  174. package/llm-docs/tutorial/queries-1.mdx +267 -0
  175. package/llm-docs/tutorial/queries-2.mdx +389 -0
  176. package/llm-docs/tutorial/refetchable-fragments.mdx +352 -0
  177. package/multi-actor-environment/ActorIdentifier.d.ts +17 -0
  178. package/multi-actor-environment/ActorIdentifier.js.flow +2 -2
  179. package/multi-actor-environment/ActorSpecificEnvironment.js.flow +15 -15
  180. package/multi-actor-environment/ActorUtils.js.flow +1 -1
  181. package/multi-actor-environment/MultiActorEnvironment.d.ts +123 -0
  182. package/multi-actor-environment/MultiActorEnvironment.js.flow +32 -24
  183. package/multi-actor-environment/MultiActorEnvironmentTypes.d.ts +225 -0
  184. package/multi-actor-environment/MultiActorEnvironmentTypes.js.flow +6 -6
  185. package/multi-actor-environment/index.d.ts +14 -0
  186. package/multi-actor-environment.d.ts +8 -0
  187. package/mutations/RelayDeclarativeMutationConfig.d.ts +70 -0
  188. package/mutations/RelayDeclarativeMutationConfig.js.flow +9 -9
  189. package/mutations/RelayRecordProxy.js.flow +8 -11
  190. package/mutations/RelayRecordSourceMutator.js.flow +4 -4
  191. package/mutations/RelayRecordSourceProxy.js.flow +4 -4
  192. package/mutations/RelayRecordSourceSelectorProxy.js.flow +6 -6
  193. package/mutations/applyOptimisticMutation.d.ts +25 -0
  194. package/mutations/applyOptimisticMutation.js.flow +2 -2
  195. package/mutations/commitLocalUpdate.d.ts +10 -0
  196. package/mutations/commitMutation.d.ts +48 -0
  197. package/mutations/commitMutation.js.flow +21 -17
  198. package/mutations/createUpdatableProxy.js.flow +19 -19
  199. package/mutations/readUpdatableFragment.js.flow +3 -3
  200. package/mutations/readUpdatableQuery.js.flow +3 -3
  201. package/mutations/validateMutation.js.flow +7 -7
  202. package/network/RelayNetwork.d.ts +12 -0
  203. package/network/RelayNetworkTypes.d.ts +145 -0
  204. package/network/RelayNetworkTypes.js.flow +18 -18
  205. package/network/RelayObservable.d.ts +197 -0
  206. package/network/RelayObservable.js.flow +32 -30
  207. package/network/RelayQueryResponseCache.d.ts +16 -0
  208. package/network/RelayQueryResponseCache.js.flow +3 -3
  209. package/network/wrapNetworkWithLogObserver.js.flow +1 -1
  210. package/package.json +2 -1
  211. package/query/GraphQLTag.d.ts +45 -0
  212. package/query/GraphQLTag.js.flow +22 -10
  213. package/query/fetchQuery.d.ts +21 -0
  214. package/query/fetchQuery.js.flow +23 -10
  215. package/query/fetchQueryInternal.d.ts +26 -0
  216. package/query/fetchQueryInternal.js.flow +4 -4
  217. package/query/fetchQuery_DEPRECATED.d.ts +17 -0
  218. package/query/fetchQuery_DEPRECATED.js.flow +1 -1
  219. package/store/ClientID.d.ts +14 -0
  220. package/store/DataChecker.js.flow +51 -15
  221. package/store/NormalizationEngine.js.flow +782 -0
  222. package/store/OperationExecutor.d.ts +51 -0
  223. package/store/OperationExecutor.js.flow +204 -98
  224. package/store/RelayConcreteVariables.js.flow +5 -5
  225. package/store/RelayErrorTrie.js.flow +12 -12
  226. package/store/RelayExperimentalGraphResponseHandler.js.flow +3 -3
  227. package/store/RelayExperimentalGraphResponseTransform.js.flow +10 -10
  228. package/store/RelayModernEnvironment.d.ts +97 -0
  229. package/store/RelayModernEnvironment.js.flow +58 -43
  230. package/store/RelayModernFragmentSpecResolver.js.flow +1 -1
  231. package/store/RelayModernOperationDescriptor.d.ts +28 -0
  232. package/store/RelayModernOperationDescriptor.js.flow +1 -1
  233. package/store/RelayModernRecord.d.ts +92 -0
  234. package/store/RelayModernRecord.js.flow +44 -20
  235. package/store/RelayModernSelector.d.ts +123 -0
  236. package/store/RelayModernSelector.js.flow +21 -21
  237. package/store/RelayModernStore.d.ts +57 -0
  238. package/store/RelayModernStore.js.flow +219 -58
  239. package/store/RelayOperationTracker.d.ts +29 -0
  240. package/store/RelayOperationTracker.js.flow +2 -2
  241. package/store/RelayOptimisticRecordSource.js.flow +2 -2
  242. package/store/RelayPublishQueue.js.flow +29 -20
  243. package/store/RelayReader.js.flow +129 -57
  244. package/store/RelayRecordSource.d.ts +26 -0
  245. package/store/RelayRecordSource.js.flow +10 -0
  246. package/store/RelayRecordState.d.ts +28 -0
  247. package/store/RelayRecordState.js.flow +1 -1
  248. package/store/RelayReferenceMarker.js.flow +5 -4
  249. package/store/RelayResponseNormalizer.d.ts +28 -0
  250. package/store/RelayResponseNormalizer.js.flow +130 -62
  251. package/store/RelayStoreSubscriptions.js.flow +52 -8
  252. package/store/RelayStoreTypes.d.ts +1327 -0
  253. package/store/RelayStoreTypes.js.flow +371 -278
  254. package/store/RelayStoreUtils.d.ts +86 -0
  255. package/store/RelayStoreUtils.js.flow +16 -8
  256. package/store/ResolverCache.js.flow +2 -2
  257. package/store/ResolverFragments.d.ts +43 -0
  258. package/store/ResolverFragments.js.flow +22 -14
  259. package/store/StoreInspector.js.flow +7 -8
  260. package/store/ViewerPattern.d.ts +11 -0
  261. package/store/cloneRelayHandleSourceField.js.flow +1 -1
  262. package/store/cloneRelayScalarHandleSourceField.js.flow +1 -1
  263. package/store/createFragmentSpecResolver.d.ts +16 -0
  264. package/store/createRelayContext.js.flow +1 -1
  265. package/store/createRelayLoggingContext.js.flow +4 -4
  266. package/store/defaultGetDataID.js.flow +2 -2
  267. package/store/isRelayModernEnvironment.d.ts +8 -0
  268. package/store/isRelayModernEnvironment.js.flow +4 -2
  269. package/store/live-resolvers/LiveResolverCache.js.flow +55 -20
  270. package/store/live-resolvers/LiveResolverSuspenseSentinel.js.flow +3 -3
  271. package/store/live-resolvers/getOutputTypeRecordIDs.js.flow +1 -1
  272. package/store/live-resolvers/isLiveStateValue.js.flow +2 -2
  273. package/store/live-resolvers/resolverDataInjector.d.ts +27 -0
  274. package/store/live-resolvers/resolverDataInjector.js.flow +8 -5
  275. package/store/observeFragmentExperimental.d.ts +46 -0
  276. package/store/observeFragmentExperimental.js.flow +50 -21
  277. package/store/observeQueryExperimental.d.ts +30 -0
  278. package/store/observeQueryExperimental.js.flow +5 -5
  279. package/store/readInlineData.d.ts +19 -0
  280. package/store/readInlineData.js.flow +5 -5
  281. package/store/waitForFragmentExperimental.d.ts +49 -0
  282. package/store/waitForFragmentExperimental.js.flow +3 -3
  283. package/subscription/requestSubscription.d.ts +27 -0
  284. package/subscription/requestSubscription.js.flow +10 -10
  285. package/util/JSResourceTypes.flow.js.flow +4 -4
  286. package/util/NormalizationNode.d.ts +235 -0
  287. package/util/NormalizationNode.js.flow +127 -123
  288. package/util/ReaderNode.d.ts +264 -0
  289. package/util/ReaderNode.js.flow +156 -151
  290. package/util/RelayConcreteNode.d.ts +120 -0
  291. package/util/RelayConcreteNode.js.flow +32 -32
  292. package/util/RelayError.d.ts +13 -0
  293. package/util/RelayError.js.flow +4 -1
  294. package/util/RelayFeatureFlags.d.ts +40 -0
  295. package/util/RelayFeatureFlags.js.flow +21 -1
  296. package/util/RelayProfiler.d.ts +121 -0
  297. package/util/RelayProfiler.js.flow +1 -1
  298. package/util/RelayReplaySubject.d.ts +25 -0
  299. package/util/RelayReplaySubject.js.flow +3 -3
  300. package/util/RelayRuntimeTypes.d.ts +59 -0
  301. package/util/RelayRuntimeTypes.js.flow +36 -33
  302. package/util/createPayloadFor3DField.d.ts +17 -0
  303. package/util/createPayloadFor3DField.js.flow +9 -5
  304. package/util/deepFreeze.d.ts +8 -0
  305. package/util/deepFreeze.js.flow +2 -2
  306. package/util/getFragmentIdentifier.d.ts +10 -0
  307. package/util/getFragmentIdentifier.js.flow +1 -1
  308. package/util/getPaginationMetadata.d.ts +20 -0
  309. package/util/getPaginationMetadata.js.flow +1 -1
  310. package/util/getPaginationVariables.d.ts +20 -0
  311. package/util/getPaginationVariables.js.flow +1 -1
  312. package/util/getPendingOperationsForFragment.d.ts +18 -0
  313. package/util/getPendingOperationsForFragment.js.flow +2 -2
  314. package/util/getRefetchMetadata.d.ts +19 -0
  315. package/util/getRefetchMetadata.js.flow +6 -5
  316. package/util/getRelayHandleKey.d.ts +8 -0
  317. package/util/getRequestIdentifier.d.ts +17 -0
  318. package/util/getValueAtPath.d.ts +8 -0
  319. package/util/getValueAtPath.js.flow +3 -3
  320. package/util/handlePotentialSnapshotErrors.d.ts +14 -0
  321. package/util/handlePotentialSnapshotErrors.js.flow +5 -5
  322. package/util/isEmptyObject.js.flow +1 -1
  323. package/util/isPromise.d.ts +8 -0
  324. package/util/isPromise.js.flow +2 -2
  325. package/util/isScalarAndEqual.d.ts +8 -0
  326. package/util/isScalarAndEqual.js.flow +1 -1
  327. package/util/recycleNodesInto.d.ts +8 -0
  328. package/util/recycleNodesInto.js.flow +2 -2
  329. package/util/registerEnvironmentWithDevTools.js.flow +1 -1
  330. package/util/shallowFreeze.js.flow +1 -1
  331. package/util/stableCopy.d.ts +8 -0
  332. package/util/stableCopy.js.flow +5 -5
  333. package/util/withProvidedVariables.d.ts +19 -0
  334. package/util/withProvidedVariables.js.flow +14 -10
@@ -0,0 +1,642 @@
1
+ ---
2
+ id: testing-relay-components
3
+ title: Testing Relay Components
4
+ slug: /guides/testing-relay-components/
5
+ description: Relay guide to testing Relay components
6
+ keywords:
7
+ - testing
8
+ - createMockEnvironment
9
+ - RelayMockEnvironment
10
+ - MockPayloadGenerator
11
+ - relay_test_operation
12
+ - queuePendingOperation
13
+ - resolver
14
+ ---
15
+
16
+ import DocsRating from '@site/src/core/DocsRating';
17
+ import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal';
18
+
19
+ ## Abstract
20
+
21
+ The purpose of this document is to cover the Relay APIs for testing Relay components.
22
+
23
+ The content is focused mostly on jest unit-tests (testing individual components) and integration tests (testing a combination of components). But these testing tools may be applied in different cases: screenshot-tests, production smoke-tests, "Redbox" tests, fuzz-tests, e2e test, etc.
24
+
25
+ What are the benefits of writing jest tests:
26
+
27
+ * In general, it improves the stability of the system. Flow helps with catching a various set of Javascript errors, but it is still possible to introduce regressions to the components. Unit-tests help find, reproduce, and fix regressions, and prevent them in the future.
28
+ * It simplifies the refactoring process: when properly written (testing public interface, not implementation) - tests help with changing the internal implementation of the components.
29
+ * It may speed up and improve the development workflow. Some people may call it Test Driven Development (TM). But essentially it's just writing tests for public interfaces of your components, and then writing the components that implement those interfaces. Jest —watch mode really shines in this case.
30
+ * It will simplify the on-boarding process for new developers. Having tests helps new developers ramp up on the new code base, allowing them to fix bugs and deliver features.
31
+
32
+ One thing to notice: while jest unit- and integration tests will help improve the stability of the system, they should be considered one part of a bigger stability infrastructure with multiple layers of automated testing: flow, e2e, screenshot, "Redbox", performance tests.
33
+
34
+ ## Testing with Relay
35
+
36
+ Testing applications that use Relay may be challenging, because of the additional data fetching layer that wraps the actual product code.
37
+
38
+ And it's not always easy to understand the mechanics of all processes that are happening behind Relay, and how to properly handle interactions with the framework.
39
+
40
+ Fortunately, there are tools that aim to simplify the process of writing tests for Relay components, by providing imperative APIs for controlling the request/response flow and additional API for mock data generation.
41
+
42
+ There are two main Relay modules that you may use in your tests:
43
+
44
+ * `createMockEnvironment(options): RelayMockEnvironment`
45
+ * `MockPayloadGenerator` and the `@relay_test_operation` directive
46
+
47
+ With `createMockEnvironment,` you will be able to create an instance of `RelayMockEnvironment`, a Relay environment specifically for your tests. The instance created by `createMockEnvironment` implements the Relay Environment Interface and it also has an additional Mock layer, with methods that allow you to resolve/reject and control the flow of operations (queries/mutations/subscriptions).
48
+
49
+ The main purpose of `MockPayloadGenerator` is to improve the process of creating and maintaining the mock data for tested components.
50
+
51
+ One of the patterns you may see in the tests for Relay components: 95% of the test code is the test preparation—the gigantic mock object with dummy data, manually created, or just a copy of a sample server response that needs to be passed as the network response. And the remaining 5% is actual test code. As a result, people don't test much. It's hard to create and manage all these dummy payloads for different cases. Hence, writing tests is time-consuming and tests are sometimes painful to maintain.
52
+
53
+ With the `MockPayloadGenerator` and `@relay_test_operation`, we want to get rid of this pattern and switch the developer's focus from the preparation of the test to the actual testing.
54
+
55
+ ## Testing with React and Relay
56
+
57
+ **[React Testing Library](https://testing-library.com/react)** is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn't provide a way to "shallowly" render a component without its children, a test runner like Jest lets you do this by [mocking](https://reactjs.org/docs/testing-recipes.html#mocking-modules).
58
+
59
+ <FbInternalOnly>
60
+
61
+ Note: The [`ReactTestRenderer`](https://www.npmjs.com/package/react-test-renderer) library has been deprecated since React v18. `ReactTestRenderer` may still be referenced internally as an alternative to React Testing Library. However, when possible, we recommend using React Testing Library (or `@testing-library/react-native`) to test your React applications with Relay.
62
+
63
+ </FbInternalOnly>
64
+
65
+ ## RelayMockEnvironment API Overview
66
+
67
+ RelayMockEnvironment is a special version of Relay Environment with additional API methods for controlling the operation flow: resolving and rejection operations, providing incremental payloads for subscriptions, working with the cache.
68
+
69
+ * Methods for finding operations executed on the environment
70
+ * `getAllOperations()` - get all operation executed during the test by the current time
71
+ * `findOperation(findFn => boolean) `- find particular operation in the list of all executed operations, this method will throw, if operation is not available. Maybe useful to find a particular operation when multiple operations executed at the same time
72
+ * `getMostRecentOperation() -` return the most recent operation, this method will throw if no operations were executed prior this call.
73
+ * Methods for resolving or rejecting operations
74
+ * `nextValue(request | operation, data)` - provide payload for operation(request), but not complete request. Practically useful when testing incremental updates and subscriptions
75
+ * `complete(request | operation)` - complete the operation, no more payloads are expected for this operation, when it's completed.
76
+ * `resolve(request | operation, data)` - resolve the request with provided GraphQL response. Essentially, it's nextValue(...) and complete(...)
77
+ * `reject(request | operation, error)` - reject the request with particular error
78
+ * `resolveMostRecentOperation(operation => data)` - resolve and getMostRecentOperation work together
79
+ * `rejectMostRecentOperation(operation => error)` - reject and getMostRecentOperation work together
80
+ * `queueOperationResolver(operation => data | error)` - adds an OperationResolver function to the queue. The passed resolver will be used to resolve/reject operations as they appear
81
+ * `queuePendingOperation(query, variables)` - in order for the `usePreloadedQuery` hook to not suspend, one must call these functions:
82
+ * `queueOperationResolver(resolver)`
83
+ * `queuePendingOperation(query, variables)`
84
+ * `preloadQuery(mockEnvironment, query, variables)` with the same `query` and `variables` that were passed to `queuePendingOperation`. `preloadQuery` must be called after `queuePendingOperation`.
85
+ * Additional utility methods
86
+ * `isLoading(request | operation)` - will return `true` if operations has not been completed, yet.
87
+ * `cachePayload(request | operation, variables, payload)` - will add payload to QueryResponse cache
88
+ * `clearCache() `- will clear QueryResponse cache
89
+
90
+ ## Mock Payload Generator and the `@relay_test_operation` Directive
91
+
92
+ `MockPayloadGenerator` may drastically simplify the process of creating and maintaining mock data for your tests. `MockPayloadGenerator` can generate dummy data for the selection that you have in your operation. There is an API to modify the generated data - Mock Resolvers. With Mock Resolvers, you may adjust the data for your needs. Mock Resolvers are defined as an object where **keys are names of GraphQL types (`ID`, `String`, `User`, `Comment`, etc),** and values are functions that return the default data for the type.
93
+
94
+ Example of a simple Mock Resolver:
95
+
96
+ ```js
97
+ {
98
+ ID() {
99
+ // Return mock value for a scalar filed with type ID
100
+ return 'my-id';
101
+ },
102
+ String() {
103
+ // Every scalar field with type String will have this default value
104
+ return "Lorem Ipsum"
105
+ }
106
+ }
107
+ ```
108
+
109
+
110
+ It is possible to define more resolvers for Object types
111
+
112
+ ```js
113
+ {
114
+ // This will be the default values for User object in the query response
115
+ User() {
116
+ return {
117
+ id: 4,
118
+ name: "Mark",
119
+ profile_picture: {
120
+ uri: "http://my-image...",
121
+ },
122
+ };
123
+ },
124
+ }
125
+ ```
126
+
127
+
128
+
129
+ ### Mock Resolver Context
130
+
131
+ The first argument of the MockResolver is the object that contains Mock Resolver Context. It is possible to return dynamic values from mock resolvers based on the context - for instance, name or alias of the field, a path in the selection, arguments, or parent type.
132
+
133
+
134
+ ```js
135
+ {
136
+ String(context) {
137
+ if (context.name === 'zip') {
138
+ return '94025';
139
+ }
140
+ if (context.path != null && context.path.join('.') === 'node.actor.name') {
141
+ return 'Current Actor Name';
142
+ }
143
+ if (context.parentType === 'Image' && context.name === 'uri') {
144
+ return 'http://my-image.url';
145
+ }
146
+ }
147
+ }
148
+ ```
149
+
150
+ ### ID Generation
151
+
152
+ The second argument of the Mock Resolver is a function that will generate a sequence of integers, useful to generate unique ids in the tests
153
+
154
+ ```js
155
+ {
156
+ // will generate strings "my-id-1", "my-id-2", etc.
157
+ ID(_, generateId) {
158
+ return `my-id-${generateId()}`;
159
+ },
160
+ }
161
+ ```
162
+
163
+ ### Float, Integer, Boolean, etc...
164
+
165
+ Please note, that for production queries we don't have full type information for Scalar fields - like Boolean, Integer, Float. And in the MockResolvers, they map to String. You can use `context` to adjust return values, based on the field name, alias, etc.
166
+
167
+ ### @relay_test_operation
168
+
169
+ Most of GraphQL type information for a specific field in the selection is not available during Relay runtime. By default, Relay, cannot get type information for a scalar field in the selection, or an interface type of the object.
170
+
171
+ Operation with the @relay_test_operation directive will have additional metadata that will contain GraphQL type info for fields in the operation's selection. And it will improve the quality of the generated data. You also will be able to define Mock resolvers for Scalar (not only ID and String) and Abstract types:
172
+
173
+ ```javascript
174
+ {
175
+ Float() {
176
+ return 123.456;
177
+ },
178
+ Boolean(context) {
179
+ if (context.name === 'can_edit') {
180
+ return true;
181
+ }
182
+ return false;
183
+ },
184
+ Node() {
185
+ return {
186
+ __typename: 'User',
187
+ id: 'my-user-id',
188
+ };
189
+ }
190
+ }
191
+ ```
192
+
193
+ ## Examples
194
+
195
+ ### Relay Component Test
196
+
197
+ Using `createMockEnvironment` and `MockPayloadGenerator` allows writing concise tests for components that use Relay hooks. Both those modules can be imported from `relay-test-utils`
198
+
199
+
200
+ ```javascript
201
+ // Say you have a component with the useLazyLoadQuery or a QueryRenderer
202
+ const MyAwesomeViewRoot = require('MyAwesomeViewRoot');
203
+ const {
204
+ createMockEnvironment,
205
+ MockPayloadGenerator,
206
+ } = require('relay-test-utils');
207
+ const {act, render} = require('@testing-library/react');
208
+
209
+ // Relay may trigger 3 different states
210
+ // for this component: Loading, Error, Data Loaded
211
+ // Here is examples of tests for those states.
212
+ test('Loading State', async () => {
213
+ const environment = createMockEnvironment();
214
+ const renderer = render(
215
+ <MyAwesomeViewRoot environment={environment} />,
216
+ );
217
+
218
+ // Here we just verify that the spinner is rendered
219
+ expect(await renderer.findByTestId('spinner')).toBeDefined();
220
+ });
221
+
222
+ test('Data Render', async () => {
223
+ const environment = createMockEnvironment();
224
+ const renderer = render(
225
+ <MyAwesomeViewRoot environment={environment} />,
226
+ );
227
+
228
+ // Wrapping in act will ensure that components
229
+ // are fully updated to their final state.
230
+ act(() => {
231
+ environment.mock.resolveMostRecentOperation(operation =>
232
+ MockPayloadGenerator.generate(operation),
233
+ );
234
+ });
235
+
236
+ // At this point operation will be resolved
237
+ // and the data for a query will be available in the store
238
+ expect(await renderer.findByTestId('myButton')).toBeDefined();
239
+ });
240
+
241
+ test('Error State', async () => {
242
+ const environment = createMockEnvironment();
243
+ const renderer = render(
244
+ <MyAwesomeViewRoot environment={environment} />,
245
+ );
246
+
247
+ // Wrapping in act will ensure that components
248
+ // are fully updated to their final state.
249
+ act(() => {
250
+ // Error can be simulated with `rejectMostRecentOperation`
251
+ environment.mock.rejectMostRecentOperation(new Error('Uh-oh'));
252
+ });
253
+
254
+ expect(await renderer.findByTestId('errorMessage')).toBeDefined();
255
+ });
256
+ ```
257
+
258
+ #### Component Tests With Deferred Fragments
259
+
260
+ When using `MockPayloadGenerator` to generate data for a Query that has fragments with `@defer` you may want to generate the deferred data as well. To do so, you can use `MockPayloadGenerator.generateWithDefer` passing the option `generateDeferredPayload`:
261
+
262
+ ```javascript
263
+ // Say you have a component with useFragment
264
+ const ChildComponent = (props: {user: ChildComponentFragment_user$key}) => {
265
+ const data = useFragment(graphql`
266
+ fragment ChildComponentFragment_user on User {
267
+ name
268
+ }
269
+ `, props.user);
270
+ return <View>{data?.name}</View>;
271
+ };
272
+
273
+ // Say you have a parent component that fetches data with useLazyLoadQuery and `@defer`s the data for the ChildComponent.
274
+ const ParentComponent = () => {
275
+ const data = useLazyLoadQuery(graphql`
276
+ query ParentComponentQuery {
277
+ user {
278
+ id
279
+ ...ChildComponentFragment_user @defer
280
+ }
281
+ }
282
+ `, {});
283
+ return (
284
+ <View>
285
+ {id}
286
+ <Suspense fallback={null}>
287
+ {data?.user && <ChildComponent user={data.user} />}
288
+ </Suspense>
289
+ </View>
290
+ );
291
+ };
292
+
293
+ const {
294
+ createMockEnvironment,
295
+ MockPayloadGenerator,
296
+ } = require('relay-test-utils');
297
+ const {act, render} = require('@testing-library/react');
298
+
299
+ test('Data Render with @defer', () => {
300
+ const environment = createMockEnvironment();
301
+ const renderer = render(
302
+ <RelayEnvironmentProvider environment={environment}>
303
+ <ParentComponent />,
304
+ </RelayEnvironmentProvider>
305
+ );
306
+
307
+ // Wrapping in ReactTestRenderer.act will ensure that components
308
+ // are fully updated to their final state.
309
+ act(() => {
310
+ const operation = environment.mock.getMostRecentOperation();
311
+ const mockData = MockPayloadGenerator.generateWithDefer(operation, null, {generateDeferredPayload: true});
312
+ environment.mock.resolve(mockData);
313
+
314
+ // You may need this to make sure all payloads are retrieved
315
+ jest.runAllTimers();
316
+ });
317
+
318
+ // At this point operation will be resolved
319
+ // and the data for a query will be available in the store
320
+ expect(renderer.container.textContent).toEqual(['id', 'name']);
321
+ });
322
+ ```
323
+
324
+ ### Fragment Component Tests
325
+
326
+ Essentially, in the example above, `resolveMostRecentOperation` will generate data for all child fragment containers (pagination, refetch). But, usually the root component may have many child fragment components and you may want to exercise a specific component that uses `useFragment`. The solution for that would be to wrap your fragment container with the `useLazyLoadQuery` component that renders a Query that spreads fragments from your fragment component:
327
+
328
+ ```javascript
329
+ test('Fragment', () => {
330
+ const environment = createMockEnvironment();
331
+ const TestRenderer = () => {
332
+ const data = useLazyLoadQuery(
333
+ graphql`
334
+ query TestQuery @relay_test_operation {
335
+ myData: node(id: "test-id") {
336
+ # Spread the fragment you want to test here
337
+ ...MyFragment
338
+ }
339
+ }
340
+ `,
341
+ {},
342
+ );
343
+ return <MyFragmentComponent myData={data.myData} />
344
+ };
345
+
346
+ const renderer = render(
347
+ <RelayEnvironmentProvider environment={environment}>
348
+ <Suspense fallback="Loading...">
349
+ <TestRenderer />
350
+ </Suspense>
351
+ </RelayEnvironmentProvider>
352
+ );
353
+
354
+ // Wrapping in act will ensure that components
355
+ // are fully updated to their final state.
356
+ act(() => {
357
+ environment.mock.resolveMostRecentOperation(operation =>
358
+ MockPayloadGenerator.generate(operation),
359
+ );
360
+ });
361
+
362
+ expect(renderer).toMatchSnapshot();
363
+ });
364
+ ```
365
+
366
+ ### Pagination Component Test
367
+
368
+ Essentially, tests for pagination components (e.g. using `usePaginationFragment`) are not different from fragment component tests. But we can do more here, we can actually see how the pagination works - we can assert the behavior of our components when performing pagination (load more, refetch).
369
+
370
+ ```js
371
+ // Pagination Example
372
+ test('`Pagination` Container', async () => {
373
+ const environment = createMockEnvironment();
374
+ const TestRenderer = () => {
375
+ const data = useLazyLoadQuery(
376
+ graphql`
377
+ query TestQuery @relay_test_operation {
378
+ myConnection: node(id: "test-id") {
379
+ connection {
380
+ # Spread the pagination fragment you want to test here
381
+ ...MyConnectionFragment
382
+ }
383
+ }
384
+ }
385
+ `,
386
+ {},
387
+ );
388
+ return <MyPaginationContainer connection={data.myConnection.connection} />
389
+ };
390
+
391
+ const renderer = render(
392
+ <RelayEnvironmentProvider environment={environment}>
393
+ <Suspense fallback="Loading...">
394
+ <TestRenderer />
395
+ </Suspense>
396
+ </RelayEnvironmentProvider>
397
+ );
398
+
399
+ // Wrapping in act will ensure that components
400
+ // are fully updated to their final state.
401
+ act(() => {
402
+ environment.mock.resolveMostRecentOperation(operation =>
403
+ MockPayloadGenerator.generate(operation, {
404
+ ID(_, generateId) {
405
+ // Why we're doing this?
406
+ // To make sure that we will generate a different set of ID
407
+ // for elements on first page and the second page.
408
+ return `first-page-id-${generateId()}`;
409
+ },
410
+ PageInfo() {
411
+ return {
412
+ has_next_page: true,
413
+ };
414
+ },
415
+ }),
416
+ );
417
+ });
418
+
419
+ // Let's find a `loadMore` button and click on it to initiate pagination request, for example
420
+ const loadMore = await renderer.findByTestId('loadMore');
421
+ expect(loadMore.props.disabled).toBe(false);
422
+ loadMore.props.onClick();
423
+
424
+ // Wrapping in act will ensure that components
425
+ // are fully updated to their final state.
426
+ act(() => {
427
+ environment.mock.resolveMostRecentOperation(operation =>
428
+ MockPayloadGenerator.generate(operation, {
429
+ ID(_, generateId) {
430
+ // See, the second page IDs will be different
431
+ return `second-page-id-${generateId()}`;
432
+ },
433
+ PageInfo() {
434
+ return {
435
+ // And the button should be disabled, now. Probably.
436
+ has_next_page: false,
437
+ };
438
+ },
439
+ }),
440
+ );
441
+ });
442
+
443
+ expect(loadMore.props.disabled).toBe(true);
444
+ });
445
+ ```
446
+
447
+ ### Refetch Component
448
+
449
+ We can use similar approach here with wrapping the component with a query. And for the sake of completeness, we will add an example here:
450
+
451
+ ```js
452
+ test('Refetch Container', async () => {
453
+ const environment = createMockEnvironment();
454
+ const TestRenderer = () => {
455
+ const data = useLazyLoadQuery(
456
+ graphql`
457
+ query TestQuery @relay_test_operation {
458
+ myData: node(id: "test-id") {
459
+ # Spread the pagination fragment you want to test here
460
+ ...MyRefetchableFragment
461
+ }
462
+ }
463
+ `,
464
+ {},
465
+ );
466
+ return <MyRefetchContainer data={data.myData} />
467
+ };
468
+
469
+ const renderer = render(
470
+ <RelayEnvironmentProvider environment={environment}>
471
+ <Suspense fallback="Loading...">
472
+ <TestRenderer />
473
+ </Suspense>
474
+ </RelayEnvironmentProvider>
475
+ );
476
+
477
+ act(() => {
478
+ environment.mock.resolveMostRecentOperation(operation =>
479
+ MockPayloadGenerator.generate(operation),
480
+ );
481
+ });
482
+
483
+ // Assuming we have refetch button in the Container
484
+ const refetchButton = await renderer.findByTestId('refetch');
485
+
486
+ // This should trigger the `refetch`
487
+ refetchButton.props.onClick();
488
+
489
+ act(() => {
490
+ environment.mock.resolveMostRecentOperation(operation =>
491
+ MockPayloadGenerator.generate(operation, {
492
+ // We can customize mock resolvers, to change the output of the refetch query
493
+ }),
494
+ );
495
+ });
496
+
497
+ expect(renderer).toMatchSnapshot();
498
+ });
499
+ ```
500
+
501
+
502
+
503
+ ### Mutations
504
+
505
+ Mutations themselves are operations, so we can test them independently (unit-test) for a specific mutation, or in combination with the view from which this mutation is called.
506
+
507
+ :::note
508
+ the `useMutation` API is an improvement over calling `commitMutation` directly.
509
+ :::
510
+
511
+ ```js
512
+ // Say, you have a mutation function
513
+ function sendMutation(environment, onCompleted, onError, variables)
514
+ commitMutation(environment, {
515
+ mutation: graphql`...`,
516
+ onCompleted,
517
+ onError,
518
+ variables,
519
+ });
520
+ }
521
+
522
+ // Example test may be written like so
523
+ test('it should send mutation', () => {
524
+ const environment = createMockEnvironment();
525
+ const onCompleted = jest.fn();
526
+ sendMutation(environment, onCompleted, jest.fn(), {});
527
+ const operation = environment.mock.getMostRecentOperation();
528
+
529
+ act(() => {
530
+ environment.mock.resolve(
531
+ operation,
532
+ MockPayloadGenerator.generate(operation)
533
+ );
534
+ });
535
+
536
+ expect(onCompleted).toBeCalled();
537
+ });
538
+ ```
539
+
540
+ ### Subscription
541
+
542
+ :::tip
543
+ The `useSubscription` API is an improvement over calling `requestSubscription` directly.
544
+ :::
545
+
546
+ We can test subscriptions similarly to how we test mutations.
547
+
548
+ ```js
549
+ // Example subscribe function
550
+ function subscribe(environment, onNext, onError, variables)
551
+ requestSubscription(environment, {
552
+ subscription: graphql`...`,
553
+ onNext,
554
+ onError,
555
+ variables,
556
+ });
557
+ }
558
+
559
+ // Example test may be written like so
560
+ test('it should subscribe', () => {
561
+ const environment = createMockEnvironment();
562
+ const onNext = jest.fn();
563
+ subscribe(environment, onNext, jest.fn(), {});
564
+ const operation = environment.mock.getMostRecentOperation();
565
+
566
+ act(() => {
567
+ environment.mock.nextValue(
568
+ operation,
569
+ MockPayloadGenerator.generate(operation)
570
+ );
571
+ });
572
+
573
+ expect(onNext).toBeCalled();
574
+ });
575
+ ```
576
+
577
+ ### Example with `queueOperationResolver`
578
+
579
+ With `queueOperationResolver` it is possible to define responses for operations that will be executed on the environment
580
+
581
+ ```javascript
582
+ // Say you have a component with the QueryRenderer
583
+ const MyAwesomeViewRoot = require('MyAwesomeViewRoot');
584
+ const {
585
+ createMockEnvironment,
586
+ MockPayloadGenerator,
587
+ } = require('relay-test-utils');
588
+
589
+ test('Data Render', async () => {
590
+ const environment = createMockEnvironment();
591
+ environment.mock.queueOperationResolver(operation =>
592
+ MockPayloadGenerator.generate(operation),
593
+ );
594
+
595
+ const renderer = render(
596
+ <MyAwesomeViewRoot environment={environment} />,
597
+ );
598
+
599
+ // At this point operation will be resolved
600
+ // and the data for a query will be available in the store
601
+ expect(await renderer.findByTestId('myButton')).toBeDefined();
602
+ });
603
+
604
+ test('Error State', async () => {
605
+ const environment = createMockEnvironment();
606
+ environment.mock.queueOperationResolver(() =>
607
+ new Error('Uh-oh'),
608
+ );
609
+ const renderer = render(
610
+ <MyAwesomeViewRoot environment={environment} />,
611
+ );
612
+
613
+ expect(await renderer.findByTestId('myButton')).toBeDefined();
614
+ });
615
+ ```
616
+
617
+ ### With Relay Hooks
618
+
619
+ The examples in this guide should work for testing components both with Relay Hooks, Containers or Renderers. When writing tests that involve the `usePreloadedQuery` hook, please also see the `queuePendingOperation` note above.
620
+
621
+ ### toMatchSnapshot(...)
622
+
623
+ Even though in all of the examples here you can see assertions with `toMatchSnapshot()`, we keep it that way just to make examples concise. But it's not the recommended way to test your components.
624
+
625
+ ### More Examples
626
+
627
+ <FbInternalOnly>
628
+
629
+ As a reference implementation I've put working examples here:
630
+ https://phabricator.internmc.facebook.com/diffusion/FBS/browse/master/xplat/js/RKJSModules/Libraries/Relay/oss/relay-test-utils/__tests__/RelayMockEnvironmentWithComponents-test.js
631
+
632
+ </FbInternalOnly>
633
+
634
+ <OssOnly>
635
+
636
+ The best source of example tests is in [the relay-experimental package](https://github.com/facebook/relay/tree/main/packages/relay-experimental/__tests__).
637
+
638
+ </OssOnly>
639
+
640
+ Testing is good. You should definitely do it.
641
+
642
+ <DocsRating />