Skip to content

Commit 7ce75e0

Browse files
committed
Write useSuspenseQuery and useMutation tests
1 parent d6a1198 commit 7ce75e0

File tree

4 files changed

+145
-27
lines changed

4 files changed

+145
-27
lines changed

packages/openapi-react-query/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"openapi-typescript-codegen": "^0.25.0",
7777
"openapi-typescript-fetch": "^2.0.0",
7878
"react": "^18.3.1",
79+
"react-error-boundary": "^4.0.13",
7980
"typescript": "^5.4.5",
8081
"vitest": "^1.6.0"
8182
},

packages/openapi-react-query/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export default function createClient<Paths extends {}, Media extends MediaType =
9191
}
9292
return data;
9393
},
94+
...options,
9495
});
9596
},
9697
useMutation: (method, path, options) => {

packages/openapi-react-query/test/index.test.tsx

+130-27
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { server, baseUrl, useMockRequestHandler } from "./fixtures/mock-server.j
33
import type { paths } from "./fixtures/api.js";
44
import createClient from "../src/index.js";
55
import createFetchClient from "openapi-fetch";
6-
import { renderHook, waitFor } from "@testing-library/react";
7-
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
8-
import React, { type ReactNode } from "react";
6+
import { act, render, renderHook, screen, waitFor } from "@testing-library/react";
7+
import { QueryClient, QueryClientProvider, QueryErrorResetBoundary } from "@tanstack/react-query";
8+
import React, { Suspense, type ReactNode } from "react";
9+
import { ErrorBoundary } from "react-error-boundary";
910

1011
const queryClient = new QueryClient({
1112
defaultOptions: {
@@ -70,7 +71,7 @@ describe("client", () => {
7071
expect(error).toBeNull();
7172
});
7273

73-
it("should resolve error properlly and have undefined data when failed request", async () => {
74+
it("should resolve error properly and have undefined data when failed request", async () => {
7475
const fetchClient = createFetchClient<paths>({ baseUrl });
7576
const client = createClient(fetchClient);
7677

@@ -109,7 +110,7 @@ describe("client", () => {
109110
});
110111

111112
describe("params", () => {
112-
it("typechecks", async () => {
113+
it("should be required if OpenAPI schema requires params", async () => {
113114
const fetchClient = createFetchClient<paths>({ baseUrl });
114115
const client = createClient(fetchClient);
115116

@@ -132,56 +133,158 @@ describe("client", () => {
132133
});
133134

134135
describe("useSuspenseQuery", () => {
135-
it("should work", async () => {
136+
it("should resolve data properly and have error as null when successfull request", async () => {
136137
const fetchClient = createFetchClient<paths>({ baseUrl });
137138
const client = createClient(fetchClient);
138139

139140
useMockRequestHandler({
140141
baseUrl,
141142
method: "get",
142-
path: "/self",
143+
path: "/string-array",
143144
status: 200,
144-
body: { message: "OK" },
145+
body: ["one", "two", "three"],
145146
});
146147

147-
const { result } = renderHook(() => client.useSuspenseQuery("get", "/self"), {
148+
const { result } = renderHook(() => client.useSuspenseQuery("get", "/string-array"), {
148149
wrapper,
149150
});
150151

151-
await waitFor(() => expect(result.current.isSuccess).toBe(true));
152+
await waitFor(() => expect(result.current.isFetching).toBe(false));
152153

153-
expect(result.current.data).toEqual({ message: "OK" });
154+
const { data, error } = result.current;
155+
156+
expect(data[0]).toBe("one");
157+
expect(error).toBeNull();
154158
});
155-
});
156159

157-
describe("useMutation", () => {
158-
it("should work", async () => {
160+
it("should properly propagate error to suspense with a failed http request", async () => {
159161
const fetchClient = createFetchClient<paths>({ baseUrl });
160162
const client = createClient(fetchClient);
163+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => { }); // to avoid sending errors to console
161164

162165
useMockRequestHandler({
163166
baseUrl,
164167
method: "get",
165-
path: "/tag/*",
166-
status: 200,
167-
body: { message: "OK" },
168+
path: "/string-array",
169+
status: 500,
170+
body: { code: 500, message: "Something went wrong" },
168171
});
169172

170-
const { result } = renderHook(() => client.useMutation("get", "/tag/{name}"), {
171-
wrapper,
173+
const TestComponent = () => {
174+
client.useSuspenseQuery("get", "/string-array");
175+
return <div />;
176+
};
177+
178+
render(
179+
<QueryClientProvider client={queryClient}>
180+
<ErrorBoundary fallbackRender={({ error }) => <p>{error.message}</p>}>
181+
<Suspense fallback={<p>loading</p>}>
182+
<TestComponent />
183+
</Suspense>
184+
</ErrorBoundary>
185+
</QueryClientProvider>,
186+
);
187+
188+
expect(await screen.findByText("Something went wrong")).toBeDefined();
189+
errorSpy.mockRestore();
190+
});
191+
});
192+
193+
describe("useMutation", () => {
194+
describe("mutate", () => {
195+
it("should resolve data properly and have error as null when successfull request", async () => {
196+
const fetchClient = createFetchClient<paths>({ baseUrl });
197+
const client = createClient(fetchClient);
198+
199+
useMockRequestHandler({
200+
baseUrl,
201+
method: "put",
202+
path: "/comment",
203+
status: 200,
204+
body: { message: "Hello" },
205+
});
206+
207+
const { result } = renderHook(() => client.useMutation("put", "/comment"), {
208+
wrapper,
209+
});
210+
211+
result.current.mutate({ body: { message: "Hello", replied_at: 0 } });
212+
213+
await waitFor(() => expect(result.current.isPending).toBe(false));
214+
215+
const { data, error } = result.current;
216+
217+
expect(data?.message).toBe("Hello");
218+
expect(error).toBeNull();
172219
});
173220

174-
result.current.mutate({
175-
params: {
176-
path: {
177-
name: "test",
178-
},
179-
},
221+
it("should resolve error properly and have undefined data when failed request", async () => {
222+
const fetchClient = createFetchClient<paths>({ baseUrl });
223+
const client = createClient(fetchClient);
224+
225+
useMockRequestHandler({
226+
baseUrl,
227+
method: "put",
228+
path: "/comment",
229+
status: 500,
230+
body: { code: 500, message: "Something went wrong" },
231+
});
232+
233+
const { result } = renderHook(() => client.useMutation("put", "/comment"), {
234+
wrapper,
235+
});
236+
237+
result.current.mutate({ body: { message: "Hello", replied_at: 0 } });
238+
239+
await waitFor(() => expect(result.current.isPending).toBe(false));
240+
241+
const { data, error } = result.current;
242+
243+
expect(data).toBeUndefined();
244+
expect(error?.message).toBe("Something went wrong");
180245
});
246+
});
247+
248+
describe("mutateAsync", () => {
249+
it("should resolve data properly", async () => {
250+
const fetchClient = createFetchClient<paths>({ baseUrl });
251+
const client = createClient(fetchClient);
181252

182-
await waitFor(() => expect(result.current.isSuccess).toBe(true));
253+
useMockRequestHandler({
254+
baseUrl,
255+
method: "put",
256+
path: "/comment",
257+
status: 200,
258+
body: { message: "Hello" },
259+
});
183260

184-
expect(result.current.data).toEqual({ message: "OK" });
261+
const { result } = renderHook(() => client.useMutation("put", "/comment"), {
262+
wrapper,
263+
});
264+
265+
const data = await result.current.mutateAsync({ body: { message: "Hello", replied_at: 0 } });
266+
267+
expect(data.message).toBe("Hello");
268+
});
269+
270+
it("should throw an error when failed request", async () => {
271+
const fetchClient = createFetchClient<paths>({ baseUrl });
272+
const client = createClient(fetchClient);
273+
274+
useMockRequestHandler({
275+
baseUrl,
276+
method: "put",
277+
path: "/comment",
278+
status: 500,
279+
body: { code: 500, message: "Something went wrong" },
280+
});
281+
282+
const { result } = renderHook(() => client.useMutation("put", "/comment"), {
283+
wrapper,
284+
});
285+
286+
expect(result.current.mutateAsync({ body: { message: "Hello", replied_at: 0 } })).rejects.toThrow();
287+
});
185288
});
186289
});
187290
});

pnpm-lock.yaml

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)