Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/frontend/src/layout/partials/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ export const Navbar = () => {
<div className="mb-6 space-y-2">
<p className="text-xs text-intg-text-4">Project</p>
<DropdownMenu>
<DropdownMenuTrigger className="w-[177px] select-none outline-none">
<DropdownMenuTrigger
className="w-[177px] select-none outline-none"
data-testid="project-dropdown"
>
<NavItem
text={project?.name as string}
leftIcon={<AcronymBox text={project?.name as string} />}
Expand All @@ -107,10 +110,12 @@ export const Navbar = () => {
{projects.map((item) => {
return (
<DropdownMenuItem
data-testid={"project-btn"}
key={item?.id}
onClick={() => {
switchProject(item as Project);
}}
className={cn(item?.slug === project?.slug ? "active" : "")}
>
<NavItem
leftIcon={<AcronymBox text={item?.name ?? ""} />}
Expand Down Expand Up @@ -147,6 +152,7 @@ export const Navbar = () => {

<DropdownMenuItem>
<Button
data-testid="create-new-project"
icon={<CirclePlusIcon />}
variant="custom"
text="New Project"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const Project = () => {
<form onSubmit={handleSubmit(onSubmit)}>
<div className="space-y-6">
<TextInput
data-testid="project-name"
label={"Project Name"}
className="font-semibold"
defaultValue={project?.name}
Expand All @@ -71,11 +72,13 @@ export const Project = () => {
<div className="flex w-full items-end gap-2">
<div className="flex-1">
<TextInput
data-testid="project-key"
label="Project Key"
value={project?.apiToken}
disabled={true}
rightIcon={
<Button
data-testid="refresh-project-key"
variant="custom"
size="sm"
disabled={loading}
Expand All @@ -87,6 +90,7 @@ export const Project = () => {
</div>
<Button
text="Copy"
data-testid="copy-project-key"
size="md"
icon={<CopyIcon size={16} />}
textAlign="center"
Expand Down
176 changes: 176 additions & 0 deletions apps/frontend/tests/projects/project.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { faker } from "@faker-js/faker";
import { expect, test } from "@playwright/test";
import fs from "fs";
import { ROUTES, userDetailsFile } from "../utils/constants";
import { waitForResponse } from "./../utils/helper";

test.describe("Create workspace", () => {
let projectSlug;
let workspaceSlug;
test.beforeAll(async () => {
const details = JSON.parse(fs.readFileSync(userDetailsFile, "utf-8"));
workspaceSlug = details.workspaceSlug;
projectSlug = details.projectSlug;
});

test("should allow user to create a new Project", async ({ page }) => {
await page.goto(ROUTES.SURVEY.LIST(workspaceSlug, projectSlug));
await page.waitForURL(ROUTES.PATTERNS.SURVEY_LIST_URL);

const oldUrl = page.url();

const newProject = faker.company.name();
await page.getByText(/invite team/i).waitFor();

await page.getByTestId("project-dropdown").waitFor();
await page.getByTestId("project-dropdown").click();
await page.getByTestId("create-new-project").click();
await page.getByText(/Create new project/i).waitFor();

await page.getByPlaceholder(/Project name/i).fill(newProject);

await page.getByRole("button", { name: /Create project/i }).click();
await expect(page.locator("div").filter({ hasText: "Success!Project created" }).nth(3)).toBeVisible();
await page.waitForURL((url) => ROUTES.PATTERNS.SURVEY_LIST_URL.test(url.pathname));

await page.getByTestId("project-dropdown").click();

const url = new URL(page.url());
const newUrl = url.pathname;

const extractProjectNameFromUrl = (url) => {
const match = url.match(/projects\/([\w-]+)\//);
return match ? match[1] : null;
};

const oldProjectName = extractProjectNameFromUrl(oldUrl);
const newProjectName = extractProjectNameFromUrl(newUrl);

expect(newProjectName).not.toEqual(oldProjectName);

projectSlug = url.pathname.split("/")[3];

const details = { workspaceSlug, projectSlug };

fs.writeFileSync(userDetailsFile, JSON.stringify(details), "utf-8");
});

test("should allow user to switch to another Project", async ({ page }) => {
await page.goto(ROUTES.SURVEY.LIST(workspaceSlug, projectSlug));
await page.waitForURL(ROUTES.PATTERNS.SURVEY_LIST_URL);

const oldUrl = page.url();

// Open the dropdown and wait for it to be visible
await page.getByTestId("project-dropdown").waitFor();
await page.getByTestId("project-dropdown").click();

const projectButtons = await page.locator('[data-testid="project-btn"]').elementHandles();

if (projectButtons.length === 0) {
throw new Error("No projects found in the dropdown.");
}

let activeIndex = -1;

for (let i = 0; i < projectButtons.length; i++) {
const isActive = await projectButtons[i].evaluate((el) => {
return (el as HTMLElement).classList.contains("active");
});
if (isActive) {
activeIndex = i;
break;
}
}

if (projectButtons.length === 1) {
if (activeIndex === -1) {
activeIndex = 0;
await projectButtons[activeIndex].click();
}
} else if (activeIndex !== -1 && activeIndex < projectButtons.length - 1) {
activeIndex = activeIndex + 1;
await projectButtons[activeIndex].click();
} else if (activeIndex !== -1 && activeIndex === projectButtons.length - 1) {
activeIndex = 0;

await projectButtons[activeIndex].click();
} else if (activeIndex === -1) {
activeIndex = 0;
await projectButtons[activeIndex].click();
}

const url = new URL(page.url());
const newUrl = url.pathname;

const extractProjectNameFromUrl = (url) => {
const match = url.match(/projects\/([\w-]+)\//);
return match ? match[1] : null;
};

const oldProjectName = extractProjectNameFromUrl(oldUrl);
const newProjectName = extractProjectNameFromUrl(newUrl);

if (projectButtons.length === 1) {
expect(newProjectName).toEqual(oldProjectName);
} else {
expect(newProjectName).not.toEqual(oldProjectName);
}

projectSlug = url.pathname.split("/")[3];

const details = { workspaceSlug, projectSlug };

fs.writeFileSync(userDetailsFile, JSON.stringify(details), "utf-8");
});

test("should allow user to update Project", async ({ page }) => {
await page.goto(ROUTES.PROJECT.SETTINGS(workspaceSlug, projectSlug));
await page.getByText(/Manage your Integraflow Project/i).waitFor();
const prevName = await page.getByTestId("project-name").inputValue();

await page.getByTestId("project-name").fill(faker.company.buzzAdjective());
await waitForResponse(page, "projectUpdate", async () => {
await page.getByRole("button", { name: "Update" }).click();
});

await page.reload();
await page.getByTestId("project-name").waitFor();

const newName = await page.getByTestId("project-name").inputValue();
expect(prevName).not.toBe(newName);
});
test("should allow user to refresh Project key", async ({ page }) => {
await page.goto(ROUTES.PROJECT.SETTINGS(workspaceSlug, projectSlug));
await page.getByText(/Manage your Integraflow Project/i).waitFor();

const previousKey = await page.getByTestId("project-key").inputValue();

await waitForResponse(page, "projectTokenReset", async () => {
await page.getByTestId("refresh-project-key").click();
});

await page.reload();
await page.getByTestId("project-key").waitFor();

const newKey = await page.getByTestId("project-key").inputValue();
expect(previousKey).not.toBe(newKey);
});

test("should allow user to copy Project key", async ({ page, context, browserName }) => {
if (browserName === "firefox" || browserName === "webkit") {
test.skip();
}

await page.goto(ROUTES.PROJECT.SETTINGS(workspaceSlug, projectSlug));
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.getByText(/Manage your Integraflow Project/i).waitFor();

const projectKey = await page.getByTestId("project-key").inputValue();
await page.getByTestId("copy-project-key").click();
const handle = await page.evaluateHandle(() => navigator.clipboard.readText());
const clipboardContent = await handle.jsonValue();

expect(clipboardContent).toBe(projectKey);
});
});
5 changes: 0 additions & 5 deletions apps/frontend/tests/survey/question.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,23 +103,18 @@ test.describe("Create questions", () => {
// // Log the inner HTML of the text editor for each question
await page.getByTestId("question-trigger").nth(0).click();
await page.waitForSelector(".ql-editor", { state: "attached" });
console.log("Question 1 HTML:", await page.locator(".ql-editor").evaluate((el) => el.innerHTML));

await page.getByTestId("question-trigger").nth(1).click();
await page.waitForSelector(".ql-editor", { state: "attached" });
console.log("Question 2 HTML:", await page.locator(".ql-editor").evaluate((el) => el.innerHTML));

await page.getByTestId("question-trigger").nth(2).click();
await page.waitForSelector(".ql-editor", { state: "attached" });
console.log("Question 3 HTML:", await page.locator(".ql-editor").evaluate((el) => el.innerHTML));

await page.getByTestId("question-trigger").nth(3).click();
await page.waitForSelector(".ql-editor", { state: "attached" });
console.log("Question 4 HTML:", await page.locator(".ql-editor").evaluate((el) => el.innerHTML));

await page.getByTestId("question-trigger").nth(0).click();
await page.waitForSelector(".ql-editor", { state: "attached" });
console.log("Question 4 HTML:", await page.locator(".ql-editor").evaluate((el) => el.innerHTML));
});

test("should allow user to delete a question", async ({ page }) => {
Expand Down
5 changes: 5 additions & 0 deletions apps/frontend/tests/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export const ROUTES = {
`/${workspaceSlug}/projects/${projectSlug}/get-started`,
},

PROJECT: {
SETTINGS: (workspaceSlug: string, projectSlug: string) =>
`/${workspaceSlug}/projects/${projectSlug}/settings/project`,
},

SURVEY: {
LIST: (workspaceSlug: string, projectSlug: string) => `/${workspaceSlug}/projects/${projectSlug}/surveys`,
SINGLE: (workspaceSlug: string, projectSlug: string, surveySlug: string) =>
Expand Down
47 changes: 0 additions & 47 deletions apps/frontend/tests/utils/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export const waitForResponse = async (page, operationName, actionCallback) => {
if (request) {
const postData = request.postData();
if (postData && postData.includes(operationName)) {
console.log("POSTDATAAAAAAAAAAAAAAAAAAAAAAAAA", postData);
return response.url().includes("/graphql") && response.status() === 200;
}
}
Expand All @@ -16,60 +15,14 @@ export const waitForResponse = async (page, operationName, actionCallback) => {

const request = response.request();
const postData = request?.postData();
console.log(postData);

if (postData) {
try {
const parsedData = JSON.parse(postData);
console.log("Parsed postData:", parsedData); // Log the parsed postData
return parsedData;
} catch (error) {
console.error("Error parsing postData:", error);
return null;
}
}
return null;
};
// export const waitForResponse = async (page, operationName, actionCallback) => {
// const [response] = await Promise.all([
// page.waitForResponse((response) => {
// const request = response.request();
// console.log("Received a response, checking request..."); // Log when a response is received

// if (request) {
// const postData = request.postData();
// console.log("Request postData:", postData); // Log the postData even if it's not the expected one

// if (postData && postData.includes(operationName)) {
// console.log(`Found matching operation: ${operationName}`); // Log if the operation name matches
// return response.url().includes("/graphql") && response.status() === 200;
// }
// }
// return false;
// }),
// actionCallback(), // Trigger the action that causes the response
// ]);

// if (!response) {
// console.error("Response not found!"); // Log when no response is matched
// return null;
// }

// console.log({ response }); // Log the response object

// const request = response.request();
// const postData = request?.postData();

// if (postData) {
// try {
// const parsedData = JSON.parse(postData);
// console.log("Parsed postData:", parsedData); // Log the parsed postData
// return parsedData;
// } catch (error) {
// console.error("Error parsing postData:", error);
// return null;
// }
// }

// return null;
// };