Files
ac0705fb68 [MM-69226] Fix Manage Teams role dropdown overflowing outside the modal (#37400)
* Fix Manage Teams dropdown overflowing outside modal for last rows

The role dropdown in the Admin Console Manage Teams modal always opened
downward, so for users with many teams the menu for the last rows was
pushed below the modal and out of view. Compute openUp for rows near the
bottom of the list, mirroring the TeamMembersDropdown behavior.

Co-authored-by: mattermost-code <[email protected]>

* Render Manage Teams role dropdown via floating menu to fix overflow

Migrate ManageTeamsDropdown from the deprecated Menu/MenuWrapper widget to
the modern components/menu (MUI Popover). The old widget positioned the
menu relative to the modal content, so for users with many teams the role
dropdown on the last rows was pushed below the modal and off-screen. The
new menu renders in a portal anchored to the trigger and opens upward for
rows near the bottom of the list, mirroring TeamMembersDropdown.

Co-authored-by: mattermost-code <[email protected]>

* Add openUp helper coverage and action tests for ManageTeamsDropdown

Extract the open-up positioning rule into a testable shouldOpenUp helper
and cover its boundaries, plus the demote and error paths, so the fix for
the dropdown overflow is exercised directly.

Co-authored-by: mattermost-code <[email protected]>

* Satisfy lint for table-driven test comment

Co-authored-by: mattermost-code <[email protected]>

* Gate onMemberChange assertions behind waitFor in dropdown tests

Move onMemberChange expectations inside waitFor so they wait for the
async updateTeamMemberSchemeRoles promise to resolve before asserting.

Co-authored-by: mattermost-code <[email protected]>

* Fix Manage Teams e2e to target MUI menu button and items

The Manage Teams role dropdown was migrated from the deprecated
components/widgets/menu (div.MenuWrapper) to the modern components/menu
(MUI Popover). The Playwright spec still clicked div.MenuWrapper and
looked for menu items inside the team row, but the new menu renders in a
portal. Target the accessible role button and role menuitem elements
instead.

---------

Co-authored-by: mattermost-code <[email protected]>

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 09:24:45 +02:00

187 lines
6.9 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {type PlaywrightExtended, expect, test} from '@mattermost/playwright-lib';
test('MM-T5520-1 should activate and deactivate users', async ({pw}) => {
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
// # Open menu and deactivate the user
const actionMenu = await userRow.openActionMenu();
await actionMenu.clickDeactivate();
// # Press confirm on the modal
await systemConsolePage.users.confirmModal.confirm();
// * Verify user is deactivated
await expect(userRow.container.getByText('Deactivated')).toBeVisible();
expect((await getUser()).delete_at).toBeGreaterThan(0);
// # Open menu and reactivate the user
const actionMenu2 = await userRow.openActionMenu();
await actionMenu2.clickActivate();
// * Verify user is activated
await expect(userRow.container.getByText('Member')).toBeVisible();
});
test('MM-T5520-2 should change user roles', async ({pw}) => {
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
// # Open menu and click Manage roles
const actionMenu = await userRow.openActionMenu();
await actionMenu.clickManageRoles();
// # Change to System Admin and click Save
const systemAdmin = systemConsolePage.page.locator('input[name="systemadmin"]');
await systemAdmin.waitFor();
await systemAdmin.click();
await systemConsolePage.users.manageRolesModal.save();
// * Verify that the role was updated
await expect(userRow.container.getByText('System Admin')).toBeVisible();
expect((await getUser()).roles).toContain('system_admin');
// # Open menu and click Manage roles
const actionMenu2 = await userRow.openActionMenu();
await actionMenu2.clickManageRoles();
// # Change to Member and click Save
const systemMember = systemConsolePage.page.locator('input[name="systemmember"]');
await systemMember.waitFor();
await systemMember.click();
await systemConsolePage.users.manageRolesModal.save();
// * Verify that the role was updated
await expect(userRow.container.getByText('Member')).toBeVisible();
expect((await getUser()).roles).toContain('system_user');
});
test('MM-T5520-3 should be able to manage teams', async ({pw}) => {
const {systemConsolePage} = await setupAndGetRandomUser(pw);
const {page} = systemConsolePage;
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
// # Open menu and click Manage teams
const actionMenu = await userRow.openActionMenu();
await actionMenu.clickManageTeams();
// The role dropdown renders its menu in a portal (MUI Popover), so the menu
// items live at the page level rather than inside the team row.
const team = page.locator('div.manage-teams__team');
// # Open the role dropdown and click Make Team Admin
await team.getByRole('button', {name: 'Team Member'}).click();
await page.getByRole('menuitem', {name: 'Make Team Admin'}).click();
// * Verify role is updated
await expect(team.getByText('Team Admin')).toBeVisible();
// # Change back to Team Member
await team.getByRole('button', {name: 'Team Admin'}).click();
await page.getByRole('menuitem', {name: 'Make Team Member'}).click();
// * Verify role is updated
await expect(team.getByText('Team Member')).toBeVisible();
// # Click Remove from Team
await team.getByRole('button', {name: 'Team Member'}).click();
await page.getByRole('menuitem', {name: 'Remove from Team'}).click();
// * The team should be detached
await team.waitFor({state: 'detached'});
await expect(team).not.toBeVisible();
});
test('MM-T5520-4 should reset the users password', async ({pw}) => {
const {systemConsolePage} = await setupAndGetRandomUser(pw);
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
// # Open menu and click Reset Password
const actionMenu = await userRow.openActionMenu();
await actionMenu.clickResetPassword();
// # Enter a random password and click Reset
await systemConsolePage.users.resetPasswordModal.fillPassword(pw.newTestPassword());
await systemConsolePage.users.resetPasswordModal.reset();
});
test('MM-T5520-5 should change the users email', async ({pw}) => {
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
const newEmail = `${pw.random.id()}@example.com`;
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
// # Open menu and click Update Email
const actionMenu = await userRow.openActionMenu();
await actionMenu.clickUpdateEmail();
// # Enter new email and click Update
await systemConsolePage.users.updateEmailModal.fillEmail(newEmail);
await systemConsolePage.users.updateEmailModal.update();
// * Verify that the email updated
await expect(userRow.container.getByText(newEmail)).toBeVisible();
expect((await getUser()).email).toEqual(newEmail);
});
test('MM-T5520-6 should revoke sessions', async ({pw}) => {
const {systemConsolePage} = await setupAndGetRandomUser(pw);
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
// # Open menu and revoke sessions
const actionMenu = await userRow.openActionMenu();
await actionMenu.clickRevokeSessions();
// # Press confirm on the modal
await systemConsolePage.users.confirmModal.confirm();
// * Verify no error is displayed
await expect(userRow.container.locator('.error')).not.toBeVisible();
});
/**
* Setup a new random user, and search for it such that it's the first row in the list
* @param pw
* @returns A function to get the refreshed user, and the System Console page for navigation
*/
async function setupAndGetRandomUser(pw: PlaywrightExtended) {
const {adminUser, adminClient} = await pw.initSetup();
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Log in as admin
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Create a random user to edit
const user = await adminClient.createUser(await pw.random.user(), '', '');
const team = await adminClient.createTeam(await pw.random.team());
await adminClient.addToTeam(team.id, user.id);
// # Visit system console
await systemConsolePage.goto();
await systemConsolePage.toBeVisible();
// # Go to Users section
await systemConsolePage.sidebar.users.click();
await systemConsolePage.users.toBeVisible();
// # Search for the user
await systemConsolePage.users.searchUsers(user.email);
// Wait for search results
const userRow = systemConsolePage.users.usersTable.getRowByIndex(0);
await expect(userRow.container.getByText(user.email)).toBeVisible();
return {getUser: () => adminClient.getUser(user.id), systemConsolePage};
}