From 90b21cad339587671a7c2f1d1f2b11c2444d225c Mon Sep 17 00:00:00 2001 From: M-ZubairAhmed Date: Mon, 12 Feb 2024 08:28:47 +0000 Subject: [PATCH] End to End test for Admin Reporting for Users, new User Management screen (#26077) https://mattermost.atlassian.net/browse/MM-56672 https://mattermost.atlassian.net/browse/MM-56666 https://mattermost.atlassian.net/browse/MM-56673 --- .../channels/generic_confirm_modal.ts | 46 ++++ .../playwright/support/ui/components/index.ts | 16 ++ .../ui/components/system_console/navbar.ts | 18 ++ .../system_users/column_toggle_menu.ts | 52 +++++ .../sections/system_users/filter_menu.ts | 46 ++++ .../sections/system_users/filter_popover.ts | 70 ++++++ .../sections/system_users/system_users.ts | 132 +++++++++++ .../ui/components/system_console/sidebar.ts | 41 ++++ .../playwright/support/ui/pages/index.ts | 2 + .../support/ui/pages/system_console.ts | 63 ++++++ e2e-tests/playwright/support/util.ts | 1 + .../system_users/actions.spec.ts | 214 ++++++++++++++++++ .../system_users/column_sort.spec.ts | 97 ++++++++ .../system_users/column_toggler.spec.ts | 134 +++++++++++ .../system_users/export_data.spec.ts | 75 ++++++ .../system_users/filter_popover.spec.ts | 166 ++++++++++++++ .../system_users/search.spec.ts | 194 ++++++++++++++++ .../export_user_data_modal.tsx | 1 + .../channels/src/components/confirm_modal.tsx | 4 +- 19 files changed, 1371 insertions(+), 1 deletion(-) create mode 100644 e2e-tests/playwright/support/ui/components/channels/generic_confirm_modal.ts create mode 100644 e2e-tests/playwright/support/ui/components/system_console/navbar.ts create mode 100644 e2e-tests/playwright/support/ui/components/system_console/sections/system_users/column_toggle_menu.ts create mode 100644 e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_menu.ts create mode 100644 e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_popover.ts create mode 100644 e2e-tests/playwright/support/ui/components/system_console/sections/system_users/system_users.ts create mode 100644 e2e-tests/playwright/support/ui/components/system_console/sidebar.ts create mode 100644 e2e-tests/playwright/support/ui/pages/system_console.ts create mode 100644 e2e-tests/playwright/tests/functional/system_console/system_users/actions.spec.ts create mode 100644 e2e-tests/playwright/tests/functional/system_console/system_users/column_sort.spec.ts create mode 100644 e2e-tests/playwright/tests/functional/system_console/system_users/column_toggler.spec.ts create mode 100644 e2e-tests/playwright/tests/functional/system_console/system_users/export_data.spec.ts create mode 100644 e2e-tests/playwright/tests/functional/system_console/system_users/filter_popover.spec.ts create mode 100644 e2e-tests/playwright/tests/functional/system_console/system_users/search.spec.ts diff --git a/e2e-tests/playwright/support/ui/components/channels/generic_confirm_modal.ts b/e2e-tests/playwright/support/ui/components/channels/generic_confirm_modal.ts new file mode 100644 index 0000000000..291d8419df --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/channels/generic_confirm_modal.ts @@ -0,0 +1,46 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator, Page} from '@playwright/test'; + +/** + * This is the generic confirm modal that is used in the app. + * It has optional cancel button, optional checkbox and confirm button along with title and message body. + * It can present in different parts of the app such as channel, system console, etc and hence its constructor + * should be able to accept the page object of the app and an optional id to uniquely identify the modal. + */ +export default class GenericConfirmModal { + readonly container: Locator; + + readonly confirmButton: Locator; + readonly cancelButton: Locator; + + constructor(page: Page, id?: string) { + const modalId = `#confirmModal ${id}`.trim(); + this.container = page.locator(modalId); + this.confirmButton = page.locator('#confirmModalButton'); + this.cancelButton = page.locator('#cancelModalButton'); + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + } + + async confirm() { + await this.confirmButton.waitFor(); + await this.confirmButton.click(); + + // Wait for the modal to disappear + await expect(this.container).not.toBeVisible(); + } + + async cancel() { + await this.cancelButton.waitFor(); + await this.cancelButton.click(); + + // Wait for the modal to disappear + await expect(this.container).not.toBeVisible(); + } +} + +export {GenericConfirmModal}; diff --git a/e2e-tests/playwright/support/ui/components/index.ts b/e2e-tests/playwright/support/ui/components/index.ts index a129f32c1c..d2492c5e90 100644 --- a/e2e-tests/playwright/support/ui/components/index.ts +++ b/e2e-tests/playwright/support/ui/components/index.ts @@ -19,6 +19,15 @@ import {PostReminderMenu} from './channels/post_reminder_menu'; import {PostMenu} from './channels/post_menu'; import {ThreadFooter} from './channels/thread_footer'; import {EmojiGifPicker} from './channels/emoji_gif_picker'; +import {GenericConfirmModal} from './channels/generic_confirm_modal'; + +import {SystemConsoleSidebar} from './system_console/sidebar'; +import {SystemConsoleNavbar} from './system_console/navbar'; + +import {SystemUsers} from './system_console/sections/system_users/system_users'; +import {SystemUsersFilterPopover} from './system_console/sections/system_users/filter_popover'; +import {SystemUsersFilterMenu} from './system_console/sections/system_users/filter_menu'; +import {SystemUsersColumnToggleMenu} from './system_console/sections/system_users/column_toggle_menu'; const components = { GlobalHeader, @@ -39,6 +48,13 @@ const components = { MainHeader, PostReminderMenu, EmojiGifPicker, + GenericConfirmModal, + SystemConsoleSidebar, + SystemConsoleNavbar, + SystemUsers, + SystemUsersFilterPopover, + SystemUsersFilterMenu, + SystemUsersColumnToggleMenu, }; export { diff --git a/e2e-tests/playwright/support/ui/components/system_console/navbar.ts b/e2e-tests/playwright/support/ui/components/system_console/navbar.ts new file mode 100644 index 0000000000..2c847d7889 --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/system_console/navbar.ts @@ -0,0 +1,18 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +export default class SystemConsoleNavbar { + readonly container: Locator; + + constructor(container: Locator) { + this.container = container; + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + } +} + +export {SystemConsoleNavbar}; diff --git a/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/column_toggle_menu.ts b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/column_toggle_menu.ts new file mode 100644 index 0000000000..469cf1d29b --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/column_toggle_menu.ts @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +class SystemUsersColumnToggleMenu { + readonly container: Locator; + + constructor(container: Locator) { + this.container = container; + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + } + + /** + * Return the locator for the menu item with the given name. + */ + async getMenuItem(menuItem: string) { + const menuItemLocator = this.container.getByRole('menuitemcheckbox').filter({hasText: menuItem}); + await menuItemLocator.waitFor(); + + return menuItemLocator; + } + + /** + * Returns the list of locators for all the menu items. + */ + async getAllMenuItems() { + const menuItemLocators = this.container.getByRole('menuitemcheckbox'); + return menuItemLocators; + } + + /** + * Pass in the item name to check/uncheck the menu item. + */ + async clickMenuItem(menuItem: string) { + const menuItemLocator = await this.getMenuItem(menuItem); + await menuItemLocator.click(); + } + + /** + * Close column toggle menu. + */ + async close() { + await this.container.press('Escape'); + await expect(this.container).not.toBeVisible(); + } +} + +export {SystemUsersColumnToggleMenu}; diff --git a/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_menu.ts b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_menu.ts new file mode 100644 index 0000000000..2e4a7b1411 --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_menu.ts @@ -0,0 +1,46 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +/** + * The dropdown menu which appears for both Role and Status filter. + */ +class SystemUsersFilterMenu { + readonly container: Locator; + + constructor(container: Locator) { + this.container = container; + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + } + + /** + * Return the locator for the menu item with the given name. + */ + async getMenuItem(menuItem: string) { + const menuItemLocator = this.container.getByText(menuItem); + await menuItemLocator.waitFor(); + + return menuItemLocator; + } + + /** + * Clicks on the menu item with the given name. + */ + async clickMenuItem(menuItem: string) { + const menuItemLocator = await this.getMenuItem(menuItem); + await menuItemLocator.click(); + } + + /** + * Close the menu. + */ + async close() { + await this.container.press('Escape'); + } +} + +export {SystemUsersFilterMenu}; diff --git a/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_popover.ts b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_popover.ts new file mode 100644 index 0000000000..80b1529b15 --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/filter_popover.ts @@ -0,0 +1,70 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +class SystemUsersFilterPopover { + readonly container: Locator; + + readonly teamMenuInput: Locator; + readonly roleMenuButton: Locator; + readonly statusMenuButton: Locator; + + readonly applyButton: Locator; + + constructor(container: Locator) { + this.container = container; + + this.teamMenuInput = this.container.locator('#asyncTeamSelectInput'); + this.roleMenuButton = this.container.locator('#DropdownInput_filterRole'); + this.statusMenuButton = this.container.locator('#DropdownInput_filterStatus'); + + this.applyButton = this.container.getByText('Apply'); + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + await expect(this.applyButton).toBeVisible(); + } + + /** + * Save the filter settings. + */ + async save() { + await this.applyButton.click(); + } + + /** + * Allows to type in the team filter for searching. + */ + async searchInTeamMenu(teamDisplayName: string) { + expect(this.teamMenuInput).toBeVisible(); + await this.teamMenuInput.fill(teamDisplayName); + } + + /** + * Opens the role filter menu. + */ + async openRoleMenu() { + expect(this.roleMenuButton).toBeVisible(); + await this.roleMenuButton.click(); + } + + /** + * Opens the status filter menu. + */ + async openStatusMenu() { + expect(this.statusMenuButton).toBeVisible(); + await this.statusMenuButton.click(); + } + + /** + * Closes the filter popover. + */ + async close() { + await this.container.press('Escape'); + await expect(this.container).not.toBeVisible(); + } +} + +export {SystemUsersFilterPopover}; diff --git a/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/system_users.ts b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/system_users.ts new file mode 100644 index 0000000000..0d2946ed65 --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/system_console/sections/system_users/system_users.ts @@ -0,0 +1,132 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +/** + * System Console -> User Management -> Users + */ +export default class SystemUsers { + readonly container: Locator; + + readonly searchInput: Locator; + readonly columnToggleMenuButton: Locator; + readonly dateRangeSelectorMenuButton: Locator; + readonly exportButton: Locator; + readonly filterPopoverButton: Locator; + readonly actionMenuButtons: Locator[]; + + readonly loadingSpinner: Locator; + + constructor(container: Locator) { + this.container = container; + + this.searchInput = this.container.getByLabel('Search users'); + this.columnToggleMenuButton = this.container.locator('#systemUsersColumnTogglerMenuButton'); + this.dateRangeSelectorMenuButton = this.container.locator('#systemUsersDateRangeSelectorMenuButton'); + this.exportButton = this.container.getByText('Export'); + this.filterPopoverButton = this.container.getByText(/Filters \(\d+\)/); + this.actionMenuButtons = Array.from(Array(10).keys()).map((index) => + this.container.locator(`#actionMenuButton-systemUsersTable-${index}`), + ); + + this.loadingSpinner = this.container.getByText('Loading'); + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + } + + async isLoadingComplete() { + await expect(this.loadingSpinner).toHaveCount(0); + } + + /** + * Returns the locator for the header of the given column. + */ + async getColumnHeader(columnName: string) { + const columnHeader = this.container.getByRole('columnheader').filter({hasText: columnName}); + return columnHeader; + } + + /** + * Checks if given column exists in the table. By searching for the column header. + */ + async doesColumnExist(columnName: string) { + const columnHeader = await this.getColumnHeader(columnName); + return await columnHeader.isVisible(); + } + + /** + * Clicks on the column header of the given column for sorting. + */ + async clickSortOnColumn(columnName: string) { + const columnHeader = await this.getColumnHeader(columnName); + await columnHeader.waitFor(); + await columnHeader.click(); + } + + /** + * Return the locator for the given row number. If '0' is passed, it will return the header row. + */ + async getNthRow(rowNumber: number) { + const row = this.container.getByRole('row').nth(rowNumber); + await row.waitFor(); + + return row; + } + + /** + * Opens the Filter popover + */ + async openFilterPopover() { + expect(this.filterPopoverButton).toBeVisible(); + await this.filterPopoverButton.click(); + } + + /** + * Open the column toggle menu + */ + async openColumnToggleMenu() { + expect(this.columnToggleMenuButton).toBeVisible(); + await this.columnToggleMenuButton.click(); + } + + /** + * Open the date range selector menu + */ + async openDateRangeSelectorMenu() { + expect(this.dateRangeSelectorMenuButton).toBeVisible(); + await this.dateRangeSelectorMenuButton.click(); + } + + /** + * Enter the given search term in the search input + */ + async enterSearchText(searchText: string) { + expect(this.searchInput).toBeVisible(); + await this.searchInput.fill(`${searchText}`); + + await this.isLoadingComplete(); + } + + /** + * Searches and verifies that the row with given text is found + */ + async verifyRowWithTextIsFound(text: string) { + const foundUser = this.container.getByText(text); + await foundUser.waitFor(); + + await expect(foundUser).toBeVisible(); + } + + /** + * Searches and verifies that the row with given text is not found + */ + async verifyRowWithTextIsNotFound(text: string) { + const foundUser = this.container.getByText(text); + await expect(foundUser).not.toBeVisible(); + } +} + +export {SystemUsers}; diff --git a/e2e-tests/playwright/support/ui/components/system_console/sidebar.ts b/e2e-tests/playwright/support/ui/components/system_console/sidebar.ts new file mode 100644 index 0000000000..0591325077 --- /dev/null +++ b/e2e-tests/playwright/support/ui/components/system_console/sidebar.ts @@ -0,0 +1,41 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect, Locator} from '@playwright/test'; + +export default class SystemConsoleSidebar { + readonly container: Locator; + + readonly searchInput: Locator; + + constructor(container: Locator) { + this.container = container; + + this.searchInput = container.getByPlaceholder('Find settings'); + } + + async toBeVisible() { + await expect(this.container).toBeVisible(); + await expect(this.searchInput).toBeVisible(); + } + + /** + * Clicks on the sidebar section link with the given name. Pass the exact name of the section. + * @param sectionName + */ + async goToItem(sectionName: string) { + const section = this.container.getByText(sectionName, {exact: true}); + await section.waitFor(); + await section.click(); + } + + /** + * Searches for the given item in the sidebar search input. + * @param itemName + */ + async searchForItem(itemName: string) { + await this.searchInput.fill(itemName); + } +} + +export {SystemConsoleSidebar}; diff --git a/e2e-tests/playwright/support/ui/pages/index.ts b/e2e-tests/playwright/support/ui/pages/index.ts index 215b7666b4..81756287bf 100644 --- a/e2e-tests/playwright/support/ui/pages/index.ts +++ b/e2e-tests/playwright/support/ui/pages/index.ts @@ -6,6 +6,7 @@ import {LandingLoginPage} from './landing_login'; import {LoginPage} from './login'; import {ResetPasswordPage} from './reset_password'; import {SignupPage} from './signup'; +import {SystemConsolePage} from './system_console'; const pages = { ChannelsPage, @@ -13,6 +14,7 @@ const pages = { LoginPage, ResetPasswordPage, SignupPage, + SystemConsolePage, }; export {pages, ChannelsPage, LandingLoginPage, LoginPage, SignupPage}; diff --git a/e2e-tests/playwright/support/ui/pages/system_console.ts b/e2e-tests/playwright/support/ui/pages/system_console.ts new file mode 100644 index 0000000000..3d73db8e4d --- /dev/null +++ b/e2e-tests/playwright/support/ui/pages/system_console.ts @@ -0,0 +1,63 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Page} from '@playwright/test'; +import {components} from '../components'; + +class SystemConsolePage { + readonly page: Page; + + readonly sidebar; + readonly navbar; + + /** + * System Console -> User Management -> Users + */ + readonly systemUsers; + readonly systemUsersFilterPopover; + readonly systemUsersRoleMenu; + readonly systemUsersStatusMenu; + readonly systemUsersDateRangeMenu; + readonly systemUsersColumnToggleMenu; + readonly systemUsersActionMenus; + + constructor(page: Page) { + this.page = page; + + // Areas of the page + this.navbar = new components.SystemConsoleNavbar(page.locator('.backstage-navbar')); + this.sidebar = new components.SystemConsoleSidebar(page.locator('.admin-sidebar')); + + // Sections and sub-sections + this.systemUsers = new components.SystemUsers(page.getByTestId('systemUsersSection')); + + // Menus & Popovers + this.systemUsersFilterPopover = new components.SystemUsersFilterPopover( + page.locator('#systemUsersFilterPopover'), + ); + this.systemUsersRoleMenu = new components.SystemUsersFilterMenu(page.locator('#DropdownInput_filterRole')); + this.systemUsersStatusMenu = new components.SystemUsersFilterMenu(page.locator('#DropdownInput_filterStatus')); + this.systemUsersColumnToggleMenu = new components.SystemUsersColumnToggleMenu( + page.locator('#systemUsersColumnTogglerMenu'), + ); + this.systemUsersDateRangeMenu = new components.SystemUsersFilterMenu( + page.locator('#systemUsersDateRangeSelectorMenu'), + ); + this.systemUsersActionMenus = Array.from(Array(10).keys()).map( + (index) => new components.SystemUsersFilterMenu(page.locator(`#actionMenu-systemUsersTable-${index}`)), + ); + } + + async toBeVisible() { + await this.page.waitForLoadState('networkidle'); + + await this.sidebar.toBeVisible(); + await this.navbar.toBeVisible(); + } + + async goto() { + await this.page.goto('/admin_console'); + } +} + +export {SystemConsolePage}; diff --git a/e2e-tests/playwright/support/util.ts b/e2e-tests/playwright/support/util.ts index fc591b940a..9a9ffdb778 100644 --- a/e2e-tests/playwright/support/util.ts +++ b/e2e-tests/playwright/support/util.ts @@ -45,3 +45,4 @@ export function getRandomId(length = 7): string { export const defaultTeam = {name: 'ad-1', displayName: 'eligendi', type: 'O'}; export const illegalRe = /[/?<>\\:*|":&();]/g; +export const simpleEmailRe = /\S+@\S+\.\S+/; diff --git a/e2e-tests/playwright/tests/functional/system_console/system_users/actions.spec.ts b/e2e-tests/playwright/tests/functional/system_console/system_users/actions.spec.ts new file mode 100644 index 0000000000..8e5f9c3360 --- /dev/null +++ b/e2e-tests/playwright/tests/functional/system_console/system_users/actions.spec.ts @@ -0,0 +1,214 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {TestBrowser} from '@e2e-support//browser_context'; +import {Client, createRandomTeam, createRandomUser} from '@e2e-support/server'; +import {expect, test} from '@e2e-support/test_fixture'; +import {components} from '@e2e-support/ui/components'; +import {SystemConsolePage} from '@e2e-support/ui/pages/system_console'; +import {getRandomId} from '@e2e-support/util'; +import {UserProfile} from '@mattermost/types/users'; + +/** + * Setup a new random user, and search for it such that it's the first row in the list + * @param pw + * @param pages + * @returns A function to get the refreshed user, and the System Console page for navigation + */ +async function setupAndGetRandomUser( + pw: {testBrowser: TestBrowser; initSetup: () => Promise<{adminUser: UserProfile | null; adminClient: Client}>}, + pages: {SystemConsolePage: typeof SystemConsolePage}, +) { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create a random user to edit for + const user = await adminClient.createUser(createRandomUser(), '', ''); + const team = await adminClient.createTeam(createRandomTeam()); + await adminClient.addToTeam(team.id, user.id); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Search for user-1 + await systemConsolePage.systemUsers.enterSearchText(user.email); + const userRow = await systemConsolePage.systemUsers.getNthRow(1); + await userRow.getByText(user.email).waitFor(); + const innerText = await userRow.innerText(); + expect(innerText).toContain(user.email); + + return {getUser: () => adminClient.getUser(user.id), systemConsolePage}; +} + +test('MM-T5520-1 should activate and deactivate users', async ({pw, pages}) => { + const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw, pages); + + // # Open menu and deactivate the user + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + const deactivate = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Deactivate'); + await deactivate.click(); + + // # Press confirm on the modal + const confirmModal = new components.GenericConfirmModal(systemConsolePage.page); + await confirmModal.confirm(); + + // * Verify user is deactivated + const firstRow = await systemConsolePage.systemUsers.getNthRow(1); + await firstRow.getByText('Deactivated').waitFor(); + expect(await firstRow.innerText()).toContain('Deactivated'); + expect((await getUser()).delete_at).toBeGreaterThan(0); + + // # Open menu and reactivate the user + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + const activate = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Activate'); + await activate.click(); + + // * Verify user is activated + await firstRow.getByText('Member').waitFor(); + expect(await firstRow.innerText()).toContain('Member'); +}); + +test('MM-T5520-2 should change user roles', async ({pw, pages}) => { + const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw, pages); + + // # Open menu and click Manage roles + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + let manageRoles = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Manage roles'); + await manageRoles.click(); + + // # Change to System Admin and click Save + const systemAdmin = systemConsolePage.page.locator('input[name="systemadmin"]'); + await systemAdmin.waitFor(); + await systemAdmin.click(); + await systemConsolePage.page.locator('button.btn-primary').click(); + + // * Verify that the modal closed and no error showed + await systemAdmin.waitFor({state: 'detached'}); + + // * Verify that the role was updated + const firstRow = await systemConsolePage.systemUsers.getNthRow(1); + expect(await firstRow.innerText()).toContain('System Admin'); + expect((await getUser()).roles).toContain('system_admin'); + + // # Open menu and click Manage roles + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + manageRoles = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Manage roles'); + await manageRoles.click(); + + // # Change to Member and click Save + const systemMember = systemConsolePage.page.locator('input[name="systemmember"]'); + await systemMember.waitFor(); + await systemMember.click(); + await systemConsolePage.page.locator('button.btn-primary').click(); + + // * Verify that the modal closed and no error showed + await systemMember.waitFor({state: 'detached'}); + + // * Verify that the role was updated + expect(await firstRow.innerText()).toContain('Member'); + expect((await getUser()).roles).toContain('system_user'); +}); + +test('MM-T5520-3 should be able to manage teams', async ({pw, pages}) => { + const {systemConsolePage} = await setupAndGetRandomUser(pw, pages); + + // # Open menu and click Manage teams + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + const manageTeams = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Manage teams'); + await manageTeams.click(); + + // # Click Make Team Admin + const team = systemConsolePage.page.locator('div.manage-teams__team'); + const teamDropdown = team.locator('div.MenuWrapper'); + await teamDropdown.click(); + const makeTeamAdmin = teamDropdown.getByText('Make Team Admin'); + await makeTeamAdmin.click(); + + // * Verify role is updated + expect(await team.innerText()).toContain('Team Admin'); + + // # Change back to Team Member + await teamDropdown.click(); + const makeTeamMember = teamDropdown.getByText('Make Team Member'); + await makeTeamMember.click(); + + // * Verify role is updated + expect(await team.innerText()).toContain('Team Member'); + + // # Click Remove From Team + await teamDropdown.click(); + const removeFromTeam = teamDropdown.getByText('Remove From Team'); + await removeFromTeam.click(); + + // * The team should be detached + await team.waitFor({state: 'detached'}); + expect(team).not.toBeVisible(); +}); + +test('MM-T5520-4 should reset the users password', async ({pw, pages}) => { + const {systemConsolePage} = await setupAndGetRandomUser(pw, pages); + + // # Open menu and click Reset Password + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + const resetPassword = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Reset password'); + await resetPassword.click(); + + // # Enter a random password and click Save + const passwordInput = systemConsolePage.page.locator('input[type="password"]'); + await passwordInput.fill(getRandomId()); + await systemConsolePage.page.locator('button.btn-primary').click(); + + // * Verify that the modal closed and no error showed + await passwordInput.waitFor({state: 'detached'}); +}); + +test('MM-T5520-5 should change the users email', async ({pw, pages}) => { + const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw, pages); + const newEmail = `${getRandomId()}@example.com`; + + // # Open menu and click Update Email + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + const updateEmail = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Update email'); + await updateEmail.click(); + + // # Enter a random password and click Save + const emailInput = await systemConsolePage.page.locator('input[type="email"]'); + await emailInput.fill(newEmail); + await systemConsolePage.page.locator('button.btn-primary').click(); + + // * Verify that the modal closed + await emailInput.waitFor({state: 'detached'}); + + // * Verify that the email updated + const firstRow = await systemConsolePage.systemUsers.getNthRow(1); + expect(await firstRow.innerText()).toContain(newEmail); + expect((await getUser()).email).toEqual(newEmail); +}); + +test('MM-T5520-6 should revoke sessions', async ({pw, pages}) => { + const {systemConsolePage} = await setupAndGetRandomUser(pw, pages); + + // # Open menu and revoke sessions + await systemConsolePage.systemUsers.actionMenuButtons[0].click(); + const removeSessions = await systemConsolePage.systemUsersActionMenus[0].getMenuItem('Remove sessions'); + await removeSessions.click(); + + // # Press confirm on the modal + const confirmModal = new components.GenericConfirmModal(systemConsolePage.page); + await confirmModal.confirm(); + + const firstRow = await systemConsolePage.systemUsers.getNthRow(1); + expect(await firstRow.innerHTML()).not.toContain('class="error"'); +}); diff --git a/e2e-tests/playwright/tests/functional/system_console/system_users/column_sort.spec.ts b/e2e-tests/playwright/tests/functional/system_console/system_users/column_sort.spec.ts new file mode 100644 index 0000000000..c548d4bd4c --- /dev/null +++ b/e2e-tests/playwright/tests/functional/system_console/system_users/column_sort.spec.ts @@ -0,0 +1,97 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect} from '@playwright/test'; + +import {test} from '@e2e-support/test_fixture'; +import {createRandomUser} from '@e2e-support/server'; +import {simpleEmailRe} from '@e2e-support/util'; + +test('MM-T5523-1 Sortable columns should sort the list when clicked', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create 10 random users + for (let i = 0; i < 10; i++) { + await adminClient.createUser(createRandomUser(), '', ''); + } + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // * Verify that 'Email' column has aria-sort attribute + const userDetailsColumnHeader = await systemConsolePage.systemUsers.getColumnHeader('Email'); + expect(await userDetailsColumnHeader.isVisible()).toBe(true); + expect(userDetailsColumnHeader).toHaveAttribute('aria-sort'); + + // # Store the first row's email before sorting + const firstRowWithoutSort = await systemConsolePage.systemUsers.getNthRow(1); + const firstRowEmailWithoutSort = await firstRowWithoutSort.getByText(simpleEmailRe).allInnerTexts(); + + // # Click on the 'Email' column header to sort + await systemConsolePage.systemUsers.clickSortOnColumn('Email'); + await systemConsolePage.systemUsers.isLoadingComplete(); + + // # Store the first row's email after sorting + const firstRowWithSort = await systemConsolePage.systemUsers.getNthRow(1); + const firstRowEmailWithSort = await firstRowWithSort.getByText(simpleEmailRe).allInnerTexts(); + + // * Verify that the first row is now different + expect(firstRowEmailWithoutSort).not.toBe(firstRowEmailWithSort); +}); + +test('MM-T5523-2 Non sortable columns should not sort the list when clicked', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create 10 random users + for (let i = 0; i < 10; i++) { + await adminClient.createUser(createRandomUser(), '', ''); + } + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // * Verify that 'Last login' column does not have aria-sort attribute + const userDetailsColumnHeader = await systemConsolePage.systemUsers.getColumnHeader('Last login'); + expect(await userDetailsColumnHeader.isVisible()).toBe(true); + expect(userDetailsColumnHeader).not.toHaveAttribute('aria-sort'); + + // # Store the first row's email without sorting + const firstRowWithoutSort = await systemConsolePage.systemUsers.getNthRow(1); + const firstRowEmailWithoutSort = await firstRowWithoutSort.getByText(simpleEmailRe).allInnerTexts(); + + // # Try to click on the 'Last login' column header to sort + await systemConsolePage.systemUsers.clickSortOnColumn('Last login'); + + // # Store the first row's email after sorting + const firstRowWithSort = await systemConsolePage.systemUsers.getNthRow(1); + const firstRowEmailWithSort = await firstRowWithSort.getByText(simpleEmailRe).allInnerTexts(); + + // * Verify that the first row's email is still the same + expect(firstRowEmailWithoutSort).toEqual(firstRowEmailWithSort); +}); diff --git a/e2e-tests/playwright/tests/functional/system_console/system_users/column_toggler.spec.ts b/e2e-tests/playwright/tests/functional/system_console/system_users/column_toggler.spec.ts new file mode 100644 index 0000000000..402b86fbfa --- /dev/null +++ b/e2e-tests/playwright/tests/functional/system_console/system_users/column_toggler.spec.ts @@ -0,0 +1,134 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect} from '@playwright/test'; + +import {test} from '@e2e-support/test_fixture'; + +test('MM-T5523-3 Should list the column names with checkboxes in the correct order', async ({pw, pages}) => { + const {adminUser} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Open the column toggle menu + await systemConsolePage.systemUsers.openColumnToggleMenu(); + await systemConsolePage.systemUsersColumnToggleMenu.toBeVisible(); + + // # Get all the menu items + const menuItems = await systemConsolePage.systemUsersColumnToggleMenu.getAllMenuItems(); + const menuItemsTexts = await menuItems.allInnerTexts(); + + // * Verify menu items exists in the correct order + expect(menuItemsTexts).toHaveLength(9); + expect(menuItemsTexts).toEqual([ + 'User details', + 'Email', + 'Member since', + 'Last login', + 'Last activity', + 'Last post', + 'Days active', + 'Messages posted', + 'Actions', + ]); +}); + +test('MM-T5523-4 Should allow certain columns to be checked and others to be disabled', async ({pw, pages}) => { + const {adminUser} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Open the column toggle menu + await systemConsolePage.systemUsers.openColumnToggleMenu(); + await systemConsolePage.systemUsersColumnToggleMenu.toBeVisible(); + + // * Verify that 'Display Name' is disabled + const displayNameMenuItem = await systemConsolePage.systemUsersColumnToggleMenu.getMenuItem('User details'); + expect(displayNameMenuItem).toBeDisabled(); + + // * Verify that 'Actions' is disabled + const actionsMenuItem = await systemConsolePage.systemUsersColumnToggleMenu.getMenuItem('Actions'); + expect(actionsMenuItem).toBeDisabled(); + + // * Verify that 'Email' however is enabled + const emailMenuItem = await systemConsolePage.systemUsersColumnToggleMenu.getMenuItem('Email'); + expect(emailMenuItem).not.toBeDisabled(); +}); + +test('MM-T5523-5 Should show/hide the columns which are toggled on/off', async ({pw, pages}) => { + const {adminUser} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Open the column toggle menu + await systemConsolePage.systemUsers.openColumnToggleMenu(); + await systemConsolePage.systemUsersColumnToggleMenu.toBeVisible(); + + // # Uncheck the Email and Last login columns to hide them + await systemConsolePage.systemUsersColumnToggleMenu.clickMenuItem('Email'); + await systemConsolePage.systemUsersColumnToggleMenu.clickMenuItem('Last login'); + + // * Close the column toggle menu + await systemConsolePage.systemUsersColumnToggleMenu.close(); + + // * Verify that Email column and Last login column are hidden + expect(await systemConsolePage.systemUsers.doesColumnExist('Email')).toBe(false); + expect(await systemConsolePage.systemUsers.doesColumnExist('Last login')).toBe(false); + + // # Now open the column toggle menu again + await systemConsolePage.systemUsers.openColumnToggleMenu(); + + // # Check the Email column to show it + await systemConsolePage.systemUsersColumnToggleMenu.clickMenuItem('Email'); + + // * Close the column toggle menu + await systemConsolePage.systemUsersColumnToggleMenu.close(); + + // * Verify that Email column is now shown + expect(await systemConsolePage.systemUsers.doesColumnExist('Email')).toBe(true); + + // * Verify that however Last login column is still hidden as we did not check it on + expect(await systemConsolePage.systemUsers.doesColumnExist('Last login')).toBe(false); +}); diff --git a/e2e-tests/playwright/tests/functional/system_console/system_users/export_data.spec.ts b/e2e-tests/playwright/tests/functional/system_console/system_users/export_data.spec.ts new file mode 100644 index 0000000000..a47352f474 --- /dev/null +++ b/e2e-tests/playwright/tests/functional/system_console/system_users/export_data.spec.ts @@ -0,0 +1,75 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {expect} from '@playwright/test'; + +import {test} from '@e2e-support/test_fixture'; +import {duration} from '@e2e-support/util'; +import {components} from '@e2e-support/ui/components'; + +test('MM-T5522 Should begin export of data when export button is pressed', async ({pw, pages}) => { + test.slow(); + + // # Skip test if no license + await pw.skipIfNoLicense(); + + const {adminUser} = await pw.initSetup(); + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Change the export duration to 30 days + await systemConsolePage.systemUsers.dateRangeSelectorMenuButton.click(); + await systemConsolePage.systemUsersDateRangeMenu.clickMenuItem('All time'); + + // # Click Export button and confirm the modal + await systemConsolePage.systemUsers.exportButton.click(); + const confirmModal = new components.GenericConfirmModal(page, 'exportUserDataModal'); + await confirmModal.confirm(); + + // # Change the export duration to all time + await systemConsolePage.systemUsers.dateRangeSelectorMenuButton.click(); + await systemConsolePage.systemUsersDateRangeMenu.clickMenuItem('Last 30 days'); + + // # Click Export button and confirm the modal + await systemConsolePage.systemUsers.exportButton.click(); + await confirmModal.confirm(); + + // # Click Export again button and confirm the modal + await systemConsolePage.systemUsers.exportButton.click(); + await confirmModal.confirm(); + + // * Verify that we are told that one is already running + expect(page.getByText('Export is in progress')).toBeVisible(); + + // # Go back to Channels and open the system bot DM + const channelsPage = new pages.ChannelsPage(page); + channelsPage.goto('ad-1/messages', '@system-bot'); + await channelsPage.centerView.toBeVisible(); + + // * Verify that we have started the export and that the second one is running second + const lastPost = await channelsPage.centerView.getLastPost(); + const postText = await lastPost.body.innerText(); + expect(postText).toContain('export of user data for the last 30 days'); + + // * Wait until the first export finishes + await channelsPage.centerView.waitUntilLastPostContains('contains user data for all time', duration.half_min); + + // * Wait until the second export finishes + await channelsPage.centerView.waitUntilLastPostContains( + 'contains user data for the last 30 days', + duration.half_min, + ); +}); diff --git a/e2e-tests/playwright/tests/functional/system_console/system_users/filter_popover.spec.ts b/e2e-tests/playwright/tests/functional/system_console/system_users/filter_popover.spec.ts new file mode 100644 index 0000000000..f654bb1588 --- /dev/null +++ b/e2e-tests/playwright/tests/functional/system_console/system_users/filter_popover.spec.ts @@ -0,0 +1,166 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {test} from '@e2e-support/test_fixture'; +import {createRandomTeam, createRandomUser} from '@e2e-support/server'; + +test('MM-T5521-7 Should be able to filter users with team filter', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create a team with a user + const team1 = await adminClient.createTeam(createRandomTeam()); + const user1 = await adminClient.createUser(createRandomUser(), '', ''); + await adminClient.addToTeam(team1.id, user1.id); + + // # Create another team with a user + const team2 = await adminClient.createTeam(createRandomTeam()); + const user2 = await adminClient.createUser(createRandomUser(), '', ''); + await adminClient.addToTeam(team2.id, user2.id); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Open the filter's popover + await systemConsolePage.systemUsers.openFilterPopover(); + await systemConsolePage.systemUsersFilterPopover.toBeVisible(); + + // # Enter the team name of the first user and select it + await systemConsolePage.systemUsersFilterPopover.searchInTeamMenu(team1.display_name); + await systemConsolePage.systemUsersFilterPopover.teamMenuInput.press('Enter'); + + // # Save the filter and close the popover + await systemConsolePage.systemUsersFilterPopover.save(); + await systemConsolePage.systemUsersFilterPopover.close(); + await systemConsolePage.systemUsers.isLoadingComplete(); + + // * Verify that the user corresponding to the first team is visible as team-1 filter was applied + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(user1.email); + + // * Verify that the user corresponding to the second team is not visible as team-2 filter was not applied + await systemConsolePage.systemUsers.verifyRowWithTextIsNotFound(user2.email); +}); + +test('MM-T5521-8 Should be able to filter users with role filter', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create a guest user + const guestUser = await adminClient.createUser(createRandomUser(), '', ''); + await adminClient.updateUserRoles(guestUser.id, 'system_guest'); + + // # Create a regular user + const regularUser = await adminClient.createUser(createRandomUser(), '', ''); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Open the filter popover + await systemConsolePage.systemUsers.openFilterPopover(); + await systemConsolePage.systemUsersFilterPopover.toBeVisible(); + + // # Open the role filter in the popover + await systemConsolePage.systemUsersFilterPopover.openRoleMenu(); + await systemConsolePage.systemUsersRoleMenu.toBeVisible(); + + // # Select the Guest role from the role filter + await systemConsolePage.systemUsersRoleMenu.clickMenuItem('Guest'); + await systemConsolePage.systemUsersRoleMenu.close(); + + // # Save the filter and close the popover + await systemConsolePage.systemUsersFilterPopover.save(); + await systemConsolePage.systemUsersFilterPopover.close(); + await systemConsolePage.systemUsers.isLoadingComplete(); + + // # Search for the guest user with the filter already applied + await systemConsolePage.systemUsers.enterSearchText(guestUser.email); + + // * Verify that guest user is visible as a 'Guest' role filter was applied + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(guestUser.email); + + // # Search for the regular user with the filter already applied + await systemConsolePage.systemUsers.enterSearchText(regularUser.email); + + // * Verify that regular user is not visible as 'Guest' role filter was applied + await systemConsolePage.systemUsers.verifyRowWithTextIsFound('No data'); +}); + +test('MM-T5521-9 Should be able to filter users with status filter', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create a user and then deactivate it + const deactivatedUser = await adminClient.createUser(createRandomUser(), '', ''); + await adminClient.updateUserActive(deactivatedUser.id, false); + + // # Create a regular user + const regularUser = await adminClient.createUser(createRandomUser(), '', ''); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Open the filter popover + await systemConsolePage.systemUsers.openFilterPopover(); + await systemConsolePage.systemUsersFilterPopover.toBeVisible(); + + // # Open the status filter in the popover + await systemConsolePage.systemUsersFilterPopover.openStatusMenu(); + await systemConsolePage.systemUsersStatusMenu.toBeVisible(); + await systemConsolePage.systemUsers.isLoadingComplete(); + + // # Select the Deactivated users from the status filter + await systemConsolePage.systemUsersStatusMenu.clickMenuItem('Deactivated users'); + await systemConsolePage.systemUsersStatusMenu.close(); + + // # Save the filter and close the popover + await systemConsolePage.systemUsersFilterPopover.save(); + await systemConsolePage.systemUsersFilterPopover.close(); + + // # Search for the deactivated user with the filter already applied + await systemConsolePage.systemUsers.enterSearchText(deactivatedUser.email); + + // * Verify that deactivated user is visible as a 'Deactivated' status filter was applied + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(deactivatedUser.email); + + // # Search for the regular user with the filter already applied + await systemConsolePage.systemUsers.enterSearchText(regularUser.email); + + // * Verify that regular user is not visible as 'Deactivated' status filter was applied + await systemConsolePage.systemUsers.verifyRowWithTextIsFound('No data'); +}); diff --git a/e2e-tests/playwright/tests/functional/system_console/system_users/search.spec.ts b/e2e-tests/playwright/tests/functional/system_console/system_users/search.spec.ts new file mode 100644 index 0000000000..c591ed10d6 --- /dev/null +++ b/e2e-tests/playwright/tests/functional/system_console/system_users/search.spec.ts @@ -0,0 +1,194 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {test} from '@e2e-support/test_fixture'; +import {createRandomUser} from '@e2e-support/server'; +import {getRandomId} from '@e2e-support/util'; + +test('MM-T5521-1 Should be able to search users with their first names', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create 2 users + const user1 = await adminClient.createUser(createRandomUser(), '', ''); + const user2 = await adminClient.createUser(createRandomUser(), '', ''); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Enter the 'First Name' of the first user in the search box + await systemConsolePage.systemUsers.enterSearchText(user1.first_name); + + // * Verify that the searched user i.e first user is found in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(user1.email); + + // * Verify that the second user doesnt appear in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsNotFound(user2.email); +}); + +test('MM-T5521-2 Should be able to search users with their last names', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create 2 users + const user1 = await adminClient.createUser(createRandomUser(), '', ''); + const user2 = await adminClient.createUser(createRandomUser(), '', ''); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Enter the 'Last Name' of the user in the search box + await systemConsolePage.systemUsers.enterSearchText(user1.last_name); + + // * Verify that the searched user i.e first user is found in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(user1.email); + + // * Verify that the second user doesnt appear in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsNotFound(user2.email); +}); + +test('MM-T5521-3 Should be able to search users with their emails', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create 2 users + const user1 = await adminClient.createUser(createRandomUser(), '', ''); + const user2 = await adminClient.createUser(createRandomUser(), '', ''); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // * Enter the 'Email' of the first user in the search box + await systemConsolePage.systemUsers.enterSearchText(user1.email); + + // * Verify that the searched user i.e first user is found in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(user1.email); + + // * Verify that the second user doesnt appear in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsNotFound(user2.email); +}); + +test('MM-T5521-4 Should be able to search users with their usernames', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create 2 users + const user1 = await adminClient.createUser(createRandomUser(), '', ''); + const user2 = await adminClient.createUser(createRandomUser(), '', ''); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + await systemConsolePage.systemUsers.toBeVisible(); + + // # Enter the 'Username' of the first user in the search box + await systemConsolePage.systemUsers.enterSearchText(user1.username); + + // * Verify that the searched user i.e first user is found in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(user1.email); + + // * Verify that the another user is not visible + await systemConsolePage.systemUsers.verifyRowWithTextIsNotFound(user2.email); +}); + +test('MM-T5521-5 Should be able to search users with their nick names', async ({pw, pages}) => { + const {adminUser, adminClient} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Create 2 users + const user1 = await adminClient.createUser(createRandomUser(), '', ''); + const user2 = await adminClient.createUser(createRandomUser(), '', ''); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + + // # Enter the 'Nickname' of the first user in the search box + await systemConsolePage.systemUsers.enterSearchText(user1.nickname); + + // * Verify that the searched user i.e first user is found in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsFound(user1.email); + + // * Verify that the second user doesnt appear in the list + await systemConsolePage.systemUsers.verifyRowWithTextIsNotFound(user2.email); +}); + +test('MM-T5521-6 Should show no user is found when user doesnt exists', async ({pw, pages}) => { + const {adminUser} = await pw.initSetup(); + + if (!adminUser) { + throw new Error('Failed to create admin user'); + } + + // # Log in as admin + const {page} = await pw.testBrowser.login(adminUser); + + // # Visit system console + const systemConsolePage = new pages.SystemConsolePage(page); + await systemConsolePage.goto(); + await systemConsolePage.toBeVisible(); + + // # Go to Users section + await systemConsolePage.sidebar.goToItem('Users'); + + // # Enter random text in the search box + await systemConsolePage.systemUsers.enterSearchText(`!${getRandomId(15)}_^^^_${getRandomId(15)}!`); + + await systemConsolePage.systemUsers.verifyRowWithTextIsFound('No data'); +}); diff --git a/webapp/channels/src/components/admin_console/system_users/system_users_export/export_user_data_modal.tsx b/webapp/channels/src/components/admin_console/system_users/system_users_export/export_user_data_modal.tsx index f3cede17ea..191e6445e1 100644 --- a/webapp/channels/src/components/admin_console/system_users/system_users_export/export_user_data_modal.tsx +++ b/webapp/channels/src/components/admin_console/system_users/system_users_export/export_user_data_modal.tsx @@ -71,6 +71,7 @@ export function ExportUserDataModal({onConfirm, onExited}: Props) { return ( { return (