import { describe, it, expect, vi, beforeEach } from "vitest";
import { useAsyncRequest } from "../useAsyncRequest";
describe("useAsyncRequest", () => {
beforeEach(() => {
vi.useFakeTimers({ shouldAdvanceTime: true });
});
it("sets isLoading to true during execution and false after", async () => {
const { isLoading, execute } = useAsyncRequest();
const promise = execute(async () => {
expect(isLoading.value).toBe(true);
return { ok: true, data: [] };
});
expect(isLoading.value).toBe(true);
await promise;
expect(isLoading.value).toBe(false);
});
it("clears previous error before new execution", async () => {
const { error, execute } = useAsyncRequest();
await execute(async () => ({ ok: false, error: { message: "fail" } }));
expect(error.value).toEqual({ message: "fail" });
await execute(async () => ({ ok: true, data: [] }));
expect(error.value).toBeNull();
});
it("sets error on failed result", async () => {
const { error, execute } = useAsyncRequest();
const result = await execute(async () => ({
ok: false,
error: { type: "api_error", message: "Bad request" },
}));
expect(result.ok).toBe(false);
expect(error.value).toEqual({ type: "api_error", message: "Bad request" });
});
it("ignores timeout error by default", async () => {
const { error, execute } = useAsyncRequest();
const result = await execute(async () => ({
ok: false,
error: { type: "timeout", message: "Aborted" },
}));
expect(result.ok).toBe(false);
expect(error.value).toBeNull();
});
it("records timeout error when ignoreTimeout is disabled", async () => {
const { error, execute } = useAsyncRequest({ ignoreTimeout: false });
const result = await execute(async () => ({
ok: false,
error: { type: "timeout", message: "Aborted" },
}));
expect(result.ok).toBe(false);
expect(error.value).toEqual({ type: "timeout", message: "Aborted" });
});
it("sets network_error on thrown exception", async () => {
const { error, execute } = useAsyncRequest();
const result = await execute(async () => {
throw new Error("Connection refused");
});
expect(result.ok).toBe(false);
expect(result.error.type).toBe("network_error");
expect(error.value.message).toBe("Connection refused");
});
it("aborts previous request before starting a new one", async () => {
const { abortController, execute } = useAsyncRequest();
let firstSignal;
execute(async (signal) => {
firstSignal = signal;
await new Promise((resolve) => setTimeout(resolve, 100));
return { ok: true, data: [] };
});
expect(abortController.value).not.toBeNull();
expect(firstSignal.aborted).toBe(false);
const secondPromise = execute(async () => ({ ok: true, data: [] }));
expect(firstSignal.aborted).toBe(true);
await secondPromise;
});
it("abort() cancels the current request", async () => {
const { abortController, execute, abort } = useAsyncRequest();
let signal;
execute(async (s) => {
signal = s;
await new Promise((resolve) => setTimeout(resolve, 100));
return { ok: true, data: [] };
});
abort();
expect(signal.aborted).toBe(true);
expect(abortController.value).toBeNull();
});
it("clear() resets error and aborts current request", async () => {
const { error, abortController, execute, clear } = useAsyncRequest();
await execute(async () => ({
ok: false,
error: { message: "fail" },
}));
expect(error.value).not.toBeNull();
let signal;
execute(async (s) => {
signal = s;
await new Promise((resolve) => setTimeout(resolve, 100));
return { ok: true, data: [] };
});
clear();
expect(error.value).toBeNull();
expect(signal.aborted).toBe(true);
expect(abortController.value).toBeNull();
});
});