diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ce5b0a14d7..caa270d414 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -20,6 +20,9 @@ jobs: - name: Install dependencies run: npm install -g pnpm && pnpm install + - name: Build Formricks JS package + run: pnpm build --filter=js + - name: Build Formbricks Image & Run run: docker-compose up -d diff --git a/apps/web/playwright/js.spec.ts b/apps/web/playwright/js.spec.ts new file mode 100644 index 0000000000..0e1b25f010 --- /dev/null +++ b/apps/web/playwright/js.spec.ts @@ -0,0 +1,134 @@ +import { expect, test } from "@playwright/test"; + +import { login, replaceEnvironmentIdInHtml, signUpAndLogin, skipOnboarding } from "./utils/helper"; +import { users } from "./utils/mock"; + +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 skipOnboarding(page); + + await page.waitForURL(/\/environments\/[^/]+\/surveys/); + + await page + .getByText("Product ExperienceProduct Market Fit (Superhuman)Measure PMF by assessing how") + .isVisible(); + + await page + .getByText("Product ExperienceProduct Market Fit (Superhuman)Measure PMF by assessing how") + .click(); + await page.getByRole("button", { name: "Settings", exact: true }).click(); + await page.locator("label").filter({ hasText: "In-App SurveyEmbed a survey" }).click(); + await page + .locator("div") + .filter({ hasText: /^Survey TriggerChoose the actions which trigger the survey\.$/ }) + .nth(1) + .click(); + await page.getByRole("combobox").click(); + await page.getByLabel("New Session").click(); + await page.getByRole("button", { name: "Publish" }).click(); + + environmentId = + /\/environments\/([^/]+)\/surveys/.exec(page.url())?.[1] ?? + (() => { + throw new Error("Unable to parse environmentId from URL"); + })(); + + await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/summary/); + }); + + test("JS Display Survey on Page", async ({ page }) => { + let currentDir = process.cwd(); + let htmlFilePath = currentDir + "/packages/js/index.html"; + + let htmlFile = replaceEnvironmentIdInHtml(htmlFilePath, environmentId); + await page.goto(htmlFile); + + // Formbricks In App Sync has happened + const syncApi = await page.waitForResponse((response) => response.url().includes("/in-app/sync")); + expect(syncApi.status()).toBe(200); + + // Formbricks Modal exists in the DOM + await expect(page.locator("#formbricks-modal-container")).toHaveCount(1); + + const displayApi = await page.waitForResponse((response) => response.url().includes("/display")); + expect(displayApi.status()).toBe(200); + + // Formbricks Modal is visible + await expect(page.getByRole("link", { name: "Powered by Formbricks" })).toBeVisible(); + }); + + test("Admin checks Display", async ({ page }) => { + await login(page, email, password); + + await page.locator("li").filter({ hasText: "In-Product SurveyProduct" }).getByRole("link").click(); + + (await page.waitForSelector("text=Responses")).isVisible(); + + // Survey should have 1 Display + await expect(page.getByText("Displays1")).toBeVisible(); + + // Survey should have 0 Responses + await expect(page.getByRole("button", { name: "Responses0% -" })).toBeVisible(); + }); + + test("JS submits Response to Survey", async ({ page }) => { + let currentDir = process.cwd(); + let htmlFilePath = currentDir + "/packages/js/index.html"; + + let htmlFile = "file:///" + htmlFilePath; + + await page.goto(htmlFile); + + // Formbricks In App Sync has happened + const syncApi = await page.waitForResponse((response) => response.url().includes("/in-app/sync")); + expect(syncApi.status()).toBe(200); + + // Formbricks Modal exists in the DOM + await expect(page.locator("#formbricks-modal-container")).toHaveCount(1); + + // Formbricks Modal is visible + await expect(page.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.getByRole("button", { name: "Next" }).click(); + await page.locator("label").filter({ hasText: "Founder" }).click(); + await page.getByRole("button", { name: "Next" }).click(); + await page.getByLabel("").fill("People who believe that PMF is necessary"); + await page.getByRole("button", { name: "Next" }).click(); + await page.getByLabel("").fill("Much higher response rates!"); + await page.getByRole("button", { name: "Next" }).click(); + await page.getByLabel("Please be as specific as").fill("Make this end to end test pass!"); + await page.getByRole("button", { name: "Finish" }).click(); + await page.getByText("Thank you!").click(); + + // Formbricks Modal is not visible + await expect(page.getByText("Powered by Formbricks")).not.toBeVisible({ timeout: 10000 }); + }); + + test("Admin validates Response", async ({ page }) => { + await login(page, email, password); + + await page.locator("li").filter({ hasText: "In-Product SurveyProduct" }).getByRole("link").click(); + + (await page.waitForSelector("text=Responses")).isVisible(); + + // Survey should have 2 Displays + await expect(page.getByText("Displays2")).toBeVisible(); + // Survey should have 1 Response + await expect(page.getByRole("button", { name: "Responses50%" })).toBeVisible(); + await expect(page.getByText("1 responses", { exact: true }).first()).toBeVisible(); + await expect(page.getByText("Clickthrough Rate (CTR)100%")).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/utils/helper.ts b/apps/web/playwright/utils/helper.ts index e0513ca17f..b9f1b19596 100644 --- a/apps/web/playwright/utils/helper.ts +++ b/apps/web/playwright/utils/helper.ts @@ -1,4 +1,5 @@ import { expect } from "@playwright/test"; +import { readFileSync, writeFileSync } from "fs"; import { Page } from "playwright"; export const signUpAndLogin = async ( @@ -42,3 +43,11 @@ export const skipOnboarding = async (page: Page): Promise => { await expect(page).toHaveURL(/\/environments\/[^/]+\/surveys/); await expect(page.getByText("My Product")).toBeVisible(); }; + +export const replaceEnvironmentIdInHtml = (filePath: string, environmentId: string): string => { + let htmlContent = readFileSync(filePath, "utf-8"); + htmlContent = htmlContent.replace(/environmentId: ".*?"/, `environmentId: "${environmentId}"`); + + writeFileSync(filePath, htmlContent); + return "file:///" + filePath; +}; diff --git a/apps/web/playwright/utils/mock.ts b/apps/web/playwright/utils/mock.ts index e022141eca..58edb39256 100644 --- a/apps/web/playwright/utils/mock.ts +++ b/apps/web/playwright/utils/mock.ts @@ -25,6 +25,13 @@ export const users = { password: "Y1I*EpURUSb32j5XijP", }, ], + js: [ + { + name: "JS User 1", + email: "js1@formbricks.com", + password: "XpP%X9UU3efj8vJa", + }, + ], }; export const teams = { diff --git a/packages/js/index.html b/packages/js/index.html index 3de01e695f..ebc735a7a4 100644 --- a/packages/js/index.html +++ b/packages/js/index.html @@ -4,12 +4,12 @@ var t = document.createElement("script"); (t.type = "text/javascript"), (t.async = !0), - (t.src = "https://unpkg.com/@formbricks/js@^1.2.0/dist/index.umd.js"); + (t.src = "./dist/index.umd.js"); var e = document.getElementsByTagName("script")[0]; e.parentNode.insertBefore(t, e), setTimeout(function () { window.formbricks.init({ - environmentId: "clnojpqde0008qh46cge60d89", + environmentId: "clqb88h3k000a5xyb5zifl824", apiHost: "http://localhost:3000", debug: true, });