Skip to content

Commit 243c538

Browse files
phryneasalessbell
andauthored
suggestion: mock global fetch explicitly (#11779)
* feat: accept min and max delay in createSchemaFetch * chore: add snapshot of invariant error and add tests * chore: update api reports and .size-limits.json * suggestion: mock global `fetch` explicitly * chore: update tests * chore: extract api * chore: update .size-limits.json * Clean up Prettier, Size-limit, and Api-Extractor --------- Co-authored-by: Alessia Bellisario <[email protected]> Co-authored-by: alessbell <[email protected]>
1 parent 5dfc79f commit 243c538

File tree

6 files changed

+271
-27
lines changed

6 files changed

+271
-27
lines changed

.api-reports/api-report-testing.md

+10-5
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,16 @@ export function createMockClient<TData>(data: TData, query: DocumentNode, variab
451451

452452
// @alpha
453453
export const createSchemaFetch: (schema: GraphQLSchema, mockFetchOpts?: {
454-
validate: boolean;
455-
}) => {
456-
mock: (uri: any, options: any) => Promise<Response>;
457-
restore: () => void;
458-
} & Disposable;
454+
validate?: boolean;
455+
delay?: {
456+
min: number;
457+
max: number;
458+
};
459+
}) => ((uri: any, options: any) => Promise<Response>) & {
460+
mockGlobal: () => {
461+
restore: () => void;
462+
} & Disposable;
463+
};
459464

460465
// Warning: (ae-forgotten-export) The symbol "TestSchemaOptions" needs to be exported by the entry point index.d.ts
461466
// Warning: (ae-forgotten-export) The symbol "ProxiedSchema" needs to be exported by the entry point index.d.ts

.api-reports/api-report-testing_core.md

+10-5
Original file line numberDiff line numberDiff line change
@@ -450,11 +450,16 @@ export function createMockClient<TData>(data: TData, query: DocumentNode, variab
450450

451451
// @alpha
452452
export const createSchemaFetch: (schema: GraphQLSchema, mockFetchOpts?: {
453-
validate: boolean;
454-
}) => {
455-
mock: (uri: any, options: any) => Promise<Response>;
456-
restore: () => void;
457-
} & Disposable;
453+
validate?: boolean;
454+
delay?: {
455+
min: number;
456+
max: number;
457+
};
458+
}) => ((uri: any, options: any) => Promise<Response>) & {
459+
mockGlobal: () => {
460+
restore: () => void;
461+
} & Disposable;
462+
};
458463

459464
// Warning: (ae-forgotten-export) The symbol "TestSchemaOptions" needs to be exported by the entry point index.d.ts
460465
// Warning: (ae-forgotten-export) The symbol "ProxiedSchema" needs to be exported by the entry point index.d.ts

.size-limits.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"dist/apollo-client.min.cjs": 39538,
3-
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32809
2+
"dist/apollo-client.min.cjs": 39539,
3+
"import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 32810
44
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`schema proxy should call invariant.error if min delay is greater than max delay 1`] = `
4+
[MockFunction] {
5+
"calls": Array [
6+
Array [
7+
"Please configure a minimum delay that is less than the maximum delay.",
8+
],
9+
],
10+
"results": Array [
11+
Object {
12+
"type": "return",
13+
"value": undefined,
14+
},
15+
],
16+
}
17+
`;

src/testing/core/__tests__/createTestSchema.test.tsx

