diff --git a/apps/web/modules/survey/editor/lib/shared-conditions-factory.test.ts b/apps/web/modules/survey/editor/lib/shared-conditions-factory.test.ts index 2acf592c2e..f08975eb03 100644 --- a/apps/web/modules/survey/editor/lib/shared-conditions-factory.test.ts +++ b/apps/web/modules/survey/editor/lib/shared-conditions-factory.test.ts @@ -89,6 +89,11 @@ vi.mock("@/modules/survey/editor/lib/utils", () => ({ { value: "isEmpty", label: "is empty" }, ]), getDefaultOperatorForElement: vi.fn().mockReturnValue("equals"), + getElementOperatorOptions: vi.fn().mockReturnValue([ + { value: "equals", label: "equals" }, + { value: "notEquals", label: "not equals" }, + { value: "isEmpty", label: "is empty" }, + ]), })); vi.mock("@paralleldrive/cuid2", () => ({ @@ -301,6 +306,30 @@ describe("shared-conditions-factory", () => { expect(mockGetDefaultOperator).toHaveBeenCalled(); }); + test("should get operator options for condition", async () => { + const { getConditionOperatorOptions } = await import("@/modules/survey/editor/lib/utils"); + const mockGetConditionOperatorOptions = vi.mocked(getConditionOperatorOptions); + mockGetConditionOperatorOptions.mockReturnValue([ + { value: "equals", label: "equals" }, + { value: "doesNotEqual", label: "does not equal" }, + ]); + + const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); + const mockCondition: TSingleCondition = { + id: "condition1", + leftOperand: { value: "question1", type: "question" }, + operator: "equals", + }; + + const operators = result.config.getOperatorOptions(mockCondition); + + expect(mockGetConditionOperatorOptions).toHaveBeenCalledWith(mockCondition, mockSurvey, mockT); + expect(operators).toEqual([ + { value: "equals", label: "equals" }, + { value: "doesNotEqual", label: "does not equal" }, + ]); + }); + test("should format left operand value", async () => { const { getFormatLeftOperandValue } = await import("@/modules/survey/editor/lib/utils"); const mockGetFormatLeftOperandValue = vi.mocked(getFormatLeftOperandValue); @@ -361,6 +390,139 @@ describe("shared-conditions-factory", () => { expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); }); + test("onUpdateCondition should correct invalid operator for element type", async () => { + const { getElementOperatorOptions } = await import("@/modules/survey/editor/lib/utils"); + const mockGetElementOperatorOptions = vi.mocked(getElementOperatorOptions); + + // Mock to return limited operators (e.g., only isEmpty and isNotEmpty) + mockGetElementOperatorOptions.mockReturnValue([ + { value: "isEmpty", label: "is empty" }, + { value: "isNotEmpty", label: "is not empty" }, + ]); + + const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); + const resourceId = "condition1"; + const updates = { + leftOperand: { + value: "question1", + type: "question" as const, + }, + operator: "equals" as TSurveyLogicConditionsOperator, // Invalid operator for this element + }; + + result.callbacks.onUpdateCondition(resourceId, updates); + + expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + + // Get the updater function that was called + const updater = mockConditionsChange.mock.calls[0][0] as (c: TConditionGroup) => TConditionGroup; + const mockConditions: TConditionGroup = { + id: "root", + connector: "and", + conditions: [ + { + id: "condition1", + leftOperand: { value: "oldQuestion", type: "question" }, + operator: "equals", + }, + ], + }; + + updater(structuredClone(mockConditions)); + + // Verify the operator was validated + expect(mockGetElementOperatorOptions).toHaveBeenCalled(); + }); + + test("onUpdateCondition should handle update with valid operator", async () => { + const { getElementOperatorOptions } = await import("@/modules/survey/editor/lib/utils"); + const mockGetElementOperatorOptions = vi.mocked(getElementOperatorOptions); + + // Mock to return operators that include the one being set + mockGetElementOperatorOptions.mockReturnValue([ + { value: "equals", label: "equals" }, + { value: "doesNotEqual", label: "does not equal" }, + ]); + + const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); + const resourceId = "condition1"; + const updates = { + leftOperand: { + value: "question1", + type: "question" as const, + }, + operator: "equals" as TSurveyLogicConditionsOperator, // Valid operator + }; + + result.callbacks.onUpdateCondition(resourceId, updates); + + expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + }); + + test("onUpdateCondition should handle update without leftOperand", () => { + const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); + const resourceId = "condition1"; + const updates = { + operator: "equals" as TSurveyLogicConditionsOperator, + }; + + result.callbacks.onUpdateCondition(resourceId, updates); + + expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + }); + + test("onUpdateCondition should handle update without operator", () => { + const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); + const resourceId = "condition1"; + const updates = { + leftOperand: { + value: "question1", + type: "question" as const, + }, + }; + + result.callbacks.onUpdateCondition(resourceId, updates); + + expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + }); + + test("onUpdateCondition should handle non-question leftOperand type", () => { + const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); + const resourceId = "condition1"; + const updates = { + leftOperand: { + value: "variable1", + type: "variable" as const, + }, + operator: "equals" as TSurveyLogicConditionsOperator, + }; + + result.callbacks.onUpdateCondition(resourceId, updates); + + expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + }); + + test("onUpdateCondition should handle element not found", async () => { + const { getElementOperatorOptions } = await import("@/modules/survey/editor/lib/utils"); + const mockGetElementOperatorOptions = vi.mocked(getElementOperatorOptions); + + const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); + const resourceId = "condition1"; + const updates = { + leftOperand: { + value: "non-existent-question", + type: "question" as const, + }, + operator: "equals" as TSurveyLogicConditionsOperator, + }; + + result.callbacks.onUpdateCondition(resourceId, updates); + + expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + // Should not call getElementOperatorOptions if element not found + expect(mockGetElementOperatorOptions).not.toHaveBeenCalled(); + }); + test("onToggleGroupConnector should toggle group connector", () => { const result = createSharedConditionsFactory(defaultParams, defaultCallbacks); const groupId = "group1"; @@ -368,6 +530,21 @@ describe("shared-conditions-factory", () => { result.callbacks.onToggleGroupConnector(groupId); expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + + // Execute the updater function to ensure it runs properly + const updater = mockConditionsChange.mock.calls[0][0] as (c: TConditionGroup) => TConditionGroup; + const mockConditions: TConditionGroup = { + id: "root", + connector: "and", + conditions: [ + { + id: "group1", + connector: "and", + conditions: [], + }, + ], + }; + updater(mockConditions); }); test("onCreateGroup should create group when includeCreateGroup is true", () => { @@ -381,6 +558,21 @@ describe("shared-conditions-factory", () => { result.callbacks.onCreateGroup!(resourceId); expect(mockConditionsChange).toHaveBeenCalledWith(expect.any(Function)); + + // Execute the updater function to ensure it runs properly + const updater = mockConditionsChange.mock.calls[0][0] as (c: TConditionGroup) => TConditionGroup; + const mockConditions: TConditionGroup = { + id: "root", + connector: "and", + conditions: [ + { + id: "condition1", + leftOperand: { value: "question1", type: "question" }, + operator: "equals", + }, + ], + }; + updater(mockConditions); }); });