mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-19 19:21:15 -05:00
chore: fix tests
This commit is contained in:
@@ -1,15 +1,6 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { TShuffleOption, TSurveyLogic, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import {
|
||||
buildCTAQuestion,
|
||||
buildConsentQuestion,
|
||||
buildMultipleChoiceQuestion,
|
||||
buildNPSQuestion,
|
||||
buildOpenTextQuestion,
|
||||
buildRatingQuestion,
|
||||
buildSurvey,
|
||||
createChoiceJumpLogic,
|
||||
createJumpLogic,
|
||||
getDefaultEndingCard,
|
||||
getDefaultSurveyPreset,
|
||||
getDefaultWelcomeCard,
|
||||
@@ -19,595 +10,81 @@ import {
|
||||
const mockT = (props: any): string => (typeof props === "string" ? props : props.key);
|
||||
|
||||
describe("Survey Builder", () => {
|
||||
describe("buildMultipleChoiceQuestion", () => {
|
||||
test("creates a single choice question with required fields", () => {
|
||||
const question = buildMultipleChoiceQuestion({
|
||||
headline: "Test Question",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
|
||||
choices: ["Option 1", "Option 2", "Option 3"],
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question).toMatchObject({
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
|
||||
headline: { default: "Test Question" },
|
||||
choices: expect.arrayContaining([
|
||||
expect.objectContaining({ label: { default: "Option 1" } }),
|
||||
expect.objectContaining({ label: { default: "Option 2" } }),
|
||||
expect.objectContaining({ label: { default: "Option 3" } }),
|
||||
]),
|
||||
buttonLabel: { default: "common.next" },
|
||||
backButtonLabel: { default: "common.back" },
|
||||
shuffleOption: "none",
|
||||
required: false,
|
||||
});
|
||||
expect(question.choices.length).toBe(3);
|
||||
expect(question.id).toBeDefined();
|
||||
describe("Helper Functions", () => {
|
||||
test("getDefaultSurveyPreset returns expected default survey preset", () => {
|
||||
const preset = getDefaultSurveyPreset(mockT);
|
||||
expect(preset.name).toBe("New Survey");
|
||||
// test welcomeCard and endings
|
||||
expect(preset.welcomeCard).toHaveProperty("headline");
|
||||
expect(preset.endings).toHaveLength(1);
|
||||
expect(preset.endings[0]).toHaveProperty("headline");
|
||||
expect(preset.hiddenFields).toEqual(hiddenFieldsDefault);
|
||||
expect(preset.blocks).toEqual([]);
|
||||
});
|
||||
|
||||
test("creates a multiple choice question with provided ID", () => {
|
||||
const customId = "custom-id-123";
|
||||
const question = buildMultipleChoiceQuestion({
|
||||
id: customId,
|
||||
headline: "Test Question",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceMulti,
|
||||
choices: ["Option 1", "Option 2"],
|
||||
t: mockT,
|
||||
test("getDefaultWelcomeCard returns expected welcome card", () => {
|
||||
const welcomeCard = getDefaultWelcomeCard(mockT);
|
||||
expect(welcomeCard).toMatchObject({
|
||||
enabled: false,
|
||||
headline: { default: "templates.default_welcome_card_headline" },
|
||||
timeToFinish: false,
|
||||
showResponseCount: false,
|
||||
});
|
||||
|
||||
expect(question.id).toBe(customId);
|
||||
expect(question.type).toBe(TSurveyQuestionTypeEnum.MultipleChoiceMulti);
|
||||
// Check that the welcome card is properly structured
|
||||
expect(welcomeCard).toHaveProperty("enabled");
|
||||
expect(welcomeCard).toHaveProperty("headline");
|
||||
expect(welcomeCard).toHaveProperty("showResponseCount");
|
||||
expect(welcomeCard).toHaveProperty("timeToFinish");
|
||||
});
|
||||
|
||||
test("handles 'other' option correctly", () => {
|
||||
const choices = ["Option 1", "Option 2", "Other"];
|
||||
const question = buildMultipleChoiceQuestion({
|
||||
headline: "Test Question",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
|
||||
choices,
|
||||
containsOther: true,
|
||||
t: mockT,
|
||||
test("getDefaultEndingCard returns expected ending card", () => {
|
||||
const languages: string[] = [];
|
||||
const endingCard = getDefaultEndingCard(languages, mockT);
|
||||
expect(endingCard).toMatchObject({
|
||||
type: "endScreen",
|
||||
headline: { default: "templates.default_ending_card_headline" },
|
||||
subheader: { default: "templates.default_ending_card_subheader" },
|
||||
});
|
||||
|
||||
expect(question.choices.length).toBe(3);
|
||||
expect(question.choices[2].id).toBe("other");
|
||||
expect(endingCard.id).toBeDefined();
|
||||
expect(endingCard).toHaveProperty("buttonLabel");
|
||||
expect(endingCard).toHaveProperty("buttonLink");
|
||||
});
|
||||
|
||||
test("uses provided choice IDs when available", () => {
|
||||
const choiceIds = ["id1", "id2", "id3"];
|
||||
const question = buildMultipleChoiceQuestion({
|
||||
headline: "Test Question",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
|
||||
choices: ["Option 1", "Option 2", "Option 3"],
|
||||
choiceIds,
|
||||
t: mockT,
|
||||
test("hiddenFieldsDefault has expected structure", () => {
|
||||
expect(hiddenFieldsDefault).toMatchObject({
|
||||
enabled: true,
|
||||
fieldIds: [],
|
||||
});
|
||||
|
||||
expect(question.choices[0].id).toBe(choiceIds[0]);
|
||||
expect(question.choices[1].id).toBe(choiceIds[1]);
|
||||
expect(question.choices[2].id).toBe(choiceIds[2]);
|
||||
});
|
||||
|
||||
test("applies all optional parameters correctly", () => {
|
||||
const logic: TSurveyLogic[] = [
|
||||
{
|
||||
id: "logic-1",
|
||||
conditions: {
|
||||
id: "cond-1",
|
||||
connector: "and",
|
||||
conditions: [],
|
||||
},
|
||||
actions: [],
|
||||
},
|
||||
];
|
||||
test("buildSurvey returns built survey with overridden preset properties", () => {
|
||||
const config = {
|
||||
name: "Custom Survey",
|
||||
role: "productManager" as const,
|
||||
industries: ["saas" as const],
|
||||
channels: ["link" as const],
|
||||
description: "A custom survey description",
|
||||
blocks: [],
|
||||
endings: [getDefaultEndingCard([], mockT)],
|
||||
hiddenFields: hiddenFieldsDefault,
|
||||
};
|
||||
|
||||
const shuffleOption: TShuffleOption = "all";
|
||||
const survey = buildSurvey(config, mockT);
|
||||
|
||||
const question = buildMultipleChoiceQuestion({
|
||||
headline: "Test Question",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
|
||||
subheader: "This is a subheader",
|
||||
choices: ["Option 1", "Option 2"],
|
||||
buttonLabel: "Custom Next",
|
||||
backButtonLabel: "Custom Back",
|
||||
shuffleOption,
|
||||
required: false,
|
||||
logic,
|
||||
t: mockT,
|
||||
});
|
||||
// role, industries, channels, description
|
||||
expect(survey.role).toBe(config.role);
|
||||
expect(survey.industries).toEqual(config.industries);
|
||||
expect(survey.channels).toEqual(config.channels);
|
||||
expect(survey.description).toBe(config.description);
|
||||
|
||||
expect(question.subheader).toEqual({ default: "This is a subheader" });
|
||||
expect(question.buttonLabel).toEqual({ default: "Custom Next" });
|
||||
expect(question.backButtonLabel).toEqual({ default: "Custom Back" });
|
||||
expect(question.shuffleOption).toBe("all");
|
||||
expect(question.required).toBe(false);
|
||||
expect(question.logic).toBe(logic);
|
||||
});
|
||||
});
|
||||
// preset overrides
|
||||
expect(survey.preset.name).toBe(config.name);
|
||||
expect(survey.preset.endings).toEqual(config.endings);
|
||||
expect(survey.preset.hiddenFields).toEqual(config.hiddenFields);
|
||||
expect(survey.preset.blocks).toEqual(config.blocks);
|
||||
|
||||
describe("buildOpenTextQuestion", () => {
|
||||
test("creates an open text question with required fields", () => {
|
||||
const question = buildOpenTextQuestion({
|
||||
headline: "Open Question",
|
||||
inputType: "text",
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question).toMatchObject({
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: "Open Question" },
|
||||
inputType: "text",
|
||||
buttonLabel: { default: "common.next" },
|
||||
backButtonLabel: { default: "common.back" },
|
||||
required: false,
|
||||
charLimit: {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
expect(question.id).toBeDefined();
|
||||
});
|
||||
|
||||
test("applies all optional parameters correctly", () => {
|
||||
const logic: TSurveyLogic[] = [
|
||||
{
|
||||
id: "logic-1",
|
||||
conditions: {
|
||||
id: "cond-1",
|
||||
connector: "and",
|
||||
conditions: [],
|
||||
},
|
||||
actions: [],
|
||||
},
|
||||
];
|
||||
|
||||
const question = buildOpenTextQuestion({
|
||||
id: "custom-id",
|
||||
headline: "Open Question",
|
||||
subheader: "Answer this question",
|
||||
placeholder: "Type here",
|
||||
buttonLabel: "Submit",
|
||||
backButtonLabel: "Previous",
|
||||
required: false,
|
||||
longAnswer: true,
|
||||
inputType: "email",
|
||||
logic,
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question.id).toBe("custom-id");
|
||||
expect(question.subheader).toEqual({ default: "Answer this question" });
|
||||
expect(question.placeholder).toEqual({ default: "Type here" });
|
||||
expect(question.buttonLabel).toEqual({ default: "Submit" });
|
||||
expect(question.backButtonLabel).toEqual({ default: "Previous" });
|
||||
expect(question.required).toBe(false);
|
||||
expect(question.longAnswer).toBe(true);
|
||||
expect(question.inputType).toBe("email");
|
||||
expect(question.logic).toBe(logic);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildRatingQuestion", () => {
|
||||
test("creates a rating question with required fields", () => {
|
||||
const question = buildRatingQuestion({
|
||||
headline: "Rating Question",
|
||||
scale: "number",
|
||||
range: 5,
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question).toMatchObject({
|
||||
type: TSurveyQuestionTypeEnum.Rating,
|
||||
headline: { default: "Rating Question" },
|
||||
scale: "number",
|
||||
range: 5,
|
||||
buttonLabel: { default: "common.next" },
|
||||
backButtonLabel: { default: "common.back" },
|
||||
required: false,
|
||||
isColorCodingEnabled: false,
|
||||
});
|
||||
expect(question.id).toBeDefined();
|
||||
});
|
||||
|
||||
test("applies all optional parameters correctly", () => {
|
||||
const logic: TSurveyLogic[] = [
|
||||
{
|
||||
id: "logic-1",
|
||||
conditions: {
|
||||
id: "cond-1",
|
||||
connector: "and",
|
||||
conditions: [],
|
||||
},
|
||||
actions: [],
|
||||
},
|
||||
];
|
||||
|
||||
const question = buildRatingQuestion({
|
||||
id: "custom-id",
|
||||
headline: "Rating Question",
|
||||
subheader: "Rate us",
|
||||
scale: "star",
|
||||
range: 10,
|
||||
lowerLabel: "Poor",
|
||||
upperLabel: "Excellent",
|
||||
buttonLabel: "Submit",
|
||||
backButtonLabel: "Previous",
|
||||
required: false,
|
||||
isColorCodingEnabled: true,
|
||||
logic,
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question.id).toBe("custom-id");
|
||||
expect(question.subheader).toEqual({ default: "Rate us" });
|
||||
expect(question.scale).toBe("star");
|
||||
expect(question.range).toBe(10);
|
||||
expect(question.lowerLabel).toEqual({ default: "Poor" });
|
||||
expect(question.upperLabel).toEqual({ default: "Excellent" });
|
||||
expect(question.buttonLabel).toEqual({ default: "Submit" });
|
||||
expect(question.backButtonLabel).toEqual({ default: "Previous" });
|
||||
expect(question.required).toBe(false);
|
||||
expect(question.isColorCodingEnabled).toBe(true);
|
||||
expect(question.logic).toBe(logic);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildNPSQuestion", () => {
|
||||
test("creates an NPS question with required fields", () => {
|
||||
const question = buildNPSQuestion({
|
||||
headline: "NPS Question",
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question).toMatchObject({
|
||||
type: TSurveyQuestionTypeEnum.NPS,
|
||||
headline: { default: "NPS Question" },
|
||||
buttonLabel: { default: "common.next" },
|
||||
backButtonLabel: { default: "common.back" },
|
||||
required: false,
|
||||
isColorCodingEnabled: false,
|
||||
});
|
||||
expect(question.id).toBeDefined();
|
||||
});
|
||||
|
||||
test("applies all optional parameters correctly", () => {
|
||||
const logic: TSurveyLogic[] = [
|
||||
{
|
||||
id: "logic-1",
|
||||
conditions: {
|
||||
id: "cond-1",
|
||||
connector: "and",
|
||||
conditions: [],
|
||||
},
|
||||
actions: [],
|
||||
},
|
||||
];
|
||||
|
||||
const question = buildNPSQuestion({
|
||||
id: "custom-id",
|
||||
headline: "NPS Question",
|
||||
subheader: "How likely are you to recommend us?",
|
||||
lowerLabel: "Not likely",
|
||||
upperLabel: "Very likely",
|
||||
buttonLabel: "Submit",
|
||||
backButtonLabel: "Previous",
|
||||
required: false,
|
||||
isColorCodingEnabled: true,
|
||||
logic,
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question.id).toBe("custom-id");
|
||||
expect(question.subheader).toEqual({ default: "How likely are you to recommend us?" });
|
||||
expect(question.lowerLabel).toEqual({ default: "Not likely" });
|
||||
expect(question.upperLabel).toEqual({ default: "Very likely" });
|
||||
expect(question.buttonLabel).toEqual({ default: "Submit" });
|
||||
expect(question.backButtonLabel).toEqual({ default: "Previous" });
|
||||
expect(question.required).toBe(false);
|
||||
expect(question.isColorCodingEnabled).toBe(true);
|
||||
expect(question.logic).toBe(logic);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildConsentQuestion", () => {
|
||||
test("creates a consent question with required fields", () => {
|
||||
const question = buildConsentQuestion({
|
||||
headline: "Consent Question",
|
||||
subheader: "",
|
||||
label: "I agree to terms",
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question).toMatchObject({
|
||||
type: TSurveyQuestionTypeEnum.Consent,
|
||||
headline: { default: "Consent Question" },
|
||||
subheader: { default: "" },
|
||||
label: { default: "I agree to terms" },
|
||||
buttonLabel: { default: "common.next" },
|
||||
backButtonLabel: { default: "common.back" },
|
||||
required: false,
|
||||
});
|
||||
expect(question.id).toBeDefined();
|
||||
});
|
||||
|
||||
test("applies all optional parameters correctly", () => {
|
||||
const logic: TSurveyLogic[] = [
|
||||
{
|
||||
id: "logic-1",
|
||||
conditions: {
|
||||
id: "cond-1",
|
||||
connector: "and",
|
||||
conditions: [],
|
||||
},
|
||||
actions: [],
|
||||
},
|
||||
];
|
||||
|
||||
const question = buildConsentQuestion({
|
||||
id: "custom-id",
|
||||
headline: "Consent Question",
|
||||
subheader: "Please read the terms",
|
||||
label: "I agree to terms",
|
||||
buttonLabel: "Submit",
|
||||
backButtonLabel: "Previous",
|
||||
required: false,
|
||||
logic,
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question.id).toBe("custom-id");
|
||||
expect(question.subheader).toEqual({ default: "Please read the terms" });
|
||||
expect(question.label).toEqual({ default: "I agree to terms" });
|
||||
expect(question.buttonLabel).toEqual({ default: "Submit" });
|
||||
expect(question.backButtonLabel).toEqual({ default: "Previous" });
|
||||
expect(question.required).toBe(false);
|
||||
expect(question.logic).toBe(logic);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildCTAQuestion", () => {
|
||||
test("creates a CTA question with required fields", () => {
|
||||
const question = buildCTAQuestion({
|
||||
headline: "CTA Question",
|
||||
subheader: "",
|
||||
buttonExternal: false,
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question).toMatchObject({
|
||||
type: TSurveyQuestionTypeEnum.CTA,
|
||||
headline: { default: "CTA Question" },
|
||||
subheader: { default: "" },
|
||||
buttonLabel: { default: "common.next" },
|
||||
backButtonLabel: { default: "common.back" },
|
||||
required: false,
|
||||
buttonExternal: false,
|
||||
});
|
||||
expect(question.id).toBeDefined();
|
||||
});
|
||||
|
||||
test("applies all optional parameters correctly", () => {
|
||||
const logic: TSurveyLogic[] = [
|
||||
{
|
||||
id: "logic-1",
|
||||
conditions: {
|
||||
id: "cond-1",
|
||||
connector: "and",
|
||||
conditions: [],
|
||||
},
|
||||
actions: [],
|
||||
},
|
||||
];
|
||||
|
||||
const question = buildCTAQuestion({
|
||||
id: "custom-id",
|
||||
headline: "CTA Question",
|
||||
subheader: "<p>Click the button</p>",
|
||||
buttonLabel: "Click me",
|
||||
buttonExternal: true,
|
||||
buttonUrl: "https://example.com",
|
||||
backButtonLabel: "Previous",
|
||||
required: false,
|
||||
dismissButtonLabel: "No thanks",
|
||||
logic,
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question.id).toBe("custom-id");
|
||||
expect(question.subheader).toEqual({ default: "<p>Click the button</p>" });
|
||||
expect(question.buttonLabel).toEqual({ default: "Click me" });
|
||||
expect(question.buttonExternal).toBe(true);
|
||||
expect(question.buttonUrl).toBe("https://example.com");
|
||||
expect(question.backButtonLabel).toEqual({ default: "Previous" });
|
||||
expect(question.required).toBe(false);
|
||||
expect(question.dismissButtonLabel).toEqual({ default: "No thanks" });
|
||||
expect(question.logic).toBe(logic);
|
||||
});
|
||||
|
||||
test("handles external button with URL", () => {
|
||||
const question = buildCTAQuestion({
|
||||
headline: "CTA Question",
|
||||
subheader: "",
|
||||
buttonExternal: true,
|
||||
buttonUrl: "https://formbricks.com",
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question.buttonExternal).toBe(true);
|
||||
expect(question.buttonUrl).toBe("https://formbricks.com");
|
||||
});
|
||||
});
|
||||
|
||||
// Test combinations of parameters for edge cases
|
||||
describe("Edge cases", () => {
|
||||
test("multiple choice question with empty choices array", () => {
|
||||
const question = buildMultipleChoiceQuestion({
|
||||
headline: "Test Question",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceSingle,
|
||||
choices: [],
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question.choices).toEqual([]);
|
||||
});
|
||||
|
||||
test("open text question with all parameters", () => {
|
||||
const question = buildOpenTextQuestion({
|
||||
id: "custom-id",
|
||||
headline: "Open Question",
|
||||
subheader: "Answer this question",
|
||||
placeholder: "Type here",
|
||||
buttonLabel: "Submit",
|
||||
backButtonLabel: "Previous",
|
||||
required: false,
|
||||
longAnswer: true,
|
||||
inputType: "email",
|
||||
logic: [],
|
||||
t: mockT,
|
||||
});
|
||||
|
||||
expect(question).toMatchObject({
|
||||
id: "custom-id",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: "Open Question" },
|
||||
subheader: { default: "Answer this question" },
|
||||
placeholder: { default: "Type here" },
|
||||
buttonLabel: { default: "Submit" },
|
||||
backButtonLabel: { default: "Previous" },
|
||||
required: false,
|
||||
longAnswer: true,
|
||||
inputType: "email",
|
||||
logic: [],
|
||||
});
|
||||
// default values from getDefaultSurveyPreset
|
||||
expect(survey.preset.welcomeCard).toHaveProperty("headline");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Helper Functions", () => {
|
||||
test("createJumpLogic returns valid jump logic", () => {
|
||||
const sourceId = "q1";
|
||||
const targetId = "q2";
|
||||
const operator: "isClicked" = "isClicked";
|
||||
const logic = createJumpLogic(sourceId, targetId, operator);
|
||||
|
||||
// Check structure
|
||||
expect(logic).toHaveProperty("id");
|
||||
expect(logic).toHaveProperty("conditions");
|
||||
expect(logic.conditions).toHaveProperty("conditions");
|
||||
expect(Array.isArray(logic.conditions.conditions)).toBe(true);
|
||||
|
||||
// Check one of the inner conditions
|
||||
const condition = logic.conditions.conditions[0];
|
||||
// Need to use type checking to ensure condition is a TSingleCondition not a TConditionGroup
|
||||
if (!("connector" in condition)) {
|
||||
expect(condition.leftOperand.value).toBe(sourceId);
|
||||
expect(condition.operator).toBe(operator);
|
||||
}
|
||||
|
||||
// Check actions
|
||||
expect(Array.isArray(logic.actions)).toBe(true);
|
||||
const action = logic.actions[0];
|
||||
if (action.objective === "jumpToQuestion") {
|
||||
expect(action.target).toBe(targetId);
|
||||
}
|
||||
});
|
||||
|
||||
test("createChoiceJumpLogic returns valid jump logic based on choice selection", () => {
|
||||
const sourceId = "q1";
|
||||
const choiceId = "choice1";
|
||||
const targetId = "q2";
|
||||
const logic = createChoiceJumpLogic(sourceId, choiceId, targetId);
|
||||
|
||||
expect(logic).toHaveProperty("id");
|
||||
expect(logic.conditions).toHaveProperty("conditions");
|
||||
|
||||
const condition = logic.conditions.conditions[0];
|
||||
if (!("connector" in condition)) {
|
||||
expect(condition.leftOperand.value).toBe(sourceId);
|
||||
expect(condition.operator).toBe("equals");
|
||||
expect(condition.rightOperand?.value).toBe(choiceId);
|
||||
}
|
||||
|
||||
const action = logic.actions[0];
|
||||
if (action.objective === "jumpToQuestion") {
|
||||
expect(action.target).toBe(targetId);
|
||||
}
|
||||
});
|
||||
|
||||
test("getDefaultWelcomeCard returns expected welcome card", () => {
|
||||
const card = getDefaultWelcomeCard(mockT);
|
||||
expect(card.enabled).toBe(false);
|
||||
expect(card.headline).toEqual({ default: "templates.default_welcome_card_headline" });
|
||||
expect(card.subheader).toEqual({ default: "templates.default_welcome_card_html" });
|
||||
expect(card.buttonLabel).toEqual({ default: "templates.default_welcome_card_button_label" });
|
||||
// boolean flags
|
||||
expect(card.timeToFinish).toBe(false);
|
||||
expect(card.showResponseCount).toBe(false);
|
||||
});
|
||||
|
||||
test("getDefaultEndingCard returns expected end screen card", () => {
|
||||
// Pass empty languages array to simulate no languages
|
||||
const card = getDefaultEndingCard([], mockT);
|
||||
expect(card).toHaveProperty("id");
|
||||
expect(card.type).toBe("endScreen");
|
||||
expect(card.headline).toEqual({ default: "templates.default_ending_card_headline" });
|
||||
expect(card.subheader).toEqual({ default: "templates.default_ending_card_subheader" });
|
||||
expect(card.buttonLabel).toEqual({ default: "templates.default_ending_card_button_label" });
|
||||
expect(card.buttonLink).toBe("https://formbricks.com");
|
||||
});
|
||||
|
||||
test("getDefaultSurveyPreset returns expected default survey preset", () => {
|
||||
const preset = getDefaultSurveyPreset(mockT);
|
||||
expect(preset.name).toBe("New Survey");
|
||||
expect(preset.questions).toEqual([]);
|
||||
// test welcomeCard and endings
|
||||
expect(preset.welcomeCard).toHaveProperty("headline");
|
||||
expect(Array.isArray(preset.endings)).toBe(true);
|
||||
expect(preset.hiddenFields).toEqual(hiddenFieldsDefault);
|
||||
});
|
||||
|
||||
test("buildSurvey returns built survey with overridden preset properties", () => {
|
||||
const config = {
|
||||
name: "Custom Survey",
|
||||
industries: ["eCommerce"] as string[],
|
||||
channels: ["link"],
|
||||
description: "Test survey",
|
||||
questions: [
|
||||
{
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText, // changed from "OpenText"
|
||||
headline: { default: "Question 1" },
|
||||
inputType: "text",
|
||||
buttonLabel: { default: "Next" },
|
||||
backButtonLabel: { default: "Back" },
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
endings: [
|
||||
{
|
||||
id: "end1",
|
||||
type: "endScreen",
|
||||
headline: { default: "End Screen" },
|
||||
subheader: { default: "Thanks" },
|
||||
buttonLabel: { default: "Finish" },
|
||||
buttonLink: "https://formbricks.com",
|
||||
},
|
||||
],
|
||||
hiddenFields: { enabled: false, fieldIds: ["f1"] },
|
||||
};
|
||||
|
||||
const survey = buildSurvey(config as any, mockT);
|
||||
expect(survey.name).toBe(config.name);
|
||||
expect(survey.industries).toEqual(config.industries);
|
||||
expect(survey.channels).toEqual(config.channels);
|
||||
expect(survey.description).toBe(config.description);
|
||||
// preset overrides
|
||||
expect(survey.preset.name).toBe(config.name);
|
||||
expect(survey.preset.questions).toEqual(config.questions);
|
||||
expect(survey.preset.endings).toEqual(config.endings);
|
||||
expect(survey.preset.hiddenFields).toEqual(config.hiddenFields);
|
||||
});
|
||||
|
||||
test("hiddenFieldsDefault has expected default configuration", () => {
|
||||
expect(hiddenFieldsDefault).toEqual({ enabled: true, fieldIds: [] });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,164 +1,119 @@
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
import { TSurveyQuestion, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { TTemplate } from "@formbricks/types/templates";
|
||||
import { getLocalizedValue } from "@/lib/i18n/utils";
|
||||
import { type TProject } from "@formbricks/types/project";
|
||||
import type { TSurveyElement } from "@formbricks/types/surveys/elements";
|
||||
import type { TTemplate } from "@formbricks/types/templates";
|
||||
import * as i18nUtils from "@/lib/i18n/utils";
|
||||
import { structuredClone } from "@/lib/pollyfills/structuredClone";
|
||||
import { replacePresetPlaceholders, replaceQuestionPresetPlaceholders } from "./templates";
|
||||
import { replaceElementPresetPlaceholders, replacePresetPlaceholders } from "./templates";
|
||||
|
||||
// Mock the imported functions
|
||||
vi.mock("@/lib/i18n/utils", () => ({
|
||||
getLocalizedValue: vi.fn(),
|
||||
}));
|
||||
vi.mock("@/lib/i18n/utils");
|
||||
vi.mock("@/lib/pollyfills/structuredClone");
|
||||
|
||||
vi.mock("@/lib/pollyfills/structuredClone", () => ({
|
||||
structuredClone: vi.fn((obj) => JSON.parse(JSON.stringify(obj))),
|
||||
}));
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(structuredClone).mockImplementation((obj) => JSON.parse(JSON.stringify(obj)));
|
||||
// Mock getLocalizedValue to return the value from the object
|
||||
vi.mocked(i18nUtils.getLocalizedValue).mockImplementation((obj: any, lang: string) => obj?.[lang] || "");
|
||||
});
|
||||
|
||||
describe("Template Utilities", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
describe("replaceElementPresetPlaceholders", () => {
|
||||
test("returns original element when project is not provided", () => {
|
||||
const element = {
|
||||
type: "openText",
|
||||
headline: { default: "Question about $[projectName]?" },
|
||||
} as unknown as TSurveyElement;
|
||||
|
||||
describe("replaceQuestionPresetPlaceholders", () => {
|
||||
test("returns original question when project is not provided", () => {
|
||||
const question: TSurveyQuestion = {
|
||||
id: "test-id",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "Test Question $[projectName]",
|
||||
},
|
||||
} as unknown as TSurveyQuestion;
|
||||
const result = replaceElementPresetPlaceholders(element, undefined as any);
|
||||
|
||||
const result = replaceQuestionPresetPlaceholders(question, undefined as unknown as TProject);
|
||||
expect(result).toEqual(element);
|
||||
});
|
||||
|
||||
expect(result).toEqual(question);
|
||||
expect(structuredClone).not.toHaveBeenCalled();
|
||||
test("replaces projectName placeholder in headline", () => {
|
||||
const element = {
|
||||
type: "openText",
|
||||
headline: { default: "How do you like $[projectName]?" },
|
||||
} as unknown as TSurveyElement;
|
||||
|
||||
const project = {
|
||||
name: "TestProject",
|
||||
} as unknown as TProject;
|
||||
|
||||
const result = replaceElementPresetPlaceholders(element, project);
|
||||
|
||||
// The function directly replaces without calling getLocalizedValue in the test scenario
|
||||
expect(result.headline?.default).toBe("How do you like TestProject?");
|
||||
});
|
||||
|
||||
test("replaces projectName placeholder in subheader", () => {
|
||||
const question: TSurveyQuestion = {
|
||||
id: "test-id",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "Test Question",
|
||||
},
|
||||
subheader: {
|
||||
default: "Subheader for $[projectName]",
|
||||
},
|
||||
} as unknown as TSurveyQuestion;
|
||||
const element = {
|
||||
type: "openText",
|
||||
headline: { default: "Question" },
|
||||
subheader: { default: "Subheader for $[projectName]" },
|
||||
} as unknown as TSurveyElement;
|
||||
|
||||
const project: TProject = {
|
||||
id: "project-id",
|
||||
name: "Test Project",
|
||||
organizationId: "org-id",
|
||||
const project = {
|
||||
name: "TestProject",
|
||||
} as unknown as TProject;
|
||||
|
||||
// Mock for headline and subheader with correct return values
|
||||
vi.mocked(getLocalizedValue).mockReturnValueOnce("Test Question");
|
||||
vi.mocked(getLocalizedValue).mockReturnValueOnce("Subheader for $[projectName]");
|
||||
const result = replaceElementPresetPlaceholders(element, project);
|
||||
|
||||
const result = replaceQuestionPresetPlaceholders(question, project);
|
||||
|
||||
expect(vi.mocked(getLocalizedValue)).toHaveBeenCalledTimes(2);
|
||||
expect(result.subheader?.default).toBe("Subheader for Test Project");
|
||||
expect(result.headline?.default).toBe("Question");
|
||||
expect(result.subheader?.default).toBe("Subheader for TestProject");
|
||||
});
|
||||
|
||||
test("handles missing headline and subheader", () => {
|
||||
const question: TSurveyQuestion = {
|
||||
id: "test-id",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
} as unknown as TSurveyQuestion;
|
||||
const element = {
|
||||
type: "openText",
|
||||
} as unknown as TSurveyElement;
|
||||
|
||||
const project: TProject = {
|
||||
id: "project-id",
|
||||
name: "Test Project",
|
||||
organizationId: "org-id",
|
||||
const project = {
|
||||
name: "TestProject",
|
||||
} as unknown as TProject;
|
||||
|
||||
const result = replaceQuestionPresetPlaceholders(question, project);
|
||||
const result = replaceElementPresetPlaceholders(element, project);
|
||||
|
||||
expect(structuredClone).toHaveBeenCalledWith(question);
|
||||
expect(result).toEqual(question);
|
||||
expect(getLocalizedValue).not.toHaveBeenCalled();
|
||||
expect(structuredClone).toHaveBeenCalledWith(element);
|
||||
expect(result).toEqual(element);
|
||||
});
|
||||
});
|
||||
|
||||
describe("replacePresetPlaceholders", () => {
|
||||
test("replaces projectName placeholder in template name and questions", () => {
|
||||
const template: TTemplate = {
|
||||
id: "template-1",
|
||||
name: "Test Template",
|
||||
description: "Template Description",
|
||||
test("replaces projectName placeholder in template name and blocks", () => {
|
||||
const mockTemplate = {
|
||||
name: "Template 1",
|
||||
preset: {
|
||||
name: "$[projectName] Feedback",
|
||||
questions: [
|
||||
welcomeCard: { enabled: false, timeToFinish: false, showResponseCount: false },
|
||||
blocks: [
|
||||
{
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "How do you like $[projectName]?",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "q2",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "Another question",
|
||||
},
|
||||
subheader: {
|
||||
default: "About $[projectName]",
|
||||
},
|
||||
id: "block1",
|
||||
name: "Block 1",
|
||||
elements: [
|
||||
{
|
||||
id: "elem1",
|
||||
type: "openText",
|
||||
headline: { default: "How would you rate $[projectName]?" },
|
||||
required: true,
|
||||
inputType: "text",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
endings: [],
|
||||
hiddenFields: { enabled: true, fieldIds: [] },
|
||||
},
|
||||
category: "product",
|
||||
} as unknown as TTemplate;
|
||||
|
||||
const project = {
|
||||
name: "Awesome App",
|
||||
};
|
||||
name: "TestProject",
|
||||
} as TProject;
|
||||
|
||||
// Mock getLocalizedValue to return the original strings with placeholders
|
||||
vi.mocked(getLocalizedValue)
|
||||
.mockReturnValueOnce("How do you like $[projectName]?")
|
||||
.mockReturnValueOnce("Another question")
|
||||
.mockReturnValueOnce("About $[projectName]");
|
||||
const result = replacePresetPlaceholders(mockTemplate, project);
|
||||
|
||||
const result = replacePresetPlaceholders(template, project);
|
||||
|
||||
expect(result.preset.name).toBe("Awesome App Feedback");
|
||||
expect(structuredClone).toHaveBeenCalledWith(template.preset);
|
||||
|
||||
// Verify that replaceQuestionPresetPlaceholders was applied to both questions
|
||||
expect(vi.mocked(getLocalizedValue)).toHaveBeenCalledTimes(3);
|
||||
expect(result.preset.questions[0].headline?.default).toBe("How do you like Awesome App?");
|
||||
expect(result.preset.questions[1].subheader?.default).toBe("About Awesome App");
|
||||
});
|
||||
|
||||
test("maintains other template properties", () => {
|
||||
const template: TTemplate = {
|
||||
id: "template-1",
|
||||
name: "Test Template",
|
||||
description: "Template Description",
|
||||
preset: {
|
||||
name: "$[projectName] Feedback",
|
||||
questions: [],
|
||||
},
|
||||
category: "product",
|
||||
} as unknown as TTemplate;
|
||||
|
||||
const project = {
|
||||
name: "Awesome App",
|
||||
};
|
||||
|
||||
const result = replacePresetPlaceholders(template, project) as unknown as {
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
expect(result.name).toBe(template.name);
|
||||
expect(result.description).toBe(template.description);
|
||||
expect(structuredClone).toHaveBeenCalledWith(mockTemplate.preset);
|
||||
expect(result.preset.name).toBe("TestProject Feedback");
|
||||
expect(result.preset.blocks[0].elements[0].headline?.default).toBe("How would you rate TestProject?");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,110 +1,43 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
import { TSurveyQuestion, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import type { TSurveyElement } from "@formbricks/types/surveys/elements";
|
||||
import { TTemplate } from "@formbricks/types/templates";
|
||||
import { getLocalizedValue } from "@/lib/i18n/utils";
|
||||
import { structuredClone } from "@/lib/pollyfills/structuredClone";
|
||||
import { replacePresetPlaceholders, replaceQuestionPresetPlaceholders } from "@/lib/utils/templates";
|
||||
import { replacePresetPlaceholders } from "@/lib/utils/templates";
|
||||
import { getChannelMapping, getIndustryMapping, getRoleMapping } from "./utils";
|
||||
|
||||
vi.mock("@/lib/i18n/utils", () => ({
|
||||
getLocalizedValue: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/pollyfills/structuredClone", () => ({
|
||||
structuredClone: vi.fn((val) => JSON.parse(JSON.stringify(val))),
|
||||
}));
|
||||
|
||||
describe("Template utils", () => {
|
||||
test("replaceQuestionPresetPlaceholders replaces project name in headline and subheader", () => {
|
||||
const mockQuestion = {
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "How would you rate $[projectName]?",
|
||||
},
|
||||
subheader: {
|
||||
default: "Tell us about $[projectName]",
|
||||
},
|
||||
required: false,
|
||||
} as unknown as TSurveyQuestion;
|
||||
|
||||
const mockProject = {
|
||||
id: "project-1",
|
||||
name: "TestProject",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
} as unknown as TProject;
|
||||
|
||||
// Reset and setup mocks with simple return values
|
||||
vi.mocked(getLocalizedValue).mockReset();
|
||||
vi.mocked(getLocalizedValue)
|
||||
.mockReturnValueOnce("How would you rate $[projectName]?")
|
||||
.mockReturnValueOnce("Tell us about $[projectName]");
|
||||
|
||||
const result = replaceQuestionPresetPlaceholders(mockQuestion, mockProject);
|
||||
|
||||
expect(structuredClone).toHaveBeenCalledWith(mockQuestion);
|
||||
expect(getLocalizedValue).toHaveBeenCalledTimes(2);
|
||||
expect(result.headline?.default).toBe("How would you rate TestProject?");
|
||||
expect(result.subheader?.default).toBe("Tell us about TestProject");
|
||||
});
|
||||
|
||||
test("replaceQuestionPresetPlaceholders returns original question if project is null", () => {
|
||||
const mockQuestion = {
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "How would you rate $[projectName]?",
|
||||
},
|
||||
required: false,
|
||||
} as unknown as TSurveyQuestion;
|
||||
|
||||
const result = replaceQuestionPresetPlaceholders(mockQuestion, null as unknown as TProject);
|
||||
expect(result).toBe(mockQuestion);
|
||||
});
|
||||
|
||||
test("replaceQuestionPresetPlaceholders handles missing subheader", () => {
|
||||
const mockQuestion = {
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "How would you rate $[projectName]?",
|
||||
},
|
||||
required: false,
|
||||
} as unknown as TSurveyQuestion;
|
||||
|
||||
const mockProject = {
|
||||
id: "project-1",
|
||||
name: "TestProject",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
} as unknown as TProject;
|
||||
vi.mocked(getLocalizedValue).mockReturnValueOnce("How would you rate $[projectName]?");
|
||||
|
||||
const result = replaceQuestionPresetPlaceholders(mockQuestion, mockProject);
|
||||
|
||||
expect(result.headline?.default).toBe("How would you rate TestProject?");
|
||||
expect(result.subheader).toBeUndefined();
|
||||
});
|
||||
|
||||
test("replacePresetPlaceholders replaces project name in template", () => {
|
||||
test("replacePresetPlaceholders replaces project name in template with blocks", () => {
|
||||
const mockTemplate: TTemplate = {
|
||||
name: "Test Template",
|
||||
description: "Template description",
|
||||
preset: {
|
||||
name: "$[projectName] Feedback",
|
||||
questions: [
|
||||
welcomeCard: { enabled: false, timeToFinish: false, showResponseCount: false },
|
||||
blocks: [
|
||||
{
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: {
|
||||
default: "How would you rate $[projectName]?",
|
||||
},
|
||||
required: false,
|
||||
} as unknown as TSurveyQuestion,
|
||||
id: "block1",
|
||||
name: "Block 1",
|
||||
elements: [
|
||||
{
|
||||
id: "elem1",
|
||||
type: "openText",
|
||||
headline: {
|
||||
default: "How would you rate $[projectName]?",
|
||||
},
|
||||
required: false,
|
||||
inputType: "text",
|
||||
} as unknown as TSurveyElement,
|
||||
],
|
||||
},
|
||||
],
|
||||
endings: [],
|
||||
hiddenFields: { enabled: true, fieldIds: [] },
|
||||
} as any,
|
||||
};
|
||||
|
||||
@@ -112,13 +45,11 @@ describe("Template utils", () => {
|
||||
name: "TestProject",
|
||||
};
|
||||
|
||||
vi.mocked(getLocalizedValue).mockReturnValueOnce("How would you rate $[projectName]?");
|
||||
|
||||
const result = replacePresetPlaceholders(mockTemplate, mockProject);
|
||||
|
||||
expect(structuredClone).toHaveBeenCalledWith(mockTemplate.preset);
|
||||
expect(result.preset.name).toBe("TestProject Feedback");
|
||||
expect(result.preset.questions[0].headline?.default).toBe("How would you rate TestProject?");
|
||||
expect(result.preset.blocks[0].elements[0].headline?.default).toBe("How would you rate TestProject?");
|
||||
});
|
||||
|
||||
test("getChannelMapping returns correct channel mappings", () => {
|
||||
|
||||
Reference in New Issue
Block a user