Skip to content
Open
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
4 changes: 2 additions & 2 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ jobs:
- name: "Install Node"
uses: actions/setup-node@v4
with:
node-version: "21.x"
node-version: "22.x"
- name: "Install Deps"
run: npm install
- name: "Test"
run: npx vitest --coverage.enabled true
run: npx vitest run --coverage.enabled true --reporter=verbose
- name: "Report Coverage"
# Set if: always() to also generate the report if tests are failing
# Only works if you set `reportOnFailure: true` in your vite config as specified above
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,9 @@ temp/

.DS_Store

# Test results and profiling reports
test-results.json
junit.xml
test-profile-report.json

# End of https://www.toptal.com/developers/gitignore/api/node,web,vscode
1,915 changes: 1,415 additions & 500 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
"@types/react": "^18.2.57",
"@types/react-dom": "^18.2.19",
"@types/uuid": "^8.3.1",
"@vitest/coverage-v8": "^2.1.2",
"@vitest/ui": "^2.1.2",
"@vitest/coverage-v8": "^3.2.4",
"@vitest/ui": "^3.2.4",
"auto-changelog": "^2.5.0",
"esbuild-plugin-file-path-extensions": "^2.1.0",
"eslint": "^8.57.1",
Expand All @@ -77,7 +77,7 @@
"typedoc": "^0.25.13",
"typescript": "^5.4.5",
"typescript-eslint": "^8.5.0",
"vitest": "^2.1.0"
"vitest": "^3.2.4"
},
"repository": {
"type": "git",
Expand Down
49 changes: 31 additions & 18 deletions src/__test__/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,55 +43,68 @@ export const waitForHoverOutline = async () => {
);
expect(hoverOutline).not.toBeNull();
});
}
export const waitForBuilderSDKToBeInitialized = async (visualBuilderPostMessage: EventManager | undefined) => {
};
export const waitForBuilderSDKToBeInitialized = async (
visualBuilderPostMessage: EventManager | undefined
) => {
await waitFor(() => {
expect(visualBuilderPostMessage?.send).toBeCalledWith(
VisualBuilderPostMessageEvents.INIT,
expect.any(Object)
);
});
}
};
interface WaitForClickActionOptions {
skipWaitForFieldType?: boolean;
}
export const triggerAndWaitForClickAction = async (visualBuilderPostMessage: EventManager | undefined, element: HTMLElement, {skipWaitForFieldType}: WaitForClickActionOptions = {}) => {
export const triggerAndWaitForClickAction = async (
visualBuilderPostMessage: EventManager | undefined,
element: HTMLElement,
{ skipWaitForFieldType }: WaitForClickActionOptions = {}
) => {
await waitForBuilderSDKToBeInitialized(visualBuilderPostMessage);
await act(async () => {
await fireEvent.click(element);
})
if(!skipWaitForFieldType) {
});
if (!skipWaitForFieldType) {
await waitFor(() => {
expect(element).toHaveAttribute("data-cslp-field-type")
})
expect(element).toHaveAttribute("data-cslp-field-type");
});
}
}
};
export const waitForToolbaxToBeVisible = async () => {
await waitFor(() => {
const toolbar = document.querySelector(
".visual-builder__focused-toolbar__field-label-container"
);
expect(toolbar).not.toBeNull();
});
}
};
const defaultRect = {
left: 10,
right: 20,
top: 10,
bottom: 20,
width: 10,
height: 5,
}
export const mockGetBoundingClientRect = (element: HTMLElement, rect = defaultRect) => {
vi.spyOn(element, "getBoundingClientRect").mockImplementation(() => rect as DOMRect);
}
};
export const mockGetBoundingClientRect = (
element: HTMLElement,
rect = defaultRect
) => {
vi.spyOn(element, "getBoundingClientRect").mockImplementation(
() => rect as DOMRect
);
};
export const getElementBytestId = (testId: string) => {
return document.querySelector(`[data-testid="${testId}"]`);
}
export const asyncRender: (componentChild: ComponentChild) => ReturnType<typeof render> = async (...args) => {
let returnValue: ReturnType<typeof render>;
};
export const asyncRender: (
componentChild: ComponentChild
) => Promise<ReturnType<typeof render>> = async (...args) => {
let returnValue!: ReturnType<typeof render>;
await act(async () => {
returnValue = render(...args);
});
return returnValue;
}
};
100 changes: 74 additions & 26 deletions src/livePreview/__test__/live-preview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
import { act, fireEvent, waitFor } from "@testing-library/preact";
import crypto from "crypto";
import { vi } from "vitest";
import { sleep } from "../../__test__/utils";
import { getDefaultConfig } from "../../configManager/config.default";
import Config from "../../configManager/configManager";
import { PublicLogger } from "../../logger/logger";
import { ILivePreviewWindowType } from "../../types/types";
import { addLivePreviewQueryTags } from '../../utils/addLivePreviewQueryTags';
import { addLivePreviewQueryTags } from "../../utils/addLivePreviewQueryTags";
import livePreviewPostMessage from "../eventManager/livePreviewEventManager";
import { LIVE_PREVIEW_POST_MESSAGE_EVENTS } from "../eventManager/livePreviewEventManager.constant";
import {
Expand Down Expand Up @@ -43,7 +42,6 @@ vi.mock("../../visualBuilder/utils/visualBuilderPostMessage", async () => {
};
});


