MM-62558 Add E2E tests for custom profile settings (#30722)

* add e2e tests for custom profile settings

* fix failed tests

* reorg folder and file convention, and add more details of the tests

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Saturn Abril
2025-04-16 10:32:27 +08:00
коммит произвёл GitHub
родитель 90953e9ee9
Коммит 49d3a1f472
20 изменённых файлов: 886 добавлений и 50 удалений

Просмотреть файл

@@ -7,4 +7,4 @@ export {getOnPremServerConfig} from './default_config';
export {initSetup, getAdminClient} from './init';
export {createRandomPost} from './post';
export {createRandomTeam} from './team';
export {createRandomUser, getDefaultAdminUser} from './user';
export {createNewUserProfile, createRandomUser, getDefaultAdminUser} from './user';

Просмотреть файл

@@ -1,11 +1,21 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mattermost/client';
import {UserProfile} from '@mattermost/types/users';
import {getRandomId} from '@/util';
import {testConfig} from '@/test_config';
export async function createNewUserProfile(client: Client4, prefix = 'user') {
const randomUser = createRandomUser(prefix);
const newUser = await client.createUser(randomUser, '', '');
newUser.password = randomUser.password;
return newUser;
}
export function createRandomUser(prefix = 'user') {
const randomId = getRandomId();

Просмотреть файл

@@ -18,6 +18,7 @@ import {
} from './flag';
import {getBlobFromAsset, getFileFromAsset} from './file';
import {
createNewUserProfile,
createRandomChannel,
createRandomPost,
createRandomTeam,
@@ -87,6 +88,9 @@ export class PlaywrightExtended {
readonly stubNotification;
readonly waitForNotification;
// ./server
readonly createNewUserProfile;
// ./visual
readonly matchSnapshot;
@@ -143,6 +147,9 @@ export class PlaywrightExtended {
this.stubNotification = stubNotification;
this.waitForNotification = waitForNotification;
// ./server
this.createNewUserProfile = createNewUserProfile;
// ./visual
this.matchSnapshot = matchSnapshot;

Просмотреть файл

@@ -0,0 +1,87 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Locator, expect} from '@playwright/test';
export default class ProfileModal {
readonly container: Locator;
readonly profileSettingsButton;
readonly securityButton;
readonly profileSettingsTab;
readonly securityTab;
readonly closeButton;
readonly saveButton;
readonly cancelButton;
readonly errorText;
constructor(container: Locator) {
this.container = container;
this.profileSettingsButton = container.locator('#profileButton');
this.securityButton = container.locator('#securityButton');
this.profileSettingsTab = new ProfileSettingsTab(container.getByRole('tabpanel', {name: 'Profile Settings'}));
this.securityTab = new SecurityTab(container.getByRole('tabpanel', {name: 'Security'}));
this.closeButton = container.getByRole('button', {name: 'Close'});
this.saveButton = container.locator('button:has-text("Save")');
this.cancelButton = container.locator('button:has-text("Cancel")');
this.errorText = container.locator('#clientError');
}
async toBeVisible() {
await expect(this.container).toBeVisible();
}
async openProfileSettingsTab() {
await expect(this.profileSettingsButton).toBeVisible();
await this.profileSettingsButton.click();
await this.profileSettingsTab.toBeVisible();
return this.profileSettingsTab;
}
async openSecurityTab() {
await expect(this.securityButton).toBeVisible();
await this.securityButton.click();
await this.securityTab.toBeVisible();
return this.securityTab;
}
async closeModal() {
await this.closeButton.click();
await expect(this.container).not.toBeVisible();
}
}
class ProfileSettingsTab {
readonly container: Locator;
constructor(container: Locator) {
this.container = container;
}
async toBeVisible() {
await expect(this.container).toBeVisible();
}
}
class SecurityTab {
readonly container: Locator;
constructor(container: Locator) {
this.container = container;
}
async toBeVisible() {
await expect(this.container).toBeVisible();
}
}

Просмотреть файл

@@ -24,6 +24,8 @@ import GenericConfirmModal from './channels/generic_confirm_modal';
import MessagePriority from './channels/message_priority';
import ScheduledDraftMenu from './channels/scheduled_draft_menu';
import ScheduledDraftModal from './channels/scheduled_draft_modal';
import UserAccountMenu from './user_account_menu';
import ProfileModal from './channels/profile_modal';
import UserProfilePopover from './channels/user_profile_popover';
import SystemConsoleSidebar from './system_console/sidebar';
import SystemConsoleNavbar from './system_console/navbar';
@@ -71,8 +73,10 @@ const components = {
SystemConsoleMobileSecurity,
MessagePriority,
UserProfilePopover,
UserAccountMenu,
DeletePostConfirmationDialog,
RestorePostConfirmationDialog,
ProfileModal,
};
export {
@@ -93,4 +97,6 @@ export {
ThreadFooter,
MessagePriority,
DeletePostConfirmationDialog,
RestorePostConfirmationDialog,
ProfileModal,
};

Просмотреть файл

@@ -0,0 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Locator, expect} from '@playwright/test';
export default class UserAccountMenu {
readonly container: Locator;
readonly setCustomStatus;
readonly online;
readonly away;
readonly dnd;
readonly offline;
readonly profile;
readonly logout;
constructor(container: Locator) {
this.container = container;
this.setCustomStatus = container.getByRole('button', {name: 'Set custom status'});
this.online = container.getByRole('menuitem', {name: 'Online'});
this.away = container.getByRole('menuitem', {name: 'Away'});
this.dnd = container.locator('[id="userAccountMenu\\.dndMenuItem"]');
this.offline = container.getByRole('menuitem', {name: 'Offline'});
this.profile = container.getByRole('menuitem', {name: 'Profile'});
this.logout = container.getByRole('menuitem', {name: 'Log out'});
}
async toBeVisible(name: string) {
await expect(this.container.getByRole('heading', {name})).toBeVisible();
}
}

Просмотреть файл

@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Page} from '@playwright/test';
import {expect, Page} from '@playwright/test';
import {components} from '@/ui/components';
@@ -11,6 +11,7 @@ export default class ChannelsPage {
readonly page: Page;
readonly globalHeader;
readonly userAccountMenuButton;
readonly searchPopover;
readonly centerView;
readonly scheduledDraftDropdown;
@@ -24,11 +25,11 @@ export default class ChannelsPage {
readonly findChannelsModal;
readonly deletePostModal;
readonly settingsModal;
readonly profileModal;
readonly postContainer;
readonly postDotMenu;
readonly postReminderMenu;
readonly userAccountMenu;
readonly emojiGifPickerPopup;
constructor(page: Page) {
@@ -42,15 +43,18 @@ export default class ChannelsPage {
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
this.appBar = new components.ChannelsAppBar(page.locator('.app-bar'));
this.messagePriority = new components.MessagePriority(page.locator('body'));
this.userAccountMenuButton = page.getByRole('button', {name: "User's account menu"});
// Modals
this.findChannelsModal = new components.FindChannelsModal(page.getByRole('dialog', {name: 'Find Channels'}));
this.deletePostModal = new components.DeletePostModal(page.locator('#deletePostModal'));
this.settingsModal = new components.SettingsModal(page.getByRole('dialog', {name: 'Settings'}));
this.profileModal = new components.ProfileModal(page.getByRole('dialog', {name: 'Profile'}));
// Menus
this.postDotMenu = new components.PostDotMenu(page.getByRole('menu', {name: 'Post extra options'}));
this.postReminderMenu = new components.PostReminderMenu(page.getByRole('menu', {name: 'Set a reminder for:'}));
this.userAccountMenu = new components.UserAccountMenu(page.locator('#userAccountMenu'));
// Popovers
this.emojiGifPickerPopup = new components.EmojiGifPicker(page.locator('#emojiGifPicker'));
@@ -67,7 +71,7 @@ export default class ChannelsPage {
}
async getLastPost() {
return this.postContainer.last();
return this.centerView.getLastPost();
}
async goto(teamName = '', channelName = '') {
@@ -89,4 +93,17 @@ export default class ChannelsPage {
async postMessage(message: string) {
await this.centerView.postCreate.postMessage(message);
}
async openUserAccountMenu() {
await this.userAccountMenuButton.click();
await expect(this.userAccountMenu.container).toBeVisible();
return this.userAccountMenu;
}
async openProfileModal() {
await this.openUserAccountMenu();
await this.userAccountMenu.profile.click();
await expect(this.profileModal.container).toBeVisible();
return this.profileModal;
}
}