Skip to content

Commit 84f8a26

Browse files
authored
Vitest 3 optimizing (#524)
1 parent 9000f02 commit 84f8a26

58 files changed

Lines changed: 6503 additions & 5817 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/unit-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ jobs:
1717
- name: "Install Node"
1818
uses: actions/setup-node@v4
1919
with:
20-
node-version: "21.x"
20+
node-version: "22.x"
2121
- name: "Install Deps"
2222
run: npm install
2323
- name: "Test"
24-
run: npx vitest --coverage.enabled true
24+
run: npm run test:coverage
2525
- name: "Report Coverage"
2626
# Set if: always() to also generate the report if tests are failing
2727
# Only works if you set `reportOnFailure: true` in your vite config as specified above

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,10 @@ temp/
138138

139139
.DS_Store
140140

141+
# Test results and profiling reports
142+
test-results.json
143+
junit.xml
144+
test-reports/
145+
test-profile-report.json
141146

142147
# End of https://www.toptal.com/developers/gitignore/api/node,web,vscode

package-lock.json

Lines changed: 1415 additions & 500 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"sideEffects": false,
2626
"scripts": {
2727
"build": "NODE_OPTIONS='--max-old-space-size=16384' tsup",
28-
"test": "vitest",
28+
"test": "vitest --run",
29+
"test:watch": "vitest",
2930
"test:once": "vitest run",
3031
"test:coverage": "vitest --coverage",
3132
"dev": "NODE_OPTIONS='--max-old-space-size=16384' tsup --watch",
@@ -57,8 +58,8 @@
5758
"@types/react": "^18.2.57",
5859
"@types/react-dom": "^18.2.19",
5960
"@types/uuid": "^8.3.1",
60-
"@vitest/coverage-v8": "^2.1.2",
61-
"@vitest/ui": "^2.1.2",
61+
"@vitest/coverage-v8": "^3.2.4",
62+
"@vitest/ui": "^3.2.4",
6263
"auto-changelog": "^2.5.0",
6364
"esbuild-plugin-file-path-extensions": "^2.1.0",
6465
"eslint": "^8.57.1",
@@ -77,7 +78,7 @@
7778
"typedoc": "^0.25.13",
7879
"typescript": "^5.4.5",
7980
"typescript-eslint": "^8.5.0",
80-
"vitest": "^2.1.0"
81+
"vitest": "^3.2.4"
8182
},
8283
"repository": {
8384
"type": "git",

src/__test__/utils.ts

Lines changed: 108 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,62 +36,146 @@ export async function sleep(waitTimeInMs = 100): Promise<void> {
3636
return new Promise((resolve) => setTimeout(resolve, waitTimeInMs));
3737
}
3838

39-
export const waitForHoverOutline = async () => {
40-
await waitFor(() => {
41-
const hoverOutline = document.querySelector(
42-
"[data-testid='visual-builder__hover-outline'][style]"
43-
);
44-
expect(hoverOutline).not.toBeNull();
45-
});
46-
}
47-
export const waitForBuilderSDKToBeInitialized = async (visualBuilderPostMessage: EventManager | undefined) => {
39+
export const waitForHoverOutline = async (options?: {
40+
timeout?: number;
41+
interval?: number;
42+
}) => {
43+
// First, wait for the outline element to exist (faster check)
44+
await waitFor(
45+
() => {
46+
const hoverOutline = document.querySelector(
47+
"[data-testid='visual-builder__hover-outline']"
48+
);
49+
expect(hoverOutline).not.toBeNull();
50+
},
51+
{
52+
timeout: options?.timeout ?? 2000,
53+
interval: options?.interval ?? 5, // Faster polling: 5ms default
54+
}
55+
);
56+
57+
// Then wait for style attribute to be set (more specific check)
58+
await waitFor(
59+
() => {
60+
const hoverOutline = document.querySelector(
61+
"[data-testid='visual-builder__hover-outline']"
62+
) as HTMLElement;
63+
expect(hoverOutline).not.toBeNull();
64+
// Check if style has meaningful values (not empty)
65+
const hasStyle =
66+
hoverOutline?.style &&
67+
(hoverOutline.style.top ||
68+
hoverOutline.style.left ||
69+
hoverOutline.style.width ||
70+
hoverOutline.style.height);
71+
expect(hasStyle).toBeTruthy();
72+
},
73+
{
74+
timeout: options?.timeout ?? 2000,
75+
interval: options?.interval ?? 5, // Faster polling: 5ms default
76+
}
77+
);
78+
};
79+
80+
export const waitForBuilderSDKToBeInitialized = async (
81+
visualBuilderPostMessage: EventManager | undefined
82+
) => {
4883
await waitFor(() => {
4984
expect(visualBuilderPostMessage?.send).toBeCalledWith(
5085
VisualBuilderPostMessageEvents.INIT,
5186
expect.any(Object)
5287
);
5388
});
54-
}
89+
};
5590
interface WaitForClickActionOptions {
5691
skipWaitForFieldType?: boolean;
5792
}
58-
export const triggerAndWaitForClickAction = async (visualBuilderPostMessage: EventManager | undefined, element: HTMLElement, {skipWaitForFieldType}: WaitForClickActionOptions = {}) => {
93+
export const triggerAndWaitForClickAction = async (
94+
visualBuilderPostMessage: EventManager | undefined,
95+
element: HTMLElement,
96+
{ skipWaitForFieldType }: WaitForClickActionOptions = {}
97+
) => {
5998
await waitForBuilderSDKToBeInitialized(visualBuilderPostMessage);
6099
await act(async () => {
61100
await fireEvent.click(element);
62-
})
63-
if(!skipWaitForFieldType) {
101+
});
102+
if (!skipWaitForFieldType) {
64103
await waitFor(() => {
65-
expect(element).toHaveAttribute("data-cslp-field-type")
66-
})
104+
expect(element).toHaveAttribute("data-cslp-field-type");
105+
});
67106
}
68-
}
107+
};
69108
export const waitForToolbaxToBeVisible = async () => {
70109
await waitFor(() => {
71110
const toolbar = document.querySelector(
72111
".visual-builder__focused-toolbar__field-label-container"
73112
);
74113
expect(toolbar).not.toBeNull();
75114
});
76-
}
115+
};
116+
117+
export const waitForCursorToBeVisible = async (options?: {
118+
timeout?: number;
119+
interval?: number;
120+
}) => {
121+
await waitFor(
122+
() => {
123+
const customCursor = document.querySelector(
124+
`[data-testid="visual-builder__cursor"]`
125+
);
126+
if (!customCursor) throw new Error("Cursor not found");
127+
expect(customCursor.classList.contains("visible")).toBeTruthy();
128+
},
129+
{
130+
timeout: options?.timeout ?? 2000, // Default 2s timeout for cursor to be visible
131+
interval: options?.interval ?? 10, // Faster polling: 10ms default
132+
}
133+
);
134+
};
135+
136+
export const waitForCursorIcon = async (
137+
icon: string,
138+
options?: { timeout?: number; interval?: number }
139+
) => {
140+
await waitFor(
141+
() => {
142+
const customCursor = document.querySelector(
143+
`[data-testid="visual-builder__cursor"]`
144+
);
145+
if (!customCursor) throw new Error("Cursor not found");
146+
expect(customCursor).toHaveAttribute("data-icon", icon);
147+
},
148+
{
149+
timeout: options?.timeout ?? 1000, // Reduced from 2s to 1s - mocks resolve immediately
150+
interval: options?.interval ?? 10, // Faster polling: 10ms default
151+
}
152+
);
153+
};
77154
const defaultRect = {
78155
left: 10,
79156
right: 20,
80157
top: 10,
81158
bottom: 20,
82159
width: 10,
83160
height: 5,
84-
}
85-
export const mockGetBoundingClientRect = (element: HTMLElement, rect = defaultRect) => {
86-
vi.spyOn(element, "getBoundingClientRect").mockImplementation(() => rect as DOMRect);
87-
}
161+
};
162+
export const mockGetBoundingClientRect = (
163+
element: HTMLElement,
164+
rect = defaultRect
165+
) => {
166+
vi.spyOn(element, "getBoundingClientRect").mockImplementation(
167+
() => rect as DOMRect
168+
);
169+
};
88170
export const getElementBytestId = (testId: string) => {
89171
return document.querySelector(`[data-testid="${testId}"]`);
90-
}
91-
export const asyncRender: (componentChild: ComponentChild) => ReturnType<typeof render> = async (...args) => {
172+
};
173+
export const asyncRender: (
174+
componentChild: ComponentChild
175+
) => ReturnType<typeof render> = async (...args) => {
92176
let returnValue: ReturnType<typeof render>;
93177
await act(async () => {
94178
returnValue = render(...args);
95179
});
96180
return returnValue;
97-
}
181+
};

