Skip to content

Commit 0f8be5c

Browse files
committed
chore: useMutation tests
1 parent c9aef13 commit 0f8be5c

File tree

2 files changed

+272
-2
lines changed

2 files changed

+272
-2
lines changed

tests/useMutation.test.ts

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
import { QueryClient, setLogger } from "react-query/core";
2+
3+
import { errorMutator, flushPromises, noop, successMutator } from "./utils";
4+
import { useMutation } from "../src/useMutation";
5+
6+
jest.mock("vue", () => {
7+
const vue = jest.requireActual("vue");
8+
return {
9+
...vue,
10+
onUnmounted: jest.fn(),
11+
};
12+
});
13+
14+
jest.mock("../src/useQueryClient", () => {
15+
const queryClient = new QueryClient();
16+
return {
17+
useQueryClient: jest.fn(() => queryClient),
18+
};
19+
});
20+
21+
describe("useMutation", () => {
22+
beforeAll(() => {
23+
setLogger({ log: noop, warn: noop, error: noop });
24+
});
25+
26+
beforeEach(() => {
27+
jest.clearAllMocks();
28+
});
29+
30+
test("should be in idle state initially", () => {
31+
const mutation = useMutation((params) => successMutator(params));
32+
33+
expect(mutation).toMatchObject({
34+
isIdle: true,
35+
isLoading: false,
36+
isError: false,
37+
isSuccess: false,
38+
});
39+
});
40+
41+
test("should change state after invoking mutate", () => {
42+
const result = "Mock data";
43+
const mutation = useMutation((params: string) => successMutator(params));
44+
45+
mutation.mutate(result);
46+
47+
expect(mutation).toMatchObject({
48+
isIdle: false,
49+
isLoading: true,
50+
isError: false,
51+
isSuccess: false,
52+
data: undefined,
53+
error: null,
54+
});
55+
});
56+
57+
test("should return error when request fails", async () => {
58+
const mutation = useMutation(errorMutator);
59+
60+
mutation.mutate();
61+
62+
await flushPromises(10);
63+
64+
expect(mutation).toMatchObject({
65+
isIdle: false,
66+
isLoading: false,
67+
isError: true,
68+
isSuccess: false,
69+
data: undefined,
70+
error: Error("Some error"),
71+
});
72+
});
73+
74+
test("should return data when request succeeds", async () => {
75+
const result = "Mock data";
76+
const mutation = useMutation((params: string) => successMutator(params));
77+
78+
mutation.mutate(result);
79+
80+
await flushPromises(10);
81+
82+
expect(mutation).toMatchObject({
83+
isIdle: false,
84+
isLoading: false,
85+
isError: false,
86+
isSuccess: true,
87+
data: "Mock data",
88+
error: null,
89+
});
90+
});
91+
92+
test("should reset state after invoking mutation.reset", async () => {
93+
const mutation = useMutation((params: string) => errorMutator(params));
94+
95+
mutation.mutate("");
96+
97+
await flushPromises(10);
98+
99+
mutation.reset();
100+
101+
expect(mutation).toMatchObject({
102+
isIdle: true,
103+
isLoading: false,
104+
isError: false,
105+
isSuccess: false,
106+
data: undefined,
107+
error: null,
108+
});
109+
});
110+
111+
describe("side effects", () => {
112+
beforeEach(() => {
113+
jest.clearAllMocks();
114+
});
115+
116+
test("should call onMutate when passed as an option", async () => {
117+
const onMutate = jest.fn();
118+
const mutation = useMutation((params: string) => successMutator(params), {
119+
onMutate,
120+
});
121+
122+
mutation.mutate("");
123+
124+
await flushPromises(10);
125+
126+
expect(onMutate).toHaveBeenCalledTimes(1);
127+
});
128+
129+
test("should call onError when passed as an option", async () => {
130+
const onError = jest.fn();
131+
const mutation = useMutation((params: string) => errorMutator(params), {
132+
onError,
133+
});
134+
135+
mutation.mutate("");
136+
137+
await flushPromises(10);
138+
139+
expect(onError).toHaveBeenCalledTimes(1);
140+
});
141+
142+
test("should call onSuccess when passed as an option", async () => {
143+
const onSuccess = jest.fn();
144+
const mutation = useMutation((params: string) => successMutator(params), {
145+
onSuccess,
146+
});
147+
148+
mutation.mutate("");
149+
150+
await flushPromises(10);
151+
152+
expect(onSuccess).toHaveBeenCalledTimes(1);
153+
});
154+
155+
test("should call onSettled when passed as an option", async () => {
156+
const onSettled = jest.fn();
157+
const mutation = useMutation((params: string) => successMutator(params), {
158+
onSettled,
159+
});
160+
161+
mutation.mutate("");
162+
163+
await flushPromises(10);
164+
165+
expect(onSettled).toHaveBeenCalledTimes(1);
166+
});
167+
168+
test("should call onError when passed as an argument of mutate function", async () => {
169+
const onError = jest.fn();
170+
const mutation = useMutation((params: string) => errorMutator(params));
171+
172+
mutation.mutate("", { onError });
173+
174+
await flushPromises(10);
175+
176+
expect(onError).toHaveBeenCalledTimes(1);
177+
});
178+
179+
test("should call onSuccess when passed as an argument of mutate function", async () => {
180+
const onSuccess = jest.fn();
181+
const mutation = useMutation((params: string) => successMutator(params));
182+
183+
mutation.mutate("", { onSuccess });
184+
185+
await flushPromises(10);
186+
187+
expect(onSuccess).toHaveBeenCalledTimes(1);
188+
});
189+
190+
test("should call onSettled when passed as an argument of mutate function", async () => {
191+
const onSettled = jest.fn();
192+
const mutation = useMutation((params: string) => successMutator(params));
193+
194+
mutation.mutate("", { onSettled });
195+
196+
await flushPromises(10);
197+
198+
expect(onSettled).toHaveBeenCalledTimes(1);
199+
});
200+
201+
test("should fire both onSettled functions", async () => {
202+
const onSettled = jest.fn();
203+
const onSettledOnFunction = jest.fn();
204+
const mutation = useMutation((params: string) => successMutator(params), {
205+
onSettled,
206+
});
207+
208+
mutation.mutate("", { onSettled: onSettledOnFunction });
209+
210+
await flushPromises(10);
211+
212+
expect(onSettled).toHaveBeenCalledTimes(1);
213+
expect(onSettledOnFunction).toHaveBeenCalledTimes(1);
214+
});
215+
});
216+
217+
describe("async", () => {
218+
beforeEach(() => {
219+
jest.clearAllMocks();
220+
});
221+
222+
test("should resolve properly", async () => {
223+
const result = "Mock data";
224+
const mutation = useMutation((params: string) => successMutator(params));
225+
226+
await expect(mutation.mutateAsync(result)).resolves.toBe(result);
227+
228+
expect(mutation).toMatchObject({
229+
isIdle: false,
230+
isLoading: false,
231+
isError: false,
232+
isSuccess: true,
233+
data: "Mock data",
234+
error: null,
235+
});
236+
});
237+
238+
test("should throw on error", async () => {
239+
const mutation = useMutation(errorMutator);
240+
241+
await expect(mutation.mutateAsync()).rejects.toThrowError("Some error");
242+
243+
expect(mutation).toMatchObject({
244+
isIdle: false,
245+
isLoading: false,
246+
isError: true,
247+
isSuccess: false,
248+
data: undefined,
249+
error: Error("Some error"),
250+
});
251+
});
252+
});
253+
});

tests/utils.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ export function flushPromises(timeout = 0): Promise<unknown> {
44
});
55
}
66

7+
export function noop(): undefined {
8+
return undefined;
9+
}
10+
711
export function simpleFetcher(): Promise<string> {
812
return new Promise((resolve) => {
913
setTimeout(() => {
@@ -32,6 +36,19 @@ export function rejectFetcher(): Promise<Error> {
3236
});
3337
}
3438

35-
export function noop(): undefined {
36-
return undefined;
39+
export function successMutator<T>(param: T): Promise<T> {
40+
return new Promise((resolve) => {
41+
setTimeout(() => {
42+
return resolve(param);
43+
}, 0);
44+
});
45+
}
46+
47+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
48+
export function errorMutator<T>(param: T): Promise<Error> {
49+
return new Promise((resolve, reject) => {
50+
setTimeout(() => {
51+
return reject(new Error("Some error"));
52+
}, 0);
53+
});
3754
}

0 commit comments

Comments
 (0)