Object.defineProperty(globalThis, "crypto", {
value: {
getRandomValues: (arr: Array<any>) => crypto.randomBytes(arr.length),
Expand Down Expand Up @@ -349,13 +347,27 @@ describe("incoming postMessage", () => {
});

livePreviewPostMessage?.destroy({ soft: true });

// Track when INIT completes
let initCompleted = false;
livePreviewPostMessage?.on(
LIVE_PREVIEW_POST_MESSAGE_EVENTS.INIT,
mockLivePreviewInitEventListener
() => {
const result = mockLivePreviewInitEventListener();
initCompleted = true;
return result;
}
);

const livePreview = new LivePreview();
await sleep();

// Wait for INIT event to complete and event listeners to be registered
await waitFor(
() => {
expect(initCompleted).toBe(true);
},
{ timeout: 3000 }
);

// set user onChange function
const userOnChange = vi.fn();
Expand Down Expand Up @@ -386,7 +398,13 @@ describe("incoming postMessage", () => {
}

new LivePreview();
await sleep();

// Wait for async init event to be processed
await waitFor(() => {
expect(Config.get().stackDetails.contentTypeUid).toBe(
"contentTypeUid"
);
});

expect(Config.get().stackDetails).toMatchObject({
apiKey: "",
Expand All @@ -397,42 +415,69 @@ describe("incoming postMessage", () => {
});

test("should navigate forward, backward and reload page on history call", async () => {
// Track when INIT completes
let initCompleted = false;
livePreviewPostMessage?.destroy({ soft: true });
livePreviewPostMessage?.on(
LIVE_PREVIEW_POST_MESSAGE_EVENTS.INIT,
() => {
const result = mockLivePreviewInitEventListener();
initCompleted = true;
return result;
}
);

new LivePreview();
await sleep();

// Wait for INIT to complete and event listeners to be registered
await waitFor(
() => {
expect(initCompleted).toBe(true);
},
{ timeout: 3000 }
);

vi.spyOn(window.history, "forward");
vi.spyOn(window.history, "back");
vi.spyOn(window.history, "go").mockImplementation(() => {});

// for forward
livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
type: "forward",
} as HistoryLivePreviewPostMessageEventData);
await sleep(0);
await livePreviewPostMessage?.send(
LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY,
{
type: "forward",
} as HistoryLivePreviewPostMessageEventData
);

expect(window.history.forward).toHaveBeenCalled();

// for back
livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
type: "backward",
} as HistoryLivePreviewPostMessageEventData);
await livePreviewPostMessage?.send(
LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY,
{
type: "backward",
} as HistoryLivePreviewPostMessageEventData
);

await sleep(0);
expect(window.history.back).toHaveBeenCalled();

// for reload
livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
type: "reload",
} as HistoryLivePreviewPostMessageEventData);
await livePreviewPostMessage?.send(
LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY,
{
type: "reload",
} as HistoryLivePreviewPostMessageEventData
);

await sleep(0);
expect(window.history.go).toHaveBeenCalled();
});
});

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

