@hyper-fetch/react
Version:
React hooks and utils for the hyper-fetch
321 lines (277 loc) • 11.1 kB
text/typescript
import type { RequestInstance, ExtractAdapterResolvedType } from "@hyper-fetch/core";
import { act, waitFor } from "@testing-library/react";
import { createHttpMockingServer } from "@hyper-fetch/testing";
import { client, createRequest, renderUseSubmit, waitForRender } from "../../utils";
const { resetMocks, startServer, stopServer, mockRequest } = createHttpMockingServer();
describe("useSubmit [ Bounce ]", () => {
const hookDebounceOptions = { bounce: true, bounceType: "debounce", bounceTime: 50 } as const;
const hookThrottleOptions = { bounce: true, bounceType: "throttle", bounceTime: 50, bounceTimeout: 50 } as const;
let request = createRequest<null, null>({ method: "POST" });
beforeAll(() => {
startServer();
});
afterEach(() => {
resetMocks();
});
afterAll(() => {
stopServer();
});
beforeEach(() => {
vi.resetModules();
client.clear();
request = createRequest({ method: "POST" });
});
describe("given debounce is active", () => {
describe("when request is about to change", () => {
it("should debounce single request", async () => {
let submitTime: number | null = null;
let startTime: number | null = null;
mockRequest(request);
const response = renderUseSubmit(request, hookDebounceOptions);
act(() => {
response.result.current.onSubmitRequestStart(() => {
startTime = +new Date();
});
submitTime = +new Date();
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
});
expect((startTime || 0) - (submitTime || 0)).toBeGreaterThanOrEqual(hookDebounceOptions.bounceTime);
});
it("should debounce multiple request triggers by bounceTime", async () => {
const spy = vi.fn();
let submitTime: number | null = null;
let startTime: number | null = null;
mockRequest(request);
const response = renderUseSubmit(request, hookDebounceOptions);
await act(async () => {
response.result.current.onSubmitRequestStart(() => {
spy();
startTime = +new Date();
});
submitTime = +new Date();
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
});
expect((startTime || 0) - (submitTime || 0)).toBeGreaterThanOrEqual(hookDebounceOptions.bounceTime);
expect(spy).toHaveBeenCalledTimes(1);
});
it("should resolve debounced methods", async () => {
mockRequest(request);
const response = renderUseSubmit(request, hookDebounceOptions);
let value: ExtractAdapterResolvedType<RequestInstance>[] = [];
await act(async () => {
const promiseOne = await response.result.current.submit();
await waitForRender(1);
const promiseTwo = await response.result.current.submit();
await waitForRender(1);
const promiseThree = await response.result.current.submit();
await waitForRender(1);
const promiseFour = await response.result.current.submit();
value = [promiseOne, promiseTwo, promiseThree, promiseFour];
});
expect(value).toHaveLength(4);
const isResponse = (res: ExtractAdapterResolvedType<RequestInstance>) => {
return Boolean(!!res.data && !res.error && res.status === 200 && res.extra);
};
expect(value).toSatisfyAny(isResponse);
expect(value).toHaveLength(4);
});
it("should change debounce time", async () => {
const newBounceTime = 200;
const spy = vi.fn();
let submitTime: number | null = null;
let startTime: number | null = null;
mockRequest(request);
const response = renderUseSubmit(request, hookDebounceOptions);
await act(async () => {
await response.rerender({ bounceTime: newBounceTime });
});
await waitForRender(1);
await act(async () => {
response.result.current.onSubmitRequestStart(() => {
spy();
startTime = +new Date();
});
submitTime = +new Date();
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
});
expect((startTime || 0) - (submitTime || 0)).toBeGreaterThanOrEqual(newBounceTime);
expect(spy).toHaveBeenCalledTimes(1);
});
});
});
describe("given debounce is off", () => {
describe("when request is about to change", () => {
it("should not debounce multiple request triggers", async () => {
const spy = vi.fn();
let startTime: number | null = null;
mockRequest(request, { delay: 0 });
const response = renderUseSubmit(request);
await act(async () => {
response.result.current.onSubmitRequestStart(() => {
spy();
startTime = +new Date();
});
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
});
expect(spy).toHaveBeenCalledTimes(4);
});
});
});
describe("given throttle is active", () => {
describe("when request is about to change", () => {
it("should throttle single request", async () => {
let submitTime: number | null = null;
let startTime: number | null = null;
mockRequest(request);
const response = renderUseSubmit(request, hookThrottleOptions);
act(() => {
response.result.current.onSubmitRequestStart(() => {
startTime = +new Date();
});
submitTime = +new Date();
response.result.current.submit();
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
expect((startTime || 0) - (submitTime || 0)).toBeGreaterThanOrEqual(hookThrottleOptions.bounceTime);
});
});
it("should throttle multiple request triggers by bounceTime", async () => {
const spy = vi.fn();
let submitTime: number | null = null;
let startTime: number | null = null;
mockRequest(request);
const response = renderUseSubmit(request, hookThrottleOptions);
await act(async () => {
response.result.current.onSubmitRequestStart(() => {
spy();
startTime = +new Date();
});
submitTime = +new Date();
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
expect((startTime || 0) - (submitTime || 0)).toBeGreaterThanOrEqual(hookThrottleOptions.bounceTime);
expect(spy).toHaveBeenCalledTimes(2);
});
});
it("should resolve throttled methods", async () => {
mockRequest(request);
const response = renderUseSubmit(request, hookThrottleOptions);
let value: ExtractAdapterResolvedType<RequestInstance>[] = [];
await act(async () => {
const promiseOne = await response.result.current.submit();
await waitForRender(1);
const promiseTwo = await response.result.current.submit();
await waitForRender(1);
const promiseThree = await response.result.current.submit();
await waitForRender(1);
const promiseFour = await response.result.current.submit();
value = [promiseOne, promiseTwo, promiseThree, promiseFour];
});
expect(value).toHaveLength(4);
const isResponse = (res: ExtractAdapterResolvedType<RequestInstance>) => {
return !!res.data && !res.error && res.status === 200 && !!res.extra;
};
expect(value).toSatisfyAny(isResponse);
expect(value).toHaveLength(4);
});
it("should change throttle time", async () => {
const newBounceTime = 200;
const spy = vi.fn();
let submitTime: number | null = null;
let startTime: number | null = null;
mockRequest(request);
const response = renderUseSubmit(request, hookThrottleOptions);
await act(async () => {
await response.rerender({ bounceTime: newBounceTime, bounceTimeout: newBounceTime });
});
await waitForRender(1);
await act(async () => {
response.result.current.onSubmitRequestStart(() => {
spy();
startTime = +new Date();
});
submitTime = +new Date();
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
expect((startTime || 0) - (submitTime || 0)).toBeGreaterThanOrEqual(newBounceTime);
expect(spy).toHaveBeenCalledTimes(2);
});
});
});
});
describe("given throttle is off", () => {
describe("when request is about to change", () => {
it("should not throttle multiple request triggers", async () => {
const spy = vi.fn();
let startTime: number | null = null;
mockRequest(request, { delay: 0 });
const response = renderUseSubmit(request);
await act(async () => {
response.result.current.onSubmitRequestStart(() => {
spy();
startTime = +new Date();
});
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
await waitForRender(10);
response.result.current.submit();
});
await waitFor(() => {
expect(startTime).not.toBeNull();
});
expect(spy).toHaveBeenCalledTimes(4);
});
});
});
});