+196-8
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ describe("schema proxy", () => {
173173
it("mocks scalars and resolvers", async () => {
174174
const Profiler = createDefaultProfiler<ViewerQueryData>();
175175

176-
using _fetch = createSchemaFetch(schema);
176+
using _fetch = createSchemaFetch(schema).mockGlobal();
177177

178178
const client = new ApolloClient({
179179
cache: new InMemoryCache(),
@@ -266,7 +266,7 @@ describe("schema proxy", () => {
266266

267267
const Profiler = createDefaultProfiler<ViewerQueryData>();
268268

269-
using _fetch = createSchemaFetch(forkedSchema);
269+
using _fetch = createSchemaFetch(forkedSchema).mockGlobal();
270270

271271
const client = new ApolloClient({
272272
cache: new InMemoryCache(),
@@ -349,7 +349,7 @@ describe("schema proxy", () => {
349349
it("does not pollute the original schema", async () => {
350350
const Profiler = createDefaultProfiler<ViewerQueryData>();
351351

352-
using _fetch = createSchemaFetch(schema);
352+
using _fetch = createSchemaFetch(schema).mockGlobal();
353353

354354
const client = new ApolloClient({
355355
cache: new InMemoryCache(),
@@ -444,7 +444,7 @@ describe("schema proxy", () => {
444444

445445
const Profiler = createDefaultProfiler<ViewerQueryData>();
446446

447-
using _fetch = createSchemaFetch(forkedSchema);
447+
using _fetch = createSchemaFetch(forkedSchema).mockGlobal();
448448

449449
const client = new ApolloClient({
450450
cache: new InMemoryCache(),
@@ -566,7 +566,7 @@ describe("schema proxy", () => {
566566

567567
const Profiler = createDefaultProfiler<ViewerQueryData>();
568568

569-
using _fetch = createSchemaFetch(forkedSchema);
569+
using _fetch = createSchemaFetch(forkedSchema).mockGlobal();
570570

571571
const client = new ApolloClient({
572572
cache: new InMemoryCache(),
@@ -709,7 +709,7 @@ describe("schema proxy", () => {
709709

710710
const { ErrorBoundary } = createTrackedErrorComponents(Profiler);
711711

712-
using _fetch = createSchemaFetch(forkedSchema);
712+
using _fetch = createSchemaFetch(forkedSchema).mockGlobal();
713713

714714
const client = new ApolloClient({
715715
cache: new InMemoryCache(),
@@ -789,7 +789,7 @@ describe("schema proxy", () => {
789789
const { ErrorBoundary } = createTrackedErrorComponents(Profiler);
790790

791791
// @ts-expect-error - we're intentionally passing an invalid schema
792-
using _fetch = createSchemaFetch(forkedSchema);
792+
using _fetch = createSchemaFetch(forkedSchema).mockGlobal();
793793

794794
const client = new ApolloClient({
795795
cache: new InMemoryCache(),
@@ -916,7 +916,7 @@ describe("schema proxy", () => {
916916

917917
const Profiler = createDefaultProfiler<ViewerQueryData>();
918918

919-
using _fetch = createSchemaFetch(forkedSchema);
919+
using _fetch = createSchemaFetch(forkedSchema).mockGlobal();
920920

921921
const client = new ApolloClient({
922922
cache: new InMemoryCache(),
@@ -1036,4 +1036,192 @@ describe("schema proxy", () => {
10361036

10371037
unmount();
10381038
});
1039+
1040+
it("createSchemaFetch respects min and max delay", async () => {
1041+
const Profiler = createDefaultProfiler<ViewerQueryData>();
1042+
1043+
const maxDelay = 2000;
1044+
1045+
using _fetch = createSchemaFetch(schema, {
1046+
delay: { min: 10, max: maxDelay },
1047+
}).mockGlobal();
1048+
1049+
const client = new ApolloClient({
1050+
cache: new InMemoryCache(),
1051+
uri,
1052+
});
1053+
1054+
const query: TypedDocumentNode<ViewerQueryData> = gql`
1055+
query {
1056+
viewer {
1057+
id
1058+
name
1059+
age
1060+
book {
1061+
id
1062+
title
1063+
publishedAt
1064+
}
1065+
}
1066+
}
1067+
`;
1068+
1069+
const Fallback = () => {
1070+
useTrackRenders();
1071+
return <div>Loading...</div>;
1072+
};
1073+
1074+
const App = () => {
1075+
return (
1076+
<React.Suspense fallback={<Fallback />}>
1077+
<Child />
1078+
</React.Suspense>
1079+
);
1080+
};
1081+
1082+
const Child = () => {
1083+
const result = useSuspenseQuery(query);
1084+
1085+
useTrackRenders();
1086+
1087+
Profiler.mergeSnapshot({
1088+
result,
1089+
} as Partial<{}>);
1090+
1091+
return <div>Hello</div>;
1092+
};
1093+
1094+
const { unmount, rerender } = renderWithClient(<App />, {
1095+
client,
1096+
wrapper: Profiler,
1097+
});
1098+
1099+
// initial suspended render
1100+
await Profiler.takeRender();
1101+
1102+
{
1103+
try {
1104+
const { snapshot: _snapshot } = await Profiler.takeRender();
1105+
} catch (e) {
1106+
// default timeout is 1000, so this throws
1107+
if (e instanceof Error) {
1108+
expect(e.message).toMatch(
1109+
/Exceeded timeout waiting for next render./
1110+
);
1111+
}
1112+
}
1113+
}
1114+
1115+
rerender(<App />);
1116+
1117+
// suspended render
1118+
await Profiler.takeRender();
1119+
1120+
{
1121+
// with a timeout > maxDelay, this passes
1122+
const { snapshot } = await Profiler.takeRender({
1123+
timeout: maxDelay + 100,
1124+
});
1125+
1126+
expect(snapshot.result?.data).toEqual({
1127+
viewer: {
1128+
__typename: "User",
1129+
age: 42,
1130+
id: "1",
1131+
name: "Jane Doe",
1132+
book: {
1133+
__typename: "TextBook",
1134+
id: "1",
1135+
publishedAt: "2024-01-01",
1136+
title: "The Book",
1137+
},
1138+
},
1139+
});
1140+
}
1141+
1142+
unmount();
1143+
});
1144+
1145+
it("should call invariant.error if min delay is greater than max delay", async () => {
1146+
using _consoleSpy = spyOnConsole.takeSnapshots("error");
1147+
const Profiler = createDefaultProfiler<ViewerQueryData>();
1148+
1149+
using _fetch = createSchemaFetch(schema, {
1150+
delay: { min: 3000, max: 1000 },
1151+
}).mockGlobal();
1152+
1153+
const client = new ApolloClient({
1154+
cache: new InMemoryCache(),
1155+
uri,
1156+
});
1157+
1158+
const query: TypedDocumentNode<ViewerQueryData> = gql`
1159+
query {
1160+
viewer {
1161+
id
1162+
name
1163+
age
1164+
book {
1165+
id
1166+
title
1167+
publishedAt
1168+
}
1169+
}
1170+
}
1171+
`;
1172+
1173+
const Fallback = () => {
1174+
useTrackRenders();
1175+
return <div>Loading...</div>;
1176+
};
1177+
1178+
const App = () => {
1179+
return (
1180+
<React.Suspense fallback={<Fallback />}>
1181+
<Child />
1182+
</React.Suspense>
1183+
);
1184+
};
1185+
1186+
const Child = () => {
1187+
const result = useSuspenseQuery(query);
1188+
1189+
useTrackRenders();
1190+
1191+
Profiler.mergeSnapshot({
1192+
result,
1193+
} as Partial<{}>);
1194+
1195+
return <div>Hello</div>;
1196+
};
1197+
1198+
const { unmount } = renderWithClient(<App />, {
1199+
client,
1200+
wrapper: Profiler,
1201+
});
1202+
1203+
// suspended render
1204+
await Profiler.takeRender();
1205+
1206+
{
1207+
const { snapshot } = await Profiler.takeRender();
1208+
1209+
expect(snapshot.result?.data).toEqual({
1210+
viewer: {
1211+
__typename: "User",
1212+
age: 42,
1213+
id: "1",
1214+
name: "Jane Doe",
1215+
book: {
1216+
__typename: "TextBook",
1217+
id: "1",
1218+
publishedAt: "2024-01-01",
1219+
title: "The Book",
1220+
},
1221+
},
1222+
});
1223+
}
1224+
1225+
unmount();
1226+
});
10391227
});

0 commit comments

Comments
 (0)