beforeEach(() => {
Expand Down Expand Up @@ -473,7 +518,9 @@ describe("testing window event listeners", () => {
});

test("should attach a load event to call requestDataSync if document is not yet loaded", () => {
const readyState = vi.spyOn(document, 'readyState', 'get').mockReturnValue('loading');
const readyState = vi
.spyOn(document, "readyState", "get")
.mockReturnValue("loading");

Config.replace({
enable: true,
Expand All @@ -488,7 +535,6 @@ describe("testing window event listeners", () => {
readyState.mockRestore();
});
test("should handle link click event if ssr is set to true", async () => {

Config.replace({
enable: true,
ssr: true,
Expand All @@ -500,14 +546,16 @@ describe("testing window event listeners", () => {

document.body.appendChild(targetElement);
await act(async () => {
livePreviewInstance = new LivePreview();
livePreviewInstance = new LivePreview();
});
await waitFor(() => {
expect(sendInitEvent).toBeCalled();
})
});
await waitFor(() => {
expect(Config.get().stackDetails.contentTypeUid).toBe('contentTypeUid');
})
expect(Config.get().stackDetails.contentTypeUid).toBe(
"contentTypeUid"
);
});
await act(async () => {
fireEvent.click(targetElement);
});
Expand Down
51 changes: 30 additions & 21 deletions src/visualBuilder/__test__/click/fields/boolean.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, waitFor, screen } from "@testing-library/preact";
import { waitFor, screen } from "@testing-library/preact";
import "@testing-library/jest-dom";
import { getFieldSchemaMap } from "../../../../__test__/data/fieldSchemaMap";
import Config from "../../../../configManager/configManager";
Expand Down Expand Up @@ -126,22 +126,32 @@ describe("When an element is clicked in visual builder mode", () => {
visualBuilder.destroy();
});

test("should have outline", () => {
expect(booleanField.classList.contains("cslp-edit-mode"));
test("should have field type attribute set", () => {
// Field type is set during click - this is what actually happens
expect(booleanField).toHaveAttribute(
"data-cslp-field-type",
"boolean"
);
});

test("should have an overlay", () => {
test("should have an overlay wrapper rendered", () => {
// Overlay wrapper is rendered (not checking for 'visible' class as it's conditional)
const overlayWrapper = document.querySelector(
".visual-builder__overlay__wrapper"
);
expect(overlayWrapper).not.toBeNull();

// Check that overlay elements exist
const overlay = document.querySelector(".visual-builder__overlay");
expect(overlay!.classList.contains("visible"));
expect(overlay).not.toBeNull();
});

test("should have a field path dropdown", async () => {
await waitFor(async () => {
const toolbar = await screen.findByTestId(
"mock-field-label-wrapper"
);
expect(toolbar).toBeInTheDocument();
});
test("should have a field path dropdown", () => {
// Component is already rendered from beforeAll setup
const toolbar = screen.getByTestId(
"mock-field-label-wrapper"
);
expect(toolbar).toBeInTheDocument();
});

test("should contain a data-cslp-field-type attribute", async () => {
Expand All @@ -158,15 +168,14 @@ describe("When an element is clicked in visual builder mode", () => {
});
});

test.skip("should send a focus field message to parent", async () => {
await waitFor(() => {
expect(visualBuilderPostMessage?.send).toBeCalledWith(
VisualBuilderPostMessageEvents.FOCUS_FIELD,
{
DOMEditStack: getDOMEditStack(booleanField),
}
);
});
test("should send a focus field message to parent", () => {
// Mock function calls are tracked synchronously
expect(visualBuilderPostMessage?.send).toBeCalledWith(
VisualBuilderPostMessageEvents.FOCUS_FIELD,
{
DOMEditStack: getDOMEditStack(booleanField),
}
);
});
});
});
Loading
Loading