- |
+ |
{rowLabel}
@@ -74,7 +74,7 @@ export const MatrixQuestionSummary = ({
)}>
+ className="hover:outline-brand-dark m-1 flex h-full w-40 cursor-default items-center justify-center rounded p-4 text-sm text-slate-950 hover:outline">
{percentage}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/MultipleChoiceSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/MultipleChoiceSummary.tsx
index aa2b933459..b69a7f56ed 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/MultipleChoiceSummary.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/MultipleChoiceSummary.tsx
@@ -75,8 +75,8 @@ export const MultipleChoiceSummary = ({
{result.others && result.others.length > 0 && (
- Other values found
- {surveyType === "app" && "User"}
+ Other values found
+ {surveyType === "app" && "User"}
{result.others
.filter((otherValue) => otherValue.value !== "")
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/PictureChoiceSummary.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/PictureChoiceSummary.tsx
index 523eedb2fe..18a3c9ab25 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/PictureChoiceSummary.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/PictureChoiceSummary.tsx
@@ -29,7 +29,7 @@ export const PictureChoiceSummary = ({
{results.map((result) => (
-
+
-
+
{formatTextWithSlashes(
recallToHeadline(questionSummary.question.headline, survey, true, "default", attributeClasses)[
@@ -52,19 +52,19 @@ export const QuestionSummaryHeader = ({
-
- {questionType && }
+
+ {questionType && }
{questionType ? questionType.label : "Unknown Question Type"} Question
{showResponses && (
-
+
{`${questionSummary.responseCount} Responses`}
)}
{insights}
{!questionSummary.question.required && (
- Optional
+ Optional
)}
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx
index 87b38762a6..afe7090732 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey.tsx
@@ -70,19 +70,19 @@ export const ShareEmbedSurvey = ({ survey, open, setOpen, webAppUrl, user }: Sha
+ className="flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-sm text-slate-500 hover:border-slate-200 md:p-8">
Configure alerts
+ className="flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-sm text-slate-500 hover:border-slate-200 md:p-8">
Setup integrations
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareSurveyResults.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareSurveyResults.tsx
index d435f46368..791d55c78b 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareSurveyResults.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareSurveyResults.tsx
@@ -60,12 +60,12 @@ export const ShareSurveyResults = ({
-
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs.tsx
index b2a87350b5..a4ca16754b 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs.tsx
@@ -36,7 +36,7 @@ export const SummaryDropOffs = ({ dropOff }: SummaryDropOffsProps) => {
{quesDropOff.ttc > 0 ? (quesDropOff.ttc / 1000).toFixed(2) + "s" : "N/A"}
{quesDropOff.impressions}
-
+
{quesDropOff.dropOffCount}
({Math.round(quesDropOff.dropOffPercentage)}%)
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/EmailTab.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/EmailTab.tsx
index 53105da597..644c4d4a63 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/EmailTab.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedTabs/EmailTab.tsx
@@ -49,7 +49,7 @@ export const EmailTab = ({ surveyId, email }: EmailTabProps) => {
};
return (
-
+
{showEmbed ? (
!disabled && !isDisabledComboBox && filterValue && setOpen(true)}
className={clsx(
- "group flex items-center justify-between rounded-md rounded-l-none bg-white px-3 py-2 text-sm",
+ "group flex items-center justify-between rounded-md rounded-l-none bg-white px-3 py-2 text-sm",
disabled || isDisabledComboBox || !filterValue ? "opacity-50" : "cursor-pointer"
)}>
{filterComboBoxValue && filterComboBoxValue?.length > 0 ? (
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/ResponseFilter.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/ResponseFilter.tsx
index cf72148eb6..2c0acd5187 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/ResponseFilter.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/ResponseFilter.tsx
@@ -201,10 +201,10 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
+ className="w-[300px] border-slate-200 bg-slate-100 p-6 sm:w-[400px] md:w-[750px] lg:w-[1000px]">
Show all responses that match
- Show all responses where...
+ Show all responses where...
-
+
In-progress
-
+
Paused
-
+
Completed
diff --git a/apps/web/app/(auth)/auth/components/Testimonial.tsx b/apps/web/app/(auth)/auth/components/Testimonial.tsx
index f5e10e39af..53d3094ccf 100644
--- a/apps/web/app/(auth)/auth/components/Testimonial.tsx
+++ b/apps/web/app/(auth)/auth/components/Testimonial.tsx
@@ -6,7 +6,7 @@ import Image from "next/image";
export const Testimonial = () => {
return (
-
+
Turn customer insights into irresistible experiences.
diff --git a/apps/web/app/(auth)/auth/login/components/SigninForm.tsx b/apps/web/app/(auth)/auth/login/components/SigninForm.tsx
index b00ad7dfb3..5cdc428198 100644
--- a/apps/web/app/(auth)/auth/login/components/SigninForm.tsx
+++ b/apps/web/app/(auth)/auth/login/components/SigninForm.tsx
@@ -244,7 +244,7 @@ export const SigninForm = ({
{publicSignUpEnabled && !totpLogin && isMultiOrgEnabled && (
-
+
New to Formbricks?
)}
-
+
Have an account?
- {headline}
- {description}
+ {headline}
+ {description}
{children}
diff --git a/apps/web/app/health/page.tsx b/apps/web/app/health/page.tsx
index a9dafe1d23..0fab6e0774 100644
--- a/apps/web/app/health/page.tsx
+++ b/apps/web/app/health/page.tsx
@@ -19,6 +19,7 @@ const checkDatabaseConnection = async () => {
try {
await prisma.$queryRaw`SELECT 1`;
} catch (e) {
+ console.error("Database connection error:", e);
throw new Error("Database could not be reached");
}
};
diff --git a/apps/web/playwright/action.spec.ts b/apps/web/playwright/action.spec.ts
index b758dd1477..d58bc9e336 100644
--- a/apps/web/playwright/action.spec.ts
+++ b/apps/web/playwright/action.spec.ts
@@ -1,19 +1,20 @@
-import { actions, users } from "@/playwright/utils/mock";
-import { Page, expect, test } from "@playwright/test";
-import { finishOnboarding, login, signUpAndLogin } from "./utils/helper";
-
-const createNoCodeClickAction = async (
- page: Page,
- username: string,
- email: string,
- password: string,
- actionName: string,
- description: string,
- selector: string
-) => {
- await signUpAndLogin(page, username, email, password);
- await finishOnboarding(page);
+import { actions } from "@/playwright/utils/mock";
+import { Page, expect } from "@playwright/test";
+import { test } from "./lib/fixtures";
+const createNoCodeClickAction = async ({
+ page,
+ name,
+ description,
+ selector,
+}: {
+ page: Page;
+ name: string;
+ description: string;
+ selector: string;
+}) => {
+ await page.goto("/");
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
await page.getByRole("link", { name: "Actions" }).click();
await page.waitForURL(/\/environments\/[^/]+\/actions/);
@@ -22,7 +23,7 @@ const createNoCodeClickAction = async (
// User fills the action name and description
await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actionName);
+ await page.getByLabel("What did your user do?").fill(name);
await expect(page.getByLabel("Description")).toBeVisible();
await page.getByLabel("Description").fill(description);
@@ -36,27 +37,32 @@ const createNoCodeClickAction = async (
await expect(page.locator("[name='noCodeConfig.elementSelector.cssSelector']")).toBeVisible();
await page.locator("[name='noCodeConfig.elementSelector.cssSelector']").fill(selector);
await page.getByRole("button", { name: "Create action", exact: true }).click();
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(500);
+
+ const successToast = await page.waitForSelector(".formbricks__toast__success");
+ expect(successToast).toBeTruthy();
+
+ const actionButton = page.getByTitle(name);
+ await expect(actionButton).toBeVisible();
};
-const createNoCodePageViewAction = async (
- page: Page,
- username: string,
- email: string,
- password: string,
- actionName: string,
- description: string,
+const createNoCodePageViewAction = async ({
+ page,
+ name,
+ description,
+ matcher,
+ noCodeType,
+}: {
+ page: Page;
+ name: string;
+ description: string;
matcher: {
label: string;
value: string;
- },
- testURL: string,
- noCodeType: string
-) => {
- await signUpAndLogin(page, username, email, password);
- await finishOnboarding(page);
-
+ };
+ noCodeType: string;
+}) => {
+ await page.goto("/");
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
await page.getByRole("link", { name: "Actions" }).click();
await page.waitForURL(/\/environments\/[^/]+\/actions/);
@@ -65,7 +71,7 @@ const createNoCodePageViewAction = async (
// User fills the action name and description
await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actionName);
+ await page.getByLabel("What did your user do?").fill(name);
await expect(page.getByLabel("Description")).toBeVisible();
await page.getByLabel("Description").fill(description);
@@ -83,30 +89,29 @@ const createNoCodePageViewAction = async (
// User fills the Page URL to track
await page.locator("[name='noCodeConfig.urlFilters.0.value']").fill(matcher.value);
- // User fills the Test URL to track
- await page.locator("[name='noCodeConfig.urlFilters.testUrl']").fill(testURL);
-
- // User clicks the Test Match button
- await page.getByRole("button", { name: "Test Match", exact: true }).click();
-
// User clicks the Create Action button
await page.getByRole("button", { name: "Create action", exact: true }).click();
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(500);
+
+ const successToast = await page.waitForSelector(".formbricks__toast__success");
+ expect(successToast).toBeTruthy();
+
+ const actionButton = page.getByTitle(name);
+ await expect(actionButton).toBeVisible();
};
-const createNoCodeAction = async (
- page: Page,
- username: string,
- email: string,
- password: string,
- actionName: string,
- description: string,
- noCodeType: string
-) => {
- await signUpAndLogin(page, username, email, password);
- await finishOnboarding(page);
-
+const createNoCodeAction = async ({
+ name,
+ description,
+ noCodeType,
+ page,
+}: {
+ page: Page;
+ name: string;
+ description: string;
+ noCodeType: string;
+}) => {
+ await page.goto("/");
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
await page.getByRole("link", { name: "Actions" }).click();
await page.waitForURL(/\/environments\/[^/]+\/actions/);
@@ -115,7 +120,7 @@ const createNoCodeAction = async (
// User fills the action name and description
await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actionName);
+ await page.getByLabel("What did your user do?").fill(name);
await expect(page.getByLabel("Description")).toBeVisible();
await page.getByLabel("Description").fill(description);
@@ -125,8 +130,50 @@ const createNoCodeAction = async (
// User clicks the Create Action button
await page.getByRole("button", { name: "Create action", exact: true }).click();
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(500);
+
+ const successToast = await page.waitForSelector(".formbricks__toast__success");
+ expect(successToast).toBeTruthy();
+
+ const actionButton = page.getByTitle(name);
+ await expect(actionButton).toBeVisible();
+};
+
+const createCodeAction = async ({
+ description,
+ key,
+ name,
+ page,
+}: {
+ page: Page;
+ name: string;
+ description: string;
+ key: string;
+}) => {
+ await page.getByRole("link", { name: "Actions" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/actions/);
+
+ // Add Action button
+ await page.getByRole("button", { name: "Add Action" }).click();
+
+ await expect(page.getByLabel("What did your user do?")).toBeVisible();
+ await page.getByLabel("What did your user do?").fill(name);
+
+ await expect(page.getByLabel("Description")).toBeVisible();
+ await page.getByLabel("Description").fill(description);
+
+ // User selects the Code tab
+ await page.getByText("Code", { exact: true }).click();
+
+ await expect(page.getByLabel("Key")).toBeVisible();
+ await page.getByLabel("Key").fill(key);
+
+ await page.getByRole("button", { name: "Create action", exact: true }).click();
+
+ const successToast = await page.waitForSelector(".formbricks__toast__success");
+ expect(successToast).toBeTruthy();
+
+ const actionButton = page.getByTitle(name);
+ await expect(actionButton).toBeVisible();
};
const getActionButtonLocator = (page: Page, actionName: string) => {
@@ -134,248 +181,245 @@ const getActionButtonLocator = (page: Page, actionName: string) => {
};
test.describe("Create and Edit No Code Click Action", async () => {
- test.describe.configure({ mode: "serial" });
- const { email, password, name: username } = users.action[0];
+ test.beforeEach(async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
- test("Create No Code Click Action by CSS Selector", async ({ page }) => {
- await createNoCodeClickAction(
- page,
- username,
- email,
- password,
- actions.create.noCode.click.name,
- actions.create.noCode.click.description,
- actions.create.noCode.click.selector
- );
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
});
- test("Edit No Code Click Action", async ({ page }) => {
- await login(page, email, password);
- await page.getByRole("link", { name: "Actions" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/actions/);
+ test("Create and Edit No Code Click Action by CSS Selector", async ({ page }) => {
+ await test.step("Create No Code Click Action", async () => {
+ await createNoCodeClickAction({
+ page,
+ name: actions.create.noCode.click.name,
+ description: actions.create.noCode.click.description,
+ selector: actions.create.noCode.click.selector,
+ });
+ });
- const actionButton = getActionButtonLocator(page, actions.create.noCode.click.name);
- await expect(actionButton).toBeVisible();
- await actionButton.click();
+ await test.step("Edit No Code Click Action", async () => {
+ const actionButton = getActionButtonLocator(page, actions.create.noCode.click.name);
+ await expect(actionButton).toBeVisible();
+ await actionButton.click();
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actions.edit.noCode.click.name);
+ await expect(page.getByLabel("What did your user do?")).toBeVisible();
+ await page.getByLabel("What did your user do?").fill(actions.edit.noCode.click.name);
- await expect(page.getByLabel("Description")).toBeVisible();
- await page.getByLabel("Description").fill(actions.edit.noCode.click.description);
+ await expect(page.getByLabel("Description")).toBeVisible();
+ await page.getByLabel("Description").fill(actions.edit.noCode.click.description);
- await expect(page.locator("[name='noCodeConfig.elementSelector.cssSelector']")).toBeVisible();
- await page
- .locator("[name='noCodeConfig.elementSelector.cssSelector']")
- .fill(actions.edit.noCode.click.selector);
+ await expect(page.locator("[name='noCodeConfig.elementSelector.cssSelector']")).toBeVisible();
+ await page
+ .locator("[name='noCodeConfig.elementSelector.cssSelector']")
+ .fill(actions.edit.noCode.click.selector);
- await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ });
});
});
test.describe("Create and Edit No Code Page view Action", async () => {
- test.describe.configure({ mode: "serial" });
- const { email, password, name: username } = users.action[1];
+ test.beforeEach(async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
- test("Create No Code Page view Action", async ({ page }) => {
- await createNoCodePageViewAction(
- page,
- username,
- email,
- password,
- actions.create.noCode.pageView.name,
- actions.create.noCode.pageView.description,
- actions.create.noCode.pageView.matcher,
- actions.create.noCode.pageView.testURL,
- "Page View"
- );
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
});
- test("Edit No Code Page view Action", async ({ page }) => {
- await login(page, email, password);
+ test("Create and Edit No Code Page view Action", async ({ page }) => {
+ await test.step("Create No Code Page view Action", async () => {
+ await createNoCodePageViewAction({
+ page,
+ name: actions.create.noCode.pageView.name,
+ description: actions.create.noCode.pageView.description,
+ matcher: actions.create.noCode.pageView.matcher,
+ noCodeType: "Page View",
+ });
+ });
- await page.getByRole("link", { name: "Actions" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/actions/);
+ await test.step("Edit No Code Page view Action", async () => {
+ await page.getByRole("link", { name: "Actions" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/actions/);
- const actionButton = getActionButtonLocator(page, actions.create.noCode.pageView.name);
- await expect(actionButton).toBeVisible();
- await actionButton.click();
+ const actionButton = getActionButtonLocator(page, actions.create.noCode.pageView.name);
+ await expect(actionButton).toBeVisible();
+ await actionButton.click();
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actions.edit.noCode.pageView.name);
+ await expect(page.getByLabel("What did your user do?")).toBeVisible();
+ await page.getByLabel("What did your user do?").fill(actions.edit.noCode.pageView.name);
- await expect(page.getByLabel("Description")).toBeVisible();
- await page.getByLabel("Description").fill(actions.edit.noCode.pageView.description);
+ await expect(page.getByLabel("Description")).toBeVisible();
+ await page.getByLabel("Description").fill(actions.edit.noCode.pageView.description);
- await expect(page.locator("[name='noCodeConfig.urlFilters.0.rule']")).toBeVisible();
- await page
- .locator("[name='noCodeConfig.urlFilters.0.rule']")
- .selectOption({ label: actions.edit.noCode.pageView.matcher.label });
+ await expect(page.locator("[name='noCodeConfig.urlFilters.0.rule']")).toBeVisible();
+ await page
+ .locator("[name='noCodeConfig.urlFilters.0.rule']")
+ .selectOption({ label: actions.edit.noCode.pageView.matcher.label });
- await page
- .locator("[name='noCodeConfig.urlFilters.0.value']")
- .fill(actions.edit.noCode.pageView.matcher.value);
+ await page
+ .locator("[name='noCodeConfig.urlFilters.0.value']")
+ .fill(actions.edit.noCode.pageView.matcher.value);
- await page.locator("[name='noCodeConfig.urlFilters.testUrl']").fill(actions.edit.noCode.pageView.testURL);
- await page.getByRole("button", { name: "Test Match", exact: true }).click();
- await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ await page
+ .locator("[name='noCodeConfig.urlFilters.testUrl']")
+ .fill(actions.edit.noCode.pageView.testURL);
+ await page.getByRole("button", { name: "Test Match", exact: true }).click();
+ await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ });
});
});
test.describe("Create and Edit No Code Exit Intent Action", async () => {
- const { email, password, name: username } = users.action[2];
- test("Create No Code Exit Intent Action", async ({ page }) => {
- await createNoCodeAction(
- page,
- username,
- email,
- password,
- actions.create.noCode.exitIntent.name,
- actions.create.noCode.exitIntent.description,
- "Exit Intent"
- );
+ test.beforeEach(async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
+
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
});
- test("Edit No Code Exit Intent Action", async ({ page }) => {
- await login(page, email, password);
- await page.getByRole("link", { name: "Actions" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/actions/);
+ test("Create and Edit No Code Exit Intent Action", async ({ page }) => {
+ await test.step("Create No Code Exit Intent Action", async () => {
+ await createNoCodeAction({
+ page,
+ name: actions.create.noCode.exitIntent.name,
+ description: actions.create.noCode.exitIntent.description,
+ noCodeType: "Exit Intent",
+ });
+ });
- const actionButton = getActionButtonLocator(page, actions.create.noCode.exitIntent.name);
- await expect(actionButton).toBeVisible();
- await actionButton.click();
+ await test.step("Edit No Code Exit Intent Action", async () => {
+ await page.getByRole("link", { name: "Actions" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/actions/);
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ const actionButton = getActionButtonLocator(page, actions.create.noCode.exitIntent.name);
+ await expect(actionButton).toBeVisible();
+ await actionButton.click();
- await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actions.edit.noCode.exitIntent.name);
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await expect(page.getByLabel("Description")).toBeVisible();
- await page.getByLabel("Description").fill(actions.edit.noCode.exitIntent.description);
+ await expect(page.getByLabel("What did your user do?")).toBeVisible();
+ await page.getByLabel("What did your user do?").fill(actions.edit.noCode.exitIntent.name);
- await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ await expect(page.getByLabel("Description")).toBeVisible();
+ await page.getByLabel("Description").fill(actions.edit.noCode.exitIntent.description);
+
+ await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ });
});
});
test.describe("Create and Edit No Code 50% scroll Action", async () => {
- const { email, password, name: username } = users.action[3];
- test("Create No Code 50% scroll Action", async ({ page }) => {
- await createNoCodeAction(
- page,
- username,
- email,
- password,
- actions.create.noCode["fiftyPercentScroll"].name,
- actions.create.noCode["fiftyPercentScroll"].description,
- "50% Scroll"
- );
+ test.beforeEach(async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
+
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
});
- test("Edit No Code 50% scroll Action", async ({ page }) => {
- await login(page, email, password);
- await page.getByRole("link", { name: "Actions" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/actions/);
+ test("Create and Edit No Code 50% scroll Action", async ({ page }) => {
+ await test.step("Create No Code 50% scroll Action", async () => {
+ await createNoCodeAction({
+ page,
+ name: actions.create.noCode["fiftyPercentScroll"].name,
+ description: actions.create.noCode["fiftyPercentScroll"].description,
+ noCodeType: "50% Scroll",
+ });
+ });
- const actionButton = getActionButtonLocator(page, actions.create.noCode["fiftyPercentScroll"].name);
- await expect(actionButton).toBeVisible();
- await actionButton.click();
+ await test.step("Edit No Code 50% scroll Action", async () => {
+ await page.getByRole("link", { name: "Actions" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/actions/);
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ const actionButton = getActionButtonLocator(page, actions.create.noCode["fiftyPercentScroll"].name);
+ await expect(actionButton).toBeVisible();
+ await actionButton.click();
- await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actions.edit.noCode["fiftyPercentScroll"].name);
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await expect(page.getByLabel("Description")).toBeVisible();
- await page.getByLabel("Description").fill(actions.edit.noCode["fiftyPercentScroll"].description);
+ await expect(page.getByLabel("What did your user do?")).toBeVisible();
+ await page.getByLabel("What did your user do?").fill(actions.edit.noCode["fiftyPercentScroll"].name);
- await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ await expect(page.getByLabel("Description")).toBeVisible();
+ await page.getByLabel("Description").fill(actions.edit.noCode["fiftyPercentScroll"].description);
+
+ await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ });
});
});
test.describe("Create and Edit Code Action", async () => {
- test.describe.configure({ mode: "serial" });
- const { email, password, name: username } = users.action[4];
+ test.beforeEach(async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
- test("Create Code Action", async ({ page }) => {
- await signUpAndLogin(page, username, email, password);
- await finishOnboarding(page);
-
- await page.getByRole("link", { name: "Actions" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/actions/);
-
- // Add Action button
- await page.getByRole("button", { name: "Add Action" }).click();
-
- await expect(page.getByLabel("What did your user do?")).toBeVisible();
- await page.getByLabel("What did your user do?").fill(actions.create.code.name);
-
- await expect(page.getByLabel("Description")).toBeVisible();
- await page.getByLabel("Description").fill(actions.create.code.description);
-
- // User selects the Code tab
- await page.getByText("Code", { exact: true }).click();
-
- await expect(page.getByLabel("Key")).toBeVisible();
- await page.getByLabel("Key").fill(actions.create.code.key);
-
- await page.getByRole("button", { name: "Create action", exact: true }).click();
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(500);
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
});
- test("Edit Code Action", async ({ page }) => {
- await login(page, email, password);
- await page.getByRole("link", { name: "Actions" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/actions/);
+ test("Create and Edit Code Action", async ({ page }) => {
+ await test.step("Create Code Action", async () => {
+ await createCodeAction({
+ page,
+ name: actions.create.code.name,
+ description: actions.create.code.description,
+ key: actions.create.code.key,
+ });
+ });
- const actionButton = getActionButtonLocator(page, actions.create.code.name);
- await expect(actionButton).toBeVisible();
- await actionButton.click();
+ await test.step("Edit Code Action", async () => {
+ await page.getByRole("link", { name: "Actions" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/actions/);
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ const actionButton = getActionButtonLocator(page, actions.create.code.name);
+ await expect(actionButton).toBeVisible();
+ await actionButton.click();
- await expect(page.getByLabel("Description")).toBeVisible();
- await page.getByLabel("Description").fill(actions.edit.code.description);
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ await expect(page.getByLabel("Description")).toBeVisible();
+ await page.getByLabel("Description").fill(actions.edit.code.description);
+
+ await page.getByRole("button", { name: "Save changes", exact: true }).click();
+ });
});
});
test.describe("Create and Delete Action", async () => {
- test.describe.configure({ mode: "serial" });
- const { email, password, name: username } = users.action[5];
+ test.beforeEach(async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
- test("Create Action", async ({ page }) => {
- await createNoCodeClickAction(
- page,
- username,
- email,
- password,
- actions.delete.noCode.name,
- actions.delete.noCode.description,
- actions.delete.noCode.selector
- );
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
});
- test("Delete Action", async ({ page }) => {
- await login(page, email, password);
+ test("Create and Delete Action", async ({ page }) => {
+ await test.step("Create Action", async () => {
+ await createNoCodeClickAction({
+ page,
+ name: actions.delete.noCode.name,
+ description: actions.delete.noCode.description,
+ selector: actions.delete.noCode.selector,
+ });
+ });
- await page.getByRole("link", { name: "Actions" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/actions/);
+ await test.step("Delete Action", async () => {
+ await page.getByRole("link", { name: "Actions" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/actions/);
- const actionButton = getActionButtonLocator(page, actions.delete.noCode.name);
- await expect(actionButton).toBeVisible();
- await actionButton.click();
+ const actionButton = getActionButtonLocator(page, actions.delete.noCode.name);
+ await expect(actionButton).toBeVisible();
+ await actionButton.click();
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await expect(page.locator("#deleteActionModalTrigger")).toBeVisible();
- await page.locator("#deleteActionModalTrigger").click();
+ await expect(page.locator("#deleteActionModalTrigger")).toBeVisible();
+ await page.locator("#deleteActionModalTrigger").click();
- await page.getByRole("button", { name: "Delete", exact: true }).click();
+ await page.getByRole("button", { name: "Delete", exact: true }).click();
+ });
});
});
diff --git a/apps/web/playwright/api/management/survey.spec.ts b/apps/web/playwright/api/management/survey.spec.ts
index e6f03a3e44..98361415d2 100644
--- a/apps/web/playwright/api/management/survey.spec.ts
+++ b/apps/web/playwright/api/management/survey.spec.ts
@@ -1,115 +1,116 @@
-import { finishOnboarding, signUpAndLogin } from "@/playwright/utils/helper";
-import { users } from "@/playwright/utils/mock";
-import { expect, test } from "@playwright/test";
-
-const { name, email, password } = users.survey[2];
+import { expect } from "@playwright/test";
+import { test } from "../../lib/fixtures";
test.describe("API Tests", () => {
let surveyId: string;
let environmentId: string;
let apiKey: string;
- test("Copy API Key for API Calls", async ({ page }) => {
- await signUpAndLogin(page, name, email, password);
- await finishOnboarding(page);
+
+ test("API Tests", async ({ page, users, request }) => {
+ const user = await users.create();
+ await user.login();
await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- environmentId =
- /\/environments\/([^/]+)\/surveys/.exec(page.url())?.[1] ??
- (() => {
- throw new Error("Unable to parse environmentId from URL");
- })();
- await page.goto(`/environments/${environmentId}/product/api-keys`);
+ await test.step("Copy API Key", async () => {
+ environmentId =
+ /\/environments\/([^/]+)\/surveys/.exec(page.url())?.[1] ??
+ (() => {
+ throw new Error("Unable to parse environmentId from URL");
+ })();
- await page.getByRole("button", { name: "Add Production API Key" }).isVisible();
- await page.getByRole("button", { name: "Add Production API Key" }).click();
- await page.getByPlaceholder("e.g. GitHub, PostHog, Slack").fill("E2E Test API Key");
- await page.getByRole("button", { name: "Add API Key" }).click();
- await page.locator(".copyApiKeyIcon").click();
+ await page.goto(`/environments/${environmentId}/product/api-keys`);
- apiKey = await page.evaluate("navigator.clipboard.readText()");
- });
+ await page.getByRole("button", { name: "Add Production API Key" }).isVisible();
+ await page.getByRole("button", { name: "Add Production API Key" }).click();
+ await page.getByPlaceholder("e.g. GitHub, PostHog, Slack").fill("E2E Test API Key");
+ await page.getByRole("button", { name: "Add API Key" }).click();
+ await page.locator(".copyApiKeyIcon").click();
- test("Create Survey from API", async ({ request }) => {
- const response = await request.post(`/api/v1/management/surveys`, {
- headers: {
- "Content-Type": "application/json",
- "x-api-key": apiKey,
- },
- data: {
- environmentId: environmentId,
- type: "link",
- name: "My new Survey from API",
- },
+ apiKey = await page.evaluate("navigator.clipboard.readText()");
});
- expect(response.ok()).toBeTruthy();
- const responseBody = await response.json();
- expect(responseBody.data.name).toEqual("My new Survey from API");
- expect(responseBody.data.environmentId).toEqual(environmentId);
- });
+ await test.step("Create Survey from API", async () => {
+ const response = await request.post(`/api/v1/management/surveys`, {
+ headers: {
+ "Content-Type": "application/json",
+ "x-api-key": apiKey,
+ },
+ data: {
+ environmentId: environmentId,
+ type: "link",
+ name: "My new Survey from API",
+ },
+ });
- test("List Surveys from API", async ({ request }) => {
- const response = await request.get(`/api/v1/management/surveys`, {
- headers: {
- "x-api-key": apiKey,
- },
- });
- expect(response.ok()).toBeTruthy();
- const responseBody = await response.json();
-
- const surveyCount = responseBody.data.length;
- expect(surveyCount).toEqual(1);
-
- surveyId = responseBody.data[0].id;
- });
-
- test("Get Survey by ID from API", async ({ request }) => {
- const responseSurvey = await request.get(`/api/v1/management/surveys/${surveyId}`, {
- headers: {
- "content-type": "application/json",
- "x-api-key": apiKey,
- },
- });
- expect(responseSurvey.ok()).toBeTruthy();
- const responseBodySurvey = await responseSurvey.json();
-
- expect(responseBodySurvey.data.id).toEqual(surveyId);
- });
-
- test("Updated Survey by ID from API", async ({ request }) => {
- const response = await request.put(`/api/v1/management/surveys/${surveyId}`, {
- headers: {
- "Content-Type": "application/json",
- "x-api-key": apiKey,
- },
- data: {
- name: "My updated Survey from API",
- },
+ expect(response.ok()).toBeTruthy();
+ const responseBody = await response.json();
+ expect(responseBody.data.name).toEqual("My new Survey from API");
+ expect(responseBody.data.environmentId).toEqual(environmentId);
});
- expect(response.ok()).toBeTruthy();
- const responseBody = await response.json();
- expect(responseBody.data.name).toEqual("My updated Survey from API");
- });
+ await test.step("List Surveys from API", async () => {
+ const response = await request.get(`/api/v1/management/surveys`, {
+ headers: {
+ "x-api-key": apiKey,
+ },
+ });
+ expect(response.ok()).toBeTruthy();
+ const responseBody = await response.json();
- test("Delete Survey by ID from API", async ({ request }) => {
- const response = await request.delete(`/api/v1/management/surveys/${surveyId}`, {
- headers: {
- "Content-Type": "application/json",
- "x-api-key": apiKey,
- },
- });
- expect(response.ok()).toBeTruthy();
- const responseBody = await response.json();
- expect(responseBody.data.name).toEqual("My updated Survey from API");
+ const surveyCount = responseBody.data.length;
+ expect(surveyCount).toEqual(1);
- const responseSurvey = await request.get(`/api/v1/management/surveys/${surveyId}`, {
- headers: {
- "content-type": "application/json",
- "x-api-key": apiKey,
- },
+ surveyId = responseBody.data[0].id;
+ });
+
+ await test.step("Get Survey by ID from API", async () => {
+ const responseSurvey = await request.get(`/api/v1/management/surveys/${surveyId}`, {
+ headers: {
+ "content-type": "application/json",
+ "x-api-key": apiKey,
+ },
+ });
+ expect(responseSurvey.ok()).toBeTruthy();
+ const responseBodySurvey = await responseSurvey.json();
+
+ expect(responseBodySurvey.data.id).toEqual(surveyId);
+ });
+
+ await test.step("Updated Survey by ID from API", async () => {
+ const response = await request.put(`/api/v1/management/surveys/${surveyId}`, {
+ headers: {
+ "Content-Type": "application/json",
+ "x-api-key": apiKey,
+ },
+ data: {
+ name: "My updated Survey from API",
+ },
+ });
+
+ expect(response.ok()).toBeTruthy();
+ const responseBody = await response.json();
+ expect(responseBody.data.name).toEqual("My updated Survey from API");
+ });
+
+ await test.step("Delete Survey by ID from API", async () => {
+ const response = await request.delete(`/api/v1/management/surveys/${surveyId}`, {
+ headers: {
+ "Content-Type": "application/json",
+ "x-api-key": apiKey,
+ },
+ });
+ expect(response.ok()).toBeTruthy();
+ const responseBody = await response.json();
+ expect(responseBody.data.name).toEqual("My updated Survey from API");
+
+ const responseSurvey = await request.get(`/api/v1/management/surveys/${surveyId}`, {
+ headers: {
+ "content-type": "application/json",
+ "x-api-key": apiKey,
+ },
+ });
+ expect(responseSurvey.ok()).toBeFalsy();
});
- expect(responseSurvey.ok()).toBeFalsy();
});
});
diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts
new file mode 100644
index 0000000000..6be7bfcf17
--- /dev/null
+++ b/apps/web/playwright/fixtures/users.ts
@@ -0,0 +1,128 @@
+import { Prisma } from "@prisma/client";
+import bcrypt from "bcryptjs";
+import { Page } from "playwright";
+import { TestInfo } from "playwright/test";
+import { prisma } from "@formbricks/database";
+
+export const login = async (user: Prisma.UserGetPayload<{ include: { memberships: true } }>, page: Page) => {
+ const csrfToken = await page
+ .context()
+ .request.get("/api/auth/csrf")
+ .then((response) => response.json())
+ .then((json) => json.csrfToken);
+ const data = {
+ email: user.email,
+ password: user.name,
+ callbackURL: "/",
+ redirect: "true",
+ json: "true",
+ csrfToken,
+ };
+
+ await page.context().request.post("/api/auth/callback/credentials", {
+ data,
+ });
+
+ await page.goto("/");
+};
+
+export const createUserFixture = (
+ user: Prisma.UserGetPayload<{ include: { memberships: true } }>,
+ page: Page
+) => {
+ return {
+ login: async () => {
+ await login(user, page);
+ },
+ };
+};
+
+export type UserFixture = ReturnType ;
+
+export const createUsersFixture = (page: Page, workerInfo: TestInfo) => {
+ const store: { users: UserFixture[] } = {
+ users: [],
+ };
+
+ return {
+ create: async (params?: {
+ name?: string;
+ email?: string;
+ organizationName?: string;
+ productName?: string;
+ withoutProduct?: boolean;
+ }) => {
+ const uname = params?.name ?? `user-${workerInfo.workerIndex}-${Date.now()}`;
+ const userEmail = params?.email ?? `${uname}@example.com`;
+ const hashedPassword = await bcrypt.hash(uname, 10);
+
+ const user = await prisma.user.create({
+ data: {
+ name: uname,
+ email: userEmail,
+ password: hashedPassword,
+ memberships: {
+ create: {
+ organization: {
+ create: {
+ name: params?.organizationName ?? "My Organization",
+ billing: {
+ plan: "free",
+ limits: { monthly: { responses: 500, miu: 1000 } },
+ stripeCustomerId: null,
+ periodStart: new Date(),
+ period: "monthly",
+ },
+ ...(!params?.withoutProduct && {
+ products: {
+ create: {
+ name: params?.productName ?? "My Product",
+ environments: {
+ create: [
+ {
+ type: "development",
+ actionClasses: {
+ create: [
+ {
+ name: "New Session",
+ description: "Gets fired when a new session is created",
+ type: "automatic",
+ },
+ ],
+ },
+ },
+ {
+ type: "production",
+ actionClasses: {
+ create: [
+ {
+ name: "New Session",
+ description: "Gets fired when a new session is created",
+ type: "automatic",
+ },
+ ],
+ },
+ },
+ ],
+ },
+ },
+ },
+ }),
+ },
+ },
+ role: "owner",
+ },
+ },
+ },
+ include: { memberships: true },
+ });
+
+ const userFixture = createUserFixture(user, page);
+
+ store.users.push(userFixture);
+
+ return userFixture;
+ },
+ get: () => store.users,
+ };
+};
diff --git a/apps/web/playwright/js.spec.ts b/apps/web/playwright/js.spec.ts
index df41dfebd7..0b51ebec03 100644
--- a/apps/web/playwright/js.spec.ts
+++ b/apps/web/playwright/js.spec.ts
@@ -1,144 +1,129 @@
-import { expect, test } from "@playwright/test";
-import { finishOnboarding, login, replaceEnvironmentIdInHtml, signUpAndLogin } from "./utils/helper";
-import { users } from "./utils/mock";
+import { expect } from "@playwright/test";
+import { test } from "./lib/fixtures";
+import { replaceEnvironmentIdInHtml } from "./utils/helper";
test.describe("JS Package Test", async () => {
- const { name, email, password } = users.js[0];
let environmentId: string;
- test.describe.configure({ mode: "serial" });
- test("Admin creates an In-App Survey", async ({ page }) => {
- await signUpAndLogin(page, name, email, password);
- await finishOnboarding(page, "app");
+ test("Tests", async ({ page, users }) => {
+ await test.step("Admin creates an In-App Survey", async () => {
+ const user = await users.create();
+ await user.login();
- await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).isVisible();
- await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- await page.getByRole("button", { name: "Use this template" }).isVisible();
- await page.getByRole("button", { name: "Use this template" }).click();
+ await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).isVisible();
+ await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).click();
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ await page.getByRole("button", { name: "Use this template" }).isVisible();
+ await page.getByRole("button", { name: "Use this template" }).click();
- await expect(page.locator("#howToSendCardTrigger")).toBeVisible();
- await page.locator("#howToSendCardTrigger").click();
+ await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/edit/);
- await expect(page.locator("#howToSendCardOption-app")).toBeVisible();
- await page.locator("#howToSendCardOption-app").click();
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await page.getByRole("button", { name: "Add action" }).click();
- await page.getByText("New SessionGets fired when a").click();
+ await expect(page.locator("#howToSendCardTrigger")).toBeVisible();
+ await page.locator("#howToSendCardTrigger").click();
- await page.locator("#recontactOptionsCardTrigger").click();
+ await expect(page.locator("#howToSendCardOption-app")).toBeVisible();
+ await page.locator("#howToSendCardOption-app").click();
- await page.locator("label").filter({ hasText: "Keep showing while conditions" }).click();
- await page.locator("#recontactDays").check();
+ await page.locator("#whenToSendCardTrigger").click();
- await page.getByRole("button", { name: "Publish" }).click();
+ await page.getByRole("button", { name: "Add action" }).click();
+ await page.getByText("New SessionGets fired when a").click();
- environmentId =
- /\/environments\/([^/]+)\/surveys/.exec(page.url())?.[1] ??
- (() => {
- throw new Error("Unable to parse environmentId from URL");
- })();
+ await page.locator("#recontactOptionsCardTrigger").click();
- await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/summary/);
- await page.waitForTimeout(1000);
+ await page.locator("label").filter({ hasText: "Keep showing while conditions" }).click();
+ await page.locator("#recontactDays").check();
- await expect(page.getByRole("link", { name: "Surveys" })).toBeVisible();
- await page.getByRole("link", { name: "Surveys" }).click();
- await expect(page.getByRole("heading", { name: "Surveys" })).toBeVisible();
- });
+ await page.getByRole("button", { name: "Publish" }).click();
- test("JS Display Survey on Page", async ({ page }) => {
- let currentDir = process.cwd();
- let htmlFilePath = currentDir + "/packages/js/index.html";
+ environmentId =
+ /\/environments\/([^/]+)\/surveys/.exec(page.url())?.[1] ??
+ (() => {
+ throw new Error("Unable to parse environmentId from URL");
+ })();
- let htmlFile = replaceEnvironmentIdInHtml(htmlFilePath, environmentId);
- await page.goto(htmlFile);
+ await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/summary/);
- // Formbricks In App Sync has happened
- const syncApi = await page.waitForResponse((response) => response.url().includes("/app/sync"));
- expect(syncApi.status()).toBe(200);
+ // await expect(page.getByRole("link", { name: "Surveys" })).toBeVisible();
+ // await page.getByRole("link", { name: "Surveys" }).click();
- // Formbricks Modal exists in the DOM
- await expect(page.locator("#formbricks-modal-container")).toHaveCount(1);
+ // await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- // const displayApi = await page.waitForResponse((response) => response.url().includes("/display"));
- // expect(displayApi.status()).toBe(200);
+ // await expect(page.getByRole("heading", { name: "Surveys" })).toBeVisible();
+ });
- // Formbricks Modal is visible
- await expect(
- page.locator("#questionCard-0").getByRole("link", { name: "Powered by Formbricks" })
- ).toBeVisible();
+ await test.step("JS display survey on page and submit response", async () => {
+ let currentDir = process.cwd();
+ let htmlFilePath = currentDir + "/packages/js/index.html";
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(1500);
- });
+ let htmlFile = replaceEnvironmentIdInHtml(htmlFilePath, environmentId);
+ await page.goto(htmlFile);
- test("JS submits Response to Survey", async ({ page }) => {
- let currentDir = process.cwd();
- let htmlFilePath = currentDir + "/packages/js/index.html";
+ // Formbricks In App Sync has happened
+ const syncApi = await page.waitForResponse((response) => response.url().includes("/app/sync"));
+ expect(syncApi.status()).toBe(200);
- let htmlFile = "file:///" + htmlFilePath;
+ // Formbricks Modal exists in the DOM
+ await expect(page.locator("#formbricks-modal-container")).toHaveCount(1);
- await page.goto(htmlFile);
+ // const displayApi = await page.waitForResponse((response) => response.url().includes("/display"));
+ // expect(displayApi.status()).toBe(200);
- // Formbricks In App Sync has happened
- const syncApi = await page.waitForResponse((response) => response.url().includes("/app/sync"));
- expect(syncApi.status()).toBe(200);
+ // Formbricks Modal exists in the DOM
+ // await expect(page.locator("#formbricks-modal-container")).toHaveCount(1);
- // Formbricks Modal exists in the DOM
- await expect(page.locator("#formbricks-modal-container")).toHaveCount(1);
+ // Formbricks Modal is visible
+ await expect(
+ page.locator("#questionCard-0").getByRole("link", { name: "Powered by Formbricks" })
+ ).toBeVisible();
- // Formbricks Modal is visible
- await expect(
- page.locator("#questionCard-0").getByRole("link", { name: "Powered by Formbricks" })
- ).toBeVisible();
+ // Fill the Survey
+ await page.getByRole("button", { name: "Happy to help!" }).click();
+ await page.locator("label").filter({ hasText: "Somewhat disappointed" }).click();
+ await page.locator("#questionCard-1").getByRole("button", { name: "Next" }).click();
+ await page.locator("label").filter({ hasText: "Founder" }).click();
+ await page.locator("#questionCard-2").getByRole("button", { name: "Next" }).click();
+ await page
+ .locator("#questionCard-3")
+ .getByLabel("textarea")
+ .fill("People who believe that PMF is necessary");
+ await page.locator("#questionCard-3").getByRole("button", { name: "Next" }).click();
+ await page.locator("#questionCard-4").getByLabel("textarea").fill("Much higher response rates!");
+ await page.locator("#questionCard-4").getByRole("button", { name: "Next" }).click();
+ await page.locator("#questionCard-5").getByLabel("textarea").fill("Make this end to end test pass!");
+ await page.getByRole("button", { name: "Finish" }).click();
- // Fill the Survey
- await page.getByRole("button", { name: "Happy to help!" }).click();
- await page.locator("label").filter({ hasText: "Somewhat disappointed" }).click();
- await page.locator("#questionCard-1").getByRole("button", { name: "Next" }).click();
- await page.locator("label").filter({ hasText: "Founder" }).click();
- await page.locator("#questionCard-2").getByRole("button", { name: "Next" }).click();
- await page
- .locator("#questionCard-3")
- .getByLabel("textarea")
- .fill("People who believe that PMF is necessary");
- await page.locator("#questionCard-3").getByRole("button", { name: "Next" }).click();
- await page.locator("#questionCard-4").getByLabel("textarea").fill("Much higher response rates!");
- await page.locator("#questionCard-4").getByRole("button", { name: "Next" }).click();
- await page.locator("#questionCard-5").getByLabel("textarea").fill("Make this end to end test pass!");
- await page.getByRole("button", { name: "Finish" }).click();
- await page.getByText("Thank you!").click();
+ // loading spinner -> wait for it to disappear
+ await page.getByTestId("loading-spinner").waitFor({ state: "hidden" });
+ await page.waitForLoadState("networkidle");
+ });
- // Formbricks Modal is not visible
- await expect(
- page.locator("#questionCard-6").getByRole("link", { name: "Powered by Formbricks" })
- ).toBeVisible();
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(5000);
- });
+ await test.step("Admin validates Displays & Response", async () => {
+ await page.goto("/");
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- test("Admin validates Displays & Response", async ({ page }) => {
- await login(page, email, password);
+ await page.getByRole("link", { name: "product Market Fit (Superhuman)" }).click();
+ (await page.waitForSelector("text=Responses")).isVisible();
- await page.getByRole("link", { name: "product Market Fit (Superhuman)" }).click();
- (await page.waitForSelector("text=Responses")).isVisible();
+ await page.waitForLoadState("networkidle");
- // Survey should have 2 Displays
- await page.waitForTimeout(1000);
- await expect(page.getByText("Impressions2")).toBeVisible();
+ const impressionsCount = await page.getByRole("button", { name: "Impressions" }).innerText();
+ expect(impressionsCount).toEqual("Impressions\n\n1");
- // Survey should have 1 Response
- await page.waitForTimeout(1000);
- await expect(page.getByRole("button", { name: "Completed50%" })).toBeVisible();
- await expect(page.getByText("1 Responses", { exact: true }).first()).toBeVisible();
- await expect(page.getByText("CTR50%")).toBeVisible();
- await expect(page.getByText("Somewhat disappointed100%")).toBeVisible();
- await expect(page.getByText("Founder100%")).toBeVisible();
- await expect(page.getByText("People who believe that PMF").first()).toBeVisible();
- await expect(page.getByText("Much higher response rates!").first()).toBeVisible();
- await expect(page.getByText("Make this end to end test").first()).toBeVisible();
+ await expect(page.getByRole("link", { name: "Responses (1)" })).toBeVisible();
+ await expect(page.getByRole("button", { name: "Completed100%" })).toBeVisible();
+
+ await expect(page.getByText("1 Responses", { exact: true }).first()).toBeVisible();
+ await expect(page.getByText("CTR100%")).toBeVisible();
+ await expect(page.getByText("Somewhat disappointed100%")).toBeVisible();
+ await expect(page.getByText("Founder100%")).toBeVisible();
+ await expect(page.getByText("People who believe that PMF").first()).toBeVisible();
+ await expect(page.getByText("Much higher response rates!").first()).toBeVisible();
+ await expect(page.getByText("Make this end to end test").first()).toBeVisible();
+ });
});
});
diff --git a/apps/web/playwright/lib/fixtures.ts b/apps/web/playwright/lib/fixtures.ts
new file mode 100644
index 0000000000..55aa7d7dea
--- /dev/null
+++ b/apps/web/playwright/lib/fixtures.ts
@@ -0,0 +1,13 @@
+import { test as base } from "@playwright/test";
+import { createUsersFixture } from "../fixtures/users";
+
+export interface Fixtures {
+ users: ReturnType;
+}
+
+export const test = base.extend({
+ users: async ({ page }, use, workerInfo) => {
+ const usersFixture = createUsersFixture(page, workerInfo);
+ await use(usersFixture);
+ },
+});
diff --git a/apps/web/playwright/onboarding.spec.ts b/apps/web/playwright/onboarding.spec.ts
index 1789579097..07d5c888e0 100644
--- a/apps/web/playwright/onboarding.spec.ts
+++ b/apps/web/playwright/onboarding.spec.ts
@@ -1,13 +1,14 @@
-import { expect, test } from "@playwright/test";
-import { signUpAndLogin } from "./utils/helper";
-import { organizations, users } from "./utils/mock";
+import { expect } from "@playwright/test";
+import { test } from "./lib/fixtures";
+import { organizations } from "./utils/mock";
const { productName } = organizations.onboarding[0];
test.describe("Onboarding Flow Test", async () => {
- test("link survey", async ({ page }) => {
- const { name, email, password } = users.onboarding[0];
- await signUpAndLogin(page, name, email, password);
+ test("link survey", async ({ page, users }) => {
+ const user = await users.create({ withoutProduct: true });
+ await user.login();
+
await page.waitForURL(/\/organizations\/[^/]+\/products\/new\/channel/);
await page.getByRole("button", { name: "100% custom branding Anywhere" }).click();
@@ -20,9 +21,10 @@ test.describe("Onboarding Flow Test", async () => {
await expect(page.getByText(productName)).toBeVisible();
});
- test("website survey", async ({ page }) => {
- const { name, email, password } = users.onboarding[1];
- await signUpAndLogin(page, name, email, password);
+ test("website survey", async ({ page, users }) => {
+ const user = await users.create({ withoutProduct: true });
+ await user.login();
+
await page.waitForURL(/\/organizations\/[^/]+\/products\/new\/channel/);
await page.getByRole("button", { name: "Enrich user profiles App with" }).click();
diff --git a/apps/web/playwright/organization.spec.ts b/apps/web/playwright/organization.spec.ts
index 2607a1625b..d402190942 100644
--- a/apps/web/playwright/organization.spec.ts
+++ b/apps/web/playwright/organization.spec.ts
@@ -1,118 +1,122 @@
-import { expect, test } from "playwright/test";
-import { finishOnboarding, login, signUpAndLogin, signupUsingInviteToken } from "./utils/helper";
-import { invites, users } from "./utils/mock";
+import { expect } from "playwright/test";
+import { test } from "./lib/fixtures";
+import { finishOnboarding, signUpAndLogin } from "./utils/helper";
+import { invites, mockUsers } from "./utils/mock";
test.describe("Invite, accept and remove organization member", async () => {
test.describe.configure({ mode: "serial" });
- const { email, password, name } = users.organization[0];
- let inviteLink: string;
+
+ const { email, name } = mockUsers.organization[0];
+ // let inviteLink: string;
test("Invite organization member", async ({ page }) => {
- await signUpAndLogin(page, name, email, password);
- await finishOnboarding(page);
+ await signUpAndLogin(page, name, email, name);
+ await finishOnboarding(page, "link");
- const dropdownTrigger = page.locator("#userDropdownTrigger");
- await expect(dropdownTrigger).toBeVisible();
- await dropdownTrigger.click();
-
- const dropdownInnerContentWrapper = page.locator("#userDropdownInnerContentWrapper");
- await expect(dropdownInnerContentWrapper).toBeVisible();
-
- await page.getByRole("link", { name: "Organization" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/settings\/members/);
-
- // Add member button
- await expect(page.getByRole("button", { name: "Add Member" })).toBeVisible();
- await page.getByRole("button", { name: "Add Member" }).click();
-
- // Fill the member name and email form
- await expect(page.getByLabel("Email")).toBeVisible();
- await page.getByLabel("Full Name").fill(invites.addMember.name);
-
- await expect(page.getByLabel("Email Address")).toBeVisible();
- await page.getByLabel("Email Address").fill(invites.addMember.email);
-
- await page.getByRole("button", { name: "Send Invitation", exact: true }).click();
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(500);
- });
-
- test("Copy invite Link", async ({ page }) => {
- await login(page, email, password);
await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- await expect(page.getByText("My Product")).toBeVisible();
- const dropdownTrigger = page.locator("#userDropdownTrigger");
- await expect(dropdownTrigger).toBeVisible();
- await dropdownTrigger.click();
+ await test.step("Invite User", async () => {
+ const dropdownTrigger = page.locator("#userDropdownTrigger");
+ await expect(dropdownTrigger).toBeVisible();
+ await dropdownTrigger.click();
- const dropdownInnerContentWrapper = page.locator("#userDropdownInnerContentWrapper");
- await expect(dropdownInnerContentWrapper).toBeVisible();
+ const dropdownInnerContentWrapper = page.locator("#userDropdownInnerContentWrapper");
+ await expect(dropdownInnerContentWrapper).toBeVisible();
- await page.getByRole("link", { name: "Organization" }).click();
+ await page.getByRole("link", { name: "Organization" }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/settings\/members/);
- await expect(page.locator("#membersInfoWrapper")).toBeVisible();
+ await page.locator('[data-testid="members-loading-card"]:first-child').waitFor({ state: "hidden" });
- const lastMemberInfo = page.locator("#membersInfoWrapper > .singleMemberInfo:last-child");
- await expect(lastMemberInfo).toBeVisible();
+ // Add member button
+ await expect(page.getByRole("button", { name: "Add Member" })).toBeVisible();
+ await page.getByRole("button", { name: "Add Member" }).click();
- const pendingSpan = lastMemberInfo.locator("span").filter({ hasText: "Pending" });
- await expect(pendingSpan).toBeVisible();
+ // Fill the member name and email form
+ await expect(page.getByLabel("Email")).toBeVisible();
+ await page.getByLabel("Full Name").fill(invites.addMember.name);
- const shareInviteButton = page.locator(".shareInviteButton").last();
- await expect(shareInviteButton).toBeVisible();
+ await expect(page.getByLabel("Email Address")).toBeVisible();
+ await page.getByLabel("Email Address").fill(invites.addMember.email);
- await shareInviteButton.click();
+ await page.getByRole("button", { name: "Send Invitation", exact: true }).click();
- const inviteLinkText = page.locator("#inviteLinkText");
- await expect(inviteLinkText).toBeVisible();
+ await page.waitForLoadState("networkidle");
- // invite link text is a paragraph, and we need the text inside it
- const inviteLinkTextContent = await inviteLinkText.textContent();
- if (inviteLinkTextContent) {
- inviteLink = inviteLinkTextContent;
- }
+ // const successToast = await page.waitForSelector(".formbricks__toast__success");
+ // expect(successToast).toBeTruthy();
+ });
+
+ await test.step("Copy invite Link", async () => {
+ await expect(page.locator("#membersInfoWrapper")).toBeVisible();
+
+ const lastMemberInfo = page.locator("#membersInfoWrapper > .singleMemberInfo:last-child");
+ await expect(lastMemberInfo).toBeVisible();
+
+ const pendingSpan = lastMemberInfo.locator("span").filter({ hasText: "Pending" });
+ await expect(pendingSpan).toBeVisible();
+
+ const shareInviteButton = page.locator(".shareInviteButton").last();
+ await expect(shareInviteButton).toBeVisible();
+
+ await shareInviteButton.click();
+
+ const inviteLinkText = await page.waitForSelector("#inviteLinkText");
+ expect(inviteLinkText).toBeTruthy();
+
+ // invite link text is a paragraph, and we need the text inside it
+ const inviteLinkTextContent = await inviteLinkText.textContent();
+ expect(inviteLinkTextContent).toBeTruthy();
+ // if (inviteLinkTextContent) {
+ // inviteLink = inviteLinkTextContent;
+ // }
+ });
});
- test("Accept invite", async ({ page }) => {
- const { email, name, password } = users.organization[1];
- page.goto(inviteLink);
+ // test("Accept invite", async ({ page }) => {
+ // const { email, name } = mockUsers.organization[1];
+ // page.goto(inviteLink);
- await page.waitForURL(/\/invite\?token=[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+/);
+ // await page.waitForURL(/\/invite\?token=[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+/);
- // Create account button
- await expect(page.getByRole("link", { name: "Create account" })).toBeVisible();
- await page.getByRole("link", { name: "Create account" }).click();
+ // // Create account button
+ // await expect(page.getByRole("link", { name: "Create account" })).toBeVisible();
+ // await page.getByRole("link", { name: "Create account" }).click();
- await signupUsingInviteToken(page, name, email, password);
- await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- });
+ // await signupUsingInviteToken(page, name, email, name);
+ // await page.waitForURL(/\/environments\/[^/]+\/surveys/);
+ // });
- test("Remove member", async ({ page }) => {
- await login(page, email, password);
+ // test("Remove member", async ({ page }) => {
+ // await apiLogin(page, email, name);
- const dropdownTrigger = page.locator("#userDropdownTrigger");
- await expect(dropdownTrigger).toBeVisible();
- await dropdownTrigger.click();
+ // await page.goto("/");
+ // await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- const dropdownInnerContentWrapper = page.locator("#userDropdownInnerContentWrapper");
- await expect(dropdownInnerContentWrapper).toBeVisible();
+ // const dropdownTrigger = page.locator("#userDropdownTrigger");
+ // await expect(dropdownTrigger).toBeVisible();
+ // await dropdownTrigger.click();
- await page.getByRole("link", { name: "Organization" }).click();
+ // const dropdownInnerContentWrapper = page.locator("#userDropdownInnerContentWrapper");
+ // await expect(dropdownInnerContentWrapper).toBeVisible();
- await expect(page.locator("#membersInfoWrapper")).toBeVisible();
+ // await page.getByRole("link", { name: "Organization" }).click();
- const lastMemberInfo = page.locator("#membersInfoWrapper > .singleMemberInfo:last-child");
- await expect(lastMemberInfo).toBeVisible();
+ // await page.waitForURL(/\/environments\/[^/]+\/settings\/members/);
- const deleteMemberButton = lastMemberInfo.locator("#deleteMemberButton");
- await expect(deleteMemberButton).toBeVisible();
+ // await page.locator('[data-testid="members-loading-card"]:first-child').waitFor({ state: "hidden" });
- await deleteMemberButton.click();
+ // await expect(page.locator("#membersInfoWrapper")).toBeVisible();
- await expect(page.getByRole("button", { name: "Delete", exact: true })).toBeVisible();
- await page.getByRole("button", { name: "Delete", exact: true }).click();
+ // const lastMemberInfo = page.locator("#membersInfoWrapper > .singleMemberInfo:last-child");
+ // await expect(lastMemberInfo).toBeVisible();
- await expect(page.getByText("organization2@formbricks.com")).not.toBeVisible();
- });
+ // const deleteMemberButton = lastMemberInfo.locator("#deleteMemberButton");
+ // await expect(deleteMemberButton).toBeVisible();
+
+ // await deleteMemberButton.click();
+
+ // await expect(page.getByRole("button", { name: "Delete", exact: true })).toBeVisible();
+ // await page.getByRole("button", { name: "Delete", exact: true }).click();
+ // });
});
diff --git a/apps/web/playwright/signup.spec.ts b/apps/web/playwright/signup.spec.ts
index cc672da639..9eccf5e109 100644
--- a/apps/web/playwright/signup.spec.ts
+++ b/apps/web/playwright/signup.spec.ts
@@ -1,7 +1,8 @@
-import { expect, test } from "@playwright/test";
-import { users } from "./utils/mock";
+import { expect } from "@playwright/test";
+import { test } from "./lib/fixtures";
+import { mockUsers } from "./utils/mock";
-const { name, email, password } = users.signup[0];
+const { name, email, password } = mockUsers.signup[0];
test.describe("Email Signup Flow Test", async () => {
test.describe.configure({ mode: "serial" });
diff --git a/apps/web/playwright/survey.spec.ts b/apps/web/playwright/survey.spec.ts
index ab905a2cb3..349c4b87f3 100644
--- a/apps/web/playwright/survey.spec.ts
+++ b/apps/web/playwright/survey.spec.ts
@@ -1,192 +1,199 @@
-import { surveys, users } from "@/playwright/utils/mock";
-import { expect, test } from "@playwright/test";
-import { createSurvey, finishOnboarding, signUpAndLogin } from "./utils/helper";
+import { surveys } from "@/playwright/utils/mock";
+import { expect } from "@playwright/test";
+import { test } from "./lib/fixtures";
+import { createSurvey } from "./utils/helper";
test.describe("Survey Create & Submit Response", async () => {
- test.describe.configure({ mode: "serial" });
let url: string | null;
- const { name, email, password } = users.survey[0];
- test("Create Survey", async ({ page }) => {
- await createSurvey(page, name, email, password, surveys.createAndSubmit);
+ test("Create survey and submit response", async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
- // Save & Publish Survey
- await page.getByRole("button", { name: "Settings", exact: true }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- await page.locator("#howToSendCardTrigger").click();
- await expect(page.locator("#howToSendCardOption-link")).toBeVisible();
- await page.locator("#howToSendCardOption-link").click();
+ await test.step("Create Survey", async () => {
+ await createSurvey(page, surveys.createAndSubmit);
- await page.getByRole("button", { name: "Publish" }).click();
+ // Save & Publish Survey
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- // Get URL
- await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/summary$/);
- await page.getByLabel("Copy survey link to clipboard").click();
- url = await page.evaluate("navigator.clipboard.readText()");
- });
+ await page.locator("#howToSendCardTrigger").click();
+ await expect(page.locator("#howToSendCardOption-link")).toBeVisible();
+ await page.locator("#howToSendCardOption-link").click();
- test("Submit Survey Response", async ({ page }) => {
- await page.goto(url!);
- await page.waitForURL(/\/s\/[A-Za-z0-9]+$/);
+ await page.getByRole("button", { name: "Publish" }).click();
- // Welcome Card
- await expect(page.getByText(surveys.createAndSubmit.welcomeCard.headline)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.welcomeCard.description)).toBeVisible();
- await page.locator("#questionCard--1").getByRole("button", { name: "Next" }).click();
+ // Get URL
+ await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/summary$/);
+ await page.getByLabel("Copy survey link to clipboard").click();
+ url = await page.evaluate("navigator.clipboard.readText()");
+ });
- // Open Text Question
- await expect(page.getByText(surveys.createAndSubmit.openTextQuestion.question)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.openTextQuestion.description)).toBeVisible();
- await expect(page.getByPlaceholder(surveys.createAndSubmit.openTextQuestion.placeholder)).toBeVisible();
- await page
- .getByPlaceholder(surveys.createAndSubmit.openTextQuestion.placeholder)
- .fill("This is my Open Text answer");
- await page.locator("#questionCard-0").getByRole("button", { name: "Next" }).click();
+ await test.step("Submit Survey Response", async () => {
+ await page.goto(url!);
+ await page.waitForURL(/\/s\/[A-Za-z0-9]+$/);
- // Single Select Question
- await expect(page.getByText(surveys.createAndSubmit.singleSelectQuestion.question)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.singleSelectQuestion.description)).toBeVisible();
- for (let i = 0; i < surveys.createAndSubmit.singleSelectQuestion.options.length; i++) {
- await expect(
- page
- .locator("#questionCard-1 label")
- .filter({ hasText: surveys.createAndSubmit.singleSelectQuestion.options[i] })
- ).toBeVisible();
- }
- await expect(page.getByText("Other")).toBeVisible();
- await expect(page.locator("#questionCard-1").getByRole("button", { name: "Next" })).toBeVisible();
- await expect(page.locator("#questionCard-1").getByRole("button", { name: "Back" })).toBeVisible();
- await page
- .locator("#questionCard-1 label")
- .filter({ hasText: surveys.createAndSubmit.singleSelectQuestion.options[0] })
- .click();
- await page.locator("#questionCard-1").getByRole("button", { name: "Next" }).click();
+ // Welcome Card
+ await expect(page.getByText(surveys.createAndSubmit.welcomeCard.headline)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.welcomeCard.description)).toBeVisible();
+ await page.locator("#questionCard--1").getByRole("button", { name: "Next" }).click();
- // Multi Select Question
- await expect(page.getByText(surveys.createAndSubmit.multiSelectQuestion.question)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.multiSelectQuestion.description)).toBeVisible();
- for (let i = 0; i < surveys.createAndSubmit.singleSelectQuestion.options.length; i++) {
- await expect(
- page
+ // Open Text Question
+ await expect(page.getByText(surveys.createAndSubmit.openTextQuestion.question)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.openTextQuestion.description)).toBeVisible();
+ await expect(page.getByPlaceholder(surveys.createAndSubmit.openTextQuestion.placeholder)).toBeVisible();
+ await page
+ .getByPlaceholder(surveys.createAndSubmit.openTextQuestion.placeholder)
+ .fill("This is my Open Text answer");
+ await page.locator("#questionCard-0").getByRole("button", { name: "Next" }).click();
+
+ // Single Select Question
+ await expect(page.getByText(surveys.createAndSubmit.singleSelectQuestion.question)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.singleSelectQuestion.description)).toBeVisible();
+ for (let i = 0; i < surveys.createAndSubmit.singleSelectQuestion.options.length; i++) {
+ await expect(
+ page
+ .locator("#questionCard-1 label")
+ .filter({ hasText: surveys.createAndSubmit.singleSelectQuestion.options[i] })
+ ).toBeVisible();
+ }
+ await expect(page.getByText("Other")).toBeVisible();
+ await expect(page.locator("#questionCard-1").getByRole("button", { name: "Next" })).toBeVisible();
+ await expect(page.locator("#questionCard-1").getByRole("button", { name: "Back" })).toBeVisible();
+ await page
+ .locator("#questionCard-1 label")
+ .filter({ hasText: surveys.createAndSubmit.singleSelectQuestion.options[0] })
+ .click();
+ await page.locator("#questionCard-1").getByRole("button", { name: "Next" }).click();
+
+ // Multi Select Question
+ await expect(page.getByText(surveys.createAndSubmit.multiSelectQuestion.question)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.multiSelectQuestion.description)).toBeVisible();
+ for (let i = 0; i < surveys.createAndSubmit.singleSelectQuestion.options.length; i++) {
+ await expect(
+ page
+ .locator("#questionCard-2 label")
+ .filter({ hasText: surveys.createAndSubmit.multiSelectQuestion.options[i] })
+ ).toBeVisible();
+ }
+ await expect(page.locator("#questionCard-2").getByRole("button", { name: "Next" })).toBeVisible();
+ await expect(page.locator("#questionCard-2").getByRole("button", { name: "Back" })).toBeVisible();
+ for (let i = 0; i < surveys.createAndSubmit.multiSelectQuestion.options.length; i++) {
+ await page
.locator("#questionCard-2 label")
.filter({ hasText: surveys.createAndSubmit.multiSelectQuestion.options[i] })
+ .click();
+ }
+ await page.locator("#questionCard-2").getByRole("button", { name: "Next" }).click();
+
+ // Rating Question
+ await expect(page.getByText(surveys.createAndSubmit.ratingQuestion.question)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.ratingQuestion.description)).toBeVisible();
+ await expect(
+ page.locator("#questionCard-3").getByText(surveys.createAndSubmit.ratingQuestion.lowLabel)
).toBeVisible();
- }
- await expect(page.locator("#questionCard-2").getByRole("button", { name: "Next" })).toBeVisible();
- await expect(page.locator("#questionCard-2").getByRole("button", { name: "Back" })).toBeVisible();
- for (let i = 0; i < surveys.createAndSubmit.multiSelectQuestion.options.length; i++) {
- await page
- .locator("#questionCard-2 label")
- .filter({ hasText: surveys.createAndSubmit.multiSelectQuestion.options[i] })
- .click();
- }
- await page.locator("#questionCard-2").getByRole("button", { name: "Next" }).click();
+ await expect(
+ page.locator("#questionCard-3").getByText(surveys.createAndSubmit.ratingQuestion.highLabel)
+ ).toBeVisible();
+ expect(await page.getByRole("group", { name: "Choices" }).locator("label").count()).toBe(5);
+ await expect(page.locator("#questionCard-3").getByRole("button", { name: "Next" })).not.toBeVisible();
+ await expect(page.locator("#questionCard-3").getByRole("button", { name: "Back" })).toBeVisible();
+ await page.locator("path").nth(3).click();
- // Rating Question
- await expect(page.getByText(surveys.createAndSubmit.ratingQuestion.question)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.ratingQuestion.description)).toBeVisible();
- await expect(
- page.locator("#questionCard-3").getByText(surveys.createAndSubmit.ratingQuestion.lowLabel)
- ).toBeVisible();
- await expect(
- page.locator("#questionCard-3").getByText(surveys.createAndSubmit.ratingQuestion.highLabel)
- ).toBeVisible();
- expect(await page.getByRole("group", { name: "Choices" }).locator("label").count()).toBe(5);
- await expect(page.locator("#questionCard-3").getByRole("button", { name: "Next" })).not.toBeVisible();
- await expect(page.locator("#questionCard-3").getByRole("button", { name: "Back" })).toBeVisible();
- await page.locator("path").nth(3).click();
+ // NPS Question
+ await expect(page.getByText(surveys.createAndSubmit.npsQuestion.question)).toBeVisible();
+ await expect(
+ page.locator("#questionCard-4").getByText(surveys.createAndSubmit.npsQuestion.lowLabel)
+ ).toBeVisible();
+ await expect(
+ page.locator("#questionCard-4").getByText(surveys.createAndSubmit.npsQuestion.highLabel)
+ ).toBeVisible();
+ await expect(page.locator("#questionCard-4").getByRole("button", { name: "Next" })).not.toBeVisible();
+ await expect(page.locator("#questionCard-4").getByRole("button", { name: "Back" })).toBeVisible();
- // NPS Question
- await expect(page.getByText(surveys.createAndSubmit.npsQuestion.question)).toBeVisible();
- await expect(
- page.locator("#questionCard-4").getByText(surveys.createAndSubmit.npsQuestion.lowLabel)
- ).toBeVisible();
- await expect(
- page.locator("#questionCard-4").getByText(surveys.createAndSubmit.npsQuestion.highLabel)
- ).toBeVisible();
- await expect(page.locator("#questionCard-4").getByRole("button", { name: "Next" })).not.toBeVisible();
- await expect(page.locator("#questionCard-4").getByRole("button", { name: "Back" })).toBeVisible();
+ for (let i = 0; i < 11; i++) {
+ await expect(page.locator("#questionCard-4").getByText(`${i}`, { exact: true })).toBeVisible();
+ }
+ await page.locator("#questionCard-4").getByText("8", { exact: true }).click();
- for (let i = 0; i < 11; i++) {
- await expect(page.locator("#questionCard-4").getByText(`${i}`, { exact: true })).toBeVisible();
- }
- await page.locator("#questionCard-4").getByText("8", { exact: true }).click();
+ // CTA Question
+ await expect(page.getByText(surveys.createAndSubmit.ctaQuestion.question)).toBeVisible();
+ await expect(
+ page.getByRole("button", { name: surveys.createAndSubmit.ctaQuestion.buttonLabel })
+ ).toBeVisible();
+ await page.getByRole("button", { name: surveys.createAndSubmit.ctaQuestion.buttonLabel }).click();
- // CTA Question
- await expect(page.getByText(surveys.createAndSubmit.ctaQuestion.question)).toBeVisible();
- await expect(
- page.getByRole("button", { name: surveys.createAndSubmit.ctaQuestion.buttonLabel })
- ).toBeVisible();
- await page.getByRole("button", { name: surveys.createAndSubmit.ctaQuestion.buttonLabel }).click();
+ // Consent Question
+ await expect(page.getByText(surveys.createAndSubmit.consentQuestion.question)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.consentQuestion.checkboxLabel)).toBeVisible();
+ await expect(page.locator("#questionCard-6").getByRole("button", { name: "Next" })).toBeVisible();
+ await expect(page.locator("#questionCard-6").getByRole("button", { name: "Back" })).toBeVisible();
+ await page.getByText(surveys.createAndSubmit.consentQuestion.checkboxLabel).check();
+ await page.locator("#questionCard-6").getByRole("button", { name: "Next" }).click();
- // Consent Question
- await expect(page.getByText(surveys.createAndSubmit.consentQuestion.question)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.consentQuestion.checkboxLabel)).toBeVisible();
- await expect(page.locator("#questionCard-6").getByRole("button", { name: "Next" })).toBeVisible();
- await expect(page.locator("#questionCard-6").getByRole("button", { name: "Back" })).toBeVisible();
- await page.getByText(surveys.createAndSubmit.consentQuestion.checkboxLabel).check();
- await page.locator("#questionCard-6").getByRole("button", { name: "Next" }).click();
+ // Picture Select Question
+ await expect(page.getByText(surveys.createAndSubmit.pictureSelectQuestion.question)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.pictureSelectQuestion.description)).toBeVisible();
+ await expect(page.locator("#questionCard-7").getByRole("button", { name: "Next" })).toBeVisible();
+ await expect(page.locator("#questionCard-7").getByRole("button", { name: "Back" })).toBeVisible();
+ await expect(page.getByRole("img", { name: "puppy-1-small.jpg" })).toBeVisible();
+ await expect(page.getByRole("img", { name: "puppy-2-small.jpg" })).toBeVisible();
+ await page.getByRole("img", { name: "puppy-1-small.jpg" }).click();
+ await page.locator("#questionCard-7").getByRole("button", { name: "Next" }).click();
- // Picture Select Question
- await expect(page.getByText(surveys.createAndSubmit.pictureSelectQuestion.question)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.pictureSelectQuestion.description)).toBeVisible();
- await expect(page.locator("#questionCard-7").getByRole("button", { name: "Next" })).toBeVisible();
- await expect(page.locator("#questionCard-7").getByRole("button", { name: "Back" })).toBeVisible();
- await expect(page.getByRole("img", { name: "puppy-1-small.jpg" })).toBeVisible();
- await expect(page.getByRole("img", { name: "puppy-2-small.jpg" })).toBeVisible();
- await page.getByRole("img", { name: "puppy-1-small.jpg" }).click();
- await page.locator("#questionCard-7").getByRole("button", { name: "Next" }).click();
+ // File Upload Question
+ await expect(page.getByText(surveys.createAndSubmit.fileUploadQuestion.question)).toBeVisible();
+ await expect(page.locator("#questionCard-8").getByRole("button", { name: "Next" })).toBeVisible();
+ await expect(page.locator("#questionCard-8").getByRole("button", { name: "Back" })).toBeVisible();
+ await expect(
+ page.locator("label").filter({ hasText: "Click or drag to upload files." }).locator("div").nth(0)
+ ).toBeVisible();
+ await page.locator("input[type=file]").setInputFiles({
+ name: "file.txt",
+ mimeType: "text/plain",
+ buffer: Buffer.from("this is test"),
+ });
+ await page.getByText("Uploading...").waitFor({ state: "hidden" });
+ await page.locator("#questionCard-8").getByRole("button", { name: "Next" }).click();
- // File Upload Question
- await expect(page.getByText(surveys.createAndSubmit.fileUploadQuestion.question)).toBeVisible();
- await expect(page.locator("#questionCard-8").getByRole("button", { name: "Next" })).toBeVisible();
- await expect(page.locator("#questionCard-8").getByRole("button", { name: "Back" })).toBeVisible();
- await expect(
- page.locator("label").filter({ hasText: "Click or drag to upload files." }).locator("div").nth(0)
- ).toBeVisible();
- await page.locator("input[type=file]").setInputFiles({
- name: "file.txt",
- mimeType: "text/plain",
- buffer: Buffer.from("this is test"),
+ // Matrix Question
+ await expect(page.getByText(surveys.createAndSubmit.matrix.question)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.matrix.description)).toBeVisible();
+ await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.rows[0] })).toBeVisible();
+ await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.rows[1] })).toBeVisible();
+ await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.rows[2] })).toBeVisible();
+ await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[0] })).toBeVisible();
+ await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[1] })).toBeVisible();
+ await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[2] })).toBeVisible();
+ await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[3] })).toBeVisible();
+ await expect(page.locator("#questionCard-9").getByRole("button", { name: "Next" })).not.toBeVisible();
+ await expect(page.locator("#questionCard-9").getByRole("button", { name: "Back" })).toBeVisible();
+ await page.getByRole("row", { name: "Rose 🌹" }).getByRole("cell").nth(1).click();
+ await page.locator("#questionCard-9").getByRole("button", { name: "Next" }).click();
+
+ // Address Question
+ await expect(page.getByText(surveys.createAndSubmit.address.question)).toBeVisible();
+ await expect(page.getByPlaceholder(surveys.createAndSubmit.address.placeholder)).toBeVisible();
+ await page.getByPlaceholder(surveys.createAndSubmit.address.placeholder).fill("This is my Address");
+ await page.getByRole("button", { name: "Finish" }).click();
+
+ // loading spinner -> wait for it to disappear
+ await page.getByTestId("loading-spinner").waitFor({ state: "hidden" });
+
+ // Thank You Card
+ await expect(page.getByText(surveys.createAndSubmit.thankYouCard.headline)).toBeVisible();
+ await expect(page.getByText(surveys.createAndSubmit.thankYouCard.description)).toBeVisible();
});
- await page.getByText("Uploading...").waitFor({ state: "hidden" });
- await page.locator("#questionCard-8").getByRole("button", { name: "Next" }).click();
-
- // Matrix Question
- await expect(page.getByText(surveys.createAndSubmit.matrix.question)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.matrix.description)).toBeVisible();
- await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.rows[0] })).toBeVisible();
- await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.rows[1] })).toBeVisible();
- await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.rows[2] })).toBeVisible();
- await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[0] })).toBeVisible();
- await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[1] })).toBeVisible();
- await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[2] })).toBeVisible();
- await expect(page.getByRole("cell", { name: surveys.createAndSubmit.matrix.columns[3] })).toBeVisible();
- await expect(page.locator("#questionCard-9").getByRole("button", { name: "Next" })).not.toBeVisible();
- await expect(page.locator("#questionCard-9").getByRole("button", { name: "Back" })).toBeVisible();
- await page.getByRole("row", { name: "Rose 🌹" }).getByRole("cell").nth(1).click();
- await page.locator("#questionCard-9").getByRole("button", { name: "Next" }).click();
-
- // Address Question
- await expect(page.getByText(surveys.createAndSubmit.address.question)).toBeVisible();
- await expect(page.getByPlaceholder(surveys.createAndSubmit.address.placeholder)).toBeVisible();
- await page.getByPlaceholder(surveys.createAndSubmit.address.placeholder).fill("This is my Address");
- await page.getByRole("button", { name: "Finish" }).click();
-
- await page.waitForTimeout(500);
-
- // Thank You Card
- await expect(page.getByText(surveys.createAndSubmit.thankYouCard.headline)).toBeVisible();
- await expect(page.getByText(surveys.createAndSubmit.thankYouCard.description)).toBeVisible();
});
});
test.describe("Multi Language Survey Create", async () => {
- test.describe.configure({ mode: "serial" });
- const { name, email, password } = users.survey[3];
- test("Create Survey", async ({ page }) => {
- await signUpAndLogin(page, name, email, password);
- await finishOnboarding(page, "app");
+ test("Create Survey", async ({ page, users }) => {
+ const user = await users.create();
+ await user.login();
+
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
//add a new language
await page.getByRole("link", { name: "Configuration" }).click();
diff --git a/apps/web/playwright/utils/helper.ts b/apps/web/playwright/utils/helper.ts
index eb366d376f..890aac278d 100644
--- a/apps/web/playwright/utils/helper.ts
+++ b/apps/web/playwright/utils/helper.ts
@@ -55,6 +55,26 @@ export const login = async (page: Page, email: string, password: string): Promis
await page.getByRole("button", { name: "Login with Email" }).click();
};
+export const apiLogin = async (page: Page, email: string, password: string) => {
+ const csrfToken = await page
+ .context()
+ .request.get("/api/auth/csrf")
+ .then((response) => response.json())
+ .then((json) => json.csrfToken);
+ const data = {
+ email,
+ password,
+ callbackURL: "/",
+ redirect: "true",
+ json: "true",
+ csrfToken,
+ };
+
+ return page.context().request.post("/api/auth/callback/credentials", {
+ data,
+ });
+};
+
export const finishOnboarding = async (
page: Page,
ProductChannel: TProductConfigChannel = "website"
@@ -113,21 +133,14 @@ export const signupUsingInviteToken = async (page: Page, name: string, email: st
await page.getByRole("button", { name: "Login with Email" }).click();
};
-export const createSurvey = async (
- page: Page,
- name: string,
- email: string,
- password: string,
- params: CreateSurveyParams
-) => {
+export const createSurvey = async (page: Page, params: CreateSurveyParams) => {
const addQuestion = "Add QuestionAdd a new question to your survey";
- await signUpAndLogin(page, name, email, password);
- await finishOnboarding(page);
-
await page.getByRole("button", { name: "Start from scratch Create a" }).click();
await page.getByRole("button", { name: "Create survey", exact: true }).click();
+ await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/edit$/);
+
// Welcome Card
await expect(page.locator("#welcome-toggle")).toBeVisible();
await page.getByText("Welcome Card").click();
diff --git a/apps/web/playwright/utils/mock.ts b/apps/web/playwright/utils/mock.ts
index 8897de6857..901216d3ec 100644
--- a/apps/web/playwright/utils/mock.ts
+++ b/apps/web/playwright/utils/mock.ts
@@ -1,4 +1,4 @@
-export const users = {
+export const mockUsers = {
signup: [
{
name: "SignUp Flow User 1",
@@ -56,44 +56,36 @@ export const users = {
{
name: "Action User 1",
email: "action1@formbricks.com",
- password: "XpP%X9UU3efj8vJa",
},
{
name: "Action User 2",
email: "action2@formbricks.com",
- password: "XpP%X9UU3efj8vJa",
},
{
name: "Action User 3",
email: "action3@formbricks.com",
- password: "XpP%X9UU3efj8vJa",
},
{
name: "Action User 4",
email: "action4@formbricks.com",
- password: "XpP%X9UU3efj8vJa",
},
{
name: "Action User 5",
email: "action5@formbricks.com",
- password: "XpP%X9UU3efj8vJa",
},
{
name: "Action User 6",
email: "action6@formbricks.com",
- password: "XpP%X9UU3efj8vJa",
},
],
organization: [
{
name: "Organization User 1",
email: "organization1@formbricks.com",
- password: "Test#1234",
},
{
name: "Organization User 2",
email: "organization2@formbricks.com",
- password: "Test#1234",
},
],
};
@@ -256,7 +248,6 @@ export const actions = {
label: "Contains",
value: "custom-url",
},
- testURL: "http://localhost:3000/custom-url",
},
exitIntent: {
name: "Create Exit Intent Action",
diff --git a/packages/ee/multi-language/components/default-language-select.tsx b/packages/ee/multi-language/components/default-language-select.tsx
index 39e2a35b22..8c68d2f09e 100644
--- a/packages/ee/multi-language/components/default-language-select.tsx
+++ b/packages/ee/multi-language/components/default-language-select.tsx
@@ -21,7 +21,7 @@ export function DefaultLanguageSelect({
1. Choose the default language for this survey:
-
+
|