Passed
Push — feature/data-request-hooks ( 13824f )
by Tristan
06:12
created

resources/assets/js/hooks/webResourceHooks/webResourceHooks.test.ts   A

Complexity

Total Complexity 5
Complexity/F 0

Size

Lines of Code 264
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 5
eloc 187
mnd 5
bc 5
fnc 0
dl 0
loc 264
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
rs 10
1
import fetchMock from "fetch-mock";
2
import { act, renderHook } from "@testing-library/react-hooks";
3
import {
4
  handleResponse,
5
  handleResponseWithoutData,
6
  useResource,
7
} from "./indexCrudReducer";
8
9
describe("webResourceHooks", (): void => {
10
  describe("handleResponseWithoutData", (): void => {
11
    it("should call handleError with a RequestError and then reject when a network error happens", async () => {
12
      expect.assertions(3);
13
14
      const error = new Error("Network request failed");
15
      const failedRequest: Promise<Response> = Promise.reject(error);
16
17
      const handleError = jest.fn();
18
      try {
19
        await handleResponseWithoutData(failedRequest, handleError);
20
      } catch (e) {
21
        // Test that handleResponseWithoutData rejects.
22
        expect(e).toBe(error);
23
      }
24
      // Test that handleError was called once.
25
      expect(handleError.mock.calls.length).toBe(1);
26
      // Test that handleError was called with a RequestError.
27
      expect(handleError.mock.calls[0][0]).toEqual({
28
        error,
29
        type: "RequestError",
30
      });
31
    });
32
    it("should call handleError with an ApiError including the response, and then reject, when given a non-ok response", async () => {
33
      expect.assertions(3);
34
35
      const expectedError = new Error("Not found");
36
      const badResponse = new Response(null, {
37
        status: 404,
38
        statusText: "Not found",
39
      });
40
      const badRequest: Promise<Response> = Promise.resolve(badResponse);
41
42
      const handleError = jest.fn();
43
      try {
44
        await handleResponseWithoutData(badRequest, handleError);
45
      } catch (e) {
46
        // Test that handleResponseWithoutData rejects.
47
        expect(e).toEqual(expectedError);
48
      }
49
      // Test that handleError was called once.
50
      expect(handleError.mock.calls.length).toBe(1);
51
      // Test that handleError was called with a RequestError.
52
      expect(handleError.mock.calls[0][0]).toEqual({
53
        error: expectedError,
54
        response: badResponse,
55
        type: "ApiError",
56
      });
57
    });
58
    it("should resolve, and not call handleError, when given a 200 response with no body.", async () => {
59
      const response = new Response(null, { status: 200 });
60
      const request: Promise<Response> = Promise.resolve(response);
61
      const handleError = jest.fn();
62
      await handleResponseWithoutData(request, handleError);
63
      // Test that handleError was called once.
64
      expect(handleError.mock.calls.length).toBe(0);
65
    });
66
  });
67
68
  describe("handleResponse", (): void => {
69
    it("should call handleError with a RequestError and then reject when a network error happens", async () => {
70
      expect.assertions(3);
71
72
      const error = new Error("Network request failed");
73
      const failedRequest: Promise<Response> = Promise.reject(error);
74
75
      const handleError = jest.fn();
76
      try {
77
        await handleResponse(failedRequest, () => {}, handleError);
78
      } catch (e) {
79
        // Test that handleResponseWithoutData rejects.
80
        expect(e).toBe(error);
81
      }
82
      // Test that handleError was called once.
83
      expect(handleError.mock.calls.length).toBe(1);
84
      // Test that handleError was called with a RequestError.
85
      expect(handleError.mock.calls[0][0]).toEqual({
86
        error,
87
        type: "RequestError",
88
      });
89
    });
90
    it("should call handleError with an ApiError including the response, and then reject, when given a non-ok response", async () => {
91
      expect.assertions(3);
92
93
      const expectedError = new Error("Not found");
94
      const badResponse = new Response(null, {
95
        status: 404,
96
        statusText: "Not found",
97
      });
98
      const badRequest: Promise<Response> = Promise.resolve(badResponse);
99
100
      const handleError = jest.fn();
101
      try {
102
        await handleResponse(badRequest, () => {}, handleError);
103
      } catch (e) {
104
        // Test that handleResponseWithoutData rejects.
105
        expect(e).toEqual(expectedError);
106
      }
107
      // Test that handleError was called once.
108
      expect(handleError.mock.calls.length).toBe(1);
109
      // Test that handleError was called with a RequestError.
110
      expect(handleError.mock.calls[0][0]).toEqual({
111
        error: expectedError,
112
        response: badResponse,
113
        type: "ApiError",
114
      });
115
    });
116
    it("should call handleError with a ParseError, and then reject, when given a 200 response but no body.", async () => {
117
      expect.assertions(3);
118
      const expectedError = new Error(
119
        "invalid json response body at  reason: Unexpected end of JSON input",
120
      );
121
      expectedError.name = "FetchError";
122
      const emptyResponse = new Response(null, { status: 200 });
123
      const emptyRequest: Promise<Response> = Promise.resolve(emptyResponse);
124
      const handleError = jest.fn();
125
      try {
126
        await handleResponse(emptyRequest, () => {}, handleError);
127
      } catch (e) {
128
        // Test that handleResponseWithoutData rejects.
129
        expect(e).toEqual(expectedError);
130
      }
131
      // Test that handleError was called once.
132
      expect(handleError.mock.calls.length).toBe(1);
133
      // Test that handleError was called with a RequestError.
134
      expect(handleError.mock.calls[0][0]).toEqual({
135
        error: expectedError,
136
        response: emptyResponse,
137
        type: "ParseError",
138
      });
139
    });
140
    it("should resolve with result of parseResponse, and not call handleError, when given a 200 response with a body.", async () => {
141
      const response = new Response(JSON.stringify({ result: 2 }));
142
      const parseResponse = (r) => ({ newResult: r.result * 2 });
143
      const expectedResult = { newResult: 4 };
144
      const request: Promise<Response> = Promise.resolve(response);
145
      const handleError = jest.fn();
146
      const result = await handleResponse(request, parseResponse, handleError);
147
      expect(handleError.mock.calls.length).toBe(0);
148
      expect(result).toEqual(expectedResult);
149
    });
150
  });
151
152
  describe("useResource hook", () => {
153
    afterEach((): void => {
154
      fetchMock.reset();
155
      fetchMock.restore();
156
    });
157
158
    const endpoint = "https://talent.test/api/test";
159
160
    it("Initially returns initialValue and a status of 'updating'", () => {
161
      fetchMock.mock(
162
        "*",
163
        {},
164
        {
165
          delay: 500,
166
        },
167
      );
168
      const initialValue = { name: "Talent Cloud", age: 3 };
169
      const parseResponse = () => ({ name: "Talent Cloud 2", age: 100 });
170
      const handleError = () => {};
171
172
      const { result } = renderHook(() =>
173
        useResource(endpoint, parseResponse, initialValue, handleError),
174
      );
175
176
      expect(result.current.value).toEqual(initialValue);
177
      expect(result.current.status).toEqual("updating");
178
    });
179
    it("After initial request completes, status changes to 'updating' when refresh() is called", async () => {
180
      fetchMock.mock("*", {});
181
      const initialValue = { name: "Talent Cloud", age: 3 };
182
      const parseResponse = () => ({ name: "Talent Cloud 2", age: 100 });
183
      const handleError = jest.fn();
184
185
      const { result, waitFor } = renderHook(() =>
186
        useResource(endpoint, parseResponse, initialValue, handleError),
187
      );
188
189
      // Wait for initial request to finish.
190
      await waitFor(
191
        () => {
192
          return result.current.status === "success";
193
        },
194
        { timeout: 2000 },
195
      );
196
      expect(handleError.mock.calls.length).toBe(0);
197
      expect(fetchMock.calls().length).toBe(1);
198
199
      expect(result.current.status).toEqual("success");
200
      act(() => {
201
        result.current.refresh();
202
      });
203
      expect(result.current.status).toEqual("updating");
204
    });
205
    it("Value changes after initial request if response is different from initialValue", async () => {
206
      fetchMock.mock("*", { name: "Talent Cloud", age: 3 });
207
      const initialValue = null;
208
      const parseResponse = (x) => x;
209
      const handleError = jest.fn();
210
211
      const { result, waitFor } = renderHook(() =>
212
        useResource(endpoint, parseResponse, initialValue, handleError),
213
      );
214
215
      expect(result.current.value).toBe(null);
216
217
      // Wait for initial request to finish.
218
      await waitFor(
219
        () => {
220
          return result.current.status === "success";
221
        },
222
        { timeout: 2000 },
223
      );
224
      expect(result.current.value).toEqual({ name: "Talent Cloud", age: 3 });
225
    });
226
    // it("Value changes after refresh if fetch response is different", async () => {
227
    //   const firstResponse = { name: "Talent Cloud", age: 10 };
228
    //   const secondResponse = { name: "Talent Cloud", age: 20 };
229
    //   fetchMock.get("*", firstResponse);
230
    //   fetchMock.get("*", secondResponse, { overwriteRoutes: false });
231
232
    //   const initialValue = null;
233
    //   const parseResponse = (x) => x;
234
    //   const handleError = jest.fn();
235
236
    //   const { result, waitFor, waitForNextUpdate } = renderHook(() =>
237
    //     useResource(endpoint, parseResponse, initialValue, handleError),
238
    //   );
239
240
    //   expect(result.current.value).toBe(null);
241
242
    //   // Wait for initial request to finish.
243
    //   await waitFor(
244
    //     () => {
245
    //       return result.current.status === "success";
246
    //     },
247
    //     { timeout: 2000 },
248
    //   );
249
    //   expect(result.current.value).toEqual(firstResponse);
250
    //   await act(async () => {
251
    //     await result.current.refresh();
252
    //     // await waitFor(
253
    //     //   () => {
254
    //     //     return result.current.status === "success";
255
    //     //   },
256
    //     //   { timeout: 2000 },
257
    //     // );
258
    //   });
259
    //   expect(fetchMock.calls().length).toEqual(2);
260
    //   expect(result.current.value).toEqual(secondResponse);
261
    // });
262
  });
263
});
264