Skip to content

Commit f3ed34e

Browse files
Merge pull request #282 from contentstack/VE-3862-unit-testing-for-live-preview-sdk-changes
Ve 3862 unit testing for Comment Icon and related code
2 parents ea98824 + 798a6ef commit f3ed34e

2 files changed

Lines changed: 283 additions & 2 deletions

File tree

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import { render, screen, waitFor, fireEvent } from "@testing-library/preact";
2+
3+
import { CslpData } from "../../../cslp/types/cslp.types";
4+
import { ISchemaFieldMap } from "../../utils/types/index.types";
5+
import visualBuilderPostMessage from "../../utils/visualBuilderPostMessage";
6+
import { VisualBuilderPostMessageEvents } from "../../utils/types/postMessage.types";
7+
import { getDiscussionIdByFieldMetaData } from "../../utils/getDiscussionIdByFieldMetaData";
8+
import { Mock, vi } from "vitest";
9+
import CommentIcon, { IActiveDiscussion } from "../CommentIcon";
10+
11+
// Mock dependencies
12+
vi.mock("../../utils/visualBuilderPostMessage", () => ({
13+
default: {
14+
send: vi.fn(),
15+
on: vi.fn(),
16+
},
17+
}));
18+
19+
vi.mock("../../utils/getDiscussionIdByFieldMetaData", () => ({
20+
getDiscussionIdByFieldMetaData: vi
21+
.fn()
22+
.mockResolvedValue({ uid: "discussionId" }),
23+
}));
24+
25+
// Mock data
26+
const mockFieldMetadata: CslpData = {
27+
entry_uid: "entry1",
28+
content_type_uid: "contentType1",
29+
fieldPathWithIndex: "fieldPath1",
30+
cslpValue: "fieldPath1",
31+
locale: "en-us",
32+
variant: "",
33+
fieldPath: "",
34+
instance: {
35+
fieldPathWithIndex: "",
36+
},
37+
multipleFieldMetadata: {
38+
index: 1,
39+
parentDetails: null,
40+
},
41+
};
42+
43+
const mockFieldSchema: ISchemaFieldMap = {
44+
uid: "uid",
45+
display_name: "Label",
46+
mandatory: false,
47+
multiple: false,
48+
non_localizable: false,
49+
unique: false,
50+
data_type: "text",
51+
field_metadata: {
52+
description: "Label field",
53+
default_value: "",
54+
version: 21,
55+
},
56+
format: "string",
57+
error_messages: {
58+
format: "string",
59+
},
60+
};
61+
62+
describe("CommentIcon", () => {
63+
beforeEach(() => {
64+
vi.clearAllMocks();
65+
});
66+
67+
test("renders loading icon when fetching discussion", async () => {
68+
render(
69+
<CommentIcon
70+
fieldMetadata={mockFieldMetadata}
71+
fieldSchema={mockFieldSchema}
72+
/>
73+
);
74+
75+
expect(
76+
screen.getByTestId(
77+
"visual-builder__focused-toolbar__multiple-field-toolbar__comment-button-loading"
78+
)
79+
).toBeInTheDocument();
80+
81+
await waitFor(() => {
82+
expect(getDiscussionIdByFieldMetaData).toHaveBeenCalledWith({
83+
fieldMetadata: mockFieldMetadata,
84+
fieldSchema: mockFieldSchema,
85+
});
86+
});
87+
});
88+
89+
test("renders AddCommentIcon when no discussion exists", async () => {
90+
(getDiscussionIdByFieldMetaData as Mock).mockResolvedValueOnce({
91+
uid: "new",
92+
});
93+
94+
render(
95+
<CommentIcon
96+
fieldMetadata={mockFieldMetadata}
97+
fieldSchema={mockFieldSchema}
98+
/>
99+
);
100+
101+
await waitFor(() => {
102+
expect(
103+
screen.getByTestId(
104+
"visual-builder__focused-toolbar__multiple-field-toolbar__comment-button"
105+
)
106+
).toBeInTheDocument();
107+
expect(screen.getByRole("button")).toHaveAttribute(
108+
"data-tooltip",
109+
"Add comment"
110+
);
111+
});
112+
});
113+
114+
115+
test("renders ReadCommentIcon when a discussion exists", async () => {
116+
const existingDiscussion: IActiveDiscussion = { uid: "discussionId" };
117+
(getDiscussionIdByFieldMetaData as Mock).mockResolvedValueOnce(
118+
existingDiscussion
119+
);
120+
121+
render(
122+
<CommentIcon
123+
fieldMetadata={mockFieldMetadata}
124+
fieldSchema={mockFieldSchema}
125+
/>
126+
);
127+
128+
await waitFor(() => {
129+
expect(
130+
screen.getByTestId(
131+
"visual-builder__focused-toolbar__multiple-field-toolbar__comment-button"
132+
)
133+
).toBeInTheDocument();
134+
});
135+
});
136+
137+
test("should not render when discussionId is null", async () => {
138+
(getDiscussionIdByFieldMetaData as Mock).mockResolvedValueOnce(null);
139+
140+
render(
141+
<CommentIcon
142+
fieldMetadata={mockFieldMetadata}
143+
fieldSchema={mockFieldSchema}
144+
/>
145+
);
146+
await waitFor(() => {
147+
expect(
148+
screen.queryByTestId(
149+
"visual-builder__focused-toolbar__multiple-field-toolbar__comment-button-loading"
150+
)
151+
).not.toBeInTheDocument();
152+
});
153+
});
154+
155+
test("sends OPEN_FIELD_COMMENT_MODAL event on button click", async () => {
156+
render(
157+
<CommentIcon
158+
fieldMetadata={mockFieldMetadata}
159+
fieldSchema={mockFieldSchema}
160+
/>
161+
);
162+
163+
await waitFor(() => {
164+
expect(screen.getByRole("button")).toBeInTheDocument();
165+
});
166+
167+
fireEvent.click(screen.getByRole("button"));
168+
169+
expect(visualBuilderPostMessage?.send).toHaveBeenCalledWith(
170+
VisualBuilderPostMessageEvents.OPEN_FIELD_COMMENT_MODAL,
171+
expect.objectContaining({ fieldMetadata: mockFieldMetadata })
172+
);
173+
});
174+
175+
test("unregisters the event listener on unmount", () => {
176+
const unregisterMock = vi.fn();
177+
(visualBuilderPostMessage?.on as Mock).mockReturnValue({
178+
unregister: unregisterMock,
179+
});
180+
181+
const { unmount } = render(
182+
<CommentIcon
183+
fieldMetadata={mockFieldMetadata}
184+
fieldSchema={mockFieldSchema}
185+
/>
186+
);
187+
unmount();
188+
189+
expect(unregisterMock).toHaveBeenCalled();
190+
});
191+
192+
test("updates activeDiscussion when receiving valid discussion data", async () => {
193+
// Prepare mock discussion event data
194+
const discussionData: IActiveDiscussion = { uid: "existingDiscussionId" };
195+
const receiveData = {
196+
data: {
197+
discussion: discussionData,
198+
entryId: mockFieldMetadata.entry_uid,
199+
contentTypeId: mockFieldMetadata.content_type_uid,
200+
fieldUID: "uid",
201+
fieldPath: mockFieldMetadata.fieldPathWithIndex,
202+
},
203+
};
204+
205+
// Mock the `on` method to simulate receiving a discussion event
206+
(visualBuilderPostMessage?.on as Mock).mockImplementation((event, callback) => {
207+
if (event === VisualBuilderPostMessageEvents.UPDATE_DISCUSSION_ID) {
208+
callback(receiveData);
209+
}
210+
return { unregister: vi.fn() };
211+
});
212+
213+
render(<CommentIcon fieldMetadata={mockFieldMetadata} fieldSchema={mockFieldSchema} />);
214+
215+
// Check if activeDiscussion is updated with received discussion data
216+
await waitFor(() => {
217+
expect(screen.getByTestId("visual-builder__focused-toolbar__multiple-field-toolbar__comment-button")).toBeInTheDocument();
218+
});
219+
});
220+
221+
test("does not update activeDiscussion when entryId does not match", async () => {
222+
// Prepare mock event data with a different entryId
223+
const receiveData = {
224+
data: {
225+
discussion: { uid: "existingDiscussionId" },
226+
entryId: "nonMatchingEntryId",
227+
contentTypeId: mockFieldMetadata.content_type_uid,
228+
fieldUID: "uid",
229+
fieldPath: mockFieldMetadata.fieldPathWithIndex,
230+
},
231+
};
232+
233+
(visualBuilderPostMessage?.on as Mock).mockImplementation((event, callback) => {
234+
if (event === VisualBuilderPostMessageEvents.UPDATE_DISCUSSION_ID) {
235+
callback(receiveData);
236+
}
237+
return { unregister: vi.fn() };
238+
});
239+
240+
render(<CommentIcon fieldMetadata={mockFieldMetadata} fieldSchema={mockFieldSchema} />);
241+
242+
await waitFor(() => {
243+
expect(
244+
screen.queryByTestId("visual-builder__focused-toolbar__multiple-field-toolbar__comment-button")
245+
).not.toBeInTheDocument();
246+
});
247+
});
248+
249+
test("does not update activeDiscussion when fieldPath does not match", async () => {
250+
// Prepare mock event data with a different fieldPath
251+
const receiveData = {
252+
data: {
253+
discussion: { uid: "existingDiscussionId" },
254+
entryId: mockFieldMetadata.entry_uid,
255+
contentTypeId: mockFieldMetadata.content_type_uid,
256+
fieldUID: "uid",
257+
fieldPath: "nonMatchingFieldPath",
258+
},
259+
};
260+
261+
(visualBuilderPostMessage?.on as Mock).mockImplementation((event, callback) => {
262+
if (event === VisualBuilderPostMessageEvents.UPDATE_DISCUSSION_ID) {
263+
callback(receiveData);
264+
}
265+
return { unregister: vi.fn() };
266+
});
267+
268+
render(<CommentIcon fieldMetadata={mockFieldMetadata} fieldSchema={mockFieldSchema} />);
269+
270+
await waitFor(() => {
271+
expect(
272+
screen.queryByTestId("visual-builder__focused-toolbar__multiple-field-toolbar__comment-button")
273+
).not.toBeInTheDocument();
274+
});
275+
});
276+
});

