From b20730125b8f3019fb20731d75ec624438f6bacf Mon Sep 17 00:00:00 2001 From: ysknsid25 Date: Fri, 4 Apr 2025 18:03:21 +0900 Subject: [PATCH] feature: support custom hook Signed-off-by: ysknsid25 --- src/types.ts | 5 +++ test/index.test.ts | 100 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/src/types.ts b/src/types.ts index 5f2e8187..dbea18a0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -65,6 +65,11 @@ export interface FetchOptions /** Default is [408, 409, 425, 429, 500, 502, 503, 504] */ retryStatusCodes?: number[]; + + /** Define custom hooks */ + hooks?: { + [key: string]: FetchHook>; + }; } export interface ResolvedFetchOptions< diff --git a/test/index.test.ts b/test/index.test.ts index f432e2d7..afcdc46b 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -533,4 +533,104 @@ describe("ofetch", () => { headers: expect.any(Headers), }); }); + + it("call custom hooks", async () => { + const customCommon = vi.fn(); + const customOnRequest = vi.fn(); + const customOnRequestError = vi.fn(); + const customOnResponse = vi.fn(); + const customOnResponseError = vi.fn(); + + const onRequest = (context) => { + context.options.hooks.customCommon(); + context.options.hooks.customOnRequest(context.request); + }; + const onRequestError = (context) => { + context.options.hooks.customCommon(); + context.options.hooks.customOnRequestError(context.error.message); + }; + const onResponse = (context) => { + context.options.hooks.customCommon(); + context.options.hooks.customOnResponse( + context.response.status, + context.response.statusText + ); + }; + const onResponseError = (context) => { + context.options.hooks.customCommon(); + context.options.hooks.customOnResponseError( + context.response.status, + context.response.statusText + ); + }; + + await $fetch(getURL("/ok"), { + onRequest, + onRequestError, + onResponse, + onResponseError, + hooks: { + customCommon, + customOnRequest, + customOnRequestError, + customOnResponse, + customOnResponseError, + }, + }); + + expect(customCommon).toHaveBeenCalledTimes(2); + expect(customOnRequest).toHaveBeenCalledOnce(); + expect(customOnRequest).toHaveBeenCalledWith("http://localhost:3000/ok"); + expect(customOnRequestError).not.toHaveBeenCalled(); + expect(customOnResponse).toHaveBeenCalledOnce(); + expect(customOnResponse).toHaveBeenCalledWith(200, "OK"); + expect(customOnResponseError).not.toHaveBeenCalled(); + + customCommon.mockReset(); + customOnRequest.mockReset(); + customOnRequestError.mockReset(); + customOnResponse.mockReset(); + customOnResponseError.mockReset(); + + await $fetch(getURL("/403"), { + onRequest, + onRequestError, + onResponse, + onResponseError, + hooks: { + customCommon, + customOnRequest, + customOnRequestError, + customOnResponse, + customOnResponseError, + }, + }).catch((error) => error); + + expect(customCommon).toHaveBeenCalledTimes(3); + expect(customOnRequest).toHaveBeenCalledOnce(); + expect(customOnRequest).toHaveBeenCalledWith("http://localhost:3000/403"); + expect(customOnRequestError).not.toHaveBeenCalled(); + expect(customOnResponse).toHaveBeenCalledOnce(); + expect(customOnResponse).toHaveBeenCalledWith(403, "Forbidden"); + expect(customOnResponseError).toHaveBeenCalledOnce(); + expect(customOnResponseError).toHaveBeenCalledWith(403, "Forbidden"); + + customCommon.mockReset(); + customOnRequest.mockReset(); + customOnRequestError.mockReset(); + customOnResponse.mockReset(); + customOnResponseError.mockReset(); + + await $fetch("/" /* non absolute is not acceptable */, { + onRequestError, + hooks: { + customCommon, + customOnRequestError, + }, + }).catch((error) => error); + + expect(customOnRequestError).toHaveBeenCalledWith( + "Failed to parse URL from /" + ); + }); });