mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-06 05:40:02 -06:00
fix: survey preview not working for suid enabled link surveys backport (#6259)
This commit is contained in:
@@ -5,6 +5,7 @@ import { ShareSurveyModal } from "@/app/(app)/environments/[environmentId]/surve
|
||||
import { SurveyStatusDropdown } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/SurveyStatusDropdown";
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { EditPublicSurveyAlertDialog } from "@/modules/survey/components/edit-public-survey-alert-dialog";
|
||||
import { useSingleUseId } from "@/modules/survey/hooks/useSingleUseId";
|
||||
import { copySurveyToOtherEnvironmentAction } from "@/modules/survey/list/actions";
|
||||
import { Badge } from "@/modules/ui/components/badge";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
@@ -12,7 +13,7 @@ import { IconBar } from "@/modules/ui/components/iconbar";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { BellRing, Eye, SquarePenIcon } from "lucide-react";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
@@ -48,17 +49,16 @@ export const SurveyAnalysisCTA = ({
|
||||
isFormbricksCloud,
|
||||
}: SurveyAnalysisCTAProps) => {
|
||||
const { t } = useTranslate();
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [modalState, setModalState] = useState<ModalState>({
|
||||
start: searchParams.get("share") === "true",
|
||||
share: false,
|
||||
});
|
||||
|
||||
const surveyUrl = useMemo(() => `${publicDomain}/s/${survey.id}`, [survey.id, publicDomain]);
|
||||
const { refreshSingleUseId } = useSingleUseId(survey);
|
||||
|
||||
const widgetSetupCompleted = survey.type === "app" && environment.appSetupCompleted;
|
||||
|
||||
@@ -102,9 +102,18 @@ export const SurveyAnalysisCTA = ({
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const getPreviewUrl = () => {
|
||||
const separator = surveyUrl.includes("?") ? "&" : "?";
|
||||
return `${surveyUrl}${separator}preview=true`;
|
||||
const getPreviewUrl = async () => {
|
||||
const surveyUrl = new URL(`${publicDomain}/s/${survey.id}`);
|
||||
|
||||
if (survey.singleUse?.enabled) {
|
||||
const newId = await refreshSingleUseId();
|
||||
if (newId) {
|
||||
surveyUrl.searchParams.set("suId", newId);
|
||||
}
|
||||
}
|
||||
|
||||
surveyUrl.searchParams.set("preview", "true");
|
||||
return surveyUrl.toString();
|
||||
};
|
||||
|
||||
const [isCautionDialogOpen, setIsCautionDialogOpen] = useState(false);
|
||||
@@ -119,7 +128,10 @@ export const SurveyAnalysisCTA = ({
|
||||
{
|
||||
icon: Eye,
|
||||
tooltip: t("common.preview"),
|
||||
onClick: () => window.open(getPreviewUrl(), "_blank"),
|
||||
onClick: async () => {
|
||||
const previewUrl = await getPreviewUrl();
|
||||
window.open(previewUrl, "_blank");
|
||||
},
|
||||
isVisible: survey.type === "link",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ import { toast } from "react-hot-toast";
|
||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
import { getSurveyUrl } from "../../utils";
|
||||
|
||||
vi.mock("react-hot-toast", () => ({
|
||||
toast: {
|
||||
@@ -11,6 +12,24 @@ vi.mock("react-hot-toast", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock the useSingleUseId hook
|
||||
vi.mock("@/modules/survey/hooks/useSingleUseId", () => ({
|
||||
useSingleUseId: vi.fn(() => ({
|
||||
singleUseId: "test-single-use-id",
|
||||
refreshSingleUseId: vi.fn().mockResolvedValue("test-single-use-id"),
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock the survey utils
|
||||
vi.mock("../../utils", () => ({
|
||||
getSurveyUrl: vi.fn((survey, publicDomain, language) => {
|
||||
if (language && language !== "en") {
|
||||
return `${publicDomain}/s/${survey.id}?lang=${language}`;
|
||||
}
|
||||
return `${publicDomain}/s/${survey.id}`;
|
||||
}),
|
||||
}));
|
||||
|
||||
const survey: TSurvey = {
|
||||
id: "survey-id",
|
||||
name: "Test Survey",
|
||||
@@ -161,7 +180,7 @@ describe("ShareSurveyLink", () => {
|
||||
expect(toast.success).toHaveBeenCalledWith("common.copied_to_clipboard");
|
||||
});
|
||||
|
||||
test("opens the preview link in a new tab when preview button is clicked (no query params)", () => {
|
||||
test("opens the preview link in a new tab when preview button is clicked (no query params)", async () => {
|
||||
render(
|
||||
<ShareSurveyLink
|
||||
survey={survey}
|
||||
@@ -175,10 +194,13 @@ describe("ShareSurveyLink", () => {
|
||||
const previewButton = screen.getByLabelText("environments.surveys.preview_survey_in_a_new_tab");
|
||||
fireEvent.click(previewButton);
|
||||
|
||||
// Wait for the async function to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(global.open).toHaveBeenCalledWith(`${surveyUrl}?preview=true`, "_blank");
|
||||
});
|
||||
|
||||
test("opens the preview link in a new tab when preview button is clicked (with query params)", () => {
|
||||
test("opens the preview link in a new tab when preview button is clicked (with query params)", async () => {
|
||||
const surveyWithParamsUrl = `${publicDomain}/s/survey-id?foo=bar`;
|
||||
render(
|
||||
<ShareSurveyLink
|
||||
@@ -193,6 +215,9 @@ describe("ShareSurveyLink", () => {
|
||||
const previewButton = screen.getByLabelText("environments.surveys.preview_survey_in_a_new_tab");
|
||||
fireEvent.click(previewButton);
|
||||
|
||||
// Wait for the async function to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(global.open).toHaveBeenCalledWith(`${surveyWithParamsUrl}&preview=true`, "_blank");
|
||||
});
|
||||
|
||||
@@ -215,7 +240,9 @@ describe("ShareSurveyLink", () => {
|
||||
});
|
||||
|
||||
test("updates the survey URL when the language is changed", () => {
|
||||
const { rerender } = render(
|
||||
const mockGetSurveyUrl = vi.mocked(getSurveyUrl);
|
||||
|
||||
render(
|
||||
<ShareSurveyLink
|
||||
survey={survey}
|
||||
publicDomain={publicDomain}
|
||||
@@ -231,16 +258,7 @@ describe("ShareSurveyLink", () => {
|
||||
const germanOption = screen.getByText("German");
|
||||
fireEvent.click(germanOption);
|
||||
|
||||
rerender(
|
||||
<ShareSurveyLink
|
||||
survey={survey}
|
||||
publicDomain={publicDomain}
|
||||
surveyUrl={surveyUrl}
|
||||
setSurveyUrl={setSurveyUrl}
|
||||
locale={locale}
|
||||
/>
|
||||
);
|
||||
expect(setSurveyUrl).toHaveBeenCalled();
|
||||
expect(surveyUrl).toContain("lang=de");
|
||||
expect(mockGetSurveyUrl).toHaveBeenCalledWith(survey, publicDomain, "de");
|
||||
expect(setSurveyUrl).toHaveBeenCalledWith(`${publicDomain}/s/${survey.id}?lang=de`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useSingleUseId } from "@/modules/survey/hooks/useSingleUseId";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Copy, SquareArrowOutUpRight } from "lucide-react";
|
||||
@@ -32,6 +33,22 @@ export const ShareSurveyLink = ({
|
||||
setSurveyUrl(url);
|
||||
};
|
||||
|
||||
const { refreshSingleUseId } = useSingleUseId(survey);
|
||||
|
||||
const getPreviewUrl = async () => {
|
||||
const previewUrl = new URL(surveyUrl);
|
||||
|
||||
if (survey.singleUse?.enabled) {
|
||||
const newId = await refreshSingleUseId();
|
||||
if (newId) {
|
||||
previewUrl.searchParams.set("suId", newId);
|
||||
}
|
||||
}
|
||||
|
||||
previewUrl.searchParams.set("preview", "true");
|
||||
return previewUrl.toString();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={"flex max-w-full flex-col items-center justify-center gap-2 md:flex-row"}>
|
||||
<SurveyLinkDisplay surveyUrl={surveyUrl} key={surveyUrl} />
|
||||
@@ -53,14 +70,9 @@ export const ShareSurveyLink = ({
|
||||
title={t("environments.surveys.preview_survey_in_a_new_tab")}
|
||||
aria-label={t("environments.surveys.preview_survey_in_a_new_tab")}
|
||||
disabled={!surveyUrl}
|
||||
onClick={() => {
|
||||
let previewUrl = surveyUrl;
|
||||
if (previewUrl.includes("?")) {
|
||||
previewUrl += "&preview=true";
|
||||
} else {
|
||||
previewUrl += "?preview=true";
|
||||
}
|
||||
window.open(previewUrl, "_blank");
|
||||
onClick={async () => {
|
||||
const url = await getPreviewUrl();
|
||||
window.open(url, "_blank");
|
||||
}}>
|
||||
{t("common.preview")}
|
||||
<SquareArrowOutUpRight />
|
||||
|
||||
@@ -14,7 +14,7 @@ export const useSingleUseId = (survey: TSurvey | TSurveyList) => {
|
||||
if (survey.singleUse?.enabled) {
|
||||
const response = await generateSingleUseIdsAction({
|
||||
surveyId: survey.id,
|
||||
isEncrypted: !!survey.singleUse?.isEncrypted,
|
||||
isEncrypted: Boolean(survey.singleUse?.isEncrypted),
|
||||
count: 1,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user