src/visualBuilder/components/__test__/fieldToolbar.test.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { act, cleanup, fireEvent, render, waitFor, screen } from "@testing-library/preact";
1+
import { act, cleanup, fireEvent, render, waitFor, screen, queryByTestId } from "@testing-library/preact";
22
import { CslpData } from "../../../cslp/types/cslp.types";
33
import { FieldSchemaMap } from "../../utils/fieldSchemaMap";
44
import {
@@ -7,7 +7,7 @@ import {
77
} from "../../utils/instanceHandlers";
88
import { ISchemaFieldMap } from "../../utils/types/index.types";
99
import FieldToolbarComponent from "../FieldToolbar";
10-
import { mockMultipleLinkFieldSchema } from "../../../__test__/data/fields";
10+
import { mockMultipleLinkFieldSchema, singleLineFieldSchema } from "../../../__test__/data/fields";
1111
import { asyncRender } from "../../../__test__/utils";
1212
import { VisualBuilderCslpEventDetails } from "../../types/visualBuilder.types";
1313

@@ -16,6 +16,11 @@ vi.mock("../../utils/instanceHandlers", () => ({
1616
handleDeleteInstance: vi.fn(),
1717
}));
1818

19+
//CommentIcon testcases are covered seperatly
20+
vi.mock("../CommentIcon", () => ({
21+
default: vi.fn(() => <div>Comment Icon</div>)
22+
}));
23+
1924
vi.mock("../../utils/visualBuilderPostMessage", async () => {
2025
return {
2126
default: {

0 commit comments

Comments
 (0)