diff --git a/src/create-client.test.ts b/src/create-client.test.ts index ab87b50..2661755 100644 --- a/src/create-client.test.ts +++ b/src/create-client.test.ts @@ -133,6 +133,36 @@ describe("create-client", () => { nockScope.done(); }); + + it("does not hang when the API is unreachable and the fetch times out", async () => { + const originalFetch = global.fetch; + const timeoutError = new DOMException( + "The operation timed out.", + "TimeoutError", + ); + const mockFetch = jest.fn().mockRejectedValue(timeoutError); + global.fetch = mockFetch; + + try { + client = await createClient("client_123abc", { + devMode: false, + redirectUri: "https://example.com/", + }); + + // initialize() has a catch-all around #refreshSession, so + // createClient should resolve even though the fetch failed + // (rather than hanging forever, as it would with no timeout). + expect(client).toBeDefined(); + expect(client.getUser()).toBeNull(); + expect(mockFetch).toHaveBeenCalledTimes(1); + + // verify the request had an AbortSignal attached for the timeout + const fetchOptions = mockFetch.mock.calls[0][1]; + expect(fetchOptions.signal).toBeInstanceOf(AbortSignal); + } finally { + global.fetch = originalFetch; + } + }); }); describe("when devMode is true", () => { diff --git a/src/http-client.ts b/src/http-client.ts index 12fa1cb..3482906 100644 --- a/src/http-client.ts +++ b/src/http-client.ts @@ -97,6 +97,7 @@ export class HttpClient { "Content-Type": "application/json", }, body: JSON.stringify(body), + signal: AbortSignal.timeout(30_000), }); }