@hyper-fetch/core
Version:
Cache, Queue and Persist your requests no matter if you are online or offline!
293 lines (258 loc) • 12.6 kB
text/typescript
import { createHttpMockingServer } from "@hyper-fetch/testing";
import { Client, interceptRequest, interceptResponse, stringifyDefaultOptions } from "client";
import { stringifyQueryParams } from "http-adapter";
const { resetMocks, startServer, stopServer } = createHttpMockingServer();
const options = {
...stringifyDefaultOptions,
encode: true,
};
describe("Client [ Utils ]", () => {
let client = new Client({ url: "shared-base-url" });
client.adapter.setQueryParamsMapperConfig(options);
let request = client.createRequest<{ payload: FormData; response: any }>()({ endpoint: "shared-nase-endpoint" });
beforeAll(() => {
startServer();
});
beforeEach(() => {
client = new Client({ url: "shared-base-url" });
client.adapter.setQueryParamsMapperConfig(options);
request = client.createRequest<{ response: any; payload: FormData }>()({ endpoint: "shared-nase-endpoint" });
resetMocks();
});
afterAll(() => {
stopServer();
});
describe("When using stringifyQueryParams util", () => {
it("should encode falsy values", async () => {
const newClient = new Client({ url: "shared-base-url" });
newClient.adapter.setQueryParamsMapperConfig({
encode: false,
arrayFormat: "bracket",
});
expect(
newClient.adapter.unstable_queryParamsMapper({ value: { test: new Date("9,9,2020").toISOString() } }),
).toBe(`?value={"test":"${new Date("9,9,2020").toISOString()}"}`);
expect(
newClient.adapter.unstable_queryParamsMapper({
page: 0,
status: ["status1", "status2"],
lastFreeDay: '{"from":"2023-06-20T10:37:27.508Z","to":"2023-06-24T10:37:27.508Z"}',
account: "test-account/123+323-123|111:;",
}),
).toBe(
'?page=0&status[]=status1&status[]=status2&lastFreeDay={"from":"2023-06-20T10:37:27.508Z","to":"2023-06-24T10:37:27.508Z"}&account=test-account/123+323-123|111:;',
);
});
it("should encode falsy values", async () => {
expect(stringifyQueryParams({ value: 0 }, options)).toBe("?value=0");
expect(stringifyQueryParams({ value: "" }, { ...options, skipEmptyString: false })).toBe("?value=");
expect(stringifyQueryParams({ value: null }, { ...options, skipNull: false })).toBe("?value=null");
expect(stringifyQueryParams({ value: "" }, { ...options, skipEmptyString: true })).toBe("");
expect(stringifyQueryParams({ value: undefined }, { ...options, skipEmptyString: true })).toBe("");
expect(stringifyQueryParams({ value: null }, { ...options, skipNull: true })).toBe("");
});
it("should encode date value to isoString by default", async () => {
expect(stringifyQueryParams({ value: new Date("2023-05-12T22:32:32") }, options)).toInclude(`?value=2023-05-12`);
});
it("should encode truthy values", async () => {
expect(stringifyQueryParams({ value: 10 }, options)).toBe("?value=10");
expect(stringifyQueryParams({ value: "test" }, options)).toBe("?value=test");
expect(stringifyQueryParams({ value: [1, 2, 3] }, options)).toBe("?value[]=1&value[]=2&value[]=3");
expect(stringifyQueryParams({ value: { data: "test" } }, options)).toBe("?value=%7B%22data%22%3A%22test%22%7D");
expect(
stringifyQueryParams(
{
value: {
filter: {
firstName: "john",
other: {
hobbies: ["dancing", "singing"],
},
},
},
},
options,
),
).toBe(
"?value=%7B%22filter%22%3A%7B%22firstName%22%3A%22john%22%2C%22other%22%3A%7B%22hobbies%22%3A%5B%22dancing%22%2C%22singing%22%5D%7D%7D%7D",
);
});
it("should encode not allowed characters values", async () => {
expect(stringifyQueryParams({ value: "[]" }, options)).toBe("?value=%5B%5D");
expect(stringifyQueryParams({ value: "/" }, options)).toBe("?value=%2F");
expect(stringifyQueryParams({ value: "," }, options)).toBe("?value=%2C");
expect(stringifyQueryParams({ value: "|" }, options)).toBe("?value=%7C");
expect(stringifyQueryParams({ value: ":" }, options)).toBe("?value=%3A");
expect(stringifyQueryParams({ value: ";" }, options)).toBe("?value=%3B");
expect(stringifyQueryParams({ value: "+" }, options)).toBe("?value=%2B");
expect(stringifyQueryParams({ value: "(" }, options)).toBe("?value=%28");
expect(stringifyQueryParams({ value: ")" }, options)).toBe("?value=%29");
expect(stringifyQueryParams({ value: "*" }, options)).toBe("?value=%2A");
expect(stringifyQueryParams({ value: "&" }, options)).toBe("?value=%26");
expect(stringifyQueryParams({ value: "^" }, options)).toBe("?value=%5E");
expect(stringifyQueryParams({ value: "%" }, options)).toBe("?value=%25");
expect(stringifyQueryParams({ value: "$" }, options)).toBe("?value=%24");
expect(stringifyQueryParams({ value: "#" }, options)).toBe("?value=%23");
expect(stringifyQueryParams({ value: "@" }, options)).toBe("?value=%40");
expect(stringifyQueryParams({ value: "!" }, options)).toBe("?value=%21");
});
it("should encode array values with different formats", async () => {
expect(
stringifyQueryParams({ value: [1, 2, 3] }, { ...options, arraySeparator: "", arrayFormat: "bracket" }),
).toBe("?value[]=1&value[]=2&value[]=3");
expect(
stringifyQueryParams(
{ value: [1, 2, 3] },
{ ...options, arraySeparator: "", arrayFormat: "bracket-separator" },
),
).toBe("?value[]=1|2|3");
expect(stringifyQueryParams({ value: [1, 2, 3] }, { ...options, arraySeparator: "", arrayFormat: "comma" })).toBe(
"?value=1,2,3",
);
expect(stringifyQueryParams({ value: [1, 2, 3] }, { ...options, arraySeparator: "", arrayFormat: "index" })).toBe(
"?value[0]=1&value[1]=2&value[2]=3",
);
expect(stringifyQueryParams({ value: [1, 2, 3] }, { ...options, arraySeparator: "", arrayFormat: "none" })).toBe(
"?value=1&value=2&value=3",
);
expect(
stringifyQueryParams({ value: [1, 2, 3] }, { ...options, arraySeparator: "", arrayFormat: "separator" }),
).toBe("?value=1|2|3");
expect(
stringifyQueryParams({ value: [1, 2, 3] }, { ...options, arrayFormat: "separator", arraySeparator: "#" }),
).toBe("?value=1#2#3");
});
it("should encode values without strict option", async () => {
expect(stringifyQueryParams({ value: "![]" }, { ...options, encode: true, strict: false })).toBe(
"?value=!%5B%5D",
);
expect(stringifyQueryParams({ value: "![]" }, { ...options, encode: false, strict: false })).toBe("?value=![]");
expect(stringifyQueryParams({ value: "![]" }, { ...options, encode: false, strict: true })).toBe("?value=![]");
});
it("should add the question mark", async () => {
expect(stringifyQueryParams("something", options)).toBe("?something");
expect(stringifyQueryParams("?something", options)).toBe("?something");
});
it("should return empty string when no query params are passed", async () => {
expect(stringifyQueryParams("", options)).toBe("");
expect(stringifyQueryParams({}, options)).toBe("");
expect(stringifyQueryParams(null, options)).toBe("");
expect(stringifyQueryParams(undefined, options)).toBe("");
});
});
describe("When using headerMapper", () => {
it("should assign default headers with headers mapper", async () => {
const defaultHeaders = {};
const headers = client.adapter.unstable_headerMapper(request, undefined);
expect(headers).toEqual(defaultHeaders);
});
it("should assign form data headers", async () => {
const defaultHeaders = {};
const data = new FormData();
const formDataRequest = request.setPayload(data);
const headers = client.adapter.unstable_headerMapper(formDataRequest, undefined);
expect(headers).toEqual(defaultHeaders);
});
it("should not re-assign form data headers", async () => {
const defaultHeaders = { "Content-Type": "some-custom-format" };
const formDataRequest = request.setHeaders(defaultHeaders);
const headers = client.adapter.unstable_headerMapper(formDataRequest, undefined);
expect(headers).toEqual(defaultHeaders);
});
it("should add content type header for object payload", async () => {
const defaultHeaders = { "Content-Type": "application/json" };
const data = { data: [] } as any;
const formDataRequest = request.setPayload(data);
const headers = client.adapter.unstable_headerMapper(formDataRequest, undefined);
expect(headers).toEqual(defaultHeaders);
});
});
describe("When using payloadMapper", () => {
it("should allow to stringify payload", async () => {
const data = { data: [] };
const payload = client.adapter.unstable_payloadMapper(data, undefined);
expect(payload).toEqual(JSON.stringify(data));
});
it("should not stringify FormData payload", async () => {
const data = new FormData();
const payload = client.adapter.unstable_payloadMapper(data, undefined);
expect(payload).toEqual(data);
});
it("should stringify null payload", async () => {
const data = null;
const payload = client.adapter.unstable_payloadMapper(data, undefined);
expect(payload).toEqual(JSON.stringify(data));
});
it("should not stringify undefined payload", async () => {
const payload = client.adapter.unstable_payloadMapper(undefined, undefined);
expect(payload).toBeUndefined();
});
it("should not stringify invalid payload", async () => {
const payload = client.adapter.unstable_payloadMapper(() => null, undefined);
expect(payload).toBeUndefined();
});
});
describe("When using stringifyValue util", () => {
it("should allow to stringify payload", async () => {
const data = { data: [] };
const payload = client.adapter.unstable_payloadMapper(data, undefined);
expect(payload).toEqual(JSON.stringify(data));
});
it("should not stringify FormData payload", async () => {
const data = new FormData();
const payload = client.adapter.unstable_payloadMapper(data, undefined);
expect(payload).toEqual(data);
});
it("should stringify null payload", async () => {
const data = null;
const payload = client.adapter.unstable_payloadMapper(data, undefined);
expect(payload).toEqual(JSON.stringify(data));
});
it("should not stringify undefined payload", async () => {
const payload = client.adapter.unstable_payloadMapper(undefined, undefined);
expect(payload).toBeUndefined();
});
it("should not stringify invalid payload", async () => {
const data: Record<string, unknown> = {};
data.a = { b: data };
const payload = client.adapter.unstable_payloadMapper(data, undefined);
expect(payload).toBe("");
});
});
describe("when interceptors are disabled", () => {
it("should skip request interceptors when disabled", async () => {
const mockInterceptor = vi.fn((req) => req);
const req = client.createRequest<{ response: any }>()({
endpoint: "shared-base-endpoint",
disableRequestInterceptors: true,
});
const result = await interceptRequest([mockInterceptor], req);
expect(mockInterceptor).not.toHaveBeenCalled();
expect(result).toBe(req);
});
it("should skip response interceptors when disabled", async () => {
const mockInterceptor = vi.fn((res) => res);
const response = {
data: { test: "data" },
status: 200,
success: true,
} as any;
const req = client.createRequest<{ response: any }>()({
endpoint: "shared-base-endpoint",
disableResponseInterceptors: true,
});
const result = await interceptResponse([mockInterceptor], response, req);
expect(mockInterceptor).not.toHaveBeenCalled();
expect(result).toBe(response);
});
it("should run interceptors when not disabled", async () => {
const mockInterceptor = vi.fn((req) => req);
const req = client.createRequest<{ response: any }>()({
endpoint: "shared-base-endpoint",
disableRequestInterceptors: false,
});
await interceptRequest([mockInterceptor], req);
expect(mockInterceptor).toHaveBeenCalledWith(req);
});
});
});