@hyper-fetch/core
Version:
Cache, Queue and Persist your requests no matter if you are online or offline!
415 lines (336 loc) • 13.1 kB
text/typescript
import { Client, createClient } from "client";
import type { AdapterInstance } from "adapter";
import { Adapter, RequestProcessingError, getAdapterHeaders, getAdapterPayload } from "adapter";
import { HttpMethods } from "../../../src/constants/http.constants";
describe("Adapter [ Base ]", () => {
let adapter: AdapterInstance;
beforeEach(() => {
adapter = new Adapter({
name: "test-adapter",
defaultMethod: HttpMethods.GET,
defaultExtra: {},
systemErrorStatus: 0,
systemErrorExtra: {},
});
});
describe("when setting mapper configs", () => {
it("should set query params mapper config", () => {
const config = { test: true };
adapter.setQueryParamsMapperConfig(config);
expect(adapter.unstable_queryParamsMapperConfig).toBe(config);
});
it("should set header mapper config", () => {
const config = { test: true };
adapter.setHeaderMapperConfig(config);
expect(adapter.unstable_headerMapperConfig).toBe(config);
});
it("should set endpoint mapper config", () => {
const config = { test: true };
adapter.setEndpointMapperConfig(config);
expect(adapter.unstable_endpointMapperConfig).toBe(config);
});
it("should set payload mapper config", () => {
const config = { test: true };
adapter.setPayloadMapperConfig(config);
expect(adapter.unstable_payloadMapperConfig).toBe(config);
});
it("should set devtools endpoint getter", () => {
const mapper = (endpoint: string) => endpoint;
adapter.setDevtoolsEndpointGetter(mapper);
expect(adapter.unstable_devtoolsEndpointGetter).toBe(mapper);
});
});
describe("when using onInitialize", () => {
it("should store and execute initialization callback", () => {
const spy = vi.fn();
const client = new Client({ url: "test" });
adapter.onInitialize(spy);
adapter.initialize(client);
expect(spy).toHaveBeenCalledWith({ client });
expect(adapter.unstable_onInitializeCallback).toBe(spy);
});
it("should handle initialization without callback", () => {
const client = new Client({ url: "test" });
// Initialize without setting onInitialize callback
expect(() => adapter.initialize(client)).not.toThrow();
expect(adapter.unstable_onInitializeCallback).toBeUndefined();
expect(adapter.initialized).toBe(true);
expect(adapter.client).toBe(client);
});
it("should handle initialization with callback set to undefined", () => {
const client = new Client({ url: "test" });
// Initialize without setting onInitialize callback
adapter.unstable_onInitializeCallback = undefined;
expect(() => adapter.initialize(client)).not.toThrow();
expect(adapter.unstable_onInitializeCallback).toBeUndefined();
expect(adapter.initialized).toBe(true);
expect(adapter.client).toBe(client);
});
});
describe("when using setFetcher", () => {
it("should bind fetcher function to adapter instance", () => {
const mock = { test: 123 } as any;
const fetcher = () => {
return mock;
};
adapter.setFetcher(fetcher);
const result = adapter.unstable_fetcher({} as any);
expect(result).toBe(mock);
});
});
describe("when using mapper setters", () => {
it("should set header mapper with config", () => {
const headerMapper = vi.fn();
const config = { test: true };
adapter.setHeaderMapperConfig(config);
adapter.setHeaderMapper(headerMapper);
adapter.unstable_headerMapper({} as any);
expect(headerMapper).toHaveBeenCalledWith({}, config);
});
it("should set payload mapper with config", () => {
const payloadMapper = vi.fn();
const config = { test: true };
adapter.setPayloadMapperConfig(config);
adapter.setPayloadMapper(payloadMapper);
adapter.unstable_payloadMapper({} as any);
expect(payloadMapper).toHaveBeenCalledWith({}, config);
});
it("should set endpoint mapper with config", () => {
const endpointMapper = vi.fn();
const config = { test: true };
adapter.setEndpointMapperConfig(config);
adapter.setEndpointMapper(endpointMapper);
adapter.unstable_endpointMapper("test");
expect(endpointMapper).toHaveBeenCalledWith("test", config);
});
it("should set query params mapper with config", () => {
const queryParamsMapper = vi.fn();
const config = { test: true };
adapter.setQueryParamsMapperConfig(config);
adapter.setQueryParamsMapper(queryParamsMapper);
adapter.unstable_queryParamsMapper({} as any);
expect(queryParamsMapper).toHaveBeenCalledWith({}, config);
});
});
describe("when using defaults setters", () => {
it("should set request defaults", () => {
const defaults = { method: HttpMethods.POST };
const callback = vi.fn().mockReturnValue(defaults);
adapter.setRequestDefaults(callback);
const result = adapter.unstable_getRequestDefaults?.({} as any);
expect(callback).toHaveBeenCalledWith({});
expect(result).toBe(defaults);
});
it("should set adapter defaults", () => {
const defaults = { timeout: 5000 };
const callback = vi.fn().mockReturnValue(defaults);
adapter.setAdapterDefaults(callback);
const result = adapter.unstable_getAdapterDefaults?.({} as any);
expect(callback).toHaveBeenCalledWith({});
expect(result).toBe(defaults);
});
it("should set internal error mapping callback", () => {
const errorMapping = (error: any) => ({ customError: error });
const callback = vi.fn().mockImplementation(errorMapping);
adapter.setInternalErrorMapping(callback);
expect(adapter.unstable_internalErrorMapping).toBe(callback);
});
});
describe("when using default unstable_devtoolsEndpointGetter", () => {
it("should return the endpoint unchanged by default", () => {
const result = adapter.unstable_devtoolsEndpointGetter("/test/endpoint");
expect(result).toBe("/test/endpoint");
});
});
describe("when using unstable_getRequestDefaults", () => {
it("should allow setting and calling unstable_getRequestDefaults", () => {
const defaults = { method: HttpMethods.POST };
const callback = vi.fn().mockReturnValue(defaults);
adapter.setRequestDefaults(callback);
expect(adapter.unstable_getRequestDefaults).toBeDefined();
const result = adapter.unstable_getRequestDefaults!({
endpoint: "/test",
} as any);
expect(callback).toHaveBeenCalledWith({ endpoint: "/test" });
expect(result).toBe(defaults);
});
it("should be undefined by default", () => {
expect(adapter.unstable_getRequestDefaults).toBeUndefined();
});
});
describe("when checking default mappers", () => {
it("should have default header mapper", () => {
expect(adapter.unstable_headerMapper).toBe(getAdapterHeaders);
});
it("should have default payload mapper", () => {
expect(adapter.unstable_payloadMapper).toBe(getAdapterPayload);
});
});
describe("when using fetch", () => {
it("should throw error if adapter is not initialized", async () => {
const newAdapter = new Adapter({
name: "test-adapter",
defaultMethod: HttpMethods.GET,
defaultExtra: {},
systemErrorStatus: 0,
systemErrorExtra: {},
});
const client = createClient({ url: "test" });
const request = client.createRequest()({
endpoint: "test",
});
// hack to prevent logger from being missed (not initialized)
newAdapter.logger = client.loggerManager.initialize(client, "Adapter");
const data = await newAdapter.fetch(request as any, "test");
expect(data.error.message).toStrictEqual(
new RequestProcessingError("Adapter test-adapter is not initialized").message,
);
expect(data.error.description).toStrictEqual(
new RequestProcessingError("Adapter test-adapter is not initialized").description,
);
});
it("should throw error if fetcher is not set", async () => {
const newAdapter = new Adapter({
name: "test-adapter",
defaultMethod: HttpMethods.GET,
defaultExtra: {},
systemErrorStatus: 0,
systemErrorExtra: {},
});
const client = createClient({ url: "test" }).setAdapter(newAdapter);
const request = client.createRequest()({
endpoint: "test",
});
const data = await client.adapter.fetch(request, "test");
expect(data.error.message).toStrictEqual(
new RequestProcessingError("Fetcher for test-adapter adapter is not set").message,
);
expect(data.error.description).toStrictEqual(
new RequestProcessingError("Fetcher for test-adapter adapter is not set").description,
);
});
it("should execute fetcher with adapter bindings", async () => {
const fetcherSpy = vi.fn().mockImplementation(({ onSuccess }) => {
onSuccess({
data: {},
status: 200,
extra: {},
error: null,
});
});
const newAdapter = new Adapter({
name: "test-adapter",
defaultMethod: HttpMethods.GET,
defaultExtra: {},
systemErrorStatus: 0,
systemErrorExtra: {},
}).setFetcher(fetcherSpy);
const client = new Client({ url: "test" });
const request = client.createRequest()({
endpoint: "test",
});
client.setAdapter(newAdapter);
await client.adapter.fetch(request, "test");
expect(fetcherSpy).toHaveBeenCalledWith(
expect.objectContaining({
request: expect.any(Object),
requestId: "test",
}),
);
});
it("should trigger plugins on adapter fetch", async () => {
const fetcherSpy = vi.fn().mockImplementation(({ onSuccess }) => {
onSuccess({
data: {},
status: 200,
extra: {},
error: null,
});
});
const client = new Client({ url: "test" });
const pluginSpy = vi.fn();
client.triggerPlugins = pluginSpy;
const newAdapter = new Adapter({
name: "test-adapter",
defaultMethod: HttpMethods.GET,
defaultExtra: {},
systemErrorStatus: 0,
systemErrorExtra: {},
}).setFetcher(fetcherSpy);
const request = client.createRequest()({
endpoint: "test",
});
client.setAdapter(newAdapter);
await client.adapter.fetch(request, "test");
expect(pluginSpy).toHaveBeenCalledWith("onAdapterFetch", {
adapter: newAdapter,
request,
requestId: "test",
});
});
it("should use mocker when mock is enabled", async () => {
const mockResponse = {
data: { mocked: true } as any,
status: 200,
extra: { headers: {} },
};
const client = new Client({ url: "test" });
const request = client
.createRequest()({
endpoint: "test",
})
.setMock(() => {
return mockResponse;
});
client.requestManager.addAbortController(request.abortKey, "test");
const response = await client.adapter.fetch(request, "test");
expect(response).toStrictEqual(expect.objectContaining(mockResponse));
});
it("should handle errors during fetch execution", async () => {
const testError = new Error("Test error");
const fetcherSpy = vi.fn().mockImplementation(() => {
throw testError;
});
const client = new Client({ url: "test" });
const newAdapter = new Adapter({
name: "test-adapter",
defaultMethod: HttpMethods.GET,
defaultExtra: { extraData: true },
systemErrorStatus: 500,
systemErrorExtra: { systemError: true },
}).setFetcher(fetcherSpy);
const request = client.createRequest()({
endpoint: "test",
});
client.setAdapter(newAdapter);
const response = await client.adapter.fetch(request, "test");
expect(response).toEqual(
expect.objectContaining({
data: null,
error: testError,
status: 500,
extra: { systemError: true },
}),
);
});
it("should use current timestamp if startTime is not set during error", async () => {
const fetcherSpy = vi.fn().mockImplementation(() => {
throw new Error("Test error");
});
const client = new Client({ url: "test" });
const newAdapter = new Adapter({
name: "test-adapter",
defaultMethod: HttpMethods.GET,
defaultExtra: {},
systemErrorStatus: 500,
systemErrorExtra: {},
}).setFetcher(fetcherSpy);
const request = client.createRequest()({
endpoint: "test",
});
client.setAdapter(newAdapter);
const response = await client.adapter.fetch(request, "test");
expect(response.requestTimestamp).toBeNumber();
});
});
});