Skip to content

Commit ae0dcad

Browse files
authored
Add support for callback on delay and default to random delay (#12532)
1 parent 7784b46 commit ae0dcad

20 files changed

+756
-77
lines changed

Diff for: .api-reports/api-report-testing.api.md

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { MockLinkOptions } from '@apollo/client/testing/core';
1111
import { mockObservableLink } from '@apollo/client/testing/core';
1212
import { mockSingleLink } from '@apollo/client/testing/core';
1313
import { MockSubscriptionLink } from '@apollo/client/testing/core';
14+
import { realisticDelay } from '@apollo/client/testing/core';
1415
import { ResultFunction } from '@apollo/client/testing/core';
1516
import { tick } from '@apollo/client/testing/core';
1617
import { wait } from '@apollo/client/testing/core';
@@ -32,6 +33,8 @@ export { mockSingleLink }
3233

3334
export { MockSubscriptionLink }
3435

36+
export { realisticDelay }
37+
3538
export { ResultFunction }
3639

3740
export { tick }

Diff for: .api-reports/api-report-testing_core.api.md

+24-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export interface MockedRequest<TVariables = Record<string, any>> {
3939
// @public (undocumented)
4040
export interface MockedResponse<out TData = Record<string, any>, out TVariables = Record<string, any>> {
4141
// (undocumented)
42-
delay?: number;
42+
delay?: number | MockLink.DelayFunction;
4343
// (undocumented)
4444
error?: Error;
4545
// (undocumented)
@@ -60,12 +60,27 @@ interface MockedSubscriptionResult {
6060
result?: FetchResult;
6161
}
6262

63+
// @public (undocumented)
64+
export namespace MockLink {
65+
// (undocumented)
66+
export interface DefaultOptions {
67+
// (undocumented)
68+
delay?: MockLink.Delay;
69+
}
70+
// (undocumented)
71+
export type Delay = number | DelayFunction;
72+
// (undocumented)
73+
export type DelayFunction = (operation: Operation) => number;
74+
}
75+
6376
// @public (undocumented)
6477
export class MockLink extends ApolloLink {
6578
constructor(mockedResponses: ReadonlyArray<MockedResponse<Record<string, any>, Record<string, any>>>, options?: MockLinkOptions);
6679
// (undocumented)
6780
addMockedResponse(mockedResponse: MockedResponse): void;
6881
// (undocumented)
82+
static defaultOptions: MockLink.DefaultOptions;
83+
// (undocumented)
6984
operation: Operation;
7085
// (undocumented)
7186
request(operation: Operation): Observable<FetchResult> | null;
@@ -75,6 +90,8 @@ export class MockLink extends ApolloLink {
7590

7691
// @public (undocumented)
7792
export interface MockLinkOptions {
93+
// (undocumented)
94+
defaultOptions?: MockLink.DefaultOptions;
7895
// (undocumented)
7996
showWarnings?: boolean;
8097
}
@@ -110,6 +127,12 @@ export class MockSubscriptionLink extends ApolloLink {
110127
unsubscribers: any[];
111128
}
112129

130+
// @public (undocumented)
131+
export function realisticDelay({ min, max, }?: {
132+
min?: number;
133+
max?: number;
134+
}): MockLink.DelayFunction;
135+
113136
// Warning: (ae-forgotten-export) The symbol "CovariantUnaryFunction" needs to be exported by the entry point index.d.ts
114137
//
115138
// @public (undocumented)

Diff for: .api-reports/api-report-testing_internal.api.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type { MaskedDocumentNode } from '@apollo/client/masking';
1515
import type { MockedProviderProps } from '@apollo/client/testing/react';
1616
import { MockedRequest } from '@apollo/client/testing/core';
1717
import type { MockedResponse } from '@apollo/client/testing/core';
18+
import { MockLink } from '@apollo/client/testing/core';
1819
import type { Observable } from 'rxjs';
1920
import type { Queries } from '@testing-library/dom';
2021
import type { queries } from '@testing-library/dom';
@@ -34,11 +35,11 @@ export function actAsync<T>(scope: () => T | Promise<T>): Promise<T>;
3435

3536
// @public (undocumented)
3637
export function addDelayToMocks<T extends MockedResponse<unknown>[]>(mocks: T, delay?: number, override?: boolean): {
37-
delay: number;
3838
request: MockedRequest<Record<string, any>>;
3939
maxUsageCount?: number;
4040
result?: FetchResult<unknown> | ResultFunction<FetchResult<unknown>, Record<string, any>> | undefined;
4141
error?: Error;
42+
delay: number | MockLink.DelayFunction;
4243
}[];
4344

4445
// @public (undocumented)

Diff for: .api-reports/api-report-testing_react.api.md

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ApolloClient } from '@apollo/client';
99
import type { ApolloLink } from '@apollo/client/link/core';
1010
import type { DefaultOptions } from '@apollo/client';
1111
import type { MockedResponse } from '@apollo/client/testing/core';
12+
import { MockLink } from '@apollo/client/testing/core';
1213
import * as React_2 from 'react';
1314
import type { Resolvers } from '@apollo/client';
1415

@@ -37,6 +38,8 @@ export interface MockedProviderProps {
3738
// (undocumented)
3839
link?: ApolloLink;
3940
// (undocumented)
41+
mockLinkDefaultOptions?: MockLink.DefaultOptions;
42+
// (undocumented)
4043
mocks?: ReadonlyArray<MockedResponse<any, any>>;
4144
// (undocumented)
4245
resolvers?: Resolvers;

Diff for: .changeset/afraid-moons-arrive.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/client": minor
3+
---
4+
5+
Allow mocked responses passed to `MockLink` to accept a callback for the `delay` option. The `delay` callback will be given the current operation which can be used to determine what delay should be used for the mock.

Diff for: .changeset/early-eggs-develop.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
"@apollo/client": minor
3+
---
4+
5+
Introduce a new `realisticDelay` helper function for use with the `delay` callback for mocked responses used with `MockLink`. `realisticDelay` will generate a random value between 20 and 50ms to provide an experience closer to unpredictable network latency. `realisticDelay` can be configured with a `min` and `max` to set different thresholds if the defaults are not sufficient.
6+
7+
```ts
8+
import { realisticDelay } from '@apollo/client/testing';
9+
10+
new MockLink([
11+
{
12+
request: { query },
13+
result: { data: { greeting: 'Hello' }},
14+
delay: realisticDelay()
15+
},
16+
{
17+
request: { query },
18+
result: { data: { greeting: 'Hello' }},
19+
delay: realisticDelay({ min: 10, max: 100 })
20+
},
21+
]);
22+
```

Diff for: .changeset/shaggy-pugs-add.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
"@apollo/client": minor
3+
---
4+
5+
Add ability to specify a default `delay` for all mocked responses passed to `MockLink`. This `delay` can be configured globally (all instances of `MockLink` will use the global defaults), or per-instance (all mocks in a single instance will use the defaults). A `delay` defined on a single mock will supercede all default delays. Per-instance defaults supercede global defaults.
6+
7+
**Global defaults**
8+
9+
```ts
10+
MockLink.defaultOptions = {
11+
// Use a default delay of 20ms for all mocks in all instances without a specified delay
12+
delay: 20,
13+
14+
// altenatively use a callback which will be executed for each mock
15+
delay: () => getRandomNumber(),
16+
17+
// or use the built-in `realisticDelay`. This is the default
18+
delay: realisticDelay(),
19+
}
20+
```
21+
22+
**Per-instance defaults**
23+
24+
```ts
25+
new MockLink(
26+
[
27+
// Use the default delay
28+
{
29+
request: { query },
30+
result: { data: { greeting: 'Hello' }},
31+
},
32+
{
33+
request: { query },
34+
result: { data: { greeting: 'Hello' }},
35+
// Override the default for this mock
36+
delay: 10
37+
},
38+
],
39+
{
40+
defaultOptions: {
41+
// Use a default delay of 20ms for all mocks without a specified delay
42+
delay: 20,
43+
44+
// altenatively use a callback which will be executed for each mock
45+
delay: () => getRandomNumber(),
46+
47+
// or use the built-in `realisticDelay`. This is the default
48+
delay: realisticDelay(),
49+
}
50+
}
51+
);
52+
```

Diff for: .changeset/swift-rivers-share.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@apollo/client": major
3+
---
4+
5+
Default the `delay` for all mocked responses passed to `MockLink` using `realisticDelay`. This ensures your test handles loading states by default and is not reliant on a specific timing.
6+
7+
If you would like to restore the old behavior, use a global default delay of `0`.
8+
9+
```ts
10+
MockLink.defaultOptions = {
11+
delay: 0
12+
}
13+
```

Diff for: .size-limits.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (CJS)": 42763,
3-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38290,
4-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 32814,
5-
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27796
2+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (CJS)": 42828,
3+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production) (CJS)": 38348,
4+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\"": 32795,
5+
"import { ApolloClient, InMemoryCache, HttpLink } from \"@apollo/client\" (production)": 27800
66
}

Diff for: src/__tests__/__snapshots__/exports.ts.snap

+2
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ Array [
320320
"createMockClient",
321321
"mockObservableLink",
322322
"mockSingleLink",
323+
"realisticDelay",
323324
"tick",
324325
"wait",
325326
"withErrorSpy",
@@ -335,6 +336,7 @@ Array [
335336
"createMockClient",
336337
"mockObservableLink",
337338
"mockSingleLink",
339+
"realisticDelay",
338340
"tick",
339341
"wait",
340342
"withErrorSpy",

Diff for: src/__tests__/optimistic.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,7 @@ describe("optimistic mutation results", () => {
13251325
{
13261326
request: { query },
13271327
result,
1328+
delay: 0,
13281329
},
13291330
{
13301331
request: { query: mutation },
@@ -1751,10 +1752,11 @@ describe("optimistic mutation results", () => {
17511752

17521753
it("will handle dependent updates", async () => {
17531754
expect.assertions(1);
1754-
const link = mockSingleLink(
1755+
const link = new MockLink([
17551756
{
17561757
request: { query },
17571758
result,
1759+
delay: 0,
17581760
},
17591761
{
17601762
request: { query: mutation },
@@ -1765,8 +1767,8 @@ describe("optimistic mutation results", () => {
17651767
request: { query: mutation },
17661768
result: mutationResult2,
17671769
delay: 20,
1768-
}
1769-
);
1770+
},
1771+
]);
17701772

17711773
const customOptimisticResponse1 = {
17721774
__typename: "Mutation",

Diff for: src/__tests__/subscribeToMore.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { InMemoryCache } from "@apollo/client/cache";
66
import type { Operation } from "@apollo/client/link/core";
77
import { ApolloLink } from "@apollo/client/link/core";
88
import {
9+
MockLink,
910
mockObservableLink,
1011
mockSingleLink,
1112
wait,
@@ -120,7 +121,7 @@ describe("subscribeToMore", () => {
120121

121122
it("calls error callback on error", async () => {
122123
const wSLink = mockObservableLink();
123-
const httpLink = mockSingleLink(req1);
124+
const httpLink = new MockLink([req1]);
124125
const link = ApolloLink.split(isSub, wSLink, httpLink);
125126

126127
const client = new ApolloClient({
@@ -147,17 +148,17 @@ describe("subscribeToMore", () => {
147148
onError,
148149
});
149150

150-
for (const result of results2) {
151-
wSLink.simulateResult(result);
152-
}
153-
154151
await expect(stream).toEmitTypedValue({
155152
data: { entry: { value: "1" } },
156153
loading: false,
157154
networkStatus: 7,
158155
partial: false,
159156
});
160157

158+
for (const result of results2) {
159+
wSLink.simulateResult(result);
160+
}
161+
161162
await expect(stream).toEmitTypedValue({
162163
data: { entry: { value: "Amanda Liu" } },
163164
loading: false,

Diff for: src/core/__tests__/ApolloClient/general.test.ts

+17-14
Original file line numberDiff line numberDiff line change
@@ -2869,18 +2869,21 @@ describe("ApolloClient", () => {
28692869
},
28702870
},
28712871
}),
2872-
link: new MockLink([
2873-
{
2874-
request: { query: queryWithoutId },
2875-
result: { data: dataWithoutId },
2876-
maxUsageCount: Number.POSITIVE_INFINITY,
2877-
},
2878-
{
2879-
request: { query: queryWithId },
2880-
result: { data: dataWithId },
2881-
maxUsageCount: Number.POSITIVE_INFINITY,
2882-
},
2883-
]),
2872+
link: new MockLink(
2873+
[
2874+
{
2875+
request: { query: queryWithoutId },
2876+
result: { data: dataWithoutId },
2877+
maxUsageCount: Number.POSITIVE_INFINITY,
2878+
},
2879+
{
2880+
request: { query: queryWithId },
2881+
result: { data: dataWithId },
2882+
maxUsageCount: Number.POSITIVE_INFINITY,
2883+
},
2884+
],
2885+
{ defaultOptions: { delay: 0 } }
2886+
),
28842887
});
28852888

28862889
const observableWithId = client.watchQuery({ query: queryWithId });
@@ -2996,7 +2999,7 @@ describe("ApolloClient", () => {
29962999
const client = new ApolloClient({
29973000
cache: new InMemoryCache({}),
29983001
link: new MockLink([
2999-
{ request: { query: queryA }, result: { data: dataA } },
3002+
{ request: { query: queryA }, result: { data: dataA }, delay: 10 },
30003003
{ request: { query: queryB }, result: { data: dataB }, delay: 20 },
30013004
]),
30023005
ssrMode: true,
@@ -6085,7 +6088,7 @@ describe("ApolloClient", () => {
60856088

60866089
await expect(stream).toEmitNext();
60876090

6088-
void client.mutate({
6091+
await client.mutate({
60896092
mutation,
60906093
refetchQueries: [
60916094
{

0 commit comments

Comments
 (0)