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
@@ -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();
|
||||
});
|
||||
79
e2e-tests/playwright/specs/accessibility/common/login.spec.ts
Обычный файл
@@ -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()),
|
||||
});
|
||||
138
e2e-tests/playwright/specs/client/upload_file.spec.ts
Обычный файл
@@ -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();
|
||||
});
|
||||
26
e2e-tests/playwright/specs/visual/channels/intro_channel.spec.ts
Обычный файл
@@ -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 |
13
e2e-tests/playwright/specs/visual/common/landing_page.spec.ts
Обычный файл
@@ -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});
|
||||
});
|
||||
Двоичные данные
e2e-tests/playwright/specs/visual/common/landing_page.spec.ts-snapshots/landing-login-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 96 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/landing_page.spec.ts-snapshots/landing-login-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 172 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/landing_page.spec.ts-snapshots/landing-login-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 84 KiB |
31
e2e-tests/playwright/specs/visual/common/login.spec.ts
Обычный файл
@@ -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);
|
||||
});
|
||||
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 188 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-error-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 186 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-error-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 488 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-error-free-edition-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 187 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-error-free-edition-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 490 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-error-free-edition-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 125 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-error-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 124 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 499 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-free-edition-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 189 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-free-edition-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 500 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-free-edition-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 120 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/login.spec.ts-snapshots/login-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 119 KiB |
40
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts
Обычный файл
@@ -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);
|
||||
});
|
||||
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 207 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-error-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 212 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-error-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 510 KiB |
|
После Ширина: | Высота: | Размер: 213 KiB |
|
После Ширина: | Высота: | Размер: 511 KiB |
|
После Ширина: | Высота: | Размер: 161 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-error-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 161 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 509 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-free-edition-chrome-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 208 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-free-edition-firefox-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 510 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-free-edition-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 155 KiB |
Двоичные данные
e2e-tests/playwright/specs/visual/common/signup_email.spec.ts-snapshots/signup-email-ipad-linux.png
Обычный файл
|
После Ширина: | Высота: | Размер: 155 KiB |