From a3130f036d0dd7597e606bb5e540990b56c16af8 Mon Sep 17 00:00:00 2001 From: Yuhao Ju Date: Fri, 6 Jan 2023 16:12:27 -0800 Subject: [PATCH 1/4] feat: add onError prop to MockedProvider --- src/testing/core/index.ts | 1 + src/testing/core/mocking/mockLink.ts | 22 ++++++++++++++++++++-- src/testing/react/MockedProvider.tsx | 7 +++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/testing/core/index.ts b/src/testing/core/index.ts index c03a7e8cf69..bc727bf8c35 100644 --- a/src/testing/core/index.ts +++ b/src/testing/core/index.ts @@ -1,5 +1,6 @@ export { MockLink, + MockOnErrorData, mockSingleLink, MockedResponse, ResultFunction diff --git a/src/testing/core/mocking/mockLink.ts b/src/testing/core/mocking/mockLink.ts index 7d0086a4c51..0a306bceafc 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 MockOnErrorData { + 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 onErrorCustomHandler: (error: Error, mockOnErrorData: MockOnErrorData) => Boolean; private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {}; constructor( mockedResponses: ReadonlyArray, - addTypename: Boolean = true + addTypename: Boolean = true, + onErrorCustomHandler?: (error: Error, mockOnErrorData: MockOnErrorData) => Boolean, ) { super(); this.addTypename = addTypename; + if (onErrorCustomHandler) { + this.onErrorCustomHandler = onErrorCustomHandler; + } if (mockedResponses) { mockedResponses.forEach(mockedResponse => { this.addMockedResponse(mockedResponse); @@ -127,7 +139,13 @@ ${unmatchedVars.map(d => ` ${stringifyForDisplay(d)}`).join('\n')} // 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 (this.onErrorCustomHandler(configError, { + key, + mockedResponses, + operation, + unmatchedVars, + }) !== false + && 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..74ab6a7527c 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, MockOnErrorData, 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, mockOnErrorData: MockOnErrorData) => 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, }); From 9cbbdaab926e7a89a725b169174e1e42ad85c6e0 Mon Sep 17 00:00:00 2001 From: Yuhao Ju Date: Fri, 6 Jan 2023 17:03:19 -0800 Subject: [PATCH 2/4] chore: fix unit tests --- src/testing/core/mocking/mockLink.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/testing/core/mocking/mockLink.ts b/src/testing/core/mocking/mockLink.ts index 0a306bceafc..94792af7fe4 100644 --- a/src/testing/core/mocking/mockLink.ts +++ b/src/testing/core/mocking/mockLink.ts @@ -139,13 +139,14 @@ ${unmatchedVars.map(d => ` ${stringifyForDisplay(d)}`).join('\n')} // example, the default implementation of onError calls // observer.error(configError) and then returns false to // prevent this extra (harmless) observer.error call. - if (this.onErrorCustomHandler(configError, { - key, - mockedResponses, - operation, - unmatchedVars, - }) !== false - && this.onError(configError, observer) !== false) { + const shouldPassToOnError = typeof this.onErrorCustomHandler === 'undefined' + || this.onErrorCustomHandler(configError, { + key, + mockedResponses, + operation, + unmatchedVars, + }) !== false; + if (shouldPassToOnError && this.onError(configError, observer) !== false) { throw configError; } } catch (error) { From b4f42feb45f5759d4ed6b6ecedee40e265cdafd4 Mon Sep 17 00:00:00 2001 From: Yuhao Ju Date: Fri, 6 Jan 2023 17:07:28 -0800 Subject: [PATCH 3/4] chore: add changeset --- .changeset/fluffy-masks-cover.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fluffy-masks-cover.md 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 From 454249b20f8a04b7f0e284e39ccf47640e734356 Mon Sep 17 00:00:00 2001 From: Yuhao Ju Date: Sat, 7 Jan 2023 09:46:05 -0800 Subject: [PATCH 4/4] chore: rename params --- src/testing/core/index.ts | 2 +- src/testing/core/mocking/mockLink.ts | 27 +++++++++++++++------------ src/testing/react/MockedProvider.tsx | 4 ++-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/testing/core/index.ts b/src/testing/core/index.ts index bc727bf8c35..31974ebb154 100644 --- a/src/testing/core/index.ts +++ b/src/testing/core/index.ts @@ -1,6 +1,6 @@ export { MockLink, - MockOnErrorData, + MockLinkData, mockSingleLink, MockedResponse, ResultFunction diff --git a/src/testing/core/mocking/mockLink.ts b/src/testing/core/mocking/mockLink.ts index 94792af7fe4..72a77a63613 100644 --- a/src/testing/core/mocking/mockLink.ts +++ b/src/testing/core/mocking/mockLink.ts @@ -29,7 +29,7 @@ export interface MockedResponse> { newData?: ResultFunction; } -export interface MockOnErrorData { +export interface MockLinkData { key: string; mockedResponses: ReadonlyArray; operation: Operation; @@ -47,18 +47,18 @@ function requestToKey(request: GraphQLRequest, addTypename: Boolean): string { export class MockLink extends ApolloLink { public operation: Operation; public addTypename: Boolean = true; - public onErrorCustomHandler: (error: Error, mockOnErrorData: MockOnErrorData) => Boolean; + public customErrorHandler: (error: Error, mockLinkData: MockLinkData) => Boolean; private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {}; constructor( mockedResponses: ReadonlyArray, addTypename: Boolean = true, - onErrorCustomHandler?: (error: Error, mockOnErrorData: MockOnErrorData) => Boolean, + customErrorHandler?: (error: Error, mockLinkData: MockLinkData) => Boolean, ) { super(); this.addTypename = addTypename; - if (onErrorCustomHandler) { - this.onErrorCustomHandler = onErrorCustomHandler; + if (customErrorHandler) { + this.customErrorHandler = customErrorHandler; } if (mockedResponses) { mockedResponses.forEach(mockedResponse => { @@ -134,18 +134,21 @@ ${unmatchedVars.map(d => ` ${stringifyForDisplay(d)}`).join('\n')} const timer = setTimeout(() => { if (configError) { try { - // 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. - const shouldPassToOnError = typeof this.onErrorCustomHandler === 'undefined' - || this.onErrorCustomHandler(configError, { + // 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 (shouldPassToOnError && this.onError(configError, observer) !== false) { throw configError; } diff --git a/src/testing/react/MockedProvider.tsx b/src/testing/react/MockedProvider.tsx index 74ab6a7527c..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, MockOnErrorData, MockedResponse } from '../core'; +import { MockLink, MockLinkData, MockedResponse } from '../core'; import { ApolloLink } from '../../link/core'; import { Resolvers } from '../../core'; import { ApolloCache } from '../../cache'; @@ -17,7 +17,7 @@ export interface MockedProviderProps { childProps?: object; children?: any; link?: ApolloLink; - onError?: (error: Error, mockOnErrorData: MockOnErrorData) => Boolean; + onError?: (error: Error, mockLinkData: MockLinkData) => Boolean; } export interface MockedProviderState {