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
This commit is contained in:
M-ZubairAhmed 2024-02-12 08:28:47 +00:00 committed by GitHub
parent 4c8a134659
commit 90b21cad33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1371 additions and 1 deletions

View File

@ -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};

View File

@ -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 {

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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};

View File

@ -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+/;

View File

@ -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"');
});

View File

@ -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);
});

View File

@ -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);
});

View File

@ -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,
);
});

View File

@ -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');
});

View File

@ -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');
});

View File

@ -71,6 +71,7 @@ export function ExportUserDataModal({onConfirm, onExited}: Props) {
return (
<ConfirmModalRedux
id='exportUserDataModal'
title={title}
message={message}
confirmButtonText={exportDataButton}

View File

@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import classNames from 'classnames';
import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
@ -8,6 +9,7 @@ import {FormattedMessage} from 'react-intl';
import './confirm_modal.scss';
type Props = {
id?: string;
/*
* Set to show modal
@ -165,12 +167,12 @@ export default class ConfirmModal extends React.Component<Props, State> {
return (
<Modal
id={classNames('confirmModal', this.props.id)}
className={'modal-confirm ' + this.props.modalClass}
dialogClassName='a11y__modal'
show={this.props.show}
onHide={this.handleCancel}
onExited={this.props.onExited}
id='confirmModal'
role='dialog'
aria-modal={true}
aria-labelledby='confirmModalLabel'