mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-03 12:21:05 -05:00
chore: search bar and preview on survey list page (#6349)
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f239ee9697
commit
68dc63ce0b
@@ -123,14 +123,14 @@ export const EnvironmentLayout = async ({ environmentId, session, children }: En
|
||||
isLicenseActive={active}
|
||||
isAccessControlAllowed={isAccessControlAllowed}
|
||||
/>
|
||||
<div id="mainContent" className="flex-1 overflow-y-auto bg-slate-50">
|
||||
<div id="mainContent" className="flex flex-1 flex-col overflow-hidden bg-slate-50">
|
||||
<TopControlBar
|
||||
environment={environment}
|
||||
environments={environments}
|
||||
membershipRole={membershipRole}
|
||||
projectPermission={projectPermission}
|
||||
/>
|
||||
<div className="mt-14">{children}</div>
|
||||
<div className="flex-1 overflow-y-auto">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,10 +44,8 @@ describe("TopControlBar", () => {
|
||||
);
|
||||
|
||||
// Check if the main div is rendered
|
||||
const mainDiv = screen.getByTestId("top-control-buttons").parentElement?.parentElement?.parentElement;
|
||||
expect(mainDiv).toHaveClass(
|
||||
"fixed inset-0 top-0 z-30 flex h-14 w-full items-center justify-end bg-slate-50 px-6"
|
||||
);
|
||||
const mainDiv = screen.getByTestId("fb__global-top-control-bar");
|
||||
expect(mainDiv).toHaveClass("flex h-14 w-full items-center justify-end bg-slate-50 px-6");
|
||||
|
||||
// Check if the mocked child component is rendered
|
||||
expect(screen.getByTestId("top-control-buttons")).toBeInTheDocument();
|
||||
|
||||
@@ -17,7 +17,9 @@ export const TopControlBar = ({
|
||||
projectPermission,
|
||||
}: SideBarProps) => {
|
||||
return (
|
||||
<div className="fixed inset-0 top-0 z-30 flex h-14 w-full items-center justify-end bg-slate-50 px-6">
|
||||
<div
|
||||
className="flex h-14 w-full items-center justify-end bg-slate-50 px-6"
|
||||
data-testid="fb__global-top-control-bar">
|
||||
<div className="shadow-xs z-10">
|
||||
<div className="flex w-fit items-center space-x-2 py-2">
|
||||
<TopControlButtons
|
||||
|
||||
@@ -7,6 +7,7 @@ import { TSurvey } from "@/modules/survey/list/types/surveys";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { wrapThrows } from "@formbricks/types/error-handlers";
|
||||
import { TProjectConfigChannel } from "@formbricks/types/project";
|
||||
@@ -43,6 +44,7 @@ export const SurveysList = ({
|
||||
currentProjectChannel,
|
||||
locale,
|
||||
}: SurveysListProps) => {
|
||||
const router = useRouter();
|
||||
const [surveys, setSurveys] = useState<TSurvey[]>([]);
|
||||
const [isFetching, setIsFetching] = useState(true);
|
||||
const [hasMore, setHasMore] = useState<boolean>(true);
|
||||
@@ -124,7 +126,10 @@ export const SurveysList = ({
|
||||
const handleDeleteSurvey = async (surveyId: string) => {
|
||||
const newSurveys = surveys.filter((survey) => survey.id !== surveyId);
|
||||
setSurveys(newSurveys);
|
||||
if (newSurveys.length === 0) setIsFetching(true);
|
||||
if (newSurveys.length === 0) {
|
||||
setIsFetching(true);
|
||||
router.refresh();
|
||||
}
|
||||
};
|
||||
|
||||
const triggerRefresh = useCallback(() => {
|
||||
|
||||
@@ -0,0 +1,388 @@
|
||||
import { TEnvironmentAuth } from "@/modules/environments/types/environment-auth";
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import { Session } from "next-auth";
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
import { TTemplateRole } from "@formbricks/types/templates";
|
||||
import { SurveysPage } from "./page";
|
||||
|
||||
// Mock all dependencies
|
||||
vi.mock("@/lib/constants", () => ({
|
||||
DEFAULT_LOCALE: "en-US",
|
||||
SURVEYS_PER_PAGE: 12,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/getPublicUrl", () => ({
|
||||
getPublicDomain: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/user/service", () => ({
|
||||
getUserLocale: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/environments/lib/utils", () => ({
|
||||
getEnvironmentAuth: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/survey/lib/project", () => ({
|
||||
getProjectWithTeamIdsByEnvironmentId: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/survey/list/components/survey-list", () => ({
|
||||
SurveysList: vi.fn(
|
||||
({ environmentId, isReadOnly, publicDomain, userId, surveysPerPage, currentProjectChannel, locale }) => (
|
||||
<div
|
||||
data-testid="surveys-list"
|
||||
data-environment-id={environmentId}
|
||||
data-readonly={isReadOnly}
|
||||
data-public-domain={publicDomain}
|
||||
data-user-id={userId}
|
||||
data-surveys-per-page={surveysPerPage}
|
||||
data-channel={currentProjectChannel}
|
||||
data-locale={locale}>
|
||||
Surveys List
|
||||
</div>
|
||||
)
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/survey/list/lib/survey", () => ({
|
||||
getSurveyCount: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/survey/templates/components/template-container", () => ({
|
||||
TemplateContainerWithPreview: vi.fn(
|
||||
({ userId, environment, project, prefilledFilters, isTemplatePage }) => (
|
||||
<div
|
||||
data-testid="template-container"
|
||||
data-user-id={userId}
|
||||
data-environment-id={environment.id}
|
||||
data-project-id={project.id}
|
||||
data-prefilled-filters={JSON.stringify(prefilledFilters)}
|
||||
data-is-template-page={isTemplatePage}>
|
||||
Template Container
|
||||
</div>
|
||||
)
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/ui/components/button", () => ({
|
||||
Button: vi.fn(({ size, asChild, children }) => (
|
||||
<button data-testid="create-survey-button" type="button" data-size={size} data-as-child={asChild}>
|
||||
{children}
|
||||
</button>
|
||||
)),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/ui/components/page-content-wrapper", () => ({
|
||||
PageContentWrapper: vi.fn(({ children }) => <div data-testid="page-content-wrapper">{children}</div>),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/ui/components/page-header", () => ({
|
||||
PageHeader: vi.fn(({ pageTitle, cta }) => (
|
||||
<div data-testid="page-header">
|
||||
<span data-testid="page-title">{pageTitle}</span>
|
||||
{cta}
|
||||
</div>
|
||||
)),
|
||||
}));
|
||||
|
||||
vi.mock("@/tolgee/server", () => ({
|
||||
getTranslate: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("next/link", () => ({
|
||||
default: vi.fn(({ href, children }) => (
|
||||
<a href={href} data-testid="link">
|
||||
{children}
|
||||
</a>
|
||||
)),
|
||||
}));
|
||||
|
||||
describe("SurveysPage", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const mockSession: Session = {
|
||||
user: {
|
||||
id: "user-123",
|
||||
},
|
||||
expires: "2024-12-31T23:59:59.999Z",
|
||||
};
|
||||
|
||||
const mockEnvironment: TEnvironment = {
|
||||
id: "env-123",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
type: "development",
|
||||
projectId: "project-123",
|
||||
appSetupCompleted: true,
|
||||
};
|
||||
|
||||
const mockProject: TProject = {
|
||||
id: "project-123",
|
||||
name: "Test Project",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
organizationId: "org-123",
|
||||
config: {
|
||||
channel: "website",
|
||||
industry: "other",
|
||||
},
|
||||
styling: {
|
||||
brandColor: {
|
||||
light: "#000000",
|
||||
},
|
||||
},
|
||||
} as TProject;
|
||||
|
||||
const mockEnvironmentAuth: TEnvironmentAuth = {
|
||||
session: mockSession,
|
||||
environment: mockEnvironment,
|
||||
project: mockProject,
|
||||
organization: {
|
||||
id: "org-123",
|
||||
name: "Test Organization",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
billing: null,
|
||||
} as any,
|
||||
currentUserMembership: {
|
||||
userId: "user-123",
|
||||
organizationId: "org-123",
|
||||
accepted: true,
|
||||
role: "admin",
|
||||
} as any,
|
||||
projectPermission: null,
|
||||
isMember: true,
|
||||
isOwner: true,
|
||||
isManager: false,
|
||||
isBilling: false,
|
||||
hasReadAccess: true,
|
||||
hasReadWriteAccess: true,
|
||||
hasManageAccess: true,
|
||||
isReadOnly: false,
|
||||
};
|
||||
|
||||
const mockTranslate = vi.fn((key: string) => key);
|
||||
const mockGetPublicDomain = vi.fn();
|
||||
const mockGetUserLocale = vi.fn();
|
||||
const mockGetEnvironmentAuth = vi.fn();
|
||||
const mockGetProjectWithTeamIdsByEnvironmentId = vi.fn();
|
||||
const mockGetSurveyCount = vi.fn();
|
||||
const mockGetTranslate = vi.fn();
|
||||
|
||||
beforeEach(async () => {
|
||||
mockGetPublicDomain.mockReturnValue("https://app.formbricks.com");
|
||||
mockGetUserLocale.mockResolvedValue("en-US");
|
||||
mockGetEnvironmentAuth.mockResolvedValue(mockEnvironmentAuth);
|
||||
mockGetProjectWithTeamIdsByEnvironmentId.mockResolvedValue(mockProject);
|
||||
mockGetSurveyCount.mockResolvedValue(5);
|
||||
mockGetTranslate.mockResolvedValue(mockTranslate);
|
||||
|
||||
// Set up mocks
|
||||
const { getPublicDomain } = await import("@/lib/getPublicUrl");
|
||||
const { getUserLocale } = await import("@/lib/user/service");
|
||||
const { getEnvironmentAuth } = await import("@/modules/environments/lib/utils");
|
||||
const { getProjectWithTeamIdsByEnvironmentId } = await import("@/modules/survey/lib/project");
|
||||
const { getSurveyCount } = await import("@/modules/survey/list/lib/survey");
|
||||
const { getTranslate } = await import("@/tolgee/server");
|
||||
|
||||
vi.mocked(getPublicDomain).mockImplementation(mockGetPublicDomain);
|
||||
vi.mocked(getUserLocale).mockImplementation(mockGetUserLocale);
|
||||
vi.mocked(getEnvironmentAuth).mockImplementation(mockGetEnvironmentAuth);
|
||||
vi.mocked(getProjectWithTeamIdsByEnvironmentId).mockImplementation(
|
||||
mockGetProjectWithTeamIdsByEnvironmentId
|
||||
);
|
||||
vi.mocked(getSurveyCount).mockImplementation(mockGetSurveyCount);
|
||||
vi.mocked(getTranslate).mockImplementation(mockGetTranslate);
|
||||
});
|
||||
|
||||
test("throws error when project is not found", async () => {
|
||||
mockGetProjectWithTeamIdsByEnvironmentId.mockResolvedValue(null);
|
||||
mockTranslate.mockReturnValue("Project not found");
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
await expect(SurveysPage({ params, searchParams })).rejects.toThrow("Project not found");
|
||||
|
||||
expect(mockGetProjectWithTeamIdsByEnvironmentId).toHaveBeenCalledWith("env-123");
|
||||
expect(mockTranslate).toHaveBeenCalledWith("common.project_not_found");
|
||||
});
|
||||
|
||||
test("redirects to billing when isBilling is true", async () => {
|
||||
const { redirect } = await import("next/navigation");
|
||||
const mockRedirect = vi.mocked(redirect);
|
||||
|
||||
mockGetEnvironmentAuth.mockResolvedValue({
|
||||
...mockEnvironmentAuth,
|
||||
isBilling: true,
|
||||
});
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
await SurveysPage({ params, searchParams });
|
||||
|
||||
expect(mockRedirect).toHaveBeenCalledWith("/environments/env-123/settings/billing");
|
||||
});
|
||||
|
||||
test("renders TemplateContainerWithPreview when survey count is 0", async () => {
|
||||
mockGetSurveyCount.mockResolvedValue(0);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({ role: "product_manager" as TTemplateRole });
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
expect(screen.getByTestId("template-container")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("template-container")).toHaveAttribute("data-user-id", "user-123");
|
||||
expect(screen.getByTestId("template-container")).toHaveAttribute("data-environment-id", "env-123");
|
||||
expect(screen.getByTestId("template-container")).toHaveAttribute("data-project-id", "project-123");
|
||||
expect(screen.getByTestId("template-container")).toHaveAttribute("data-is-template-page", "false");
|
||||
|
||||
const prefilledFilters = JSON.parse(
|
||||
screen.getByTestId("template-container").getAttribute("data-prefilled-filters") || "[]"
|
||||
);
|
||||
expect(prefilledFilters).toEqual(["website", "other", "product_manager"]);
|
||||
});
|
||||
|
||||
test("renders surveys list when survey count is greater than 0", async () => {
|
||||
mockGetSurveyCount.mockResolvedValue(5);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
expect(screen.getByTestId("page-content-wrapper")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("page-header")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("page-title")).toHaveTextContent("common.surveys");
|
||||
expect(screen.getByTestId("create-survey-button")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("surveys-list")).toBeInTheDocument();
|
||||
|
||||
// Check SurveysList props
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-environment-id", "env-123");
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-readonly", "false");
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute(
|
||||
"data-public-domain",
|
||||
"https://app.formbricks.com"
|
||||
);
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-user-id", "user-123");
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-surveys-per-page", "12");
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-channel", "website");
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-locale", "en-US");
|
||||
});
|
||||
|
||||
test("does not render create survey button when user is read-only", async () => {
|
||||
mockGetEnvironmentAuth.mockResolvedValue({
|
||||
...mockEnvironmentAuth,
|
||||
isReadOnly: true,
|
||||
});
|
||||
mockGetSurveyCount.mockResolvedValue(5);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
expect(screen.getByTestId("page-header")).toBeInTheDocument();
|
||||
expect(screen.queryByTestId("create-survey-button")).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-readonly", "true");
|
||||
});
|
||||
|
||||
test("renders TemplateContainer when user is read-only and no surveys exist", async () => {
|
||||
mockGetEnvironmentAuth.mockResolvedValue({
|
||||
...mockEnvironmentAuth,
|
||||
isReadOnly: true,
|
||||
});
|
||||
mockGetSurveyCount.mockResolvedValue(0);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
// When survey count is 0, it should render TemplateContainer regardless of read-only status
|
||||
expect(screen.getByTestId("template-container")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("template-container")).toHaveAttribute("data-user-id", "user-123");
|
||||
expect(screen.getByTestId("template-container")).toHaveAttribute("data-environment-id", "env-123");
|
||||
});
|
||||
|
||||
test("handles project with null channel and industry", async () => {
|
||||
const projectWithNullConfig = {
|
||||
...mockProject,
|
||||
config: {
|
||||
channel: null,
|
||||
industry: null,
|
||||
},
|
||||
};
|
||||
mockGetProjectWithTeamIdsByEnvironmentId.mockResolvedValue(projectWithNullConfig);
|
||||
mockGetSurveyCount.mockResolvedValue(0);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
expect(screen.getByTestId("template-container")).toBeInTheDocument();
|
||||
const prefilledFilters = JSON.parse(
|
||||
screen.getByTestId("template-container").getAttribute("data-prefilled-filters") || "[]"
|
||||
);
|
||||
expect(prefilledFilters).toEqual([null, null, null]);
|
||||
});
|
||||
|
||||
test("handles project with null styling", async () => {
|
||||
const projectWithNullStyling = {
|
||||
...mockProject,
|
||||
styling: null,
|
||||
};
|
||||
mockGetProjectWithTeamIdsByEnvironmentId.mockResolvedValue(projectWithNullStyling);
|
||||
mockGetSurveyCount.mockResolvedValue(0);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
expect(screen.getByTestId("template-container")).toBeInTheDocument();
|
||||
// Should handle null styling gracefully
|
||||
});
|
||||
|
||||
test("handles getUserLocale returning null", async () => {
|
||||
mockGetUserLocale.mockResolvedValue(null);
|
||||
mockGetSurveyCount.mockResolvedValue(5);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
expect(screen.getByTestId("surveys-list")).toHaveAttribute("data-locale", "en-US");
|
||||
});
|
||||
|
||||
test("creates survey button with correct link", async () => {
|
||||
mockGetSurveyCount.mockResolvedValue(5);
|
||||
|
||||
const params = Promise.resolve({ environmentId: "env-123" });
|
||||
const searchParams = Promise.resolve({});
|
||||
|
||||
const result = await SurveysPage({ params, searchParams });
|
||||
render(result);
|
||||
|
||||
expect(screen.getByTestId("link")).toHaveAttribute("href", "/environments/env-123/surveys/templates");
|
||||
expect(screen.getByTestId("create-survey-button")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -2,10 +2,10 @@ import { DEFAULT_LOCALE, SURVEYS_PER_PAGE } from "@/lib/constants";
|
||||
import { getPublicDomain } from "@/lib/getPublicUrl";
|
||||
import { getUserLocale } from "@/lib/user/service";
|
||||
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
|
||||
import { TemplateList } from "@/modules/survey/components/template-list";
|
||||
import { getProjectWithTeamIdsByEnvironmentId } from "@/modules/survey/lib/project";
|
||||
import { SurveysList } from "@/modules/survey/list/components/survey-list";
|
||||
import { getSurveyCount } from "@/modules/survey/list/lib/survey";
|
||||
import { TemplateContainerWithPreview } from "@/modules/survey/templates/components/template-container";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||
@@ -73,6 +73,17 @@ export const SurveysPage = async ({
|
||||
highlightBorderColor: null,
|
||||
};
|
||||
|
||||
if (surveyCount === 0)
|
||||
return (
|
||||
<TemplateContainerWithPreview
|
||||
userId={session.user.id}
|
||||
environment={environment}
|
||||
project={projectWithRequiredProps}
|
||||
prefilledFilters={prefilledFilters}
|
||||
isTemplatePage={false}
|
||||
/>
|
||||
);
|
||||
|
||||
let content;
|
||||
if (surveyCount > 0) {
|
||||
content = (
|
||||
@@ -101,20 +112,6 @@ export const SurveysPage = async ({
|
||||
</h2>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
content = (
|
||||
<>
|
||||
<h1 className="px-6 text-3xl font-extrabold text-slate-700">
|
||||
{t("environments.surveys.all_set_time_to_create_first_survey")}
|
||||
</h1>
|
||||
<TemplateList
|
||||
environmentId={environment.id}
|
||||
project={projectWithRequiredProps}
|
||||
userId={session.user.id}
|
||||
prefilledFilters={prefilledFilters}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <PageContentWrapper>{content}</PageContentWrapper>;
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
import { Project } from "@prisma/client";
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { cleanup, render, screen } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { TProjectConfigChannel, TProjectConfigIndustry } from "@formbricks/types/project";
|
||||
import { TTemplateRole } from "@formbricks/types/templates";
|
||||
import { TemplateContainerWithPreview } from "./template-container";
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock("@/app/lib/templates", () => ({
|
||||
customSurveyTemplate: vi.fn(() => ({
|
||||
preset: {
|
||||
questions: [{ id: "q1" }],
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/survey/components/template-list", () => ({
|
||||
TemplateList: vi.fn(() => <div data-testid="template-list">Template List</div>),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/survey/templates/components/menu-bar", () => ({
|
||||
MenuBar: vi.fn(() => <div data-testid="menu-bar">Menu Bar</div>),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/ui/components/preview-survey", () => ({
|
||||
PreviewSurvey: vi.fn(() => <div data-testid="preview-survey">Preview Survey</div>),
|
||||
}));
|
||||
|
||||
vi.mock("@/modules/ui/components/search-bar", () => ({
|
||||
SearchBar: vi.fn(({ placeholder, onChange, value }) => (
|
||||
<input
|
||||
data-testid="search-bar"
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
)),
|
||||
}));
|
||||
|
||||
vi.mock("../lib/minimal-survey", () => ({
|
||||
getMinimalSurvey: vi.fn(() => ({})),
|
||||
}));
|
||||
|
||||
const mockProject = {
|
||||
id: "project1",
|
||||
name: "Test Project",
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
organizationId: "org1",
|
||||
config: {
|
||||
channel: "website" as TProjectConfigChannel,
|
||||
industry: "technology" as TProjectConfigIndustry,
|
||||
},
|
||||
} as Project;
|
||||
|
||||
const mockEnvironment = {
|
||||
id: "env1",
|
||||
appSetupCompleted: true,
|
||||
};
|
||||
|
||||
const mockPrefilledFilters: (TProjectConfigChannel | TProjectConfigIndustry | TTemplateRole | null)[] = [];
|
||||
|
||||
describe("TemplateContainerWithPreview", () => {
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test("renders MenuBar when isTemplatePage is true", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("menu-bar")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("does not render MenuBar when isTemplatePage is false", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByTestId("menu-bar")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("displays correct title when isTemplatePage is true", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("environments.surveys.templates.create_a_new_survey")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("displays correct title when isTemplatePage is false", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("environments.surveys.all_set_time_to_create_first_survey")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders SearchBar with correct placeholder", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={true}
|
||||
/>
|
||||
);
|
||||
|
||||
const searchBar = screen.getByTestId("search-bar");
|
||||
expect(searchBar).toBeInTheDocument();
|
||||
expect(searchBar).toHaveAttribute("placeholder", "common.search");
|
||||
});
|
||||
|
||||
test("renders TemplateList component", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("template-list")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders PreviewSurvey component in aside", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={true}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("preview-survey")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("has correct container structure with h-full class", () => {
|
||||
render(
|
||||
<TemplateContainerWithPreview
|
||||
project={mockProject}
|
||||
environment={mockEnvironment}
|
||||
userId="user1"
|
||||
prefilledFilters={mockPrefilledFilters}
|
||||
isTemplatePage={true}
|
||||
/>
|
||||
);
|
||||
|
||||
const container = screen.getByTestId("menu-bar").parentElement;
|
||||
expect(container).toHaveClass("flex", "h-full", "flex-col");
|
||||
});
|
||||
});
|
||||
@@ -18,6 +18,7 @@ type TemplateContainerWithPreviewProps = {
|
||||
environment: Pick<Environment, "id" | "appSetupCompleted">;
|
||||
userId: string;
|
||||
prefilledFilters: (TProjectConfigChannel | TProjectConfigIndustry | TTemplateRole | null)[];
|
||||
isTemplatePage?: boolean;
|
||||
};
|
||||
|
||||
export const TemplateContainerWithPreview = ({
|
||||
@@ -25,6 +26,7 @@ export const TemplateContainerWithPreview = ({
|
||||
environment,
|
||||
userId,
|
||||
prefilledFilters,
|
||||
isTemplatePage = true,
|
||||
}: TemplateContainerWithPreviewProps) => {
|
||||
const { t } = useTranslate();
|
||||
const initialTemplate = customSurveyTemplate(t);
|
||||
@@ -34,12 +36,14 @@ export const TemplateContainerWithPreview = ({
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<MenuBar />
|
||||
{isTemplatePage && <MenuBar />}
|
||||
<div className="relative z-0 flex flex-1 overflow-hidden">
|
||||
<div className="flex-1 flex-col overflow-auto bg-slate-50">
|
||||
<div className="mb-3 ml-6 mt-6 flex flex-col items-center justify-between md:flex-row md:items-end">
|
||||
<h1 className="text-2xl font-bold text-slate-800">
|
||||
{t("environments.surveys.templates.create_a_new_survey")}
|
||||
{isTemplatePage
|
||||
? t("environments.surveys.templates.create_a_new_survey")
|
||||
: t("environments.surveys.all_set_time_to_create_first_survey")}
|
||||
</h1>
|
||||
<div className="px-6">
|
||||
<SearchBar
|
||||
|
||||
@@ -17,32 +17,4 @@ describe("PageContentWrapper", () => {
|
||||
|
||||
expect(getByText("Test Content")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("applies default classes", () => {
|
||||
const { container } = render(
|
||||
<PageContentWrapper>
|
||||
<div>Test Content</div>
|
||||
</PageContentWrapper>
|
||||
);
|
||||
|
||||
const wrapper = container.firstChild as HTMLElement;
|
||||
expect(wrapper).toHaveClass("h-full");
|
||||
expect(wrapper).toHaveClass("space-y-6");
|
||||
expect(wrapper).toHaveClass("p-6");
|
||||
});
|
||||
|
||||
test("applies additional className when provided", () => {
|
||||
const { container } = render(
|
||||
<PageContentWrapper className="rounded-lg bg-gray-100">
|
||||
<div>Test Content</div>
|
||||
</PageContentWrapper>
|
||||
);
|
||||
|
||||
const wrapper = container.firstChild as HTMLElement;
|
||||
expect(wrapper).toHaveClass("h-full");
|
||||
expect(wrapper).toHaveClass("space-y-6");
|
||||
expect(wrapper).toHaveClass("p-6");
|
||||
expect(wrapper).toHaveClass("bg-gray-100");
|
||||
expect(wrapper).toHaveClass("rounded-lg");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,5 +6,5 @@ interface PageContentWrapperProps {
|
||||
}
|
||||
|
||||
export const PageContentWrapper = ({ children, className }: PageContentWrapperProps) => {
|
||||
return <div className={cn("h-full space-y-6 p-6", className)}>{children}</div>;
|
||||
return <div className={cn("min-h-full space-y-6 p-6", className)}>{children}</div>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user