UNPKG

passbolt-styleguide

Version:

Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.

689 lines (559 loc) 25.4 kB
/** * Passbolt ~ Open source password manager for teams * Copyright (c) 2020 Passbolt SA (https://www.passbolt.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) 2020 Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 2.11.0 */ /** * Unit tests on FilterUserByShortcut in regard of specifications */ import { defaultProps, propsWithFirstUserAttentionRequired, propsWithNoUsersAccountRecoveryRequestFilter, propsWithNoUsersMissingMetadataKeyFilter, propsWithNoUsersWithAllStatusesFilter, propsWithNoUsersWithTextSearch, propsWithNullUsers, propsWithGroups, createGroup, createUser, } from "./DisplayUsers.test.data"; import DisplayUsersPage from "./DisplayUsers.test.page"; import { denyRbacContext } from "../../../../shared/context/Rbac/RbacContext.test.data"; import { defaultUserAppContext } from "../../../contexts/ExtAppContext.test.data"; import DisplayUsersContextualMenu from "../DisplayUsersContextualMenu/DisplayUsersContextualMenu"; import { act } from "react"; beforeEach(() => { jest.resetModules(); jest.clearAllMocks(); }); describe("Display Users", () => { let page; // The page to test against const props = defaultProps(); // The props to pass describe("As LU, I should see the appropriate list of users", () => { it("As LU, I should see initially an empty content when there are no users", async () => { page = new DisplayUsersPage(propsWithNullUsers()); expect(page.hasEmptyContent).toBeTruthy(); }); it("As LU, I should see an empty content when there are no users matching the text search", async () => { page = new DisplayUsersPage(propsWithNoUsersWithTextSearch()); expect(page.hasEmptyContentWithTextSearch).toBeTruthy(); }); it("As LU, I should see an empty content when there are no users filtered by all statuses filter", async () => { page = new DisplayUsersPage(propsWithNoUsersWithAllStatusesFilter()); expect(page.hasEmptyContentWithFilterApplied).toBeTruthy(); }); it("As LU, I should see an empty content when there are no users filtered by attention required filter", async () => { page = new DisplayUsersPage(propsWithNoUsersAccountRecoveryRequestFilter()); expect(page.hasEmptyContentWithFilterApplied).toBeTruthy(); }); it("As LU, I should see an empty content when there are no users filtered by missing metadata keys filter", async () => { page = new DisplayUsersPage(propsWithNoUsersMissingMetadataKeyFilter()); expect(page.hasEmptyContentWithFilterApplied).toBeTruthy(); }); it("AS LU, I should see the appropriate filtered list of users", async () => { await act(async () => { page = new DisplayUsersPage(props); }); expect(page.usersCount).toBe(2); expect((await page.user(1)).username).toBe("carol@passbolt.com"); expect((await page.user(2)).username).toBe("dame@passbolt.com"); }); it("AS LU, I should see the appropriate filtered list of users with a user attention required", async () => { expect.assertions(3); await act(async () => { page = new DisplayUsersPage(propsWithFirstUserAttentionRequired()); }); expect(page.usersCount).toBe(2); expect((await page.user(1)).attentionRequired).toBeTruthy(); expect((await page.user(2)).attentionRequired).toBeTruthy(); }); }); describe("As LU, I should select users", () => { beforeEach(() => { page = new DisplayUsersPage(props); }); it("As LU, I should select one user", async () => { await (await page.user(1)).select(); expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalledWith( props.userWorkspaceContext.filteredUsers[0], ); }); it("As LU, I should unselect one user", async () => { await (await page.user(1)).select(); expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalledWith( props.userWorkspaceContext.filteredUsers[0], ); await (await page.user(1)).select(); expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalledWith( props.userWorkspaceContext.filteredUsers[0], ); }); }); describe("As LU, I should sort the user by property column", () => { it("As LU, I should sort the users by fullname", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortByFullname(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("profile"); }); it("As LU, I should sort the users by username", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortByUsername(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("username"); }); it("As LU, I should sort the users by role", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortByRole(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("role_id"); }); it("As LU, I should sort the users by suspended", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortBySuspended(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("disabled"); }); it("As LU, I should sort the users by modified", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortByModified(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("modified"); }); it("As LU, I should sort the users by the last login date", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortByLastLoggedIn(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("last_logged_in"); }); it("As LU, I should sort the users by mfa enabled", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortByMFAEnabled(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("is_mfa_enabled"); }); it("As LU, I should sort the users by account recovery status", async () => { await act(async () => { page = new DisplayUsersPage(props); }); await page.sortByAccountRecoveryStatus(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("account_recovery_user_setting.status"); }); }); describe("As LU, I should not see suspended, mfa and account recovery column for users", () => { it("As LU, I should see 6 column", async () => { const props = defaultProps({ rbacContext: denyRbacContext(), context: defaultUserAppContext() }); await act(async () => { page = new DisplayUsersPage(props); }); expect(page.columnCount).toStrictEqual(6); // The first column is the checkbox expect(page.column(2).name).toStrictEqual("Name"); expect(page.column(3).name).toStrictEqual("Username"); expect(page.column(4).name).toStrictEqual("Role"); expect(page.column(5).name).toStrictEqual("Modified"); expect(page.column(6).name).toStrictEqual("Last logged in"); }); }); describe("As LU having account recovery view allowed, I should not see suspended, mfa column for users", () => { beforeEach(() => {}); it("As LU, I should see 6 column", async () => { const props = defaultProps({ context: defaultUserAppContext() }); await act(async () => { page = new DisplayUsersPage(props); }); expect(page.columnCount).toStrictEqual(7); // The first column is the checkbox expect(page.column(2).name).toStrictEqual("Name"); expect(page.column(3).name).toStrictEqual("Username"); expect(page.column(4).name).toStrictEqual("Role"); expect(page.column(5).name).toStrictEqual("Modified"); expect(page.column(6).name).toStrictEqual("Last logged in"); expect(page.column(7).name).toStrictEqual("Account recovery"); }); }); describe("As LU, I should handle drag and drop operations", () => { beforeEach(() => { page = new DisplayUsersPage(props); }); it("As LU, I should trigger drag context when dragging a user", async () => { await (await page.user(1)).dragStart(); // Verify that drag context is called when drag starts expect(props.dragContext.onDragStart).toHaveBeenCalled(); }); it("As LU, I should trigger drag end when drag operation completes", async () => { await (await page.user(1)).dragEnd(); // Verify that drag context is called when drag ends expect(props.dragContext.onDragEnd).toHaveBeenCalled(); }); it("As LU, I should see user get selected when starting drag on unselected user", async () => { // Ensure user is not selected initially props.userWorkspaceContext.selectedUsers = []; await (await page.user(1)).dragStart(); // When dragging an unselected user, the drag context should be called expect(props.dragContext.onDragStart).toHaveBeenCalled(); }); it("As LU, I should be able to drag multiple selected users", async () => { // Pre-select both users props.userWorkspaceContext.selectedUsers = props.userWorkspaceContext.filteredUsers; await (await page.user(1)).dragStart(); // Drag should include all selected users expect(props.dragContext.onDragStart).toHaveBeenCalled(); }); }); describe("As LU, I should handle right-click context menu", () => { beforeEach(() => { page = new DisplayUsersPage(props); }); it("As LU, I should display context menu when right-clicking a user", async () => { await (await page.user(1)).rightClick(); // Verify context menu is shown expect(props.contextualMenuContext.show).toHaveBeenCalledWith( DisplayUsersContextualMenu, expect.objectContaining({ user: expect.any(Object), }), ); }); it("As LU, I should select user when right-clicking unselected user", async () => { // Ensure user is not selected props.userWorkspaceContext.selectedUsers = []; await (await page.user(1)).rightClick(); // User should be selected expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalled(); expect(props.contextualMenuContext.show).toHaveBeenCalled(); }); it("As LU, I should show context menu without changing selection when right-clicking selected user", async () => { const firstUser = props.userWorkspaceContext.filteredUsers[0]; // Pre-select the user props.userWorkspaceContext.selectedUsers = [firstUser]; // Clear the mock to track only this interaction props.userWorkspaceContext.onUserSelected.single.mockClear(); await (await page.user(1)).rightClick(); // Context menu should show expect(props.contextualMenuContext.show).toHaveBeenCalled(); // Note: Selection behavior depends on selectUserIfNotAlreadySelected implementation }); it("As LU, I should see context menu with position coordinates", async () => { await (await page.user(1)).rightClick(); // Context menu should be called with coordinates const callArgs = props.contextualMenuContext.show.mock.calls[0]; expect(callArgs[0]).toBe(DisplayUsersContextualMenu); expect(callArgs[1]).toHaveProperty("user"); // Coordinates are provided by the event (pageX, pageY) expect(callArgs[1]).toHaveProperty("left"); expect(callArgs[1]).toHaveProperty("top"); }); }); describe("As LU, I should handle checkbox selection", () => { beforeEach(() => { page = new DisplayUsersPage(props); }); it("As LU, I should select user when clicking checkbox", async () => { await (await page.user(1)).clickCheckbox(); // Checkbox click should trigger user selection expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalled(); }); it("As LU, I should be able to toggle user selection via checkbox", async () => { // First click selects await (await page.user(1)).clickCheckbox(); expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalledTimes(1); // Second click toggles (unselects) await (await page.user(1)).clickCheckbox(); expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalledTimes(2); }); }); describe("As LU, I should verify sorting state", () => { it("As LU, I should see active sort indicator on sorted column", async () => { // Set a specific sort props.userWorkspaceContext.sorter = { propertyName: "username", asc: true, }; await act(async () => { page = new DisplayUsersPage(props); }); // The component should render with sorted state expect(page.usersCount).toBe(2); }); it("As LU, I should see ascending sort indicator when sort is ascending", async () => { props.userWorkspaceContext.sorter = { propertyName: "username", asc: true, }; await act(async () => { page = new DisplayUsersPage(props); }); // Component should show ascending indicator expect(page.usersCount).toBe(2); }); it("As LU, I should see descending sort indicator when sort is descending", async () => { props.userWorkspaceContext.sorter = { propertyName: "username", asc: false, }; await act(async () => { page = new DisplayUsersPage(props); }); // Component should show descending indicator expect(page.usersCount).toBe(2); }); it("As LU, I should be able to change sort direction", async () => { await act(async () => { page = new DisplayUsersPage(props); }); // Wait for the table to be visible // await screen.findByRole("table"); // Click the same column twice should reverse direction await page.sortByUsername(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledWith("username"); await page.sortByUsername(); expect(props.userWorkspaceContext.onSorterChanged).toHaveBeenCalledTimes(2); }); }); describe("As LU, I should handle conditional user selection correctly", () => { beforeEach(() => { page = new DisplayUsersPage(props); }); it("As LU, I should auto-select user when performing action on unselected user", async () => { // Ensure user is not selected props.userWorkspaceContext.selectedUsers = []; // Right-click should auto-select await (await page.user(1)).rightClick(); expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalled(); }); it("As LU, I should maintain selection when performing action on already selected user", async () => { const firstUser = props.userWorkspaceContext.filteredUsers[0]; props.userWorkspaceContext.selectedUsers = [firstUser]; // Clear mock to track only new calls props.userWorkspaceContext.onUserSelected.single.mockClear(); // Action on already selected user await (await page.user(1)).rightClick(); // Context menu shows expect(props.contextualMenuContext.show).toHaveBeenCalled(); }); it("As LU, I should be able to change selection by clicking different user", async () => { const firstUser = props.userWorkspaceContext.filteredUsers[0]; props.userWorkspaceContext.selectedUsers = [firstUser]; // Click second user await (await page.user(2)).select(); // Should select the second user expect(props.userWorkspaceContext.onUserSelected.single).toHaveBeenCalledWith( props.userWorkspaceContext.filteredUsers[1], ); }); }); describe("As LU, I should get correct disabled group IDs for drag operations", () => { const loggedInUserId = "logged-in-user-id"; const selectedUserId = "selected-user-id"; const otherUserId = "other-user-id"; it("As LU, I should get empty disabled group IDs when groups is null", async () => { const testProps = propsWithGroups({ groups: null, loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toEqual([]); }); it("As LU, I should get empty disabled group IDs when groups is undefined", async () => { const testProps = propsWithGroups({ groups: undefined, loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toEqual([]); }); it("As LU, I should get empty disabled group IDs when loggedInUser is null", async () => { const testProps = propsWithGroups({ groups: [createGroup({ id: "group-1", members: [] })], loggedInUserId: null, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toEqual([]); }); it("As LU, I should get disabled group ID when I am not admin of the group", async () => { const groupWhereNotAdmin = createGroup({ id: "group-not-admin", members: [ { userId: loggedInUserId, isAdmin: false }, { userId: otherUserId, isAdmin: true }, ], }); const testProps = propsWithGroups({ groups: [groupWhereNotAdmin], loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toContain("group-not-admin"); }); it("As LU, I should get disabled group ID when I am not a member of the group", async () => { const groupWhereNotMember = createGroup({ id: "group-not-member", members: [{ userId: otherUserId, isAdmin: true }], }); const testProps = propsWithGroups({ groups: [groupWhereNotMember], loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toContain("group-not-member"); }); it("As LU, I should get disabled group ID when I am admin but selected user is already in group", async () => { const groupWithSelectedUserAlreadyMember = createGroup({ id: "group-user-already-member", members: [ { userId: loggedInUserId, isAdmin: true }, { userId: selectedUserId, isAdmin: false }, ], }); const testProps = propsWithGroups({ groups: [groupWithSelectedUserAlreadyMember], loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toContain("group-user-already-member"); }); it("As LU, I should not get disabled group ID when I am admin and selected user is not in group", async () => { const groupWhereCanAdd = createGroup({ id: "group-can-add", members: [ { userId: loggedInUserId, isAdmin: true }, { userId: otherUserId, isAdmin: false }, ], }); const testProps = propsWithGroups({ groups: [groupWhereCanAdd], loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).not.toContain("group-can-add"); }); it("As LU, I should get correct disabled group IDs with multiple groups of mixed admin status", async () => { const groupWhereAdmin = createGroup({ id: "group-admin", members: [{ userId: loggedInUserId, isAdmin: true }], }); const groupWhereNotAdmin = createGroup({ id: "group-not-admin", members: [{ userId: loggedInUserId, isAdmin: false }], }); const groupWhereNotMember = createGroup({ id: "group-not-member", members: [{ userId: otherUserId, isAdmin: true }], }); const testProps = propsWithGroups({ groups: [groupWhereAdmin, groupWhereNotAdmin, groupWhereNotMember], loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).not.toContain("group-admin"); expect(draggedItems.disabledGroupIds).toContain("group-not-admin"); expect(draggedItems.disabledGroupIds).toContain("group-not-member"); }); it("As LU, I should get disabled group ID if any selected user is already a member when dragging multiple users", async () => { const selectedUser1 = "selected-user-1"; const selectedUser2 = "selected-user-2"; const groupWithOneSelectedUser = createGroup({ id: "group-with-one-selected", members: [ { userId: loggedInUserId, isAdmin: true }, { userId: selectedUser1, isAdmin: false }, ], }); const groupWithNoSelectedUsers = createGroup({ id: "group-with-none-selected", members: [ { userId: loggedInUserId, isAdmin: true }, { userId: otherUserId, isAdmin: false }, ], }); const testProps = propsWithGroups({ groups: [groupWithOneSelectedUser, groupWithNoSelectedUsers], loggedInUserId, selectedUsers: [createUser(selectedUser1), createUser(selectedUser2)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toContain("group-with-one-selected"); expect(draggedItems.disabledGroupIds).not.toContain("group-with-none-selected"); }); it("As LU, I should get empty disabled group IDs when groups array is empty", async () => { const testProps = propsWithGroups({ groups: [], loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toEqual([]); }); it("As LU, I should get disabled group ID for group with empty members array", async () => { const emptyGroup = createGroup({ id: "empty-group", members: [], }); const testProps = propsWithGroups({ groups: [emptyGroup], loggedInUserId, selectedUsers: [createUser(selectedUserId)], }); page = new DisplayUsersPage(testProps); await (await page.user(1)).dragStart(); expect(testProps.dragContext.onDragStart).toHaveBeenCalled(); const draggedItems = testProps.dragContext.onDragStart.mock.calls[0][2]; expect(draggedItems.disabledGroupIds).toContain("empty-group"); }); }); });