src/livePreview/__test__/live-preview.test.ts

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import { act, fireEvent, waitFor } from "@testing-library/preact";
66
import crypto from "crypto";
77
import { vi } from "vitest";
8-
import { sleep } from "../../__test__/utils";
98
import { getDefaultConfig } from "../../configManager/config.default";
109
import Config from "../../configManager/configManager";
1110
import { PublicLogger } from "../../logger/logger";
@@ -54,12 +53,6 @@ const TITLE_CSLP_TAG = "content-type-1.entry-uid-1.en-us.field-title";
5453
const DESC_CSLP_TAG = "content-type-2.entry-uid-2.en-us.field-description";
5554
const LINK_CSLP_TAG = "content-type-3.entry-uid-3.en-us.field-link";
5655

57-
global.ResizeObserver = vi.fn().mockImplementation(() => ({
58-
observe: vi.fn(),
59-
unobserve: vi.fn(),
60-
disconnect: vi.fn(),
61-
}));
62-
6356
describe("cslp tooltip", () => {
6457
beforeEach(() => {
6558
Config.reset();
@@ -349,13 +342,27 @@ describe("incoming postMessage", () => {
349342
});
350343

351344
livePreviewPostMessage?.destroy({ soft: true });
345+
346+
// Track when INIT completes
347+
let initCompleted = false;
352348
livePreviewPostMessage?.on(
353349
LIVE_PREVIEW_POST_MESSAGE_EVENTS.INIT,
354-
mockLivePreviewInitEventListener
350+
() => {
351+
const result = mockLivePreviewInitEventListener();
352+
initCompleted = true;
353+
return result;
354+
}
355355
);
356356

357357
const livePreview = new LivePreview();
358-
await sleep();
358+
359+
// Wait for INIT event to complete and event listeners to be registered
360+
await waitFor(
361+
() => {
362+
expect(initCompleted).toBe(true);
363+
},
364+
{ timeout: 3000 }
365+
);
359366

360367
// set user onChange function
361368
const userOnChange = vi.fn();
@@ -386,7 +393,13 @@ describe("incoming postMessage", () => {
386393
}
387394

388395
new LivePreview();
389-
await sleep();
396+
397+
// Wait for async init event to be processed
398+
await waitFor(() => {
399+
expect(Config.get().stackDetails.contentTypeUid).toBe(
400+
"contentTypeUid"
401+
);
402+
});
390403

391404
expect(Config.get().stackDetails).toMatchObject({
392405
apiKey: "",
@@ -397,35 +410,51 @@ describe("incoming postMessage", () => {
397410
});
398411

399412
test("should navigate forward, backward and reload page on history call", async () => {
413+
// Track when INIT completes
414+
let initCompleted = false;
415+
livePreviewPostMessage?.destroy({ soft: true });
416+
livePreviewPostMessage?.on(
417+
LIVE_PREVIEW_POST_MESSAGE_EVENTS.INIT,
418+
() => {
419+
const result = mockLivePreviewInitEventListener();
420+
initCompleted = true;
421+
return result;
422+
}
423+
);
424+
400425
new LivePreview();
401-
await sleep();
426+
427+
// Wait for INIT to complete and event listeners to be registered
428+
await waitFor(
429+
() => {
430+
expect(initCompleted).toBe(true);
431+
},
432+
{ timeout: 3000 }
433+
);
402434

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

407439
// for forward
408-
livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
440+
await livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
409441
type: "forward",
410442
} as HistoryLivePreviewPostMessageEventData);
411-
await sleep(0);
412443

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

415446
// for back
416-
livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
447+
await livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
417448
type: "backward",
418449
} as HistoryLivePreviewPostMessageEventData);
419450

420-
await sleep(0);
421451
expect(window.history.back).toHaveBeenCalled();
422452

423453
// for reload
424-
livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
454+
await livePreviewPostMessage?.send(LIVE_PREVIEW_POST_MESSAGE_EVENTS.HISTORY, {
425455
type: "reload",
426456
} as HistoryLivePreviewPostMessageEventData);
427457

428-
await sleep(0);
429458
expect(window.history.go).toHaveBeenCalled();
430459
});
431460
});

src/livePreview/editButton/__test__/editButtonAction.test.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ const VARIANT_TITLE_CSLP_TAG =
2323
const DESC_CSLP_TAG = "content-type-2.entry-uid-2.en-us.field-description";
2424
const LINK_CSLP_TAG = "content-type-3.entry-uid-3.en-us.field-link";
2525

26-
global.ResizeObserver = vi.fn().mockImplementation(() => ({
27-
observe: vi.fn(),
28-
unobserve: vi.fn(),
29-
disconnect: vi.fn(),
30-
}));
31-
3226
describe("cslp tooltip", () => {
3327
beforeEach(() => {
3428
Config.reset();

src/preview/__test__/contentstack-live-preview-HOC.test.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ Object.defineProperty(globalThis, "crypto", {
2222
},
2323
});
2424

25-
global.ResizeObserver = vi.fn().mockImplementation(() => ({
26-
observe: vi.fn(),
27-
unobserve: vi.fn(),
28-
disconnect: vi.fn(),
29-
}));
30-
3125
describe("Live Preview HOC init", () => {
3226
beforeEach(() => {
3327
Config.reset();

0 commit comments

Comments
 (0)