Skip to content

Commit

Permalink
Merge branch 'release-3.10' of github.com:apollographql/apollo-client…
Browse files Browse the repository at this point in the history
… into issue-11758-merging-resolvers
  • Loading branch information
alessbell committed Apr 10, 2024
2 parents 49d9557 + 110ce9e commit cd40f9c
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 40 deletions.
14 changes: 7 additions & 7 deletions .api-reports/api-report-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,22 +450,22 @@ type ConcastSourcesIterable<T> = Iterable<Source<T>>;
export function createMockClient<TData>(data: TData, query: DocumentNode, variables?: {}): ApolloClient<NormalizedCacheObject>;

// @alpha
export const createMockFetch: (schema: GraphQLSchema, mockFetchOpts?: {
export const createMockSchema: (staticSchema: GraphQLSchema, mocks: {
[key: string]: any;
}) => GraphQLSchema;

// @alpha
export const createSchemaFetch: (schema: GraphQLSchema, mockFetchOpts?: {
validate: boolean;
}) => {
mock: (uri: any, options: any) => Promise<Response>;
restore: () => void;
} & Disposable;

// @alpha
export const createMockSchema: (staticSchema: GraphQLSchema, mocks: {
[key: string]: any;
}) => GraphQLSchema;

// Warning: (ae-forgotten-export) The symbol "ProxiedSchema" needs to be exported by the entry point index.d.ts
//
// @alpha
export const createProxiedSchema: (schemaWithMocks: GraphQLSchema, resolvers: Resolvers) => ProxiedSchema;
export const createTestSchema: (schemaWithMocks: GraphQLSchema, resolvers: Resolvers) => ProxiedSchema;

// @public (undocumented)
namespace DataProxy {
Expand Down
4 changes: 2 additions & 2 deletions .api-reports/api-report-testing_core.md
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ type ConcastSourcesIterable<T> = Iterable<Source<T>>;
export function createMockClient<TData>(data: TData, query: DocumentNode, variables?: {}): ApolloClient<NormalizedCacheObject>;

// @alpha
export const createMockFetch: (schema: GraphQLSchema, mockFetchOpts?: {
export const createSchemaFetch: (schema: GraphQLSchema, mockFetchOpts?: {
validate: boolean;
}) => {
mock: (uri: any, options: any) => Promise<Response>;
Expand All @@ -459,7 +459,7 @@ export const createMockFetch: (schema: GraphQLSchema, mockFetchOpts?: {
// Warning: (ae-forgotten-export) The symbol "ProxiedSchema" needs to be exported by the entry point index.d.ts
//
// @alpha
export const createProxiedSchema: (schemaWithMocks: GraphQLSchema, resolvers: Resolvers) => ProxiedSchema;
export const createTestSchema: (schemaWithMocks: GraphQLSchema, resolvers: Resolvers) => ProxiedSchema;

// @public (undocumented)
namespace DataProxy {
Expand Down
5 changes: 5 additions & 0 deletions .changeset/green-garlics-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": minor
---

Rename `createProxiedSchema` to `createTestSchema` and `createMockFetch` to `createSchemaFetch`.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@

- [#11465](https://github.com/apollographql/apollo-client/pull/11465) [`7623da7`](https://github.com/apollographql/apollo-client/commit/7623da7720855b0c19e13ff9124679f426a39725) Thanks [@alessbell](https://github.com/alessbell)! - Add `watchFragment` method to the cache and expose it on ApolloClient, refactor `useFragment` using `watchFragment`.

## 3.9.11

### Patch Changes

- [#11769](https://github.com/apollographql/apollo-client/pull/11769) [`04132af`](https://github.com/apollographql/apollo-client/commit/04132af121c9b48d6e03eb733b9b91f825defbac) Thanks [@jerelmiller](https://github.com/jerelmiller)! - Fix an issue where using `skipToken` or the `skip` option with `useSuspenseQuery` in React's strict mode would perform a network request.

## 3.9.10

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const react17TestFileIgnoreList = [
ignoreTSFiles,
// We only support Suspense with React 18, so don't test suspense hooks with
// React 17
"src/testing/core/__tests__/createProxiedSchema.test.tsx",
"src/testing/core/__tests__/createTestSchema.test.tsx",
"src/react/hooks/__tests__/useSuspenseQuery.test.tsx",
"src/react/hooks/__tests__/useBackgroundQuery.test.tsx",
"src/react/hooks/__tests__/useLoadableQuery.test.tsx",
Expand Down
8 changes: 4 additions & 4 deletions src/__tests__/__snapshots__/exports.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,9 @@ Array [
"MockSubscriptionLink",
"MockedProvider",
"createMockClient",
"createMockFetch",
"createMockSchema",
"createProxiedSchema",
"createSchemaFetch",
"createTestSchema",
"itAsync",
"mockObservableLink",
"mockSingleLink",
Expand All @@ -389,8 +389,8 @@ Array [
"MockLink",
"MockSubscriptionLink",
"createMockClient",
"createMockFetch",
"createProxiedSchema",
"createSchemaFetch",
"createTestSchema",
"itAsync",
"mockObservableLink",
"mockSingleLink",
Expand Down
186 changes: 186 additions & 0 deletions src/react/hooks/__tests__/useBackgroundQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2100,6 +2100,192 @@ it("does not make network requests when `skipToken` is used", async () => {
}
});

it("does not make network requests when `skipToken` is used in strict mode", async () => {
const { query, mocks } = setupSimpleCase();
const Profiler = createDefaultProfiler<SimpleCaseData>();
const { SuspenseFallback, ReadQueryHook } =
createDefaultTrackedComponents(Profiler);
const user = userEvent.setup();

let fetchCount = 0;

const link = new ApolloLink((operation) => {
return new Observable((observer) => {
fetchCount++;

const mock = mocks.find(({ request }) =>
equal(request.query, operation.query)
);

if (!mock) {
throw new Error("Could not find mock for operation");
}

observer.next((mock as any).result);
observer.complete();
});
});

const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});

function App() {
useTrackRenders();
const [skip, setSkip] = React.useState(true);
const [queryRef] = useBackgroundQuery(query, skip ? skipToken : undefined);

return (
<>
<button onClick={() => setSkip((skip) => !skip)}>Toggle skip</button>
<Suspense fallback={<SuspenseFallback />}>
{queryRef && <ReadQueryHook queryRef={queryRef} />}
</Suspense>
</>
);
}

renderWithClient(<App />, {
client,
wrapper: ({ children }) => (
<React.StrictMode>
<Profiler>{children}</Profiler>
</React.StrictMode>
),
});

// initial skipped result
await Profiler.takeRender();
expect(fetchCount).toBe(0);

// Toggle skip to `false`
await act(() => user.click(screen.getByText("Toggle skip")));
await Profiler.takeRender();

{
const { snapshot } = await Profiler.takeRender();

expect(snapshot.result).toEqual({
data: { greeting: "Hello" },
error: undefined,
networkStatus: NetworkStatus.ready,
});
}

expect(fetchCount).toBe(1);

// Toggle skip to `true`
await act(() => user.click(screen.getByText("Toggle skip")));

{
const { snapshot } = await Profiler.takeRender();

expect(snapshot.result).toEqual({
data: { greeting: "Hello" },
error: undefined,
networkStatus: NetworkStatus.ready,
});
}

expect(fetchCount).toBe(1);

await expect(Profiler).not.toRerender();
});

it("does not make network requests when using `skip` option in strict mode", async () => {
const { query, mocks } = setupSimpleCase();
const Profiler = createDefaultProfiler<SimpleCaseData>();
const { SuspenseFallback, ReadQueryHook } =
createDefaultTrackedComponents(Profiler);
const user = userEvent.setup();

let fetchCount = 0;

const link = new ApolloLink((operation) => {
return new Observable((observer) => {
fetchCount++;

const mock = mocks.find(({ request }) =>
equal(request.query, operation.query)
);

if (!mock) {
throw new Error("Could not find mock for operation");
}

observer.next((mock as any).result);
observer.complete();
});
});

const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});

function App() {
useTrackRenders();
const [skip, setSkip] = React.useState(true);
const [queryRef] = useBackgroundQuery(query, { skip });

return (
<>
<button onClick={() => setSkip((skip) => !skip)}>Toggle skip</button>
<Suspense fallback={<SuspenseFallback />}>
{queryRef && <ReadQueryHook queryRef={queryRef} />}
</Suspense>
</>
);
}

renderWithClient(<App />, {
client,
wrapper: ({ children }) => (
<React.StrictMode>
<Profiler>{children}</Profiler>
</React.StrictMode>
),
});

// initial skipped result
await Profiler.takeRender();
expect(fetchCount).toBe(0);

// Toggle skip to `false`
await act(() => user.click(screen.getByText("Toggle skip")));
await Profiler.takeRender();

{
const { snapshot } = await Profiler.takeRender();

expect(snapshot.result).toEqual({
data: { greeting: "Hello" },
error: undefined,
networkStatus: NetworkStatus.ready,
});
}

expect(fetchCount).toBe(1);

// Toggle skip to `true`
await act(() => user.click(screen.getByText("Toggle skip")));

{
const { snapshot } = await Profiler.takeRender();

expect(snapshot.result).toEqual({
data: { greeting: "Hello" },
error: undefined,
networkStatus: NetworkStatus.ready,
});
}

expect(fetchCount).toBe(1);

await expect(Profiler).not.toRerender();
});

it("result is referentially stable", async () => {
const { query, mocks } = setupSimpleCase();

Expand Down
94 changes: 94 additions & 0 deletions src/react/hooks/__tests__/useSuspenseQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5560,6 +5560,100 @@ describe("useSuspenseQuery", () => {
expect(fetchCount).toBe(1);
});

// https://github.com/apollographql/apollo-client/issues/11768
it("does not make network requests when using `skipToken` with strict mode", async () => {
const { query, mocks } = useVariablesQueryCase();

let fetchCount = 0;

const link = new ApolloLink((operation) => {
return new Observable((observer) => {
fetchCount++;

const mock = mocks.find(({ request }) =>
equal(request.variables, operation.variables)
);

if (!mock) {
throw new Error("Could not find mock for operation");
}

observer.next(mock.result);
observer.complete();
});
});

const { result, rerender } = renderSuspenseHook(
({ skip, id }) =>
useSuspenseQuery(query, skip ? skipToken : { variables: { id } }),
{ mocks, link, strictMode: true, initialProps: { skip: true, id: "1" } }
);

expect(fetchCount).toBe(0);

rerender({ skip: false, id: "1" });

expect(fetchCount).toBe(1);

await waitFor(() => {
expect(result.current).toMatchObject({
...mocks[0].result,
networkStatus: NetworkStatus.ready,
error: undefined,
});
});

rerender({ skip: true, id: "2" });

expect(fetchCount).toBe(1);
});

it("does not make network requests when using `skip` with strict mode", async () => {
const { query, mocks } = useVariablesQueryCase();

let fetchCount = 0;

const link = new ApolloLink((operation) => {
return new Observable((observer) => {
fetchCount++;

const mock = mocks.find(({ request }) =>
equal(request.variables, operation.variables)
);

if (!mock) {
throw new Error("Could not find mock for operation");
}

observer.next(mock.result);
observer.complete();
});
});

const { result, rerender } = renderSuspenseHook(
({ skip, id }) => useSuspenseQuery(query, { skip, variables: { id } }),
{ mocks, link, strictMode: true, initialProps: { skip: true, id: "1" } }
);

expect(fetchCount).toBe(0);

rerender({ skip: false, id: "1" });

expect(fetchCount).toBe(1);

await waitFor(() => {
expect(result.current).toMatchObject({
...mocks[0].result,
networkStatus: NetworkStatus.ready,
error: undefined,
});
});

rerender({ skip: true, id: "2" });

expect(fetchCount).toBe(1);
});

it("`skip` result is referentially stable", async () => {
const { query, mocks } = useSimpleQueryCase();

Expand Down
Loading

0 comments on commit cd40f9c

Please sign in to comment.