From 23238c846165ad0a5ddcec00773d77e18582dec1 Mon Sep 17 00:00:00 2001 From: caichi <54824604+caichi-t@users.noreply.github.com> Date: Wed, 28 Feb 2024 17:52:19 +0900 Subject: [PATCH] test(web): add simple, comprehensive, and minimum e2e tests (#1081) * add account and myIntegrations tests * add: logout test * add: integrations test * small fix * add: terrain test * add: member test * add: workspace test * add: project test * fix: terrain swtich test * add: overview test * add: accesibility test * add: asset test * add: request test * fix: comment on Asset test * add: schema test * move utils for workspace * move utils for project * move utils for model * move utils for group * move utils for comment * fix: missing import * move handleFieldForm * move createRequest func * add: content test * fix some codes * Revert "fix: synchronize account name with personal workspace name when updating account name" This reverts commit 388e67b5135da458c4bcc2ba20b12f56c551dc5c. * fix: request state * add: notification closing * fix: exclude path in vite.config * fix: integrations test * Apply suggestions from code review Co-authored-by: Nour Balaha --------- Co-authored-by: Nour Balaha --- web/e2e/common/notification.ts | 9 + web/e2e/general/auth.spec.ts | 8 + web/e2e/general/project.spec.ts | 63 ++++++ web/e2e/general/workspace.spec.ts | 45 +++++ web/e2e/project/accessibility.spec.ts | 29 +++ web/e2e/project/asset.spec.ts | 129 ++++++++++++ web/e2e/project/content.spec.ts | 185 ++++++++++++++++++ web/e2e/project/overview.spec.ts | 48 +++++ web/e2e/project/request.spec.ts | 164 ++++++++++++++++ web/e2e/project/schema.spec.ts | 53 +++++ web/e2e/project/utils/comment.ts | 36 ++++ web/e2e/project/utils/field.ts | 12 ++ web/e2e/project/utils/group.ts | 51 +++++ web/e2e/project/utils/item.ts | 21 ++ web/e2e/project/utils/model.ts | 53 +++++ web/e2e/project/utils/project.ts | 26 +++ web/e2e/project/utils/workspace.ts | 21 ++ web/e2e/settings/account.spec.ts | 97 +++++++++ web/e2e/settings/integrations.spec.ts | 64 ++++++ web/e2e/settings/member.spec.ts | 23 +++ web/e2e/settings/myIntegrations.spec.ts | 118 +++++++++++ web/e2e/settings/settings.spec.ts | 125 ++++++++++++ .../molecules/Common/Header/index.tsx | 6 +- .../Project/Request/RequestDetails/hooks.ts | 1 + web/vite.config.ts | 2 +- 25 files changed, 1387 insertions(+), 2 deletions(-) create mode 100644 web/e2e/common/notification.ts create mode 100644 web/e2e/general/auth.spec.ts create mode 100644 web/e2e/general/project.spec.ts create mode 100644 web/e2e/general/workspace.spec.ts create mode 100644 web/e2e/project/accessibility.spec.ts create mode 100644 web/e2e/project/asset.spec.ts create mode 100644 web/e2e/project/content.spec.ts create mode 100644 web/e2e/project/overview.spec.ts create mode 100644 web/e2e/project/request.spec.ts create mode 100644 web/e2e/project/schema.spec.ts create mode 100644 web/e2e/project/utils/comment.ts create mode 100644 web/e2e/project/utils/field.ts create mode 100644 web/e2e/project/utils/group.ts create mode 100644 web/e2e/project/utils/item.ts create mode 100644 web/e2e/project/utils/model.ts create mode 100644 web/e2e/project/utils/project.ts create mode 100644 web/e2e/project/utils/workspace.ts create mode 100644 web/e2e/settings/account.spec.ts create mode 100644 web/e2e/settings/integrations.spec.ts create mode 100644 web/e2e/settings/member.spec.ts create mode 100644 web/e2e/settings/myIntegrations.spec.ts create mode 100644 web/e2e/settings/settings.spec.ts diff --git a/web/e2e/common/notification.ts b/web/e2e/common/notification.ts new file mode 100644 index 0000000000..e5c229e1f7 --- /dev/null +++ b/web/e2e/common/notification.ts @@ -0,0 +1,9 @@ +import { Page } from "@playwright/test"; + +export async function closeNotification(page: Page) { + await page + .locator(".ant-notification-notice") + .last() + .locator(".ant-notification-notice-close") + .click(); +} diff --git a/web/e2e/general/auth.spec.ts b/web/e2e/general/auth.spec.ts new file mode 100644 index 0000000000..44545f71fe --- /dev/null +++ b/web/e2e/general/auth.spec.ts @@ -0,0 +1,8 @@ +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("Logout has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.locator("a").nth(1).click(); + await page.getByText("Logout").click(); + await expect(page.getByLabel("Log In")).toBeVisible(); +}); diff --git a/web/e2e/general/project.spec.ts b/web/e2e/general/project.spec.ts new file mode 100644 index 0000000000..d7ae81eaff --- /dev/null +++ b/web/e2e/general/project.spec.ts @@ -0,0 +1,63 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("Project CRUD and searching has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByRole("button", { name: "plus New Project" }).click(); + await page.getByLabel("Project name").click(); + await page.getByLabel("Project name").fill("project name"); + await page.getByLabel("Project alias").click(); + await page.getByLabel("Project alias").fill("project alias"); + await page.getByLabel("Project description").click(); + await page.getByLabel("Project description").fill("project description"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("input: createProject invalid alias"); + await closeNotification(page); + await page.getByLabel("Project alias").click(); + await page.getByLabel("Project alias").fill("project-alias"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created project!"); + await closeNotification(page); + + await expect(page.getByText("project name", { exact: true })).toBeVisible(); + await expect(page.getByText("project description", { exact: true })).toBeVisible(); + await page.locator(".ant-input-affix-wrapper").click(); + await page.getByPlaceholder("search projects").fill("no project"); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByText("project name", { exact: true })).not.toBeVisible(); + await page.getByRole("button", { name: "close-circle" }).click(); + await expect(page.getByText("project name", { exact: true })).toBeVisible(); + await page.getByText("project name", { exact: true }).click(); + await expect(page.getByText("project name").nth(1)).toBeVisible(); + await expect(page.getByText("project description")).toBeVisible(); + + await page.getByText("Settings").click(); + await page.getByLabel("Name").click(); + await page.getByLabel("Name").fill("new project name"); + await page.getByLabel("Description").click(); + await page.getByLabel("Description").fill("new project description"); + await page.locator("form").getByRole("button", { name: "Save changes" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated project!"); + await closeNotification(page); + + await expect(page.locator("#root")).toContainText("Project Settings / new project name"); + await expect(page.locator("header")).toContainText("new project name"); + await page.getByRole("row", { name: "Owner" }).getByRole("switch").click(); + await page.getByRole("button", { name: "Save changes" }).nth(1).click(); + await expect(page.getByRole("row", { name: "Owner" }).getByRole("switch")).toHaveAttribute( + "aria-checked", + "false", + ); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated request roles!"); + await closeNotification(page); + + await page.getByText("Overview").click(); + await expect(page.locator("#root")).toContainText("new project name"); + await expect(page.locator("#root")).toContainText("new project description"); + await page.getByText("Settings").click(); + await page.getByRole("button", { name: "Delete Project" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted project!"); + await closeNotification(page); + await expect(page.getByText("new project name", { exact: true })).not.toBeVisible(); +}); diff --git a/web/e2e/general/workspace.spec.ts b/web/e2e/general/workspace.spec.ts new file mode 100644 index 0000000000..4fd3f5fbd2 --- /dev/null +++ b/web/e2e/general/workspace.spec.ts @@ -0,0 +1,45 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("Workspace CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByRole("button", { name: "Create a Workspace" }).click(); + await page.getByLabel("Workspace name").click(); + await page.getByLabel("Workspace name").fill("workspace name"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created workspace!"); + await closeNotification(page); + + await page.getByText("Workspace", { exact: true }).click(); + await page.getByLabel("Workspace Name").click(); + await page.getByLabel("Workspace Name").fill("new workspace name"); + await page.getByRole("button", { name: "Save changes" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated workspace!"); + await closeNotification(page); + + await expect(page.locator("header")).toContainText("new workspace name"); + await page.getByRole("button", { name: "Remove Workspace" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted workspace!"); + await closeNotification(page); + + await page.locator("a").first().click(); + await expect(page.getByText("new workspace name")).not.toBeVisible(); +}); + +test("Workspace Creating from tab has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.locator("a").first().click(); + await page.getByText("Create Workspace").click(); + await page.getByLabel("Workspace name").click(); + await page.getByLabel("Workspace name").fill("workspace name"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created workspace!"); + await closeNotification(page); + + await page.getByText("Workspace", { exact: true }).click(); + await page.getByRole("button", { name: "Remove Workspace" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted workspace!"); + await closeNotification(page); +}); diff --git a/web/e2e/project/accessibility.spec.ts b/web/e2e/project/accessibility.spec.ts new file mode 100644 index 0000000000..51fd9f80db --- /dev/null +++ b/web/e2e/project/accessibility.spec.ts @@ -0,0 +1,29 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +import { createProject, deleteProject } from "./utils/project"; + +test("Update settings on Accesibility page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + + await page.getByText("Accessibility").click(); + await page.getByText("Private").click(); + await page.getByText("Public", { exact: true }).click(); + await page.getByRole("textbox").click(); + await page.getByRole("textbox").fill("new-e2e-project-alias"); + await page.getByRole("switch").click(); + await page.getByRole("button", { name: "Save changes" }).click(); + await expect(page.getByRole("alert").last()).toContainText( + "Successfully updated publication settings!", + ); + await closeNotification(page); + await expect(page.locator("form")).toContainText("Public"); + await expect(page.getByRole("textbox")).toHaveValue("new-e2e-project-alias"); + await expect(page.getByRole("switch")).toHaveAttribute("aria-checked", "true"); + await expect(page.locator("tbody")).toContainText( + "http://localhost:8080/api/p/new-e2e-project-alias/assets", + ); + + await deleteProject(page); +}); diff --git a/web/e2e/project/asset.spec.ts b/web/e2e/project/asset.spec.ts new file mode 100644 index 0000000000..c3006c2d32 --- /dev/null +++ b/web/e2e/project/asset.spec.ts @@ -0,0 +1,129 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +import { crudComment } from "./utils/comment"; +import { createProject, deleteProject } from "./utils/project"; + +const uploadFileUrl = + "https://assets.cms.plateau.reearth.io/assets/11/6d05db-ed47-4f88-b565-9eb385b1ebb0/13100_tokyo23-ku_2022_3dtiles%20_1_1_op_bldg_13101_chiyoda-ku_lod1/tileset.json"; +const uploadFileName = "tileset.json"; + +test("Asset CRUD and Searching has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + + await page.getByText("Asset").click(); + await page.getByRole("button", { name: "upload Upload Asset" }).click(); + await page.getByRole("tab", { name: "URL" }).click(); + await page.getByPlaceholder("Please input a valid URL").click(); + await page.getByPlaceholder("Please input a valid URL").fill(uploadFileUrl); + await page.getByRole("button", { name: "Upload", exact: true }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully added asset!"); + await closeNotification(page); + await expect(page.getByText(uploadFileName)).toBeVisible(); + await page.getByPlaceholder("Please enter").click(); + await page.getByPlaceholder("Please enter").fill("no asset"); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByText(uploadFileName)).not.toBeVisible(); + await page.getByPlaceholder("Please enter").click(); + await page.getByPlaceholder("Please enter").fill(""); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByText(uploadFileName)).toBeVisible(); + await page.getByLabel("edit").locator("svg").click(); + await page + .locator("div") + .filter({ hasText: /^Unknown Type$/ }) + .nth(1) + .click(); + await page.getByText("GEOJSON/KML/CZML").click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Asset was successfully updated!"); + await closeNotification(page); + await page.getByLabel("Back").click(); + await page.getByLabel("", { exact: true }).check(); + await page.getByText("Delete").click(); + await expect(page.getByText(uploadFileName)).not.toBeVisible(); + await expect(page.getByRole("alert").last()).toContainText( + "One or more assets were successfully deleted!", + ); + await closeNotification(page); + + await deleteProject(page); +}); + +test("Donwloading asset has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + + await page.getByText("Asset").click(); + await page.getByRole("button", { name: "upload Upload Asset" }).click(); + await page.getByRole("tab", { name: "URL" }).click(); + await page.getByPlaceholder("Please input a valid URL").click(); + await page.getByPlaceholder("Please input a valid URL").fill(uploadFileUrl); + await page.getByRole("button", { name: "Upload", exact: true }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully added asset!"); + await closeNotification(page); + await expect(page.getByText(uploadFileName)).toBeVisible(); + + await page.getByLabel("", { exact: true }).check(); + const downloadPromise = page.waitForEvent("download"); + await page.getByRole("button", { name: "download Download" }).click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toEqual(uploadFileName); + + await page.getByLabel("edit").locator("svg").click(); + const download1Promise = page.waitForEvent("download"); + await page.getByRole("button", { name: "download Download" }).click(); + const download1 = await download1Promise; + expect(download1.suggestedFilename()).toEqual(uploadFileName); + + await page.getByLabel("Back").click(); + await page.getByLabel("", { exact: true }).check(); + await page.getByText("Delete").click(); + await expect(page.getByRole("alert").last()).toContainText( + "One or more assets were successfully deleted!", + ); + await closeNotification(page); + await deleteProject(page); +}); + +test("Comment CRUD on edit page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + + await page.getByText("Asset").click(); + await page.getByRole("button", { name: "upload Upload Asset" }).click(); + await page.getByRole("tab", { name: "URL" }).click(); + await page.getByPlaceholder("Please input a valid URL").click(); + await page.getByPlaceholder("Please input a valid URL").fill(uploadFileUrl); + await page.getByRole("button", { name: "Upload", exact: true }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully added asset!"); + await closeNotification(page); + await expect(page.getByText(uploadFileName)).toBeVisible(); + + await page.getByRole("cell", { name: "edit" }).locator("svg").click(); + await page.getByLabel("message").click(); + await expect(page.getByText("CommentsComment")).toBeVisible(); + await crudComment(page); + await deleteProject(page); +}); + +test("Comment CRUD on Asset page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + + await page.getByText("Asset").click(); + await page.getByRole("button", { name: "upload Upload Asset" }).click(); + await page.getByRole("tab", { name: "URL" }).click(); + await page.getByPlaceholder("Please input a valid URL").click(); + await page.getByPlaceholder("Please input a valid URL").fill(uploadFileUrl); + await page.getByRole("button", { name: "Upload", exact: true }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully added asset!"); + await closeNotification(page); + await expect(page.getByText(uploadFileName)).toBeVisible(); + + await page.getByRole("button", { name: "0" }).click(); + await expect(page.getByText("CommentsNo comments.Comment")).toBeVisible(); + await crudComment(page); + await deleteProject(page); +}); diff --git a/web/e2e/project/content.spec.ts b/web/e2e/project/content.spec.ts new file mode 100644 index 0000000000..5fae73b1f3 --- /dev/null +++ b/web/e2e/project/content.spec.ts @@ -0,0 +1,185 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +import { crudComment } from "./utils/comment"; +import { handleFieldForm } from "./utils/field"; +import { createRequest } from "./utils/item"; +import { createModel } from "./utils/model"; +import { createProject, deleteProject } from "./utils/project"; +import { createWorkspace, deleteWorkspace } from "./utils/workspace"; + +test("Item CRUD and searching has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + await createModel(page); + await page + .locator("li") + .filter({ hasText: "TextHeading and titles, one-" }) + .locator("div") + .first() + .click(); + await handleFieldForm(page, "text"); + await closeNotification(page); + await page.getByText("Content").click(); + await page.getByRole("button", { name: "plus New Item" }).click(); + await page.getByLabel("text").click(); + await page.getByLabel("text").fill("text"); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created Item!"); + await closeNotification(page); + await page.getByLabel("Back").click(); + await expect(page.getByRole("cell", { name: "text", exact: true })).toBeVisible(); + await page.getByPlaceholder("Please enter").click(); + await page.getByPlaceholder("Please enter").fill("no field"); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByRole("cell", { name: "text", exact: true })).not.toBeVisible(); + await page.getByPlaceholder("Please enter").fill(""); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByRole("cell", { name: "text", exact: true })).toBeVisible(); + await page.getByRole("link", { name: "edit", exact: true }).click(); + await page.getByLabel("text").click(); + + await page.getByLabel("text").click(); + await page.getByLabel("text").fill("new text"); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated Item!"); + await closeNotification(page); + await page.getByLabel("Back").click(); + await expect(page.getByRole("cell", { name: "new text" })).toBeVisible(); + await page.getByLabel("", { exact: true }).check(); + await page.getByText("Delete").click(); + await expect(page.getByRole("alert").last()).toContainText( + "One or more items were successfully deleted!", + ); + await closeNotification(page); + await expect(page.getByRole("cell", { name: "new text" })).not.toBeVisible(); + await deleteProject(page); +}); + +test("Publishing and Unpublishing item has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + await createModel(page); + await page + .locator("li") + .filter({ hasText: "TextHeading and titles, one-" }) + .locator("div") + .first() + .click(); + await handleFieldForm(page, "text"); + await closeNotification(page); + await page.getByText("Content").click(); + await page.getByRole("button", { name: "plus New Item" }).click(); + await page.getByLabel("text").click(); + await page.getByLabel("text").fill("text"); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created Item!"); + await closeNotification(page); + await page.getByRole("button", { name: "Publish" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully published items!"); + await closeNotification(page); + await expect(page.getByText("PUBLIC")).toBeVisible(); + await page.getByLabel("Back").click(); + await expect(page.getByText("PUBLIC")).toBeVisible(); + await page.getByLabel("", { exact: true }).check(); + await page.getByText("Unpublish").click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully unpublished items!"); + await closeNotification(page); + await expect(page.getByText("DRAFT")).toBeVisible(); + await page.getByRole("link", { name: "edit", exact: true }).click(); + await expect(page.getByText("DRAFT")).toBeVisible(); + await page.getByRole("button", { name: "Publish" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully published items!"); + await closeNotification(page); + await expect(page.getByText("PUBLIC")).toBeVisible(); + await page.getByRole("button", { name: "ellipsis" }).click(); + await page.getByText("Unpublish").click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully unpublished items!"); + await closeNotification(page); + await expect(page.getByText("DRAFT")).toBeVisible(); + await page.getByLabel("Back").click(); + await expect(page.getByText("DRAFT")).toBeVisible(); + await deleteProject(page); +}); + +const requestTitle = "title"; + +test("Creating a new request and adding to request has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + const username = await page.locator("a").nth(1).locator("div").nth(2).locator("p").innerText(); + await createWorkspace(page); + await createProject(page); + await createModel(page); + await createRequest(page, username, requestTitle); + await page.getByLabel("Back").click(); + await page.getByRole("button", { name: "plus New Item" }).click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created Item!"); + await closeNotification(page); + await page.getByRole("button", { name: "ellipsis" }).click(); + await page.getByText("Add to Request").click(); + await page.getByLabel("", { exact: true }).check(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated Request!"); + await closeNotification(page); + await page.getByText("Request", { exact: true }).click(); + await page.getByRole("button", { name: "edit" }).click(); + await expect(page.getByRole("button", { name: "right e2e model name" }).first()).toBeVisible(); + await expect(page.getByRole("button", { name: "right e2e model name" }).nth(1)).toBeVisible(); + await deleteProject(page); + await deleteWorkspace(page); +}); + +test("Comment CRUD on Content page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + await createModel(page); + await page + .locator("li") + .filter({ hasText: "TextHeading and titles, one-" }) + .locator("div") + .first() + .click(); + await handleFieldForm(page, "text"); + await closeNotification(page); + await page.getByText("Content").click(); + await page.getByRole("button", { name: "plus New Item" }).click(); + await page.getByLabel("text").click(); + await page.getByLabel("text").fill("text"); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created Item!"); + await closeNotification(page); + + await page.getByLabel("Back").click(); + + await page.getByRole("button", { name: "0" }).click(); + await expect(page.getByText("CommentsNo comments.Comment")).toBeVisible(); + await crudComment(page); + + await deleteProject(page); +}); + +test("Comment CRUD on edit page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + await createModel(page); + await page + .locator("li") + .filter({ hasText: "TextHeading and titles, one-" }) + .locator("div") + .first() + .click(); + await handleFieldForm(page, "text"); + await closeNotification(page); + await page.getByText("Content").click(); + await page.getByRole("button", { name: "plus New Item" }).click(); + await page.getByLabel("text").click(); + await page.getByLabel("text").fill("text"); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created Item!"); + await closeNotification(page); + await page.getByLabel("message").click(); + await expect(page.getByText("CommentsComment")).toBeVisible(); + await crudComment(page); + await deleteProject(page); +}); diff --git a/web/e2e/project/overview.spec.ts b/web/e2e/project/overview.spec.ts new file mode 100644 index 0000000000..021190fe22 --- /dev/null +++ b/web/e2e/project/overview.spec.ts @@ -0,0 +1,48 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +import { createProject, deleteProject } from "./utils/project"; + +test("Model CRUD on Overview page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + + await page.getByRole("button", { name: "plus New Model" }).click(); + await page.getByLabel("Model name").click(); + await page.getByLabel("Model name").fill("model name"); + await page.getByLabel("Model description").click(); + await page.getByLabel("Model description").fill("model description"); + await page.getByLabel("Model key").click(); + await page.getByLabel("Model key").fill("model key"); + await expect(page.getByRole("button", { name: "Ok" })).not.toBeEnabled(); + await page.getByLabel("Model key").click(); + await page.getByLabel("Model key").fill("model-key"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created model!"); + await closeNotification(page); + await expect(page.getByTitle("model name")).toBeVisible(); + await expect(page.getByText("#model-key")).toBeVisible(); + await expect(page.getByRole("menuitem", { name: "model name" }).locator("span")).toBeVisible(); + await page.getByText("Overview").click(); + await page.getByRole("list").locator("a").click(); + await page.getByText("Edit", { exact: true }).click(); + await page.getByLabel("Model name").click(); + await page.getByLabel("Model name").fill("new model name"); + await page.getByLabel("Model description").click(); + await page.getByLabel("Model description").fill("new model description"); + await page.getByLabel("Model key").click(); + await page.getByLabel("Model key").fill("new-model-key"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated model!"); + await closeNotification(page); + await expect(page.locator("#root")).toContainText("new model name"); + await expect(page.locator("#root")).toContainText("new model description"); + await page.getByRole("list").locator("a").click(); + await page.getByText("Delete").click(); + await page.getByRole("button", { name: "Delete Model" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted model!"); + await closeNotification(page); + await expect(page.locator("#root")).not.toContainText("new model name"); + + await deleteProject(page); +}); diff --git a/web/e2e/project/request.spec.ts b/web/e2e/project/request.spec.ts new file mode 100644 index 0000000000..37f6db5197 --- /dev/null +++ b/web/e2e/project/request.spec.ts @@ -0,0 +1,164 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +import { crudComment } from "./utils/comment"; +import { createRequest } from "./utils/item"; +import { createModel } from "./utils/model"; +import { createProject, deleteProject } from "./utils/project"; +import { createWorkspace, deleteWorkspace } from "./utils/workspace"; + +const requestTitle = "title"; + +test("Request creating, searching, updating reviewer, and approving has succeeded", async ({ + reearth, + page, +}) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + const username = await page.locator("a").nth(1).locator("div").nth(2).locator("p").innerText(); + + await createWorkspace(page); + await createProject(page); + await createModel(page); + await createRequest(page, username, requestTitle); + + await page.getByText("Request", { exact: true }).click(); + await expect(page.getByText(requestTitle, { exact: true })).toBeVisible(); + await expect(page.getByText("WAITING")).toBeVisible(); + await page.getByPlaceholder("Please enter").click(); + await page.getByPlaceholder("Please enter").fill("no request"); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByText(requestTitle, { exact: true })).not.toBeVisible(); + await expect(page.getByText("WAITING")).not.toBeVisible(); + await page.getByPlaceholder("Please enter").fill(""); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByText(requestTitle, { exact: true })).toBeVisible(); + await expect(page.getByText("WAITING")).toBeVisible(); + + await page.getByRole("button", { name: "edit" }).click(); + await page.getByRole("button", { name: "Assign to" }).click(); + await page.getByLabel("close-circle").locator("svg").click(); + await page.locator(".ant-select-selection-overflow").click(); + await page.locator(".ant-select-item").click(); + await page.getByRole("heading", { name: "Reviewer" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated request!"); + await closeNotification(page); + await page.getByRole("button", { name: "Approve" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully approved request!"); + await closeNotification(page); + await expect(page.getByText(requestTitle, { exact: true })).not.toBeVisible(); + await page.getByRole("cell", { name: "State filter" }).getByRole("button").click(); + await page.getByRole("menuitem", { name: "WAITING" }).getByLabel("").uncheck(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByText("title", { exact: true })).toBeVisible(); + await expect(page.locator("tbody").getByText("APPROVED")).toBeVisible(); + + await deleteProject(page); + await deleteWorkspace(page); +}); + +test("Request closing and reopening has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + const username = await page.locator("a").nth(1).locator("div").nth(2).locator("p").innerText(); + + await createWorkspace(page); + await createProject(page); + await createModel(page); + await createRequest(page, username, requestTitle); + + await page.getByText("Request", { exact: true }).click(); + await expect(page.getByText(requestTitle, { exact: true })).toBeVisible(); + await expect(page.getByText("WAITING")).toBeVisible(); + await page.getByRole("button", { name: "edit" }).click(); + + await page.getByRole("button", { name: "Close" }).click(); + await expect(page.getByRole("alert").last()).toContainText( + "One or more requests were successfully closed!", + ); + await closeNotification(page); + await expect(page.getByText(requestTitle, { exact: true })).not.toBeVisible(); + await expect(page.getByText("WAITING")).not.toBeVisible(); + + await page.getByRole("cell", { name: "State filter" }).getByRole("button").click(); + await page.getByRole("menuitem", { name: "WAITING" }).getByLabel("").uncheck(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.locator("tbody").getByText("CLOSED")).toBeVisible(); + await page.getByRole("button", { name: "edit" }).click(); + await expect(page.getByText("CLOSED", { exact: true })).toBeVisible(); + await expect(page.getByText("Closed", { exact: true })).toBeVisible(); + await page.getByRole("button", { name: "Reopen" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated request!"); + await closeNotification(page); + await page.getByLabel("Back").click(); + await expect(page.getByText("title", { exact: true })).toBeVisible(); + await expect(page.getByText("WAITING")).toBeVisible(); + await page.getByLabel("", { exact: true }).check(); + await page.getByText("Close").click(); + await expect(page.getByRole("alert").last()).toContainText( + "One or more requests were successfully closed!", + ); + await closeNotification(page); + await page.getByRole("cell", { name: "State filter" }).getByRole("button").click(); + await page.getByRole("menuitem", { name: "WAITING" }).getByLabel("").uncheck(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.locator("tbody").getByText("CLOSED")).toBeVisible(); + await page.getByRole("button", { name: "edit" }).click(); + await expect(page.getByText("CLOSED", { exact: true })).toBeVisible(); + + await deleteProject(page); + await deleteWorkspace(page); +}); + +test("Comment CRUD on edit page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + const username = await page.locator("a").nth(1).locator("div").nth(2).locator("p").innerText(); + + await createWorkspace(page); + await createProject(page); + await createModel(page); + await createRequest(page, username, requestTitle); + + await page.getByText("Request", { exact: true }).click(); + await expect(page.getByText(requestTitle, { exact: true })).toBeVisible(); + await expect(page.getByText("WAITING")).toBeVisible(); + await page.getByRole("button", { name: "edit" }).click(); + + await page.locator("#content").click(); + await page.locator("#content").fill("comment"); + await page.getByRole("button", { name: "Comment" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created comment!"); + await closeNotification(page); + await expect(page.getByText("comment").nth(1)).toBeVisible(); + await page.getByLabel("edit").locator("svg").click(); + await page.locator("textarea").filter({ hasText: "comment" }).click(); + await page.locator("textarea").filter({ hasText: "comment" }).fill("new comment"); + await page.getByLabel("check").locator("svg").first().click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated comment!"); + await closeNotification(page); + await expect(page.getByText("new comment").nth(1)).toBeVisible(); + await page.getByLabel("delete").locator("svg").click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted comment!"); + await closeNotification(page); + await expect(page.getByText("new comment").nth(1)).not.toBeVisible(); + + await deleteProject(page); + await deleteWorkspace(page); +}); + +test("Comment CRUD on Request page has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + const username = await page.locator("a").nth(1).locator("div").nth(2).locator("p").innerText(); + + await createWorkspace(page); + await createProject(page); + await createModel(page); + await createRequest(page, username, requestTitle); + + await page.getByText("Request", { exact: true }).click(); + await page.getByRole("button", { name: "0" }).click(); + await expect(page.getByText("CommentsNo comments.Comment")).toBeVisible(); + + await crudComment(page); + + await deleteProject(page); + await deleteWorkspace(page); +}); diff --git a/web/e2e/project/schema.spec.ts b/web/e2e/project/schema.spec.ts new file mode 100644 index 0000000000..7882f36a47 --- /dev/null +++ b/web/e2e/project/schema.spec.ts @@ -0,0 +1,53 @@ +import { Page } from "@playwright/test"; + +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +import { handleFieldForm } from "./utils/field"; +import { crudGroup } from "./utils/group"; +import { createModel, crudModel } from "./utils/model"; +import { createProject, deleteProject } from "./utils/project"; + +async function deleteField(page: Page, name: string, key = name) { + await page.getByLabel("delete").locator("svg").click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.locator("body")).toContainText("Successfully deleted field!"); + await closeNotification(page); + await expect(page.getByText(`${name} #${key}`)).not.toBeVisible(); +} + +test("Model CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + await crudModel(page); + await deleteProject(page); +}); + +test("Group CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + await crudGroup(page); + await deleteProject(page); +}); + +test("Text field CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createProject(page); + await createModel(page); + await page + .locator("li") + .filter({ hasText: "TextHeading and titles, one-" }) + .locator("div") + .first() + .click(); + await handleFieldForm(page, "text"); + await expect(page.getByRole("alert").last()).toContainText("Successfully created field!"); + await closeNotification(page); + await page.getByRole("img", { name: "ellipsis" }).locator("svg").click(); + await handleFieldForm(page, "new text", "new-text"); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated field!"); + await closeNotification(page); + await deleteField(page, "new text", "new-text"); + + await deleteProject(page); +}); diff --git a/web/e2e/project/utils/comment.ts b/web/e2e/project/utils/comment.ts new file mode 100644 index 0000000000..501d2597ef --- /dev/null +++ b/web/e2e/project/utils/comment.ts @@ -0,0 +1,36 @@ +import { Page } from "@playwright/test"; + +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect } from "@reearth-cms/e2e/utils"; + +async function createComment(page: Page) { + await page.locator("#content").click(); + await page.locator("#content").fill("comment"); + await page.getByRole("button", { name: "Comment" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created comment!"); + await closeNotification(page); + await expect(page.getByRole("main")).toContainText("comment"); +} + +async function updateComment(page: Page) { + await page.getByRole("main").getByRole("complementary").getByLabel("edit").locator("svg").click(); + await page.locator("textarea").filter({ hasText: "comment" }).click(); + await page.locator("textarea").filter({ hasText: "comment" }).fill("new comment"); + await page.getByLabel("check").locator("svg").first().click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated comment!"); + await closeNotification(page); + await expect(page.getByRole("main")).toContainText("new comment"); +} + +async function deleteComment(page: Page) { + await page.getByLabel("delete").locator("svg").click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted comment!"); + await closeNotification(page); + await expect(page.getByRole("main")).not.toContainText("new comment"); +} + +export async function crudComment(page: Page) { + await createComment(page); + await updateComment(page); + await deleteComment(page); +} diff --git a/web/e2e/project/utils/field.ts b/web/e2e/project/utils/field.ts new file mode 100644 index 0000000000..fa26705754 --- /dev/null +++ b/web/e2e/project/utils/field.ts @@ -0,0 +1,12 @@ +import { Page } from "@playwright/test"; + +import { expect } from "@reearth-cms/e2e/utils"; + +export async function handleFieldForm(page: Page, name: string, key = name) { + await page.getByLabel("Display name").click(); + await page.getByLabel("Display name").fill(name); + await page.getByLabel("Settings").locator("#key").click(); + await page.getByLabel("Settings").locator("#key").fill(key); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByText(`${name} #${key}`)).toBeVisible(); +} diff --git a/web/e2e/project/utils/group.ts b/web/e2e/project/utils/group.ts new file mode 100644 index 0000000000..907c096cc7 --- /dev/null +++ b/web/e2e/project/utils/group.ts @@ -0,0 +1,51 @@ +import { Page } from "@playwright/test"; + +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect } from "@reearth-cms/e2e/utils"; + +async function createGroup(page: Page) { + await page.getByText("Schema").click(); + await page.getByRole("button", { name: "plus Add" }).last().click(); + await page.getByLabel("Group name").click(); + await page.getByLabel("Group name").fill("e2e group name"); + await page.getByLabel("Group key").click(); + await page.getByLabel("Group key").fill("e2e-group-key"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created group!"); + await closeNotification(page); + await page.getByText("e2e group name").click(); + await expect(page.getByTitle("e2e group name")).toBeVisible(); + await expect(page.getByText("#e2e-group-key")).toBeVisible(); +} + +const updateGroupName = "new e2e group name"; + +async function updateGroup(page: Page) { + await page.getByRole("button", { name: "more" }).hover(); + await page.getByText("Edit", { exact: true }).click(); + await page.getByLabel("Update Group").locator("#name").click(); + await page.getByLabel("Update Group").locator("#name").fill(updateGroupName); + await page.getByLabel("Update Group").locator("#key").click(); + await page.getByLabel("Update Group").locator("#key").fill("new-e2e-group-key"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated group!"); + await closeNotification(page); + await expect(page.getByTitle(updateGroupName)).toBeVisible(); + await expect(page.getByText("#new-e2e-group-key")).toBeVisible(); + await expect(page.getByRole("menuitem", { name: updateGroupName }).locator("span")).toBeVisible(); +} + +async function deleteGroup(page: Page) { + await page.getByRole("button", { name: "more" }).hover(); + await page.getByText("Delete").click(); + await page.getByRole("button", { name: "Delete Group" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted group!"); + await closeNotification(page); + await expect(page.getByTitle(updateGroupName)).not.toBeVisible(); +} + +export async function crudGroup(page: Page) { + await createGroup(page); + await updateGroup(page); + await deleteGroup(page); +} diff --git a/web/e2e/project/utils/item.ts b/web/e2e/project/utils/item.ts new file mode 100644 index 0000000000..20f44eefb0 --- /dev/null +++ b/web/e2e/project/utils/item.ts @@ -0,0 +1,21 @@ +import { Page } from "@playwright/test"; + +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect } from "@reearth-cms/e2e/utils"; + +export async function createRequest(page: Page, reviewerName: string, title: string) { + await page.getByText("Content").click(); + await page.getByRole("button", { name: "plus New Item" }).click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created Item!"); + await closeNotification(page); + await page.getByRole("button", { name: "New Request" }).click(); + await page.getByLabel("Title").click(); + await page.getByLabel("Title").fill(title); + await page.locator(".ant-select-selection-overflow").click(); + + await page.getByTitle(reviewerName).locator("div").click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created request!"); + await closeNotification(page); +} diff --git a/web/e2e/project/utils/model.ts b/web/e2e/project/utils/model.ts new file mode 100644 index 0000000000..11dd002636 --- /dev/null +++ b/web/e2e/project/utils/model.ts @@ -0,0 +1,53 @@ +import { Page } from "@playwright/test"; + +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect } from "@reearth-cms/e2e/utils"; + +export async function createModel(page: Page) { + await page.getByText("Schema").click(); + await page.getByRole("button", { name: "plus Add" }).first().click(); + await page.getByLabel("Model name").click(); + await page.getByLabel("Model name").fill("e2e model name"); + await page.getByLabel("Model key").click(); + await page.getByLabel("Model key").fill("e2e-model-key"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created model!"); + await closeNotification(page); + await expect(page.getByTitle("e2e model name")).toBeVisible(); + await expect(page.getByText("#e2e-model-key")).toBeVisible(); + await expect( + page.getByRole("menuitem", { name: "e2e model name" }).locator("span"), + ).toBeVisible(); +} + +const updateModelName = "new e2e model name"; + +async function updateModel(page: Page) { + await page.getByRole("button", { name: "more" }).hover(); + await page.getByText("Edit", { exact: true }).click(); + await page.getByLabel("Update Model").locator("#name").click(); + await page.getByLabel("Update Model").locator("#name").fill(updateModelName); + await page.getByLabel("Update Model").locator("#key").click(); + await page.getByLabel("Update Model").locator("#key").fill("new-e2e-model-key"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated model!"); + await closeNotification(page); + await expect(page.getByTitle(updateModelName)).toBeVisible(); + await expect(page.getByText("#new-e2e-model-key")).toBeVisible(); + await expect(page.getByRole("menuitem", { name: updateModelName }).locator("span")).toBeVisible(); +} + +async function deleteModel(page: Page) { + await page.getByRole("button", { name: "more" }).hover(); + await page.getByText("Delete").click(); + await page.getByRole("button", { name: "Delete Model" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted model!"); + await closeNotification(page); + await expect(page.getByTitle(updateModelName)).not.toBeVisible(); +} + +export async function crudModel(page: Page) { + await createModel(page); + await updateModel(page); + await deleteModel(page); +} diff --git a/web/e2e/project/utils/project.ts b/web/e2e/project/utils/project.ts new file mode 100644 index 0000000000..a16e09ab7d --- /dev/null +++ b/web/e2e/project/utils/project.ts @@ -0,0 +1,26 @@ +import { Page } from "@playwright/test"; + +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect } from "@reearth-cms/e2e/utils"; + +export async function createProject(page: Page) { + await page.getByRole("button", { name: "plus New Project" }).first().click(); + await page.getByRole("dialog").locator("#name").click(); + await page.getByRole("dialog").locator("#name").fill("e2e project name"); + await page.getByLabel("Project alias").click(); + await page.getByLabel("Project alias").fill("e2e-project-alias"); + await page.getByLabel("Project description").click(); + await page.getByLabel("Project description").fill("e2e project description"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created project!"); + await closeNotification(page); + await page.getByText("e2e project name", { exact: true }).click(); +} + +export async function deleteProject(page: Page) { + await page.getByText("Settings").first().click(); + await page.getByRole("button", { name: "Delete Project" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted project!"); + await closeNotification(page); +} diff --git a/web/e2e/project/utils/workspace.ts b/web/e2e/project/utils/workspace.ts new file mode 100644 index 0000000000..a8d1cea040 --- /dev/null +++ b/web/e2e/project/utils/workspace.ts @@ -0,0 +1,21 @@ +import { Page } from "@playwright/test"; + +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect } from "@reearth-cms/e2e/utils"; + +export async function createWorkspace(page: Page) { + await page.getByRole("button", { name: "Create a Workspace" }).click(); + await page.getByLabel("Workspace name").click(); + await page.getByLabel("Workspace name").fill("e2e workspace name"); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created workspace!"); + await closeNotification(page); +} + +export async function deleteWorkspace(page: Page) { + await page.getByText("Workspace", { exact: true }).click(); + await page.getByRole("button", { name: "Remove Workspace" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted workspace!"); + await closeNotification(page); +} diff --git a/web/e2e/settings/account.spec.ts b/web/e2e/settings/account.spec.ts new file mode 100644 index 0000000000..ed3e318cf0 --- /dev/null +++ b/web/e2e/settings/account.spec.ts @@ -0,0 +1,97 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("Name and email has updated", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByText("Account").click(); + + const username = await page.getByLabel("Account Name").inputValue(); + await page.getByLabel("Account Name").click(); + await page.getByLabel("Account Name").fill("new name"); + const email = await page.getByLabel("Your Email").inputValue(); + await page.getByLabel("Your Email").click(); + await page.getByLabel("Your Email").fill("test@test.com"); + await page + .locator("form") + .filter({ hasText: "Account NameThis is your ID" }) + .getByRole("button") + .click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated user!"); + await closeNotification(page); + + await page.getByLabel("Account Name").click(); + await page.getByLabel("Account Name").fill(username); + await page.getByLabel("Your Email").click(); + await page.getByLabel("Your Email").fill(email); + await page + .locator("form") + .filter({ hasText: "Account NameThis is your ID" }) + .getByRole("button") + .click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated user!"); + await closeNotification(page); +}); + +test("Language has updated from English to Japanese", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByText("Account").click(); + + await page + .locator("div") + .filter({ hasText: /^English$/ }) + .nth(3) + .click(); + await page.getByTitle("日本語").click(); + await page + .locator("form") + .filter({ hasText: "Service Language日本語日本語This" }) + .getByRole("button") + .click(); + await expect(page.getByRole("alert").last()).toContainText("言語設定の更新に成功しました。"); + await closeNotification(page); + await expect(page.locator("#root")).toContainText("ホーム"); + await page + .locator("div") + .filter({ hasText: /^日本語UIの言語設定を変更します。$/ }) + .locator("div") + .nth(3) + .click(); + await page.getByTitle("English").click(); + await page + .locator("form") + .filter({ hasText: "利用言語EnglishEnglishUI" }) + .getByRole("button") + .click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated language!"); + await closeNotification(page); +}); + +test("Language has updated from English to Auto", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByText("Account").click(); + + await page + .locator("div") + .filter({ hasText: /^EnglishThis will change the UI language$/ }) + .locator("div") + .nth(3) + .click(); + await page.getByTitle("Auto").click(); + await page + .locator("form") + .filter({ hasText: "Service LanguageAutoAutoThis" }) + .getByRole("button") + .click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated language!"); + await closeNotification(page); + await page + .locator("div") + .filter({ hasText: /^AutoThis will change the UI language$/ }) + .locator("div") + .nth(3) + .click(); + await page.getByTitle("English").click(); + await page.locator("form").filter({ hasText: "Service" }).getByRole("button").click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated language!"); + await closeNotification(page); +}); diff --git a/web/e2e/settings/integrations.spec.ts b/web/e2e/settings/integrations.spec.ts new file mode 100644 index 0000000000..8c305d8cfd --- /dev/null +++ b/web/e2e/settings/integrations.spec.ts @@ -0,0 +1,64 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("Integration CRUD and searching has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByText("My Integrations").click(); + + await page.locator("div").filter({ hasText: "Create new integration" }).nth(4).click(); + await page.getByLabel("Integration Name").click(); + await page.getByLabel("Integration Name").fill("e2e integration name"); + await page.getByLabel("Description").click(); + await page.getByLabel("Description").fill("e2e integration description"); + await page.getByRole("button", { name: "Create" }).click(); + await closeNotification(page); + await page.getByText("Integrations", { exact: true }).click(); + await page.getByRole("button", { name: "api Connect Integration" }).first().click(); + await page + .locator("div") + .filter({ hasText: /^e2e integration name$/ }) + .first() + .click(); + await page.getByRole("button", { name: "Connect", exact: true }).click(); + await expect(page.getByRole("alert").last()).toContainText( + "Successfully connected integration to the workspace!", + ); + await closeNotification(page); + await expect(page.getByRole("cell", { name: "e2e integration name", exact: true })).toBeVisible(); + await page.getByPlaceholder("Please enter").click(); + await page.getByPlaceholder("Please enter").fill("e2e integration name"); + await page.getByRole("button", { name: "search" }).click(); + await page.getByRole("cell", { name: "setting" }).locator("svg").click(); + await page + .locator("div") + .filter({ hasText: /^Reader$/ }) + .nth(4) + .click(); + await page.getByTitle("Writer").click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText( + "Successfully updated workspace integration!", + ); + await closeNotification(page); + await expect(page.getByRole("cell", { name: "WRITER" })).toBeVisible(); + await page.getByPlaceholder("Please enter").click(); + await page.getByPlaceholder("Please enter").fill("no integration"); + await page.getByRole("button", { name: "search" }).click(); + await expect( + page.getByRole("cell", { name: "e2e integration name", exact: true }), + ).not.toBeVisible(); + await page.getByPlaceholder("Please enter").click(); + await page.getByPlaceholder("Please enter").fill("e2e integration name"); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByRole("cell", { name: "e2e integration name", exact: true })).toBeVisible(); + await page.getByLabel("", { exact: true }).check(); + await page.getByText("Remove").click(); + await expect(page.getByRole("alert").last()).toContainText( + "One or more integrations were successfully deleted!", + ); + await closeNotification(page); + await page.getByText("My Integrations").click(); + await page.getByText("e2e integration namee2e").first().click(); + await page.getByRole("button", { name: "Remove Integration" }).click(); + await page.getByRole("button", { name: "OK" }).click(); +}); diff --git a/web/e2e/settings/member.spec.ts b/web/e2e/settings/member.spec.ts new file mode 100644 index 0000000000..d1b280fa94 --- /dev/null +++ b/web/e2e/settings/member.spec.ts @@ -0,0 +1,23 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("Searching current members has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByRole("button", { name: "Create a Workspace" }).click(); + await page.getByLabel("Workspace name").click(); + await page.getByLabel("Workspace name").fill("test workspace"); + await page.getByRole("button", { name: "OK" }).click(); + await page.getByText("Member").click(); + await expect(page.getByRole("cell", { name: "OWNER" })).toBeVisible(); + await page.getByPlaceholder("search for a member").click(); + await page.getByPlaceholder("search for a member").fill("no member"); + await page.getByRole("button", { name: "search" }).click(); + await expect(page.getByRole("cell", { name: "OWNER" })).not.toBeVisible(); + await page.getByRole("button", { name: "close-circle" }).click(); + await expect(page.getByRole("cell", { name: "OWNER" })).toBeVisible(); + await page.getByText("Workspace", { exact: true }).click(); + await page.getByRole("button", { name: "Remove Workspace" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted workspace!"); + await closeNotification(page); +}); diff --git a/web/e2e/settings/myIntegrations.spec.ts b/web/e2e/settings/myIntegrations.spec.ts new file mode 100644 index 0000000000..5e0529f2bc --- /dev/null +++ b/web/e2e/settings/myIntegrations.spec.ts @@ -0,0 +1,118 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("MyIntegration CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByText("My Integrations").click(); + + await page.locator("div").filter({ hasText: "Create new integration" }).nth(4).click(); + await page.getByLabel("Integration Name").click(); + await page.getByLabel("Integration Name").fill("name"); + await page.getByLabel("Description").click(); + await page.getByLabel("Description").fill("description"); + await page.getByRole("button", { name: "Create" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created integration!"); + await closeNotification(page); + + await page.getByText("namedescription", { exact: true }).click(); + await page.getByLabel("Integration Name").click(); + await page.getByLabel("Integration Name").fill("newName"); + await page.getByLabel("Description").click(); + await page.getByLabel("Description").fill("newDescription"); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated integration!"); + await closeNotification(page); + + await expect(page.locator("#root")).toContainText("newName"); + await page.getByLabel("Back").click(); + await expect(page.getByRole("main")).toContainText("newNamenewDescription"); + await page.getByText("newNamenewDescription").click(); + await page.getByRole("button", { name: "Remove Integration" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted integration!"); + await closeNotification(page); + await expect(page.getByRole("main")).not.toContainText("newNamenewDescription"); +}); + +test("Webhook CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await page.getByText("My Integrations").click(); + + await page.locator("div").filter({ hasText: "Create new integration" }).nth(4).click(); + await page.getByLabel("Integration Name").click(); + await page.getByLabel("Integration Name").fill("name"); + await page.getByLabel("Description").click(); + await page.getByLabel("Description").fill("description"); + await page.getByRole("button", { name: "Create" }).click(); + await page.getByText("namedescription", { exact: true }).click(); + + await page.getByRole("tab", { name: "Webhook" }).click(); + await page + .locator("div") + .filter({ hasText: /^New Webhook$/ }) + .getByRole("button") + .click(); + await page.getByLabel("Webhook").locator("#name").click(); + await page.getByLabel("Webhook").locator("#name").fill("webhook name"); + await page.getByLabel("Url").click(); + await page.getByLabel("Url").fill("http://test.com"); + await page.getByLabel("Secret").click(); + await page.getByLabel("Secret").fill("secret"); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully created webhook!"); + await closeNotification(page); + await expect(page.getByLabel("Webhook")).toContainText("webhook name"); + await expect(page.getByLabel("Webhook")).toContainText("http://test.com"); + + await page.getByLabel("Webhook").getByLabel("setting").locator("svg").click(); + await page.getByLabel("Webhook").locator("#name").click(); + await page.getByLabel("Webhook").locator("#name").fill("new webhook name"); + await page.getByLabel("Url").click(); + await page.getByLabel("Url").fill("http://new.com"); + await page.getByLabel("Secret").click(); + await page.getByLabel("Secret").fill("new secret"); + await page.getByLabel("Create").check(); + await expect(page.getByLabel("Create")).toBeChecked(); + await page.getByLabel("Upload").check(); + await expect(page.getByLabel("Upload")).toBeChecked(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated webhook!"); + await closeNotification(page); + await expect(page.getByLabel("Webhook")).toContainText("new webhook name"); + await expect(page.getByLabel("Webhook")).toContainText("http://new.com"); + await page.getByLabel("Webhook").getByLabel("setting").locator("svg").click(); + await expect(page.getByLabel("Secret")).toHaveValue("new secret"); + await expect(page.getByLabel("Create")).toBeChecked(); + await expect(page.getByLabel("Upload")).toBeChecked(); + await page.getByLabel("Webhook").locator("svg").click(); + await page.getByRole("switch", { name: "OFF" }).click(); + await expect(page.getByRole("switch")).toContainText("ON"); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated webhook!"); + await closeNotification(page); + await page.getByLabel("delete").locator("svg").click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted webhook!"); + await closeNotification(page); + await expect(page.getByLabel("Webhook")).not.toContainText("new webhook name"); + + await page + .locator("p") + .filter({ hasText: "Create a new New Webhook" }) + .getByRole("button") + .click(); + await page.getByLabel("Webhook").locator("#name").click(); + await page.getByLabel("Webhook").locator("#name").fill("webhook name"); + await page.getByLabel("Url").click(); + await page.getByLabel("Url").fill("http://test.com"); + await page.getByLabel("Secret").click(); + await page.getByLabel("Secret").fill("secret"); + await page.getByRole("button", { name: "Save" }).click(); + await page.getByLabel("delete").locator("svg").click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted webhook!"); + await closeNotification(page); + + await page.getByRole("tab", { name: "General" }).click(); + await page.getByRole("button", { name: "Remove Integration" }).click(); + await page.getByRole("button", { name: "OK" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully deleted integration!"); + await closeNotification(page); +}); diff --git a/web/e2e/settings/settings.spec.ts b/web/e2e/settings/settings.spec.ts new file mode 100644 index 0000000000..c492ea475a --- /dev/null +++ b/web/e2e/settings/settings.spec.ts @@ -0,0 +1,125 @@ +import { closeNotification } from "@reearth-cms/e2e/common/notification"; +import { createWorkspace, deleteWorkspace } from "@reearth-cms/e2e/project/utils/workspace"; +import { expect, test } from "@reearth-cms/e2e/utils"; + +test("Tiles CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createWorkspace(page); + await page.getByText("Settings").click(); + + await page.getByRole("button", { name: "plus Add new Tiles option" }).click(); + await page + .locator("div") + .filter({ hasText: /^Default$/ }) + .nth(4) + .click(); + await page.getByTitle("Labelled").click(); + await page.getByRole("button", { name: "OK" }).click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated"); + await closeNotification(page); + await page + .locator("div:last-child > .ant-card-actions > li:nth-child(2) > span > .anticon") + .click(); + await expect(page.getByText("Labelled", { exact: true })).toBeVisible(); + await page + .locator("div") + .filter({ hasText: /^Labelled$/ }) + .nth(4) + .click(); + await page.getByTitle("URL").locator("div").click(); + await page.getByLabel("Name").click(); + await page.getByLabel("Name").fill("url"); + await page.getByRole("textbox", { name: "URL :", exact: true }).click(); + await page.getByRole("textbox", { name: "URL :", exact: true }).fill("http://url.com"); + await page.getByLabel("Image URL").click(); + await page.getByLabel("Image URL").fill("http://image.com"); + await page.getByRole("button", { name: "OK" }).click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated"); + await closeNotification(page); + await expect(page.getByText("url", { exact: true })).toBeVisible(); + await expect(page.locator("img")).toBeVisible(); + await page + .locator("div:last-child > .ant-card-actions > li:nth-child(2) > span > .anticon") + .click(); + await expect(page.locator("form")).toContainText("URL"); + await expect(page.getByLabel("Name")).toHaveValue("url"); + await expect(page.getByLabel("URL", { exact: true })).toHaveValue("http://url.com"); + await expect(page.getByLabel("Image URL")).toHaveValue("http://image.com"); + await page.getByLabel("Close", { exact: true }).click(); + await page + .locator("div:last-child > .ant-card-actions > li:nth-child(1) > span > .anticon") + .click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated"); + await closeNotification(page); + await expect(page.getByText("url", { exact: true })).not.toBeVisible(); + await deleteWorkspace(page); +}); + +test("Terrain on/off and CRUD has succeeded", async ({ reearth, page }) => { + await reearth.goto("/", { waitUntil: "domcontentloaded" }); + await createWorkspace(page); + await page.getByText("Settings").click(); + + await expect(page.getByRole("switch")).toBeEnabled(); + await page.getByRole("switch").click(); + await expect(page.getByRole("switch")).toHaveAttribute("aria-checked", "true"); + await expect(page.getByRole("button", { name: "plus Add new Terrain option" })).toBeVisible(); + await page.getByRole("button", { name: "plus Add new Terrain option" }).click(); + await page + .locator("div") + .filter({ hasText: /^Cesium World Terrain$/ }) + .nth(4) + .click(); + await page.getByTitle("ArcGIS Terrain").click(); + await page.getByRole("button", { name: "OK" }).click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated workspace!"); + await closeNotification(page); + await page.getByLabel("edit").locator("svg").click(); + await expect(page.locator("form")).toContainText("ArcGIS Terrain"); + await page + .locator("div") + .filter({ hasText: /^ArcGIS Terrain$/ }) + .nth(4) + .click(); + await page.getByTitle("Cesium Ion").click(); + await page.getByLabel("Name").click(); + await page.getByLabel("Name").fill("name"); + await page.getByLabel("Terrain Cesium Ion asset ID").click(); + await page.getByLabel("Terrain Cesium Ion asset ID").fill("id"); + await page.getByLabel("Terrain Cesium Ion access").click(); + await page.getByLabel("Terrain Cesium Ion access").fill("token"); + await page.getByLabel("Terrain URL").click(); + await page.getByLabel("Terrain URL").fill("http://terrain.com"); + await page.getByLabel("Image URL").click(); + await page.getByLabel("Image URL").fill("http://image.com"); + await page.getByRole("button", { name: "OK" }).click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated workspace!"); + await closeNotification(page); + await expect(page.getByText("name", { exact: true })).toBeVisible(); + await page.getByLabel("edit").locator("svg").click(); + await expect(page.locator("form")).toContainText("Cesium Ion"); + await expect(page.getByLabel("Name")).toHaveValue("name"); + await expect(page.getByLabel("Terrain Cesium Ion asset ID")).toHaveValue("id"); + await expect(page.getByLabel("Terrain Cesium Ion access")).toHaveValue("token"); + await expect(page.getByLabel("Terrain URL")).toHaveValue("http://terrain.com"); + await expect(page.getByLabel("Image URL")).toHaveValue("http://image.com"); + await page.getByLabel("Close", { exact: true }).click(); + await page.getByLabel("delete").locator("svg").click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated workspace!"); + await closeNotification(page); + await expect(page.getByText("name", { exact: true })).not.toBeVisible(); + + await page.getByRole("switch").click(); + await page.getByRole("button", { name: "Save" }).click(); + await expect(page.getByRole("alert").last()).toContainText("Successfully updated workspace!"); + await closeNotification(page); + await expect(page.getByRole("switch")).toHaveAttribute("aria-checked", "false"); + await expect(page.getByRole("button", { name: "plus Add new Terrain option" })).not.toBeVisible(); + await deleteWorkspace(page); +}); diff --git a/web/src/components/molecules/Common/Header/index.tsx b/web/src/components/molecules/Common/Header/index.tsx index e1f33c7ab4..8d884ed761 100644 --- a/web/src/components/molecules/Common/Header/index.tsx +++ b/web/src/components/molecules/Common/Header/index.tsx @@ -130,7 +130,11 @@ const HeaderMolecule: React.FC = ({ {t("Re:Earth CMS")} )} - + {currentProject?.name && ( / diff --git a/web/src/components/organisms/Project/Request/RequestDetails/hooks.ts b/web/src/components/organisms/Project/Request/RequestDetails/hooks.ts index e76d87c971..256928c5f5 100644 --- a/web/src/components/organisms/Project/Request/RequestDetails/hooks.ts +++ b/web/src/components/organisms/Project/Request/RequestDetails/hooks.ts @@ -29,6 +29,7 @@ export default () => { const { data: rawRequest, loading: requestLoading } = useGetRequestQuery({ variables: { requestId: requestId ?? "" }, skip: !requestId, + fetchPolicy: "cache-and-network", }); const me: User | undefined = useMemo(() => { diff --git a/web/vite.config.ts b/web/vite.config.ts index c7e81b6c6b..10a18989d4 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -39,7 +39,7 @@ export default defineConfig({ test: { environment: "jsdom", setupFiles: "./src/test/setup.ts", - exclude: [...configDefaults.exclude, "e2e/*"], + exclude: [...configDefaults.exclude, "e2e/**/*"], coverage: { all: true, include: ["src/**/*.ts", "src/**/*.tsx"],