UNPKG

@serenity-is/corelib

Version:
411 lines (348 loc) 13.5 kB
import { mockFetch, unmockFetch } from "../test/mocks"; import { getActiveRequests, getCookie, getServiceOptions, isSameOrigin, requestFinished, requestStarting, resolveServiceUrl, resolveUrl, serviceCall } from "./services"; import { ServiceError, ServiceOptions, ServiceResponse } from "./servicetypes"; vi.mock("./config", () => ({ __esModule: true, Config: { applicationPath: "/app/" } })); describe("resolveUrl", () => { it("should resolve URL that starts with ~/", () => { const url = "~/test"; const resolvedUrl = resolveUrl(url); expect(resolvedUrl).toBe("/app/test"); }); it("should return the same URL if it starts with /", () => { const url = "/test"; const resolvedUrl = resolveUrl(url); expect(resolvedUrl).toBe(url); }); it("should return the same URL if it contains ://", () => { const url = "http://example.com/test"; const resolvedUrl = resolveUrl(url); expect(resolvedUrl).toBe(url); }); }); describe("resolveServiceUrl", () => { it("should return URL as is if URL is undefined", () => { const url: string = undefined; const resolvedUrl = resolveServiceUrl(url); expect(resolvedUrl).toBeUndefined(); }); it("should return URL as is if URL is null", () => { const url: string = null; const resolvedUrl = resolveServiceUrl(url); expect(resolvedUrl).toBeNull(); }); it("should return URL as is if URL is empty", () => { const url: string = ""; const resolvedUrl = resolveServiceUrl(url); expect(resolvedUrl).toBe(""); }); it("should return URL as is if URL starts with /", () => { const url: string = null; const resolvedUrl = resolveServiceUrl(url); expect(resolvedUrl).toBe(url); }); it("should use resolveUrl if URL starts with ~/", () => { const url = "~/test"; const resolvedUrl = resolveServiceUrl(url); expect(resolvedUrl).toBe("/app/test"); }); it("should return URL as is if URL contains ://", () => { const url = "http://example.com/test"; const resolvedUrl = resolveServiceUrl(url); expect(resolvedUrl).toBe(url); }); it("should resolve service URL if does not start with / or ~/ and does not contain ://", () => { const url = "test"; const resolvedUrl = resolveServiceUrl(url); expect(resolvedUrl).toBe("/app/Services/test"); }); }); describe("getCookie", () => { it("should return the value of the specified cookie", () => { document.cookie = "cookie1=value1"; document.cookie = "cookie2=value2"; const cookieValue = getCookie("cookie2"); expect(cookieValue).toBe("value2"); }); it("should return an empty string if the specified cookie does not exist", () => { const cookieValue = getCookie("nonexistentCookie"); expect(cookieValue).toBeUndefined(); }); it("should use jQuery.cookie plugin if available", () => { const jQuery = (window as any).jQuery = vi.fn(); try { const cookieSpy = (jQuery as any).cookie = vi.fn().mockReturnValue("value"); const cookieValue = getCookie("cookie"); expect(cookieValue).toBe("value"); expect(cookieSpy).toHaveBeenCalledWith("cookie"); } finally { delete (jQuery as any).cookie; } }); }); describe("isSameOrigin", () => { it("should return true if the URL has the same origin as the current page", () => { const url = window.location.protocol + "//" + window.location.host + "/test"; const isSame = isSameOrigin(url); expect(isSame).toBe(true); }); it("should return false if the URL has a different origin than the current page", () => { const url = "http://example.com/test"; const isSame = isSameOrigin(url); expect(isSame).toBe(false); }); it("should return false if the URL has the same origin as the current page with a different port", () => { const url = window.location.protocol + "//" + window.location.hostname + ":9991/test"; const isSame = isSameOrigin(url); expect(isSame).toBe(false); }); it("should return false if the URL has the same origin as the current page with a different protocol", () => { const url = "https://" + window.location.hostname + ":4431/test"; const isSame = isSameOrigin(url); expect(isSame).toBe(false); }); it("should return false if the URL has the same origin as the current page with a different subdomain", () => { const url = window.location.protocol + "//" + "subdomain." + window.location.hostname + "/test"; const isSame = isSameOrigin(url); expect(isSame).toBe(false); }); }); describe("async serviceCall", () => { beforeEach(() => { mockFetch(); }); afterEach(() => { unmockFetch(); }); it("should throw if fetch is not available", async () => { const fetch = window.fetch; window.fetch = null; try { await expect(async () => serviceCall({ url: "http://localhost:1234" })).rejects.toThrow("The fetch method is not available"); } finally { window.fetch = fetch; } }); it("should make a successful service call and return the response", async () => { const response = { data: "Success" }; const options = { url: "/test" }; const mockSpy = mockFetch({ "/test": () => response }); const result = await serviceCall(options); expect(result).toEqual(response); expect(mockSpy.requests.length).toBe(1); expect(mockSpy.requests[0].url).toBe("/test"); }); it("should handle an error response and throw an error", async () => { let error: ServiceError; const response: ServiceResponse = { Error: { Message: "Error" } }; const options: ServiceOptions<any> = { url: "/test", onError: (resp, inf) => { error = resp?.Error; } }; const mockSpy = mockFetch({ "/test": (info) => { info.status = 403; info.statusText = "Some error" return response; } }); const promise = serviceCall(options); const old = window.alert; window.alert = () => {}; try { await expect(promise).rejects.toThrow("Service fetch to '/test' resulted in HTTP 403 error: Some error!"); } finally { window.alert = old; } expect(mockSpy.requests.length).toBe(1); expect(mockSpy.requests[0].url).toBe("/test"); expect(mockSpy.requests[0].status).toBe(403); expect(error).toStrictEqual(response.Error); }); }); describe("getServiceOptions", () => { it("should set default options if not provided", () => { const options = getServiceOptions({}); expect(options.allowRedirect).toBe(true); expect(options.async).toBe(true); expect(options.blockUI).toBe(true); expect(options.method).toBe("POST"); expect(options.headers["Accept"]).toBe("application/json"); expect(options.headers["Content-Type"]).toBe("application/json"); }); it("should override default options if provided", () => { const customOptions = { allowRedirect: false, async: false, blockUI: false, method: "GET", headers: { "Accept": "text/plain", "Content-Type": "text/plain" } }; const options = getServiceOptions(customOptions); expect(options.allowRedirect).toBe(false); expect(options.async).toBe(false); expect(options.blockUI).toBe(false); expect(options.method).toBe("GET"); expect(options.headers["Accept"]).toBe("text/plain"); expect(options.headers["Content-Type"]).toBe("text/plain"); }); it("should resolve service URL if service is provided", () => { const options = getServiceOptions({ service: "testService" }); expect(options.url).toBe("/app/Services/testService"); }); it("should resolve URL if url is provided", () => { const options = getServiceOptions({ url: "~/testUrl" }); expect(options.url).toBe("/app/testUrl"); }); it("should add CSRF token header if same origin", () => { document.cookie = "CSRF-TOKEN=testToken"; const options = getServiceOptions({ url: window.location.href }); expect(options.headers["X-CSRF-TOKEN"]).toBe("testToken"); }); it("should not add CSRF token header if different origin", () => { document.cookie = "CSRF-TOKEN=testToken"; const options = getServiceOptions({ url: "http://example.com" }); expect(options.headers["X-CSRF-TOKEN"]).toBeUndefined(); }); }); describe("requestStarting", () => { afterEach(() => { vi.restoreAllMocks(); }); it("should increment activeRequests", () => { const initialActiveRequests = getActiveRequests(); requestStarting(); try { expect(getActiveRequests()).toBe(initialActiveRequests + 1); } finally { requestFinished(); } }); it("should trigger ajaxStart event if jQuery is available and $.active is 0", () => { const jQuery: any = (window as any).jQuery = vi.fn(); try { const eventTriggerSpy = vi.fn(); jQuery.active = 0; jQuery.event = { trigger: eventTriggerSpy }; requestStarting(); try { expect(jQuery.active).toBe(1); expect(eventTriggerSpy).toHaveBeenCalledWith("ajaxStart"); } finally { requestFinished(); } } finally { delete (window as any).jQuery; } }); it("should not trigger ajaxStart event if jQuery is available and $.active is not 0", () => { const jQuery: any = (window as any).jQuery = vi.fn(); const eventTriggerSpy = vi.fn(); jQuery.active = 1; jQuery.event = { trigger: eventTriggerSpy }; try { requestStarting(); try { expect(jQuery.active).toBe(2); expect(eventTriggerSpy).not.toHaveBeenCalled(); } finally { requestFinished(); } } finally { delete (window as any).jQuery } }); it("should trigger ajaxStart event if jQuery is not available and activeRequests is 1", () => { const eventDispatchSpy = vi.spyOn(document, "dispatchEvent"); requestStarting(); try { expect(eventDispatchSpy).toHaveBeenCalledWith(new Event("ajaxStart")); } finally { requestFinished(); } }); it("should not trigger ajaxStart event if jQuery is not available and activeRequests is not 1", () => { requestStarting(); try { const eventDispatchSpy = vi.spyOn(document, "dispatchEvent"); requestStarting(); try { expect(eventDispatchSpy).not.toHaveBeenCalled(); } finally { requestFinished(); } } finally { requestFinished(); } }); }); describe("synchronous serviceCall", () => { beforeEach(() => { mockFetch(); }); afterEach(() => { unmockFetch(); }); it("should make a successful service call and return the response", async () => { const response = { data: "Success" }; const options = { url: "/test", async: false }; const mockSpy = mockFetch({ "/test": () => response }); const result = await serviceCall(options); expect(result).toEqual(response); expect(mockSpy.requests.length).toBe(1); expect(mockSpy.requests[0].url).toBe("/test"); expect(mockSpy.requests[0].isXHR).toBe(true); }); it("should handle an error response and throw an error", async () => { let error: ServiceError; const response: ServiceResponse = { Error: { Message: "Error" } }; const options: ServiceOptions<any> = { async: false, url: "/test", onError: (resp, inf) => { error = resp?.Error; } }; const mockSpy = mockFetch({ "/test": (info) => { info.status = 403; info.statusText = "Some error" return response; } }); const old = window.alert; window.alert = () => {}; try { const promise = serviceCall(options); await expect(promise).rejects.toThrow("Service call to '/test' resulted in HTTP 403 error: Some error!"); } finally { window.alert = old; } expect(mockSpy.requests.length).toBe(1); expect(mockSpy.requests[0].url).toBe("/test"); expect(mockSpy.requests[0].status).toBe(403); expect(mockSpy.requests[0].isXHR).toBe(true); expect(error).toStrictEqual(response.Error); }); });