diff --git a/.changeset/fluffy-masks-cover.md b/.changeset/fluffy-masks-cover.md new file mode 100644 index 00000000000..38ec989910e --- /dev/null +++ b/.changeset/fluffy-masks-cover.md @@ -0,0 +1,5 @@ +--- +"@apollo/client": minor +--- + +Add onError prop to MockProvider diff --git a/src/testing/core/index.ts b/src/testing/core/index.ts index c03a7e8cf69..31974ebb154 100644 --- a/src/testing/core/index.ts +++ b/src/testing/core/index.ts @@ -1,5 +1,6 @@ export { MockLink, + MockLinkData, mockSingleLink, MockedResponse, ResultFunction diff --git a/src/testing/core/mocking/mockLink.ts b/src/testing/core/mocking/mockLink.ts index 7d0086a4c51..72a77a63613 100644 --- a/src/testing/core/mocking/mockLink.ts +++ b/src/testing/core/mocking/mockLink.ts @@ -29,6 +29,13 @@ export interface MockedResponse> { newData?: ResultFunction; } +export interface MockLinkData { + key: string; + mockedResponses: ReadonlyArray; + operation: Operation; + unmatchedVars: Array>; +} + function requestToKey(request: GraphQLRequest, addTypename: Boolean): string { const queryString = request.query && @@ -40,14 +47,19 @@ function requestToKey(request: GraphQLRequest, addTypename: Boolean): string { export class MockLink extends ApolloLink { public operation: Operation; public addTypename: Boolean = true; + public customErrorHandler: (error: Error, mockLinkData: MockLinkData) => Boolean; private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {}; constructor( mockedResponses: ReadonlyArray, - addTypename: Boolean = true + addTypename: Boolean = true, + customErrorHandler?: (error: Error, mockLinkData: MockLinkData) => Boolean, ) { super(); this.addTypename = addTypename; + if (customErrorHandler) { + this.customErrorHandler = customErrorHandler; + } if (mockedResponses) { mockedResponses.forEach(mockedResponse => { this.addMockedResponse(mockedResponse); @@ -122,12 +134,22 @@ ${unmatchedVars.map(d => ` ${stringifyForDisplay(d)}`).join('\n')} const timer = setTimeout(() => { if (configError) { try { + // The customErrorHandler function can return false to indicate that + // configError need not be passed to the onError function. + const shouldPassToOnError = typeof this.customErrorHandler !== 'function' + || this.customErrorHandler(configError, { + key, + mockedResponses, + operation, + unmatchedVars, + }) !== false; + // The onError function can return false to indicate that // configError need not be passed to observer.error. For // example, the default implementation of onError calls // observer.error(configError) and then returns false to // prevent this extra (harmless) observer.error call. - if (this.onError(configError, observer) !== false) { + if (shouldPassToOnError && this.onError(configError, observer) !== false) { throw configError; } } catch (error) { diff --git a/src/testing/react/MockedProvider.tsx b/src/testing/react/MockedProvider.tsx index b64526411ff..2f022f73970 100644 --- a/src/testing/react/MockedProvider.tsx +++ b/src/testing/react/MockedProvider.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { ApolloClient, DefaultOptions } from '../../core'; import { InMemoryCache as Cache } from '../../cache'; import { ApolloProvider } from '../../react/context'; -import { MockLink, MockedResponse } from '../core'; +import { MockLink, MockLinkData, MockedResponse } from '../core'; import { ApolloLink } from '../../link/core'; import { Resolvers } from '../../core'; import { ApolloCache } from '../../cache'; @@ -17,6 +17,7 @@ export interface MockedProviderProps { childProps?: object; children?: any; link?: ApolloLink; + onError?: (error: Error, mockLinkData: MockLinkData) => Boolean; } export interface MockedProviderState { @@ -40,7 +41,8 @@ export class MockedProvider extends React.Component< defaultOptions, cache, resolvers, - link + link, + onError, } = this.props; const client = new ApolloClient({ cache: cache || new Cache({ addTypename }), @@ -48,6 +50,7 @@ export class MockedProvider extends React.Component< link: link || new MockLink( mocks || [], addTypename, + onError, ), resolvers, });