mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 10:19:51 -06:00
219
apps/web/lib/auth.test.ts
Normal file
219
apps/web/lib/auth.test.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { AuthenticationError } from "@formbricks/types/errors";
|
||||
import {
|
||||
hasOrganizationAccess,
|
||||
hasOrganizationAuthority,
|
||||
hasOrganizationOwnership,
|
||||
hashPassword,
|
||||
isManagerOrOwner,
|
||||
isOwner,
|
||||
verifyPassword,
|
||||
} from "./auth";
|
||||
|
||||
// Mock prisma
|
||||
vi.mock("@formbricks/database", () => ({
|
||||
prisma: {
|
||||
membership: {
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe("Password Management", () => {
|
||||
test("hashPassword should hash a password", async () => {
|
||||
const password = "testPassword123";
|
||||
const hashedPassword = await hashPassword(password);
|
||||
expect(hashedPassword).toBeDefined();
|
||||
expect(hashedPassword).not.toBe(password);
|
||||
});
|
||||
|
||||
test("verifyPassword should verify a correct password", async () => {
|
||||
const password = "testPassword123";
|
||||
const hashedPassword = await hashPassword(password);
|
||||
const isValid = await verifyPassword(password, hashedPassword);
|
||||
expect(isValid).toBe(true);
|
||||
});
|
||||
|
||||
test("verifyPassword should reject an incorrect password", async () => {
|
||||
const password = "testPassword123";
|
||||
const hashedPassword = await hashPassword(password);
|
||||
const isValid = await verifyPassword("wrongPassword", hashedPassword);
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Organization Access", () => {
|
||||
const mockUserId = "user123";
|
||||
const mockOrgId = "org123";
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
test("hasOrganizationAccess should return true when user has membership", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "member",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const hasAccess = await hasOrganizationAccess(mockUserId, mockOrgId);
|
||||
expect(hasAccess).toBe(true);
|
||||
});
|
||||
|
||||
test("hasOrganizationAccess should return false when user has no membership", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue(null);
|
||||
|
||||
const hasAccess = await hasOrganizationAccess(mockUserId, mockOrgId);
|
||||
expect(hasAccess).toBe(false);
|
||||
});
|
||||
|
||||
test("isManagerOrOwner should return true for manager role", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "manager",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const isManager = await isManagerOrOwner(mockUserId, mockOrgId);
|
||||
expect(isManager).toBe(true);
|
||||
});
|
||||
|
||||
test("isManagerOrOwner should return true for owner role", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "owner",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const isOwner = await isManagerOrOwner(mockUserId, mockOrgId);
|
||||
expect(isOwner).toBe(true);
|
||||
});
|
||||
|
||||
test("isManagerOrOwner should return false for member role", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "member",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const isManagerOrOwnerRole = await isManagerOrOwner(mockUserId, mockOrgId);
|
||||
expect(isManagerOrOwnerRole).toBe(false);
|
||||
});
|
||||
|
||||
test("isOwner should return true only for owner role", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "owner",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const isOwnerRole = await isOwner(mockUserId, mockOrgId);
|
||||
expect(isOwnerRole).toBe(true);
|
||||
});
|
||||
|
||||
test("isOwner should return false for non-owner roles", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "manager",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const isOwnerRole = await isOwner(mockUserId, mockOrgId);
|
||||
expect(isOwnerRole).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Organization Authority", () => {
|
||||
const mockUserId = "user123";
|
||||
const mockOrgId = "org123";
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
test("hasOrganizationAuthority should return true for manager", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "manager",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const hasAuthority = await hasOrganizationAuthority(mockUserId, mockOrgId);
|
||||
expect(hasAuthority).toBe(true);
|
||||
});
|
||||
|
||||
test("hasOrganizationAuthority should throw for non-member", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue(null);
|
||||
|
||||
await expect(hasOrganizationAuthority(mockUserId, mockOrgId)).rejects.toThrow(AuthenticationError);
|
||||
});
|
||||
|
||||
test("hasOrganizationAuthority should throw for member role", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "member",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
await expect(hasOrganizationAuthority(mockUserId, mockOrgId)).rejects.toThrow(AuthenticationError);
|
||||
});
|
||||
|
||||
test("hasOrganizationOwnership should return true for owner", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "owner",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
const hasOwnership = await hasOrganizationOwnership(mockUserId, mockOrgId);
|
||||
expect(hasOwnership).toBe(true);
|
||||
});
|
||||
|
||||
test("hasOrganizationOwnership should throw for non-member", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue(null);
|
||||
|
||||
await expect(hasOrganizationOwnership(mockUserId, mockOrgId)).rejects.toThrow(AuthenticationError);
|
||||
});
|
||||
|
||||
test("hasOrganizationOwnership should throw for non-owner roles", async () => {
|
||||
vi.mocked(prisma.membership.findUnique).mockResolvedValue({
|
||||
id: "membership123",
|
||||
userId: mockUserId,
|
||||
organizationId: mockOrgId,
|
||||
role: "manager",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
|
||||
await expect(hasOrganizationOwnership(mockUserId, mockOrgId)).rejects.toThrow(AuthenticationError);
|
||||
});
|
||||
});
|
||||
46
apps/web/lib/getSurveyUrl.test.ts
Normal file
46
apps/web/lib/getSurveyUrl.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
// Create a mock module for constants with proper types
|
||||
const constantsMock = {
|
||||
SURVEY_URL: undefined as string | undefined,
|
||||
WEBAPP_URL: "http://localhost:3000" as string,
|
||||
};
|
||||
|
||||
// Mock the constants module
|
||||
vi.mock("./constants", () => constantsMock);
|
||||
|
||||
describe("getSurveyDomain", () => {
|
||||
beforeEach(() => {
|
||||
// Reset the mock values before each test
|
||||
constantsMock.SURVEY_URL = undefined;
|
||||
constantsMock.WEBAPP_URL = "http://localhost:3000";
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
test("should return WEBAPP_URL when SURVEY_URL is not set", async () => {
|
||||
const { getSurveyDomain } = await import("./getSurveyUrl");
|
||||
const domain = getSurveyDomain();
|
||||
expect(domain).toBe("http://localhost:3000");
|
||||
});
|
||||
|
||||
test("should return SURVEY_URL when it is set", async () => {
|
||||
constantsMock.SURVEY_URL = "https://surveys.example.com";
|
||||
const { getSurveyDomain } = await import("./getSurveyUrl");
|
||||
const domain = getSurveyDomain();
|
||||
expect(domain).toBe("https://surveys.example.com");
|
||||
});
|
||||
|
||||
test("should handle empty string SURVEY_URL by returning WEBAPP_URL", async () => {
|
||||
constantsMock.SURVEY_URL = "";
|
||||
const { getSurveyDomain } = await import("./getSurveyUrl");
|
||||
const domain = getSurveyDomain();
|
||||
expect(domain).toBe("http://localhost:3000");
|
||||
});
|
||||
|
||||
test("should handle undefined SURVEY_URL by returning WEBAPP_URL", async () => {
|
||||
constantsMock.SURVEY_URL = undefined;
|
||||
const { getSurveyDomain } = await import("./getSurveyUrl");
|
||||
const domain = getSurveyDomain();
|
||||
expect(domain).toBe("http://localhost:3000");
|
||||
});
|
||||
});
|
||||
51
apps/web/lib/hashString.test.ts
Normal file
51
apps/web/lib/hashString.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { hashString } from "./hashString";
|
||||
|
||||
describe("hashString", () => {
|
||||
test("should return a string", () => {
|
||||
const input = "test string";
|
||||
const hash = hashString(input);
|
||||
|
||||
expect(typeof hash).toBe("string");
|
||||
expect(hash.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("should produce consistent hashes for the same input", () => {
|
||||
const input = "test string";
|
||||
const hash1 = hashString(input);
|
||||
const hash2 = hashString(input);
|
||||
|
||||
expect(hash1).toBe(hash2);
|
||||
});
|
||||
|
||||
test("should handle empty strings", () => {
|
||||
const hash = hashString("");
|
||||
|
||||
expect(typeof hash).toBe("string");
|
||||
expect(hash.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("should handle special characters", () => {
|
||||
const input = "!@#$%^&*()_+{}|:<>?";
|
||||
const hash = hashString(input);
|
||||
|
||||
expect(typeof hash).toBe("string");
|
||||
expect(hash.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("should handle unicode characters", () => {
|
||||
const input = "Hello, 世界!";
|
||||
const hash = hashString(input);
|
||||
|
||||
expect(typeof hash).toBe("string");
|
||||
expect(hash.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("should handle long strings", () => {
|
||||
const input = "a".repeat(1000);
|
||||
const hash = hashString(input);
|
||||
|
||||
expect(typeof hash).toBe("string");
|
||||
expect(hash.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
195
apps/web/lib/jwt.test.ts
Normal file
195
apps/web/lib/jwt.test.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { env } from "@/lib/env";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import {
|
||||
createEmailToken,
|
||||
createInviteToken,
|
||||
createToken,
|
||||
createTokenForLinkSurvey,
|
||||
getEmailFromEmailToken,
|
||||
verifyInviteToken,
|
||||
verifyToken,
|
||||
verifyTokenForLinkSurvey,
|
||||
} from "./jwt";
|
||||
|
||||
// Mock environment variables
|
||||
vi.mock("@/lib/env", () => ({
|
||||
env: {
|
||||
ENCRYPTION_KEY: "0".repeat(32), // 32-byte key for AES-256-GCM
|
||||
NEXTAUTH_SECRET: "test-nextauth-secret",
|
||||
} as typeof env,
|
||||
}));
|
||||
|
||||
// Mock prisma
|
||||
vi.mock("@formbricks/database", () => ({
|
||||
prisma: {
|
||||
user: {
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe("JWT Functions", () => {
|
||||
const mockUser = {
|
||||
id: "test-user-id",
|
||||
email: "test@example.com",
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
(prisma.user.findUnique as any).mockResolvedValue(mockUser);
|
||||
});
|
||||
|
||||
describe("createToken", () => {
|
||||
test("should create a valid token", () => {
|
||||
const token = createToken(mockUser.id, mockUser.email);
|
||||
expect(token).toBeDefined();
|
||||
expect(typeof token).toBe("string");
|
||||
});
|
||||
|
||||
test("should throw error if ENCRYPTION_KEY is not set", () => {
|
||||
const originalKey = env.ENCRYPTION_KEY;
|
||||
try {
|
||||
(env as any).ENCRYPTION_KEY = undefined;
|
||||
expect(() => createToken(mockUser.id, mockUser.email)).toThrow("ENCRYPTION_KEY is not set");
|
||||
} finally {
|
||||
(env as any).ENCRYPTION_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("createTokenForLinkSurvey", () => {
|
||||
test("should create a valid survey link token", () => {
|
||||
const surveyId = "test-survey-id";
|
||||
const token = createTokenForLinkSurvey(surveyId, mockUser.email);
|
||||
expect(token).toBeDefined();
|
||||
expect(typeof token).toBe("string");
|
||||
});
|
||||
|
||||
test("should throw error if ENCRYPTION_KEY is not set", () => {
|
||||
const originalKey = env.ENCRYPTION_KEY;
|
||||
try {
|
||||
(env as any).ENCRYPTION_KEY = undefined;
|
||||
expect(() => createTokenForLinkSurvey("test-survey-id", mockUser.email)).toThrow(
|
||||
"ENCRYPTION_KEY is not set"
|
||||
);
|
||||
} finally {
|
||||
(env as any).ENCRYPTION_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("createEmailToken", () => {
|
||||
test("should create a valid email token", () => {
|
||||
const token = createEmailToken(mockUser.email);
|
||||
expect(token).toBeDefined();
|
||||
expect(typeof token).toBe("string");
|
||||
});
|
||||
|
||||
test("should throw error if ENCRYPTION_KEY is not set", () => {
|
||||
const originalKey = env.ENCRYPTION_KEY;
|
||||
try {
|
||||
(env as any).ENCRYPTION_KEY = undefined;
|
||||
expect(() => createEmailToken(mockUser.email)).toThrow("ENCRYPTION_KEY is not set");
|
||||
} finally {
|
||||
(env as any).ENCRYPTION_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
|
||||
test("should throw error if NEXTAUTH_SECRET is not set", () => {
|
||||
const originalSecret = env.NEXTAUTH_SECRET;
|
||||
try {
|
||||
(env as any).NEXTAUTH_SECRET = undefined;
|
||||
expect(() => createEmailToken(mockUser.email)).toThrow("NEXTAUTH_SECRET is not set");
|
||||
} finally {
|
||||
(env as any).NEXTAUTH_SECRET = originalSecret;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getEmailFromEmailToken", () => {
|
||||
test("should extract email from valid token", () => {
|
||||
const token = createEmailToken(mockUser.email);
|
||||
const extractedEmail = getEmailFromEmailToken(token);
|
||||
expect(extractedEmail).toBe(mockUser.email);
|
||||
});
|
||||
|
||||
test("should throw error if ENCRYPTION_KEY is not set", () => {
|
||||
const originalKey = env.ENCRYPTION_KEY;
|
||||
try {
|
||||
(env as any).ENCRYPTION_KEY = undefined;
|
||||
expect(() => getEmailFromEmailToken("invalid-token")).toThrow("ENCRYPTION_KEY is not set");
|
||||
} finally {
|
||||
(env as any).ENCRYPTION_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("createInviteToken", () => {
|
||||
test("should create a valid invite token", () => {
|
||||
const inviteId = "test-invite-id";
|
||||
const token = createInviteToken(inviteId, mockUser.email);
|
||||
expect(token).toBeDefined();
|
||||
expect(typeof token).toBe("string");
|
||||
});
|
||||
|
||||
test("should throw error if ENCRYPTION_KEY is not set", () => {
|
||||
const originalKey = env.ENCRYPTION_KEY;
|
||||
try {
|
||||
(env as any).ENCRYPTION_KEY = undefined;
|
||||
expect(() => createInviteToken("test-invite-id", mockUser.email)).toThrow(
|
||||
"ENCRYPTION_KEY is not set"
|
||||
);
|
||||
} finally {
|
||||
(env as any).ENCRYPTION_KEY = originalKey;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("verifyTokenForLinkSurvey", () => {
|
||||
test("should verify valid survey link token", () => {
|
||||
const surveyId = "test-survey-id";
|
||||
const token = createTokenForLinkSurvey(surveyId, mockUser.email);
|
||||
const verifiedEmail = verifyTokenForLinkSurvey(token, surveyId);
|
||||
expect(verifiedEmail).toBe(mockUser.email);
|
||||
});
|
||||
|
||||
test("should return null for invalid token", () => {
|
||||
const result = verifyTokenForLinkSurvey("invalid-token", "test-survey-id");
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("verifyToken", () => {
|
||||
test("should verify valid token", async () => {
|
||||
const token = createToken(mockUser.id, mockUser.email);
|
||||
const verified = await verifyToken(token);
|
||||
expect(verified).toEqual({
|
||||
id: mockUser.id,
|
||||
email: mockUser.email,
|
||||
});
|
||||
});
|
||||
|
||||
test("should throw error if user not found", async () => {
|
||||
(prisma.user.findUnique as any).mockResolvedValue(null);
|
||||
const token = createToken(mockUser.id, mockUser.email);
|
||||
await expect(verifyToken(token)).rejects.toThrow("User not found");
|
||||
});
|
||||
});
|
||||
|
||||
describe("verifyInviteToken", () => {
|
||||
test("should verify valid invite token", () => {
|
||||
const inviteId = "test-invite-id";
|
||||
const token = createInviteToken(inviteId, mockUser.email);
|
||||
const verified = verifyInviteToken(token);
|
||||
expect(verified).toEqual({
|
||||
inviteId,
|
||||
email: mockUser.email,
|
||||
});
|
||||
});
|
||||
|
||||
test("should throw error for invalid token", () => {
|
||||
expect(() => verifyInviteToken("invalid-token")).toThrow("Invalid or expired invite token");
|
||||
});
|
||||
});
|
||||
});
|
||||
353
apps/web/lib/responses.test.ts
Normal file
353
apps/web/lib/responses.test.ts
Normal file
@@ -0,0 +1,353 @@
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { TSurveyQuestionType, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { convertResponseValue, getQuestionResponseMapping, processResponseData } from "./responses";
|
||||
|
||||
// Mock the recall and i18n utils
|
||||
vi.mock("@/lib/utils/recall", () => ({
|
||||
parseRecallInfo: vi.fn((text) => text),
|
||||
}));
|
||||
|
||||
vi.mock("./i18n/utils", () => ({
|
||||
getLocalizedValue: vi.fn((obj, lang) => obj[lang] || obj.default),
|
||||
}));
|
||||
|
||||
describe("Response Processing", () => {
|
||||
describe("processResponseData", () => {
|
||||
test("should handle string input", () => {
|
||||
expect(processResponseData("test")).toBe("test");
|
||||
});
|
||||
|
||||
test("should handle number input", () => {
|
||||
expect(processResponseData(42)).toBe("42");
|
||||
});
|
||||
|
||||
test("should handle array input", () => {
|
||||
expect(processResponseData(["a", "b", "c"])).toBe("a; b; c");
|
||||
});
|
||||
|
||||
test("should filter out empty values from array", () => {
|
||||
const input = ["a", "", "c"];
|
||||
expect(processResponseData(input)).toBe("a; c");
|
||||
});
|
||||
|
||||
test("should handle object input", () => {
|
||||
const input = { key1: "value1", key2: "value2" };
|
||||
expect(processResponseData(input)).toBe("key1: value1\nkey2: value2");
|
||||
});
|
||||
|
||||
test("should filter out empty values from object", () => {
|
||||
const input = { key1: "value1", key2: "", key3: "value3" };
|
||||
expect(processResponseData(input)).toBe("key1: value1\nkey3: value3");
|
||||
});
|
||||
|
||||
test("should return empty string for unsupported types", () => {
|
||||
expect(processResponseData(undefined as any)).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("convertResponseValue", () => {
|
||||
const mockOpenTextQuestion = {
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText as const,
|
||||
headline: { default: "Test Question" },
|
||||
required: true,
|
||||
inputType: "text" as const,
|
||||
longAnswer: false,
|
||||
charLimit: { enabled: false },
|
||||
};
|
||||
|
||||
const mockRankingQuestion = {
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.Ranking as const,
|
||||
headline: { default: "Test Question" },
|
||||
required: true,
|
||||
choices: [
|
||||
{ id: "1", label: { default: "Choice 1" } },
|
||||
{ id: "2", label: { default: "Choice 2" } },
|
||||
],
|
||||
shuffleOption: "none" as const,
|
||||
};
|
||||
|
||||
const mockFileUploadQuestion = {
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.FileUpload as const,
|
||||
headline: { default: "Test Question" },
|
||||
required: true,
|
||||
allowMultipleFiles: true,
|
||||
};
|
||||
|
||||
const mockPictureSelectionQuestion = {
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.PictureSelection as const,
|
||||
headline: { default: "Test Question" },
|
||||
required: true,
|
||||
allowMulti: false,
|
||||
choices: [
|
||||
{ id: "1", imageUrl: "image1.jpg", label: { default: "Choice 1" } },
|
||||
{ id: "2", imageUrl: "image2.jpg", label: { default: "Choice 2" } },
|
||||
],
|
||||
};
|
||||
|
||||
test("should handle ranking type with string input", () => {
|
||||
expect(convertResponseValue("answer", mockRankingQuestion)).toEqual(["answer"]);
|
||||
});
|
||||
|
||||
test("should handle ranking type with array input", () => {
|
||||
expect(convertResponseValue(["answer1", "answer2"], mockRankingQuestion)).toEqual([
|
||||
"answer1",
|
||||
"answer2",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle fileUpload type with string input", () => {
|
||||
expect(convertResponseValue("file.jpg", mockFileUploadQuestion)).toEqual(["file.jpg"]);
|
||||
});
|
||||
|
||||
test("should handle fileUpload type with array input", () => {
|
||||
expect(convertResponseValue(["file1.jpg", "file2.jpg"], mockFileUploadQuestion)).toEqual([
|
||||
"file1.jpg",
|
||||
"file2.jpg",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle pictureSelection type with string input", () => {
|
||||
expect(convertResponseValue("1", mockPictureSelectionQuestion)).toEqual(["image1.jpg"]);
|
||||
});
|
||||
|
||||
test("should handle pictureSelection type with array input", () => {
|
||||
expect(convertResponseValue(["1", "2"], mockPictureSelectionQuestion)).toEqual([
|
||||
"image1.jpg",
|
||||
"image2.jpg",
|
||||
]);
|
||||
});
|
||||
|
||||
test("should handle pictureSelection type with invalid choice", () => {
|
||||
expect(convertResponseValue("invalid", mockPictureSelectionQuestion)).toEqual([]);
|
||||
});
|
||||
|
||||
test("should handle default case with string input", () => {
|
||||
expect(convertResponseValue("answer", mockOpenTextQuestion)).toBe("answer");
|
||||
});
|
||||
|
||||
test("should handle default case with number input", () => {
|
||||
expect(convertResponseValue(42, mockOpenTextQuestion)).toBe("42");
|
||||
});
|
||||
|
||||
test("should handle default case with array input", () => {
|
||||
expect(convertResponseValue(["a", "b", "c"], mockOpenTextQuestion)).toBe("a; b; c");
|
||||
});
|
||||
|
||||
test("should handle default case with object input", () => {
|
||||
const input = { key1: "value1", key2: "value2" };
|
||||
expect(convertResponseValue(input, mockOpenTextQuestion)).toBe("key1: value1\nkey2: value2");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getQuestionResponseMapping", () => {
|
||||
const mockSurvey = {
|
||||
id: "survey1",
|
||||
type: "link" as const,
|
||||
status: "inProgress" as const,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
name: "Test Survey",
|
||||
environmentId: "env1",
|
||||
createdBy: null,
|
||||
questions: [
|
||||
{
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText as const,
|
||||
headline: { default: "Question 1" },
|
||||
required: true,
|
||||
inputType: "text" as const,
|
||||
longAnswer: false,
|
||||
charLimit: { enabled: false },
|
||||
},
|
||||
{
|
||||
id: "q2",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceMulti as const,
|
||||
headline: { default: "Question 2" },
|
||||
required: true,
|
||||
choices: [
|
||||
{ id: "1", label: { default: "Option 1" } },
|
||||
{ id: "2", label: { default: "Option 2" } },
|
||||
],
|
||||
shuffleOption: "none" as const,
|
||||
},
|
||||
],
|
||||
hiddenFields: {
|
||||
enabled: false,
|
||||
fieldIds: [],
|
||||
},
|
||||
displayOption: "displayOnce" as const,
|
||||
delay: 0,
|
||||
languages: [
|
||||
{
|
||||
language: {
|
||||
id: "lang1",
|
||||
code: "default",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
alias: null,
|
||||
projectId: "proj1",
|
||||
},
|
||||
default: true,
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
endings: [],
|
||||
displayLimit: null,
|
||||
autoClose: null,
|
||||
autoComplete: null,
|
||||
recontactDays: null,
|
||||
runOnDate: null,
|
||||
closeOnDate: null,
|
||||
welcomeCard: {
|
||||
enabled: false,
|
||||
timeToFinish: false,
|
||||
showResponseCount: false,
|
||||
},
|
||||
showLanguageSwitch: false,
|
||||
isBackButtonHidden: false,
|
||||
isVerifyEmailEnabled: false,
|
||||
isSingleResponsePerEmailEnabled: false,
|
||||
displayPercentage: 100,
|
||||
styling: null,
|
||||
projectOverwrites: null,
|
||||
verifyEmail: null,
|
||||
inlineTriggers: [],
|
||||
pin: null,
|
||||
triggers: [],
|
||||
followUps: [],
|
||||
segment: null,
|
||||
recaptcha: null,
|
||||
surveyClosedMessage: null,
|
||||
singleUse: {
|
||||
enabled: false,
|
||||
isEncrypted: false,
|
||||
},
|
||||
resultShareKey: null,
|
||||
};
|
||||
|
||||
const mockResponse = {
|
||||
id: "response1",
|
||||
surveyId: "survey1",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
finished: true,
|
||||
data: {
|
||||
q1: "Answer 1",
|
||||
q2: ["Option 1", "Option 2"],
|
||||
},
|
||||
language: "default",
|
||||
meta: {
|
||||
url: undefined,
|
||||
country: undefined,
|
||||
action: undefined,
|
||||
source: undefined,
|
||||
userAgent: undefined,
|
||||
},
|
||||
notes: [],
|
||||
tags: [],
|
||||
person: null,
|
||||
personAttributes: {},
|
||||
ttc: {},
|
||||
variables: {},
|
||||
contact: null,
|
||||
contactAttributes: {},
|
||||
singleUseId: null,
|
||||
};
|
||||
|
||||
test("should map questions to responses correctly", () => {
|
||||
const mapping = getQuestionResponseMapping(mockSurvey, mockResponse);
|
||||
expect(mapping).toHaveLength(2);
|
||||
expect(mapping[0]).toEqual({
|
||||
question: "Question 1",
|
||||
response: "Answer 1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
});
|
||||
expect(mapping[1]).toEqual({
|
||||
question: "Question 2",
|
||||
response: "Option 1; Option 2",
|
||||
type: TSurveyQuestionTypeEnum.MultipleChoiceMulti,
|
||||
});
|
||||
});
|
||||
|
||||
test("should handle missing response data", () => {
|
||||
const response = {
|
||||
id: "response1",
|
||||
surveyId: "survey1",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
finished: true,
|
||||
data: {},
|
||||
language: "default",
|
||||
meta: {
|
||||
url: undefined,
|
||||
country: undefined,
|
||||
action: undefined,
|
||||
source: undefined,
|
||||
userAgent: undefined,
|
||||
},
|
||||
notes: [],
|
||||
tags: [],
|
||||
person: null,
|
||||
personAttributes: {},
|
||||
ttc: {},
|
||||
variables: {},
|
||||
contact: null,
|
||||
contactAttributes: {},
|
||||
singleUseId: null,
|
||||
};
|
||||
const mapping = getQuestionResponseMapping(mockSurvey, response);
|
||||
expect(mapping).toHaveLength(2);
|
||||
expect(mapping[0].response).toBe("");
|
||||
expect(mapping[1].response).toBe("");
|
||||
});
|
||||
|
||||
test("should handle different language", () => {
|
||||
const survey = {
|
||||
...mockSurvey,
|
||||
questions: [
|
||||
{
|
||||
id: "q1",
|
||||
type: TSurveyQuestionTypeEnum.OpenText as const,
|
||||
headline: { default: "Question 1", en: "Question 1 EN" },
|
||||
required: true,
|
||||
inputType: "text" as const,
|
||||
longAnswer: false,
|
||||
charLimit: { enabled: false },
|
||||
},
|
||||
],
|
||||
};
|
||||
const response = {
|
||||
id: "response1",
|
||||
surveyId: "survey1",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
finished: true,
|
||||
data: { q1: "Answer 1" },
|
||||
language: "en",
|
||||
meta: {
|
||||
url: undefined,
|
||||
country: undefined,
|
||||
action: undefined,
|
||||
source: undefined,
|
||||
userAgent: undefined,
|
||||
},
|
||||
notes: [],
|
||||
tags: [],
|
||||
person: null,
|
||||
personAttributes: {},
|
||||
ttc: {},
|
||||
variables: {},
|
||||
contact: null,
|
||||
contactAttributes: {},
|
||||
singleUseId: null,
|
||||
};
|
||||
const mapping = getQuestionResponseMapping(survey, response);
|
||||
expect(mapping[0].question).toBe("Question 1 EN");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user