@hyper-fetch/react
Version:
React hooks and utils for the hyper-fetch
276 lines (247 loc) • 10 kB
text/typescript
import { createClient } from "@hyper-fetch/core";
import { getValidCacheData, getInitialState, initialState } from "helpers";
import { isEmpty, isEqual } from "utils";
describe("useTrackedState [ Utils ]", () => {
describe("when isEmpty gets triggered", () => {
it("should return true for empty array or object", async () => {
expect(isEmpty([])).toBe(true);
expect(isEmpty({})).toBe(true);
});
it("should return false", async () => {
expect(isEmpty(null)).toBe(false);
expect(isEmpty(new Date())).toBe(false);
expect(isEmpty(NaN)).toBe(false);
expect(isEmpty("")).toBe(false);
expect(isEmpty(false)).toBe(false);
expect(isEmpty(undefined)).toBe(false);
expect(isEmpty(0)).toBe(false);
expect(isEmpty(true)).toBe(false);
expect(isEmpty(123)).toBe(false);
expect(isEmpty("123")).toBe(false);
expect(isEmpty([1, 2, 3])).toBe(false);
expect(isEmpty({ something: 1 })).toBe(false);
});
});
describe("when isEqual gets triggered", () => {
it("should return false for non equal values", async () => {
expect(isEqual(true, false)).toBe(false);
expect(isEqual(null, {})).toBe(false);
expect(isEqual(new Date(), {})).toBe(false);
expect(isEqual(new Date(), null)).toBe(false);
expect(isEqual(NaN, {})).toBe(false);
expect(isEqual(NaN, null)).toBe(false);
expect(isEqual("1", 1)).toBe(false);
expect(isEqual(0, -1)).toBe(false);
expect(isEqual("2", "3")).toBe(false);
expect(isEqual([], [1])).toBe(false);
expect(isEqual([1], [2])).toBe(false);
expect(isEqual(["1"], [1])).toBe(false);
expect(isEqual([null], [NaN])).toBe(false);
expect(isEqual([null], [new Date()])).toBe(false);
expect(isEqual([null], [{}])).toBe(false);
expect(isEqual([new Date()], [{}])).toBe(false);
expect(isEqual([NaN], [{}])).toBe(false);
expect(isEqual([{ someKey: 1 }], [{ someKey: 2 }])).toBe(false);
expect(isEqual([{ someKey: "1" }], [{ someKey: 1 }])).toBe(false);
expect(isEqual([{ someKey: null }], [{ someKey: {} }])).toBe(false);
expect(isEqual([{ someKey: null }], [{ someKey: NaN }])).toBe(false);
expect(isEqual([{ someKey: null }], [{ someKey: new Date() }])).toBe(false);
expect(isEqual([{ someKey: {} }], [{ someKey: new Date() }])).toBe(false);
expect(isEqual([{ someKey: {} }], [{ someKey: NaN }])).toBe(false);
expect(isEqual({ someKey: 1 }, { otherKey: 1 })).toBe(false);
expect(isEqual({ someKey: 1 }, { someKey: 1, otherKey: 1 })).toBe(false);
expect(isEqual({ someKey: 1 }, { someKey: 2 })).toBe(false);
expect(isEqual({ someKey: "1" }, { someKey: 1 })).toBe(false);
expect(isEqual({ someKey: null }, { someKey: {} })).toBe(false);
expect(isEqual({ someKey: null }, { someKey: NaN })).toBe(false);
expect(isEqual({ someKey: null }, { someKey: new Date() })).toBe(false);
expect(isEqual({ someKey: {} }, { someKey: new Date() })).toBe(false);
expect(isEqual({ someKey: {} }, { someKey: NaN })).toBe(false);
expect(isEqual({ someKey: 1 }, { otherKey: 1 })).toBe(false);
expect(isEqual({ someKey: 1 }, { someKey: 1, otherKey: 1 })).toBe(false);
});
it("should return true for equal arguments", async () => {
const date = new Date();
expect(isEqual(true, true)).toBe(true);
expect(isEqual(false, false)).toBe(true);
expect(isEqual(undefined, undefined)).toBe(true);
expect(isEqual(null, null)).toBe(true);
expect(isEqual(date, date)).toBe(true);
expect(isEqual(date, date)).toBe(true);
expect(isEqual(NaN, NaN)).toBe(true);
expect(isEqual("1", "1")).toBe(true);
expect(isEqual(0, 0)).toBe(true);
expect(isEqual(1, 1)).toBe(true);
expect(isEqual("2", "2")).toBe(true);
expect(isEqual([], [])).toBe(true);
expect(isEqual([1], [1])).toBe(true);
expect(isEqual(["1"], ["1"])).toBe(true);
expect(isEqual([NaN], [NaN])).toBe(true);
expect(isEqual([date], [date])).toBe(true);
expect(isEqual([null], [null])).toBe(true);
expect(isEqual([{}], [{}])).toBe(true);
expect(isEqual([{ someKey: 1 }], [{ someKey: 1 }])).toBe(true);
expect(isEqual([{ someKey: "1" }], [{ someKey: "1" }])).toBe(true);
expect(isEqual([{ someKey: null }], [{ someKey: null }])).toBe(true);
expect(isEqual([{ someKey: NaN }], [{ someKey: NaN }])).toBe(true);
expect(isEqual([{ someKey: date }], [{ someKey: date }])).toBe(true);
expect(isEqual([{ someKey: {} }], [{ someKey: {} }])).toBe(true);
expect(isEqual({ someKey: 1, otherKey: 1 }, { someKey: 1, otherKey: 1 })).toBe(true);
expect(isEqual({ someKey: "1" }, { someKey: "1" })).toBe(true);
expect(isEqual({ someKey: {} }, { someKey: {} })).toBe(true);
expect(isEqual({ someKey: NaN }, { someKey: NaN })).toBe(true);
expect(isEqual({ someKey: null }, { someKey: null })).toBe(true);
expect(isEqual({ someKey: date }, { someKey: date })).toBe(true);
expect(isEqual({}, {})).toBe(true);
expect(isEqual([null], [null])).toBe(true);
});
it("should return false when comparison throws an error", async () => {
// Create an object with a getter that throws
const throwingObj = {
get evil() {
throw new Error("Comparison error");
},
};
const obj1 = { prop: throwingObj };
const obj2 = { prop: { key: 2 } };
// Should return false when comparison throws
expect(isEqual(obj1, obj2)).toBe(false);
});
});
describe("when getValidCacheData gets triggered", () => {
const mockRequest = {
staleTime: 1000,
client: {
cache: {
version: 1,
},
},
cacheKey: "test-key",
cacheTime: 2000,
} as any;
it("should return initialResponse data when cache is stale", () => {
const initialResponse = {
data: "test-data",
status: 200,
success: true,
requestTimestamp: new Date().getTime(),
responseTimestamp: new Date().getTime(),
};
const result = getValidCacheData(mockRequest, initialResponse, null);
expect(result).toEqual(
expect.objectContaining({
data: "test-data",
error: null,
status: 200,
success: true,
extra: null,
staleTime: 1000,
version: 1,
cacheKey: "test-key",
cacheTime: 2000,
}),
);
});
it("should return null when no cache data and no initialResponse", () => {
const result = getValidCacheData(mockRequest, null, null);
expect(result).toBeNull();
});
it("should return cache data when not stale", () => {
const currentTime = new Date().getTime();
const cacheData = {
data: "cached-data",
error: null,
status: 200,
success: true,
extra: null,
responseTimestamp: currentTime, // Recent timestamp within stale time
} as any;
const result = getValidCacheData(mockRequest, null, cacheData);
expect(result).toBe(cacheData);
});
it("should return stale cache data for stale-while-revalidate", () => {
const staleTimestamp = new Date().getTime() - 5000; // Well beyond the 1000ms staleTime
const cacheData = {
data: "stale-data",
error: null,
status: 200,
success: true,
extra: null,
responseTimestamp: staleTimestamp,
} as any;
const result = getValidCacheData(mockRequest, null, cacheData);
expect(result).toBe(cacheData);
});
});
describe("when getInitialState handles async responseMapper", () => {
it("should return initialState when responseMapper returns a Promise", () => {
const mockRequest = {
client: {
cache: { get: vi.fn().mockReturnValue(null) },
defaultExtra: null,
},
queryKey: "test",
cacheKey: "test",
unstable_responseMapper: () => Promise.resolve({ data: "test" }),
} as any;
const mockDispatcher = {
getQueue: vi.fn().mockReturnValue({ stopped: false }),
hasRunningRequests: vi.fn().mockReturnValue(false),
} as any;
const result = getInitialState({
initialResponse: { data: "test" },
dispatcher: mockDispatcher,
request: mockRequest,
disabled: false,
revalidate: false,
});
expect(result).toEqual(initialState);
});
});
describe("when getInitialState handles sync responseMapper", () => {
it("should apply sync responseMapper to cache data", () => {
const currentTime = new Date().getTime();
const cacheData = {
data: "original-data",
error: null,
status: 200,
success: true,
extra: null,
responseTimestamp: currentTime,
retries: 0,
};
const client = createClient({ url: "http://localhost:3000" });
const mockRequest = client.createRequest()({ endpoint: "/test" });
mockRequest.unstable_responseMapper = (data: any) => ({
...data,
data: `mapped-${data.data}`,
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
client.cache = {
get: vi.fn().mockReturnValue(cacheData),
version: "1",
};
const mockDispatcher = {
getQueue: vi.fn().mockReturnValue({ stopped: false }),
hasRunningRequests: vi.fn().mockReturnValue(false),
} as any;
const result = getInitialState({
initialResponse: null,
dispatcher: mockDispatcher,
request: mockRequest,
disabled: false,
revalidate: false,
});
expect(result).toEqual(
expect.objectContaining({
data: "mapped-original-data",
error: null,
status: 200,
success: true,
loading: false,
}),
);
});
});
});