From a70fd4e51748945fa85e5356cb0c45cdaafb6ea0 Mon Sep 17 00:00:00 2001 From: Nathan Panchout Date: Wed, 14 Jan 2026 09:35:29 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=85(frontend)=20add=20utilities=20for=20e?= =?UTF-8?q?2e=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added several utilities to facilitate maintenance and test writing --- .../app-drive/utils-embedded-grid.ts | 32 +++- .../e2e/__tests__/app-drive/utils-explorer.ts | 28 ++- .../e2e/__tests__/app-drive/utils-item.ts | 24 +++ .../e2e/__tests__/app-drive/utils-navigate.ts | 50 ++++++ .../e2e/__tests__/app-drive/utils-tree.ts | 32 ++-- .../__tests__/app-drive/utils/move-utils.ts | 60 +++++++ .../__tests__/app-drive/utils/share-utils.ts | 162 ++++++++++++++++++ .../app-drive/utils/starred-utils.ts | 30 ++++ .../__tests__/app-drive/utils/types-utils.ts | 3 + 9 files changed, 392 insertions(+), 29 deletions(-) create mode 100644 src/frontend/apps/e2e/__tests__/app-drive/utils-navigate.ts create mode 100644 src/frontend/apps/e2e/__tests__/app-drive/utils/move-utils.ts create mode 100644 src/frontend/apps/e2e/__tests__/app-drive/utils/share-utils.ts create mode 100644 src/frontend/apps/e2e/__tests__/app-drive/utils/starred-utils.ts create mode 100644 src/frontend/apps/e2e/__tests__/app-drive/utils/types-utils.ts diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils-embedded-grid.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils-embedded-grid.ts index 4c5fc4ad..d18288cf 100644 --- a/src/frontend/apps/e2e/__tests__/app-drive/utils-embedded-grid.ts +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils-embedded-grid.ts @@ -1,16 +1,36 @@ -import { expect, Page } from "@playwright/test"; +import { expect } from "@playwright/test"; +import { PageOrLocator } from "./utils/types-utils"; -export const getRowItem = async (page: Page, itemName: string) => { - const row = page +const getRowLocator = (page: PageOrLocator, itemName: string) => { + return page .getByRole("row", { name: itemName }) .filter({ hasText: itemName }) .first(); +}; +export const expectRowItem = async (page: PageOrLocator, itemName: string) => { + const row = getRowLocator(page, itemName); + await expect(row).toBeVisible(); +}; + +export const expectRowItemIsNotVisible = async ( + page: PageOrLocator, + itemName: string +) => { + const row = getRowLocator(page, itemName); + await expect(row).not.toBeVisible(); +}; + +export const getRowItem = async (page: PageOrLocator, itemName: string) => { + const row = getRowLocator(page, itemName); await expect(row).toBeVisible(); return row; }; -export const getRowItemActions = async (page: Page, itemName: string) => { +export const getRowItemActions = async ( + page: PageOrLocator, + itemName: string +) => { const row = await getRowItem(page, itemName); const actions = row .getByRole("button", { @@ -23,13 +43,13 @@ export const getRowItemActions = async (page: Page, itemName: string) => { }; export const clickOnRowItemActions = async ( - page: Page, + page: PageOrLocator, itemName: string, actionName: string ) => { const actions = await getRowItemActions(page, itemName); await actions.click({ force: true }); // Because dnd-kit add an aria-disabled attribute on parent and playwright don't interact with it - const action = page.getByRole("menuitem", { name: "Info" }); + const action = page.getByRole("menuitem", { name: actionName }); await expect(action).toBeVisible(); await action.click(); }; diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils-explorer.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils-explorer.ts index 27df787c..227a130f 100644 --- a/src/frontend/apps/e2e/__tests__/app-drive/utils-explorer.ts +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils-explorer.ts @@ -1,8 +1,9 @@ import { expect, Page } from "@playwright/test"; import { expectTreeItemIsSelected } from "./utils-tree"; +import { PageOrLocator } from "./utils/types-utils"; export const expectExplorerBreadcrumbs = async ( - page: Page, + page: PageOrLocator, expected: string[], hidden: string[] = [] ) => { @@ -34,3 +35,28 @@ export const expectCurrentFolder = async ( ); await expectExplorerBreadcrumbs(page, expected); }; + +export const expectDefaultRoute = async ( + page: Page, + breadcrumbLabel: string, + route: string +) => { + const defaultRouteButton = page.getByTestId("default-route-button"); + await expect(defaultRouteButton).toBeVisible(); + await expect(defaultRouteButton).toContainText(breadcrumbLabel); + const currentUrl = page.url(); + expect(currentUrl).toContain(route); +}; + +export const clickOnBreadcrumbButtonAction = async ( + page: Page, + actionName: string +) => { + const breadcrumbs = page.getByTestId("explorer-breadcrumbs"); + await expect(breadcrumbs).toBeVisible(); + const lastBreadcrumbButton = breadcrumbs + .getByTestId("breadcrumb-button") + .last(); + await lastBreadcrumbButton.click(); + await page.getByRole("menuitem", { name: actionName }).click(); +}; diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils-item.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils-item.ts index 35b75dae..0f9fe15d 100644 --- a/src/frontend/apps/e2e/__tests__/app-drive/utils-item.ts +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils-item.ts @@ -1,5 +1,6 @@ import { expect, Page } from "@playwright/test"; import { getItemTree, openTreeNode } from "./utils-tree"; +import { getRowItem } from "./utils-embedded-grid"; export const createWorkspace = async (page: Page, workspaceName: string) => { await page.getByRole("button", { name: "add Create" }).click(); @@ -20,3 +21,26 @@ export const createFolder = async (page: Page, folderName: string) => { await page.getByRole("textbox", { name: "Folder name" }).fill(folderName); await page.getByRole("button", { name: "Create" }).click(); }; + +export const createFolderInCurrentFolder = async ( + page: Page, + folderName: string +) => { + await page.getByTestId("create-folder-button").click(); + await page.getByTestId("create-folder-input").click(); + await page.getByTestId("create-folder-input").fill(folderName); + await page.getByRole("button", { name: "Create" }).click(); + const folderItem = await getRowItem(page, folderName); + await expect(folderItem).toBeVisible(); + return folderItem; +}; + +export const deleteCurrentFolder = async (page: Page) => { + const breadcrumbs = page.getByTestId("explorer-breadcrumbs"); + await expect(breadcrumbs).toBeVisible(); + const lastBreadcrumbButton = breadcrumbs + .getByTestId("breadcrumb-button") + .last(); + await lastBreadcrumbButton.click(); + await page.getByRole("menuitem", { name: "Delete" }).click(); +}; diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils-navigate.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils-navigate.ts new file mode 100644 index 00000000..f3454ee6 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils-navigate.ts @@ -0,0 +1,50 @@ +import { Page, expect } from "@playwright/test"; +import { + expectDefaultRoute, + expectExplorerBreadcrumbs, +} from "./utils-explorer"; +import { getRowItem } from "./utils-embedded-grid"; +import { clickOnItemInTree } from "./utils-tree"; + +export const clickToRecent = async (page: Page) => { + await page.getByRole("link", { name: "Recents" }).click(); + await expectDefaultRoute(page, "Recents", "/explorer/items/recent"); +}; + +export const clickToMyFiles = async (page: Page) => { + await page.getByRole("link", { name: "My files" }).click(); + await expectDefaultRoute(page, "My files", "/explorer/items/my_files"); +}; + +export const clickToSharedWithMe = async (page: Page) => { + await page.getByRole("link", { name: "Shared with me" }).click(); + await expectDefaultRoute( + page, + "Shared with me", + "/explorer/items/shared-with-me" + ); +}; + +export const clickToTrash = async (page: Page) => { + await page.getByRole("link", { name: "Trash" }).click(); + const breadcrumbs = page.getByTestId("trash-page-breadcrumbs"); + await expect(breadcrumbs).toBeVisible(); + await expect(breadcrumbs).toContainText("Trash"); + const currentUrl = page.url(); + expect(currentUrl).toContain("/explorer/trash"); +}; + +export const clickToFavorites = async (page: Page) => { + await clickOnItemInTree(page, "Starred"); + await expectDefaultRoute(page, "Starred", "/explorer/items/favorites"); +}; + +export const navigateToFolder = async ( + page: Page, + folderName: string, + expectedBreadcrumbs: string[] +) => { + const folderItem = await getRowItem(page, folderName); + await folderItem.dblclick(); + await expectExplorerBreadcrumbs(page, expectedBreadcrumbs); +}; diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils-tree.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils-tree.ts index 9ede8df6..0ef1bbf6 100644 --- a/src/frontend/apps/e2e/__tests__/app-drive/utils-tree.ts +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils-tree.ts @@ -1,19 +1,20 @@ import { expect, Page } from "@playwright/test"; -export const getItemTree = async (page: Page, itemTitle: string) => { +export const getItemTree = async ( + page: Page, + itemTitle: string, + isVisible: boolean = true +) => { const item = page.getByRole("treeitem").filter({ hasText: itemTitle }); let itemTree = item.first(); - await expect(itemTree).toBeVisible(); + if (isVisible) { + await expect(itemTree).toBeVisible(); + } else { + await expect(itemTree).not.toBeVisible(); + } return itemTree; }; -export const toggleItemInTree = async (page: Page, itemTitle: string) => { - const item = await getItemTree(page, itemTitle); - const arrow = item.getByText("keyboard_arrow_right"); - await expect(arrow).toBeVisible(); - await arrow.click(); -}; - export const getItemContent = async (page: Page, itemTitle: string) => { const item = await getItemTree(page, itemTitle); const itemContent = item.getByTestId("tree_item_content"); @@ -49,19 +50,6 @@ export const clickOnMoreActionsButtonFromItem = async ( await moreActionsButton.click(); }; -export const addChildrenFromTreeItem = async ( - page: Page, - itemTitle: string, - childrenTitle: string -) => { - await clickOnAddChildrenButtonFromItem(page, itemTitle); - const modal = page.getByRole("dialog"); - await expect(modal).toBeVisible(); - await page.getByText("Folder name").click(); - await page.getByRole("textbox", { name: "Folder name" }).fill(childrenTitle); - await page.getByRole("button", { name: "Create" }).click(); -}; - export const openTreeNode = async (page: Page, itemTitle: string) => { const item = await getItemTree(page, itemTitle); await expect(item).toBeVisible(); diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils/move-utils.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils/move-utils.ts new file mode 100644 index 00000000..f3d03253 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils/move-utils.ts @@ -0,0 +1,60 @@ +import { expect } from "@playwright/test"; + +import { PageOrLocator } from "./types-utils"; +import { getRowItem } from "../utils-embedded-grid"; + +export const getMoveFolderModal = async (page: PageOrLocator) => { + const moveFolderModal = page.getByLabel("Move folder modal"); + await expect(moveFolderModal).toBeVisible(); + return moveFolderModal; +}; + +export const getMoveConfirmationModal = async (page: PageOrLocator) => { + const moveConfirmationModal = page.getByLabel("Move confirmation modal"); + await expect(moveConfirmationModal).toBeVisible(); + return moveConfirmationModal; +}; +export const expectMoveFolderModal = async (page: PageOrLocator) => { + await getMoveFolderModal(page); // getMoveFolderModal already checks if the modal is visible +}; + +export const acceptMoveItem = async (page: PageOrLocator) => { + const moveFolderModal = await getMoveFolderModal(page); + await moveFolderModal.getByRole("button", { name: "Move here" }).click(); + const moveConfirmationModal = await getMoveConfirmationModal(page); + await expect( + moveConfirmationModal.getByText("Transfer rights") + ).toBeVisible(); + await expect( + moveConfirmationModal.getByText("You are about to move the") + ).toBeVisible(); + await moveConfirmationModal + .getByRole("button", { name: "Move anyway" }) + .click(); +}; + +export const searchAndSelectItem = async ( + page: PageOrLocator, + itemName: string +) => { + const moveFolderModal = await getMoveFolderModal(page); + await moveFolderModal.getByPlaceholder("Search for a folder").click(); + await moveFolderModal.getByPlaceholder("Search for a folder").fill(itemName); + await expect(moveFolderModal.getByText("Search results")).toBeVisible(); + const folderToSelect = await getRowItem(moveFolderModal, itemName); + await folderToSelect.dblclick(); +}; + +export const clickAndAcceptMoveToRoot = async (page: PageOrLocator) => { + const moveFolderModal = await getMoveFolderModal(page); + await moveFolderModal.getByRole("button", { name: "Move to root" }).click(); + const moveConfirmationModal = await getMoveConfirmationModal(page); + await expect( + moveConfirmationModal.getByText( + "Moved documents will be accessible via your 'My files' tab. People who had access to the documents only through inherited rights from a parent will no longer be able to access them." + ) + ).toBeVisible(); + await moveConfirmationModal + .getByRole("button", { name: "Move anyway" }) + .click(); +}; diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils/share-utils.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils/share-utils.ts new file mode 100644 index 00000000..94ed0aef --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils/share-utils.ts @@ -0,0 +1,162 @@ +import { Page, expect } from "@playwright/test"; +import { clickOnBreadcrumbButtonAction } from "../utils-explorer"; + +export const getShareModal = async (page: Page) => { + return page.getByLabel("Share modal"); +}; + +export const expectShareModal = async (page: Page) => { + const shareModal = await getShareModal(page); + await expect(shareModal).toBeVisible(); + return shareModal; +}; + +export const openShareModal = async (page: Page) => { + await clickOnBreadcrumbButtonAction(page, "Share"); + const shareModal = await expectShareModal(page); + return shareModal; +}; + +export const closeShareModal = async (page: Page) => { + const shareModal = await getShareModal(page); + await shareModal.getByRole("button", { name: "Close" }).click(); + await expect(shareModal).not.toBeVisible(); +}; + +export const getMemberItem = async (page: Page, userName: string) => { + const shareModal = await getShareModal(page); + const membersList = shareModal.getByTestId("members-list"); + const memberItem = membersList + .getByTestId("share-member-item") + .filter({ hasText: userName }); + return memberItem; +}; + +export const expectUserInMembersList = async ( + page: Page, + userName: string, + role: string +) => { + const memberItem = await getMemberItem(page, userName); + await expect(memberItem).toBeVisible(); + await expect(memberItem).toContainText(role); + return memberItem; +}; + +export const expectAllowedRoles = async ( + page: Page, + userName: string, + allowedRoles: string[], + notAllowedRoles: string[] +) => { + const memberItem = await getMemberItem(page, userName); + await expect(memberItem).toBeVisible(); + const roleDropdown = memberItem.getByTestId("access-role-dropdown-button"); + await expect(roleDropdown).toBeVisible(); + await roleDropdown.click(); + + for (const role of allowedRoles) { + const roleItem = page.getByRole("menuitem", { name: role }); + await expect(roleItem).toBeVisible(); + await expect(roleItem).toBeEnabled(); + } + + for (const role of notAllowedRoles) { + const roleItem = page.getByRole("menuitem", { name: role }); + await expect(roleItem).toBeVisible(); + await expect(roleItem).toBeDisabled(); + } + await closeDropdowns(page); +}; + +export const selectLinkReach = async (page: Page, linkReach: string) => { + const linkReachDropdown = page.getByTestId( + "share-link-reach-dropdown-button" + ); + await linkReachDropdown.click(); + const linkReachItem = page.getByRole("menuitem", { name: linkReach }); + await linkReachItem.click(); +}; + +export const expectLinkReachSelected = async ( + page: Page, + linkReach: string +) => { + const linkReachDropdown = page.getByTestId( + "share-link-reach-dropdown-button" + ); + await expect(linkReachDropdown).toBeVisible(); + await linkReachDropdown.click(); + const linkReachItem = page.getByRole("menuitem", { name: linkReach }); + await expect(linkReachItem).toBeVisible(); + await expect(linkReachItem).toContainText("check"); // we have the right icon + await closeDropdowns(page); +}; + +export const closeDropdowns = async (page: Page) => { + await page.locator("body").click(); +}; + +export const expectAllowedLinkReach = async ( + page: Page, + allowedLinkReach: string[], + notAllowedLinkReach: string[] +) => { + const shareModal = await getShareModal(page); + const linkReachDropdown = shareModal.getByTestId( + "share-link-reach-dropdown-button" + ); + await expect(linkReachDropdown).toBeVisible(); + await linkReachDropdown.click(); + + for (const linkReach of allowedLinkReach) { + const roleItem = page.getByRole("menuitem", { name: linkReach }); + await expect(roleItem).toBeVisible(); + await expect(roleItem).toBeEnabled(); + } + + for (const linkReach of notAllowedLinkReach) { + const roleItem = page.getByRole("menuitem", { name: linkReach }); + await expect(roleItem).toBeVisible(); + await expect(roleItem).toBeDisabled(); + } + await closeDropdowns(page); +}; + +export const getUserSearchResult = async (page: Page, userName: string) => { + const shareModal = await getShareModal(page); + const userSearchList = shareModal.getByTestId("search-users-list"); + await expect(userSearchList).toBeVisible(); + const userSearchItem = userSearchList + .getByTestId("search-user-item") + .filter({ hasText: userName }); + return userSearchItem; +}; + +export const selectRoleUser = async (page: Page, userRole: string) => { + await page.getByTestId("access-role-dropdown-button").click(); + await page.getByRole("menuitem", { name: userRole }).click(); +}; + +export const shareCurrentItemWithWebkitUser = async ( + page: Page, + userRole: string = "Reader" +) => { + await clickOnBreadcrumbButtonAction(page, "Share"); + const shareModal = await expectShareModal(page); + await expect( + shareModal.getByRole("combobox", { name: "Quick search input" }) + ).toBeVisible(); + await shareModal + .getByRole("combobox", { name: "Quick search input" }) + .click(); + await shareModal + .getByRole("combobox", { name: "Quick search input" }) + .fill("webkit"); + const userSearchItem = await getUserSearchResult(page, "user@webkit.test"); + await expect(userSearchItem).toBeVisible(); + await userSearchItem.click(); + await selectRoleUser(page, userRole); + await page.getByRole("button", { name: "Share" }).click(); + await expectUserInMembersList(page, "user@webkit.test", userRole); +}; diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils/starred-utils.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils/starred-utils.ts new file mode 100644 index 00000000..c070a992 --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils/starred-utils.ts @@ -0,0 +1,30 @@ +import { Page, expect } from "@playwright/test"; +import { getItemTree, openTreeNode } from "../utils-tree"; +import { + clickOnRowItemActions, + expectRowItem, + expectRowItemIsNotVisible, +} from "../utils-embedded-grid"; +import { clickToFavorites } from "../utils-navigate"; + +export const verifyItemIsStarred = async (page: Page, itemName: string) => { + await openTreeNode(page, "Starred"); + await getItemTree(page, itemName); // get and verify the item is in the tree + await clickToFavorites(page); + await expectRowItem(page, itemName); +}; + +export const verifyItemIsNotStarred = async (page: Page, itemName: string) => { + await openTreeNode(page, "Starred"); + await getItemTree(page, itemName, false); + await clickToFavorites(page); + await expectRowItemIsNotVisible(page, itemName); +}; + +export const starItem = async (page: Page, itemName: string) => { + await clickOnRowItemActions(page, itemName, "Star"); +}; + +export const unstarItem = async (page: Page, itemName: string) => { + await clickOnRowItemActions(page, itemName, "Unstar"); +}; diff --git a/src/frontend/apps/e2e/__tests__/app-drive/utils/types-utils.ts b/src/frontend/apps/e2e/__tests__/app-drive/utils/types-utils.ts new file mode 100644 index 00000000..c89b5e7b --- /dev/null +++ b/src/frontend/apps/e2e/__tests__/app-drive/utils/types-utils.ts @@ -0,0 +1,3 @@ +import { Locator, Page } from "@playwright/test"; + +export type PageOrLocator = Page | Locator;