Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/livePreview/__test__/live-preview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ describe("incoming postMessage", () => {

describe("testing window event listeners", () => {
let addEventListenerMock: any;
let sendInitEvent = vi.fn().mockImplementation(mockLivePreviewInitEventListener);
const sendInitEvent = vi.fn().mockImplementation(mockLivePreviewInitEventListener);
let livePreviewInstance: LivePreview;

beforeEach(() => {
Expand Down
4 changes: 2 additions & 2 deletions src/livePreview/eventManager/postMessageEvent.hooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Config, { setConfigFromParams } from "../../configManager/configManager";
import { ILivePreviewWindowType } from "../../types/types";
import { addParamsToUrl } from "../../utils";
import { addParamsToUrl, isOpeningInTimeline } from "../../utils";
import livePreviewPostMessage from "./livePreviewEventManager";
import { LIVE_PREVIEW_POST_MESSAGE_EVENTS } from "./livePreviewEventManager.constant";
import {
Expand Down Expand Up @@ -95,7 +95,7 @@ export function sendInitializeLivePreviewPostMessageEvent(): void {
// "init message did not contain contentTypeUid or entryUid."
// );
}
if (Config.get().ssr) {
if (Config.get().ssr || isOpeningInTimeline()) {
addParamsToUrl();
}
Config.set("windowType", windowType);
Expand Down
81 changes: 81 additions & 0 deletions src/utils/__test__/addLivePreviewQueryTags.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { describe, test, expect, vi, beforeEach } from "vitest";
import { addLivePreviewQueryTags } from "../addLivePreviewQueryTags";
import { PublicLogger } from "../../logger/logger";

// Mock the logger
vi.mock("../../logger/logger", () => ({
PublicLogger: {
error: vi.fn(),
},
}));

describe("addLivePreviewQueryTags", () => {
beforeEach(() => {
vi.clearAllMocks();
});

test("should return original URL when no live preview parameters in current location", () => {
// This test works with current document.location (likely has no live preview params)
const targetUrl = "http://example.com/target-page";

const result = addLivePreviewQueryTags(targetUrl);

// Should return unchanged since no live preview params in current location
expect(result).toBe(targetUrl);
});

test("should log error and return original link when target URL is invalid", () => {
const targetUrl = "not-a-valid-url-at-all-invalid";

const result = addLivePreviewQueryTags(targetUrl);

expect(PublicLogger.error).toHaveBeenCalledWith("Error while adding live preview to URL");
expect(result).toBe(targetUrl);
});

test("should handle empty string input", () => {
const targetUrl = "";

const result = addLivePreviewQueryTags(targetUrl);

expect(PublicLogger.error).toHaveBeenCalledWith("Error while adding live preview to URL");
expect(result).toBe(targetUrl);
});

test("should handle malformed URLs gracefully", () => {
const targetUrl = "http://";

const result = addLivePreviewQueryTags(targetUrl);

expect(PublicLogger.error).toHaveBeenCalledWith("Error while adding live preview to URL");
expect(result).toBe(targetUrl);
});

test("should handle valid URLs without errors", () => {
const targetUrl = "https://example.com/valid-page";

const result = addLivePreviewQueryTags(targetUrl);

// Should not throw errors and return some result
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});

test("should add live preview query tags to URL with all required query parameters", () => {
const originalUrl =
"http://example.com?live_preview=hash&content_type_uid=ctuid&entry_uid=entryuid";
const expectedUrl =
"http://example.com/?live_preview=hash&content_type_uid=ctuid&entry_uid=entryuid";

global.window = Object.create(window);
Object.defineProperty(window, "location", {
value: {
href: originalUrl,
},
writable: true,
});
const result = addLivePreviewQueryTags(originalUrl);

expect(result).toEqual(expectedUrl);
});
});
260 changes: 231 additions & 29 deletions src/utils/__test__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,254 @@
import { PublicLogger } from "../../logger/logger";
import { addLivePreviewQueryTags, hasWindow } from "../index";
import { hasWindow, addParamsToUrl } from "../index";
import { vi } from "vitest";

vi.mock("../../logger/logger", () => ({
PublicLogger: {
error: vi.fn(),
},
// Mock addLivePreviewQueryTags function
vi.mock("../addLivePreviewQueryTags", () => ({
addLivePreviewQueryTags: vi.fn()
}));

// Import the mocked function after setting up the mock
import { addLivePreviewQueryTags } from "../addLivePreviewQueryTags";

describe("hasWindow() function", () => {
test("must check if window is available", () => {
expect(hasWindow()).toBe(typeof window !== "undefined");
});
});

describe("addLivePreviewQueryTags", () => {
test("should add live preview query tags to URL with all required query parameters", () => {
const originalUrl =
"http://example.com?live_preview=hash&content_type_uid=ctuid&entry_uid=entryuid";
const expectedUrl =
"http://example.com/?live_preview=hash&content_type_uid=ctuid&entry_uid=entryuid";

global.window = Object.create(window);
Object.defineProperty(window, "location", {
value: {
href: originalUrl,
},
writable: true,
describe("addParamsToUrl", () => {
let mockAddEventListener: any;
let mockDocument: any;
let clickHandler: (event: any) => void;

beforeEach(() => {
// Reset all mocks
vi.clearAllMocks();

// Mock window.addEventListener to capture the click handler
mockAddEventListener = vi.fn((event, handler) => {
if (event === "click") {
clickHandler = handler;
}
});

// Setup mock return value for addLivePreviewQueryTags
vi.mocked(addLivePreviewQueryTags).mockImplementation((url) => `${url}?live_preview=test&content_type_uid=test&entry_uid=test`);

// Mock document and window
mockDocument = {
location: {
origin: "https://example.com"
}
};

const result = addLivePreviewQueryTags(originalUrl);
global.window = {
addEventListener: mockAddEventListener,
document: mockDocument
} as any;

global.document = mockDocument as any;
});

afterEach(() => {
vi.restoreAllMocks();
});

expect(result).toEqual(expectedUrl);
test("should add event listener when function is called", () => {
addParamsToUrl();

expect(mockAddEventListener).toHaveBeenCalledWith("click", expect.any(Function));
});

test("should log error and return original link if an error occurs while adding live preview query tags", () => {
const originalUrl =
"http://example.com?live_preview=hash&content_type_uid=ctuid&entry_uid=entryuid";
const expectedLoggedError = "Error while adding live preview to URL";
describe("when clicking on elements", () => {
beforeEach(() => {
addParamsToUrl();
});

test("should handle click directly on anchor tag", () => {
// Create mock anchor element
const mockAnchor = {
href: "https://example.com/page",
closest: vi.fn().mockReturnValue(null),
contains: vi.fn().mockReturnValue(true)
};
mockAnchor.closest.mockReturnValue(mockAnchor); // closest('a') returns self

const mockEvent = {
target: mockAnchor
};

// Trigger the click event
clickHandler(mockEvent);

vi.spyOn(global, "URL").mockImplementation(() => {
throw new Error("Mock error");
expect(mockAnchor.closest).toHaveBeenCalledWith('a');
expect(mockAnchor.contains).toHaveBeenCalledWith(mockAnchor);
expect(addLivePreviewQueryTags).toHaveBeenCalledWith("https://example.com/page");
expect(mockAnchor.href).toBe("https://example.com/page?live_preview=test&content_type_uid=test&entry_uid=test");
});

const result = addLivePreviewQueryTags(originalUrl);
test("should handle click on child element of anchor tag", () => {
// Create mock child element and parent anchor
const mockChild = {
closest: vi.fn()
};

const mockAnchor = {
href: "https://example.com/child-page",
contains: vi.fn().mockReturnValue(true)
};

mockChild.closest.mockReturnValue(mockAnchor); // closest('a') returns parent anchor

const mockEvent = {
target: mockChild
};

// Trigger the click event
clickHandler(mockEvent);

expect(mockChild.closest).toHaveBeenCalledWith('a');
expect(mockAnchor.contains).toHaveBeenCalledWith(mockChild);
expect(addLivePreviewQueryTags).toHaveBeenCalledWith("https://example.com/child-page");
expect(mockAnchor.href).toBe("https://example.com/child-page?live_preview=test&content_type_uid=test&entry_uid=test");
});

test("should not process click when no anchor element is found", () => {
const mockElement = {
closest: vi.fn().mockReturnValue(null)
};

const mockEvent = {
target: mockElement
};

clickHandler(mockEvent);

expect(mockElement.closest).toHaveBeenCalledWith('a');
expect(addLivePreviewQueryTags).not.toHaveBeenCalled();
});

test("should not process click when anchor doesn't contain clicked element", () => {
const mockChild = {
closest: vi.fn()
};

const mockAnchor = {
href: "https://example.com/page",
contains: vi.fn().mockReturnValue(false) // Anchor doesn't contain the clicked element
};

mockChild.closest.mockReturnValue(mockAnchor);

const mockEvent = {
target: mockChild
};

clickHandler(mockEvent);

expect(mockChild.closest).toHaveBeenCalledWith('a');
expect(mockAnchor.contains).toHaveBeenCalledWith(mockChild);
expect(addLivePreviewQueryTags).not.toHaveBeenCalled();
});

expect(PublicLogger.error).toHaveBeenCalledWith(expectedLoggedError);
test("should not process external links", () => {
const mockAnchor = {
href: "https://external-site.com/page",
closest: vi.fn().mockReturnValue(null),
contains: vi.fn().mockReturnValue(true)
};
mockAnchor.closest.mockReturnValue(mockAnchor);

const mockEvent = {
target: mockAnchor
};

expect(result).toEqual(originalUrl);
clickHandler(mockEvent);

expect(addLivePreviewQueryTags).not.toHaveBeenCalled();
expect(mockAnchor.href).toBe("https://external-site.com/page"); // Unchanged
});

test("should not process links that already contain live_preview", () => {
const mockAnchor = {
href: "https://example.com/page?live_preview=existing",
closest: vi.fn().mockReturnValue(null),
contains: vi.fn().mockReturnValue(true)
};
mockAnchor.closest.mockReturnValue(mockAnchor);

const mockEvent = {
target: mockAnchor
};

clickHandler(mockEvent);

expect(addLivePreviewQueryTags).not.toHaveBeenCalled();
expect(mockAnchor.href).toBe("https://example.com/page?live_preview=existing"); // Unchanged
});

test("should not process links without href", () => {
const mockAnchor = {
href: "",
closest: vi.fn().mockReturnValue(null),
contains: vi.fn().mockReturnValue(true)
};
mockAnchor.closest.mockReturnValue(mockAnchor);

const mockEvent = {
target: mockAnchor
};

clickHandler(mockEvent);

expect(addLivePreviewQueryTags).not.toHaveBeenCalled();
expect(mockAnchor.href).toBe(""); // Unchanged
});

test("should handle case when addLivePreviewQueryTags returns empty string", () => {
vi.mocked(addLivePreviewQueryTags).mockReturnValue("");

const originalHref = "https://example.com/page";
const mockAnchor = {
href: originalHref,
closest: vi.fn().mockReturnValue(null),
contains: vi.fn().mockReturnValue(true)
};
mockAnchor.closest.mockReturnValue(mockAnchor);

const mockEvent = {
target: mockAnchor
};

clickHandler(mockEvent);

expect(addLivePreviewQueryTags).toHaveBeenCalledWith(originalHref);
expect(mockAnchor.href).toBe(originalHref); // Falls back to original href when empty string returned
});

test("should handle nested child elements", () => {
// Create deeply nested structure: span > button > a
const mockDeepChild = {
closest: vi.fn()
};

const mockAnchor = {
href: "https://example.com/nested-page",
contains: vi.fn().mockReturnValue(true)
};

mockDeepChild.closest.mockReturnValue(mockAnchor);

const mockEvent = {
target: mockDeepChild
};

clickHandler(mockEvent);

expect(mockDeepChild.closest).toHaveBeenCalledWith('a');
expect(mockAnchor.contains).toHaveBeenCalledWith(mockDeepChild);
expect(addLivePreviewQueryTags).toHaveBeenCalledWith("https://example.com/nested-page");
expect(mockAnchor.href).toBe("https://example.com/nested-page?live_preview=test&content_type_uid=test&entry_uid=test");
});
});
});
Loading
Loading