From ceabae9dedddffd10414216be5913dd011ecb780 Mon Sep 17 00:00:00 2001 From: Ru Chern Chong Date: Wed, 16 Oct 2024 22:47:23 +0800 Subject: [PATCH] Add error handling for fetchApi helper --- utils/fetchApi.test.ts | 55 +++++++++++++++++++++++++++++++++++++++--- utils/fetchApi.ts | 32 +++++++++++++++++------- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/utils/fetchApi.test.ts b/utils/fetchApi.test.ts index 2ca3eb6..c24e4ba 100644 --- a/utils/fetchApi.test.ts +++ b/utils/fetchApi.test.ts @@ -2,15 +2,64 @@ import fetch from "jest-fetch-mock"; import { fetchApi } from "@/utils/fetchApi"; describe("fetchApi", () => { - beforeEach(() => fetch.resetMocks()); + beforeEach(() => { + fetch.resetMocks(); + }); it("should return data for a successful API call", async () => { - fetch.mockResponseOnce(JSON.stringify({ data: "test" })); + const mockResponse = { data: "test" }; + fetch.mockResponseOnce(JSON.stringify(mockResponse)); const url = "https://example.com/api/test"; const data = await fetchApi(url); expect(fetch).toHaveBeenCalledTimes(1); - expect(data).toEqual({ data: "test" }); + expect(fetch).toHaveBeenCalledWith(url, { + cache: "no-store", + headers: { + Authorization: `Bearer ${process.env.SG_CARS_TRENDS_API_TOKEN}`, + }, + }); + expect(data).toEqual(mockResponse); + }); + + it("should throw an error for a non-OK response", async () => { + const errorBody = "Error occurred"; + fetch.mockResponseOnce(errorBody, { status: 404, statusText: "Not Found" }); + + const url = "https://example.com/api/test"; + await expect(fetchApi(url)).rejects.toThrow( + `API call failed: ${url} - 404 - Not Found`, + ); + }); + + it("should merge custom options with default options", async () => { + fetch.mockResponseOnce(JSON.stringify({ data: "test" })); + + const url = "https://example.com/api/test"; + const customOptions = { + method: "POST", + headers: { + "Custom-Header": "CustomValue", + }, + }; + + await fetchApi(url, customOptions); + + expect(fetch).toHaveBeenCalledWith(url, { + method: "POST", + cache: "no-store", + headers: { + Authorization: `Bearer ${process.env.SG_CARS_TRENDS_API_TOKEN}`, + "Custom-Header": "CustomValue", + }, + }); + }); + + it("should handle network errors", async () => { + fetch.mockReject(new Error("Network error")); + + const url = "https://example.com/api/test"; + await expect(fetchApi(url)).rejects.toThrow("Network error"); }); }); diff --git a/utils/fetchApi.ts b/utils/fetchApi.ts index 8e3e2c1..03c4e2e 100644 --- a/utils/fetchApi.ts +++ b/utils/fetchApi.ts @@ -2,21 +2,35 @@ export const fetchApi = async ( url: string, options: RequestInit = {}, ): Promise => { - options = { - ...options, + const defaultOptions: RequestInit = { // TODO: Remove this later cache: "no-store", headers: { Authorization: `Bearer ${process.env.SG_CARS_TRENDS_API_TOKEN}`, }, }; - const response = await fetch(url, options); - if (!response.ok) { - throw new Error( - `API call failed: ${url} - ${response.status} - ${response.statusText}`, - ); - } + const mergedOptions: RequestInit = { + ...defaultOptions, + ...options, + headers: { + ...defaultOptions.headers, + ...options.headers, + }, + }; + + try { + const response = await fetch(url, mergedOptions); - return response.json(); + if (!response.ok) { + throw new Error( + `API call failed: ${url} - ${response.status} - ${response.statusText}`, + ); + } + + return response.json(); + } catch (e) { + console.error(e.message); + throw e; + } };