mirror of
https://github.com/mattermost/mattermost.git
synced 2026-02-03 20:40:00 -05:00
fix flaky notifications tests and migrated to playwright (#34449)
Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
5fe3987e91
commit
d7aebb555d
7 changed files with 297 additions and 139 deletions
|
|
@ -1,137 +0,0 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @channels @enterprise @system_console
|
||||
|
||||
describe('System Console', () => {
|
||||
before(() => {
|
||||
// * Check if server has license for ID Loaded Push Notifications
|
||||
cy.apiRequireLicenseForFeature('IDLoadedPushNotifications');
|
||||
|
||||
// # Update to default config
|
||||
cy.apiUpdateConfig({
|
||||
EmailSettings: {
|
||||
PushNotificationContents: 'full',
|
||||
FeedbackName: 'Mattermost Test Team',
|
||||
FeedbackEmail: 'feedback@mattertest.com',
|
||||
},
|
||||
SupportSettings: {
|
||||
SupportEmail: 'support@mattertest.com',
|
||||
},
|
||||
});
|
||||
|
||||
// # Visit Notifications admin console page
|
||||
cy.visit('/admin_console/environment/notifications');
|
||||
cy.get('.admin-console__header').should('be.visible').and('have.text', 'Notifications');
|
||||
});
|
||||
|
||||
it('Push Notification Contents', () => {
|
||||
// * Verify that setting is visible and matches text content
|
||||
cy.findByTestId('EmailSettings.PushNotificationContents').
|
||||
scrollIntoView().should('be.visible').
|
||||
find('label').should('be.visible').and('have.text', 'Push Notification Contents:');
|
||||
|
||||
// * Verify that the help text is visible and matches text content
|
||||
cy.findByTestId('EmailSettings.PushNotificationContentshelp-text').should('be.visible').within((el) => {
|
||||
const contents = [
|
||||
'Generic description with only sender name',
|
||||
' - Includes only the name of the person who sent the message in push notifications, with no information about channel name or message contents. ',
|
||||
'Generic description with sender and channel names',
|
||||
' - Includes the name of the person who sent the message and the channel it was sent in, but not the message contents. ',
|
||||
'Full message content sent in the notification payload',
|
||||
' - Includes the message contents in the push notification payload that is relayed through Apple\'s Push Notification Service (APNS) or Google\'s Firebase Cloud Messaging (FCM). It is ',
|
||||
'highly recommended',
|
||||
' this option only be used with an "https" protocol to encrypt the connection and protect confidential information sent in messages.',
|
||||
'Full message content fetched from the server on receipt',
|
||||
' - The notification payload relayed through APNS or FCM contains no message content, instead it contains a unique message ID used to fetch message content from the server when a push notification is received by a device. If the server cannot be reached, a generic notification will be displayed.',
|
||||
];
|
||||
cy.wrap(el).should('have.text', contents.join(''));
|
||||
|
||||
cy.get('strong').eq(0).should('have.text', contents[0]);
|
||||
cy.get('strong').eq(1).should('have.text', contents[2]);
|
||||
cy.get('strong').eq(2).should('have.text', contents[4]);
|
||||
cy.get('strong').eq(3).should('have.text', contents[6]);
|
||||
cy.get('strong').eq(4).should('have.text', contents[8]);
|
||||
});
|
||||
|
||||
// * Verify that the option/dropdown is visible and has default value
|
||||
cy.findByTestId('EmailSettings.PushNotificationContentsdropdown').
|
||||
should('be.visible').
|
||||
and('have.value', 'full');
|
||||
|
||||
const options = [
|
||||
{label: 'Generic description with only sender name', value: 'generic_no_channel'},
|
||||
{label: 'Generic description with sender and channel names', value: 'generic'},
|
||||
{label: 'Full message content sent in the notification payload', value: 'full'},
|
||||
{label: 'Full message content fetched from the server on receipt', value: 'id_loaded'},
|
||||
];
|
||||
|
||||
// # Select each value and save
|
||||
// * Verify that the config is correctly saved in the server
|
||||
options.forEach((option) => {
|
||||
cy.findByTestId('EmailSettings.PushNotificationContentsdropdown').
|
||||
select(option.label).
|
||||
and('have.value', option.value);
|
||||
cy.get('#saveSetting').click();
|
||||
|
||||
cy.apiGetConfig().then(({config}) => {
|
||||
expect(config.EmailSettings.PushNotificationContents).to.equal(option.value);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T1210+MM-41671 Can change Support Email setting', () => {
|
||||
// # Scroll Support Email section into view and verify that it's visible
|
||||
cy.findByTestId('SupportSettings.SupportEmail').scrollIntoView().should('be.visible');
|
||||
|
||||
// * Verify that setting label is visible and matches text content
|
||||
cy.findByTestId('SupportSettings.SupportEmaillabel').should('be.visible').and('have.text', 'Support Email Address:');
|
||||
|
||||
// * Verify that the help text is visible and matches text content
|
||||
cy.findByTestId('SupportSettings.SupportEmailhelp-text').find('span').should('be.visible').and('have.text', 'Email address displayed on support emails.');
|
||||
|
||||
const newEmail = 'changed_for_test_support@example.com';
|
||||
|
||||
// * Verify that set value is visible and matches text
|
||||
cy.findByTestId('SupportSettings.SupportEmail').find('input').clear().type(newEmail).should('have.value', newEmail);
|
||||
|
||||
// # Save setting
|
||||
cy.get('#saveSetting').click();
|
||||
|
||||
// * Verify that the config is correctly saved in the server
|
||||
cy.apiGetConfig().then(({config}) => {
|
||||
expect(config.SupportSettings.SupportEmail).to.equal(newEmail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('MM-41671 cannot save the notifications page if mandatory fields are missing', () => {
|
||||
const tests = [
|
||||
{name: 'Support Email cannot be empty', field: 'SupportSettings.SupportEmail'},
|
||||
{name: 'Notification Display Name cannot be empty', field: 'EmailSettings.FeedbackName'},
|
||||
{name: 'Notification Email Address cannot be empty', field: 'SupportSettings.SupportEmail'},
|
||||
];
|
||||
|
||||
tests.forEach((test) => {
|
||||
it(test.name, () => {
|
||||
// # Clear the field
|
||||
cy.findByTestId(test.field).find('input').clear();
|
||||
|
||||
// * Ensures the save button is disabled
|
||||
cy.get('#saveSetting').should('be.disabled');
|
||||
|
||||
// # Insert something in the field
|
||||
cy.findByTestId(test.field).find('input').type(test.field);
|
||||
|
||||
// * Ensures the save button is disabled
|
||||
cy.get('#saveSetting').should('be.not.disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -2,13 +2,13 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Client4} from '@mattermost/client';
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
import {PluginManifest} from '@mattermost/types/plugins';
|
||||
import {PreferenceType} from '@mattermost/types/preferences';
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import {defaultTeam} from './util';
|
||||
import {createRandomTeam, getAdminClient, getDefaultAdminUser, makeClient} from './server';
|
||||
import {testConfig} from './test_config';
|
||||
import {defaultTeam} from './util';
|
||||
|
||||
export async function baseGlobalSetup() {
|
||||
let adminClient: Client4;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,25 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Client4} from '@mattermost/client';
|
||||
import {AdminConfig} from '@mattermost/types/config';
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
|
||||
import {testConfig} from '@/test_config';
|
||||
|
||||
// Extend Client4 with methods used for testing
|
||||
declare module '@mattermost/client' {
|
||||
interface Client4 {
|
||||
updateConfigX: (config: Partial<AdminConfig>) => Promise<AdminConfig>;
|
||||
}
|
||||
}
|
||||
|
||||
// updateConfigX merges the given config with the current config and updates it to the server
|
||||
Client4.prototype.updateConfigX = async function (this: Client4, config: Partial<AdminConfig>) {
|
||||
const currentConfig = await this.getConfig();
|
||||
const newConfig = {...currentConfig, ...config};
|
||||
return await this.updateConfig(newConfig);
|
||||
};
|
||||
|
||||
// Variable to hold cache
|
||||
const clients: Record<string, ClientCache> = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import DeletePostConfirmationDialog from './channels/delete_post_confirmation_di
|
|||
import RestorePostConfirmationDialog from './channels/restore_post_confirmation_dialog';
|
||||
import SystemConsoleFeatureDiscovery from './system_console/sections/system_users/feature_discovery';
|
||||
import SystemConsoleMobileSecurity from './system_console/sections/system_users/mobile_security';
|
||||
import SystemConsoleNotifications from './system_console/sections/site_configuration/notifications';
|
||||
import ScheduledPost from './channels/scheduled_post';
|
||||
import SendMessageNowModal from './channels/send_message_now_modal';
|
||||
import DeleteScheduledPostModal from './channels/delete_scheduled_post_modal';
|
||||
|
|
@ -91,6 +92,7 @@ const components = {
|
|||
SystemUsersColumnToggleMenu,
|
||||
SystemConsoleFeatureDiscovery,
|
||||
SystemConsoleMobileSecurity,
|
||||
SystemConsoleNotifications,
|
||||
MessagePriority,
|
||||
UserProfilePopover,
|
||||
UserAccountMenu,
|
||||
|
|
@ -143,6 +145,7 @@ export {
|
|||
SystemUsersColumnToggleMenu,
|
||||
SystemConsoleFeatureDiscovery,
|
||||
SystemConsoleMobileSecurity,
|
||||
SystemConsoleNotifications,
|
||||
MessagePriority,
|
||||
UserProfilePopover,
|
||||
UserAccountMenu,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Locator, expect} from '@playwright/test';
|
||||
|
||||
/**
|
||||
* System Console -> Site Configuration -> Notifications
|
||||
*/
|
||||
export default class SystemConsoleNotifications {
|
||||
readonly container: Locator;
|
||||
|
||||
// header
|
||||
readonly header: Locator;
|
||||
|
||||
// Notification Display Name
|
||||
readonly notificationDisplayName: Locator;
|
||||
readonly notificationDisplayNameInput: Locator;
|
||||
readonly notificationDisplayNameHelpText: Locator;
|
||||
|
||||
// Notification From Address
|
||||
readonly notificationFromAddress: Locator;
|
||||
readonly notificationFromAddressInput: Locator;
|
||||
readonly notificationFromAddressHelpText: Locator;
|
||||
|
||||
// Support Email Address
|
||||
readonly supportEmailAddress: Locator;
|
||||
readonly supportEmailAddressInput: Locator;
|
||||
readonly supportEmailHelpText: Locator;
|
||||
|
||||
// Push Notification Contents
|
||||
readonly pushNotificationContents: Locator;
|
||||
readonly pushNotificationContentsDropdown: Locator;
|
||||
readonly pushNotificationContentsHelpText: Locator;
|
||||
|
||||
// Save button
|
||||
readonly saveButton: Locator;
|
||||
readonly errorMessage: Locator;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
|
||||
// header
|
||||
this.header = this.container.locator('.admin-console__header').getByText('Notifications');
|
||||
|
||||
// Notification Display Name
|
||||
this.notificationDisplayName = this.container.getByTestId('EmailSettings.FeedbackNameinput');
|
||||
this.notificationDisplayNameInput = this.container.getByTestId('EmailSettings.FeedbackNameinput');
|
||||
this.notificationDisplayNameHelpText = this.container.getByTestId('EmailSettings.FeedbackNamehelp-text');
|
||||
|
||||
// Notification From Address
|
||||
this.notificationFromAddress = this.container.getByLabel('Notification From Address:');
|
||||
this.notificationFromAddressInput = this.container.getByTestId('EmailSettings.FeedbackEmailinput');
|
||||
this.notificationFromAddressHelpText = this.container.getByTestId('EmailSettings.FeedbackEmailhelp-text');
|
||||
|
||||
// Support Email Address
|
||||
this.supportEmailAddress = this.container.getByLabel('Support Email Address:');
|
||||
this.supportEmailAddressInput = this.container.getByTestId('SupportSettings.SupportEmailinput');
|
||||
this.supportEmailHelpText = this.container.getByTestId('SupportSettings.SupportEmailhelp-text');
|
||||
|
||||
// Push Notification Contents
|
||||
this.pushNotificationContents = this.container.getByTestId('EmailSettings.PushNotificationContents');
|
||||
this.pushNotificationContentsDropdown = this.container.getByTestId(
|
||||
'EmailSettings.PushNotificationContentsdropdown',
|
||||
);
|
||||
this.pushNotificationContentsHelpText = this.container.getByTestId(
|
||||
'EmailSettings.PushNotificationContentshelp-text',
|
||||
);
|
||||
|
||||
// Save button and error message
|
||||
this.saveButton = this.container.getByTestId('saveSetting');
|
||||
this.errorMessage = this.container.locator('.has-error');
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,11 @@ export default class SystemConsolePage {
|
|||
readonly sidebar;
|
||||
readonly navbar;
|
||||
|
||||
// Site Configuration
|
||||
|
||||
// System Console > Notifications
|
||||
readonly notifications;
|
||||
|
||||
/**
|
||||
* System Console -> User Management -> Users
|
||||
*/
|
||||
|
|
@ -34,6 +39,12 @@ export default class SystemConsolePage {
|
|||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
|
||||
// Site Configuration
|
||||
// System Console > Notifications
|
||||
this.notifications = new components.SystemConsoleNotifications(
|
||||
page.getByTestId('sysconsole_section_notifications'),
|
||||
);
|
||||
|
||||
// Areas of the page
|
||||
this.navbar = new components.SystemConsoleNavbar(page.locator('.backstage-navbar'));
|
||||
this.sidebar = new components.SystemConsoleSidebar(page.locator('.admin-sidebar'));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,189 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {AdminConfig} from '@mattermost/types/config';
|
||||
|
||||
import {expect, test} from '@mattermost/playwright-lib';
|
||||
|
||||
/**
|
||||
* @objective Verify that the Push Notification Contents setting is properly displayed and can be changed to all available options
|
||||
*/
|
||||
test('Push Notification Contents setting displays correctly and saves all options', async ({pw}) => {
|
||||
const {adminUser, adminClient} = await pw.initSetup();
|
||||
|
||||
// # Update to default config
|
||||
await adminClient.updateConfigX({
|
||||
EmailSettings: {
|
||||
PushNotificationContents: 'full',
|
||||
FeedbackName: 'Mattermost Test Team',
|
||||
FeedbackEmail: 'feedback@mattertest.com',
|
||||
},
|
||||
SupportSettings: {
|
||||
SupportEmail: 'support@mattertest.com',
|
||||
},
|
||||
} as Partial<AdminConfig>);
|
||||
|
||||
if (!adminUser) {
|
||||
throw new Error('Failed to get admin user');
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit Notifications admin console page
|
||||
await systemConsolePage.goto();
|
||||
await systemConsolePage.toBeVisible();
|
||||
await systemConsolePage.sidebar.goToItem('Notifications');
|
||||
|
||||
// # Wait for Notifications section to load
|
||||
const notifications = systemConsolePage.notifications;
|
||||
await notifications.toBeVisible();
|
||||
|
||||
// * Verify that setting is visible and matches text content
|
||||
await notifications.pushNotificationContents.scrollIntoViewIfNeeded();
|
||||
await expect(notifications.pushNotificationContents).toBeVisible();
|
||||
|
||||
// * Verify that the help text is visible and matches text content
|
||||
const helpText = notifications.pushNotificationContentsHelpText;
|
||||
await expect(helpText).toBeVisible();
|
||||
|
||||
const contents = [
|
||||
'Generic description with only sender name',
|
||||
' - Includes only the name of the person who sent the message in push notifications, with no information about channel name or message contents. ',
|
||||
'Generic description with sender and channel names',
|
||||
' - Includes the name of the person who sent the message and the channel it was sent in, but not the message contents. ',
|
||||
'Full message content sent in the notification payload',
|
||||
" - Includes the message contents in the push notification payload that is relayed through Apple's Push Notification Service (APNS) or Google's Firebase Cloud Messaging (FCM). It is ",
|
||||
'highly recommended',
|
||||
' this option only be used with an "https" protocol to encrypt the connection and protect confidential information sent in messages.',
|
||||
'Full message content fetched from the server on receipt',
|
||||
' - The notification payload relayed through APNS or FCM contains no message content, instead it contains a unique message ID used to fetch message content from the server when a push notification is received by a device. If the server cannot be reached, a generic notification will be displayed.',
|
||||
];
|
||||
await expect(helpText).toHaveText(contents.join(''));
|
||||
|
||||
const strongElements = helpText.locator('strong');
|
||||
await expect(strongElements.nth(0)).toHaveText(contents[0]);
|
||||
await expect(strongElements.nth(1)).toHaveText(contents[2]);
|
||||
await expect(strongElements.nth(2)).toHaveText(contents[4]);
|
||||
await expect(strongElements.nth(3)).toHaveText(contents[6]);
|
||||
await expect(strongElements.nth(4)).toHaveText(contents[8]);
|
||||
|
||||
// * Verify that the option/dropdown is visible and has default value
|
||||
const dropdown = notifications.pushNotificationContentsDropdown;
|
||||
await expect(dropdown).toBeVisible();
|
||||
await expect(dropdown).toHaveValue('full');
|
||||
|
||||
const options = [
|
||||
{label: 'Generic description with only sender name', value: 'generic_no_channel'},
|
||||
{label: 'Generic description with sender and channel names', value: 'generic'},
|
||||
{label: 'Full message content sent in the notification payload', value: 'full'},
|
||||
{label: 'Full message content fetched from the server on receipt', value: 'id_loaded'},
|
||||
];
|
||||
|
||||
// # Select each value and save
|
||||
// * Verify that the config is correctly saved in the server
|
||||
for (const option of options) {
|
||||
await dropdown.selectOption({label: option.label});
|
||||
await expect(dropdown).toHaveValue(option.value);
|
||||
|
||||
await notifications.saveButton.click();
|
||||
|
||||
// * Verify config is saved
|
||||
const {adminClient} = await pw.getAdminClient();
|
||||
const config = await adminClient.getConfig();
|
||||
expect(config.EmailSettings?.PushNotificationContents).toBe(option.value);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @objective Verify that the Support Email setting can be changed and saved
|
||||
*/
|
||||
test('MM-T1210 Can change Support Email setting', async ({pw}) => {
|
||||
const {adminUser} = await pw.getAdminClient();
|
||||
|
||||
if (!adminUser) {
|
||||
throw new Error('Failed to get admin user');
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit Notifications admin console page
|
||||
await systemConsolePage.goto();
|
||||
await systemConsolePage.toBeVisible();
|
||||
await systemConsolePage.sidebar.goToItem('Notifications');
|
||||
|
||||
// # Wait for Notifications section to load
|
||||
const notifications = systemConsolePage.notifications;
|
||||
await notifications.toBeVisible();
|
||||
|
||||
// # Scroll Support Email section into view and verify that it's visible
|
||||
const supportEmailSetting = notifications.supportEmailAddress;
|
||||
await supportEmailSetting.scrollIntoViewIfNeeded();
|
||||
await expect(supportEmailSetting).toBeVisible();
|
||||
|
||||
// * Verify that the help text is visible and matches text content
|
||||
await expect(notifications.supportEmailHelpText).toBeVisible();
|
||||
await expect(notifications.supportEmailHelpText).toHaveText('Email address displayed on support emails.');
|
||||
|
||||
// # Clear and type new email
|
||||
const newEmail = 'changed_for_test_support@example.com';
|
||||
await notifications.supportEmailAddressInput.clear();
|
||||
await notifications.supportEmailAddressInput.fill(newEmail);
|
||||
|
||||
// * Verify that set value is visible and matches text
|
||||
await expect(notifications.supportEmailAddressInput).toHaveValue(newEmail);
|
||||
|
||||
// # Save setting
|
||||
await notifications.saveButton.click();
|
||||
|
||||
// * Verify that the config is correctly saved in the server
|
||||
const {adminClient} = await pw.getAdminClient();
|
||||
const config = await adminClient.getConfig();
|
||||
expect(config.SupportSettings?.SupportEmail).toBe(newEmail);
|
||||
});
|
||||
|
||||
/**
|
||||
* @objective Verify that the save button is disabled when mandatory fields are empty
|
||||
*/
|
||||
test('MM-41671 cannot save the notifications page if mandatory fields are missing', async ({pw}) => {
|
||||
const {adminUser} = await pw.getAdminClient();
|
||||
if (!adminUser) {
|
||||
throw new Error('Failed to get admin user');
|
||||
}
|
||||
|
||||
// # Log in as admin
|
||||
const {systemConsolePage} = await pw.testBrowser.login(adminUser);
|
||||
|
||||
// # Visit Notifications admin console page
|
||||
await systemConsolePage.goto();
|
||||
await systemConsolePage.toBeVisible();
|
||||
await systemConsolePage.sidebar.goToItem('Notifications');
|
||||
|
||||
// # Wait for Notifications section to load
|
||||
const notifications = systemConsolePage.notifications;
|
||||
await notifications.toBeVisible();
|
||||
|
||||
const tests = [
|
||||
{name: 'Support Email Address', fieldInput: notifications.supportEmailAddressInput},
|
||||
{name: 'Notification Display Name', fieldInput: notifications.notificationDisplayNameInput},
|
||||
{name: 'Notification From Address', fieldInput: notifications.notificationFromAddressInput},
|
||||
];
|
||||
|
||||
for (const testCase of tests) {
|
||||
// # Clear the field
|
||||
await expect(testCase.fieldInput).toBeVisible();
|
||||
await testCase.fieldInput.clear();
|
||||
|
||||
// * Error message is shown and save button is disabled
|
||||
await expect(notifications.errorMessage).toHaveText(`"${testCase.name}" is required`);
|
||||
await expect(notifications.saveButton).toBeDisabled();
|
||||
|
||||
// # Insert something in the field
|
||||
await testCase.fieldInput.fill('anything');
|
||||
|
||||
// * Ensure no error message is shown and the save button is not disabled
|
||||
await expect(notifications.errorMessage).toHaveCount(0);
|
||||
await expect(notifications.saveButton).not.toBeDisabled();
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue