MM-63669 E2E/Playwright: Move "e2e-tests/playwright/test" to "e2e-tests/playwright" folder (#30647)

* move "e2e-tests/playwright/test" to  "e2e-tests/playwright/test" and expose "ensurePluginsLoaded"

* add test setup, and expose ensurePluginsLoaded and ensureServerDeployment to pw
Этот коммит содержится в:
Saturnino Abril
2025-04-07 22:26:29 +08:00
коммит произвёл GitHub
родитель 62753a1481
Коммит a35a6d7a3a
80 изменённых файлов: 165 добавлений и 154 удалений

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

@@ -0,0 +1,109 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Page} from '@playwright/test';
import {UserProfile} from '@mattermost/types/users';
import {expect, test, ChannelsPage} from '@mattermost/playwright-lib';
test('MM-63451 should be able to navigate the account settings menu with the keyboard after opening it with the mouse', async ({
pw,
}) => {
// # Create and sign in a new user
const {user} = await pw.initSetup();
// # Log in a user in new browser context
const {page, channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Click on the account menu button
await channelsPage.globalHeader.accountMenuButton.click();
await testMenuWithKeyboard(user, page, channelsPage);
});
test('MM-63451 should be able to navigate the account settings menu with the keyboard after opening it with the keyboard', async ({
pw,
}) => {
// # Create and sign in a new user
const {user} = await pw.initSetup();
// # Log in a user in new browser context
const {page, channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Focus the account menu button
await channelsPage.globalHeader.accountMenuButton.focus();
await expect(channelsPage.globalHeader.accountMenuButton).toBeFocused();
await page.keyboard.press('Space');
await testMenuWithKeyboard(user, page, channelsPage);
});
async function testMenuWithKeyboard(user: UserProfile, page: Page, channelsPage: ChannelsPage) {
// * Should start focused on the first menu item
await expect(page.getByRole('menuitem', {name: '@' + user.username})).toBeFocused();
// * Should be able to scroll down through the menu with the keyboard
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Set custom status'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Online'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Away'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Do not disturb Disables all notifications'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Offline'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Profile'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Log Out'})).toBeFocused();
// * Should be able to scroll back up through the menu with the keyboard
await page.keyboard.press('ArrowUp');
await expect(page.getByRole('menuitem', {name: 'Profile'})).toBeFocused();
await page.keyboard.press('ArrowUp');
await expect(page.getByRole('menuitem', {name: 'Offline'})).toBeFocused();
await page.keyboard.press('ArrowUp');
await expect(page.getByRole('menuitem', {name: 'Do not disturb Disables all notifications'})).toBeFocused();
// * Should be able to move into the submenu by pressing the right arrow
await page.keyboard.press('ArrowRight');
await expect(page.getByRole('menuitem', {name: "Don't clear"})).toBeFocused();
// * Should be able to scroll through the submenu with the keyboard
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: '30 mins'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: '1 hour'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: '2 hours'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Tomorrow'})).toBeFocused();
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: 'Choose date and time'})).toBeFocused();
// * Should wrap around when you reach the end
await page.keyboard.press('ArrowDown');
await expect(page.getByRole('menuitem', {name: "Don't clear"})).toBeFocused();
await page.keyboard.press('ArrowUp');
await expect(page.getByRole('menuitem', {name: 'Choose date and time'})).toBeFocused();
// * Should be able to close the submenu by pressing the left arrow
await page.keyboard.press('ArrowLeft');
await expect(page.getByRole('menuitem', {name: 'Do not disturb Disables all notifications'})).toBeFocused();
// * Should be able to close the menu by pressing escape
await page.keyboard.press('Escape');
await expect(page.getByRole('menuitem')).toHaveCount(0);
// * Should be focused back on the menu button
await expect(channelsPage.globalHeader.accountMenuButton).toBeFocused();
}

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

@@ -0,0 +1,162 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test.fixme('Base channel accessibility', async ({pw, axe}) => {
// # Create and sign in a new user
const {user} = await pw.initSetup();
// # Log in a user in new browser context
const {page, channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage('hello');
// # Analyze the page
// Disable 'color-contrast' to be addressed by MM-53814
const accessibilityScanResults = await axe.builder(page, {disableColorContrast: true}).analyze();
// * Should have no violation
expect(accessibilityScanResults.violations).toHaveLength(0);
});
test('Post actions tab support', async ({pw, axe}) => {
// # Create and sign in a new user
const {user, adminClient} = await pw.initSetup();
const config = await adminClient.getConfig();
const license = await adminClient.getClientLicenseOld();
// # Log in a user in new browser context
const {page, channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage('hello');
const post = await channelsPage.centerView.getLastPost();
await post.hover();
await post.postMenu.toBeVisible();
// # Open the dot menu
await post.postMenu.dotMenuButton.press('Enter');
// * Dot menu should be visible and have focused
await channelsPage.postDotMenu.toBeVisible();
await expect(channelsPage.postDotMenu.replyMenuItem).toBeFocused();
// # Analyze the page
const accessibilityScanResults = await axe
.builder(page, {disableColorContrast: true})
.include('.MuiList-root.MuiList-padding')
.analyze();
// * Should have no violation
expect(accessibilityScanResults.violations).toHaveLength(0);
// * Should move focus to Forward after arrow down
await channelsPage.postDotMenu.replyMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.forwardMenuItem).toBeFocused();
// * Should move focus to Follow message after arrow down
await channelsPage.postDotMenu.forwardMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.followMessageMenuItem).toBeFocused();
// * Should move focus to Mark as Unread after arrow down
await channelsPage.postDotMenu.followMessageMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.markAsUnreadMenuItem).toBeFocused();
// * Should move focus to Remind after arrow down
await channelsPage.postDotMenu.markAsUnreadMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.remindMenuItem).toBeFocused();
// * Should move focus to Save after arrow down
await channelsPage.postDotMenu.remindMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.saveMenuItem).toBeFocused();
// * Should move focus to Pin to Channel after arrow down
await channelsPage.postDotMenu.saveMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.pinToChannelMenuItem).toBeFocused();
if (config.FeatureFlags['MoveThreadsEnabled'] && license.IsLicensed === 'true') {
// * Should move focus to Move Thread after arrow down
await channelsPage.postDotMenu.pinToChannelMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.moveThreadMenuItem).toBeFocused();
// * Should move focus to Copy Link after arrow down
await channelsPage.postDotMenu.moveThreadMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.copyLinkMenuItem).toBeFocused();
} else {
// * Should move focus to Copy Link after arrow down
await channelsPage.postDotMenu.pinToChannelMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.copyLinkMenuItem).toBeFocused();
}
// * Should move focus to Edit after arrow down
await channelsPage.postDotMenu.copyLinkMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.editMenuItem).toBeFocused();
// * Should move focus to Copy Text after arrow down
await channelsPage.postDotMenu.editMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.copyTextMenuItem).toBeFocused();
// * Should move focus to Delete after arrow down
await channelsPage.postDotMenu.copyTextMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.deleteMenuItem).toBeFocused();
// * Then, should move focus back to Reply after arrow down
await channelsPage.postDotMenu.deleteMenuItem.press('ArrowDown');
await expect(channelsPage.postDotMenu.replyMenuItem).toBeFocused();
// * Should move focus to Delete after arrow uo
await channelsPage.postDotMenu.container.press('ArrowUp');
expect(await channelsPage.postDotMenu.deleteMenuItem).toBeFocused();
// # Set focus to Remind
await channelsPage.postDotMenu.remindMenuItem.focus();
await expect(channelsPage.postDotMenu.remindMenuItem).toBeFocused();
// * Reminder menu should still be hidden
await expect(channelsPage.postReminderMenu.container).toBeHidden();
// # Press arrow right
await channelsPage.postDotMenu.remindMenuItem.press('ArrowRight');
// * Reminder menu should be visible
expect(channelsPage.postReminderMenu.container).toBeVisible();
// * Should have focus on 30 mins after submenu opens
expect(await channelsPage.postReminderMenu.thirtyMinsMenuItem).toBeFocused();
// * Should move focus to 1 hour after arrow down
await channelsPage.postReminderMenu.thirtyMinsMenuItem.press('ArrowDown');
expect(await channelsPage.postReminderMenu.oneHourMenuItem).toBeFocused();
// * Should move focus to 2 hours after arrow down
await channelsPage.postReminderMenu.oneHourMenuItem.press('ArrowDown');
expect(await channelsPage.postReminderMenu.twoHoursMenuItem).toBeFocused();
// * Should move focus to Tomorrow after arrow down
await channelsPage.postReminderMenu.twoHoursMenuItem.press('ArrowDown');
expect(await channelsPage.postReminderMenu.tomorrowMenuItem).toBeFocused();
// * Should move focus to Custom after arrow down
await channelsPage.postReminderMenu.tomorrowMenuItem.press('ArrowDown');
expect(await channelsPage.postReminderMenu.customMenuItem).toBeFocused();
// * Then, should move focus back to 30 mins after arrow down
await channelsPage.postReminderMenu.customMenuItem.press('ArrowDown');
expect(await channelsPage.postReminderMenu.thirtyMinsMenuItem).toBeFocused();
// * Should hide Reminder menu and focus to Remind menu after arrow left
await channelsPage.postReminderMenu.container.press('ArrowLeft');
await expect(channelsPage.postReminderMenu.container).toBeHidden();
await expect(channelsPage.postDotMenu.remindMenuItem).toBeFocused();
// * Should hide Dot menu of Escape
await channelsPage.postDotMenu.container.press('Escape');
await expect(channelsPage.postDotMenu.container).toBeHidden();
});

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

@@ -0,0 +1,79 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('/login accessibility quick check', async ({pw, axe}) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// # Go to login page
await pw.loginPage.goto();
await pw.loginPage.toBeVisible();
// # Analyze the page
const accessibilityScanResults = await axe.builder(pw.loginPage.page).analyze();
// * Should have no violation
expect(accessibilityScanResults.violations).toHaveLength(0);
});
test('/login accessibility tab support', async ({pw}) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// # Go to login page
await pw.loginPage.goto();
await pw.loginPage.toBeVisible();
// * Should have focused at login input on page load
expect(await pw.loginPage.loginInput).toBeFocused();
// * Should move focus to password input after tab
await pw.loginPage.loginInput.press('Tab');
expect(await pw.loginPage.passwordInput).toBeFocused();
// * Should move focus to password toggle button after tab
await pw.loginPage.passwordInput.press('Tab');
expect(await pw.loginPage.passwordToggleButton).toBeFocused();
// * Should move focus to forgot password link after tab
await pw.loginPage.passwordToggleButton.press('Tab');
expect(await pw.loginPage.forgotPasswordLink).toBeFocused();
// * Should move focus to forgot password link after tab
await pw.loginPage.forgotPasswordLink.press('Tab');
expect(await pw.loginPage.signInButton).toBeFocused();
// * Should move focus to about link after tab
await pw.loginPage.signInButton.press('Tab');
expect(await pw.loginPage.footer.aboutLink).toBeFocused();
// * Should move focus to privacy policy link after tab
await pw.loginPage.footer.aboutLink.press('Tab');
expect(await pw.loginPage.footer.privacyPolicyLink).toBeFocused();
// * Should move focus to terms link after tab
await pw.loginPage.footer.privacyPolicyLink.press('Tab');
expect(await pw.loginPage.footer.termsLink).toBeFocused();
// * Should move focus to help link after tab
await pw.loginPage.footer.termsLink.press('Tab');
expect(await pw.loginPage.footer.helpLink).toBeFocused();
// # Move focus to login input
await pw.loginPage.loginInput.focus();
expect(await pw.loginPage.loginInput).toBeFocused();
// * Should move focus to login body after shift+tab
await pw.loginPage.loginInput.press('Shift+Tab');
expect(await pw.loginPage.bodyCard).toBeFocused();
// * Should move focus to create account link after shift+tab
await pw.loginPage.bodyCard.press('Shift+Tab');
expect(await pw.loginPage.createAccountLink).toBeFocused();
// * Should move focus to login body after tab
await pw.loginPage.createAccountLink.press('Shift+Tab');
expect(await pw.loginPage.header.logo).toBeFocused();
});

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

@@ -0,0 +1,61 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('/reset_password accessibility quick check', async ({pw, axe}) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// # Go to reset password page
await pw.resetPasswordPage.goto();
await pw.resetPasswordPage.toBeVisible();
// # Analyze the page
const accessibilityScanResults = await axe
.builder(pw.resetPasswordPage.page, {disableColorContrast: true})
.analyze();
// * Should have no violation
expect(accessibilityScanResults.violations).toHaveLength(0);
});
test('/reset_password accessibility tab support', async ({pw}) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// # Go to reset password page
await pw.resetPasswordPage.goto();
await pw.resetPasswordPage.toBeVisible();
// * Should have focused at email input on page load
expect(await pw.resetPasswordPage.emailInput).toBeFocused();
// * Should move focus to reset button after tab
await pw.resetPasswordPage.emailInput.press('Tab');
expect(await pw.resetPasswordPage.resetButton).toBeFocused();
// * Should move focus to about link after tab
await pw.resetPasswordPage.resetButton.press('Tab');
expect(await pw.resetPasswordPage.footer.aboutLink).toBeFocused();
// * Should move focus to privacy policy link after tab
await pw.resetPasswordPage.footer.aboutLink.press('Tab');
expect(await pw.resetPasswordPage.footer.privacyPolicyLink).toBeFocused();
// * Should move focus to terms link after tab
await pw.resetPasswordPage.footer.privacyPolicyLink.press('Tab');
expect(await pw.resetPasswordPage.footer.termsLink).toBeFocused();
// * Should move focus to help link after tab
await pw.resetPasswordPage.footer.termsLink.press('Tab');
expect(await pw.resetPasswordPage.footer.helpLink).toBeFocused();
// # Move focus to email input
await pw.resetPasswordPage.emailInput.focus();
expect(await pw.resetPasswordPage.emailInput).toBeFocused();
// * Should move focus to back button after shift+tab
await pw.resetPasswordPage.emailInput.press('Shift+Tab');
expect(await pw.resetPasswordPage.header.backButton).toBeFocused();
});

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

@@ -0,0 +1,115 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('/signup_user_complete accessibility quick check', async ({pw, axe}) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// # Go to reset password page
await pw.signupPage.goto();
await pw.signupPage.toBeVisible();
// # Analyze the page
const accessibilityScanResults = await axe
.builder(pw.signupPage.page, {disableColorContrast: true, disableLinkInTextBlock: true})
.analyze();
// * Should have no violation
expect(accessibilityScanResults.violations).toHaveLength(0);
});
test('/signup_user_complete accessibility tab support', async ({pw}, testInfo) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// # Go to reset password page
await pw.signupPage.goto();
await pw.signupPage.toBeVisible();
// * Should have focused at email input on page load
expect(await pw.signupPage.emailInput).toBeFocused();
// * Should move focus to username input after tab
await pw.signupPage.emailInput.press('Tab');
expect(await pw.signupPage.usernameInput).toBeFocused();
// * Should move focus to password input after tab
await pw.signupPage.usernameInput.press('Tab');
expect(await pw.signupPage.passwordInput).toBeFocused();
// * Should move focus to password toggle button after tab
await pw.signupPage.passwordInput.press('Tab');
expect(await pw.signupPage.passwordToggleButton).toBeFocused();
// * Should move focus to newsletter checkbox after tab
await pw.signupPage.passwordToggleButton.press('Tab');
expect(await pw.signupPage.newsLetterCheckBox).toBeFocused();
// * Should move focus to newsletter privacy policy link after tab
await pw.signupPage.newsLetterCheckBox.press('Tab');
expect(await pw.signupPage.newsLetterPrivacyPolicyLink).toBeFocused();
// * Should move focus to newsletter unsubscribe link after tab
await pw.signupPage.newsLetterPrivacyPolicyLink.press('Tab');
expect(await pw.signupPage.newsLetterUnsubscribeLink).toBeFocused();
// * Should move focus to agreement terms of use link after tab
await pw.signupPage.newsLetterUnsubscribeLink.press('Tab');
expect(await pw.signupPage.agreementTermsOfUseLink).toBeFocused();
// * Should move focus to agreement privacy policy link after tab
await pw.signupPage.agreementTermsOfUseLink.press('Tab');
expect(await pw.signupPage.agreementPrivacyPolicyLink).toBeFocused();
// * Should move focus to privacy policy link after tab
await pw.signupPage.footer.aboutLink.press('Tab');
expect(await pw.signupPage.footer.privacyPolicyLink).toBeFocused();
// * Should move focus to terms link after tab
await pw.signupPage.footer.privacyPolicyLink.press('Tab');
expect(await pw.signupPage.footer.termsLink).toBeFocused();
// * Should move focus to help link after tab
await pw.signupPage.footer.termsLink.press('Tab');
expect(await pw.signupPage.footer.helpLink).toBeFocused();
// # Move focus to email input
await pw.signupPage.emailInput.focus();
expect(await pw.signupPage.emailInput).toBeFocused();
// * Should move focus to sign up body after shift+tab
await pw.signupPage.emailInput.press('Shift+Tab');
expect(await pw.signupPage.bodyCard).toBeFocused();
// * Should move focus to sign up body after shift+tab
await pw.signupPage.emailInput.press('Shift+Tab');
expect(await pw.signupPage.bodyCard).toBeFocused();
if (testInfo.project.name === 'ipad') {
// * Should move focus to header back button after shift+tab
await pw.signupPage.bodyCard.press('Shift+Tab');
expect(await pw.signupPage.header.backButton).toBeFocused();
// * Should move focus to log in link after shift+tab
await pw.signupPage.header.backButton.press('Shift+Tab');
expect(await pw.signupPage.loginLink).toBeFocused();
// * Should move focus to header logo after shift+tab
await pw.signupPage.loginLink.press('Shift+Tab');
expect(await pw.signupPage.header.logo).toBeFocused();
} else {
// * Should move focus to log in link after shift+tab
await pw.signupPage.bodyCard.press('Shift+Tab');
expect(await pw.signupPage.loginLink).toBeFocused();
// * Should move focus to header back button after shift+tab
await pw.signupPage.loginLink.press('Shift+Tab');
expect(await pw.signupPage.header.backButton).toBeFocused();
// * Should move focus to header logo after shift+tab
await pw.signupPage.header.backButton.press('Shift+Tab');
expect(await pw.signupPage.header.logo).toBeFocused();
}
});

25
e2e-tests/playwright/specs/client/schema.ts Обычный файл
Просмотреть файл

@@ -0,0 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {z} from 'zod';
const FileInfoSchema = z.object({
id: z.string(),
user_id: z.string(),
channel_id: z.string(),
create_at: z.number().int(),
update_at: z.number().int(),
delete_at: z.number().int(),
name: z.string(),
extension: z.string(),
size: z.number().int(),
mime_type: z.string(),
mini_preview: z.nullable(z.any()),
remote_id: z.string(),
archived: z.boolean(),
});
export const FileUploadResponseSchema = z.object({
file_infos: z.array(FileInfoSchema),
client_ids: z.array(z.string()),
});

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

@@ -0,0 +1,138 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Client4} from '@mattermost/client';
import {ServerChannel} from '@mattermost/types/channels';
import {FileUploadResponse} from '@mattermost/types/files';
import {Team} from '@mattermost/types/teams';
import {UserProfile} from '@mattermost/types/users';
import {expect, test, getFileFromAsset, getBlobFromAsset} from '@mattermost/playwright-lib';
import {FileUploadResponseSchema} from './schema';
let userClient: Client4;
let user: UserProfile;
let team: Team;
let townSquareChannel: ServerChannel;
const filename = 'mattermost-icon_128x128.png';
const file = getFileFromAsset(filename);
const blob = getBlobFromAsset(filename);
test.beforeEach(async ({pw}) => {
({userClient, user, team} = await pw.initSetup());
townSquareChannel = await userClient.getChannelByName(team.id, 'town-square');
});
test('should succeed with File', async ({pw}) => {
// # Prepare data with File
const clientId = pw.random.id();
const formData = new FormData();
formData.set('channel_id', townSquareChannel.id);
formData.set('client_ids', clientId);
formData.set('files', file, filename);
// # Do upload then validate the response
const data = await userClient.uploadFile(formData);
validateFileUploadResponse(data, clientId, user.id, townSquareChannel.id);
});
test('should succeed with Blob', async ({pw}) => {
// # Prepare data with Blob
const clientId = pw.random.id();
const formData = new FormData();
formData.set('channel_id', townSquareChannel.id);
formData.set('client_ids', clientId);
formData.set('files', blob, filename);
// # Do upload then validate the response
const data = await userClient.uploadFile(formData);
validateFileUploadResponse(data, clientId, user.id, townSquareChannel.id);
});
test('should succeed even with channel_id only', async () => {
// # Set without channel ID
const formData = new FormData();
formData.set('channel_id', townSquareChannel.id);
// # Do upload then validate the response
const data = await userClient.uploadFile(formData);
// * Validate that it doe snot throw an error
const validate = () => FileUploadResponseSchema.parse(data);
expect(validate).not.toThrow();
// * Validate that file_infos and client_ids are as expected
expect(data.client_ids).toMatchObject([]);
expect(data.file_infos.length).toBe(0);
});
test('should fail on invalid channel ID', async ({pw}) => {
const clientId = pw.random.id();
// # Set with invalid channel ID
let formData = new FormData();
formData.set('channel_id', 'invalid.channel.id');
formData.set('client_ids', clientId);
formData.set('files', file, filename);
await expect(userClient.uploadFile(formData)).rejects.toThrowError(
'Invalid or missing channel_id parameter in request URL.',
);
// # Set without channel ID
formData = new FormData();
formData.set('client_ids', clientId);
formData.set('files', file, filename);
await expect(userClient.uploadFile(formData)).rejects.toThrowError(
'Invalid or missing channel_id in request body.',
);
});
test('should fail on missing files', async ({pw}) => {
const clientId = pw.random.id();
// # Set with invalid channel ID
const formData = new FormData();
formData.set('channel_id', townSquareChannel.id);
formData.set('client_ids', clientId);
await expect(userClient.uploadFile(formData)).rejects.toThrowError(
'Unable to upload file(s). Have 1 client_ids for 0 files.',
);
});
test('should fail on incorrect order setting up FormData', async ({pw}) => {
const clientId = pw.random.id();
// # Set with files before client_ids
const formData = new FormData();
formData.set('channel_id', townSquareChannel.id);
formData.set('files', file, filename);
formData.set('client_ids', clientId);
await expect(userClient.uploadFile(formData)).rejects.toThrowError(
'Invalid or missing client_ids in request body.',
);
});
function validateFileUploadResponse(data: FileUploadResponse, clientId: string, userId: string, channelId: string) {
// * Validate the schema
const validate = () => FileUploadResponseSchema.parse(data);
expect(validate).not.toThrow();
// * Validate that file_infos and client_ids are as expected
expect(data.client_ids).toMatchObject([clientId]);
expect(data.file_infos.length).toBe(1);
// * Validate important contents of file_infos
const fileInfo = data.file_infos[0];
expect(fileInfo.user_id).toBe(userId);
expect(fileInfo.channel_id).toBe(channelId);
expect(fileInfo.delete_at).toBe(0);
expect(fileInfo.extension).toBe('png');
expect(fileInfo.mime_type).toBe('image/png');
expect(fileInfo.archived).toBe(false);
}

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

@@ -0,0 +1,72 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T53377 Profile popover should show correct fields after at-mention autocomplete', async ({pw}) => {
// # Initialize with specific config and get admin client
const {user, adminClient, team} = await pw.initSetup();
await adminClient.patchConfig({
PrivacySettings: {
ShowEmailAddress: false,
ShowFullName: false,
},
});
// # Create and add another user using admin client
const testUser2 = await adminClient.createUser(pw.random.user(), '', '');
await adminClient.addToTeam(team.id, testUser2.id);
// # Log in as user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Send mentions quickly
await channelsPage.centerView.postCreate.postMessage(`@${user.username} @${testUser2.username}`);
// # Open profile popover for current user
const firstMention = channelsPage.centerView.container.getByText(`@${user.username}`, {exact: true});
await firstMention.click();
// * Verify all fields are visible for current user
const popover = channelsPage.userProfilePopover;
await expect(popover.container.getByText(`@${user.username}`)).toBeVisible();
await expect(popover.container.getByText(`${user.first_name} ${user.last_name}`)).toBeVisible();
await expect(popover.container.getByText(user.email)).toBeVisible();
// # Close profile popover
await popover.close();
// # Open profile popover for other user
const secondMention = channelsPage.centerView.container.getByText(`@${testUser2.username}`, {exact: true});
await secondMention.click();
// * Verify only username is visible for other user
await expect(popover.container.getByText(`@${testUser2.username}`)).toBeVisible();
await expect(popover.container.getByText(testUser2.email)).not.toBeVisible();
// # Close profile popover
await popover.close();
// # Trigger autocomplete
await channelsPage.centerView.postCreate.writeMessage(`@${user.username}`);
// # Wait for autocomplete
const suggestionList = channelsPage.centerView.postCreate.suggestionList;
await expect(suggestionList.getByText(`@${user.username}`)).toBeVisible();
// # Clear textbox
await channelsPage.centerView.postCreate.writeMessage('');
// # Open profile popover for current user again
await firstMention.click();
// * Verify all fields are still visible
await expect(popover.container.getByText(`@${user.username}`)).toBeVisible();
await expect(popover.container.getByText(`${user.first_name} ${user.last_name}`)).toBeVisible();
await expect(popover.container.getByText(user.email)).toBeVisible();
});

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

@@ -0,0 +1,125 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T5435_1 Global Drafts link in sidebar should be hidden when another user deleted root post and user removes the deleted post ', async ({
pw,
}) => {
const {adminClient, team, adminUser, user} = await pw.initSetup();
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Get the default channel of the team for getting the channel id
const channel = await adminClient.getChannelByName(team.id, 'town-square');
// # Create a post in the channel by admin
const adminPost = await adminClient.createPost(
pw.random.post({
channel_id: channel.id,
user_id: adminUser.id,
}),
);
// # Log in as user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
const lastPostByAdmin = await channelsPage.centerView.getLastPost();
await lastPostByAdmin.toBeVisible();
// # Open the last post sent by admin in RHS
await lastPostByAdmin.hover();
await lastPostByAdmin.postMenu.toBeVisible();
await lastPostByAdmin.postMenu.reply();
// # Post a message as a user
const sidebarRight = channelsPage.sidebarRight;
await sidebarRight.toBeVisible();
await sidebarRight.postCreate.postMessage('Replying to a thread');
// # Write a message in the reply thread but don't send it now so that it becomes a draft
const draftMessageByUser = 'I should be in drafts by User';
await sidebarRight.postCreate.writeMessage(draftMessageByUser);
// # Close the RHS for draft to be saved
await sidebarRight.close();
// * Verify drafts link in channel sidebar is visible
await channelsPage.sidebarLeft.draftsVisible();
// # Delete the last post by admin
try {
await adminClient.deletePost(adminPost.id);
} catch (error) {
throw new Error(`Failed to delete post by admin: ${error}`);
}
// # Open the last post in the channel sent by admin again
await lastPostByAdmin.body.click();
// * Verify drafts in user's textbox is still visible
const rhsTextboxValue = await sidebarRight.postCreate.getInputValue();
expect(rhsTextboxValue).toBe(draftMessageByUser);
// # Click on remove post
const deletedPostByAdminInRHS = await sidebarRight.getPostById(adminPost.id);
await deletedPostByAdminInRHS.remove();
// * Verify the drafts links should also be removed from sidebar
await channelsPage.sidebarLeft.draftsNotVisible();
});
test('MM-T5435_2 Global Drafts link in sidebar should be hidden when user deletes root post ', async ({pw}) => {
const {user} = await pw.initSetup();
// # Log in as user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Post a message in the channel
await channelsPage.centerView.postCreate.postMessage('Message which will be deleted');
// # Start a thread by clicking on reply menuitem from post options menu
const post = await channelsPage.centerView.getLastPost();
await post.hover();
await post.postMenu.toBeVisible();
await post.postMenu.reply();
const sidebarRight = channelsPage.sidebarRight;
await sidebarRight.toBeVisible();
// # Post a message in the thread
await sidebarRight.postCreate.postMessage('Replying to a thread');
// # Write a message in the reply thread but don't send it
await sidebarRight.postCreate.writeMessage('I should be in drafts');
// # Close the RHS for draft to be saved
await sidebarRight.close();
// * Verify drafts link in channel sidebar is visible
await channelsPage.sidebarLeft.draftsVisible();
// # Click on the dot menu of the post and select delete
await post.hover();
await post.postMenu.toBeVisible();
await post.postMenu.openDotMenu();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.deleteMenuItem.click();
// # Confirm the delete from the modal
await channelsPage.deletePostModal.toBeVisible();
await channelsPage.deletePostModal.confirm();
// * Verify drafts link in channel sidebar is visible
await channelsPage.sidebarLeft.draftsNotVisible();
});

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

@@ -0,0 +1,354 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Page} from '@playwright/test';
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T5654_1 should be able to add attachments while editing a post', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const {user} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.hover();
await post.postMenu.toBeVisible();
// open the dot menu
await post.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.writeMessage('Edited message');
await channelsPage.centerView.postEdit.sendMessage();
const updatedPost = await channelsPage.centerView.getLastPost();
await updatedPost.toBeVisible();
await updatedPost.toContainText('Edited message');
});
test('MM-T5654_2 should be able to add attachments while editing a threaded post', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const {user} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.hover();
await post.postMenu.toBeVisible();
// open the dot menu
await post.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.replyMenuItem.click();
await channelsPage.sidebarRight.toBeVisible();
await channelsPage.sidebarRight.postCreate.toBeVisible();
await channelsPage.sidebarRight.postCreate.postMessage('Replying to the post');
await channelsPage.sidebarRight.toContainText('Replying to the post');
const replyPost = await channelsPage.sidebarRight.getLastPost();
await replyPost.toBeVisible();
await replyPost.hover();
await replyPost.postMenu.toBeVisible();
await replyPost.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.sidebarRight.postEdit.toBeVisible();
await channelsPage.sidebarRight.postEdit.writeMessage('Edited reply message');
await channelsPage.sidebarRight.postEdit.sendMessage();
let updatedReplyPost = await channelsPage.sidebarRight.getLastPost();
await updatedReplyPost.toBeVisible();
await updatedReplyPost.toContainText('Edited reply message');
// now we'll edit the reply post and files to it
await updatedReplyPost.hover();
await updatedReplyPost.postMenu.toBeVisible();
await updatedReplyPost.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.sidebarRight.postEdit.toBeVisible();
await channelsPage.sidebarRight.postEdit.writeMessage('Edited reply message with files');
await channelsPage.sidebarRight.postEdit.addFiles(['sample_text_file.txt', 'mattermost.png']);
await pw.wait(pw.duration.half_sec);
await channelsPage.sidebarRight.postEdit.sendMessage();
await pw.wait(pw.duration.half_sec);
await channelsPage.sidebarRight.postEdit.toNotBeVisible();
await updatedReplyPost.toBeVisible();
await updatedReplyPost.toContainText('Edited reply message with files');
await updatedReplyPost.toContainText('sample_text_file.txt');
await updatedReplyPost.toContainText('mattermost.png');
// now we'll remove the files
await updatedReplyPost.hover();
await updatedReplyPost.postMenu.toBeVisible();
await updatedReplyPost.postMenu.clickOnDotMenu();
await moveMouseToCenter(page);
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.sidebarRight.postEdit.toBeVisible();
await channelsPage.sidebarRight.postEdit.removeFile('sample_text_file.txt');
await pw.wait(pw.duration.half_sec);
await channelsPage.sidebarRight.postEdit.removeFile('mattermost.png');
await pw.wait(pw.duration.half_sec);
await channelsPage.sidebarRight.postEdit.sendMessage();
updatedReplyPost = await channelsPage.sidebarRight.getLastPost();
await updatedReplyPost.toBeVisible();
await updatedReplyPost.toContainText('Edited reply message with files');
expect(updatedReplyPost).not.toContain('sample_text_file.txt');
expect(updatedReplyPost).not.toContain('mattermost.png');
});
test('MM-T5654_3 should be able to edit post message originally containing files', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const {user} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage, ['sample_text_file.txt']);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.hover();
await post.postMenu.toBeVisible();
// open the dot menu
await post.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.writeMessage('Edited message');
await channelsPage.centerView.postEdit.sendMessage();
const updatedPost = await channelsPage.centerView.getLastPost();
await updatedPost.toBeVisible();
await updatedPost.toContainText('Edited message');
});
test('MM-T5654_4 should be able to add files when editing a post', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const {user} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.hover();
await post.postMenu.toBeVisible();
// open the dot menu
await post.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.writeMessage('Edited message');
await channelsPage.centerView.postEdit.addFiles(['sample_text_file.txt']);
await channelsPage.centerView.postEdit.sendMessage();
const updatedPost = await channelsPage.centerView.getLastPost();
await updatedPost.toBeVisible();
await updatedPost.toContainText('Edited message');
await updatedPost.toContainText('sample_text_file.txt');
// now we'll add multiple files
await post.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.addFiles(['mattermost.png', 'archive.zip']);
await channelsPage.centerView.postEdit.sendMessage();
const secondUpdatedPost = await channelsPage.centerView.getLastPost();
await secondUpdatedPost.toBeVisible();
await secondUpdatedPost.toContainText('Edited message');
await secondUpdatedPost.toContainText('sample_text_file.txt');
await secondUpdatedPost.toContainText('mattermost.png');
await secondUpdatedPost.toContainText('archive.zip');
});
test('MM-5654_5 should be able to remove attachments while editing a post', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const {user} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage, [
'sample_text_file.txt',
'mattermost.png',
'archive.zip',
]);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.toContainText(originalMessage);
await post.toContainText('sample_text_file.txt');
await post.toContainText('mattermost.png');
await post.toContainText('archive.zip');
await post.hover();
await post.postMenu.toBeVisible();
await post.postMenu.clickOnDotMenu();
await moveMouseToCenter(page);
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.removeFile('sample_text_file.txt');
await channelsPage.centerView.postEdit.sendMessage();
const updatedPost = await channelsPage.centerView.getLastPost();
await updatedPost.toBeVisible();
await updatedPost.toContainText(originalMessage);
await updatedPost.toContainText('mattermost.png');
await updatedPost.toContainText('archive.zip');
expect(updatedPost).not.toContain('archive.zip');
});
test('MM-T5655_1 removing message content and files should delete the post', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const {user} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage, ['sample_text_file.txt']);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.toContainText(originalMessage);
await post.toContainText('sample_text_file.txt');
await post.hover();
await post.postMenu.toBeVisible();
await post.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.removeFile('sample_text_file.txt');
await channelsPage.centerView.postEdit.writeMessage('');
await channelsPage.centerView.postEdit.sendMessage();
await channelsPage.centerView.postEdit.deleteConfirmationDialog.toBeVisible();
await channelsPage.centerView.postEdit.deleteConfirmationDialog.confirmDeletion();
await channelsPage.centerView.postEdit.deleteConfirmationDialog.notToBeVisible();
expect(channelsPage).not.toContain(originalMessage);
expect(channelsPage).not.toContain('sample_text_file.txt');
});
test('MM-T5655_2 should be able to remove all files when editing a post', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const {user} = await pw.initSetup();
const {channelsPage, page} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage, [
'sample_text_file.txt',
'mattermost.png',
'archive.zip',
]);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.toContainText(originalMessage);
await post.toContainText('sample_text_file.txt');
await post.toContainText('mattermost.png');
await post.toContainText('archive.zip');
await post.hover();
await post.postMenu.toBeVisible();
await post.postMenu.clickOnDotMenu();
await moveMouseToCenter(page);
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.removeFile('sample_text_file.txt');
await channelsPage.centerView.postEdit.removeFile('mattermost.png');
await channelsPage.centerView.postEdit.removeFile('archive.zip');
await channelsPage.centerView.postEdit.sendMessage();
const updatedPost = await channelsPage.centerView.getLastPost();
await updatedPost.toBeVisible();
await updatedPost.toContainText(originalMessage);
expect(updatedPost).not.toContain('archive.zip');
expect(updatedPost).not.toContain('mattermost.png');
expect(updatedPost).not.toContain('sample_text_file.txt');
});
test('MM-T5656_1 should be able to restore previously edited post version that contains attachments', async ({pw}) => {
const originalMessage = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit';
const newMessage = 'New Message';
const {user} = await pw.initSetup();
const {channelsPage} = await pw.testBrowser.login(user);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage(originalMessage, ['sample_text_file.txt']);
const post = await channelsPage.centerView.getLastPost();
await post.toBeVisible();
await post.toContainText(originalMessage);
await post.toContainText('sample_text_file.txt');
await post.hover();
await post.postMenu.toBeVisible();
await post.postMenu.dotMenuButton.click();
await channelsPage.postDotMenu.toBeVisible();
await channelsPage.postDotMenu.editMenuItem.click();
await channelsPage.centerView.postEdit.toBeVisible();
await channelsPage.centerView.postEdit.removeFile('sample_text_file.txt');
await channelsPage.centerView.postEdit.writeMessage(newMessage);
await channelsPage.centerView.postEdit.sendMessage();
const updatedPost = await channelsPage.centerView.getLastPost();
await updatedPost.toBeVisible();
await updatedPost.toContainText(newMessage);
expect(updatedPost).not.toContain('sample_text_file.txt');
const postID = await channelsPage.centerView.getLastPostID();
await channelsPage.centerView.clickOnLastEditedPost(postID);
await channelsPage.sidebarRight.toBeVisible();
await channelsPage.sidebarRight.verifyCurrentVersionPostMessage(postID, newMessage);
await channelsPage.sidebarRight.restorePreviousPostVersion();
await channelsPage.centerView.postEdit.restorePostConfirmationDialog.toBeVisible();
await channelsPage.centerView.postEdit.restorePostConfirmationDialog.confirmRestore();
await channelsPage.centerView.postEdit.restorePostConfirmationDialog.notToBeVisible();
const restoredPost = await channelsPage.centerView.getLastPost();
await restoredPost.toBeVisible();
expect(restoredPost.toContainText('sample_text_file.txt'));
});
async function moveMouseToCenter(page: Page) {
await page.mouse.move(0, 0);
}

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

@@ -0,0 +1,95 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test.fixme(
'MM-T5445 Should search, select and post correct Gif when Gif picker is opened from center textbox',
async ({pw}) => {
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Open emoji/gif picker
await channelsPage.centerView.postCreate.openEmojiPicker();
await channelsPage.emojiGifPickerPopup.toBeVisible();
// # Open gif tab
await channelsPage.emojiGifPickerPopup.openGifTab();
// # Search for gif
await channelsPage.emojiGifPickerPopup.searchGif('hello');
// # Select the first gif
const {img: firstSearchGifResult, alt: altOfFirstSearchGifResult} =
await channelsPage.emojiGifPickerPopup.getNthGif(0);
await firstSearchGifResult.click();
// # Send the selected gif as a message
await channelsPage.centerView.postCreate.sendMessage();
// * Verify that last message has the gif
const lastPost = await channelsPage.centerView.getLastPost();
await lastPost.toBeVisible();
await expect(lastPost.body.getByLabel('file thumbnail')).toHaveAttribute('alt', altOfFirstSearchGifResult);
},
);
test.fixme(
'MM-T5446 Should search, select and post correct Gif when Gif picker is opened from RHS textbox',
async ({pw}) => {
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Send a message
await channelsPage.centerView.postCreate.postMessage('Message to open RHS');
// # Open the last post sent in RHS
const lastPost = await channelsPage.centerView.getLastPost();
await lastPost.hover();
await lastPost.postMenu.toBeVisible();
await lastPost.postMenu.reply();
const sidebarRight = channelsPage.sidebarRight;
await sidebarRight.toBeVisible();
// # Send a message in the thread
await sidebarRight.postCreate.toBeVisible();
await sidebarRight.postCreate.writeMessage('Replying to a thread');
await sidebarRight.postCreate.sendMessage();
// # Open emoji/gif picker
await sidebarRight.postCreate.openEmojiPicker();
await channelsPage.emojiGifPickerPopup.toBeVisible();
// # Open gif tab
await channelsPage.emojiGifPickerPopup.openGifTab();
// # Search for gif
await channelsPage.emojiGifPickerPopup.searchGif('hello');
// # Select the first gif
const {img: firstSearchGifResult, alt: altOfFirstSearchGifResult} =
await channelsPage.emojiGifPickerPopup.getNthGif(0);
await firstSearchGifResult.click();
// # Send the selected gif as a message in the thread
await sidebarRight.postCreate.sendMessage();
// * Verify that last message has the gif
const lastPostInRHS = await sidebarRight.getLastPost();
await lastPostInRHS.toBeVisible();
await expect(lastPostInRHS.body.getByLabel('file thumbnail')).toHaveAttribute('alt', altOfFirstSearchGifResult);
},
);

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

@@ -0,0 +1,48 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T5139: Message Priority - Standard message priority and system setting', async ({pw}) => {
// # Setup test environment
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// Open menu
await channelsPage.centerView.postCreate.openPriorityMenu();
// Use messagePriority for dialog interactions
await channelsPage.messagePriority.verifyPriorityDialog();
await channelsPage.messagePriority.verifyStandardOptionSelected();
// # Close menu and post message
await channelsPage.messagePriority.closePriorityMenu();
const testMessage = 'This is just a test message';
await channelsPage.postMessage(testMessage);
// # Verify message posts without priority label
const lastPost = await channelsPage.centerView.getLastPost();
await lastPost.toBeVisible();
await lastPost.toContainText(testMessage);
await expect(lastPost.container.locator('.post-priority')).not.toBeVisible();
// # Open post in RHS and verify
await lastPost.container.click();
await channelsPage.sidebarRight.toBeVisible();
// # Get RHS post and verify content
const rhsPost = await channelsPage.sidebarRight.getLastPost();
await rhsPost.toBeVisible();
await rhsPost.toContainText(testMessage);
await expect(rhsPost.container.locator('.post-priority')).not.toBeVisible();
// # Verify RHS formatting bar doesn't have priority button
await expect(channelsPage.sidebarRight.postCreate.priorityButton).not.toBeVisible();
});

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

@@ -0,0 +1,56 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T483 Channel-wide mentions with uppercase letters', async ({pw, headless, browserName}) => {
test.skip(
headless && browserName !== 'firefox',
'Works across browsers and devices, except in headless mode, where stubbing the Notification API is supported only in Firefox and WebKit.',
);
// Initialize setup and get the required users and team
const {team, adminUser, user} = await pw.initSetup();
// Log in as the admin in one browser session and navigate to the "town-square" channel
const {page: adminPage, channelsPage: adminChannelsPage} = await pw.testBrowser.login(adminUser);
await adminChannelsPage.goto(team.name, 'town-square');
await adminChannelsPage.toBeVisible();
// Stub the Notification in the admin's browser to capture notifications
await pw.stubNotification(adminPage, 'granted');
// Log in as the regular user in a separate browser and navigate to the "off-topic" channel
const {channelsPage: otherChannelsPage} = await pw.testBrowser.login(user);
await otherChannelsPage.goto(team.name, 'off-topic');
await otherChannelsPage.toBeVisible();
// Post a channel-wide mention message "@ALL" in uppercase from the user's browser
const message = `@ALL good morning, ${team.name}!`;
await otherChannelsPage.postMessage(message);
// Wait for a notification to be received in the admin's browser and verify its content
const notifications = await pw.waitForNotification(adminPage);
expect(notifications.length).toBe(1);
const notification = notifications[0];
expect(notification.title).toBe('Off-Topic');
expect(notification.body).toBe(`@${user.username}: ${message}`);
expect(notification.tag).toBe(`@${user.username}: ${message}`);
expect(notification.icon).toContain('.png');
expect(notification.requireInteraction).toBe(false);
expect(notification.silent).toBe(false);
// Verify the last post as viewed by the regular user in the "off-topic" channel contains the message and is highlighted
const otherLastPost = await otherChannelsPage.centerView.getLastPost();
await otherLastPost.toContainText(message);
await expect(otherLastPost.container.locator('.mention--highlight')).toBeVisible();
await expect(otherLastPost.container.locator('.mention--highlight').getByText('@ALL')).toBeVisible();
// Admin navigates to the "off-topic" channel and verifies the message is posted and highlighted correctly
await adminChannelsPage.goto(team.name, 'off-topic');
const adminLastPost = await adminChannelsPage.centerView.getLastPost();
await adminLastPost.toContainText(message);
await expect(adminLastPost.container.locator('.mention--highlight')).toBeVisible();
await expect(adminLastPost.container.locator('.mention--highlight').getByText('@ALL')).toBeVisible();
});

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

@@ -0,0 +1,411 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Page} from '@playwright/test';
import {expect, PlaywrightExtended, test} from '@mattermost/playwright-lib';
import type {ChannelsPage, ScheduledDraftPage} from '@mattermost/playwright-lib';
test.skip('MM-T5643_1 should create a scheduled message from a channel', async ({pw}) => {
test.setTimeout(pw.duration.four_min);
const draftMessage = 'Scheduled Draft';
// # Skip test if no license
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
const {page, channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
await setupChannelPage(channelsPage, draftMessage);
await scheduleMessage(channelsPage);
await channelsPage.centerView.verifyscheduledDraftChannelInfo();
const postBoxIndicator = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await verifyScheduledDraft(channelsPage, scheduledDraftPage, draftMessage, postBoxIndicator);
// # Hover and verify options
await scheduledDraftPage.verifyOnHoverActionItems(draftMessage);
// # Go back and wait for message to arrive
await goBackToChannelAndWaitForMessageToArrive(pw, page);
// * Verify the message has been sent and there's no more scheduled messages
await expect(channelsPage.centerView.scheduledDraftChannelInfoMessage).not.toBeVisible();
await expect(channelsPage.sidebarLeft.scheduledDraftCountonLHS).not.toBeVisible();
await expect(await channelsPage.getLastPost()).toHaveText(draftMessage);
await channelsPage.sidebarLeft.assertNoPendingScheduledDraft();
});
test('MM-T5643_6 should create a scheduled message under a thread post ', async ({pw}) => {
const draftMessage = 'Scheduled Threaded Message';
// # Skip test if no license
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage('Root Message');
// # Start a thread by clicking on reply menuitem from post options menu
const post = await channelsPage.centerView.getLastPost();
await replyToLastPost(post);
const sidebarRight = channelsPage.sidebarRight;
await sidebarRight.toBeVisible();
// # Post a message in the thread
await sidebarRight.postCreate.postMessage('Replying to a thread');
// # Write a message in the reply thread but don't send it
await sidebarRight.postCreate.writeMessage(draftMessage);
await expect(sidebarRight.postCreate.input).toHaveText(draftMessage);
await sidebarRight.postCreate.scheduleDraftMessageButton.isVisible();
await sidebarRight.postCreate.scheduleDraftMessageButton.click();
await scheduleMessage(channelsPage);
await sidebarRight.postBoxIndicator.isVisible();
const messageLocator = sidebarRight.scheduledDraftChannelInfoMessage.first();
await expect(messageLocator).toContainText('Message scheduled for');
// Save the time displayed in the thread
const scheduledDraftThreadedPanelInfo = await sidebarRight.postBoxIndicator.innerText();
await channelsPage.sidebarRight.clickOnSeeAllscheduledDrafts();
const scheduledDraftPageInfo = await scheduledDraftPage.scheduledDraftPageInfo.innerHTML();
await channelsPage.sidebarLeft.assertscheduledDraftCountLHS('1');
await scheduledDraftPage.toBeVisible();
await scheduledDraftPage.assertBadgeCountOnTab('1');
await scheduledDraftPage.assertscheduledDraftBody(draftMessage);
await compareMessageTimestamps(scheduledDraftThreadedPanelInfo, scheduledDraftPageInfo, scheduledDraftPage);
// # Hover and verify options
await scheduledDraftPage.verifyOnHoverActionItems(draftMessage);
await scheduledDraftPage.sendScheduledMessage(draftMessage);
await sidebarRight.toBeVisible();
(await sidebarRight.getLastPost()).toContainText(draftMessage);
await expect(channelsPage.sidebarRight.scheduledDraftChannelInfoMessage.first()).not.toBeVisible();
await expect(channelsPage.sidebarLeft.scheduledDraftCountonLHS).not.toBeVisible();
await channelsPage.sidebarLeft.assertNoPendingScheduledDraft();
});
test('MM-T5644 should reschedule a scheduled message', async ({pw}) => {
const draftMessage = 'Scheduled Draft';
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
const {channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
await setupChannelPage(channelsPage, draftMessage);
await scheduleMessage(channelsPage);
// * Verify the Initial Date and time of scheduled Draft
await channelsPage.centerView.verifyscheduledDraftChannelInfo();
const postBoxIndicator = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await verifyScheduledDraft(channelsPage, scheduledDraftPage, draftMessage, postBoxIndicator);
await scheduledDraftPage.openRescheduleModal(draftMessage);
// # Reschedule it to 2 days from today
await channelsPage.scheduledDraftModal.selectDay(2);
await channelsPage.scheduledDraftModal.confirm();
// # Note the new Scheduled time
const scheduledDraftPageInfo = await scheduledDraftPage.getTimeStampOfMessage(draftMessage);
// # Go to Channel
await channelsPage.goto();
// * Verify the New Time reflecting in the channel
const rescheduledDraftChannelInfo = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await compareMessageTimestamps(rescheduledDraftChannelInfo, scheduledDraftPageInfo, scheduledDraftPage);
});
test('MM-T5645 should delete a scheduled message', async ({pw}) => {
const draftMessage = 'Scheduled Draft';
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
const {channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
await setupChannelPage(channelsPage, draftMessage);
await scheduleMessage(channelsPage);
const postBoxIndicator = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await verifyScheduledDraft(channelsPage, scheduledDraftPage, draftMessage, postBoxIndicator);
await scheduledDraftPage.deleteScheduledMessage(draftMessage);
await expect(scheduledDraftPage.scheduledDraftPanel(draftMessage)).not.toBeVisible();
await expect(scheduledDraftPage.noscheduledDraftIcon).toBeVisible();
});
test('MM-T5643_9 should send a scheduled message immediately', async ({pw}) => {
const draftMessage = 'Scheduled Draft';
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
const {channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
await setupChannelPage(channelsPage, draftMessage);
await scheduleMessage(channelsPage);
const postBoxIndicator = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await verifyScheduledDraft(channelsPage, scheduledDraftPage, draftMessage, postBoxIndicator);
await scheduledDraftPage.sendScheduledMessage(draftMessage);
await pw.wait(pw.duration.two_sec);
await expect(scheduledDraftPage.scheduledDraftPanel(draftMessage)).not.toBeVisible();
// Verify message has arrived
await expect(channelsPage.centerView.scheduledDraftChannelInfoMessage).not.toBeVisible();
await expect(channelsPage.sidebarLeft.scheduledDraftCountonLHS).not.toBeVisible();
await expect(await channelsPage.getLastPost()).toHaveText(draftMessage);
});
test('MM-T5643_3 should create a scheduled message from a DM', async ({pw}) => {
const draftMessage = 'Scheduled Draft';
// # Skip test if no license
await pw.skipIfNoLicense();
const {user, team} = await pw.initSetup();
const {user: user2} = await pw.initSetup();
const {page, channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
await setupChannelPage(channelsPage, draftMessage, team.name, `@${user2.username}`);
await scheduleMessage(channelsPage);
await channelsPage.centerView.verifyscheduledDraftChannelInfo();
const postBoxIndicator = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await verifyScheduledDraft(channelsPage, scheduledDraftPage, draftMessage, postBoxIndicator);
// # Hover and verify options
await scheduledDraftPage.verifyOnHoverActionItems(draftMessage);
await scheduledDraftPage.sendScheduledMessage(draftMessage);
await page.waitForSelector(channelsPage.centerView.scheduledDraftChannelInfoMessageLocator, {state: 'hidden'});
// * Verify the message has been sent and there's no more scheduled messages
await expect(channelsPage.centerView.scheduledDraftChannelInfoMessage).not.toBeVisible();
await expect(channelsPage.sidebarLeft.scheduledDraftCountonLHS).not.toBeVisible();
await expect(await channelsPage.getLastPost()).toHaveText(draftMessage);
await channelsPage.sidebarLeft.assertNoPendingScheduledDraft();
});
test('MM-T5648 should create a draft and then schedule it', async ({pw}) => {
const draftMessage = 'Draft to be Scheduled';
await pw.skipIfNoLicense();
const {user, team} = await pw.initSetup();
const {channelsPage, draftPage, scheduledDraftPage} = await pw.testBrowser.login(user);
// await setupChannelPage(channelsPage, draftMessage);
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.writeMessage(draftMessage);
// go to drafts page
await draftPage.goTo(team.name);
await draftPage.toBeVisible();
await draftPage.assertBadgeCountOnTab('1');
await draftPage.assertDraftBody(draftMessage);
await draftPage.verifyScheduleIcon(draftMessage);
await draftPage.openScheduleModal(draftMessage);
// # Reschedule it to 2 days from today
await channelsPage.scheduledDraftModal.selectDay(2);
await channelsPage.scheduledDraftModal.confirm();
await scheduledDraftPage.goTo(team.name);
await scheduledDraftPage.toBeVisible();
await scheduledDraftPage.assertBadgeCountOnTab('1');
await scheduledDraftPage.assertscheduledDraftBody(draftMessage);
});
test('MM-T5644 should edit scheduled message', async ({pw}) => {
const draftMessage = 'Scheduled Draft';
// # Skip test if no license
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
const {page, channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
await setupChannelPage(channelsPage, draftMessage);
await scheduleMessage(channelsPage);
await channelsPage.centerView.verifyscheduledDraftChannelInfo();
const postBoxIndicator = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await verifyScheduledDraft(channelsPage, scheduledDraftPage, draftMessage, postBoxIndicator);
// # Hover and verify options
await scheduledDraftPage.verifyOnHoverActionItems(draftMessage);
const updatedText = 'updated text';
await scheduledDraftPage.editText(updatedText);
await scheduledDraftPage.sendScheduledMessage(updatedText);
// * Verify the message has been sent and there's no more scheduled messages
await page.waitForSelector(channelsPage.centerView.scheduledDraftChannelInfoMessageLocator, {state: 'hidden'});
await expect(channelsPage.centerView.scheduledDraftChannelInfoMessage).not.toBeVisible();
await expect(channelsPage.sidebarLeft.scheduledDraftCountonLHS).not.toBeVisible();
await expect(await channelsPage.getLastPost()).toHaveText(updatedText);
await channelsPage.sidebarLeft.assertNoPendingScheduledDraft();
});
test('MM-T5650 should copy scheduled message', async ({pw, browserName}) => {
// Skip this test in Firefox clipboard permissions are not supported
test.skip(browserName === 'firefox', 'Test not supported in Firefox');
// # Skip test if no license
await pw.skipIfNoLicense();
const draftMessage = 'Scheduled Draft';
const {user} = await pw.initSetup();
const {page, channelsPage, scheduledDraftPage} = await pw.testBrowser.login(user);
await setupChannelPage(channelsPage, draftMessage);
await scheduleMessage(channelsPage);
await channelsPage.centerView.verifyscheduledDraftChannelInfo();
const postBoxIndicator = await channelsPage.centerView.scheduledDraftChannelInfoMessageText.innerText();
await verifyScheduledDraft(channelsPage, scheduledDraftPage, draftMessage, postBoxIndicator);
await scheduledDraftPage.copyScheduledMessage(draftMessage);
await page.goBack();
await channelsPage.centerView.postCreate.input.focus();
await page.keyboard.down('ControlOrMeta');
await page.keyboard.press('V');
await page.keyboard.up('ControlOrMeta');
// * Assert the message typed is same as the copied message
await expect(channelsPage.centerView.postCreate.input).toHaveText(draftMessage);
});
async function goBackToChannelAndWaitForMessageToArrive(pw: PlaywrightExtended, page: Page): Promise<void> {
await page.goBack();
await pw.wait(pw.duration.two_min);
await page.reload();
}
async function replyToLastPost(post: any): Promise<void> {
await post.hover();
await post.postMenu.toBeVisible();
await post.postMenu.reply();
}
async function setupChannelPage(
channelsPage: ChannelsPage,
draftMessage: string,
teamName?: string,
channelName?: string,
): Promise<void> {
await channelsPage.goto(teamName, channelName);
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.writeMessage(draftMessage);
await channelsPage.centerView.postCreate.clickOnScheduleDraftDropdownButton();
}
/**
* Schedules a draft message by selecting a custom time and confirming.
*/
async function scheduleMessage(channelsPage: ChannelsPage): Promise<void> {
await channelsPage.scheduledDraftDropdown.toBeVisible();
await channelsPage.scheduledDraftDropdown.selectCustomTime();
await channelsPage.scheduledDraftModal.toBeVisible();
await channelsPage.scheduledDraftModal.selectDay();
await channelsPage.scheduledDraftModal.selectTime();
await channelsPage.scheduledDraftModal.confirm();
}
/**
* Extracts and verifies the scheduled message on the scheduled page and in the channel.
*/
async function verifyScheduledDraft(
channelsPage: ChannelsPage,
scheduledDraftPage: ScheduledDraftPage,
draftMessage: string,
postBoxIndicator: string,
): Promise<void> {
await verifyscheduledDraftCount(channelsPage, '1');
await scheduledDraftPage.toBeVisible();
await scheduledDraftPage.assertBadgeCountOnTab('1');
await scheduledDraftPage.assertscheduledDraftBody(draftMessage);
const scheduledDraftPageInfo = await scheduledDraftPage.getTimeStampOfMessage(draftMessage);
await compareMessageTimestamps(postBoxIndicator, scheduledDraftPageInfo, scheduledDraftPage);
}
/**
* Verifies the scheduled message count on the sidebar.
*/
async function verifyscheduledDraftCount(page: ChannelsPage, expectedCount: string): Promise<void> {
await page.centerView.clickOnSeeAllscheduledDrafts();
await page.sidebarLeft.assertscheduledDraftCountLHS(expectedCount);
}
/**
* Compares the time in the channel and the scheduled page to ensure consistency.
*/
async function compareMessageTimestamps(
timeInChannel: string,
scheduledDraftPageInfo: string,
scheduledDraftPage: ScheduledDraftPage,
): Promise<void> {
// Extract time from channel using the same date pattern
const matchedTimeInChannel = timeInChannel.match(scheduledDraftPage.datePattern);
const timeInSchedulePage = extractTimeFromHtml(scheduledDraftPageInfo, scheduledDraftPage);
if (!matchedTimeInChannel || !timeInSchedulePage) {
throw new Error('Could not extract date and time from one or both elements.');
}
const firstElementTime = matchedTimeInChannel[0];
const secondElementTime = timeInSchedulePage[0];
// Compare extracted times
expect(firstElementTime).toBe(secondElementTime);
}
/**
* Removes HTML tags from the scheduled message content and extracts the time pattern.
*/
function extractTimeFromHtml(htmlContent: string, scheduledDraftPage: ScheduledDraftPage): RegExpMatchArray | null {
// Remove all HTML tags and match the expected time pattern using the datePattern from scheduledDraftPage
const cleanedText = htmlContent.replace(/<\/?[^>]+(>|$)/g, '');
// Use the datePattern to extract the exact match for time
const matchedTime = cleanedText.match(scheduledDraftPage.datePattern);
return matchedTime;
}

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

@@ -0,0 +1,55 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T5424 Find channel search returns only 50 results when there are more than 50 channels with similar names', async ({
pw,
}) => {
const {adminClient, user, team} = await pw.initSetup();
const commonName = 'test_channel';
// # Create more than 50 channels with similar names
const channelsRes = [];
for (let i = 0; i < 100; i++) {
let suffix = i.toString();
if (i < 10) {
suffix = `0${i}`;
}
const channel = pw.random.channel({
teamId: team.id,
name: `${commonName}_${suffix}`,
displayName: `Test Channel ${suffix}`,
});
channelsRes.push(adminClient.createChannel(channel));
}
await Promise.all(channelsRes);
// # Log in a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Click on "Find channel" and type "test_channel"
await channelsPage.sidebarLeft.findChannelButton.click();
await channelsPage.findChannelsModal.toBeVisible();
await channelsPage.findChannelsModal.input.fill(commonName);
const limitCount = 50;
// # Only 50 results for similar name should be displayed.
await expect(channelsPage.findChannelsModal.searchList).toHaveCount(limitCount);
for (let i = 0; i < limitCount; i++) {
let suffix = i.toString();
if (i < 10) {
suffix = `0${i}`;
}
await expect(channelsPage.findChannelsModal.container.getByTestId(`${commonName}_${suffix}`)).toBeVisible();
}
});

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

@@ -0,0 +1,59 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('Search box suggestion must be case insensitive', async ({pw}) => {
const {user} = await pw.initSetup();
// # Log in a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Open the search UI
await channelsPage.globalHeader.openSearch();
const searchWord = 'off';
const searchOutput = 'In:off-topic';
const channelName = 'Off-Topic';
// Should work as expected when using lowercase
// # Type in lowercase "off" to search for the "Off-Topic" channel
const {searchInput} = channelsPage.searchPopover;
await searchInput.pressSequentially(`In:${searchWord}`);
// * The suggestion should be visible
await expect(channelsPage.searchPopover.selectedSuggestion).toBeVisible();
await expect(channelsPage.searchPopover.selectedSuggestion).toHaveText(channelName);
// # Press Enter to select the suggestion and another Enter to search
await searchInput.press('Enter');
await searchInput.press('Enter');
// * The search box should contain the selected suggestion
await expect(channelsPage.globalHeader.searchBox.getByText(searchOutput, {exact: true})).toBeVisible();
// Should work as expected when using uppercase
// # Open the search bar
await channelsPage.globalHeader.openSearch();
// # Clear its content
await channelsPage.searchPopover.clearIfPossible();
// # Type in uppercase "OFF" to search for the "Off-Topic" channel
await searchInput.pressSequentially(`In:${searchWord.toUpperCase()}`);
// * The suggestion should be visible
await expect(channelsPage.searchPopover.selectedSuggestion).toBeVisible();
await expect(channelsPage.searchPopover.selectedSuggestion).toHaveText(channelName);
// # Press Enter to select the suggestion and another Enter to search
await searchInput.press('Enter');
await searchInput.press('Enter');
// * The search box should contain the selected suggestion
await expect(channelsPage.globalHeader.searchBox.getByText(searchOutput, {exact: true})).toBeVisible();
});

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

@@ -0,0 +1,89 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('team selector must show all my teams', async ({pw}) => {
const {adminClient, user, team} = await pw.initSetup();
// # create 2 more teams and add the user to them
const teams = [team];
for (let i = 0; i < 2; i++) {
const newTeam = await adminClient.createTeam(pw.random.team('team', 'Team', 'O', true));
await adminClient.addUsersToTeam(newTeam.id, [user.id]);
teams.push(newTeam);
}
// # Log in a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit a default channel page
await channelsPage.goto(team.name);
await channelsPage.toBeVisible();
// # Open the search UI
await channelsPage.globalHeader.openSearch();
// * Check that the team selector is visible
const page = channelsPage.page;
await expect(page.getByTestId('searchTeamsSelectorMenuButton')).toBeVisible();
// # Click on the team selector
await page.getByTestId('searchTeamsSelectorMenuButton').click();
// * Check that the team selector is visible
const teamSelector = page.getByRole('menu', {name: 'Select team'});
await expect(teamSelector).toBeVisible();
// * Check that the team selector has the 3 teams
teams.forEach(async (t) => {
await expect(teamSelector.getByText(t.display_name)).toBeVisible();
});
// * Check that All teams is also visible
await expect(teamSelector.getByText('All teams')).toBeVisible();
// * No <input> should be visible in the menu
await expect(teamSelector.getByLabel('Search teams')).not.toBeVisible();
// now create and join 3 more teams
for (let i = 0; i < 3; i++) {
const newTeam = await adminClient.createTeam(pw.random.team('team', 'Team', 'O', true));
await adminClient.addUsersToTeam(newTeam.id, [user.id]);
teams.push(newTeam);
}
// refresh the page
await channelsPage.goto(team.name);
// # Open the search UI
await channelsPage.globalHeader.openSearch();
// # Click on the team selector
await page.getByTestId('searchTeamsSelectorMenuButton').click();
// * Check that the team selector is visible
await expect(teamSelector).toBeVisible();
// * Check that the team selector has the 6 teams
teams.forEach(async (t) => {
await expect(teamSelector.getByText(t.display_name)).toBeVisible();
});
// * Check that All teams is also visible
await expect(teamSelector.getByText('All teams')).toBeVisible();
// because there's more than 4 teams, the filter input should be visible
await expect(teamSelector.getByLabel('Search teams')).toBeVisible();
// # Type the name of the first team
await page.getByLabel('Search teams').fill(teams[3].display_name);
// * Check that the team selector is visible
await expect(teamSelector).toBeVisible();
// * Noew team [0] and [3] should be visible - 0 is visible because it was currently selected.
await expect(teamSelector.getByText(teams[0].display_name)).toBeVisible();
await expect(teamSelector.getByText(teams[3].display_name)).toBeVisible();
// * Check that All teams is also visible
await expect(teamSelector.getByText('All teams')).toBeVisible();
// * Check that the other teams are not visible
teams.slice(1, 3).forEach(async (t) => {
await expect(teamSelector.getByText(t.display_name)).not.toBeVisible();
});
});

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

@@ -0,0 +1,279 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
let keywords: string[];
const highlightWithoutNotificationClass = 'non-notification-highlight';
test.beforeAll(async ({pw}) => {
keywords = [`AB${pw.random.id()}`, `CD${pw.random.id()}`, `EF${pw.random.id()}`, `Highlight me ${pw.random.id()}`];
});
test('MM-T5465-1 Should add the keyword when enter, comma or tab is pressed on the textbox', async ({pw}) => {
// # Skip test if no license
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
await channelsPage.centerView.postCreate.postMessage('Hello World');
// # Open settings modal
await channelsPage.globalHeader.openSettings();
await channelsPage.settingsModal.toBeVisible();
// # Open notifications tab
await channelsPage.settingsModal.openNotificationsTab();
// # Open keywords that get highlighted section
await channelsPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight');
const keywordsInput = await channelsPage.settingsModal.notificationsSettings.getKeywordsInput();
// # Enter keyword 1
await keywordsInput.fill(keywords[0]);
// # Press Comma on the textbox
await keywordsInput.press(',');
// # Enter keyword 2
await keywordsInput.fill(keywords[1]);
// # Press Tab on the textbox
await keywordsInput.press('Tab');
// # Enter keyword 3
await keywordsInput.fill(keywords[2]);
// # Press Enter on the textbox
await keywordsInput.press('Enter');
// * Verify that the keywords have been added to the collapsed description
const keysWithHighlightDesc = channelsPage.settingsModal.notificationsSettings.keysWithHighlightDesc;
await keysWithHighlightDesc.waitFor();
for (const keyword of keywords.slice(0, 3)) {
expect(await keysWithHighlightDesc).toContainText(keyword);
}
});
test('MM-T5465-2 Should highlight the keywords when a message is sent with the keyword in center', async ({pw}) => {
// # Skip test if no license
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Open settings modal
await channelsPage.globalHeader.openSettings();
await channelsPage.settingsModal.toBeVisible();
// # Open notifications tab
await channelsPage.settingsModal.openNotificationsTab();
// # Open keywords that get highlighted section
await channelsPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight');
// # Enter the keyword
const keywordsInput = await channelsPage.settingsModal.notificationsSettings.getKeywordsInput();
await keywordsInput.fill(keywords[3]);
await keywordsInput.press('Tab');
// # Save the keyword
await channelsPage.settingsModal.notificationsSettings.save();
// # Close the settings modal
await channelsPage.settingsModal.closeModal();
// # Post a message without the keyword
const messageWithoutKeyword = 'This message does not contain the keyword';
await channelsPage.centerView.postCreate.postMessage(messageWithoutKeyword);
const lastPostWithoutHighlight = await channelsPage.centerView.getLastPost();
// * Verify that the keywords are not highlighted
await expect(lastPostWithoutHighlight.container.getByText(messageWithoutKeyword)).toBeVisible();
await expect(lastPostWithoutHighlight.container.getByText(messageWithoutKeyword)).not.toHaveClass(
highlightWithoutNotificationClass,
);
// # Post a message with the keyword
const messageWithKeyword = `This message contains the keyword ${keywords[3]}`;
await channelsPage.centerView.postCreate.postMessage(messageWithKeyword);
const lastPostWithHighlight = await channelsPage.centerView.getLastPost();
// * Verify that the keywords are highlighted
await expect(lastPostWithHighlight.container.getByText(messageWithKeyword)).toBeVisible();
await expect(lastPostWithHighlight.container.getByText(keywords[3])).toHaveClass(highlightWithoutNotificationClass);
});
test('MM-T5465-3 Should highlight the keywords when a message is sent with the keyword in rhs', async ({pw}) => {
// # Skip test if no license
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Open settings modal
await channelsPage.globalHeader.openSettings();
await channelsPage.settingsModal.toBeVisible();
// # Open notifications tab
await channelsPage.settingsModal.openNotificationsTab();
// # Open keywords that get highlighted section
await channelsPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight');
// # Enter the keyword
const keywordsInput = await channelsPage.settingsModal.notificationsSettings.getKeywordsInput();
await keywordsInput.fill(keywords[3]);
await keywordsInput.press('Tab');
// # Save the keyword
await channelsPage.settingsModal.notificationsSettings.save();
// # Close the settings modal
await channelsPage.settingsModal.closeModal();
// # Post a message without the keyword
const messageWithoutKeyword = 'This message does not contain the keyword';
await channelsPage.centerView.postCreate.postMessage(messageWithoutKeyword);
const lastPostWithoutHighlight = await channelsPage.centerView.getLastPost();
// # Open the message in the RHS
await lastPostWithoutHighlight.hover();
await lastPostWithoutHighlight.postMenu.toBeVisible();
await lastPostWithoutHighlight.postMenu.reply();
await channelsPage.sidebarRight.toBeVisible();
// # Post a message with the keyword in the RHS
const messageWithKeyword = `This message contains the keyword ${keywords[3]}`;
await channelsPage.sidebarRight.postCreate.postMessage(messageWithKeyword);
// * Verify that the keywords are highlighted
const lastPostWithHighlightInRHS = await channelsPage.sidebarRight.getLastPost();
await expect(lastPostWithHighlightInRHS.container.getByText(messageWithKeyword)).toBeVisible();
await expect(lastPostWithHighlightInRHS.container.getByText(keywords[3])).toHaveClass(
highlightWithoutNotificationClass,
);
});
test('MM-T5465-4 Highlighted keywords should not appear in the Recent Mentions', async ({pw}) => {
// # Skip test if no license
await pw.skipIfNoLicense();
const {user} = await pw.initSetup();
// # Log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Open settings modal
await channelsPage.globalHeader.openSettings();
await channelsPage.settingsModal.toBeVisible();
// # Open notifications tab
await channelsPage.settingsModal.openNotificationsTab();
// # Open keywords that get highlighted section
await channelsPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight');
// # Enter the keyword
const keywordsInput = await channelsPage.settingsModal.notificationsSettings.getKeywordsInput();
await keywordsInput.fill(keywords[0]);
await keywordsInput.press('Tab');
// # Save the keyword
await channelsPage.settingsModal.notificationsSettings.save();
// # Close the settings modal
await channelsPage.settingsModal.closeModal();
// # Open the recent mentions
await channelsPage.globalHeader.openRecentMentions();
// * Verify recent mentions is empty
await channelsPage.sidebarRight.toBeVisible();
await expect(channelsPage.sidebarRight.container.getByText('No mentions yet')).toBeVisible();
});
test('MM-T5465-5 Should highlight keywords in message sent from another user', async ({pw}) => {
// # Skip test if no license
await pw.skipIfNoLicense();
const {adminClient, team, adminUser, user} = await pw.initSetup();
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Get the default channel of the team for getting the channel id
const channel = await adminClient.getChannelByName(team.id, 'town-square');
const highlightKeyword = keywords[0];
const messageWithKeyword = `This received message contains the ${highlightKeyword} keyword `;
// # Create a post containing the keyword in the channel by admin
await adminClient.createPost(
pw.random.post({
message: messageWithKeyword,
channel_id: channel.id,
user_id: adminUser.id,
}),
);
// # Now log in as a user in new browser context
const {channelsPage} = await pw.testBrowser.login(user);
// # Visit default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// # Open settings modal
await channelsPage.globalHeader.openSettings();
await channelsPage.settingsModal.toBeVisible();
// # Open notifications tab
await channelsPage.settingsModal.openNotificationsTab();
// # Open keywords that get highlighted section
await channelsPage.settingsModal.notificationsSettings.expandSection('keysWithHighlight');
// # Enter the keyword
const keywordsInput = await channelsPage.settingsModal.notificationsSettings.getKeywordsInput();
await keywordsInput.fill(keywords[0]);
await keywordsInput.press('Tab');
// # Save the keyword
await channelsPage.settingsModal.notificationsSettings.save();
// # Close the settings modal
await channelsPage.settingsModal.closeModal();
// * Verify that the keywords are highlighted in the last message received
const lastPostWithHighlight = await channelsPage.centerView.getLastPost();
await expect(lastPostWithHighlight.container.getByText(messageWithKeyword)).toBeVisible();
await expect(lastPostWithHighlight.container.getByText(highlightKeyword)).toHaveClass(
highlightWithoutNotificationClass,
);
});

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

@@ -0,0 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Page} from '@playwright/test';
import {expect, test} from '@mattermost/playwright-lib';
// Helper function to intercept API request and modify the response
async function interceptConfigWithLandingPage(page: Page, enabled: boolean) {
const apiUrl = '**/api/v4/config/client?format=old**';
await page.route(apiUrl, (route) => {
route.fulfill({
status: 200,
body: JSON.stringify({
EnableDesktopLandingPage: enabled,
EnableSignInWithUsername: 'true',
}),
headers: {'Content-Type': 'application/json'},
});
});
}
test('MM-T5640_1 should not see landing page ', async ({pw, page}) => {
await interceptConfigWithLandingPage(page, false);
// Navigate to your starting URL
await page.goto('/');
// Wait until the URL contains '/login'
await page.waitForURL(/.*\/login.*/);
// At this point, the URL should contain '/login'
expect(page.url()).toContain('/login');
// Verify the login page is visible
await pw.loginPage.toBeVisible();
});
test('MM-T5640_2 should see landing page', async ({pw, page}) => {
// Navigate to your starting URL
await page.goto('/');
// Wait until the URL contains '/landing'
await page.waitForURL(/.*\/landing.*/, {timeout: pw.duration.ten_sec});
// At this point, the URL should contain '/landing'
expect(page.url()).toContain('/landing');
// Verify the landing page is visible
await pw.landingLoginPage.toBeVisible();
});

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

@@ -0,0 +1,125 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('should be able to enable mobile security settings when licensed', async ({pw}) => {
const {adminUser, adminClient} = await pw.initSetup();
const license = await adminClient.getClientLicenseOld();
test.skip(license.SkuShortName !== 'enterprise', 'Skipping test - server has no enterprise license');
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Log in as admin
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Visit system console
await systemConsolePage.goto();
await systemConsolePage.toBeVisible();
// # Go to Mobile Security section
await systemConsolePage.sidebar.goToItem('Mobile Security');
await systemConsolePage.mobileSecurity.toBeVisible();
// # Enable Biometric Authentication
await systemConsolePage.mobileSecurity.clickEnableBiometricAuthenticationToggleTrue();
// * Verify only Biometric Authentication is enabled
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(false);
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
// # Save settings
await systemConsolePage.mobileSecurity.clickSaveButton();
// # Wait until the save button has settled
await pw.waitUntil(async () => (await systemConsolePage.mobileSecurity.saveButton.textContent()) === 'Save');
// # Go to any other section and come back to Mobile Security
await systemConsolePage.sidebar.goToItem('Users');
await systemConsolePage.systemUsers.toBeVisible();
await systemConsolePage.sidebar.goToItem('Mobile Security');
// * Verify Biometric Authentication is still enabled
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(false);
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
// # Enable Prevent Screen Capture
await systemConsolePage.mobileSecurity.clickPreventScreenCaptureToggleTrue();
// * Verify only Biometric Authentication and Prevent Screen Capture are enabled
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
// # Save settings
await systemConsolePage.mobileSecurity.clickSaveButton();
// # Wait until the save button has settled
await pw.waitUntil(async () => (await systemConsolePage.mobileSecurity.saveButton.textContent()) === 'Save');
// # Go to any other section and come back to Mobile Security
await systemConsolePage.sidebar.goToItem('Users');
await systemConsolePage.systemUsers.toBeVisible();
await systemConsolePage.sidebar.goToItem('Mobile Security');
// * Verify Biometric Authentication and Prevent Screen Capture are still enabled
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(false);
// # Enable Jailbreak Protection
await systemConsolePage.mobileSecurity.clickJailbreakProtectionToggleTrue();
// * Verify all toggles are enabled
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(true);
// # Save settings
await systemConsolePage.mobileSecurity.clickSaveButton();
// # Wait until the save button has settled
await pw.waitUntil(async () => (await systemConsolePage.mobileSecurity.saveButton.textContent()) === 'Save');
// # Go to any other section and come back to Mobile Security
await systemConsolePage.sidebar.goToItem('Users');
await systemConsolePage.systemUsers.toBeVisible();
await systemConsolePage.sidebar.goToItem('Mobile Security');
// * Verify all toggles are still enabled
expect(await systemConsolePage.mobileSecurity.enableBiometricAuthenticationToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.preventScreenCaptureToggleTrue.isChecked()).toBe(true);
expect(await systemConsolePage.mobileSecurity.jailbreakProtectionToggleTrue.isChecked()).toBe(true);
});
test('should show mobile security upsell when not licensed', async ({pw}) => {
const {adminUser, adminClient} = await pw.initSetup();
const license = await adminClient.getClientLicenseOld();
test.skip(license.SkuShortName === 'enterprise', 'Skipping test - server has enterprise license');
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Log in as admin
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Visit system console
await systemConsolePage.goto();
await systemConsolePage.toBeVisible();
// # Go to Mobile Security section
await systemConsolePage.sidebar.goToItem('Mobile Security');
await systemConsolePage.featureDiscovery.toBeVisible();
// * Verify title is correct
await systemConsolePage.featureDiscovery.toHaveTitle('Enhance mobile app security with Mattermost Enterprise');
});

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

@@ -0,0 +1,202 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {type PlaywrightExtended, expect, test} from '@mattermost/playwright-lib';
/**
* 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: 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 for
const user = await adminClient.createUser(pw.random.user(), '', '');
const team = await adminClient.createTeam(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.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}) => {
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
// # 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
await systemConsolePage.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}) => {
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
// # 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();
systemConsolePage.saveRoleChange();
// * 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.saveRoleChange();
// * 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}) => {
const {systemConsolePage} = await setupAndGetRandomUser(pw);
// # 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}) => {
const {systemConsolePage} = await setupAndGetRandomUser(pw);
// # 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(pw.random.id());
await systemConsolePage.clickResetButton();
// * 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}) => {
const {getUser, systemConsolePage} = await setupAndGetRandomUser(pw);
const newEmail = `${pw.random.id()}@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.clickResetButton();
// * 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}) => {
const {systemConsolePage} = await setupAndGetRandomUser(pw);
// # 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
await systemConsolePage.confirmModal.confirm();
const firstRow = await systemConsolePage.systemUsers.getNthRow(1);
expect(await firstRow.innerHTML()).not.toContain('class="error"');
});

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

@@ -0,0 +1,91 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T5523-1 Sortable columns should sort the list when clicked', async ({pw}) => {
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 10 random users
for (let i = 0; i < 10; i++) {
await adminClient.createUser(pw.random.user(), '', '');
}
// # Visit system console
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(pw.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(pw.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}) => {
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 10 random users
for (let i = 0; i < 10; i++) {
await adminClient.createUser(pw.random.user(), '', '');
}
// # Visit system console
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(pw.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(pw.simpleEmailRe).allInnerTexts();
// * Verify that the first row's email is still the same
expect(firstRowEmailWithoutSort).toEqual(firstRowEmailWithSort);
});

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

@@ -0,0 +1,129 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('MM-T5523-3 Should list the column names with checkboxes in the correct order', async ({pw}) => {
const {adminUser} = await pw.initSetup();
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Log in as admin
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Visit system console
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}) => {
const {adminUser} = await pw.initSetup();
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Log in as admin
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Visit system console
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}) => {
const {adminUser} = await pw.initSetup();
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Log in as admin
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Visit system console
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);
});

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

@@ -0,0 +1,68 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test.fixme('MM-T5522 Should begin export of data when export button is pressed', async ({pw}) => {
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, channelsPage, systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Visit system console
await systemConsolePage.goto();
await systemConsolePage.toBeVisible();
// # Go to Users section
await systemConsolePage.sidebar.goToItem('Users');
await systemConsolePage.systemUsers.toBeVisible();
// # Change the export pw.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();
await systemConsolePage.exportModal.confirm();
// # Change the export pw.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 systemConsolePage.exportModal.confirm();
// # Click Export again button and confirm the modal
await systemConsolePage.systemUsers.exportButton.click();
await systemConsolePage.exportModal.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
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', pw.duration.half_min);
// * Wait until the second export finishes
await channelsPage.centerView.waitUntilLastPostContains(
'contains user data for the last 30 days',
pw.duration.half_min,
);
});

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

@@ -0,0 +1,162 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test} from '@mattermost/playwright-lib';
test('MM-T5521-7 Should be able to filter users with team filter', async ({pw}) => {
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 team with a user
const team1 = await adminClient.createTeam(pw.random.team());
const user1 = await adminClient.createUser(pw.random.user(), '', '');
await adminClient.addToTeam(team1.id, user1.id);
// # Create another team with a user
const team2 = await adminClient.createTeam(pw.random.team());
const user2 = await adminClient.createUser(pw.random.user(), '', '');
await adminClient.addToTeam(team2.id, user2.id);
// # Visit system console
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}) => {
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 guest user
const guestUser = await adminClient.createUser(pw.random.user(), '', '');
await adminClient.updateUserRoles(guestUser.id, 'system_guest');
// # Create a regular user
const regularUser = await adminClient.createUser(pw.random.user(), '', '');
// # Visit system console
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}) => {
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 user and then deactivate it
const deactivatedUser = await adminClient.createUser(pw.random.user(), '', '');
await adminClient.updateUserActive(deactivatedUser.id, false);
// # Create a regular user
const regularUser = await adminClient.createUser(pw.random.user(), '', '');
// # Visit system console
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');
});

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

@@ -0,0 +1,186 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test} from '@mattermost/playwright-lib';
test('MM-T5521-1 Should be able to search users with their first names', async ({pw}) => {
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 2 users
const user1 = await adminClient.createUser(pw.random.user(), '', '');
const user2 = await adminClient.createUser(pw.random.user(), '', '');
// # Visit system console
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}) => {
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 2 users
const user1 = await adminClient.createUser(pw.random.user(), '', '');
const user2 = await adminClient.createUser(pw.random.user(), '', '');
// # Visit system console
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}) => {
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 2 users
const user1 = await adminClient.createUser(pw.random.user(), '', '');
const user2 = await adminClient.createUser(pw.random.user(), '', '');
// # Visit system console
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}) => {
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 2 users
const user1 = await adminClient.createUser(pw.random.user(), '', '');
const user2 = await adminClient.createUser(pw.random.user(), '', '');
// # Visit system console
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}) => {
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 2 users
const user1 = await adminClient.createUser(pw.random.user(), '', '');
const user2 = await adminClient.createUser(pw.random.user(), '', '');
// # Visit system console
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}) => {
const {adminUser} = await pw.initSetup();
if (!adminUser) {
throw new Error('Failed to create admin user');
}
// # Log in as admin
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
// # Visit system console
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(`!${pw.random.id(15)}_^^^_${pw.random.id(15)}!`);
await systemConsolePage.systemUsers.verifyRowWithTextIsFound('No data');
});

14
e2e-tests/playwright/specs/test_setup.ts Обычный файл
Просмотреть файл

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test as setup} from '@mattermost/playwright-lib';
setup('ensure plugins are loaded', async ({pw}) => {
// Ensure all products as plugin are installed and active.
await pw.ensurePluginsLoaded();
});
setup('ensure server deployment', async ({pw}) => {
// Ensure server is on expected deployment type.
await pw.ensureServerDeployment();
});

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

@@ -0,0 +1,26 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect, test} from '@mattermost/playwright-lib';
test('Intro to channel as regular user', async ({pw, browserName, viewport}, testInfo) => {
// Create and sign in a new user
const {user} = await pw.initSetup();
// Log in a user in new browser context
const {page, channelsPage} = await pw.testBrowser.login(user);
// Visit a default channel page
await channelsPage.goto();
await channelsPage.toBeVisible();
// Wait for Playbooks icon to be loaded in App bar, except in iphone
await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
// Hide dynamic elements of Channels page
await pw.hideDynamicChannelsContent(page);
// Match snapshot of channel intro page
const testArgs = {page: page, browserName, viewport};
await pw.matchSnapshot(testInfo, testArgs);
});

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 68 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 107 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 86 KiB

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

@@ -0,0 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test} from '@mattermost/playwright-lib';
test('/landing#/login', async ({pw, page, browserName, viewport}, testInfo) => {
// Go to landing login page
await pw.landingLoginPage.goto();
await pw.landingLoginPage.toBeVisible();
// Match snapshot of landing page
await pw.matchSnapshot(testInfo, {page, browserName, viewport});
});

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 96 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 172 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 84 KiB

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

@@ -0,0 +1,31 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test} from '@mattermost/playwright-lib';
test('/login', async ({pw, page, browserName, viewport}, testInfo) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// Go to login page
const {adminClient} = await pw.getAdminClient();
await pw.loginPage.goto();
await pw.loginPage.toBeVisible();
// Click to other element to remove focus from email input
await pw.loginPage.title.click();
// Match snapshot of login page
const testArgs = {page, browserName, viewport};
const license = await adminClient.getClientLicenseOld();
const editionSuffix = license.IsLicensed === 'true' ? '' : 'free edition';
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} ${editionSuffix}`}, testArgs);
// Click sign in button without entering user credential
await pw.loginPage.signInButton.click();
await pw.loginPage.userErrorLabel.waitFor();
await pw.waitForAnimationEnd(pw.loginPage.bodyCard);
// Match snapshot of login page with error
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} error ${editionSuffix}`}, testArgs);
});

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 188 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 186 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 488 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 187 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 490 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 125 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 124 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 499 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 189 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 500 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 120 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 119 KiB

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

@@ -0,0 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {test} from '@mattermost/playwright-lib';
test('/signup_email', async ({pw, page, browserName, viewport}, testInfo) => {
// Set up the page not to redirect to the landing page
await pw.hasSeenLandingPage();
// Go to login page
const {adminClient} = await pw.getAdminClient();
await pw.loginPage.goto();
await pw.loginPage.toBeVisible();
// Create an account
await pw.loginPage.createAccountLink.click();
// Should have redirected to signup page
await pw.signupPage.toBeVisible();
// Click to other element to remove focus from email input
await pw.signupPage.title.click();
// Match snapshot of signup_email page
const testArgs = {page, browserName, viewport};
const license = await adminClient.getClientLicenseOld();
const editionSuffix = license.IsLicensed === 'true' ? '' : 'free edition';
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} ${editionSuffix}`}, testArgs);
// Click sign in button without entering user credential
const invalidUser = {email: 'invalid', username: 'a', password: 'b'};
await pw.signupPage.create(invalidUser, false);
await pw.signupPage.emailError.waitFor();
await pw.signupPage.usernameError.waitFor();
await pw.signupPage.passwordError.waitFor();
await pw.waitForAnimationEnd(pw.signupPage.bodyCard);
// Match snapshot of signup_email page
await pw.matchSnapshot({...testInfo, title: `${testInfo.title} error ${editionSuffix}`}, testArgs);
});

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 207 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 212 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 510 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 213 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 511 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 161 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 161 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 509 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 208 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 510 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 155 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 155 KiB