Skip to content

Commit 1ba0b65

Browse files
committed
test: added test cases
1 parent 9ee8f8e commit 1ba0b65

5 files changed

Lines changed: 304 additions & 13 deletions

File tree

src/visualBuilder/eventManager/__test__/useVariantsPostMessageEvent.spec.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { FieldSchemaMap } from "../../../visualBuilder/utils/fieldSchemaMap";
2121
import { visualBuilderStyles } from "../../../visualBuilder/visualBuilder.style";
2222
import visualBuilderPostMessage from "../../../visualBuilder/utils/visualBuilderPostMessage";
2323
import { EventManager } from "@contentstack/advanced-post-message";
24+
import * as cslpdata from "../../../cslp/cslpdata";
2425

2526
const mockVisualBuilderPostMessage =
2627
visualBuilderPostMessage as MockedObject<EventManager>;
@@ -339,7 +340,7 @@ describe("addVariantFieldClass", () => {
339340
const variantUid = "variant-123";
340341
const highlightVariantFields = true;
341342

342-
addVariantFieldClass(variantUid, highlightVariantFields);
343+
addVariantFieldClass(variantUid, highlightVariantFields, []);
343344

344345
// Verify querySelectorAll was called with the correct selector
345346
expect(mockQuerySelectorAll).toHaveBeenCalledWith("[data-cslp]");
@@ -370,7 +371,7 @@ describe("addVariantFieldClass", () => {
370371
const variantUid = "variant-123";
371372
const highlightVariantFields = false;
372373

373-
addVariantFieldClass(variantUid, highlightVariantFields);
374+
addVariantFieldClass(variantUid, highlightVariantFields, []);
374375

375376
// First element has the variant ID but should not get highlight class
376377
expect(mockElements[0].getAttribute).toHaveBeenCalledWith("data-cslp");
@@ -381,6 +382,23 @@ describe("addVariantFieldClass", () => {
381382
"visual-builder__variant-field"
382383
);
383384
});
385+
386+
it("should handle lower order variant fields correctly", () => {
387+
// @ts-expect-error mocking only required properties
388+
vi.spyOn(cslpdata, "extractDetailsFromCslp").mockImplementation((cslpValue) => {
389+
return {
390+
variant: cslpValue.split(":")[1]
391+
}
392+
});
393+
const variantUid = "variant-456";
394+
const highlightVariantFields = false;
395+
const variantOrder = ["variant-123", "variant-456"];
396+
397+
addVariantFieldClass(variantUid, highlightVariantFields, variantOrder);
398+
399+
// Verify that classes were added to elements correctly
400+
expect(mockElements[0].classList.add).toHaveBeenCalledWith("visual-builder__variant-field", "visual-builder__lower-order-variant-field");
401+
});
384402
});
385403

386404
describe("removeVariantFieldClass", () => {

src/visualBuilder/eventManager/useVariantsPostMessageEvent.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { visualBuilderStyles } from "../visualBuilder.style";
33
import visualBuilderPostMessage from "../utils/visualBuilderPostMessage";
44
import { VisualBuilderPostMessageEvents } from "../utils/types/postMessage.types";
55
import { FieldSchemaMap } from "../utils/fieldSchemaMap";
6+
import { extractDetailsFromCslp } from "../../cslp/cslpdata";
67

78
interface VariantFieldsEvent {
89
data: {
@@ -40,18 +41,13 @@ function isLowerOrderVariant(variant_uid: string, dataCslp: string, variantOrder
4041
if(!variantOrder || variantOrder.length === 0) {
4142
return false;
4243
}
43-
const indexOfVariant = variantOrder.indexOf(variant_uid);
44-
let indexOfDataCslp = -1;
45-
for (let i = variantOrder.length-1; i >= 0; i--) {
46-
if (dataCslp.includes(variantOrder[i])) {
47-
indexOfDataCslp = i;
48-
break;
49-
}
50-
}
51-
if(indexOfDataCslp < 0) {
44+
const {variant: cslpVariant} = extractDetailsFromCslp(dataCslp);
45+
const indexOfCmsVariant = variantOrder.lastIndexOf(variant_uid);
46+
const indexOfCslpVariant = variantOrder.lastIndexOf(cslpVariant || "");
47+
if(indexOfCslpVariant < 0) {
5248
return false;
5349
}
54-
return indexOfDataCslp < indexOfVariant;
50+
return indexOfCslpVariant < indexOfCmsVariant;
5551
}
5652

5753
export function addVariantFieldClass(
@@ -74,7 +70,7 @@ export function addVariantFieldClass(
7470
element.classList.add("visual-builder__base-field");
7571
}
7672
else if (isLowerOrderVariant(variant_uid, dataCslp, variantOrder)) {
77-
element.classList.add("visual-builder__variant-field");
73+
element.classList.add("visual-builder__variant-field", "visual-builder__lower-order-variant-field");
7874
}
7975
else {
8076
element.classList.add("visual-builder__disabled-variant-field");
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach, MockedObject } from 'vitest';
2+
import { EventManager } from "@contentstack/advanced-post-message";
3+
import { getResolvedVariantPermissions, FieldContext, ResolvedVariantPermissions } from '../getResolvedVariantPermissions';
4+
import { VisualBuilderPostMessageEvents } from '../types/postMessage.types';
5+
import visualBuilderPostMessage from '../visualBuilderPostMessage';
6+
7+
// Mock the visualBuilderPostMessage module
8+
vi.mock('../visualBuilderPostMessage', () => {
9+
return {
10+
default: {
11+
send: vi.fn(),
12+
},
13+
};
14+
});
15+
16+
const mockVisualBuilderPostMessage = visualBuilderPostMessage as MockedObject<EventManager>;
17+
18+
describe('getResolvedVariantPermissions', () => {
19+
const mockFieldContext: FieldContext = {
20+
content_type_uid: 'content_type_123',
21+
entry_uid: 'entry_456',
22+
locale: 'en-us',
23+
variant: 'variant_789',
24+
fieldPathWithIndex: 'title',
25+
};
26+
27+
const mockSuccessResponse: ResolvedVariantPermissions = {
28+
update: true,
29+
error: false,
30+
};
31+
32+
const mockErrorResponse: ResolvedVariantPermissions = {
33+
update: false,
34+
error: true,
35+
};
36+
37+
beforeEach(() => {
38+
vi.clearAllMocks();
39+
// Mock console.warn to avoid noise in test output
40+
vi.spyOn(console, 'warn').mockImplementation(() => {});
41+
});
42+
43+
afterEach(() => {
44+
vi.restoreAllMocks();
45+
});
46+
47+
describe('Success scenarios', () => {
48+
it('should return resolved permissions when postMessage returns valid response', async () => {
49+
50+
mockVisualBuilderPostMessage.send.mockResolvedValue(mockSuccessResponse);
51+
52+
53+
const result = await getResolvedVariantPermissions(mockFieldContext);
54+
55+
56+
expect(mockVisualBuilderPostMessage.send).toHaveBeenCalledWith(
57+
VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS,
58+
mockFieldContext
59+
);
60+
expect(result).toEqual(mockSuccessResponse);
61+
});
62+
63+
it('should return resolved permissions with update: false when postMessage returns false', async () => {
64+
65+
const responseWithUpdateFalse = { update: false, error: false };
66+
mockVisualBuilderPostMessage.send.mockResolvedValue(responseWithUpdateFalse);
67+
68+
69+
const result = await getResolvedVariantPermissions(mockFieldContext);
70+
71+
72+
expect(result).toEqual(responseWithUpdateFalse);
73+
});
74+
75+
it('should handle response with only update property', async () => {
76+
77+
const responseWithOnlyUpdate = { update: true };
78+
mockVisualBuilderPostMessage.send.mockResolvedValue(responseWithOnlyUpdate);
79+
80+
81+
const result = await getResolvedVariantPermissions(mockFieldContext);
82+
83+
84+
expect(result).toEqual(responseWithOnlyUpdate);
85+
});
86+
});
87+
88+
describe('Null/undefined response handling', () => {
89+
it('should return default error response when postMessage returns null', async () => {
90+
91+
mockVisualBuilderPostMessage.send.mockResolvedValue(null);
92+
93+
94+
const result = await getResolvedVariantPermissions(mockFieldContext);
95+
96+
97+
expect(result).toEqual({
98+
update: true,
99+
error: true,
100+
});
101+
});
102+
103+
it('should return default error response when postMessage returns undefined', async () => {
104+
105+
mockVisualBuilderPostMessage.send.mockResolvedValue(undefined);
106+
107+
108+
const result = await getResolvedVariantPermissions(mockFieldContext);
109+
110+
111+
expect(result).toEqual({
112+
update: true,
113+
error: true,
114+
});
115+
});
116+
});
117+
118+
describe('Error handling', () => {
119+
it('should return default error response and log warning when postMessage throws error', async () => {
120+
121+
const mockError = new Error('Network error');
122+
mockVisualBuilderPostMessage.send.mockRejectedValue(mockError);
123+
124+
125+
const result = await getResolvedVariantPermissions(mockFieldContext);
126+
127+
128+
expect(console.warn).toHaveBeenCalledWith(
129+
'Error retrieving resolved variant permissions',
130+
mockError
131+
);
132+
expect(result).toEqual({
133+
update: true,
134+
error: true,
135+
});
136+
});
137+
138+
it('should handle different types of errors', async () => {
139+
140+
const mockError = 'String error';
141+
mockVisualBuilderPostMessage.send.mockRejectedValue(mockError);
142+
143+
144+
const result = await getResolvedVariantPermissions(mockFieldContext);
145+
146+
147+
expect(console.warn).toHaveBeenCalledWith(
148+
'Error retrieving resolved variant permissions',
149+
mockError
150+
);
151+
expect(result).toEqual({
152+
update: true,
153+
error: true,
154+
});
155+
});
156+
157+
it('should handle promise rejection without error object', async () => {
158+
159+
mockVisualBuilderPostMessage.send.mockRejectedValue(null);
160+
161+
162+
const result = await getResolvedVariantPermissions(mockFieldContext);
163+
164+
165+
expect(console.warn).toHaveBeenCalledWith(
166+
'Error retrieving resolved variant permissions',
167+
null
168+
);
169+
expect(result).toEqual({
170+
update: true,
171+
error: true,
172+
});
173+
});
174+
});
175+
176+
describe('Edge cases', () => {
177+
it('should handle empty field context object', async () => {
178+
179+
const emptyFieldContext = {} as FieldContext;
180+
mockVisualBuilderPostMessage.send.mockResolvedValue(mockSuccessResponse);
181+
182+
183+
const result = await getResolvedVariantPermissions(emptyFieldContext);
184+
185+
186+
expect(mockVisualBuilderPostMessage.send).toHaveBeenCalledWith(
187+
VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS,
188+
emptyFieldContext
189+
);
190+
expect(result).toEqual(mockSuccessResponse);
191+
});
192+
193+
it('should handle field context with undefined variant', async () => {
194+
195+
const fieldContextWithUndefinedVariant: FieldContext = {
196+
...mockFieldContext,
197+
variant: undefined,
198+
};
199+
mockVisualBuilderPostMessage.send.mockResolvedValue(mockSuccessResponse);
200+
201+
202+
const result = await getResolvedVariantPermissions(fieldContextWithUndefinedVariant);
203+
204+
205+
expect(mockVisualBuilderPostMessage.send).toHaveBeenCalledWith(
206+
VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS,
207+
fieldContextWithUndefinedVariant
208+
);
209+
expect(result).toEqual(mockSuccessResponse);
210+
});
211+
212+
it('should handle field context with null variant', async () => {
213+
214+
const fieldContextWithNullVariant: FieldContext = {
215+
...mockFieldContext,
216+
variant: null as any,
217+
};
218+
mockVisualBuilderPostMessage.send.mockResolvedValue(mockSuccessResponse);
219+
220+
221+
const result = await getResolvedVariantPermissions(fieldContextWithNullVariant);
222+
223+
224+
expect(mockVisualBuilderPostMessage.send).toHaveBeenCalledWith(
225+
VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS,
226+
fieldContextWithNullVariant
227+
);
228+
expect(result).toEqual(mockSuccessResponse);
229+
});
230+
});
231+
232+
describe('Type safety', () => {
233+
it('should maintain proper typing for ResolvedVariantPermissions interface', async () => {
234+
235+
const response: ResolvedVariantPermissions = {
236+
update: true,
237+
error: false,
238+
};
239+
mockVisualBuilderPostMessage.send.mockResolvedValue(response);
240+
241+
242+
const result = await getResolvedVariantPermissions(mockFieldContext);
243+
244+
245+
expect(typeof result.update).toBe('boolean');
246+
expect(typeof result.error).toBe('boolean');
247+
expect(result).toHaveProperty('update');
248+
expect(result).toHaveProperty('error');
249+
});
250+
251+
it('should handle response with additional properties', async () => {
252+
253+
const responseWithExtraProps = {
254+
update: true,
255+
error: false,
256+
extraProperty: 'should be ignored',
257+
};
258+
mockVisualBuilderPostMessage.send.mockResolvedValue(responseWithExtraProps);
259+
260+
261+
const result = await getResolvedVariantPermissions(mockFieldContext);
262+
263+
264+
expect(result).toEqual(responseWithExtraProps);
265+
});
266+
});
267+
});

src/visualBuilder/utils/getResolvedVariantPermissions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,22 @@ export type FieldContext = Pick<CslpData, "content_type_uid" | "entry_uid" | "lo
66

77
export interface ResolvedVariantPermissions {
88
update: boolean;
9+
error?: boolean;
910
}
1011

1112
export async function getResolvedVariantPermissions(fieldContext: FieldContext) {
1213
try {
1314
const result = await visualBuilderPostMessage?.send<ResolvedVariantPermissions>(VisualBuilderPostMessageEvents.GET_RESOLVED_VARIANT_PERMISSIONS, fieldContext);
1415
return result ?? {
1516
update: true,
17+
error: true,
1618
};
1719
}
1820
catch(e) {
1921
console.warn("Error retrieving resolved variant permissions", e);
2022
return {
2123
update: true,
24+
error: true,
2225
};
2326
}
2427
}

src/visualBuilder/utils/isFieldDisabled.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ export const isFieldDisabled = (
114114
flags.updateRestrictDueToWorkflowStagePermission = true;
115115
}
116116

117+
if(VisualBuilder.VisualBuilderGlobalState.value.audienceMode
118+
&& editableElement.classList.contains("visual-builder__lower-order-variant-field")) {
119+
// If resolvedVariantPermissions errors out for any reason, we need to disable editing
120+
// for lower order (priority) variant fields with updateRestrictDueToDisabledVariant's message
121+
flags.updateRestrictDueToDisabledVariant = resolvedVariantPermissions ? !!resolvedVariantPermissions.error : false;
122+
}
123+
117124
if (
118125
VisualBuilder.VisualBuilderGlobalState.value.audienceMode &&
119126
!editableElement.classList.contains("visual-builder__variant-field") &&

0 commit comments

Comments
 (0)