Этот коммит содержится в:
Mario Vitale
2023-03-27 16:28:42 +02:00
родитель da7a6825ce
Коммит ba6b97fb62
1142 изменённых файлов: 44 добавлений и 44 удалений

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

@@ -0,0 +1,76 @@
// 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: @boards
describe('Card badges', () => {
before(() => {
// # Login as new user
cy.apiInitSetup({loginAfter: true});
cy.clearLocalStorage();
});
it('MM-T5395 Shows and hides card badges', () => {
cy.visit('/boards');
// Create new board
cy.uiCreateNewBoard('Testing');
// Add a new card
cy.uiAddNewCard('Card');
// Add some comments
cy.log('**Add some comments**');
addComment('Some comment');
addComment('Another comment');
addComment('Additional comment');
// Add card description
cy.log('**Add card description**');
cy.findByText('Add a description...').click();
cy.findByRole('combobox').type('## Header\n- [ ] one\n- [x] two{esc}');
// Add checkboxes
cy.log('**Add checkboxes**');
cy.findByRole('button', {name: 'Add content'}).click();
cy.findByRole('button', {name: 'checkbox'}).click();
cy.focused().type('three{enter}');
cy.focused().type('four{enter}');
cy.focused().type('{esc}');
cy.findByDisplayValue('three').prev().click();
// Close card dialog
cy.log('**Close card dialog**');
cy.findByRole('button', {name: 'Close dialog'}).click();
cy.findByRole('dialog').should('not.exist');
// Show card badges
cy.log('**Show card badges**');
cy.findByRole('button', {name: 'Properties menu'}).click();
cy.findByRole('button', {name: 'Comments and description'}).click();
cy.findByTitle('This card has a description').should('exist');
cy.findByTitle('Comments').contains('3').should('exist');
cy.findByTitle('Checkboxes').contains('2/4').should('exist');
// Hide card badges
cy.log('**Hide card badges**');
cy.findByRole('button', {name: 'Comments and description'}).click();
cy.findByRole('button', {name: 'Properties menu'}).click();
cy.findByTitle('This card has a description').should('not.exist');
cy.findByTitle('Comments').should('not.exist');
cy.findByTitle('Checkboxes').should('not.exist');
});
const addComment = (text: string) => {
cy.findByText('Add a comment...').click();
cy.findByRole('combobox').type(text).blur();
cy.findByRole('button', {name: 'Send'}).click();
};
});

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

@@ -0,0 +1,118 @@
// 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: @boards
describe('Card URL Property', () => {
before(() => {
// # Login as new user
cy.apiInitSetup({loginAfter: true});
cy.clearLocalStorage();
});
const url = 'https://mattermost.com';
const changedURL = 'https://mattermost.com/blog';
it('MM-T5396 Allows to create and edit URL property', () => {
cy.visit('/boards');
// Create new board
cy.uiCreateNewBoard('Testing');
// Add a new card
cy.uiAddNewCard('Card');
// Add URL property
cy.log('**Add URL property**');
cy.findByRole('button', {name: '+ Add a property'}).click();
cy.findByRole('button', {name: 'URL'}).click();
cy.findByRole('textbox', {name: 'URL'}).type('{enter}');
// Enter URL
cy.log('**Enter URL**');
cy.findByPlaceholderText('Empty').type(`${url}{enter}`);
// Check buttons
cy.log('**Check link**');
cy.get('.URLProperty').trigger('mouseover');
cy.log('**Check buttons**');
// Change URL
cy.log('**Change URL**');
cy.get('.URLProperty Button[title=\'Edit\']').click({force: true});
cy.findByRole('textbox', {name: url}).clear().type(`${changedURL}{enter}`);
cy.findByRole('link', {name: changedURL}).should('exist');
// Close card dialog
cy.log('**Close card dialog**');
cy.findByRole('button', {name: 'Close dialog'}).click();
cy.findByRole('dialog').should('not.exist');
// Show URL property
showURLProperty();
// Copy URL to clipboard
cy.log('**Copy URL to clipboard**');
cy.document().then((doc) => cy.spy(doc, 'execCommand')).as('exec');
cy.get('.URLProperty Button[title=\'Edit\']').should('not.exist');
cy.get('.URLProperty Button[title=\'Copy\']').click({force: true});
cy.findByText('Copied!').should('exist');
cy.findByText('Copied!').should('not.exist');
cy.get('@exec').should('have.been.calledOnceWith', 'copy');
// Add table view
addView('Table');
// Check buttons
cy.log('**Check buttons**');
cy.get('.URLProperty Button[title=\'Edit\']').should('exist');
cy.get('.URLProperty Button[title=\'Copy\']').should('exist');
cy.findByRole('button', {name: 'Copy'}).should('not.exist');
// Add gallery view
addView('Gallery');
showURLProperty();
// Check buttons
cy.log('**Check buttons**');
cy.get('.URLProperty Button[title=\'Edit\']').should('not.exist');
cy.get('.URLProperty Button[title=\'Copy\']').should('exist');
// Add calendar view
addView('Calendar');
showURLProperty();
// Check buttons
cy.log('**Check buttons**');
cy.get('.URLProperty Button[title=\'Edit\']').should('not.exist');
cy.get('.URLProperty Button[title=\'Copy\']').should('exist');
});
type ViewType = 'Board' | 'Table' | 'Gallery' | 'Calendar'
const addView = (type: ViewType) => {
cy.log(`**Add ${type} view**`);
cy.findByRole('button', {name: 'View menu'}).click();
cy.findByText('Add view').trigger('mouseover');
cy.findByRole('button', {name: type}).click();
cy.findByRole('textbox', {name: `${type} view`}).should('exist');
};
const showURLProperty = () => {
cy.log('**Show URL property**');
cy.findByRole('button', {name: 'Properties'}).click();
cy.findByRole('button', {name: 'URL'}).click();
cy.findByRole('button', {name: 'Properties'}).click();
cy.findByRole('link', {name: changedURL}).should('exist');
};
});

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

@@ -0,0 +1,222 @@
// 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: @boards
import timeouts from '../../fixtures/timeouts';
describe('Create and delete board / card', () => {
const timestamp = new Date().toLocaleString();
const boardTitle = `Test Board (${timestamp})`;
const cardTitle = `Test Card (${timestamp})`;
beforeEach(() => {
// # Login as new user
cy.apiAdminLogin().apiInitSetup({loginAfter: true});
cy.clearLocalStorage();
});
it('MM-T4274 Create an Empty Board', () => {
cy.visit('/boards');
// Tests for template selector
cy.findByText('Use this template').should('exist').click();
// Some options are present
cy.contains('Meeting Agenda').should('exist');
cy.contains('Personal Goals').should('exist');
cy.contains('Project Tasks').should('exist');
// Create empty board
cy.findByText('Create an empty board').should('exist').click({force: true});
cy.get('.BoardComponent').should('exist');
// Change Title
cy.findByPlaceholderText('Untitled board').should('be.visible').wait(timeouts.HALF_SEC).as('editableTitle');
cy.get('@editableTitle').should('be.visible').
clear().
type('Testing').
type('{enter}').
should('have.value', 'Testing');
});
it('MM-T5397 Can create and delete a board and a card', () => {
// Visit a page and create new empty board
cy.visit('/boards');
cy.uiCreateEmptyBoard();
// Change board title
cy.log('**Change board title**');
cy.get('.Editable.title').
type(boardTitle).
type('{enter}').
should('have.value', boardTitle);
// Rename board view
cy.log('**Rename board view**');
const boardViewTitle = `Test board (${timestamp})`;
cy.get(".ViewHeader>.viewSelector>.Editable[title='Board view']").should('exist');
cy.get('.ViewHeader>.viewSelector>.Editable').
clear().
type(boardViewTitle).
type('{esc}');
cy.get(`.ViewHeader .Editable[title='${boardViewTitle}']`).should('exist');
// Create card
cy.log('**Create card**');
cy.get('.ViewHeader').contains('New').click();
cy.get('.CardDetail').should('exist');
//Check title has focus when card is created
cy.log('**Check title has focus when card is created**');
cy.get('.CardDetail .EditableArea.title').
should('have.focus');
// Change card title
cy.log('**Change card title**');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.get('.CardDetail .EditableArea.title').
click().
should('have.focus').
type(cardTitle).
should('have.value', cardTitle);
// Close card dialog
cy.log('**Close card dialog**');
cy.get('.Dialog Button[title=\'Close dialog\']').
should('be.visible').
click();
// Create a card by clicking on the + button
cy.log('**Create a card by clicking on the + button**');
cy.get('.KanbanColumnHeader button .AddIcon').click();
cy.get('.CardDetail').should('exist');
cy.get('.Dialog.dialog-back .wrapper').click({force: true});
// Create table view
cy.log('**Create table view**');
cy.get('.ViewHeader').get('.DropdownIcon').first().parent().click();
cy.get('.ViewHeader').contains('Add view').trigger('mouseover');
cy.get('.ViewHeader').
contains('Add view').
parent().
contains('Table').
click();
cy.get(".ViewHeader .Editable[title='Table view']").should('exist');
cy.get(`.TableRow [value='${cardTitle}']`).should('exist');
// Rename table view
cy.log('**Rename table view**');
const tableViewTitle = `Test table (${timestamp})`;
cy.get(".ViewHeader .Editable[title='Table view']").
clear().
type(tableViewTitle).
type('{esc}');
cy.get(`.ViewHeader .Editable[title='${tableViewTitle}']`).should('exist');
// Sort the table
cy.log('**Sort the table**');
cy.get('.ViewHeader').contains('Sort').click();
cy.get('.ViewHeader').
contains('Sort').
parent().
contains('Name').
click();
// Delete board
cy.log('**Delete board**');
cy.get('.Sidebar .octo-sidebar-list').then((el) => {
cy.log(el.text());
});
cy.get('.Sidebar .octo-sidebar-list').
contains(boardTitle).
parent().
find('.MenuWrapper').
find('button.IconButton').
click({force: true});
cy.contains('Delete board').click({force: true});
cy.get('.DeleteBoardDialog button.danger').click({force: true});
cy.contains(boardTitle).should('not.exist');
});
it('MM-T4433 Scrolls the kanban board when dragging card to edge', () => {
// Visit a page and create new empty board
cy.visit('/boards');
cy.uiCreateEmptyBoard();
// Create 10 empty groups
cy.log('**Create new empty groups**');
for (let i = 0; i < 10; i++) {
cy.contains('+ Add a group').scrollIntoView().should('be.visible').click();
cy.get('.KanbanColumnHeader .Editable[value=\'New group\']').should('have.length', i + 1);
}
// Create empty card in last group
cy.log('**Create new empty card in first group**');
cy.get('.octo-board-column').last().contains('+ New').scrollIntoView().click();
cy.get('.Dialog').should('exist');
cy.get('.Dialog Button[title=\'Close dialog\']').should('be.visible').click();
cy.get('.KanbanCard').scrollIntoView().should('exist');
// Drag card to right corner and expect scroll to occur
cy.get('.Kanban').invoke('scrollLeft').should('not.equal', 0);
cy.get('.KanbanCard').
trigger('dragstart');
// wait necessary to trigger scroll animation for some time
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.get('.Kanban').
trigger('dragover', {clientX: 400, clientY: Cypress.config().viewportHeight / 2}).
wait(timeouts.TEN_SEC).
trigger('dragend');
cy.get('.Kanban').invoke('scrollLeft').should('equal', 0);
});
it('MM-T5398 cut/undo/redo work in comments', () => {
const isMAC = navigator.userAgent.indexOf('Mac') !== -1;
const ctrlKey = isMAC ? 'meta' : 'ctrl';
// Visit a page and create new empty board
cy.visit('/boards');
cy.uiCreateEmptyBoard();
// Create card
cy.log('**Create card**');
cy.get('.ViewHeader').contains('New').click();
cy.get('.CardDetail').should('exist');
cy.log('**Add comment**');
cy.get('.CommentsList').
should('exist').
findAllByTestId('preview-element').
click();
cy.get('.CommentsList .MarkdownEditor').
type('Test Text');
cy.log('**Cut comment**');
cy.get('.CommentsList .MarkdownEditor').
type('{selectAll}').
trigger('cut').
should('have.text', '');
cy.log('**Undo comment**');
cy.get('.CommentsList .MarkdownEditor').
type(`{${ctrlKey}+z}`).
should('have.text', 'Test Text');
cy.log('**Redo comment**');
cy.get('.CommentsList .MarkdownEditor').
type(`{shift+${ctrlKey}+z}`).
should('have.text', '');
});
});

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

@@ -0,0 +1,68 @@
// 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: @boards
describe('Group board by different properties', () => {
before(() => {
// # Login as new user
cy.apiInitSetup({loginAfter: true});
cy.clearLocalStorage();
});
it('MM-T4291 Group by different property', () => {
cy.visit('/boards');
// Create new board
cy.uiCreateNewBoard('Testing');
// Add a new group
cy.uiAddNewGroup('Group 1');
// Add a new card to the group
cy.log('**Add a new card to the group**');
cy.findAllByRole('button', {name: '+ New'}).eq(1).click();
cy.findByRole('dialog').should('exist');
cy.findByTestId('select-non-editable').findByText('Group 1').should('exist');
cy.get('#mainBoardBody').findByText('Untitled').should('exist');
// Add new select property
cy.log('**Add new select property**');
cy.findAllByRole('button', {name: '+ Add a property'}).click();
cy.findAllByRole('button', {name: 'Select'}).click();
cy.findByRole('textbox', {name: 'Select'}).type('{enter}');
cy.findByRole('dialog').findByRole('button', {name: 'Select'}).should('exist');
// Close card dialog
cy.log('**Close card dialog**');
cy.findByRole('button', {name: 'Close dialog'}).should('exist').click();
cy.findByRole('dialog').should('not.exist');
// Group by new select property
cy.log('**Group by new select property**');
cy.findByRole('button', {name: /Group by:/}).click();
cy.findByRole('button', {name: 'Status'}).get('.CheckIcon').should('exist');
cy.findByRole('button', {name: 'Select'}).click();
cy.findByTitle(/empty Select property/).contains('No Select');
cy.get('#mainBoardBody').findByText('Untitled').should('exist');
// Add another new group
cy.log('**Add another new group**');
cy.findByRole('button', {name: '+ Add a group'}).click();
cy.findByRole('textbox', {name: 'New group'}).should('exist');
// Add a new card to another group
cy.log('**Add a new card to another group**');
cy.findAllByRole('button', {name: '+ New'}).eq(1).click();
cy.findByRole('dialog').should('exist');
cy.findAllByTestId('select-non-editable').last().findByText('New group').should('exist');
});
});

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

@@ -0,0 +1,104 @@
// 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: @boards
describe('Manage groups', () => {
beforeEach(() => {
// # Login as new user
cy.apiAdminLogin().apiInitSetup({loginAfter: true});
cy.clearLocalStorage();
});
it('MM-T4284 Adding a group', () => {
// Visit a page and create new empty board
cy.visit('/boards');
cy.uiCreateEmptyBoard();
cy.contains('+ Add a group').click({force: true});
cy.get('.KanbanColumnHeader .Editable[value=\'New group\']').should('exist');
cy.get('.KanbanColumnHeader .Editable[value=\'New group\']').
clear().
type('Group 1').
blur();
cy.get('.KanbanColumnHeader .Editable[value=\'Group 1\']').should('exist');
});
it('MM-T4285 Adding group color', () => {
// Visit a page and create new empty board
cy.visit('/boards');
cy.uiCreateEmptyBoard();
cy.contains('+ Add a group').click({force: true});
cy.get('.KanbanColumnHeader .Editable[value=\'New group\']').should('exist');
cy.get('.KanbanColumnHeader').last().within(() => {
cy.get('.icon-dots-horizontal').click({force: true});
cy.get('.menu-options').should('exist').first().within(() => {
cy.contains('Hide').should('exist');
cy.contains('Delete').should('exist');
// Some colours
cy.contains('Brown').should('exist');
cy.contains('Gray').should('exist');
cy.contains('Orange').should('exist');
// Click on green
cy.contains('Green').should('be.visible').click();
});
});
cy.get('.KanbanColumnHeader').last().within(() => {
cy.get('.Label.propColorGreen').should('exist');
});
});
it('MM-T4287 Hiding/unhiding a group', () => {
// Step 1: Create an empty board and add a group
cy.visit('/boards');
cy.uiCreateEmptyBoard();
cy.contains('+ Add a group').click({force: true});
cy.get('.KanbanColumnHeader .Editable[value=\'New group\']').should('exist');
cy.get('.KanbanColumnHeader .Editable[value=\'New group\']').
clear().
type('Group 1').
blur();
cy.get('.KanbanColumnHeader .Editable[value=\'Group 1\']').should('exist');
// Step 2: Click on the three dots next to "Group 1"
cy.get('.KanbanColumnHeader').last().within(() => {
cy.get('.icon-dots-horizontal').click({force: true});
cy.get('.menu-options').should('exist').first().within(() => {
cy.contains('Hide').should('exist');
cy.contains('Delete').should('exist');
// Some colours
cy.contains('Brown').should('exist');
cy.contains('Gray').should('exist');
cy.contains('Orange').should('exist');
});
});
// Step 3: Click on "Hide"
cy.contains('Hide').click({force: true});
cy.get('.octo-board-hidden-item').contains('Group 1').should('exist');
cy.get('.KanbanColumnHeader .Editable[value=\'Group 1\']').should('not.exist');
// Step 4: Click "Group 1", then click "Show" in the dropdown
cy.contains('Group 1').click({force: true});
cy.contains('Show').click({force: true});
cy.get('.octo-board-hidden-item').should('not.exist');
cy.get('.KanbanColumnHeader .Editable[value=\'Group 1\']').should('exist');
});
});

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

@@ -0,0 +1,301 @@
// 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 @accessibility @mfa
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {isMac} from '../../../utils';
describe('Verify Accessibility Support in different sections in Settings and Profile Dialog', () => {
const accountSettings = {
profile: [
{key: 'name', label: 'Full Name', type: 'text'},
{key: 'username', label: 'Username', type: 'text'},
{key: 'nickname', label: 'Nickname', type: 'text'},
{key: 'position', label: 'Position', type: 'text'},
{key: 'email', label: 'Email', type: 'text'},
{key: 'picture', label: 'Profile Picture', type: 'image'},
],
security: [
{key: 'password', label: 'Password', type: 'text'},
{key: 'mfa', label: 'Multi-factor Authentication', type: 'optional'},
],
};
const settings = {
notifications: [
{key: 'desktop', label: 'Desktop Notifications', type: 'radio'},
{key: 'email', label: 'Email Notifications', type: 'radio'},
{key: 'push', label: 'Mobile Push Notifications', type: 'radio'},
{key: 'keys', label: 'Words That Trigger Mentions', type: 'checkbox'},
{key: 'comments', label: 'Reply notifications', type: 'radio'},
],
display: [
{key: 'theme', label: 'Theme', type: 'radio'},
{key: 'clock', label: 'Clock Display', type: 'radio'},
{key: 'name_format', label: 'Teammate Name Display', type: 'none'},
{key: 'availabilityStatus', label: 'Show online availability on profile images', type: 'radio'},
{key: 'lastactive', label: 'Share last active time', type: 'radio'},
{key: 'timezone', label: 'Timezone', type: 'none'},
{key: 'collapse', label: 'Default Appearance of Image Previews', type: 'radio'},
{key: 'message_display', label: 'Message Display', type: 'radio'},
{key: 'click_to_reply', label: 'Click to open threads', type: 'radio'},
{key: 'channel_display_mode', label: 'Channel Display', type: 'radio'},
{key: 'one_click_reactions_enabled', label: 'Quick reactions on messages', type: 'radio'},
{key: 'languages', label: 'Language', type: 'dropdown'},
],
sidebar: [
{key: 'showUnreadsCategory', label: 'Group unread channels separately', type: 'multiple'},
{key: 'limitVisibleGMsDMs', label: 'Number of direct messages to show', type: 'radio'},
],
advanced: [
{key: 'advancedCtrlSend', label: `Send Messages on ${isMac() ? '⌘+ENTER' : 'CTRL+ENTER'}`, type: 'radio'},
{key: 'formatting', label: 'Enable Post Formatting', type: 'radio'},
{key: 'joinLeave', label: 'Enable Join/Leave Messages', type: 'radio'},
// As only setting in advancedPreviewFeatures was related to editor preview this isn't required at the moment,
// may later on we can add it if we add more settings inside it
// {key: 'advancedPreviewFeatures', label: 'Preview Pre-release Features', type: 'checkbox'},
],
};
let url;
before(() => {
// # Update Configs
cy.apiUpdateConfig({
ServiceSettings: {
EnableMultifactorAuthentication: true,
},
DisplaySettings: {
ExperimentalTimezone: true,
},
SamlSettings: {
Enable: false,
},
});
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
url = offTopicUrl;
cy.visit(offTopicUrl);
cy.postMessage('hello');
});
});
afterEach(() => {
// # Close the modal
cy.uiClose();
});
it('MM-T1465_1 Verify Label & Tab behavior in section links', () => {
// * Verify aria-label and tab support in section of Account settings modal
cy.uiOpenProfileModal();
cy.findByRole('tab', {name: 'profile settings'}).should('be.visible').focus().should('be.focused');
['profile settings', 'security'].forEach((text) => {
// * Verify aria-label on each tab and it supports navigating to the next tab with arrow keys
cy.focused().should('have.attr', 'aria-label', text).type('{downarrow}');
});
cy.uiClose();
// * Verify aria-label and tab support in section of Settings modal
cy.uiOpenSettingsModal();
cy.findByRole('tab', {name: 'notifications'}).should('be.visible').focus().should('be.focused');
['notifications', 'display', 'sidebar', 'advanced'].forEach((text) => {
// * Verify aria-label on each tab and it supports navigating to the next tab with arrow keys
cy.focused().should('have.attr', 'aria-label', text).type('{downarrow}');
});
});
it('MM-T1465_2 Verify Accessibility Support in each section in Settings and Profile Dialog', () => {
cy.visit(url);
// # Open account settings modal
cy.uiOpenProfileModal();
// * Verify if the focus goes to the individual fields in Profile section
cy.findByRole('tab', {name: 'profile settings'}).click().tab();
verifySettings(accountSettings.profile);
// * Verify if the focus goes to the individual fields in Security section
cy.findByRole('tab', {name: 'security'}).click().tab();
verifySettings(accountSettings.security);
cy.uiClose();
// # Open settings modal
cy.uiOpenSettingsModal();
// * Verify if the focus goes to the individual fields in Notifications section
cy.findByRole('tab', {name: 'notifications'}).click().tab();
verifySettings(settings.notifications);
// // * Verify if the focus goes to the individual fields in Display section
cy.findByRole('tab', {name: 'display'}).click().tab();
verifySettings(settings.display);
// // * Verify if the focus goes to the individual fields in Sidebar section
cy.findByRole('tab', {name: 'sidebar'}).click().tab();
verifySettings(settings.sidebar);
// // * Verify if the focus goes to the individual fields in Advanced section
cy.findByRole('tab', {name: 'advanced'}).click().tab();
verifySettings(settings.advanced);
});
it('MM-T1481 Verify Correct Radio button behavior in Settings and Profile', () => {
cy.visit(url);
cy.uiOpenSettingsModal();
cy.get('#notificationsButton').click();
cy.get('#desktopEdit').click();
cy.get('#desktopNotificationAllActivity').check().should('be.checked').tab().check();
cy.get('#desktopNotificationMentions').should('be.checked').tab().check();
cy.get('#desktopNotificationNever').should('be.checked');
});
it('MM-T1482 Input fields in Settings and Profile should read labels', () => {
cy.visit(url);
cy.uiOpenProfileModal();
accountSettings.profile.forEach((section) => {
if (section.type === 'text') {
cy.get(`#${section.key}Edit`).click();
cy.get('.setting-list-item .form-group').each(($el) => {
if ($el.find('input').length) {
cy.wrap($el).find('.control-label').invoke('text').then((label) => {
cy.wrap($el).find('input').should('have.attr', 'aria-label', label);
});
}
});
}
});
});
it('MM-T1485 Language dropdown should read labels', () => {
cy.visit(url);
cy.uiOpenSettingsModal();
cy.get('#displayButton').click();
cy.get('#languagesEdit').click();
cy.get('#displayLanguage').within(() => {
cy.get('input').should('have.attr', 'aria-autocomplete', 'list').and('have.attr', 'aria-labelledby', 'changeInterfaceLanguageLabel').as('inputEl');
});
cy.get('#changeInterfaceLanguageLabel').should('be.visible').and('have.text', 'Change interface language');
// # When enter key is pressed on dropdown, it should expand and collapse
cy.get('@inputEl').typeWithForce('{enter}');
cy.get('#displayLanguage>div').should('have.class', 'react-select__control--menu-is-open');
cy.get('@inputEl').typeWithForce('{enter}');
cy.get('#displayLanguage>div').should('not.have.class', 'react-select__control--menu-is-open');
// # Press down arrow twice and check aria label
cy.get('@inputEl').typeWithForce('{enter}');
cy.get('@inputEl').typeWithForce('{downarrow}{downarrow}');
cy.get('#displayLanguage>span').as('ariaEl').within(($el) => {
cy.wrap($el).should('have.attr', 'aria-live', 'assertive');
cy.get('#aria-context').should('contain', 'option English (Australia) focused').and('contain', 'Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu.');
});
// # Check if language setting gets changed after user presses enter
cy.get('@inputEl').typeWithForce('{enter}');
cy.get('#displayLanguage').should('contain', 'English (Australia)');
cy.get('@ariaEl').get('#aria-selection-event').should('contain', 'option English (Australia), selected');
// # Press down arrow, then up arrow and press enter
cy.get('@inputEl').typeWithForce('{downarrow}{downarrow}{downarrow}{uparrow}');
cy.get('@ariaEl').get('#aria-context').should('contain', 'option English (US) focused');
cy.get('@inputEl').typeWithForce('{enter}');
cy.get('#displayLanguage').should('contain', 'English (US)');
cy.get('@ariaEl').get('#aria-selection-event').should('contain', 'option English (US), selected');
});
it('MM-T1488 Profile Picture should read labels', () => {
cy.visit(url);
// # Go to Edit Profile picture
cy.uiOpenProfileModal();
cy.get('#pictureEdit').click();
// * Verify image alt in profile image
cy.get('.profile-img').should('have.attr', 'alt', 'profile image');
cy.get('#generalSettings').then((el) => {
if (el.find('.profile-img__remove').length > 0) {
cy.findByTestId('removeSettingPicture').click();
cy.uiSave();
cy.get('#pictureEdit').click();
}
});
// * Check Labels in different buttons
cy.findByTestId('inputSettingPictureButton').should('have.attr', 'aria-label', 'Select');
cy.uiSaveButton().should('have.attr', 'disabled');
cy.findByTestId('cancelSettingPicture').should('have.attr', 'aria-label', 'Cancel');
// # Upload a pic and save
cy.findByTestId('uploadPicture').attachFile('mattermost-icon.png');
cy.uiSave();
// # Click on Edit Profile Picture
cy.get('#pictureEdit').click();
// * Verify image alt in profile image
cy.get('.profile-img').should('have.attr', 'alt', 'profile image');
// # Option to Remove Profile picture should be present
cy.findByTestId('removeSettingPicture').within(() => {
cy.get('.sr-only').should('have.text', 'Remove Profile Picture');
});
// # Check tab behavior
cy.findByTestId('removeSettingPicture').focus().tab({shift: true}).tab();
cy.findByTestId('removeSettingPicture').should('have.class', 'a11y--active a11y--focused').tab();
cy.findByTestId('inputSettingPictureButton').should('have.class', 'a11y--active a11y--focused').tab();
cy.findByTestId('cancelSettingPicture').should('have.class', 'a11y--active a11y--focused');
// # Remove the profile picture and check the tab behavior
cy.findByTestId('removeSettingPicture').click().wait(TIMEOUTS.HALF_SEC);
cy.findByTestId('inputSettingPictureButton').focus().tab({shift: true}).tab();
cy.findByTestId('inputSettingPictureButton').should('have.class', 'a11y--active a11y--focused').tab();
cy.uiSaveButton().should('be.focused').tab();
cy.findByTestId('cancelSettingPicture').should('have.class', 'a11y--active a11y--focused');
cy.uiSave();
});
it('MM-T1496 Security Settings screen should read labels', () => {
cy.visit(url);
// # Go to Security Settings
cy.uiOpenProfileModal();
cy.get('#securityButton').click();
// * Check Tab behavior in MFA section
cy.get('#mfaEdit').click();
cy.get('#passwordEdit').focus().tab({shift: true}).tab().tab();
cy.get('.setting-list a.btn').should('have.class', 'a11y--active a11y--focused').tab();
cy.get('#cancelSetting').should('have.class', 'a11y--active a11y--focused');
// * Check Tab behavior in Sign-In Method if its available
cy.get('.user-settings').then((el) => {
if (el.find('#signinEdit').length) {
cy.get('#signinEdit').click();
cy.get('#mfaEdit').focus().tab({shift: true}).tab().tab();
cy.get('.setting-list a.btn').should('have.class', 'a11y--active a11y--focused').tab();
cy.get('#cancelSetting').should('have.class', 'a11y--active a11y--focused');
}
});
});
});
function verifySettings(settings) {
settings.forEach((setting) => {
cy.focused().should('have.id', `${setting.key}Edit`);
cy.findByText(setting.label);
cy.focused().tab();
});
}

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

@@ -0,0 +1,81 @@
// 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 @accessibility
describe('Verify Accessibility Support in different Buttons', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
// # Post a message
cy.postMessage('hello');
});
});
it('MM-T1459 Accessibility Support in RHS expand and close icons', () => {
cy.clickPostCommentIcon();
cy.get('#rhsContainer').
should('be.visible').
within(() => {
// * Verify accessibility support in Sidebar Expand and Shrink icon
cy.get('button.sidebar--right__expand').
should('have.attr', 'aria-label', 'Expand').
within(() => {
cy.get('.icon-arrow-expand').should('have.attr', 'aria-label', 'Expand Sidebar Icon');
cy.get('.icon-arrow-collapse').should('have.attr', 'aria-label', 'Collapse Sidebar Icon');
});
// * Verify accessibility support in Close icon
cy.get('#rhsCloseButton').
should('have.attr', 'aria-label', 'Close').
within(() => {
cy.get('.icon-close').should('have.attr', 'aria-label', 'Close Sidebar Icon');
});
// # Close the sidebar
cy.get('#rhsCloseButton').click();
});
});
it('MM-T1461 Accessibility Support in different buttons in Channel Header', () => {
// # Ensure the focus is on the Toggle Favorites button
cy.uiGetChannelFavoriteButton().
focus().
tab({shift: true}).
tab();
// * Verify accessibility support in Favorites button
cy.uiGetChannelFavoriteButton().
should('be.focused').
and('have.attr', 'aria-label', 'add to favorites').
click();
// * Verify accessibility support if Channel is added to Favorites
cy.uiGetChannelFavoriteButton().
should('have.attr', 'aria-label', 'remove from favorites');
// # Ensure the focus is on the Toggle Favorites button
cy.uiGetChannelFavoriteButton().
focus().
tab({shift: true}).
tab();
// # Press tab until the focus is on the Pinned posts button
cy.focused().tab().tab();
// * Verify accessibility support in Pinned Posts button
cy.uiGetChannelPinButton().
should('be.focused').
and('have.attr', 'aria-label', 'Pinned posts').
tab().tab().tab().tab();
});
});

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

@@ -0,0 +1,189 @@
// 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.
// ***************************************************************
// Group: @channels @accessibility
describe('Verify Accessibility Support in Dropdown Menus', () => {
let offTopicUrl;
before(() => {
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
cy.apiLogin(sysadmin);
cy.apiInitSetup().then(({offTopicUrl: url}) => {
offTopicUrl = url;
});
cy.apiCreateTeam('other-team', 'Other Team');
});
});
beforeEach(() => {
// Visit the Off Topic channel
cy.visit(offTopicUrl);
cy.postMessage('hello');
});
it('MM-T1464 Accessibility Support in Channel Menu Dropdown', () => {
// # Press tab from the Channel Favorite button
cy.uiGetChannelFavoriteButton().
focus().
tab({shift: true}).
tab().
tab({shift: true});
// * Verify the aria-label in channel menu button
cy.uiGetChannelHeaderButton().
findByLabelText('channel menu').
should('be.focused').click();
// * Verify the accessibility support in the Channel Dropdown menu
cy.uiGetChannelMenu().
parent().
should('have.attr', 'aria-label', 'channel menu').
and('have.attr', 'role', 'menu');
// * Verify the first option is not selected by default
cy.uiGetChannelMenu().children().eq(0).should('not.be.focused');
// # Press tab
cy.focused().tab();
// * Verify the accessibility support in the Channel Dropdown menu items
const menuItems = [
'View Info',
'Move to...',
'Notification Preferences',
'Mute Channel',
'Add Members',
'Manage Members',
'Edit Channel Header',
'Edit Channel Purpose',
'Rename Channel',
'Convert to Private Channel',
'Archive Channel',
'Leave Channel',
];
menuItems.forEach((item) => {
// * Verify that the menu item is focused
cy.uiGetChannelMenu().findByText(item).parent().should('be.focused');
// # Press tab for next item
cy.focused().tab();
});
// * Verify if menu is closed when we press Escape
cy.get('body').typeWithForce('{esc}');
cy.uiGetChannelMenu({exist: false});
});
it('MM-T1476 Accessibility Support in Team Menu Dropdown', () => {
// # Open team menu
cy.uiGetLHSHeader().click();
// * Verify the accessibility support in the Main Menu Dropdown
cy.findByRole('menu').
should('exist').
and('have.attr', 'aria-label', 'team menu').
and('have.class', 'a11y__popup');
// * Verify the first option is not selected by default
cy.uiGetLHSTeamMenu().find('.MenuItem').
children().eq(0).
should('not.be.focused').
focus();
// * Verify the accessibility support in the Main Menu Dropdown items
const menuItems = [
{id: 'invitePeople', label: 'Invite People dialog'},
{id: 'teamSettings', label: 'Team Settings dialog'},
{id: 'manageMembers', label: 'Manage Members dialog'},
{id: 'joinTeam', text: 'Join Another Team'},
{id: 'leaveTeam', label: 'Leave Team dialog'},
{id: 'createTeam', text: 'Create a Team'},
];
menuItems.forEach((item) => {
// * Verify that the menu item is focused
if (item.label) {
cy.focused().should('have.attr', 'aria-label', item.label);
} else {
cy.focused().should('have.text', item.text);
}
// # Press tab for next item
cy.focused().tab();
});
cy.uiGetLHSTeamMenu().find('.MenuItem').each((el) => {
cy.wrap(el).should('have.attr', 'role', 'menuitem');
});
// * Verify if menu is closed when we press Escape
cy.get('body').typeWithForce('{esc}');
cy.uiGetLHSTeamMenu().should('not.exist');
});
it('MM-T1477 Accessibility Support in Status Dropdown - KNOWN ISSUE: MM-45716', () => {
// # Focus to set status button
cy.uiGetSetStatusButton().focus().tab({shift: true}).tab();
// * Verify the aria-label in status menu button
cy.uiGetSetStatusButton().
should('be.focused').
click();
// * Verify the accessibility support in the Status Dropdown
cy.uiGetStatusMenuContainer().
should('have.attr', 'aria-label', 'set status').
and('have.class', 'a11y__popup').
and('have.attr', 'role', 'menu');
// * Verify the first option is not selected by default
cy.uiGetStatusMenuContainer().find('.dropdown-menu').children().eq(0).should('not.be.focused');
// # Press tab
cy.focused().tab();
// * Verify first focus is on menu header which is the profile image
cy.uiGetStatusMenuContainer().within(() => {
cy.findByAltText('user profile image').should('be.focused');
});
// # Press tab
cy.focused().tab();
// * Verify the accessibility support in the Status Dropdown menu items
const menuItems = [
{id: 'status-menu-custom-status', label: 'Set a Custom Status dialog'},
{id: 'status-menu-online', label: 'online'},
{id: 'status-menu-away', label: 'away'},
{id: 'status-menu-dnd_menuitem', label: 'do not disturb. disables all notifications'},
{id: 'status-menu-offline', label: 'offline'},
{id: 'accountSettings', label: 'Profile dialog'},
{id: 'logout', label: 'Log Out'},
];
menuItems.forEach((item) => {
// * Verify that the menu item is focused
cy.uiGetStatusMenuContainer().find(`#${item.id}`).
should('be.visible').
findAllByLabelText(item.label).first().
should('be.focused');
// # Press tab for next item
cy.focused().tab();
});
// * Verify if menu is closed when we press Escape
cy.get('body').typeWithForce('{esc}');
cy.uiGetStatusMenuContainer({exist: false});
});
});

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

@@ -0,0 +1,77 @@
// 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 @accessibility
// * Verify the accessibility support in the different images
describe('Verify Accessibility Support in Different Images', () => {
let otherUser;
before(() => {
cy.apiInitSetup().then(({offTopicUrl, user}) => {
otherUser = user;
// Visit the Off Topic channel
cy.visit(offTopicUrl);
});
});
it('MM-T1508 Accessibility support in different images', () => {
// * Verify image alt in profile image
cy.uiGetProfileHeader().
find('.Avatar').
should('have.attr', 'alt', 'user profile image');
// # Upload an image in the post
cy.get('#fileUploadInput').attachFile('small-image.png');
cy.postMessage('Image upload');
// * Verify accessibility in images uploaded in a post
cy.getLastPostId().then((postId) => {
cy.get(`#${postId}_message`).within(() => {
cy.get('img').should('be.visible').should('have.attr', 'aria-label', 'file thumbnail small-image.png');
});
});
// # Post a message as a different user
cy.getCurrentChannelId().then((channelId) => {
const message = `hello from ${otherUser.username}: ${Date.now()}`;
cy.postMessageAs({sender: otherUser, message, channelId});
});
// # Open profile popover
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.get('.user-popover').click();
});
// * Verify image alt in profile popover
cy.get('#user-profile-popover').within(() => {
cy.get('.Avatar').should('have.attr', 'alt', `${otherUser.username} profile image`);
});
});
// # Open Settings > Display > Themes
cy.uiOpenSettingsModal('Display').within(() => {
cy.get('#displayButton').click();
cy.get('#displaySettingsTitle').should('exist');
cy.get('#themeTitle').scrollIntoView().should('be.visible');
cy.get('#themeEdit').click();
// * Verify image alt in Theme Images
cy.get('#displaySettings').within(() => {
cy.get('.appearance-section>div').children().each(($el) => {
cy.wrap($el).get('#denim-theme-icon').should('have.text', 'Denim theme icon');
});
});
});
});
});

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

@@ -0,0 +1,148 @@
// 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 @accessibility
import {getRandomId} from '../../../utils';
function postMessages(testChannel, otherUser, count) {
for (let index = 0; index < count; index++) {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
const otherMessage = `hello from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id});
}
}
describe('Verify Accessibility keyboard usability across different regions in the app', () => {
const count = 5;
let testUser;
let otherUser;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testUser = user;
testChannel = channel;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(team.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id).then(() => {
// # Login as test user, visit test channel and post few messages
cy.apiLogin(user);
cy.visit(`/${team.name}/channels/${testChannel.name}`);
});
});
});
});
});
it('MM-T1513_2 Verify Keyboard support in Search Results', () => {
// # Post few messages
postMessages(testChannel, otherUser, count);
// # Search for a term
cy.get('#searchBox').typeWithForce('hello').typeWithForce('{enter}');
// # Change the focus to search results
cy.get('#searchContainer').within(() => {
cy.get('button.sidebar--right__expand').focus().tab({shift: true}).tab();
cy.focused().tab().tab().tab().tab();
});
cy.get('body').type('{downarrow}{uparrow}');
// # Use down arrow keys and verify if results are highlighted sequentially
for (let index = 0; index < count; index++) {
cy.get('#search-items-container').children('.search-item__container').eq(index).then(($el) => {
// * Verify search result is highlighted
cy.get($el).find('.post').should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{downarrow}');
});
}
// # Use up arrow keys and verify if results are highlighted sequentially
for (let index = count; index > 0; index--) {
cy.get('#search-items-container').children('.search-item__container').eq(index).then(($el) => {
// * Verify search result is highlighted
cy.get($el).find('.post').should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{uparrow}');
});
}
});
it('MM-T1513_1 Verify Keyboard support in RHS', () => {
// # Post Message as Current user
const message = `hello from ${testUser.username}: ${getRandomId()}`;
cy.postMessage(message);
// # Post few replies on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
for (let index = 0; index < count; index++) {
const replyMessage = `A reply ${getRandomId()}`;
cy.postMessageReplyInRHS(replyMessage);
const messageFromOther = `reply from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: messageFromOther, channelId: testChannel.id, rootId: postId});
}
});
// * Verify that the highlight order is in the reverse direction in RHS
cy.get('#rhsContainer .post-right__content').should('have.attr', 'data-a11y-order-reversed', 'true').and('have.attr', 'data-a11y-focus-child', 'true');
// # Change the focus to the last post
cy.get('#rhsContainer').within(() => {
cy.uiGetReplyTextBox().focus().tab({shift: true});
});
cy.get('body').type('{uparrow}{downarrow}');
// # Use up arrow keys and verify if results are highlighted sequentially
const total = (count * 2) + 1; // # total number of expected posts in RHS
let row = total - 1; // # the row index which should be focused
for (let index = count; index > 0; index--) {
cy.get('#rhsContainer .post-right-comments-container .post').eq(row).then(($el) => {
// * Verify search result is highlighted
cy.get($el).should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{uparrow}');
});
row--;
}
// # Use down arrow keys and verify if posts are highlighted sequentially
for (let index = count; index > 0; index--) {
cy.get('#rhsContainer .post-right-comments-container .post').eq(row).then(($el) => {
// * Verify search result is highlighted
cy.get($el).should('have.class', 'a11y--active a11y--focused');
cy.get('body').type('{downarrow}');
});
row++;
}
});
it('MM-T1499 Verify Screen reader should not switch to virtual cursor mode', () => {
// # Open RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
// * Verify Screen reader should not switch to virtual cursor mode. This is handled by adding a role=application attribute
const regions = ['#sidebar-left', '#rhsContainer .post-right__content', '.search__form', '#advancedTextEditorCell'];
regions.forEach((region) => {
cy.get(region).should('have.attr', 'role', 'application');
});
cy.get(`#post_${postId}`).children('.post__content').eq(0).should('have.attr', 'role', 'application');
cy.get(`#rhsPost_${postId}`).children('.post__content').eq(0).should('have.attr', 'role', 'application');
});
});
});

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

@@ -0,0 +1,140 @@
// 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 @accessibility
import {getRandomId} from '../../../utils';
function postMessages(testChannel, otherUser, count) {
for (let index = 0; index < count; index++) {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
const otherMessage = `hello from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id});
}
}
function verifyNavSupport(element, label, tabOrder) {
cy.get(element).
should('have.attr', 'aria-label', label).
and('have.attr', 'data-a11y-sort-order', tabOrder).
and('have.class', 'a11y__region a11y--active');
}
describe('Verify Quick Navigation support across different regions in the app', () => {
let otherUser;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testChannel = channel;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(team.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id).then(() => {
// # Login as test user, visit test channel and post few messages
cy.apiLogin(user);
cy.visit(`/${team.name}/channels/${testChannel.name}`);
// # Post few messages
postMessages(testChannel, otherUser, 5);
});
});
});
});
});
it('MM-T1460_1 Verify Navigation Support in Post List & Post Input', () => {
// # Shift focus to the last post
cy.uiGetPostTextBox().focus().tab({shift: true}).tab({shift: true});
cy.get('body').type('{uparrow}{downarrow}');
// * Verify post region reads out correctly
verifyNavSupport('#virtualizedPostListContent > div', 'message list main region', '1');
// # Shift focus to the post input
cy.uiGetPostTextBox().focus().tab().tab({shift: true});
// * Verify post input region reads out correctly
verifyNavSupport('#advancedTextEditorCell', 'Login Successful message input complimentary region', '2');
});
it('MM-T1460_3 Verify Navigation Support in RHS Post List & RHS Post Input', () => {
// # Open RHS and reply
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
const replyMessage = 'A reply to an older post';
cy.postMessageReplyInRHS(replyMessage);
});
// * Verify post message in RHS
cy.uiGetRHS().within(() => {
// # Shift the focus to the last post
cy.uiGetReplyTextBox().focus().tab({shift: true}).type('{uparrow}');
// * Verify post region on RHS reads out correctly
verifyNavSupport('.post-right__content', 'message details complimentary region', '3');
// # Shift the focus to the RHS input
cy.uiGetReplyTextBox().focus().tab().tab({shift: true});
// * Verify post input on RHS reads out correctly
cy.get('#advancedTextEditorCell').
should('have.attr', 'aria-label', 'Login Successful message input complimentary region').
and('have.attr', 'data-a11y-sort-order', '2').
and('have.class', 'a11y__region');
cy.uiGetReplyTextBox().
should('have.class', 'a11y--active a11y--focused');
});
});
it('MM-T1460_5 Verify Navigation Support in LHS Sidebar', () => {
// # Change the focus to Main Menu button
cy.uiGetLHSAddChannelButton().focus().tab({shift: true}).tab();
// # Change the focus to the LHS sidebar
cy.focused().tab();
// * Verify nav support in LHS channel navigator
verifyNavSupport('#lhsNavigator', 'channel navigator region', '6');
// # Change the focus to the LHS sidebar
cy.focused().tab().tab().tab().tab();
// * Verify nav support in LHS sidebar
verifyNavSupport('#sidebar-left', 'channel sidebar region', '7');
});
it('MM-T1460_6 Verify Navigation Support in Channel Header', () => {
// # Change the focus to Main Menu button
cy.get('#toggleFavorite').focus().tab({shift: true}).tab();
// * Verify nav support in LHS sidebar header
verifyNavSupport('#channel-header', 'channel header region', '8');
});
it('MM-T1460_8 Verify Navigation Support in Search Results', () => {
// # Search for some text
cy.get('#searchBox').should('be.visible').type('hello {enter}');
// # Change the focus to search results
cy.get('#searchContainer').within(() => {
cy.get('button.sidebar--right__expand').focus().tab({shift: true}).tab();
cy.focused().tab().tab().tab().tab();
});
cy.get('body').type('{downarrow}{uparrow}');
// * Verify nav support in Search Results Container
verifyNavSupport('#search-items-container', 'Search Results complimentary region', '3');
});
});

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

@@ -0,0 +1,86 @@
// 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 @accessibility
let previousEmoji = 'grinning';
function verifyArrowKeysEmojiNavigation(arrowKey, count) {
for (let index = 0; index < count; index++) {
cy.get('body').type(arrowKey);
// eslint-disable-next-line no-loop-func
cy.get('.emoji-picker__preview-name').invoke('text').then((selectedEmoji) => {
expect(selectedEmoji).not.equal(previousEmoji);
previousEmoji = selectedEmoji;
});
}
}
describe('Verify Accessibility Support in Popovers', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
// # Post a message
cy.postMessage(`hello from test user: ${Date.now()}`);
});
});
it('MM-T1489 Accessibility Support in Emoji Popover on click of Emoji Reaction button', () => {
cy.getLastPostId().then((postId) => {
// # Open the Emoji Popover
cy.clickPostReactionIcon(postId);
// * Verify accessibility support in Emoji Search input
cy.get('#emojiPickerSearch').should('have.attr', 'aria-label', 'Search for an emoji');
// # Focus on the first emoji Category
cy.get('#emojiPickerCategories').children().eq(0).focus().tab({shift: true}).tab();
const categories = [
{ariaLabel: 'emoji_picker.smileys-emotion', header: 'Smileys & Emotion'},
{ariaLabel: 'emoji_picker.people-body', header: 'People & Body'},
{ariaLabel: 'emoji_picker.animals-nature', header: 'Animals & Nature'},
{ariaLabel: 'emoji_picker.food-drink', header: 'Food & Drink'},
{ariaLabel: 'emoji_picker.travel-places', header: 'Travel Places'},
{ariaLabel: 'emoji_picker.activities', header: 'Activities'},
{ariaLabel: 'emoji_picker.objects', header: 'Objects'},
{ariaLabel: 'emoji_picker.symbols', header: 'Symbols'},
{ariaLabel: 'emoji_picker.flags', header: 'Flags'},
];
// * Verify if emoji Categories gets the focus when tab is pressed
cy.get('#emojiPickerCategories').children('.emoji-picker__category').each(($el, index) => {
// * Verify each category
if (index < categories.length) {
// * Verify accessibility support in emoji category
cy.get($el).should('have.class', 'a11y--active a11y--focused').should('have.attr', 'aria-label', categories[index].ariaLabel);
// * Verify if corresponding section is displayed when emoji category has focus and clicked
cy.get($el).trigger('click').tab();
// * Verify if corresponding section is displayed
cy.findByText(categories[index].header).should('be.visible');
// * Verify emoji navigation using arrow keys
verifyArrowKeysEmojiNavigation('{rightarrow}', 3);
verifyArrowKeysEmojiNavigation('{leftarrow}', 2);
// # Press tab
cy.get($el).focus().tab();
}
});
// # Close the Emoji Popover
cy.get('body').click();
});
});
});

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

@@ -0,0 +1,363 @@
// 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 @accessibility
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Verify Accessibility Support in Post', () => {
let testUser;
let otherUser;
let testTeam;
let testChannel;
let emojiPickerEnabled;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testUser = user;
testTeam = team;
testChannel = channel;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
});
});
cy.apiGetConfig().then(({config}) => {
emojiPickerEnabled = config.ServiceSettings.EnableEmojiPicker;
});
});
});
beforeEach(() => {
// # Login as test user and visit the test channel
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.get('#postListContent', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
it('MM-T1479 Verify Reader reads out the post correctly on Center Channel', () => {
const {lastMessage} = postMessages(testChannel, otherUser, 1);
performActionsToLastPost();
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true});
cy.get('body').type('{uparrow}{downarrow}');
// * Verify post message in Center Channel
cy.getLastPostId().then((postId) => {
// * Verify reader reads out the post correctly
verifyPostLabel(`#post_${postId}`, otherUser.username, `wrote, ${lastMessage}, 2 reactions, message is saved and pinned`);
});
});
it('MM-T1480 Verify Reader reads out the post correctly on RHS', () => {
const {lastMessage} = postMessages(testChannel, otherUser, 1);
performActionsToLastPost();
// # Post a reply on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
const replyMessage = 'A reply to an older post';
cy.postMessageReplyInRHS(replyMessage);
// * Verify post message in RHS
cy.get('#rhsContainer').within(() => {
// # Shift the focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}');
// * Verify reader reads out the post correctly
verifyPostLabel(`#rhsPost_${postId}`, otherUser.username, `wrote, ${lastMessage}, 2 reactions, message is saved and pinned`);
});
// * Verify reply message in RHS
cy.getLastPostId().then((replyId) => {
cy.get('#rhsContainer').within(() => {
// # Shift the focus to the last reply message
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}{downarrow}');
// * Verify reader reads out the post correctly
verifyPostLabel(`#rhsPost_${replyId}`, testUser.username, `replied, ${replyMessage}`);
});
});
});
});
it('MM-T1486_1 Verify different Post Focus on Center Channel', () => {
postMessages(testChannel, otherUser, 5);
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}');
// * Verify if focus changes to different posts when we use up arrows
for (let index = 1; index < 5; index++) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#post_${postId}`).should('be.focused');
cy.get('body').type('{uparrow}');
});
}
// * Verify if focus changes to different posts when we use down arrows
for (let index = 5; index > 0; index--) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#post_${postId}`).should('be.focused');
cy.get('body').type('{downarrow}');
});
}
});
it('MM-T1486_2 Verify different Post Focus on RHS', () => {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
// # Post few replies on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
for (let index = 0; index < 3; index++) {
const replyMessage = `A reply ${getRandomId()}`;
cy.postMessageReplyInRHS(replyMessage);
const otherMessage = `reply from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id, rootId: postId});
}
});
cy.get('#rhsContainer').within(() => {
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}');
});
// * Verify if focus changes to different posts when we use up arrows
for (let index = 1; index < 5; index++) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#rhsPost_${postId}`).should('be.focused');
cy.get('body').type('{uparrow}');
});
}
// * Verify if focus changes to different posts when we use down arrows
for (let index = 5; index > 1; index--) {
cy.getNthPostId(-index - 1).then((postId) => {
cy.get(`#rhsPost_${postId}`).should('be.focused');
cy.get('body').type('{downarrow}');
});
}
});
it('MM-T1486_3 Verify Tab support on Post on Center Channel', () => {
postMessages(testChannel, otherUser, 1);
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true}).type('{uparrow}{downarrow}');
cy.focused().tab();
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
// * Verify focus is on profile image
cy.get('.status-wrapper button').first().should('be.focused');
cy.focused().tab();
// * Verify focus is on the username
cy.get('button.user-popover').should('be.focused').and('have.attr', 'aria-label', otherUser.username);
cy.focused().tab();
// * Verify focus is on the time
cy.get(`#CENTER_time_${postId}`).should('be.focused');
cy.focused().tab();
// eslint-disable-next-line no-negated-condition
if (!emojiPickerEnabled) {
// * Verify focus is on the actions button
cy.get(`#CENTER_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
cy.focused().tab();
} else {
for (let i = 0; i < 3; i++) {
// * Verify focus is on the reactions button
cy.get(`#recent_reaction_${i}`).should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
cy.focused().tab();
}
}
// * Verify focus is on the reactions button
cy.get(`#CENTER_reaction_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'add reaction');
cy.focused().tab();
// * Verify focus is on the save post button
cy.get(`#CENTER_flagIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'save');
cy.focused().tab();
// * Verify focus is on the comment button
cy.get(`#CENTER_commentIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'reply');
cy.focused().tab();
if (emojiPickerEnabled) {
// * Verify focus is on the more button
cy.get(`#CENTER_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'More');
cy.focused().tab();
}
// * Verify focus is on the post text
cy.get(`#postMessageText_${postId}`).should('be.focused').and('have.attr', 'aria-readonly', 'true');
});
});
});
it('MM-T1486_4 Verify Tab support on Post on RHS', () => {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
// # Post few replies on RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible');
const replyMessage = `A reply ${getRandomId()}`;
cy.postMessageReplyInRHS(replyMessage);
const otherMessage = `reply from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: otherMessage, channelId: testChannel.id, rootId: postId});
});
cy.get('#rhsContainer').within(() => {
// # Shift focus to the last post
cy.get('#FormattingControl_bold').focus().tab({shift: true}).tab({shift: true});
});
// * Verify reverse tab on RHS
cy.getLastPostId().then((postId) => {
cy.get(`#rhsPost_${postId}`).within(() => {
// * Verify focus is on the post text
cy.get(`#rhsPostMessageText_${postId}`).should('be.focused').and('have.attr', 'aria-readonly', 'true');
cy.focused().tab({shift: true});
if (emojiPickerEnabled) {
// * Verify focus is on the actions button
cy.get(`#RHS_COMMENT_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'More');
cy.focused().tab({shift: true});
}
// * Verify focus is on the save icon
cy.get(`#RHS_COMMENT_flagIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'save');
cy.focused().tab({shift: true});
// * Verify focus is on the reactions button
cy.get(`#RHS_COMMENT_reaction_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'add reaction');
cy.focused().tab({shift: true});
// eslint-disable-next-line no-negated-condition
if (!emojiPickerEnabled) {
// * Verify focus is on the actions button
cy.get(`#RHS_COMMENT_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
cy.focused().tab({shift: true});
} else {
cy.get('#recent_reaction_0').should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
cy.focused().tab({shift: true});
}
// * Verify focus is on the time
cy.get(`#RHS_COMMENT_time_${postId}`).should('be.focused');
cy.focused().tab({shift: true});
// * Verify focus is on the username
cy.get('button.user-popover').should('be.focused').and('have.attr', 'aria-label', otherUser.username);
cy.focused().tab({shift: true});
});
});
});
it('MM-T1462 Verify incoming messages are read', () => {
// # Make channel as read by switching back and forth to testChannel
cy.uiGetLhsSection('CHANNELS').findByText('Off-Topic').click();
cy.get('#postListContent', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
cy.uiGetLhsSection('CHANNELS').findByText(testChannel.display_name).click();
cy.get('#postListContent', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// # Submit a post as another user
const message = `verify incoming message from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message, channelId: testChannel.id});
cy.uiWaitUntilMessagePostedIncludes(message);
// # Get the element which stores the incoming messages
cy.get('#postListContent').within(() => {
cy.get('.sr-only').should('have.attr', 'aria-live', 'polite').as('incomingMessage');
});
// * Verify incoming message is read
cy.get('@incomingMessage').invoke('text').then((text) => {
expect(text).contain(message);
});
});
});
function postMessages(testChannel, otherUser, count) {
let lastMessage;
for (let index = 0; index < count; index++) {
// # Post Message as Current user
const message = `hello from current user: ${getRandomId()}`;
cy.postMessage(message);
lastMessage = `hello from ${otherUser.username}: ${getRandomId()}`;
cy.postMessageAs({sender: otherUser, message: lastMessage, channelId: testChannel.id});
}
return {lastMessage};
}
function performActionsToLastPost() {
// # Take some actions on the last post
cy.getLastPostId().then((postId) => {
// # Add grinning reaction
cy.clickPostReactionIcon(postId);
cy.findAllByTestId('grinning').first().trigger('mouseover', {force: true});
cy.get('.sprite-preview').should('be.visible');
cy.get('.emoji-picker__preview').should('be.visible').and('have.text', ':grinning:');
cy.findAllByTestId('grinning').first().click({force: true});
cy.get(`#postReaction-${postId}-grinning`).should('be.visible');
// # Add smile reaction
cy.clickPostReactionIcon(postId);
cy.findAllByTestId('smile').first().trigger('mouseover', {force: true});
cy.get('.sprite-preview').should('be.visible');
cy.get('.emoji-picker__preview').should('be.visible').and('have.text', ':smile:');
cy.findAllByTestId('smile').first().click({force: true});
cy.get(`#postReaction-${postId}-smile`).should('be.visible');
// # Save the post
cy.clickPostSaveIcon(postId);
// # Pin the post
cy.clickPostDotMenu(postId);
cy.get(`#pin_post_${postId}`).click();
cy.clickPostDotMenu(postId);
cy.get('body').type('{esc}');
});
}
function verifyPostLabel(elementId, username, labelSuffix) {
// # Shift focus to the last post
cy.get(elementId).as('lastPost').should('be.focused');
// * Verify reader reads out the post correctly
cy.get('@lastPost').then((el) => {
// # Get the post time
cy.wrap(el).find('time.post__time').invoke('text').then((time) => {
const expectedLabel = `At ${time} ${Cypress.dayjs().format('dddd, MMMM D')}, ${username} ${labelSuffix}`;
cy.wrap(el).should('have.attr', 'aria-label', expectedLabel);
});
});
}

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

@@ -0,0 +1,105 @@
// 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.
// ***************************************************************
// Group: @channels @accessibility @smoke
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Verify Accessibility Support in Channel Sidebar Navigation', () => {
let testUser;
let testTeam;
let testChannel;
let offTopicUrl;
before(() => {
let otherUser;
let otherChannel;
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, channel, user, offTopicUrl: url}) => {
testUser = user;
testTeam = team;
testChannel = channel;
offTopicUrl = url;
cy.apiCreateUser().then(({user: regularUser}) => {
cy.apiAddUserToTeam(testTeam.id, regularUser.id);
});
return cy.apiCreateChannel(testTeam.id, 'test', 'Test');
}).then(({channel}) => {
otherChannel = channel;
return cy.apiAddUserToChannel(otherChannel.id, testUser.id);
}).then(() => {
return cy.apiCreateUser({prefix: 'other'});
}).then(({user}) => {
otherUser = user;
return cy.apiAddUserToTeam(testTeam.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(testChannel.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(otherChannel.id, otherUser.id);
}).then(() => {
// # Post messages to have unread messages to test user
for (let index = 0; index < 5; index++) {
cy.postMessageAs({sender: otherUser, message: 'This is an old message', channelId: otherChannel.id});
}
// # Login as test user and visit the off-topic channel
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
it('MM-T1472 Verify Tab Support in Direct Messages section', () => {
const usersPrefixes = ['a', 'c', 'd'];
usersPrefixes.forEach((prefix) => {
//# Create users with prefixes in alphabetical order
cy.apiCreateUser({prefix}).then(({user: newUser}) => {
cy.apiCreateDirectChannel([testUser.id, newUser.id]).then(({channel}) => {
// # Post message in The DM channel
cy.postMessageAs({sender: newUser, message: 'test', channelId: channel.id});
});
});
});
// # Trigger DM with a user
cy.uiAddDirectMessage().click();
searchAndSelectUser('user');
searchAndSelectUser('user');
cy.uiGetButton('Go').click();
cy.wait(TIMEOUTS.TWO_SEC);
// # Press tab to the Create DM button and verify if the Plus button has focus
cy.uiAddDirectMessage().
tab({shift: true}).tab().
should('be.focused').
tab({shift: true}).tab({shift: true});
cy.focused().parent().parent().next().find('.SidebarChannel').each((el, i) => {
if (i === 0) {
cy.focused().findByText('DIRECT MESSAGES');
cy.focused().tab().tab().tab();
}
// * Verify if focus changes to different channels in Direct Messages section
cy.wrap(el).find('.SidebarLink').should('be.focused');
cy.focused().tab().tab().tab();
});
});
});
function searchAndSelectUser(text) {
cy.findByRole('textbox', {name: 'Search for people'}).
typeWithForce(text).
wait(TIMEOUTS.ONE_SEC);
cy.get('.more-modal__row.clickable').
first().
click();
}

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

@@ -0,0 +1,185 @@
// 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 @accessibility @smoke
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Verify Accessibility Support in Channel Sidebar Navigation', () => {
let testUser;
let testTeam;
let testChannel;
let offTopicUrl;
before(() => {
let otherUser;
let otherChannel;
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, channel, user, offTopicUrl: url}) => {
testUser = user;
testTeam = team;
testChannel = channel;
offTopicUrl = url;
return cy.apiCreateChannel(testTeam.id, 'test', 'Test');
}).then(({channel}) => {
otherChannel = channel;
return cy.apiAddUserToChannel(otherChannel.id, testUser.id);
}).then(() => {
return cy.apiCreateUser({prefix: 'other'});
}).then(({user}) => {
otherUser = user;
return cy.apiAddUserToTeam(testTeam.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(testChannel.id, otherUser.id);
}).then(() => {
return cy.apiAddUserToChannel(otherChannel.id, otherUser.id);
}).then(() => {
// # Post messages to have unread messages to test user
for (let index = 0; index < 5; index++) {
cy.postMessageAs({sender: otherUser, message: 'This is an old message', channelId: otherChannel.id});
}
});
});
beforeEach(() => {
// # Login as test user and visit the off-topic channel
cy.apiLogin(testUser);
(cy as any).apiSaveSidebarSettingPreference();
cy.visit(offTopicUrl);
cy.get('#postListContent').should('be.visible');
});
it('MM-T1470 Verify Tab Support in Channels section', () => {
// # Create some Public Channels
Cypress._.times(2, () => {
cy.apiCreateChannel(testTeam.id, 'public', 'public');
});
// # Wait for few seconds
cy.wait(TIMEOUTS.ONE_SEC);
// # Press tab to the Add Public Channel button
cy.uiGetLHSAddChannelButton().focus().tab().tab({shift: true});
// * Verify if the Plus button has focus
cy.findByRole('button', {name: 'Add Channel Dropdown'}).should('be.focused');
cy.focused().tab();
// * Verify if the Plus button has focus
cy.findByRole('button', {name: 'Find Channels'}).should('be.focused');
cy.focused().tab();
// * Verify if Insights has focus
cy.focused().should('have.text', 'Insights').tab();
// * Verify if focus changes to different channels in Unread section
cy.get('.SidebarChannel.unread').each((el) => {
cy.wrap(el).find('.unread-title').should('be.focused');
cy.focused().tab().tab();
});
cy.focused().parent().next().find('.SidebarChannel').each((el, i) => {
if (i === 0) {
cy.focused().findByText('CHANNELS');
cy.focused().tab().tab().tab();
}
// * Verify if focus changes to different channels in Channels section
cy.wrap(el).find('.SidebarLink').should('be.focused');
cy.focused().tab().tab();
});
});
it('MM-T1473 Verify Tab Support in Unreads section', () => {
// # Press tab from the Main Menu button
cy.uiGetLHSAddChannelButton().focus().tab().tab().tab();
// * Verify if focus changes to different channels in Unread section
cy.get('.SidebarChannel.unread').each((el) => {
cy.wrap(el).find('.unread-title').should('be.focused');
cy.focused().tab().tab();
});
});
it('MM-T1474 Verify Tab Support in Favorites section', () => {
// # Mark few channels as Favorites
markAsFavorite('off-topic');
markAsFavorite(testChannel.name);
// # Press tab from the add channel button down to all unread channels
cy.uiGetLHSAddChannelButton().focus().tab().tab().tab();
cy.get('.SidebarChannel.unread').each(() => {
cy.focused().tab().tab();
});
// * Verify if focus changes to different channels in Favorite Channels section
cy.focused().tab().tab().parent().next().find('.SidebarChannel').each((el, i) => {
if (i === 0) {
cy.focused().findByText('FAVORITES');
cy.focused().tab().tab().tab();
}
cy.wrap(el).find('.SidebarLink').should('be.focused');
cy.focused().tab().tab();
});
});
it('MM-T1475 Verify Up & Down Arrow support in Channel Sidebar', () => {
cy.apiCreateChannel(testTeam.id, 'public', 'Public', 'O');
cy.apiCreateChannel(testTeam.id, 'private', 'Private', 'P');
// # Mark few channels as Favorites
markAsFavorite('off-topic');
markAsFavorite(testChannel.name);
// # Press tab from the Main Menu button
cy.uiGetLHSAddChannelButton().focus().tab().tab().tab();
// # Press Down Arrow and then Up Arrow
cy.get('body').type('{downarrow}{uparrow}');
// * Verify if Unread Channels section has focus
cy.uiGetLhsSection('UNREADS').should('be.focused');
// # Press Down Arrow and check the focus
cy.get('body').type('{downarrow}');
// * Verify if Favorite Channels section has focus
cy.uiGetLhsSection('FAVORITES').should('be.focused');
// # Press Down Arrow and check the focus
cy.get('body').type('{downarrow}');
// * Verify if Public Channels section has focus
cy.uiGetLhsSection('CHANNELS').should('be.focused');
// # Press Down Arrow and check the focus
cy.get('body').type('{downarrow}');
// * Verify if Direct Messages section has focus
cy.uiGetLhsSection('DIRECT MESSAGES').should('be.focused');
});
});
function markAsFavorite(channelName) {
// # Visit the channel
cy.get(`#sidebarItem_${channelName}`).click();
cy.get('#postListContent').should('be.visible');
// # Remove from Favorites if already set
cy.get('#channelHeaderInfo').then((el) => {
if (el.find('#toggleFavorite.active').length) {
cy.get('#toggleFavorite').click();
}
});
// # mark it as Favorite
cy.get('#toggleFavorite').click();
}

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

@@ -0,0 +1,113 @@
// 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.
// ***************************************************************
// Group: @channels @account_settings
describe('Account Settings', () => {
let testUser: Cypress.UserProfile;
let testTeam: Cypress.Team;
let offTopic: string;
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup({userPrefix: 'other', loginAfter: true}).then(({offTopicUrl, user, team}) => {
cy.visit(offTopicUrl);
offTopic = offTopicUrl;
testUser = user;
testTeam = team;
});
});
it('MM-T2049 Account Settings link in own popover', () => {
// # Click avatar to open profile popover
cy.get('div.status-wrapper').should('be.visible').click();
// # Click account settings link
cy.get('#accountSettings').should('be.visible').click();
// # Check if account settings modal is open
cy.get('#accountSettingsModal').should('be.visible');
cy.uiClose();
});
it('MM-T2081 Password: Error on blank', () => {
// # Go to Profile > Security
cy.uiOpenProfileModal('Security');
// * Check that the Security tab is loaded
cy.get('#securityButton').should('be.visible');
// # Click the Security tab
cy.get('#securityButton').click();
// # Click "Edit" to the right of "Password"
cy.get('#passwordEdit').should('be.visible').click();
// # Save the settings
cy.uiSave();
// * Check that there is an error
cy.get('#clientError').should('be.visible').should('contain', 'Please enter your current password.');
cy.get('#serverError').should('not.exist');
cy.uiClose();
});
it('MM-T2074 New email not visible to other users until it has been confirmed', () => {
// # Login as admin
cy.apiLogout();
cy.apiAdminLogin();
// * Set require email verification to true
cy.visit('/admin_console/authentication/email');
cy.get('[id="EmailSettings.EnableSignInWithEmailtrue"]').should('be.visible').click();
cy.get('#saveSetting').invoke('attr', 'disabled').
then((disabled) => {
disabled ? '' : cy.uiSave();
});
cy.visit(offTopic);
// # Create user
cy.apiCreateUser({prefix: 'test'}).then(({user: newUser}) => {
// # Add user to team
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
// # Create DM channel
cy.apiCreateDirectChannel([testUser.id, newUser.id]).then(({channel}) => {
// # Login to first user
cy.uiLogout();
cy.uiLogin(testUser);
cy.visit(offTopic);
// * Update email
const oldEMail = testUser.email;
const newEMail = 'test@example.com';
cy.uiOpenProfileModal();
cy.get('#emailEdit').should('be.visible').click();
cy.get('#primaryEmail').should('be.visible').type(newEMail);
cy.get('#confirmEmail').should('be.visible').type(newEMail);
cy.get('#currentPassword').should('be.visible').type(testUser.password);
cy.uiSaveAndClose();
// * Send DM
cy.postMessageAs({sender: testUser, message: `@${newUser.username}`, channelId: channel.id});
// # Login to 2nd user
cy.uiLogout();
cy.uiLogin(newUser);
// * Check if email updated
cy.visit(`/${testTeam.name}/messages/@${testUser.username}`);
cy.get('#channelIntro .user-popover').should('be.visible').click();
cy.get('#user-profile-popover').should('be.visible').should('contain', oldEMail);
});
});
});
});
});

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

@@ -0,0 +1,49 @@
// 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 @account_setting
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Profile > Profile Settings', () => {
let otherUser: Cypress.UserProfile;
let testChannel: Cypress.Channel;
before(() => {
cy.apiInitSetup().then(({team, channel, offTopicUrl}) => {
testChannel = channel;
cy.apiCreateUser().then(({user}) => {
otherUser = user;
cy.apiAddUserToTeam(team.id, user.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, user.id);
});
});
// # Go to off-topic
cy.visit(offTopicUrl);
});
});
it('MM-T285 Status Menu stays open', () => {
// # Click the hamburger button
cy.uiGetSetStatusButton().click();
// * Menu should be visible
cy.uiGetStatusMenu();
// # Post a message as other user
cy.postMessageAs({sender: otherUser, message: 'abc', channelId: testChannel.id}).wait(TIMEOUTS.FIVE_SEC);
// * Menu should still be visible
cy.uiGetStatusMenu();
});
});

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

@@ -0,0 +1,78 @@
// 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 @account_setting
describe('Profile > Profile Settings > Position', () => {
beforeEach(() => {
cy.apiAdminLogin();
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
// # Visit off-topic channel
cy.visit(offTopicUrl);
});
});
it('MM-T2063 Position', () => {
const position = 'Master hacker';
// # Open 'Profile' modal and view the default 'Profile Settings'
cy.uiOpenProfileModal().within(() => {
// # Open 'Position' setting
cy.findByRole('heading', {name: 'Position'}).should('be.visible').click();
// # Enter new 'Position'
cy.findByRole('textbox', {name: 'Position'}).should('be.visible').type(position);
// # Save and close the modal
cy.uiSaveAndClose();
});
// # Post message in the main channel
cy.postMessage('hello from master hacker');
// # Click on the profile image
cy.get('.profile-icon > img').as('profileIconForPopover').click();
// # Verify that the popover is visible and contains position
cy.contains('#user-profile-popover', position).should('be.visible');
});
it('MM-T2064 Position / 128 characters', () => {
const longPosition = 'Master Hacker II'.repeat(8);
// # Open 'Profile' modal and view the default 'Profile Settings'
cy.uiOpenProfileModal().within(() => {
const minPositionHeader = () => cy.findByRole('heading', {name: 'Position'});
const maxPositionInput = () => cy.findByRole('textbox', {name: 'Position'});
// # Fill-in the position field with a value of 128 characters
minPositionHeader().click();
maxPositionInput().type(longPosition);
cy.uiSave();
maxPositionInput().should('not.exist');
minPositionHeader().click();
maxPositionInput().invoke('val').then((val) => {
// * Verify that the input value is 128 characters
expect(val.toString().length).to.equal(128);
});
// # Try to edit the field with maximum characters
maxPositionInput().focus().type('random');
maxPositionInput().invoke('val').then((val) => {
// * Verify that the position hasn't changed
expect(val).to.equal(longPosition);
});
// # Save position
cy.uiSave();
});
});
});

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

@@ -0,0 +1,233 @@
// 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 @account_setting
import * as TIMEOUTS from '../../../../fixtures/timeouts';
import {reUrl, getRandomId} from '../../../../utils';
describe('Profile > Profile Settings > Email', () => {
let siteName;
let testUser: Cypress.UserProfile;
let otherUser;
let offTopicUrl;
let origConfig: Cypress.AdminConfig;
before(() => {
// Get config
cy.apiGetConfig().then(({config}) => {
origConfig = config;
const newConfig = {
...origConfig,
EmailSettings: {
...origConfig.EmailSettings,
RequireEmailVerification: true,
},
};
cy.apiUpdateConfig(newConfig).then(({config}) => {
siteName = config.TeamSettings.SiteName;
});
cy.apiInitSetup().then(({user, offTopicUrl: url}) => {
testUser = user;
offTopicUrl = url;
cy.apiVerifyUserEmailById(testUser.id);
return cy.apiCreateUser({});
}).then(({user: user1}) => {
otherUser = user1;
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
});
beforeEach(() => {
// # Go to Profile
cy.uiOpenProfileModal();
});
afterEach(() => {
// # Close modal
cy.get('body').type('{esc}');
});
it('MM-T2065 Email: Can "change" to existing email address and save', () => {
// # Click "Edit" to the right of "Email"
cy.get('#emailEdit').should('be.visible').click();
// # Type the same email
cy.get('#primaryEmail').should('be.visible').type(testUser.email);
cy.get('#confirmEmail').should('be.visible').type(testUser.email);
cy.get('#currentPassword').should('be.visible').type('SampleUs@r-1');
// # Save the settings
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// * Check that the email verification message is not showed.
cy.get('.announcement-bar').should('not.exist');
});
it('MM-T2066 email address required', () => {
// # Click "Edit" to the right of "Email"
cy.get('#emailEdit').should('be.visible').click();
// # Save the settings
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// * Check that the correct error message is shown.
cy.get('#clientError').should('be.visible').should('have.text', 'Please enter a valid email address');
});
it('MM-T2067 email address already taken error', () => {
// # Click "Edit" to the right of "Email"
cy.get('#emailEdit').should('be.visible').click();
// # Type different email
cy.get('#primaryEmail').should('be.visible').type(otherUser.email);
cy.get('#confirmEmail').should('be.visible').type(otherUser.email);
cy.get('#currentPassword').should('be.visible').type(otherUser.password);
// # Save the settings
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// * Check that the correct error message is shown.
cy.get('#serverError').should('be.visible').should('have.text', 'An account with that email already exists.');
});
it('MM-T2068 email address and confirmation don\'t match', () => {
// # Click "Edit" to the right of "Email"
cy.get('#emailEdit').should('be.visible').click();
// # Type some random email
cy.get('#primaryEmail').should('be.visible').type('random@example.com');
cy.get('#confirmEmail').should('be.visible').clear();
cy.get('#currentPassword').should('be.visible').type('randompass');
// # Save the settings
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// * Check that the correct error message is shown.
cy.get('#clientError').should('be.visible').should('have.text', 'The new emails you entered do not match.');
});
// This test is a combination of 4 sub-tests because they are sub-parts of the same test.
// Doing them individually would have a dependency with the previous test.
// Hence, a combined single test for everything is better.
it('MM-T2069 Email: Can update email address and verify through email notification', () => {
// # Click "Edit" to the right of "Email"
cy.get('#emailEdit').should('be.visible').click();
const randomId = getRandomId();
const username = `user-${randomId}`;
const email = `${username}@example.com`;
// # Type new email
cy.get('#primaryEmail').should('be.visible').type(email);
cy.get('#confirmEmail').should('be.visible').type(email);
cy.get('#currentPassword').should('be.visible').type(testUser.password);
// # Save the settings and close
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
cy.uiClose();
// * Verify the announcement bar
cy.get('.announcement-bar').should('be.visible').should('contain.text', 'Check your email inbox to verify the address.');
// # Reload the page
cy.reload();
// * Check that the email verification message is not showed.
cy.get('.announcement-bar').should('not.exist');
cy.getRecentEmail({username, email}).then((data) => {
// * Verify the subject
expect(data.subject).to.equal(`[${siteName}] Verify new email address`);
// * Verify the body
expect(data.body).to.contain('You successfully updated your email');
const matched = data.body[6].match(reUrl);
assert(matched.length > 0);
const permalink = matched[0];
// # Click on the link
cy.visit(permalink);
// * Verify announcement bar
cy.get('.announcement-bar').should('be.visible').should('contain.text', 'Email verified');
// # Wait for one second for the mail to be sent out.
cy.wait(TIMEOUTS.FIVE_SEC);
cy.getRecentEmail(testUser).then(({subject}) => {
// * Verify the subject
expect(subject).to.equal(`[${siteName}] Your email address has changed`);
});
cy.uiOpenProfileModal();
// * Verify new email address
cy.get('#emailDesc').should('be.visible').should('have.text', email);
});
});
it('MM-T2073 - Verify email verification message after logout', () => {
// # Click "Edit" to the right of "Email"
cy.get('#emailEdit').should('be.visible').click();
const randomId = getRandomId();
const username = `user-${randomId}`;
const email = `${username}@example.com`;
// # Type new email
cy.get('#primaryEmail').should('be.visible').type(email);
cy.get('#confirmEmail').should('be.visible').type(email);
cy.get('#currentPassword').should('be.visible').type(testUser.password);
// # Save the settings
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// # Close modal then logout
cy.get('body').type('{esc}');
cy.uiOpenUserMenu('Log Out');
// # Wait for one second for the mail to be sent out.
cy.wait(TIMEOUTS.ONE_SEC);
cy.getRecentEmail({username, email}).then((data) => {
// * Verify the subject
expect(data.subject).to.equal(`[${siteName}] Verify new email address`);
// * Verify email body
expect(data.body[1]).to.contain('You successfully updated your email');
const matched = data.body[6].match(reUrl);
assert(matched.length > 0);
const permalink = matched[0];
cy.visit(permalink);
// * Verify login text
cy.get('.AlertBanner.success').should('be.visible').within(() => {
cy.get('.AlertBanner__title').should('contain.text', 'Email Verified');
});
// # Do login
cy.get('#input_loginId').should('be.visible').clear().type(email);
cy.get('#input_password-input').should('be.visible').type(testUser.password);
cy.get('#saveSetting').should('not.be.disabled').click();
// * Check that the email verification message is not showed.
cy.get('.announcement-bar').should('not.exist');
});
});
});

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

@@ -0,0 +1,84 @@
// 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 @account_setting
describe('Profile > Profile Settings > Full Name', () => {
let testUser;
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({user, offTopicUrl}) => {
testUser = user;
cy.visit(offTopicUrl);
});
});
it('MM-T2043 Enter first name', () => {
// # Go to Profile
cy.uiOpenProfileModal();
// # Click "Edit" to the right of "Full Name"
cy.get('#nameEdit').should('be.visible').click();
// # Clear the first name
cy.get('#firstName').clear();
// # Type a new first name
cy.get('#firstName').should('be.visible').type(testUser.first_name + '_new');
// # Save the settings
cy.uiSave();
// * Check that the first name was correctly updated
cy.get('#nameDesc').should('be.visible').should('contain', testUser.first_name + '_new ' + testUser.last_name);
// # Close the modal
cy.uiClose();
});
it('MM-T2042 Full Name starting blank stays blank', () => {
// # Go to Profile
cy.uiOpenProfileModal();
// # Click "Edit" to the right of "Full Name"
cy.get('#nameEdit').should('be.visible').click();
// # Clear the first name
cy.get('#firstName').should('be.visible').clear();
// # Clear the last name
cy.get('#lastName').should('be.visible').clear();
// # Save the settings
cy.uiSave();
// # Click "Edit" to the right of "Full Name"
cy.get('#nameEdit').should('be.visible').click();
// # Save the settings
cy.uiSave();
// * Check that the full name was correctly cleared
cy.findByText("Click 'Edit' to add your full name").should('be.visible');
// # Click "Edit" to the right of "Full Name"
cy.get('#nameEdit').should('be.visible').click();
// * Check that first name is blank
cy.get('#firstName').should('be.visible').should('have.value', '');
// * Check that last name is blank
cy.get('#lastName').should('be.visible').should('have.value', '');
// # Close the modal
cy.uiClose();
});
});

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

@@ -0,0 +1,81 @@
// 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 @account_setting
import {getRandomId} from '../../../../utils';
describe('Profile > Profile Settings> Full Name', () => {
let firstUser;
let secondUser;
const firstName = 'This Is a Long Name';
const lastName = 'That Should Truncate';
before(() => {
cy.apiInitSetup().then(({team, user, offTopicUrl}) => {
firstUser = user;
cy.apiCreateUser().then(({user: user1}) => {
secondUser = user1;
cy.apiAddUserToTeam(team.id, secondUser.id);
cy.apiLogin(firstUser);
cy.visit(offTopicUrl);
});
});
});
it('MM-T2046 Full Name - Truncated in popover', () => {
// # Go to Profile -> Profile Settings -> Full Name -> Edit
cy.uiOpenProfileModal();
// # Open Full Name section
cy.get('#nameDesc').click();
// # Enter First Name `This Is a Very Long Name`
cy.get('#firstName').clear().type(firstName);
// # Enter Last Name `That Should Truncate`
cy.get('#lastName').clear().type(lastName);
// # Save
cy.get('#saveSetting').click();
// * Full name field shows first and last name.
cy.contains('#nameDesc', `${firstName} ${lastName}`);
// # Close account settings modal
cy.uiClose();
});
it('MM-T2047 Truncated in popover (visual verification)', () => {
// # Open user profile popover
cy.postMessage(`this is a test message ${getRandomId()}`);
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).should('be.visible');
cy.get(`#post_${postId} img`).click();
cy.get('#user-profile-popover').should('be.visible');
// * Popover user name should show truncated to 'This Is a Long Name That Should Tr...'
cy.findByTestId(`popover-fullname-${firstUser.username}`).should('have.css', 'text-overflow', 'ellipsis');
});
});
it('MM-T2048 Empty full name: @ still displays before username', () => {
// # Open any user list ("View Members", "Add Members", "Manage Members", ..)
cy.uiOpenTeamMenu('View Members');
// # Find a user who hasn't set their full name
cy.get('.modal-title').should('be.visible');
cy.get('#searchUsersInput').should('be.visible').type(secondUser.nickname);
// * Username is preceded with `@`, consistent with other users
cy.contains('button.user-popover', `@${secondUser.username}`);
});
});

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

@@ -0,0 +1,42 @@
// 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 @account_setting
describe('Account Settings', () => {
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T2045 Full Name - Link in help text', () => {
// # Go to Profile
cy.uiOpenProfileModal();
// * Ensure that the Profile tab is loaded
cy.get('#generalSettingsTitle').should('be.visible').should('contain', 'Profile');
// # Click "Edit" to the right of Full Name
cy.get('#nameEdit').click();
// # Click "Notifications" link in help text
cy.get('#extraInfo').within(() => {
cy.findByText('Notifications').click();
});
// * Verify that the modal switched to "Profile" modal
cy.findByRole('dialog', {name: 'Profile'}).should('be.visible');
// * Verify that the view switches to notifications tab
cy.get('#notificationSettingsTitle').should('be.visible').should('contain', 'Notifications');
});
});

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

@@ -0,0 +1,181 @@
// 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 @account_setting
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('Settings > Sidebar > General', () => {
let testUser;
let testTeam;
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team, user, offTopicUrl}) => {
testUser = user;
testTeam = team;
cy.visit(offTopicUrl);
});
});
it('MM-T3848 No nickname is present', () => {
// # Open 'Profile' modal and view the default 'Profile'
cy.uiOpenProfileModal().within(() => {
// # Open 'Nickname' setting
cy.uiGetHeading('Nickname').click();
// # Clear the nickname text field contents
cy.uiGetTextbox('Nickname').should('be.visible').clear();
// # Save and close the modal
cy.uiSaveAndClose();
});
// # Open team menu and click "View Members"
cy.uiOpenTeamMenu('View Members');
// # Search for username and check that no nickname is present
cy.get('.modal-title').should('be.visible');
cy.get('#searchUsersInput').should('be.visible').type(testUser.first_name).wait(TIMEOUTS.ONE_SEC);
cy.findByTestId('userListItemDetails').find('.more-modal__name').should('be.visible').then((el) => {
expect(getInnerText(el)).equal(`@${testUser.username} - ${testUser.first_name} ${testUser.last_name}`);
});
// # Close Team Members modal
cy.uiClose();
});
it('MM-T268 Profile > Profile Settings > Add Nickname', () => {
const newNickname = 'victor_nick';
// # Open 'Profile' modal and view the default 'Profile Settings'
cy.uiOpenProfileModal().within(() => {
// # Open 'Nickname' setting
cy.uiGetHeading('Nickname').click();
// # Clear the nickname text field contents
cy.uiGetTextbox('Nickname').should('be.visible').clear().type('victor_nick');
// # Save and close the modal
cy.uiSaveAndClose();
});
// # Open team menu and click "View Members"
cy.uiOpenTeamMenu('View Members');
// # Search for username and check that expected nickname is present
cy.get('.modal-title').should('be.visible');
cy.get('#searchUsersInput').should('be.visible').type(testUser.first_name).wait(TIMEOUTS.ONE_SEC);
cy.findByTestId('userListItemDetails').find('.more-modal__name').should('be.visible').then((el) => {
expect(getInnerText(el)).equal(`@${testUser.username} - ${testUser.first_name} ${testUser.last_name} (${newNickname})`);
});
// # Close Channel Members modal
cy.uiClose();
});
it('MM-T2060 Nickname and username styles', () => {
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Go to Settings > Display
cy.uiOpenSettingsModal('Display');
// # Click Edit button beside Teammate Name Display
cy.get('#name_formatEdit').click();
// # Choose Show first and last name
cy.get('#name_formatFormatC').check();
// # Click Save button to save the settings
cy.uiSave();
cy.uiClose();
// # Open channel menu and click 'Add Members'
cy.uiOpenChannelMenu('Add Members');
// * Verify that the modal is open
cy.get('#addUsersToChannelModal').should('be.visible').findByText(`Add people to ${channel.display_name}`);
// # Type into the input box to search for a user
cy.get('#selectItems input').typeWithForce('sys').wait(TIMEOUTS.ONE_SEC);
// * Verify that the username span contains the '@' symbol and the dark colour
cy.get('#multiSelectList > div > .more-modal__details > .more-modal__name > span').should('contain', '@').and('have.css', 'color', 'rgb(63, 67, 80)');
// # Close modal
cy.get('body').type('{esc}');
// # Open DM modal from the sidebar
cy.uiAddDirectMessage().click();
// # Go to direct messages modal
cy.get('.more-modal').should('be.visible').within(() => {
cy.findByText('Direct Messages').click();
cy.get('#selectItems input').typeWithForce('@');
// * Verify that the username span contains the '@' symbol and the dark colour
cy.get('.more-modal__details > .more-modal__name').should('contain', '@').and('have.css', 'color', 'rgb(63, 67, 80)');
});
// # Exit the modal
cy.get('body').type('{esc}');
});
});
it('MM-T2061 Nickname should reset on cancel of edit', () => {
// # Open 'Profile' modal and view the default 'Profile Settings'
cy.uiOpenProfileModal().within(() => {
// # Open 'Nickname' setting
cy.uiGetHeading('Nickname').click();
// # Clear the nickname text field contents
cy.uiGetTextbox('Nickname').should('be.visible').clear().type('nickname_edit');
// # Cancel the edit of nickname
cy.uiCancelButton().click();
});
// # Click edit of nickname
cy.uiGetHeading('Nickname').click();
// * Check if element is present and contains old nickname
cy.uiGetTextbox('Nickname').should('be.visible').should('contain', '');
// # Close the modal
cy.uiClose();
});
it('MM-T2062 Clear nickname and save', () => {
// # Open 'Profile' modal and view the default 'Profile Settings'
cy.uiOpenProfileModal().within(() => {
// # Open 'Nickname' setting
cy.uiGetHeading('Nickname').click();
// # Clear the nickname
cy.uiGetTextbox('Nickname').clear();
// * Check if nickname element is present and it does not contain any nickname
cy.uiGetTextbox('Nickname').should('contain', '');
cy.uiSaveButton().click();
});
// * Verify nickname help text is visible
cy.get('#nicknameDesc').should('be.visible').should('contain', "Click 'Edit' to add a nickname");
// # Close the modal
cy.uiClose();
});
function getInnerText(el) {
return el[0].innerText.replace(/\n/g, '').replace(/\s/g, ' ');
}
});

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

@@ -0,0 +1,88 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Stage: @prod
// Group: @channels @account_setting
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('Account Settings', () => {
beforeEach(() => {
cy.apiAdminLogin().apiInitSetup({loginAfter: true}).its('user').as('user');
});
it('MM-T2079 Can remove profile pic then choose different pic without saving in between', function() {
// # Add a profile picture (aka "old profile picture")
setInitialPicture(this.user);
cy.visit('/');
// # Save the id of the old picture
getProfilePictureId().as('idOld');
// # Open Profile > Profile Settings > Profile Picture > Edit
cy.uiOpenProfileModal().findByRole('button', {name: /picture edit/i}).click();
// # Click the X to remove the old profile picture but do not click save
cy.findByRole('button', {name: /remove profile picture/i}).click();
// # Select a new profile picture and click Save
cy.findByTestId('uploadPicture').attachFile('png-image-file.png');
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
cy.findByRole('button', {name: /close/i}).click();
// * Verify that new profile image exists and isn't equal to the old one
getProfilePictureId().then((idNew) => expect(idNew).to.exist.and.not.to.be.equal(this.idOld));
});
it('MM-T2075_1 Profile Picture: Cancel out of adding profile picture', () => {
verifyProfilePictureDoesNotUpdateAfterCancel();
});
it('MM-T2075_2 Profile Picture: Cancel out of changing profile picture ', function() {
// # Add a profile picture (aka "old profile picture")
setInitialPicture(this.user);
verifyProfilePictureDoesNotUpdateAfterCancel();
});
});
function getProfilePictureId() {
return cy.uiGetProfileHeader().find('.Avatar').invoke('attr', 'src').then((urlString) => {
const url = new URL(urlString);
const params = new URLSearchParams(url.search);
return params.get('_');
});
}
function setInitialPicture(user) {
cy.apiUploadFile('image', 'mattermost-icon.png', {
url: `/api/v4/users/${user.id}/image`,
method: 'POST',
successStatus: 200,
});
}
function verifyProfilePictureDoesNotUpdateAfterCancel() {
let idOld;
cy.visit('/');
// # Save the id of the old picture
getProfilePictureId().then((id) => {
idOld = id;
});
// # Open Profile > Profile Settings > Profile Picture > Edit
cy.uiOpenProfileModal().findByRole('button', {name: /picture edit/i}).click();
// # Select a new profile picture
cy.findByTestId('uploadPicture').attachFile('png-image-file.png');
cy.uiCancel().wait(TIMEOUTS.HALF_SEC);
cy.uiClose();
// * Verify that 'new' profile image exists and equal to the old one
getProfilePictureId().then((idNew) => expect(idNew).to.equal(idOld));
}

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

@@ -0,0 +1,92 @@
// 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 @account_setting
// TODO: Remove import once e2e/cypress/tests/support/index.ts is converted to Typescript.
import 'cypress-file-upload';
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('Profile > Profile Settings > Profile Picture', () => {
before(() => {
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T2080 Can remove profile pic', () => {
const customImageMatch = 'image?_=';
// * Verify the default profile image is shown first
cy.uiGetProfileHeader().
find('.Avatar').
should('have.attr', 'src').
and('not.include', customImageMatch);
// # Go to Profile
cy.uiOpenProfileModal();
// # Click "Edit" to the right of "Profile Picture"
cy.get('#pictureEdit').should('be.visible').click();
// # Upload and save profile picture
cy.findByTestId('uploadPicture').attachFile('mattermost-icon.png');
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// # Close modal
cy.get('body').type('{esc}');
// * Profile picture is updated
cy.uiGetProfileHeader().
find('.Avatar').
should('have.attr', 'src').
and('include', customImageMatch);
// # Go to Profile
cy.uiOpenProfileModal();
// # Click "Edit" to the right of "Profile Picture"
cy.get('#pictureEdit').should('be.visible').click();
// # Remove profile picture
cy.findByTestId('removeSettingPicture').click();
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// * Check that we are back in the "General" section of the Profile
cy.get('#pictureEdit').should('be.visible');
// # Close modal
cy.get('body').type('{esc}');
// * Verify the default profile image is shown
cy.uiGetProfileHeader().
find('.Avatar').
should('have.attr', 'src').
and('not.include', customImageMatch);
});
it('MM-T2077 Profile picture: non image file shows error', () => {
// # Go to Profile
cy.uiOpenProfileModal();
// # Click "Edit" to the right of "Profile Picture"
cy.get('#pictureEdit').should('be.visible').click();
// # Upload and save profile picture
cy.findByTestId('uploadPicture').attachFile('txt-changed-as-png.png');
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// # Verify error message
cy.get('.has-error').
should('be.visible').
and('contain', 'Image limits check failed. Resolution is too high.');
});
});

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

@@ -0,0 +1,246 @@
// 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 @account_setting
import {getRandomId} from '../../../../utils';
describe('Settings > Sidebar > General > Edit', () => {
let testTeam;
let testUser;
let testChannel;
let otherUser;
let offTopicUrl;
before(() => {
// # Login as admin and visit off-topic
cy.apiInitSetup().then(({user, team, channel, offTopicUrl: url}) => {
testUser = user;
testTeam = team;
testChannel = channel;
offTopicUrl = url;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
});
});
cy.visit(offTopicUrl);
});
});
beforeEach(() => {
// # Go to Profile
cy.uiOpenProfileModal();
});
it('MM-T2050 Username cannot be blank', () => {
// # Clear the username textfield contents
cy.get('#usernameEdit').click();
cy.get('#username').clear();
cy.uiSave();
// * Check if element is present and contains expected text values
cy.get('#clientError').should('be.visible').should('contain', 'Username must begin with a letter, and contain between 3 to 22 lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\', and \'_\'.');
// # Click "x" button to close Profile modal
cy.uiClose();
});
it('MM-T2051 Username min 3 characters', () => {
// # Edit the username field
cy.get('#usernameEdit').click();
// # Add the username to textfield contents
cy.get('#username').clear().type('te');
cy.uiSave();
// * Check if element is present and contains expected text values
cy.get('#clientError').should('be.visible').should('contain', 'Username must begin with a letter, and contain between 3 to 22 lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\', and \'_\'.');
// # Click "x" button to close Profile modal
cy.uiClose();
});
it('MM-T2052 Username already taken', () => {
// # Edit the username field
cy.get('#usernameEdit').click();
// # Add the username to textfield contents
cy.get('#username').clear().type(otherUser.username);
cy.uiSave();
// * Check if element is present and contains expected text values
cy.get('#serverError').should('be.visible').should('contain', 'An account with that username already exists.');
// # Click "x" button to close Profile modal
cy.uiClose();
});
it('MM-T2053 Username w/ dot, dash, underscore still searches', () => {
let tempUser;
// # Create a temporary user
cy.apiCreateUser({prefix: 'temp'}).then(({user: user1}) => {
tempUser = user1;
cy.apiAddUserToTeam(testTeam.id, tempUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, tempUser.id);
});
// # Login the temporary user
cy.apiLogin(tempUser);
cy.visit(offTopicUrl);
cy.uiOpenProfileModal();
// # Step 1
// # Edit the username field
cy.get('#usernameEdit').click();
// # Step 2
// # Add the username to textfield contents
const newTempUserName = 'a-' + tempUser.username;
cy.get('#username').clear().type(newTempUserName);
cy.uiSaveAndClose();
// # Step 3
// * Verify that we've logged in as the temp user
cy.visit(offTopicUrl);
cy.uiOpenUserMenu().findByText(`@${newTempUserName}`);
cy.uiGetSetStatusButton().click();
// # Step 4
const text = `${newTempUserName} test message!`;
// # Post the user name mention declared earlier
cy.postMessage(text);
// # Click on the @ button
cy.uiGetRecentMentionButton().should('be.visible').click();
cy.get('#search-items-container').should('be.visible').within(() => {
// * Ensure that the mentions are visible in the RHS
cy.findByText(newTempUserName);
cy.findByText(`${newTempUserName} test message!`);
});
// # Click on the @ button to toggle off
cy.uiGetRecentMentionButton().should('be.visible').click();
});
});
it('MM-T2054 Username cannot start with dot, dash, or underscore', () => {
// # Edit the username field
cy.get('#usernameEdit').click();
const prefixes = [
'.',
'-',
'_',
];
for (const prefix of prefixes) {
// # Add username to textfield contents
cy.get('#username').clear().type(prefix).type('{backspace}.').type(otherUser.username);
cy.uiSave();
// * Check if element is present and contains expected text values
cy.get('#clientError').should('be.visible').should('contain', 'Username must begin with a letter, and contain between 3 to 22 lowercase characters made up of numbers, letters, and the symbols \'.\', \'-\', and \'_\'.');
}
// # Click "x" button to close Profile modal
cy.uiClose();
});
it('MM-T2055 Usernames that are reserved', () => {
// # Edit the username field
cy.get('#usernameEdit').click();
const usernames = [
'all',
'channel',
'here',
'matterbot',
];
for (const username of usernames) {
// # Add username to textfield contents
cy.get('#username').clear().type(username);
cy.uiSave();
// * Check if element is present and contains expected text values
cy.get('#clientError').should('be.visible').should('contain', 'This username is reserved, please choose a new one.');
}
// # Click "x" button to close Profile modal
cy.uiClose();
});
it('MM-T2056 Username changes when viewed by other user', () => {
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
// # Post a message in off-topic
cy.postMessage('Testing username update');
// # Login as other user
cy.apiLogin(otherUser);
cy.visit(offTopicUrl);
// # Get last post in off-topic for verifying username
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
// # Open profile popover
cy.get('.user-popover').click();
});
// * Verify username in profile popover
cy.get('#user-profile-popover').within(() => {
cy.get('#userPopoverUsername').should('be.visible').and('contain', `${testUser.username}`);
});
});
// # Login as test user
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
// # Open account settings modal
cy.uiOpenProfileModal();
// # Open Full Name section
cy.get('#usernameDesc').click();
const randomId = getRandomId();
// # Save username with a randomId
cy.get('#username').clear().type(`${otherUser.username}-${randomId}`);
// # save form
cy.uiSave();
cy.apiLogin(otherUser);
cy.visit(offTopicUrl);
// # Get last post in off-topic for verifying username
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.get('.user-popover').click();
});
// * Verify that new username is in profile popover
cy.get('#user-profile-popover').within(() => {
cy.get('#userPopoverUsername').should('be.visible').and('contain', `${otherUser.username}-${randomId}`);
});
});
});
});

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

@@ -0,0 +1,52 @@
// 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 @account_setting
describe('Profile > Security > View Access History', () => {
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
beforeEach(() => {
// # Go to Profile
cy.uiOpenProfileModal('Security');
// * Check that the Security tab is loaded
cy.get('#securityButton').should('be.visible');
// # Click the Security tab
cy.get('#securityButton').click();
});
it('MM-T2087 View Access History', () => {
// # Click "View Access History" link
cy.findByText('View Access History').should('be.visible').click();
// * Check that the Access History modal and table are visible
cy.get('#accessHistoryModalLabel').should('be.visible');
cy.get('.modal-body table').should('be.visible');
// * Check that the Access History table has the expected length
cy.get('.modal-body table thead tr th span').should('be.visible').should('have.length', 4);
// * Check that the Access History table header has the expected columns
cy.get('.modal-body table thead tr th span').eq(0).should('be.visible').should('contain', 'Timestamp');
cy.get('.modal-body table thead tr th span').eq(1).should('be.visible').should('contain', 'Action');
cy.get('.modal-body table thead tr th span').eq(2).should('be.visible').should('contain', 'IP Address');
cy.get('.modal-body table thead tr th span').eq(3).should('be.visible').should('contain', 'Session ID');
// * Check that the Access History table body is visible and not empty
cy.findByTestId('auditTableBody').should('be.visible').should('not.empty');
});
});

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

@@ -0,0 +1,73 @@
// 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 @account_setting
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('Profile > Security > View and Log Out of Active Sessions', () => {
const platforms = [
'Linux',
'Macintosh',
'Windows',
'Native Desktop App',
'iPhone Native App',
'Android Native App',
'iPhone Native Classic App',
'Android Native Classic App',
];
const platformRegex = new RegExp(`${platforms.join('|')}`, 'g');
let testUser: Cypress.UserProfile;
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl, user}) => {
testUser = user;
cy.visit(offTopicUrl);
// # Go to Profile
cy.uiOpenProfileModal('Security');
// * Check that the Security tab is loaded
cy.get('#securityButton').should('be.visible');
// # Click the Security tab
cy.get('#securityButton').click();
});
});
it('MM-T2088 View and Logout of Active Sessions (Se)', () => {
// # Go to "View and Logout of Active Sessions"
cy.findByText('View and Log Out of Active Sessions').should('be.visible').click();
// * Verify an appropriate platform is shown
cy.get('.report__platform').contains(platformRegex);
// # Click "More info" for the login session
cy.get('.report__info a').should('be.visible').and('have.text', 'More info').click();
// * Verify info is displayed
cy.get('.report__info').should('contain', 'Last activity:').
and('contain', 'First time active:').
and('contain', 'OS:').
and('contain', 'Browser:').
and('contain', 'Session ID:');
// # Click "Log Out" to log out of the specified session
cy.findByText('Log Out').click();
// * Verify we have been logged out, and we can log back in
cy.uiLogin(testUser);
// * Verify we have successfully logged back in
cy.get('#channel_view', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
});

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

@@ -0,0 +1,154 @@
// 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 @account_setting
import moment from 'moment-timezone';
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('Profile', () => {
let siteName: string;
let testUser: Cypress.UserProfile;
let offTopic: string;
before(() => {
cy.apiGetConfig().then(({config}) => {
siteName = config.TeamSettings.SiteName;
});
// # Login as new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({user, offTopicUrl}) => {
testUser = user;
offTopic = offTopicUrl;
cy.visit(offTopicUrl);
});
});
beforeEach(() => {
// # Reload the page to help run each test cleanly
cy.reload();
// # Go to Profile
cy.uiOpenProfileModal('Security');
// * Check that the Security tab is loaded
cy.get('#securityButton').should('be.visible');
// # Click the Security tab
cy.get('#securityButton').click();
// # Click "Edit" to the right of "Password"
cy.get('#passwordEdit').should('be.visible').click();
});
it('MM-T2085 Password: Valid values in password change fields allow the form to save successfully', () => {
// # Enter valid values in password change fields
enterPasswords(testUser.password, 'passwd', 'passwd');
// # Save the settings
cy.uiSave();
// * Check that there are no errors
cy.get('#clientError').should('not.exist');
cy.get('#serverError').should('not.exist');
});
it('MM-T2082 Password: New password confirmation mismatch produces error', () => {
// # Enter mismatching passwords for new password and confirm fields
enterPasswords(testUser.password, 'newPW', 'NewPW');
// # Save
cy.uiSave();
// * Verify for error message: "The new passwords you entered do not match."
cy.get('#clientError').should('be.visible').should('have.text', 'The new passwords you entered do not match.');
});
it('MM-T2083 Password: Too few characters in new password produces error', () => {
// # Enter a New password two letters long
enterPasswords(testUser.password, 'pw', 'pw');
// # Save
cy.uiSave();
// * Verify for error message: "Must be 5-64 characters long."
cy.get('#clientError').should('be.visible').should('have.text', 'Must be 5-64 characters long.');
});
it('MM-T2084 Password: Cancel out of password changes causes no changes to be made', () => {
// # Enter new valid passwords
enterPasswords(testUser.password, 'newPasswd', 'newPasswd');
// # Click 'Cancel'
cy.uiCancel();
// * Check that user is no longer in password edit page to verify the 'Cancel' action
cy.get('#currentPassword').should('not.exist');
cy.get('#passwordEdit').should('be.visible');
// # Logout
cy.apiLogout();
// * Verify that user cannot login with the cancelled password
cy.get('#input_loginId').type(testUser.username);
cy.get('#input_password-input').type('newPasswd');
cy.get('#saveSetting').should('not.be.disabled').click();
cy.findByText('The email/username or password is invalid.').should('be.visible');
// * Verify that user can successfully login with the old password
cy.apiLogin(testUser);
cy.visit(offTopic);
cy.get('#channelHeaderTitle').should('contain', 'Off-Topic');
});
it('MM-T2086 Password: Timestamp and email', () => {
// # Enter valid values in password change fields
enterPasswords(testUser.password, 'passwd', 'passwd');
// # Get current date
const now = moment(Date.now());
// # Save the settings
cy.uiSave();
// # Create expected date and time formats
const date = now.format('MMM DD, YYYY');
const time = now.format('hh:mm A');
// # Create expected timestamp
const timestamp = `Last updated ${date} at ${time}`;
// * Verify that password description field contains valid timestamp
cy.get('#passwordDesc').should('have.text', timestamp);
// # Wait for the mail to be sent out.
cy.wait(TIMEOUTS.FIVE_SEC);
cy.getRecentEmail(testUser).then(({subject}) => {
// * Verify the subject
expect(subject).to.equal(
`[${siteName}] Your password has been updated`,
);
});
});
});
function enterPasswords(currentPassword, newPassword, confirmPassword) {
// # Enter Current password
cy.get('#currentPassword').should('be.visible').type(currentPassword);
// # Enter New password
cy.get('#newPassword').should('be.visible').type(newPassword);
// # Retype New password incorrectly
cy.get('#confirmPassword').should('be.visible').type(confirmPassword);
}

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

@@ -0,0 +1,89 @@
// 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.
// ***************************************************************
// - Requires openldap and keycloak running
// - Requires keycloak certificate at fixtures folder
// -> copy ./mattermost-server/build/docker/keycloak/keycloak.crt to ./mattermost-webapp/e2e/cypress/tests/fixtures/keycloak.crt
// - Requires Cypress' chromeWebSecurity to be false
// Group: @channels @enterprise @ldap @saml @keycloak
import {getAdminAccount} from '../../../support/env';
import {getRandomId} from '../../../utils';
import {getKeycloakServerSettings} from '../../../utils/config';
describe('AD / LDAP', () => {
let samlLdapUser;
let testTeamId;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiRequireLicenseForFeature('LDAP', 'SAML');
// # Create new LDAP user
cy.createLDAPUser().then((user) => {
samlLdapUser = user;
});
// # Create new team
cy.apiCreateTeam('saml-team', 'SAML Team').then(({team}) => {
testTeamId = team.id;
});
const samlConfig = getKeycloakServerSettings();
cy.apiUpdateConfig(samlConfig).then(() => {
// # Require keycloak with realm setup
cy.apiRequireKeycloak();
// # Upload certificate, overwrite existing
cy.apiUploadSAMLIDPCert('keycloak.crt');
// # Create Keycloak user and login for the first time
cy.keycloakCreateUsers([samlLdapUser]);
cy.doKeycloakLogin(samlLdapUser);
// # Wait for the UI to be ready which indicates SAML registration is complete
cy.findByText('Logout').click();
});
});
beforeEach(() => {
cy.apiAdminLogin();
cy.apiGetUserByEmail(samlLdapUser.email).then(({user}) => {
cy.apiAddUserToTeam(testTeamId, user.id);
cy.apiRevokeUserSessions(user.id);
});
});
it('MM-T3666 - SAML / LDAP sync with ID Attribute', () => {
// # Login via Keycloak
cy.doKeycloakLogin(samlLdapUser);
// * Check the user settings
cy.verifyAccountNameSettings(samlLdapUser.firstname, samlLdapUser.lastname);
// # Update LDAP user then sync
const randomId = getRandomId();
const newFirstName = `Firstname${randomId}`;
const newLastName = `Lastname${randomId}`;
cy.updateLDAPUser({
...samlLdapUser,
firstname: newFirstName,
lastname: newLastName,
});
const admin = getAdminAccount();
cy.runLdapSync(admin);
// # Reload the page
cy.reload();
// * Check the user settings is in sync with the new attributes
cy.verifyAccountNameSettings(newFirstName, newLastName);
});
});

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

@@ -0,0 +1,128 @@
// 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.
// ***************************************************************
// - Requires openldap and keycloak running
// - Requires keycloak certificate at fixtures folder
// -> copy ./mattermost-server/build/docker/keycloak/keycloak.crt to ./mattermost-webapp/e2e/cypress/tests/fixtures/keycloak.crt
// - Requires Cypress' chromeWebSecurity to be false
// Group: @channels @enterprise @ldap @saml @keycloak
import {getAdminAccount} from '../../../support/env';
import {generateLDAPUser} from '../../../support/ldap_server_commands';
import {getKeycloakServerSettings} from '../../../utils/config';
describe('AD / LDAP', () => {
const nonLDAPUser = generateLDAPUser();
let samlLdapUser;
let testTeamId;
before(() => {
cy.createLDAPUser().then((user) => {
samlLdapUser = user;
});
cy.shouldNotRunOnCloudEdition();
cy.apiRequireLicenseForFeature('LDAP', 'SAML');
// # Create new LDAP user
cy.createLDAPUser().then((user) => {
samlLdapUser = user;
});
// # Create new team
cy.apiCreateTeam('saml-team', 'SAML Team').then(({team}) => {
testTeamId = team.id;
});
const samlConfig = getKeycloakServerSettings();
cy.apiUpdateConfig(samlConfig).then(() => {
// # Require keycloak with realm setup
cy.apiRequireKeycloak();
// # Upload certificate, overwrite existing
cy.apiUploadSAMLIDPCert('keycloak.crt');
// # Create Keycloak user and login for the first time
cy.keycloakCreateUsers([samlLdapUser, nonLDAPUser]);
cy.doKeycloakLogin(samlLdapUser);
// # Wait for the UI to be ready which indicates SAML registration is complete
cy.findByText('Logout').click();
});
});
it('MM-T3664 - SAML User, Not in LDAP', () => {
// # Login to Keycloak
cy.doKeycloakLogin(nonLDAPUser);
// * Should render an error since user is not registered in LDAP
cy.findByText('User not registered on AD/LDAP server.');
// # Run LDAP Sync
const admin = getAdminAccount();
cy.runLdapSync(admin);
// # REgister as LDAP user
cy.createLDAPUser({user: nonLDAPUser});
// # Add user to team
cy.apiAdminLogin();
cy.apiGetUserByEmail(nonLDAPUser.email).then(({user}) => {
cy.apiAddUserToTeam(testTeamId, user.id);
});
// # Login to Keycloak
cy.doKeycloakLogin(nonLDAPUser);
// * Should successfully login and view the default channel
cy.postMessage('hello');
// * Check user setting is in sync with LDAP
cy.verifyAccountNameSettings(nonLDAPUser.firstname, nonLDAPUser.lastname);
});
it('MM-T3665 - Deactivate user in SAML', () => {
// # Add user to team
cy.apiAdminLogin();
cy.apiGetUserByEmail(samlLdapUser.email).then(({user}) => {
cy.apiAddUserToTeam(testTeamId, user.id);
});
// # Login to Keycloak
cy.doKeycloakLogin(samlLdapUser);
// * Should successfully login and view the default channel
cy.postMessage('hello');
// # Suspend Keycloak user
cy.keycloakSuspendUser(samlLdapUser.email);
// # Login to Keycloak
cy.doKeycloakLogin(samlLdapUser);
// * Verify login failed
cy.verifyKeycloakLoginFailed();
// # Activate user in keycloak
cy.keycloakUnsuspendUser(samlLdapUser.email);
// # Login again
cy.findByText('Password').type(samlLdapUser.password);
cy.findAllByText('Log In').last().click();
// * Should successfully login and view the default channel
cy.postMessage('hello');
// * Check the user settings
cy.verifyAccountNameSettings(samlLdapUser.firstname, samlLdapUser.lastname);
});
});

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

@@ -0,0 +1,115 @@
// 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.
// ***************************************************************
// - Requires openldap and keycloak running
// - Requires keycloak certificate at fixtures folder
// -> copy ./mattermost-server/build/docker/keycloak/keycloak.crt to ./mattermost-webapp/e2e/cypress/tests/fixtures/keycloak.crt
// - Requires Cypress' chromeWebSecurity to be false
// Group: @channels @enterprise @ldap @saml @keycloak
import {getAdminAccount} from '../../../support/env';
import {getRandomId} from '../../../utils';
import {getKeycloakServerSettings} from '../../../utils/config';
describe('AD / LDAP', () => {
const admin = getAdminAccount();
const samlConfig = getKeycloakServerSettings();
let samlLdapUser;
let testTeamId;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiRequireLicenseForFeature('LDAP', 'SAML');
// # Create new LDAP user
cy.createLDAPUser().then((user) => {
samlLdapUser = user;
});
// # Create new team
cy.apiCreateTeam('saml-team', 'SAML Team').then(({team}) => {
testTeamId = team.id;
});
cy.apiUpdateConfig(samlConfig).then(() => {
// # Require keycloak with realm setup
cy.apiRequireKeycloak();
// # Upload certificate, overwrite existing
cy.apiUploadSAMLIDPCert('keycloak.crt');
// # Create Keycloak user and login for the first time
cy.keycloakCreateUsers([samlLdapUser]);
cy.doKeycloakLogin(samlLdapUser);
// # Wait for the UI to be ready which indicates SAML registration is complete
cy.findByText('Logout').click();
// # Add user to team
cy.apiAdminLogin();
cy.apiGetUserByEmail(samlLdapUser.email).then(({user}) => {
cy.apiAddUserToTeam(testTeamId, user.id);
});
});
});
it('MM-T3013_1 - SAML LDAP Sync Off, user attributes pulled from SAML', () => {
// # Login to Keycloak
cy.doKeycloakLogin(samlLdapUser);
// * Check the user settings
cy.verifyAccountNameSettings(samlLdapUser.firstname, samlLdapUser.lastname);
// # Run LDAP Sync
cy.runLdapSync(admin);
// Refresh make sure user not logged out.
cy.reload();
// * Check the user settings
cy.verifyAccountNameSettings(samlLdapUser.firstname, samlLdapUser.lastname);
});
it('MM-T3013_2 - SAML LDAP Sync On, user attributes pulled from LDAP', () => {
const testConfig = {
...samlConfig,
SamlSettings: {
...samlConfig.SamlSettings,
EnableSyncWithLdap: true,
},
};
cy.apiAdminLogin();
cy.apiUpdateConfig(testConfig);
// # Login to Keycloak
cy.doKeycloakLogin(samlLdapUser);
// * Check the user settings
cy.verifyAccountNameSettings(samlLdapUser.firstname, samlLdapUser.lastname);
// # Update LDAP user then sync
const randomId = getRandomId();
const newFirstName = `Firstname${randomId}`;
const newLastName = `Lastname${randomId}`;
cy.updateLDAPUser({
...samlLdapUser,
firstname: newFirstName,
lastname: newLastName,
});
cy.runLdapSync(admin);
// # Refresh make sure user not logged out.
cy.reload();
// * Check the user settings
cy.verifyAccountNameSettings(newFirstName, newLastName);
});
});

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

@@ -0,0 +1,84 @@
// 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 @channel
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Archived channels', () => {
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit created channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T1720 Cannot add reactions to existing reactions', () => {
const messageText = 'Test add reaction in archive channels';
// # Post a message in the channel
cy.postMessage(messageText);
// # Get the last post for reference of ID
cy.getLastPostId().then((postId) => {
// # Click the add reaction icon
cy.clickPostReactionIcon(postId);
// # Search for the emoji in emoji search bar
cy.findByPlaceholderText('Search emojis').should('exist').and('be.visible').
clear().
type('slightly_frowning_face', {delay: TIMEOUTS.QUARTER_SEC, force: true});
// # Choose "slightly_frowning_face" emoji
cy.clickEmojiInEmojiPicker('slightly_frowning_face');
// * Should have added the reaction with count
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).should('have.text', '1');
// * Should have add reaction button
cy.get(`#addReaction-${postId}`).should('exist');
// # Archive the channel
cy.uiArchiveChannel();
// * Should not have add reaction button
cy.get(`#addReaction-${postId}`).should('not.exist');
// # Click on existing "slightly_frowning_face" emoji
cy.get(`#postReaction-${postId}-slightly_frowning_face`).click();
// * Reaction count should not have incremented
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).should('have.text', '1');
// # Remove focus so we can open RHS
cy.get('#channel_view').click();
// # Open RHS for the post
cy.clickPostCommentIcon(postId);
cy.get(`#rhsPost_${postId}`).within(() => {
// * Should not have add reaction button
cy.get(`#addReaction-${postId}`).should('not.exist');
// # Click on existing "slightly_frowning_face" emoji
cy.get(`#postReaction-${postId}-slightly_frowning_face`).click();
// * Reaction count should not have incremented
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).should('have.text', '1');
});
});
});
});

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

@@ -0,0 +1,102 @@
// 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 @channel
describe('Archive channel header spec', () => {
before(() => {
cy.visit('/admin_console/user_management/permissions/system_scheme');
// # Click reset to defaults and confirm
cy.findByTestId('resetPermissionsToDefault').click();
cy.get('#confirmModalButton').click();
// # Ensure permissions for converting channel to private is enabled
cy.findByTestId('all_users-public_channel-convert_public_channel_to_private-checkbox').then((el) => {
if (!el.hasClass('checked')) {
el.click();
}
});
// # Save the settings
cy.uiSave();
cy.uiSaveButton().should('be.visible');
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit create channel
cy.apiInitSetup({loginAfter: true}).then(({channelUrl}) => {
cy.visit(channelUrl);
});
});
it('MM-T1717 Archived channel details cannot be edited', () => {
// # click on channel drop-down menu
cy.get('#channelHeaderTitle').click();
// * The dropdown menu of the channel header should be visible;
cy.get('#channelLeaveChannel').should('be.visible');
// * Rename menu option should be visible;
cy.get('#channelRename').should('be.visible');
// * Edit header menu option should be visible;
cy.get('#channelEditHeader').should('be.visible');
// * Edit purpose menu option should be visible;
cy.get('#channelEditPurpose').should('be.visible');
// * Convert channel to private menu option should be visible;
cy.get('#channelConvertToPrivate').should('be.visible');
// * Archive channel menu option should be visible;
cy.get('#channelArchiveChannel').should('be.visible');
// * Add members menu option should be visible;
cy.get('#channelAddMembers').should('be.visible');
// * Notification preferences option should be visible;
cy.get('#channelNotificationPreferences').should('be.visible');
// # Close the channel dropdown menu
cy.get('#channelHeaderTitle').click();
// # Archive the channel
cy.uiArchiveChannel();
// * Rename menu option should not be visible;
cy.get('#channelRename').should('not.exist');
// * Edit header menu option should not be visible;
cy.get('#channelEditHeader').should('not.exist');
// * Edit purpose menu option should not be visible;
cy.get('#channelEditPurpose').should('not.exist');
// * Convert channel to private menu option should not be visible;
cy.get('#channelConvertToPrivate').should('not.exist');
// * Archive channel menu option should not be visible;
cy.get('#channelArchiveChannel').should('not.exist');
// * Add members menu option should not be visible;
cy.get('#channelAddMembers').should('not.exist');
// * Notification preferences option should not be visible;
cy.get('#channelNotificationPreferences').should('not.exist');
// # Close the channel dropdown menu
cy.get('#channelHeaderTitle').click();
});
});

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

@@ -0,0 +1,55 @@
// 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 @channel
describe('Archive channel members spec', () => {
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit create channel
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({team, channel}) => {
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T1719 Archived channel members cannot be managed', () => {
// # click on channel drop-down menu
cy.get('#channelHeaderTitle').click();
// * View members menu option should not be visible;
cy.get('#channelViewMembers').should('not.exist');
// * Manage members menu option should be visible;
cy.get('#channelManageMembers').should('be.visible');
// # Close the channel dropdown menu
cy.get('#channelHeaderTitle').click();
// # Archive the channel
cy.uiArchiveChannel();
// # click on channel drop-down menu
cy.get('#channelHeaderTitle').click();
// * Manage members menu option should not be visible;
cy.get('#channelManageMembers').should('not.exist');
// # click on view members menu option;
cy.get('#channelViewMembers button').should('be.visible').click();
// * Ensure there are no options to change channel roles or membership
cy.uiGetRHS().findByText('Manage').should('not.exist');
});
});

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

@@ -0,0 +1,129 @@
// 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 @channel
import {getRandomId} from '../../../utils';
import {createArchivedChannel} from './helpers';
describe('Leave an archived channel', () => {
let testTeam;
let testChannel;
let testUser;
const testArchivedMessage = `this is an archived post ${getRandomId()}`;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit test channel
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
cy.apiCreateUser({prefix: 'second'}).then(({user: second}) => {
cy.apiAddUserToTeam(testTeam.id, second.id);
});
cy.visit(`/${team.name}/channels/${testChannel.name}`);
cy.postMessageAs({sender: testUser, message: testArchivedMessage, channelId: testChannel.id});
});
});
it('MM-T1704 Archived channels appear in channel switcher after refresh', () => {
// # Archive the channel
cy.apiLogin(testUser);
cy.uiArchiveChannel();
// # Switch to another channel
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Use CTRL / CMD+K shortcut to open channel switcher.
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing the name of the archived channel in the search bar of the channel switcher
cy.get('#quickSwitchInput').type(testChannel.display_name);
// * The archived channel appears in channel switcher search results
cy.get('#suggestionList').should('be.visible');
cy.get('#suggestionList').find(`#switchChannel_${testChannel.name}`).should('be.visible');
// # Reload the app (refresh the web page)
cy.reload().then(() => {
// # Return to the channel switcher
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing the name of the archived channel in the search bar of the channel switcher
cy.get('#quickSwitchInput').type(testChannel.display_name).then(() => {
// * The archived channel appears in channel switcher search results
cy.get('#suggestionList').should('be.visible');
cy.get('#suggestionList').find(`#switchChannel_${testChannel.name}`).should('be.visible');
});
});
});
it('MM-T1705 User can unarchive a public channel', () => {
// # As a user with appropriate permission, archive a public channel:
cy.apiAdminLogin();
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.contains('#channelHeaderTitle', 'Off-Topic');
const messageText = `archived text ${getRandomId()}`;
createArchivedChannel({prefix: 'unarchive-'}, [messageText]).then(({name}) => {
// # View the archived channel, noting that it is read-only
cy.uiGetPostTextBox({exist: false});
// # Unarchive the channel:
cy.uiUnarchiveChannel().then(() => {
// * Channel is no longer read-only
cy.uiGetPostTextBox();
// * Channel is displayed in LHS with the normal icon, not an archived channel icon
cy.get(`#sidebarItem_${name}`).scrollIntoView().should('be.visible');
cy.get(`#sidebarItem_${name}`).find('.icon-globe').should('be.visible');
});
});
});
it('MM-T1706 User can unarchive a private channel', () => {
// # As a user with appropriate permission, archive a private channel:
cy.apiAdminLogin();
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.contains('#channelHeaderTitle', 'Off-Topic');
const messageText = `archived text ${getRandomId()}`;
const channelOptions = {
prefix: 'private-unarchive-',
isPrivate: true,
};
createArchivedChannel(channelOptions, [messageText]).then(({name}) => {
// # View the archived channel, noting that it is read-only
cy.uiGetPostTextBox({exist: false});
// # Unarchive the channel:
cy.uiUnarchiveChannel().then(() => {
// * Channel is no longer read-only
cy.uiGetPostTextBox();
// * Channel is displayed in LHS with the normal icon, not an archived channel icon
cy.get(`#sidebarItem_${name}`).find('.icon-lock-outline').should('be.visible');
});
});
});
});

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

@@ -0,0 +1,132 @@
// 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 @channel
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Archived channels', () => {
let testTeam;
let testUser;
let otherUser;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit created channel
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser({prefix: 'second'}).then(({user: second}) => {
cy.apiAddUserToTeam(testTeam.id, second.id);
otherUser = second;
cy.apiLogin(testUser);
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
});
});
});
});
it('MM-T1716 Text box in center channel and in RHS should not be visible', () => {
// # Post a message in the channel
cy.postMessage('Test archive reply');
cy.getLastPostId().then((id) => {
cy.clickPostCommentIcon(id);
// * RHS should be visible
cy.get('#rhsContainer').should('be.visible');
// * RHS text box should be visible
cy.uiGetReplyTextBox();
// # Archive the channel
cy.uiArchiveChannel();
// * Post text box should not be visible
cy.uiGetPostTextBox({exist: false});
// * RHS should not be visible
cy.get('#rhsContainer').should('not.exist');
// # Open RHS
cy.clickPostCommentIcon(id);
cy.get('#rhsContainer').should('be.visible');
// * RHS text box should not be visible
cy.uiGetReplyTextBox({exist: false});
});
});
it('MM-T1722 Can click reply arrow on a post from archived channel, from saved posts list', () => {
// # Create a channel that will be archived
cy.apiCreateChannel(testTeam.id, 'archived-channel', 'Archived Channel').then(({channel}) => {
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Post message
cy.postMessage('Test');
// # Create permalink to post
cy.getLastPostId().then((id) => {
const permalink = `${Cypress.config('baseUrl')}/${testTeam.name}/pl/${id}`;
// # Click on ... button of last post
cy.clickPostDotMenu(id);
// # Click on "Copy Link"
cy.uiClickCopyLink(permalink, id);
// # Post the message in another channel
cy.get('#sidebarItem_off-topic').click();
cy.postMessage(permalink).wait(TIMEOUTS.ONE_SEC);
});
// # Archive the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
cy.uiArchiveChannel();
});
// # Change user
cy.apiLogout();
cy.reload();
cy.apiLogin(otherUser);
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Read the message and save post
cy.get('a.markdown__link').click();
cy.getNthPostId(1).then((postId) => {
cy.clickPostSaveIcon(postId);
});
// # View saved posts
cy.uiGetSavedPostButton().click();
cy.wait(TIMEOUTS.HALF_SEC);
// * RHS should be visible
cy.get('#searchContainer').should('be.visible');
// * Should be able to click on reply
cy.get('#search-items-container div.post-message__text-container > div').last().should('have.attr', 'id').and('not.include', ':').
invoke('replace', 'rhsPostMessageText_', '').then((rhsPostId) => {
cy.clickPostCommentIcon(rhsPostId, 'SEARCH');
// * RHS text box should not be visible
cy.uiGetReplyTextBox({exist: false});
});
});
});

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

@@ -0,0 +1,74 @@
// 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 @channel
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Archived channels', () => {
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit created channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T1718 Reaction icon should not be visible for archived channel posts', () => {
const messageText = 'Test archive reaction';
// # Post a message in the channel
cy.postMessage(messageText);
// # Get the last post for reference of ID
cy.getLastPostId().then((postId) => {
// # Click on post dot menu so we can check for reaction icon
cy.clickPostDotMenu();
// * Reaction icon should be visible as channel is not archived
cy.wait(TIMEOUTS.HALF_SEC).get(`#CENTER_reaction_${postId}`).should('be.visible');
// # Archive the channel
cy.uiArchiveChannel();
// # Click on post dot menu so we can check for reaction icon
cy.clickPostDotMenu(postId);
// * Reaction icon on center channel post should not be visible anymore
cy.wait(TIMEOUTS.HALF_SEC).get(`#CENTER_reaction_${postId}`).should('not.exist');
// # Remove focus so we can open RHS
cy.get('#channel_view').click();
// # Open RHS for the post
cy.clickPostCommentIcon(postId);
// # Click on post dot menu so we can check for reaction icon
cy.clickPostDotMenu(postId, 'RHS_ROOT');
// * Reaction icon on post in RHS should not be visible anymore
cy.wait(TIMEOUTS.HALF_SEC).get(`#RHS_ROOT_reaction_${postId}`).should('not.exist');
// # Search for "Test archive reaction"
cy.get('#searchBox').should('be.visible').type(messageText).type('{enter}');
// # Click on post dot menu so we can check for reaction icon
cy.clickPostDotMenu(postId, 'SEARCH');
// * Reaction icon on post in search results should not be visible anymore
cy.wait(TIMEOUTS.HALF_SEC).get(`#searchResult_reaction_${postId}`).should('not.exist');
});
});
});

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

@@ -0,0 +1,134 @@
// 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 @channel
import {getRandomId} from '../../../utils';
import {createArchivedChannel} from './helpers';
describe('archive tests while preventing viewing archived channels', () => {
let testTeam;
let testChannel;
let testUser;
const testArchivedMessage = `this is an archived post ${getRandomId()}`;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit test channel
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
cy.apiCreateUser({prefix: 'second'}).then(({user: second}) => {
cy.apiAddUserToTeam(testTeam.id, second.id);
});
cy.visit(`/${team.name}/channels/${testChannel.name}`);
});
});
it('MM-T1707 Unarchived channels can be searched the same as before they where archived', () => {
// # Post some text in the channel such as "Pineapple"
const messageText = `pineapple ${getRandomId()}`;
// # Archive the channel
createArchivedChannel({prefix: 'pineapple-'}, [messageText]).then(() => {
// # Unarchive the channel
cy.uiUnarchiveChannel().then(() => {
// # Remove search on archived channels
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: false,
},
});
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.contains('#channelHeaderTitle', 'Off-Topic');
cy.postMessage(getRandomId());
// # Search for the post from step 1')
cy.get('#searchBox').click().clear().type(`${messageText}{enter}`);
// * Post is returned by search, since it's not archived anymore
cy.get('#searchContainer').should('be.visible');
cy.get('.search-item-snippet').first().contains(messageText);
});
});
});
it('MM-T1708 An archived channel can\'t be searched when "Allow users to view archived channels" is set to False in "Site Configuration > Users and Teams" in the System Console', () => {
// # First, as system admin, ensure that System Console > Users and Teams > Allow users to view archived channels is set to `false`.
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: false,
},
});
cy.apiLogin(testUser);
// # Open a channel other than Town Square
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.contains('#channelHeaderTitle', 'Off-Topic');
// # Create or locate a channel you're a member of
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.get('#channelHeaderTitle').should('be.visible');
// # Post distinctive text in the channel such as "I like pineapples"
cy.postMessageAs({sender: testUser, message: testArchivedMessage, channelId: testChannel.id});
// # Select Archive Channel from the header menu
cy.uiArchiveChannel();
// # Archive dialogue message reads "This will archive the channel from the team and make its contents inaccessible for all users" (Mobile dialogue makes no mention of the data will be accessible)
cy.get('#searchBox').click().clear().type(`${testArchivedMessage}{enter}`);
// * Post is not returned by search
cy.get('#searchContainer').should('be.visible');
cy.get('.no-results__wrapper').should('be.visible');
});
it('MM-T1709 Archive a channel while search results are displayed in RHS', () => {
const messageText = `search ${getRandomId()} pineapples`;
// # Post a unique string of text in a channel
cy.uiCreateChannel({prefix: 'archive-while-searching'}).then(() => {
cy.postMessage(messageText);
// # Search for the string of text from step 1
cy.get('#searchBox').click().clear().type(`${messageText}{enter}`);
// * Post is returned by search, since it's not archived anymore
cy.get('#searchContainer').should('be.visible');
// # Observe the post is displayed in RHS results
cy.get('.search-item-snippet').first().should('contain.text', messageText);
// # While the RHS is still open with the results, archive the channel the post was made in
cy.uiArchiveChannel();
cy.get('#searchContainer').should('be.visible');
cy.get('.no-results__wrapper').should('be.visible');
});
});
it('MM-T1710 archived channels are not listed on the "in:" autocomplete', () => {
// # Archive a channel and make a mental note of the channel name
// # Type "in:" and note the list of channels that appear
cy.get('#searchBox').click().clear().type(`in:${testChannel.name}`);
cy.findByTestId(testChannel.name).should('not.exist');
});
});

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

@@ -0,0 +1,138 @@
// 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.
// ***************************************************************
// Group: @channels @channel
import {getRandomId} from '../../../utils';
import {createArchivedChannel} from './helpers';
describe('Leave an archived channel', () => {
let testTeam;
let testChannel;
const testArchivedMessage = `this is an archived post ${getRandomId()}`;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
testChannel = channel;
});
});
it('MM-T1672_1 User can close archived channel (1/2)', () => {
// # Open a channel that's not the town square
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # repeat searching and navigating to the archived channel steps 3 times.
[1, 2, 3].forEach((i) => {
// * ensure we are not on an archived channel
cy.get('#channelInfoModalLabel span.icon__archive').should('not.exist');
// # Search for a post in an archived channel
cy.get('#searchBox').click().clear().type(`${testArchivedMessage}{enter}`);
// # Open the archived channel by selecting Jump from search results and then selecting the link to move to the most recent posts in the channel
cy.get('#searchContainer').should('be.visible');
cy.get('a.search-item__jump').first().click();
cy.get(`#sidebarItem_${testChannel.name}`).should('be.visible');
cy.url().should('satisfy', (testUrl) => {
return testUrl.endsWith(`${testTeam.name}/channels/${testChannel.name}`); // wait for permalink to turn into channel url
});
if (i < 3) {
// # Close an archived channel by clicking "Close Channel" button in the footer
cy.get('#channelArchivedMessage button').click();
} else {
// # Click the header menu and select Archive Channel
cy.uiOpenChannelMenu('Archive Channel');
}
// * The user is returned to the channel they were previously viewing and the archived channel is removed from the drawer
cy.get(`#sidebarItem_${testChannel.name}`).should('not.exist');
cy.url().should('include', `${testTeam.name}/channels/off-topic`);
});
});
it('MM-T1672_2 User can close archived channel (2/2)', () => {
// # Add text to channel you land on (after closing the archived channel via Close Channel button)
// * Able to add test
cy.postMessage('some text');
cy.getLastPostId().then((postId) => {
cy.get(`#${postId}_message`).should('be.visible');
});
});
it('MM-T1678 Open an archived channel using CTRL/CMD+K', () => {
// # Select CTRL/CMD+K (or ⌘+K) to open the channel switcher
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing the name of a public or private channel on this team that has been archived
cy.findByRole('textbox', {name: 'quick switch input'}).type(testChannel.display_name);
// # Select an archived channel from the list
cy.get('#suggestionList').should('be.visible');
cy.findByTestId(testChannel.name).should('be.visible');
cy.findByTestId(testChannel.name).click();
// * Channel name visible in header
cy.get('#channelHeaderTitle').should('contain', testChannel.display_name);
// * Archived icon is visible in header
cy.get('#channelHeaderInfo .icon-archive-outline').should('be.visible');
// * Channel is listed In drawer
cy.get(`#sidebarItem_${testChannel.name}`).should('be.visible');
cy.get(`#sidebarItem_${testChannel.name} .icon-archive-outline`).should('be.visible');
// * footer shows you are viewing an archived channel
cy.get('#channelArchivedMessage').should('be.visible');
});
it('MM-T1679 Open an archived channel using jump from search results', () => {
// # Create or locate post in an archived channel where the test user had permissions to edit channel details
// generate a sufficiently large set of messages to make the toast appear
const messageList = Array.from({length: 40}, (_, i) => `${i}. any - ${getRandomId()}`);
createArchivedChannel({prefix: 'archived-search-for'}, messageList).then(({name}) => {
// # Locate the post in a search
cy.get('#searchBox').click().clear().type(`${messageList[1]}{enter}`);
// # Click jump to open an archive post in permalink view
cy.get('#searchContainer').should('be.visible');
cy.get('a.search-item__jump').first().click();
// * Archived channel is opened in permalink view
cy.get('.post--highlight').should('be.visible');
// # Click on You are viewing an archived channel.
cy.get('.toast__jump').should('be.visible').click();
// * Channel is listed In drawer
// * Channel name visible in header
cy.get(`#sidebarItem_${name}`).should('be.visible');
cy.get(`#sidebarItem_${name} .icon-archive-outline`).should('be.visible');
// * Archived icon is visible in header
cy.get('#channelHeaderInfo .icon-archive-outline').should('be.visible');
// * Footer shows "You are viewing an archived channel. New messages cannot be posted. Close Channel"
cy.get('#channelArchivedMessage').should('be.visible');
});
});
});

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

@@ -0,0 +1,185 @@
// 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.
// ***************************************************************
// Group: @channels @channel
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Leave an archived channel', () => {
let testTeam;
let testUser;
let otherUser;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit test channel
cy.apiInitSetup({
loginAfter: true,
promoteNewUserAsAdmin: true,
}).then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser({prefix: 'second'}).then(({user: second}) => {
cy.apiAddUserToTeam(testTeam.id, second.id);
otherUser = second;
});
});
});
it('MM-T1670 Can view channel info for an archived channel', () => {
cy.apiCreateArchivedChannel('archived-a', 'Archived A', 'O', testTeam.id).then((channel) => {
// # Visit the archived channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel menu and click View Info
cy.uiOpenChannelMenu('View Info');
// * Channel title is shown with archived icon
cy.get('.sidebar--right__header i.icon-archive-outline').should('be.visible');
cy.contains('.sidebar--right__header', `${channel.display_name}`).should('be.visible');
// * Channel URL is listed (non-linked text)
cy.url().then((loc) => {
cy.contains('div[class^="ChannelLink"]', loc).should('be.visible');
});
});
});
it('MM-T1671 Can view members', () => {
cy.apiCreateChannel(testTeam.id, 'archived-b', 'Archived B').then(({channel}) => {
cy.apiAddUserToChannel(channel.id, otherUser.id);
// # Archive the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
cy.uiArchiveChannel();
// # Open channel menu and click View Members
cy.uiOpenChannelMenu('View Members');
// * Channel Members modal opens
cy.get('div#channelMembersModal').should('be.visible');
// # Ensure there are no options to change channel roles or membership
// * Membership or role cannot be changed
cy.findAllByTestId('userListItemActions').eq(0).should('not.be.visible');
cy.findAllByTestId('userListItemActions').eq(1).should('not.be.visible');
// # Use search box to refine list of members
// * Search box works as before to refine member list
cy.get('#searchUsersInput').type(`${otherUser.first_name.substring(0, 5)}{enter}`);
cy.findAllByTestId('userListItemDetails').should('have.length', 2);
});
});
it('MM-T1673 Close channel after viewing two archived channels in a row', () => {
// # Create an archived channel and post a message
const messageC = 'archived channel C message';
cy.apiCreateArchivedChannel('archived-c', 'Archived C', 'O', testTeam.id, [messageC], testUser).then((archivedChannelC) => {
// # Create another archived channel and post a message
const messageD = 'archived channel D message';
cy.apiCreateArchivedChannel('archived-d', 'Archived D', 'O', testTeam.id, [messageD], testUser).then((archivedChannelD) => {
// # Visit town-square and post a message
const previousChannel = `/${testTeam.name}/channels/town-square`;
cy.visit(previousChannel);
// # Search for content from an archived channel
cy.get('#searchBox').click().clear().type(`${messageD}{enter}`);
// # Open the channel from search results
cy.get('#searchContainer').should('be.visible');
cy.get('#loadingSpinner').should('not.exist');
// # Open the channel from search result by clicking Jump
cy.get('#searchContainer').should('be.visible').findByText('Jump').click().wait(TIMEOUTS.ONE_SEC);
cy.url().should('contain', `${testTeam.name}/channels/${archivedChannelD.name}`);
// # Search for content from a different archived channel
cy.get('#searchBox').click().clear().type(`${messageC}{enter}`);
// # Open the channel from search result by clicking Jump
cy.get('#searchContainer').should('be.visible').findByText('Jump').click().wait(TIMEOUTS.ONE_SEC);
cy.url().should('contain', `${testTeam.name}/channels/${archivedChannelC.name}`);
// # Select "Close Channel"
cy.findByRole('button', {name: 'Close Channel'}).click();
// * User is returned to previously viewed (non-archived) channel
cy.url().should('include', previousChannel);
});
});
});
it('MM-T1674 CTRL/CMD+K list public archived channels you are a member of', () => {
const commonName = `common${getRandomId()}`;
cy.apiCreateChannel(testTeam.id, `${commonName}-a`, commonName).then(({channel}) => {
// # Create a public channel then archived
cy.apiCreateArchivedChannel(`${commonName}-b-archived`, `${commonName} Archived E`, 'O', testTeam.id, ['any message'], testUser).then((archivedChannel) => {
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Select CTRL/⌘+k) to open the channel switcher
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing the name of a public channel on this team that has been archived which the test user belongs to
cy.findByRole('textbox', {name: 'quick switch input'}).type(commonName);
// * Suggestion list should be visible and have three elements (the two channels and the divider)
cy.get('#suggestionList').should('be.visible').children().should('have.length', 3);
// * Both active and archived public channels should be visible
cy.findByTestId(channel.name).should('be.visible').find('.icon-globe').should('be.visible');
cy.findByTestId(archivedChannel.name).should('be.visible').find('.icon-archive-outline').should('be.visible');
});
});
});
it('MM-T1675 CTRL/CMD+K list private archived channels you are a member of', () => {
const commonName = `common${getRandomId()}`;
cy.apiCreateChannel(testTeam.id, `${commonName}-a`, commonName).then(({channel}) => {
// # Create a private channel then archived
cy.apiCreateArchivedChannel(`${commonName}-b-archived`, `${commonName} Archived F`, 'P', testTeam.id, ['any message'], testUser).then((archivedChannel) => {
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Select CTRL/⌘+k) to open the channel switcher
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing the name of a private channel on this team that has been archived which the test user belongs to
cy.findByRole('textbox', {name: 'quick switch input'}).type(commonName);
// * Suggestion list should be visible and have three elements (the two channels and the divider)
cy.get('#suggestionList').should('be.visible').children().should('have.length', 3);
// * Both active public and archived private channels should be visible
cy.findByTestId(channel.name).should('be.visible').find('.icon-globe').should('be.visible');
cy.findByTestId(archivedChannel.name).should('be.visible').find('.icon-archive-outline').should('be.visible');
});
});
});
it('MM-T1676 CTRL/CMD+K does not show private archived channels you are not a member of', () => {
// # As another user, create or locate a private channel that the test user is not a member of and archive the channel
cy.apiLogin(otherUser);
cy.apiCreateArchivedChannel('archived-g', 'Archived G', 'O', testTeam.id, ['any message'], testUser).then((archivedChannel) => {
// # As the test user, select CTRL/CMD+K (or ⌘+k) to open the channel switcher
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.contains('#channelHeaderTitle', 'Off-Topic');
cy.typeCmdOrCtrl().type('K', {release: true});
// * Private archived channels you are not a member of are not available on channel switcher
cy.contains('#suggestionList', archivedChannel.name).should('not.exist');
});
});
});

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

@@ -0,0 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import * as TIMEOUTS from '../../../fixtures/timeouts';
export function createArchivedChannel(channelOptions, messages, memberUsernames?) {
return cy.uiCreateChannel(channelOptions).then((newChannel) => {
if (memberUsernames) {
cy.uiAddUsersToCurrentChannel(memberUsernames);
}
if (messages) {
let messageList = messages;
if (!Array.isArray(messages)) {
messageList = [messages];
}
messageList.forEach((message) => {
cy.postMessage(message);
});
}
cy.uiArchiveChannel();
// # Wait for sometime and verify that the archived message is shown
cy.wait(TIMEOUTS.FIVE_SEC);
cy.get('#channelArchivedMessage', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
return cy.wrap({name: newChannel.name});
});
}

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

@@ -0,0 +1,128 @@
// 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 @channel
import {getAdminAccount} from '../../../support/env';
import {getRandomId} from '../../../utils';
describe('Archived channels', () => {
let testTeam;
let testUser;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
it('MM-T1682 Join an archived public channel by selecting a permalink to one of its posts', () => {
// # Log in as another user
cy.apiAdminLogin();
// # Create a new channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
// # Make a post in the new channel and get a permalink for it
cy.postMessageAs({
sender: getAdminAccount(),
message: 'post',
channelId: channel.id,
}).then((post) => {
const permalink = `/${testTeam.name}/pl/${post.id}`;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Log out and back in as the test user
cy.apiLogin(testUser);
// # Visit the permalink
cy.visit(permalink);
// * Verify that we've logged in as the test user
verifyUsername(testUser.username);
verifyViewingArchivedChannel(channel);
});
});
});
it('MM-T1683 Join an archived channel by selecting a link to channel', () => {
// # Log in as another user
cy.apiAdminLogin();
// # Create a new channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
const channelLink = `/${testTeam.name}/channels/${channel.name}`;
// # Visit the channel and archive it
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
cy.uiArchiveChannel();
// # Visit off-topic
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Make a post linking to the archived channel
const linkText = `link ${getRandomId()}`;
cy.getCurrentChannelId().then((currentChannelId) => {
cy.postMessageAs({
sender: getAdminAccount(),
message: `This is a link: [${linkText}](${channelLink})`,
channelId: currentChannelId,
});
});
// # Log out and back in as the test user
cy.apiLogin(testUser);
// # Visit off-topic
cy.visit(`/${testTeam.name}/channels/off-topic`);
// * Verify that we've logged in as the test user
verifyUsername(testUser.username);
// * Verify that the link exists and then click on it
cy.contains('a', linkText).should('be.visible').click();
verifyViewingArchivedChannel(channel);
});
});
});
function verifyViewingArchivedChannel(channel) {
// * Verify that we've switched to the correct channel and that the header contains the archived icon
cy.get('#channelHeaderTitle').should('contain', channel.display_name);
cy.get('#channelHeaderInfo .icon__archive').should('be.visible');
// * Verify that the channel is visible in the sidebar with the archived icon
cy.get(`#sidebarItem_${channel.name}`).should('be.visible').
find('.icon-archive-outline').should('be.visible');
// * Verify that the archived channel banner is visible at the bottom of the channel view
cy.get('#channelArchivedMessage').should('be.visible');
}
function verifyUsername(username) {
// * Verify that we've logged in as the test user
cy.uiOpenUserMenu().findByText(`@${username}`);
// # Close the user menu
cy.uiGetSetStatusButton().click();
}

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

@@ -0,0 +1,61 @@
// 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 @channel
describe('Leaving archived channels', () => {
let testTeam;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
testTeam = team;
});
});
it('MM-T1685 User can leave archived public channel', () => {
// # Create a new public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave the channel
cy.uiLeaveChannel();
// * Verify that we've switched to Town Square
cy.url().should('include', '/channels/town-square');
});
});
it('MM-T1686 User can leave archived private channel', () => {
// # Create a new private channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel', 'P').then(({channel}) => {
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave the channel
cy.uiLeaveChannel(true);
// * Verify that we've switched to Town Square
cy.url().should('include', '/channels/town-square');
});
});
});

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

@@ -0,0 +1,73 @@
// 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 @channel
describe('Archived channels', () => {
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit created channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T1721 Archive channel posts menu should have copy link and reply options', () => {
// # Click to add a channel description
cy.findByRoleExtended('button', {name: 'Add a channel header'}).should('be.visible').click();
// # Add channel header for system message
const header = 'this is a header!';
cy.get('#editChannelHeaderModalLabel').should('be.visible');
cy.get('textarea#edit_textbox').should('be.visible').type(`${header}{enter}`);
cy.getLastPostId().then((postId) => {
// * Check post if it is the system message
cy.get(`#postMessageText_${postId}`).should('contain', header);
cy.clickPostDotMenu(postId).then(() => {
// * Copy link menu item should not be visible
cy.findByText('Copy Link').should('not.exist');
// * Reply post menu item should not be visible
cy.findByText('Reply').should('not.exist');
cy.get(`#CENTER_dropdown_${postId}`).should('be.visible').type('{esc}');
});
});
const messageText = 'Test archive channel post menu';
// # Post a message in the channel
cy.postMessage(messageText);
// # Get the last post for reference of ID
cy.getLastPostId().then((postId) => {
// # Archive the channel
cy.uiArchiveChannel();
// # Wait until a system message is posted that the channel has been archived
cy.uiWaitUntilMessagePostedIncludes('archived the channel');
// # Click on post dot menu
cy.clickPostDotMenu(postId);
// * Copy link menu item should be visible
cy.findByText('Copy Link').scrollIntoView().should('be.visible');
// * Reply post menu item should be visible
cy.findByText('Reply').should('be.visible');
cy.get(`#CENTER_dropdown_${postId}`).should('be.visible').type('{esc}');
});
});
});

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

@@ -0,0 +1,185 @@
// 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 @system_console @authentication
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Authentication', () => {
let testUser;
let testUserAlreadyInTeam;
let testTeam;
before(() => {
// # Do email test if setup properly
cy.shouldHaveEmailEnabled();
cy.apiInitSetup().then(({user, team}) => {
testUserAlreadyInTeam = user;
testTeam = team;
cy.apiCreateUser().then(({user: newUser}) => {
testUser = newUser;
});
});
});
beforeEach(() => {
// # Log in as a admin.
cy.apiAdminLogin();
});
it('MM-T1756 - Restrict Domains - Multiple - success', () => {
// # Set restricted domain
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'mattermost.com, test.com',
},
}).then(() => {
cy.apiLogout();
// # Go to front page
cy.visit('/login');
// * Assert that create account button is visible
cy.findByText('Don\'t have an account?', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// # Go to sign up with email page
cy.visit('/signup_user_complete');
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(`Hossein2Cool4School${getRandomId()}@mattermost.com`);
cy.get('#input_password-input').type('Test123456!');
cy.get('#input_name').clear().type(`HosseinIs2Cool${getRandomId()}`);
cy.findByText('Create Account').click();
// * Make sure account was created successfully and we are at the select team page
cy.findByText('Teams you can join:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
});
it('MM-T1757 - Restrict Domains - Multiple - fail', () => {
// # Set restricted domain
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'mattermost.com, test.com',
},
}).then(() => {
cy.apiLogin(testUserAlreadyInTeam);
cy.visit('/');
// # Open Profile
cy.uiOpenProfileModal();
// # Click "Edit" to the right of "Email"
cy.get('#emailEdit').should('be.visible').click();
// # Type new email
cy.get('#primaryEmail').should('be.visible').type('user-123123@example.com');
cy.get('#confirmEmail').should('be.visible').type('user-123123@example.com');
cy.get('#currentPassword').should('be.visible').type(testUser.password);
// # Save the settings
cy.uiSave().wait(TIMEOUTS.HALF_SEC);
// * Assert an error exist and is what is expected
cy.findByText('The email you provided does not belong to an accepted domain. Please contact your administrator or sign up with a different email.').should('be.visible');
});
});
it('MM-T1758 - Restrict Domains - Team invite closed team', () => {
// # Set restricted domain
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'mattermost.com, test.com',
},
}).then(() => {
cy.apiLogout();
cy.visit(`/signup_user_complete/?id=${testTeam.invite_id}`);
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(`Hossein_Is_The_Best_PROGRAMMER${getRandomId()}@BestInTheWorld.com`);
cy.get('#input_password-input').type('Test123456!');
cy.get('#input_name').clear().type(`HosseinIs2Cool${getRandomId()}`);
cy.findByText('Create Account').click();
// * Make sure account was not created successfully
cy.findByText('The email you provided does not belong to an accepted domain. Please contact your administrator or sign up with a different email.').should('be.visible');
});
});
it('MM-T1763 - Security - Signup: Email verification not required, user immediately sees Town Square', () => {
// # Disable email verification
cy.apiUpdateConfig({
EmailSettings: {
RequireEmailVerification: false,
},
}).then(({config}) => {
cy.apiLogout();
// # Go to front page
cy.visit('/login');
// * Assert that create account button is visible
cy.findByText('Don\'t have an account?', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// # Go to sign up with email page
cy.visit('/signup_user_complete');
const username = `Hossein${getRandomId()}`;
const email = `${username.toLowerCase()}@example.com`;
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(email);
cy.get('#input_password-input').type('Test123456!');
cy.get('#input_name').clear().type(username);
cy.findByText('Create Account').click();
// * Make sure account was created successfully and we are on the team joining page
cy.findByText('Teams you can join:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
cy.getRecentEmail({username, email}).then(({subject}) => {
// * Verify the subject
expect(subject).to.include(`[${config.TeamSettings.SiteName}] You joined`);
});
});
});
it('MM-T1765 - Authentication - Email - Creation with email = false', () => {
// # Disable user sign up and enable GitLab
cy.apiUpdateConfig({
EmailSettings: {
EnableSignUpWithEmail: false,
},
GitLabSettings: {
Enable: true,
},
}).then(() => {
cy.apiLogout();
cy.visit(`/signup_user_complete/?id=${testTeam.invite_id}`);
cy.findByText('Create your account with one of the following:').should('exist');
cy.findByText('GitLab', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// * Email and Password option does not exist
cy.findByText('Email address').should('not.exist');
cy.findByText('Choose a Password').should('not.exist');
});
});
});

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

@@ -0,0 +1,176 @@
// 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 @system_console @authentication @mfa
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Authentication', () => {
beforeEach(() => {
// # Log in as admin.
cy.apiAdminLogin();
});
it('MM-T1771 - Minimum password length error field shows below 5 and above 64', () => {
cy.visit('/admin_console/authentication/password');
cy.findByPlaceholderText('E.g.: "5"', {timeout: TIMEOUTS.ONE_MIN}).clear().type('88');
cy.uiSave();
// * Ensure error appears when saving a password outside of the limits
cy.findByText('Minimum password length must be a whole number greater than or equal to 5 and less than or equal to 64.', {timeout: TIMEOUTS.ONE_MIN}).
should('exist').
and('be.visible');
cy.findByPlaceholderText('E.g.: "5"').clear().type('3');
cy.uiSave();
// * Ensure error appears when saving a password outside of the limits
cy.findByText('Minimum password length must be a whole number greater than or equal to 5 and less than or equal to 64.', {timeout: TIMEOUTS.ONE_MIN}).
should('exist').
and('be.visible');
});
it('MM-T1772 - Change minimum password length, verify help text and error message', () => {
cy.visit('/admin_console/authentication/password');
cy.findByPlaceholderText('E.g.: "5"', {timeout: TIMEOUTS.ONE_MIN}).clear().type('7');
cy.uiSave();
cy.findByText('Must be 7-64 characters long.').should('be.visible');
cy.apiLogout();
// # Go to sign up with email page
cy.visit('/signup_user_complete');
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(`Hossein_Is_The_Best_PROGRAMMER${getRandomId()}@BestInTheWorld.com`);
cy.get('#input_name').clear().type(`BestUsername${getRandomId()}`);
cy.get('#input_password-input').clear().type('less');
cy.findByText('Create Account').click();
// * Assert the error is what is expected;
cy.findByText('Must be 7-64 characters long.').should('be.visible');
cy.get('#input_password-input').clear().type('greaterthan7');
cy.findByText('Create Account').click();
// * Assert that we are not shown an MFA screen and instead a Teams You Can join page
cy.findByText('Teams you can join:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
it('MM-T1773 - Minimum password length field resets to default after saving invalid value', () => {
cy.visit('/admin_console/authentication/password');
cy.findByPlaceholderText('E.g.: "5"', {timeout: TIMEOUTS.ONE_MIN}).clear().type('10');
cy.uiSave();
cy.reload();
// * Ensure the limit 10 appears
cy.findByPlaceholderText('E.g.: "5"').invoke('val').should('equal', '10');
cy.findByPlaceholderText('E.g.: "5"').clear();
cy.uiSave();
// * Ensure the limit 10 appears
cy.findByPlaceholderText('E.g.: "5"').invoke('val').should('equal', '5');
});
it('MM-T1774 - Select all Password Requirements, verify help text and error on bad password', () => {
cy.apiUpdateConfig({
PasswordSettings: {
Lowercase: true,
Number: true,
Uppercase: true,
Symbol: true,
},
});
cy.apiLogout();
// # Go to sign up with email page
cy.visit('/signup_user_complete');
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(`Hossein_Is_The_Best_PROGRAMMER${getRandomId()}@BestInTheWorld.com`);
cy.get('#input_name').clear().type(`BestUsernameInTheWorld${getRandomId()}`);
['NOLOWERCASE123!', 'noupppercase123!', 'NoNumber!', 'NoSymbol123'].forEach((option) => {
cy.get('#input_password-input').clear().type(option);
cy.findByText('Create Account').click();
// * Assert the error is what is expected;
cy.findByText('Must be 5-64 characters long and include both lowercase and uppercase letters, numbers, and special characters.').should('be.visible');
});
});
it('MM-T1777 - Multi-factor Authentication option hidden in Profile when disabled', () => {
cy.apiUpdateConfig({
ServiceSettings: {
EnableMultifactorAuthentication: false,
},
});
cy.visit('/');
// # Go to Profile
cy.uiOpenProfileModal('Security');
// * Assert that Multi-factor Authentication text does not exist
cy.findByText('Multi-factor Authentication').should('not.exist');
});
it('MM-T1779 - Multi-factor Authentication option appears in Profile when enabled', () => {
cy.apiUpdateConfig({
ServiceSettings: {
EnableMultifactorAuthentication: true,
},
});
cy.visit('/');
// # Go to Profile
cy.uiOpenProfileModal('Security');
// * Assert that Multi-factor Authentication text does exist
cy.findByText('Multi-factor Authentication').should('be.visible');
});
it('MM-T1780 - Multi-factor Authentication false: User can log in without being prompted for MFA', () => {
cy.apiUpdateConfig({
ServiceSettings: {
EnableMultifactorAuthentication: true,
EnforceMultifactorAuthentication: false,
},
});
cy.apiCreateUser().then(({user: newUser}) => {
cy.apiLogout();
// # Login as a new user and visit default page
cy.apiLogin(newUser);
cy.visit('/');
// * Assert that we are not shown a MFA screen and instead a Teams You Can join page
cy.findByText('Teams you can join:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
});
});

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

@@ -0,0 +1,75 @@
// 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 @system_console @authentication
describe('Authentication', () => {
beforeEach(() => {
// # Log in as a admin.
cy.apiAdminLogin();
});
after(() => {
cy.apiAdminLogin({failOnStatusCode: false});
cy.apiUpdateConfig({});
});
const testCases = [
{
title: 'MM-T1767 - Email signin false Username signin true',
signinWithEmail: false,
signinWithUsername: true,
},
{
title: 'MM-T1768 - Email signin true Username signin true',
signinWithEmail: true,
signinWithUsername: true,
},
{
title: 'MM-T1769 - Email signin true Username signin false',
signinWithEmail: true,
signinWithUsername: false,
},
];
testCases.forEach(({title, signinWithEmail, signinWithUsername}) => {
it(title, () => {
cy.apiUpdateConfig({
EmailSettings: {
EnableSignInWithEmail: signinWithEmail,
EnableSignInWithUsername: signinWithUsername,
},
LdapSettings: {
Enable: false,
},
});
cy.apiLogout();
// # Go to front page
cy.visit('/login');
// # Remove autofocus from login input
cy.focused().blur();
let expectedPlaceholderText;
if (signinWithEmail && signinWithUsername) {
expectedPlaceholderText = 'Email or Username';
} else if (signinWithEmail) {
expectedPlaceholderText = 'Email';
} else {
expectedPlaceholderText = 'Username';
}
// * Make sure the username field has expected placeholder text
cy.findByPlaceholderText(expectedPlaceholderText).should('exist').and('be.visible');
});
});
});

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

@@ -0,0 +1,280 @@
// 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 @system_console @authentication
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {reUrl, getRandomId} from '../../../utils';
describe('Authentication', () => {
let testUser;
before(() => {
// # Do email test if setup properly
cy.shouldHaveEmailEnabled();
cy.apiCreateUser().then(({user: newUser}) => {
testUser = newUser;
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
it('MM-T1764 - Security - Signup: Email verification required (after having created account when verification was not required)', () => {
// # Update Configs
cy.apiUpdateConfig({
EmailSettings: {
RequireEmailVerification: false,
},
});
// # Login as test user and make sure it goes to team selection
cy.apiLogin(testUser);
cy.visit('/');
cy.url().should('include', '/select_team');
cy.findByText('Teams you can join:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
cy.apiAdminLogin();
// # Update Configs
cy.apiUpdateConfig({
EmailSettings: {
RequireEmailVerification: true,
},
});
cy.apiLogout();
// # Login as test user and make sure it goes to team selection
cy.visit('/login');
// # Remove autofocus from login input
cy.get('.login-body-card-content').should('be.visible').focus();
// # Clear email/username field and type username
cy.apiGetClientLicense().then(({isLicensed}) => {
cy.findByPlaceholderText(isLicensed ? 'Email, Username or AD/LDAP Username' : 'Email or Username', {timeout: TIMEOUTS.ONE_MIN}).clear().type(testUser.username);
});
// # Clear password field and type password
cy.findByPlaceholderText('Password').clear().type(testUser.password);
// # Hit enter to login
cy.get('#saveSetting').should('not.be.disabled').click();
cy.wait(TIMEOUTS.THREE_SEC);
// * Assert that email verification has been sent and then resend to make sure it gets resent
cy.findByText('Resend Email').should('be.visible').and('exist').click();
cy.findByText('Verification email sent').should('be.visible').and('exist');
cy.findByText('Youre almost done!').should('be.visible').and('exist');
cy.findByText('Please verify your email address. Check your inbox for an email.').should('be.visible').and('exist');
cy.getRecentEmail(testUser).then(({body}) => {
const permalink = body[6].match(reUrl)[0];
// # Visit permalink (e.g. click on email link), view in browser to proceed
cy.visit(permalink);
// # Clear password field and type password
cy.get('#input_password-input').clear().type(testUser.password);
// # Hit enter to login
cy.get('#saveSetting').should('not.be.disabled').click();
// * Should show the join team stuff
cy.findByText('Teams you can join:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
});
it('MM-T1770 - Default password settings', () => {
cy.apiGetClientLicense().then(({isCloudLicensed}) => {
const newConfig = {
PasswordSettings: {
MinimumLength: null,
Lowercase: null,
Number: null,
Uppercase: null,
Symbol: null,
},
ServiceSettings: {},
};
if (!isCloudLicensed) {
newConfig.ServiceSettings = {
MaximumLoginAttempts: null,
};
}
cy.apiUpdateConfig(newConfig);
// * Ensure password has a minimum length of 8 and no password requirements are checked
cy.apiGetConfig().then(({config: {PasswordSettings}}) => {
expect(PasswordSettings.MinimumLength).equal(8);
expect(PasswordSettings.Lowercase).equal(false);
expect(PasswordSettings.Number).equal(false);
expect(PasswordSettings.Uppercase).equal(false);
expect(PasswordSettings.Symbol).equal(false);
});
cy.visit('/admin_console/authentication/password');
cy.get('.admin-console__header').should('be.visible').and('have.text', 'Password');
cy.findByTestId('passwordMinimumLengthinput').should('be.visible').and('have.value', '8');
cy.findByLabelText('At least one lowercase letter').get('input').should('not.be.checked');
cy.findByLabelText('At least one uppercase letter').get('input').should('not.be.checked');
cy.findByLabelText('At least one number').get('input').should('not.be.checked');
cy.findByLabelText('At least one symbol (e.g. "~!@#$%^&*()")').get('input').should('not.be.checked');
if (!isCloudLicensed) {
cy.findByTestId('maximumLoginAttemptsinput').should('be.visible').and('have.value', '10');
}
});
});
it('MM-T1783 - Username validation shows errors for various username requirements', () => {
// # Go to sign up with email page
cy.visit('/signup_user_complete');
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type('Hossein_Is_The_Best_PROGRAMMER@BestInTheWorld.com');
cy.get('#input_password-input').type('Test123456!');
['1user', 'te', 'user#1', 'user!1'].forEach((option) => {
cy.get('#input_name').clear().type(option);
cy.findByText('Create Account').click();
// * Assert the error is what is expected;
cy.findByText('Usernames have to begin with a lowercase letter and be 3-22 characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.').should('be.visible');
});
});
it('MM-T1752 - Enable account creation - true', () => {
// # Enable open server
cy.apiUpdateConfig({
TeamSettings: {
EnableUserCreation: true,
},
});
// # Logout and go to front page
cy.apiLogout();
cy.visit('/login');
// * Assert that create account button is visible
cy.findByText('Don\'t have an account?', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// # Go to sign up with email page
cy.visit('/signup_user_complete');
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(`Hossein_Is_The_Best_PROGRAMMER${getRandomId()}@BestInTheWorld.com`);
cy.get('#input_password-input').type('Test123456!');
cy.get('#input_name').clear().type(`HosseinIs2Cool${getRandomId()}`);
cy.findByText('Create Account').click();
// * Make sure account was created successfully and we are on the team joining page
cy.findByText('Teams you can join:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
it('MM-T1753 - Enable account creation - false', () => {
// # Disable user account creation
cy.apiUpdateConfig({
TeamSettings: {
EnableUserCreation: false,
},
});
cy.apiLogout();
// # Go to front page
cy.visit('/login');
// * Assert that create account button is visible
cy.findByText('Don\'t have an account?', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// # Go to sign up with email page
cy.visit('/signup_user_complete');
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(`Hossein_Is_The_Best_PROGRAMMER${getRandomId()}@BestInTheWorld.com`);
cy.get('#input_password-input').type('Test123456!');
cy.get('#input_name').clear().type(`HosseinIs2Cool${getRandomId()}`);
cy.findByText('Create Account').click();
// * Make sure account was not created successfully and we are on the team joining page
cy.findByText('User sign-up with email is disabled.').should('be.visible').and('exist');
});
it('MM-T1754 - Restrict Domains - Account creation link on signin page', () => {
// # Enable user account creation and set restricted domain
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'test.com',
EnableUserCreation: true,
},
});
cy.apiLogout();
// # Go to front page
cy.visit('/login');
// * Assert that create account button is visible
cy.findByText('Don\'t have an account?', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
// # Go to sign up with email page
cy.visit('/signup_user_complete');
cy.get('#input_email', {timeout: TIMEOUTS.ONE_MIN}).type(`Hossein_Is_The_Best_PROGRAMMER${getRandomId()}@BestInTheWorld.com`);
cy.get('#input_password-input').type('Test123456!');
cy.get('#input_name').clear().type(`HosseinIs2Cool${getRandomId()}`);
cy.findByText('Create Account').click();
// * Make sure account was not created successfully
cy.findByText('The email you provided does not belong to an accepted domain. Please contact your administrator or sign up with a different email.').should('be.visible').and('exist');
});
it('MM-T1755 - Restrict Domains - Email invite', () => {
// # Enable user account creation and set restricted domain
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'test.com',
EnableUserCreation: true,
},
});
cy.visit('/');
// # Open team menu and click on "Invite People"
cy.uiOpenTeamMenu('Invite People');
// # Click invite members if needed
cy.findByText('Copy invite link').click();
// # Input email, select member
cy.findByText('Enter a name or email address').type('HosseinTheBestProgrammer@Mattermost.com{downarrow}{downarrow}{enter}');
// # Click invite members button
cy.findByRole('button', {name: 'Invite'}).click({force: true});
// * Verify message is what you expect it to be
cy.contains('The following email addresses do not belong to an accepted domain:', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').and('exist');
});
});

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

@@ -0,0 +1,69 @@
// 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 @system_console @authentication @not_cloud
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Authentication', () => {
before(() => {
cy.shouldNotRunOnCloudEdition();
});
beforeEach(() => {
// # Log in as admin.
cy.apiAdminLogin();
});
it('MM-T1762 - Invite Salt', () => {
cy.visit('/admin_console/site_config/public_links');
// * Check that public link salt is masked
cy.findByText('********************************').should('be.visible');
// # Click "Regenerate"
cy.findByText('Regenerate', {timeout: TIMEOUTS.ONE_MIN}).click();
// * Check that new public link is generated and unmasked
cy.findByText('********************************').should('not.exist');
});
it('MM-T1775 - Maximum Login Attempts field resets to default after saving invalid value', () => {
cy.visit('/admin_console/authentication/password');
cy.findByPlaceholderText('E.g.: "10"', {timeout: TIMEOUTS.ONE_MIN}).clear().type('ten');
cy.uiSave();
// * Ensure error appears when saving a password outside of the limits
cy.findByPlaceholderText('E.g.: "10"').invoke('val').should('equal', '10');
// * Verify that the config remain the same
cy.apiGetConfig().then(({config}) => {
expect(config.ServiceSettings.MaximumLoginAttempts).to.equal(10);
});
});
it('MM-T1776 - Maximum Login Attempts field successfully saves valid change', () => {
cy.visit('/admin_console/authentication/password');
cy.findByPlaceholderText('E.g.: "10"', {timeout: TIMEOUTS.ONE_MIN}).clear().type('2');
cy.uiSaveConfig();
// * Ensure error appears when saving a password outside of the limits
cy.findByPlaceholderText('E.g.: "10"').invoke('val').should('equal', '2');
// * Verify that the config have changed
cy.apiGetConfig().then(({config}) => {
expect(config.ServiceSettings.MaximumLoginAttempts).to.equal(2);
});
});
});

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

@@ -0,0 +1,32 @@
// 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.
// ***************************************************************
// Group: @channels @system_console @authentication
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Authentication', () => {
before(() => {
// # Enable open server
cy.apiUpdateConfig({
TeamSettings: {
EnableOpenServer: false,
},
});
// # Logout and visit default page
cy.apiLogout();
cy.visit('/');
});
it('MM-T1760 - Enable Open Server false: Create account link is hidden', () => {
// * Assert that create account button is not visible
cy.findByText('Don\'t have an account?', {timeout: TIMEOUTS.TEN_SEC}).should('not.exist');
});
});

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

@@ -0,0 +1,102 @@
// 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.
// ***************************************************************
import {
getPostTextboxInput,
getQuickChannelSwitcherInput,
SimpleUser,
startAtMention,
verifySuggestionAtChannelSwitcher,
verifySuggestionAtPostTextbox,
} from './helpers';
export function doTestPostextbox(mention: string, ...suggestion: Cypress.UserProfile[]) {
getPostTextboxInput();
startAtMention(mention);
verifySuggestionAtPostTextbox(...suggestion);
}
export function doTestQuickChannelSwitcher(mention: string, ...suggestion: Cypress.UserProfile[]) {
getQuickChannelSwitcherInput();
startAtMention(mention);
verifySuggestionAtChannelSwitcher(...suggestion);
}
export function doTestUserChannelSection(prefix: string, testTeam: Cypress.Team, testUsers: Record<string, SimpleUser>) {
const thor = testUsers.thor;
const loki = testUsers.loki;
// # Create new channel and add user to channel
const channelName = 'new-channel';
cy.apiCreateChannel(testTeam.id, channelName, channelName).then(({channel}) => {
cy.apiGetUserByEmail(thor.email).then(({user}) => {
cy.apiAddUserToChannel(channel.id, user.id);
});
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
});
// # Start an at mention that should return 2 users (in this case, the users share a last name)
cy.uiGetPostTextBox().
as('input').
clear().
type(`@${prefix}odinson`);
// * Thor should be a channel member
cy.uiVerifyAtMentionInSuggestionList(thor as Cypress.UserProfile, true);
// * Loki should NOT be a channel member
cy.uiVerifyAtMentionInSuggestionList(loki as Cypress.UserProfile, false);
}
export function doTestDMChannelSidebar(testUsers: Record<string, SimpleUser>) {
const thor = testUsers.thor;
// # Open of the add direct message modal
cy.uiAddDirectMessage().click({force: true});
// # Type username into input
cy.get('.more-direct-channels').
find('input').
should('exist').
type(thor.username, {force: true});
cy.intercept({
method: 'POST',
url: '/api/v4/users/search',
}).as('searchUsers');
cy.wait('@searchUsers').then((interception) => {
expect(interception.response.body.length === 1);
});
// * There should only be one result
cy.get('#moreDmModal').find('.more-modal__row').
as('result').
its('length').
should('equal', 1);
// * Result should have appropriate text
cy.get('@result').
find('.more-modal__name').
should('have.text', `@${thor.username} - ${thor.first_name} ${thor.last_name} (${thor.nickname})`);
cy.get('@result').
find('.more-modal__description').
should('have.text', thor.email);
// # Click on the result to add user
cy.get('@result').click({force: true});
// # Click "Go"
cy.uiGetButton('Go').click();
// # Should land on direct message channel for that user
cy.get('#channelHeaderTitle').should('have.text', thor.username + ' ');
}

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

@@ -0,0 +1,113 @@
// 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 @autocomplete
import {getRandomLetter} from '../../../../utils';
import {doTestQuickChannelSwitcher} from '../common_test';
import {createSearchData} from '../helpers';
describe('Autocomplete with Database - Users', () => {
const prefix = getRandomLetter(3);
let testUsers;
before(() => {
cy.apiGetClientLicense().then(({isCloudLicensed}) => {
if (!isCloudLicensed) {
cy.shouldHaveElasticsearchDisabled();
}
});
createSearchData(prefix).then((searchData) => {
testUsers = searchData.users;
cy.apiLogin(searchData.sysadmin);
// # Navigate to the new teams town square
cy.visit(`/${searchData.team.name}/channels/town-square`);
// # Open quick channel switcher
cy.typeCmdOrCtrl().type('k');
cy.findByRole('textbox', {name: 'quick switch input'}).should('be.visible');
});
});
describe('search for user in channel switcher', () => {
describe('by @username', () => {
it('MM-T4071_1 Full username returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}ironman`, testUsers.ironman);
});
it('MM-T4071_2 Unique partial username returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}doc`, testUsers.doctorstrange);
});
it('MM-T4071_3 Partial username returns all users that match', () => {
doTestQuickChannelSwitcher(`@${prefix}i`, testUsers.ironman);
});
});
describe('by @firstname', () => {
it('MM-T4072_1 Full first name returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}tony`, testUsers.ironman);
});
it('MM-T4072_2 Unique partial first name returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}wa`, testUsers.deadpool);
});
it('MM-T4072_3 Partial first name returns all users that match', () => {
doTestQuickChannelSwitcher(`@${prefix}ste`, testUsers.captainamerica, testUsers.doctorstrange);
});
});
describe('by @lastname', () => {
it('MM-T4073_1 Full last name returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}stark`, testUsers.ironman);
});
it('MM-T4073_2 Unique partial last name returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}ban`, testUsers.hulk);
});
it('MM-T4073_3 Partial last name returns all users that match', () => {
doTestQuickChannelSwitcher(`@${prefix}ba`, testUsers.hawkeye, testUsers.hulk);
});
});
describe('by @nickname', () => {
it('MM-T4074_1 Full nickname returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}ronin`, testUsers.hawkeye);
});
it('MM-T4074_2 Unique partial nickname returns single user', () => {
doTestQuickChannelSwitcher(`@${prefix}gam`, testUsers.hulk);
});
it('MM-T4074_3 Partial nickname returns all users that match', () => {
doTestQuickChannelSwitcher(`@${prefix}pro`, testUsers.captainamerica, testUsers.ironman);
});
});
describe('special characters in usernames are returned', () => {
it('MM-T4075_1 Username with dot', () => {
doTestQuickChannelSwitcher(`@${prefix}dot.dot`, testUsers.dot);
});
it('MM-T4075_2 Username dash', () => {
doTestQuickChannelSwitcher(`@${prefix}dash-dash`, testUsers.dash);
});
it('MM-T4075_3 Username underscore', () => {
doTestQuickChannelSwitcher(`@${prefix}under_score`, testUsers.underscore);
});
});
});
});

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

@@ -0,0 +1,109 @@
// 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 @autocomplete
import {getRandomLetter} from '../../../../utils';
import {doTestPostextbox} from '../common_test';
import {createSearchData} from '../helpers';
describe('Autocomplete with Database - Users', () => {
const prefix = getRandomLetter(3);
let testUsers;
before(() => {
cy.apiGetClientLicense().then(({isCloudLicensed}) => {
if (!isCloudLicensed) {
cy.shouldHaveElasticsearchDisabled();
}
});
createSearchData(prefix).then((searchData) => {
testUsers = searchData.users;
cy.apiLogin(searchData.sysadmin);
// # Navigate to the new teams town square
cy.visit(`/${searchData.team.name}/channels/town-square`);
});
});
describe('search for user in message input box', () => {
describe('by @username', () => {
it('MM-T4076_1 Full username returns single user', () => {
doTestPostextbox(`@${prefix}ironman`, testUsers.ironman);
});
it('MM-T4076_2 Unique partial username returns single user', () => {
doTestPostextbox(`@${prefix}doc`, testUsers.doctorstrange);
});
it('MM-T4076_3 Partial username returns all users that match', () => {
doTestPostextbox(`@${prefix}i`, testUsers.ironman);
});
});
describe('by @firstname', () => {
it('MM-T4077_1 Full first name returns single user', () => {
doTestPostextbox(`@${prefix}tony`, testUsers.ironman);
});
it('MM-T4077_2 Unique partial first name returns single user', () => {
doTestPostextbox(`@${prefix}wa`, testUsers.deadpool);
});
it('MM-T4077_3 Partial first name returns all users that match', () => {
doTestPostextbox(`@${prefix}ste`, testUsers.captainamerica, testUsers.doctorstrange);
});
});
describe('by @lastname', () => {
it('MM-T4078_1 Full last name returns single user', () => {
doTestPostextbox(`@${prefix}stark`, testUsers.ironman);
});
it('MM-T4078_2 Unique partial last name returns single user', () => {
doTestPostextbox(`@${prefix}ban`, testUsers.hulk);
});
it('MM-T4078_3 Partial last name returns all users that match', () => {
doTestPostextbox(`@${prefix}ba`, testUsers.hawkeye, testUsers.hulk);
});
});
describe('by @nickname', () => {
it('MM-T4079_1 Full nickname returns single user', () => {
doTestPostextbox(`@${prefix}ronin`, testUsers.hawkeye);
});
it('MM-T4079_2 Unique partial nickname returns single user', () => {
doTestPostextbox(`@${prefix}gam`, testUsers.hulk);
});
it('MM-T4079_3 Partial nickname returns all users that match', () => {
doTestPostextbox(`@${prefix}pro`, testUsers.captainamerica, testUsers.ironman);
});
});
describe('special characters in usernames are returned', () => {
it('MM-T4080_1 Username with dot', () => {
doTestPostextbox(`@${prefix}dot.dot`, testUsers.dot);
});
it('MM-T4080_2 Username with dash', () => {
doTestPostextbox(`@${prefix}dash-dash`, testUsers.dash);
});
it('MM-T4080_3 Username with underscore', () => {
doTestPostextbox(`@${prefix}under_score`, testUsers.underscore);
});
});
});
});

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

@@ -0,0 +1,44 @@
// 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 @autocomplete
import {getRandomLetter} from '../../../../utils';
import {doTestDMChannelSidebar, doTestUserChannelSection} from '../common_test';
import {createSearchData} from '../helpers';
describe('Autocomplete with Database - Users', () => {
const prefix = getRandomLetter(3);
let testUsers;
let testTeam;
before(() => {
cy.apiGetClientLicense().then(({isCloudLicensed}) => {
if (!isCloudLicensed) {
cy.shouldHaveElasticsearchDisabled();
}
});
createSearchData(prefix).then((searchData) => {
testUsers = searchData.users;
testTeam = searchData.team;
cy.apiLogin(searchData.sysadmin);
});
});
it('MM-T4081 Users in correct in/out of channel sections', () => {
doTestUserChannelSection(prefix, testTeam, testUsers);
});
it('MM-T4082 DM can be opened with a user not on your team or in your DM channel sidebar', () => {
doTestDMChannelSidebar(testUsers);
});
});

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

@@ -0,0 +1,300 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getAdminAccount} from '../../../support/env';
export type SimpleUser = Pick<Cypress.UserProfile, 'username' | 'first_name' | 'last_name' | 'nickname' | 'password' | 'email'>;
function createPrivateChannel(teamId: string, userToAdd: Cypress.UserProfile = null) {
// # Create a private channel as sysadmin
return createChannel('P', teamId, userToAdd);
}
function createPublicChannel(teamId: string, userToAdd: Cypress.UserProfile = null) {
// # Create a public channel as sysadmin
return createChannel('O', teamId, userToAdd);
}
function createSearchData(prefix: string) {
return cy.apiCreateCustomAdmin({loginAfter: true, hideAdminTrialModal: true}).then(({sysadmin}) => {
const users = getTestUsers(prefix);
cy.apiLogin(sysadmin);
// # Create new team for tests
return cy.apiCreateTeam('search', 'Search').then(({team}) => {
// # Create pool of users for tests
Cypress._.forEach(users, (testUser) => {
cy.apiCreateUser({user: testUser}).then(({user}) => {
cy.apiAddUserToTeam(team.id, user.id);
});
});
return cy.wrap({sysadmin, team, users});
});
});
}
function enableElasticSearch() {
// # Enable elastic search via the API
cy.apiUpdateConfig({
ElasticsearchSettings: {
EnableAutocomplete: true,
EnableIndexing: true,
EnableSearching: true,
Sniff: false,
},
} as Cypress.AdminConfig);
// # Navigate to the elastic search setting page
cy.visit('/admin_console/environment/elasticsearch');
// * Test the connection and verify that we are successful
cy.contains('button', 'Test Connection').click();
cy.get('.alert-success').should('have.text', 'Test successful. Configuration saved.');
// # Index so we are up to date
cy.contains('button', 'Index Now').click();
// # Small wait to ensure new row is added
cy.wait(TIMEOUTS.ONE_SEC).get('.job-table__table').find('tbody > tr').eq(0).as('firstRow');
// * Newest row should eventually result in Success
const checkFirstRow = () => {
return cy.get('@firstRow').then((el) => {
return el.find('.status-icon-success').length > 0;
});
};
const options = {
timeout: TIMEOUTS.TWO_MIN,
interval: TIMEOUTS.TWO_SEC,
errorMsg: 'Reindex did not succeed in time',
};
cy.waitUntil(checkFirstRow, options);
}
function getTestUsers(prefix = ''): Record<string, SimpleUser> {
if (Cypress.env('searchTestUsers')) {
return JSON.parse(Cypress.env('searchTestUsers'));
}
return {
ironman: generatePrefixedUser({
username: 'ironman',
first_name: 'Tony',
last_name: 'Stark',
nickname: 'protoncannon',
}, prefix),
hulk: generatePrefixedUser({
username: 'hulk',
first_name: 'Bruce',
last_name: 'Banner',
nickname: 'gammaray',
}, prefix),
hawkeye: generatePrefixedUser({
username: 'hawkeye',
first_name: 'Clint',
last_name: 'Barton',
nickname: 'ronin',
}, prefix),
deadpool: generatePrefixedUser({
username: 'deadpool',
first_name: 'Wade',
last_name: 'Wilson',
nickname: 'merc',
}, prefix),
captainamerica: generatePrefixedUser({
username: 'captainamerica',
first_name: 'Steve',
last_name: 'Rogers',
nickname: 'professional',
}, prefix),
doctorstrange: generatePrefixedUser({
username: 'doctorstrange',
first_name: 'Stephen',
last_name: 'Strange',
nickname: 'sorcerersupreme',
}, prefix),
thor: generatePrefixedUser({
username: 'thor',
first_name: 'Thor',
last_name: 'Odinson',
nickname: 'mjolnir',
}, prefix),
loki: generatePrefixedUser({
username: 'loki',
first_name: 'Loki',
last_name: 'Odinson',
nickname: 'trickster',
}, prefix),
dot: generatePrefixedUser({
username: 'dot.dot',
first_name: 'z1First',
last_name: 'z1Last',
nickname: 'z1Nick',
}, prefix),
dash: generatePrefixedUser({
username: 'dash-dash',
first_name: 'z2First',
last_name: 'z2Last',
nickname: 'z2Nick',
}, prefix),
underscore: generatePrefixedUser({
username: 'under_score',
first_name: 'z3First',
last_name: 'z3Last',
nickname: 'z3Nick',
}, prefix),
};
}
function getPostTextboxInput() {
cy.wait(TIMEOUTS.HALF_SEC);
cy.uiGetPostTextBox().
as('input').
clear();
}
function getQuickChannelSwitcherInput() {
cy.findByRole('textbox', {name: 'quick switch input'}).
should('be.visible').
as('input').
clear();
}
function searchAndVerifyChannel(channel: Cypress.Channel, shouldExist = true) {
const name = channel.display_name;
searchForChannel(name);
if (shouldExist) {
// * Channel should appear in suggestions list
cy.get('#suggestionList').findByTestId(channel.name).should('be.visible');
} else {
// * Suggestion list and channel item should not appear
cy.get('#suggestionList').should('not.exist');
cy.findByTestId(channel.name).should('not.exist');
}
}
function searchAndVerifyUser(user: Cypress.UserProfile) {
// # Start @ mentions autocomplete with username
cy.uiGetPostTextBox().
as('input').
clear().
type(`@${user.username}`);
// * Suggestion list should appear
cy.get('#suggestionList', {timeout: TIMEOUTS.FIVE_SEC}).should('be.visible');
// * Verify user appears in results post-change
return cy.uiVerifyAtMentionSuggestion(user);
}
function searchForChannel(name: string) {
// # Open up channel switcher
cy.typeCmdOrCtrl().type('k').wait(TIMEOUTS.ONE_SEC);
// # Clear out and type in the name
cy.findByRole('textbox', {name: 'quick switch input'}).
should('be.visible').
as('input').
clear().
type(name);
}
function startAtMention(string: string) {
// # Get the expected input
cy.get('@input').clear().type(string);
// * Suggestion list should appear
cy.get('#suggestionList').should('be.visible');
}
function verifySuggestionAtPostTextbox(...expectedUsers: Cypress.UserProfile[]) {
expectedUsers.forEach((user) => {
cy.wait(TIMEOUTS.HALF_SEC);
cy.uiVerifyAtMentionSuggestion(user);
});
}
function verifySuggestionAtChannelSwitcher(...expectedUsers: Cypress.UserProfile[]) {
expectedUsers.forEach((user) => {
cy.findByTestId(user.username).
should('be.visible').
and('have.text', `${user.first_name} ${user.last_name} (${user.nickname})@${user.username}`);
});
}
function createChannel(channelType: string, teamId: string, userToAdd: Cypress.UserProfile = null) {
// # Create a channel as sysadmin
return cy.externalRequest({
user: getAdminAccount(),
method: 'POST',
path: 'channels',
data: {
team_id: teamId,
name: 'test-channel' + Date.now(),
display_name: 'Test Channel ' + Date.now(),
type: channelType,
header: '',
purpose: '',
},
}).then(({data: channel}) => {
if (userToAdd) {
// # Get user profile by email
return cy.apiGetUserByEmail(userToAdd.email).then(({user}) => {
// # Add user to team
cy.externalRequest({
user: getAdminAccount(),
method: 'post',
path: `channels/${channel.id}/members`,
data: {user_id: user.id},
}).then(() => {
// # Explicitly wait to give some time to index before searching
cy.wait(TIMEOUTS.TWO_SEC);
return cy.wrap(channel);
});
});
}
// # Explicitly wait to give some time to index before searching
cy.wait(TIMEOUTS.TWO_SEC);
return cy.wrap(channel);
});
}
function generatePrefixedUser(user: Omit<SimpleUser, 'password' | 'email'>, prefix: string) {
return {
username: withPrefix(user.username, prefix),
password: 'passwd',
first_name: withPrefix(user.first_name, prefix),
last_name: withPrefix(user.last_name, prefix),
email: createEmail(user.username, prefix),
nickname: withPrefix(user.nickname, prefix),
};
}
function withPrefix(name: string, prefix: string) {
return prefix + name;
}
function createEmail(name: string, prefix: string) {
return `${prefix}${name}@sample.mattermost.com`;
}
export {
createPrivateChannel,
createPublicChannel,
createSearchData,
enableElasticSearch,
getTestUsers,
getPostTextboxInput,
getQuickChannelSwitcherInput,
searchAndVerifyChannel,
searchAndVerifyUser,
searchForChannel,
startAtMention,
verifySuggestionAtChannelSwitcher,
verifySuggestionAtPostTextbox,
};

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

@@ -0,0 +1,59 @@
// 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 @messaging @benchmark
import {reportBenchmarkResults} from '../../../utils/benchmark';
describe('Message', () => {
before(() => {
// # Create new team and new user and visit off-topic channel
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('Current user posting message in empty channel', () => {
// # Wait for posts to load
cy.get('#postListContent', {timeout: 10000}).should('be.visible');
// # Reset selector measurements
cy.window().then((win) => {
win.resetTrackedSelectors();
// # Post message "One"
cy.postMessage('One');
cy.getLastPostId().then((postId) => {
const divPostId = `#postMessageText_${postId}`;
// * Check that the message was created
cy.get(divPostId).find('p').should('have.text', 'One');
reportBenchmarkResults(cy, win);
});
});
});
it('Typing one character into create post textbox', () => {
// # Wait for posts to load
cy.get('#postListContent', {timeout: 10000}).should('be.visible');
// # Reset selector measurements
cy.window().then((win) => {
win.resetTrackedSelectors();
// # Push a character key such as "A"
cy.uiGetPostTextBox().type('A').then(() => {
reportBenchmarkResults(cy, win);
});
});
});
});

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

@@ -0,0 +1,195 @@
// 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 @bot_accounts @mfa
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Bot accounts ownership and API', () => {
let newTeam;
let newUser;
let newChannel;
let botId;
let botName;
beforeEach(() => {
cy.apiAdminLogin();
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
EnforceMultifactorAuthentication: false,
},
};
cy.apiUpdateConfig(newSettings);
cy.apiInitSetup().then(({team, user, channel, townSquareUrl}) => {
newTeam = team;
newUser = user;
newChannel = channel;
cy.visit(townSquareUrl);
cy.postMessage('hello');
});
// # Create a test bot
cy.apiCreateBot().then(({bot}) => {
({user_id: botId, display_name: botName} = bot);
cy.apiPatchUserRoles(bot.user_id, ['system_admin', 'system_user']);
});
});
it('MM-T1862 Only system admin are able to create bots', () => {
// # Open product switch menu and click "Integrations"
cy.uiOpenProductMenu('Integrations');
// * Confirm integrations are visible
cy.url().should('include', `/${newTeam.name}/integrations`);
cy.get('.backstage-header').findByText('Integrations').should('be.visible');
// # Login as a regular user
cy.apiLogin(newUser);
cy.visit(`/${newTeam.name}/channels/town-square`);
// # Click product switch button
cy.uiOpenProductMenu();
// * Confirm "Integrations" is not visible
cy.uiGetProductMenu().should('not.contain', 'Integrations');
});
it('MM-T1863 Only System Admin are able to create bots (API)', () => {
// # Login as a regular user
cy.apiLogin(newUser);
// # Try to create a new bot as a regular user
const botName3 = 'stay-enabled-bot-' + Date.now();
cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/api/v4/bots',
method: 'POST',
failOnStatusCode: false,
body: {
username: botName3,
display_name: 'some text',
description: 'some text',
},
}).then((response) => {
// * Validate that request was denied
expect(response.status).to.equal(403);
});
});
it('MM-T1864 Create bot (API)', () => {
// * This call will fail if bot was not created
cy.apiCreateBot();
});
it('MM-T1865 Create post as bot', () => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
cy.apiCreatePost(newChannel.id, msg1, '', {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token);
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate post was created
cy.findByText(msg1).should('be.visible');
});
});
it('MM-T1866 Create two posts in a row to the same channel', () => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
const msg2 = 'this is a bot message2 ' + botName;
cy.apiCreatePost(newChannel.id, msg1, '', {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token).then(({body: post1}) => {
cy.apiCreatePost(newChannel.id, msg2, '', {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token).then(({body: post2}) => {
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate posts were created
cy.get(`#postMessageText_${post1.id}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
cy.get(`#postMessageText_${post2.id}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg2);
// * Validate first post has an image
cy.get(`#post_${post1.id}`).find('.Avatar').should('be.visible');
// * Validate that the second one doesn't
cy.get(`#post_${post2.id}`).should('have.class', 'same--user');
});
});
});
});
it('MM-T1867 Post as a bot and include an @ mention', () => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
cy.apiCreatePost(newChannel.id, msg1 + ' to @sysadmin', '', {}, token);
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
// * Assert that the last message posted contains highlighted mention
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).find('.mention--highlight').should('be.visible');
});
});
});
it('MM-T1868 BOT has a member role and is not in target channel and team', () => {
// # Create a test bot (member)
cy.apiCreateBot().then(({bot}) => {
// # Create token for the bot
cy.apiCreateToken(bot.user_id).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
// # Try posting
cy.apiCreatePost(newChannel.id, 'this is a bot message ' + bot.username, '', {}, token, false).then((response) => {
// * Validate that posting was not allowed
expect(response.status).to.equal(403);
});
});
});
});
it('MM-T1869 BOT has a system admin role and is not in target channel and team', () => {
const botName3 = 'stay-enabled-bot-' + Date.now();
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
// # Try posting
cy.apiCreatePost(newChannel.id, 'this is a bot message ' + botName3, '', {}, token).then((response) => {
// * Validate that posting was allowed
expect(response.status).to.equal(201);
});
});
});
});

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

@@ -0,0 +1,310 @@
// 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 @bot_accounts @mfa
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Bot accounts ownership and API', () => {
let newTeam;
let newUser;
let newChannel;
let botId;
let botUsername;
let botName;
let adminUser;
beforeEach(() => {
cy.apiAdminLogin().then(({user}) => {
adminUser = user;
});
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
EnforceMultifactorAuthentication: false,
},
};
cy.apiUpdateConfig(newSettings);
cy.apiInitSetup().then(({team, user, channel, townSquareUrl}) => {
newTeam = team;
newUser = user;
newChannel = channel;
cy.visit(townSquareUrl);
cy.postMessage('hello');
});
// # Create a test bot
cy.apiCreateBot().then(({bot}) => {
({user_id: botId, username: botUsername, display_name: botName} = bot);
cy.apiPatchUserRoles(bot.user_id, ['system_admin', 'system_user']);
});
});
it('MM-T1870 BOT has a system admin role and can also post to private channels they do not belong to', () => {
const channelName = 'channel' + Date.now();
// # Create private channel that bot doesn't belong to
cy.apiCreateChannel(newTeam.id, channelName, channelName, 'P').then(({channel}) => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.apiCreatePost(channel.id, msg1 + ' to @sysadmin', '', {}, token);
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
});
});
});
it('MM-T1872 Bot can post to DM channel', () => {
// # Create DM channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
const msg1 = 'this is a bot message ' + botName;
// # Post test message
cy.postBotMessage({message: msg1, token, channelId: channel.id});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
});
});
});
it('MM-T1874 Bots can post when MFA is enforced', () => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
const msg1 = 'this is a bot message ' + botName;
cy.postBotMessage({channelId: newChannel.id, message: msg1, props: {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token});
// # Visit test channel
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate post was created
cy.findByText(msg1).should('be.visible');
const newSettings = {
ServiceSettings: {
EnforceMultifactorAuthentication: true,
},
};
cy.apiUpdateConfig(newSettings);
const msg2 = 'this is a bot message2 ' + botName;
cy.postBotMessage({channelId: newChannel.id, message: msg2, props: {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token});
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate post was created
cy.findByText(msg2).should('be.visible');
});
});
it('MM-T1875 A bot cannot create another bot', () => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
// # Try to create a new bot
cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest', Authorization: `Bearer ${token}`},
url: '/api/v4/bots',
method: 'POST',
failOnStatusCode: false,
body: {
username: botName + '333',
display_name: 'some text',
description: 'some text',
},
}).then((response) => {
// * Validate that request was denied
expect(response.status).to.equal(403);
});
});
});
it('MM-T1877 Reactivate a deactivated bot', () => {
// # Create private channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg1, token});
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
// # Disable the bot
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().parent().findByText('Disable').click();
// # Try to post again
const msg2 = 'this is a bot message2 ' + botName;
// # Logout to allow posting as bot
cy.apiLogout();
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg2, token, failOnStatus: false}).then(({status}) => {
// * Validate that posting failed
expect(status, 403);
});
cy.apiAdminLogin();
// # Enable the bot again
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().parent().findByText('Enable').click();
// # Try to post again
// * Validate that posting works
cy.postBotMessage({channelId: channel.id, message: msg2, token});
// * Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg2);
});
});
});
});
it('MM-T1878 Disable token can not be used to post', () => {
// # Create DM channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token, id}) => {
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg1, token});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
// # Disable the bot token
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
cy.get(`#${id}_deactivate`).click();
});
// # Try to post again
const msg2 = 'this is a bot message2 ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg2, token, failOnStatus: false}).then(({status}) => {
// * Validate that posting failed
expect(status, 403);
});
// # Enable the bot token again
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findAllByText('Bot Accounts');
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
cy.get(`#${id}_activate`).click().wait(TIMEOUTS.ONE_SEC);
// # Try to post again
// * Validate that posting works
cy.postBotMessage({channelId: channel.id, message: msg2, token});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg2);
});
});
});
});
});
it('MM-T1880 Deleted token can not be used to post', () => {
// # Create private channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token, id}) => {
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg1, token});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
// # Disable the bot token
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Delete token
cy.get(`#${id}_delete`).click();
cy.get('#confirmModalButton').click();
// # Try to post again
const msg2 = 'this is a bot message2 ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg2, token, failOnStatus: false}).then(({status}) => {
// * Validate that posting failed
expect(status, 403);
});
});
});
});
});
});

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

@@ -0,0 +1,76 @@
// 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 @not_cloud @bot_accounts
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Bot accounts ownership and API', () => {
let newTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup({
promoteNewUserAsAdmin: true,
loginAfter: true,
}).then(({team}) => {
newTeam = team;
});
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
DisableBotsWhenOwnerIsDeactivated: true,
},
};
cy.apiUpdateConfig(newSettings);
});
it('MM-T1861 Bots do not re-enable if the owner is re-activated', () => {
// # Create another admin account
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
// # Login as the new admin
cy.apiLogin(sysadmin);
// # Create a new bot as the new admin
cy.apiCreateBot({prefix: 'stay-enabled-bot'}).then(({bot}) => {
// # Login again as main admin
cy.apiAdminLogin();
// # Deactivate the newly created admin
cy.apiDeactivateUser(sysadmin.id);
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// * Validate that the plugin is disabled since its owner is deactivated
cy.get('.bot-list__disabled').scrollIntoView().should('be.visible');
// # Re-activate the newly created admin
cy.apiActivateUser(sysadmin.id);
// # Repeat the test to confirm it stays disabled
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// * Validate that the plugin is disabled even though its owner is activated
cy.get('.bot-list__disabled').scrollIntoView().should('be.visible');
});
});
});
});

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

@@ -0,0 +1,54 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
describe('Bot channel intro and avatar', () => {
let team;
let bot;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
});
cy.makeClient().then(async (client) => {
// # Create bot
bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
});
});
it('MM-T1839 Bots have default profile image visible', () => {
// # Open bot DM channel
cy.visit(`/${team.name}/messages/@${bot.username}`);
// # Get channel intro and bot-post Avatars
cy.get(`#channelIntro .profile-icon > img.Avatar, img.Avatar[alt="${bot.username} profile image"]`).
should(($imgs) => {
// * Verify imgs downloaded
expect($imgs[0].naturalWidth).to.be.greaterThan(0);
expect($imgs[1].naturalWidth).to.be.greaterThan(0);
}).
each(($img) => {
// * Verify img visible and has src
cy.wrap($img).
should('be.visible').
and('have.attr', 'src').
then((url) => cy.request({url, encoding: 'binary'})).
then(({body}) => {
// * Verify matches expected default bot avatar
cy.fixture('bot-default-avatar.png', 'binary').should('deep.equal', body);
});
});
});
});

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

@@ -0,0 +1,66 @@
// 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 @bot_accounts
import {getRandomId} from '../../../utils';
describe('Create bot', () => {
it('MM-T1810 Create a Bot via the UI', () => {
cy.apiUpdateConfig({
ServiceSettings: {
EnableUserAccessTokens: true,
},
});
createBot();
});
it('MM-T1811 Create a Bot when personal access tokens are set to False', () => {
cy.apiUpdateConfig({
ServiceSettings: {
EnableUserAccessTokens: false,
},
});
createBot();
});
});
function createBot() {
cy.apiInitSetup().then(({team}) => {
// # Go to town-square channel
cy.visit(`/${team.name}/channels/town-square`);
cy.postMessage('hello');
// # Go to bot integrations page
cy.uiOpenProductMenu('Integrations');
cy.get('a.integration-option[href$="/bots"]').click();
cy.get('#addBotAccount').click();
// # Fill and submit form
cy.get('#username').type(`bot-${getRandomId()}`);
cy.get('#displayName').type('Test Bot');
cy.get('#saveBot').click();
// * Verify confirmation page
cy.url().
should('include', `/${team.name}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
should((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
cy.get('#doneButton').click();
});
}

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

@@ -0,0 +1,78 @@
// 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 @not_cloud @bot_accounts
import {getRandomId} from '../../../utils';
import {createBotInteractive} from './helpers';
describe('Bot accounts - CRUD Testing', () => {
let newTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.shouldHavePluginUploadEnabled();
// # Set ServiceSettings to expected values
const newSettings = {
EmailSettings: {
SMTPServer: '',
},
PluginSettings: {
Enable: true,
},
};
cy.apiUpdateConfig(newSettings);
// # Create a test bot
cy.apiCreateBot();
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
});
});
it('MM-T1849 Create a Personal Access Token when email config is invalid', () => {
// # Create a test bot and validate that token is created
const botUsername = `bot-${getRandomId()}`;
createBotInteractive(newTeam, botUsername);
cy.get('#doneButton').click();
// # Add a new token to the bot
// * Check that the previously created bot is listed
cy.findByText(`Test Bot (@${botUsername})`).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the 'Create token' button
cy.wrap(el[0].parentElement.parentElement).findByText('Create New Token').should('be.visible').click();
// # Add description
cy.wrap(el[0].parentElement.parentElement).find('input').click().type('description!');
// # Save
cy.findByTestId('saveSetting').click();
// # Click Close button
cy.wrap(el[0].parentElement.parentElement).findByText('Close').should('be.visible').click();
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// * Check that token is visible
cy.wrap(el[0].parentElement.parentElement).findAllByText(/Token ID:/).should('have.length', 2);
});
});
});

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

@@ -0,0 +1,198 @@
// 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 @bot_accounts
import {getRandomId} from '../../../utils';
import {createBotInteractive} from './helpers';
describe('Bot accounts - CRUD Testing', () => {
let newTeam;
let testBot;
before(() => {
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
});
cy.apiAdminLogin();
});
beforeEach(() => {
// # Create a test bot
cy.apiCreateBot().then(({bot}) => {
testBot = bot;
});
});
it('MM-T1841 Long description text', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the edit button
cy.wrap(el[0].parentElement.parentElement).findByText('Edit').should('be.visible').click();
// * Validate redirect to edit screen
cy.url().should('include', `/${newTeam.name}/integrations/bots/edit`);
// # type long string
const longDescription = 'A'.repeat(1020); // 1024 is the limit
cy.get('#description').clear().type(longDescription);
// * Validate that it's fully typed
cy.get('#description').should('have.value', longDescription);
// # type some more characters
cy.get('#description').type('{end}12345');
// * Validate that it's partially updated
cy.get('#description').should('have.value', longDescription + '1234');
// # Update the bot
cy.get('#saveBot').click();
// * Validate that bot saved correctly
cy.url().should('include', `/${newTeam.name}/integrations/bots`);
// * Validate that the description exists
cy.findAllByText(longDescription + '1234').should('exist');
});
});
it('MM-T1842 Change BOT role', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the edit button
cy.wrap(el[0].parentElement.parentElement).findByText('Edit').should('be.visible').click();
// * Validate redirect to edit screen
cy.url().should('include', `/${newTeam.name}/integrations/bots/edit`);
// # Select sysadmin
cy.get('select').select('System Admin');
// * Validate that permissions are set and read only
cy.get('#postChannels').should('be.checked').should('be.disabled');
cy.get('#postAll').should('be.checked').should('be.disabled');
// # Update the bot
cy.get('#saveBot').click();
// * Validate that bot saved correctly
cy.url().should('include', `/${newTeam.name}/integrations/bots`);
});
});
it('MM-T1843 ID along with actual token is created', () => {
// # Create the bot and validate token is visible
createBotInteractive(newTeam);
cy.get('#doneButton').click();
});
it('MM-T1844 Token is hidden when you return to the page but ID is still visible', () => {
// # Create the bot and validate token is visible
const botUsername = `bot-${getRandomId()}`;
createBotInteractive(newTeam, botUsername).then((text) => {
// # Close the Add dialog
cy.get('#doneButton').click();
// * Check that the previously created bot is listed
cy.findByText(`Test Bot (@${botUsername})`).then((el) => {
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// * Validate that token is NOT visible on the next page
const token = text.substr(text.indexOf('Token: ') + 7, 26);
cy.findByText(new RegExp(token)).should('not.exist');
});
});
});
it('MM-T1845 Create a new token via the UI', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the 'Create token' button
cy.wrap(el[0].parentElement.parentElement).findByText('Create New Token').should('be.visible').click();
// # Try saving without description
cy.findByTestId('saveSetting').click();
cy.wrap(el[0].parentElement.parentElement).find('input').scrollIntoView();
// * Check for error message
cy.get('#clientError').should('be.visible');
// # Add description
cy.wrap(el[0].parentElement.parentElement).find('input').click().type(testBot.username + 'description!');
// # Save and check that no error is visible
cy.findByTestId('saveSetting').click();
cy.get('#clientError').should('not.exist');
cy.findAllByText(testBot.username + 'description!').should('exist');
});
});
it('MM-T1848 Delete Token', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the 'Create token' button
cy.wrap(el[0].parentElement.parentElement).findByText('Create New Token').should('be.visible').click();
// # Add description
cy.wrap(el[0].parentElement.parentElement).find('input').click().type('description!');
// # Save
cy.findByTestId('saveSetting').click();
// # Click Close button
cy.wrap(el[0].parentElement.parentElement).findByText('Close').should('be.visible').click();
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// * Check that token is visible
cy.wrap(el[0].parentElement.parentElement).findByText(/Token ID:/).should('be.visible');
// # Click Delete button
cy.wrap(el[0].parentElement.parentElement).findByText('Delete').should('be.visible').click();
// * Validate that confirmation dialog is visible and click the delete button
cy.get('#confirmModalButton').should('be.visible').click();
// * Check that token is not visible
cy.wrap(el[0].parentElement.parentElement).findByText(/Token ID:/).should('not.exist');
});
});
});

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

@@ -0,0 +1,98 @@
// 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 @bot_accounts
describe('Bot display name', () => {
let offTopicChannel;
let otherSysadmin;
before(() => {
cy.intercept('**/api/v4/**').as('resources');
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
EnableUserAccessTokens: false,
},
};
cy.apiUpdateConfig(newSettings);
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
otherSysadmin = sysadmin;
cy.apiLogin(otherSysadmin);
cy.apiInitSetup().then(({team}) => {
cy.apiGetChannelByName(team.name, 'off-topic').then(({channel}) => {
offTopicChannel = channel;
});
cy.visit(`/${team.name}/channels/off-topic`);
cy.wait('@resources');
});
});
});
it('MM-T1813 Display name for bots stays current', () => {
cy.makeClient({user: otherSysadmin}).then((client) => {
// # Create a bot and get bot user id
cy.apiCreateBot().then(({bot}) => {
const botUserId = bot.user_id;
const firstMessage = 'This is the first message from a bot that will change its name';
const secondMessage = 'This is the second message from a bot that has changed its name';
// # Get token from bot's id
cy.apiAccessToken(botUserId, 'Create token').then(({token}) => {
//# Add bot to team
cy.apiAddUserToTeam(offTopicChannel.team_id, botUserId);
// # Post message as bot through api with auth token
const props = {attachments: [{pretext: 'Some Pretext', text: 'Some Text'}]};
cy.postBotMessage({token, message: firstMessage, props, channelId: offTopicChannel.id}).
its('id').
should('exist').
as('botPost');
cy.uiWaitUntilMessagePostedIncludes(firstMessage);
// # Go to the channel
cy.get('#sidebarItem_off-topic').click({force: true});
// * Verify bot display name
cy.get('@botPost').then((postIdA) => {
cy.get(`#post_${postIdA} button.user-popover`).click();
cy.get('#user-profile-popover').
should('be.visible');
cy.findByTestId(`popover-fullname-${bot.username}`).
should('have.text', bot.display_name);
}).then(() => {
// # Change display name after prior verification
cy.wrap(client.patchBot(bot.user_id, {display_name: `NEW ${bot.display_name}`})).then((newBot) => {
cy.postBotMessage({token, message: secondMessage, props, channelId: offTopicChannel.id}).
its('id').
should('exist').
as('newBotPost');
cy.uiWaitUntilMessagePostedIncludes(secondMessage);
// * Verify changed display name
cy.get('@newBotPost').then(() => {
cy.get('#user-profile-popover').
should('be.visible');
cy.findByTestId(`popover-fullname-${bot.username}`).
should('have.text', newBot.display_name);
});
});
});
});
});
});
});
});

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

@@ -0,0 +1,93 @@
// 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 @bot_accounts
import * as MESSAGES from '../../../fixtures/messages';
import {getRandomId} from '../../../utils';
describe('Edit bot', () => {
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, townSquareUrl}) => {
testTeam = team;
cy.visit(townSquareUrl);
cy.postMessage('hello');
});
});
it('MM-T1840 Description allows for special character', () => {
const userName = `bot-${getRandomId()}`;
const description = MESSAGES.LARGE.concat('!@#$%&*');
// # Create bot
createBot(userName, testTeam.name);
// * Set alias for bot entry in bot list, this also checks that the bot entry exists
cy.get('.backstage-list__item').contains('.backstage-list__item', userName).as('botEntry');
cy.get('@botEntry').then((el) => {
// # Find the edit link for the bot
const editLink = el.find('.item-actions>a');
if (editLink.text() === 'Edit') {
// # Click the edit link for the bot
cy.wrap(editLink).click();
// * Check that user name is as expected
cy.get('#username').should('have.value', userName);
// * Check that details are empty
cy.get('#displayName').should('have.value', '');
cy.get('#description').should('have.value', '');
// # Set long description
cy.get('#description').clear().type(description);
// # Click update button
cy.get('#saveBot').click();
}
});
// * Get bot entry in bot list by username
cy.get('@botEntry').then((el) => {
cy.wrap(el).scrollIntoView();
// * Confirm long description is as expected
cy.wrap(el.find('.bot-details__description')).should('have.text', description);
});
});
function createBot(userName, teamName) {
// # Go to bot integrations page
cy.uiOpenProductMenu('Integrations');
cy.get('a.integration-option[href$="/bots"]').click();
cy.get('#addBotAccount').click();
// # Fill and submit form
cy.get('#username').type(userName);
cy.get('#saveBot').click();
// * Verify confirmation page
cy.url().
should('include', `/${teamName}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
should((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
cy.get('#doneButton').click();
}
});

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

@@ -0,0 +1,132 @@
// 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 @bot_accounts
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Edit bot username', () => {
let team;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
});
});
it('MM-T2923 Edit bot username.', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Verify that the setting is enabled
cy.findByTestId('ServiceSettings.EnableBotAccountCreationtrue', {timeout: TIMEOUTS.ONE_MIN}).should('be.checked');
// # Visit the integrations
goToCreateBot();
const initialBotName = `bot-${getRandomId()}`;
// # Fill and submit form
cy.get('#username').clear().type(initialBotName);
cy.get('#displayName').clear().type('Test Bot');
cy.get('#saveBot').click();
cy.get('#doneButton').click();
// * Set alias for bot entry in bot list, this also checks that the bot entry exists
cy.get('.backstage-list__item').contains('.backstage-list__item', initialBotName).as('botEntry');
cy.get('@botEntry').then((el) => {
// # Find the edit link for the bot
const editLink = el.find('.item-actions>a');
if (editLink.text() === 'Edit') {
// # Click the edit link for the bot
cy.wrap(editLink).click();
// * Check that user name is as expected
cy.get('#username').should('have.value', initialBotName);
// * Check that the display name is correct
cy.get('#displayName').should('have.value', 'Test Bot');
// * Check that description is empty
cy.get('#description').should('have.value', '');
const newBotName = `bot-${getRandomId()}`;
// * Enter the new user name
cy.get('#username').clear().type(newBotName);
// # Click update button
cy.get('#saveBot').click();
cy.wrap(newBotName);
}
}).then((newBotName) => {
// * Set alias for bot entry in bot list, this also checks that the bot entry exists
cy.get('.backstage-list__item').contains('.backstage-list__item', newBotName).as('newbotEntry');
// * Get bot entry in bot list by username
cy.get('@newbotEntry').then((el) => {
cy.wrap(el).scrollIntoView();
});
});
});
it('MM-T1838 Bot naming convention is enforced', () => {
goToCreateBot();
// # Attempt invalid bot usernames
tryUsername('be', NAMING_WARNING_STANDARD);
tryUsername('@be', NAMING_WARNING_STANDARD);
tryUsername('abe.', NAMING_WARNING_ENDING_PERIOD);
// # Attempt valid bot username
const validBotName = `abe-the-bot-${getRandomId()}`;
tryUsername(validBotName);
});
const NAMING_WARNING_STANDARD = 'Usernames have to begin with a lowercase letter and be 3-22 characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.';
const NAMING_WARNING_ENDING_PERIOD = 'Bot usernames cannot have a period as the last character';
function tryUsername(name, warningMessage) {
cy.get('#username').clear().type(name);
cy.get('#saveBot').click();
if (warningMessage) {
// * Verify expected warning
cy.get('.backstage-form__footer .has-error').should('have.text', warningMessage);
} else {
// * Verify confirmation page
cy.url().
should('include', `/${team.name}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
and('include.text', name).
and((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
// # back to start
goToCreateBot();
}
}
function goToCreateBot() {
cy.visit(`/${team.name}/integrations/bots`);
// * Assert that adding bots possible
cy.get('#addBotAccount', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').click();
}
});

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

@@ -0,0 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {getRandomId} from '../../../utils';
import * as TIMEOUTS from '../../../fixtures/timeouts';
export function createBotInteractive(team, username = `bot-${getRandomId()}`) {
// # Visit the Integrations > Bot Accounts page
cy.visit(`/${team.name}/integrations/bots`);
// # Click add bot
cy.get('#addBotAccount').click();
// # Fill and submit form
cy.get('#username').type(username);
cy.get('#displayName').type('Test Bot');
cy.get('#saveBot').click();
// * Verify confirmation page
cy.url({timeout: TIMEOUTS.ONE_MIN}).
should('include', `/${team.name}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
should((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
return cy.get('div.backstage-form').invoke('text');
}

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

@@ -0,0 +1,71 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
import {generateRandomUser} from '../../../support/api/user';
describe('Bots in lists', () => {
let team;
let channel;
let bots;
let createdUsers;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
});
cy.makeClient().then(async (client) => {
// # Create bots
bots = await Promise.all([
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
]);
// # Create users
createdUsers = await Promise.all([
client.createUser(generateRandomUser()),
client.createUser(generateRandomUser()),
]);
await Promise.all([
...bots,
...createdUsers,
].map(async (user) => {
// * Verify username exists
cy.wrap(user).its('username');
// # Add to team and channel
await client.addToTeam(team.id, user.user_id ?? user.id);
await client.addToChannel(user.user_id ?? user.id, channel.id);
}));
});
});
it('MM-T1834 Bots are not listed on “Users” list in System Console > Users', () => {
// # Go to system console > users
cy.visit('/admin_console/user_management/users');
bots.forEach(({username}) => {
// # Search for bot
cy.get('#searchUsers').clear().type(`@${username}`);
// * Verify bot not in list
cy.findByTestId('noUsersFound').should('have.text', 'No users found');
// * Verify pseudo checksum total of non bot users
cy.get('#searchableUserListTotal').contains('0 users of').should('be.visible');
});
});
});

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

@@ -0,0 +1,105 @@
// 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.
// ***************************************************************
// Group: @channels @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
import {generateRandomUser} from '../../../support/api/user';
describe('Bots in lists', () => {
let team;
let channel;
let testUser;
const STATUS_PRIORITY = {
online: 0,
away: 1,
dnd: 2,
offline: 3,
ooo: 3,
};
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
testUser = out.user;
});
cy.makeClient().then(async (client) => {
// # Create bots
const bots = await Promise.all([
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
]);
// # Create users
const createdUsers = await Promise.all([
client.createUser(generateRandomUser()),
client.createUser(generateRandomUser()),
]);
await Promise.all([
...bots,
...createdUsers,
].map(async (user) => {
// * Verify username exists
cy.wrap(user).its('username');
// # Add to team and channel
await client.addToTeam(team.id, user.user_id ?? user.id);
await client.addToChannel(user.user_id ?? user.id, channel.id);
}));
});
});
it('MM-T1835 Channel Members list for BOTs', () => {
cy.makeClient({user: testUser}).then((client) => {
// # Login as regular user and visit a channel
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Open channel members
cy.get('.channel-header__trigger').click();
cy.findByText('Manage Members').click();
cy.get('.more-modal__row .more-modal__name').then(async ($query) => {
// # Extract usernames from jQuery collection
const usernames = $query.toArray().map(({innerText}) => innerText.split('\n')[0]);
// # Get users
const profiles = await client.getProfilesByUsernames(usernames);
const statuses = await client.getStatusesByIds(profiles.map((user) => user.id));
const users = Cypress._.zip(profiles, statuses).map(([profile, status]) => ({...profile, ...status}));
// # Sort 'em
const sortedUsers = Cypress._.sortBy(users, [
({is_bot: isBot}) => (isBot ? 1 : 0), // users first
({status}) => STATUS_PRIORITY[status],
({username}) => username,
]);
// * Verify order of member-dropdown users against API-sourced/data-sorted version
cy.wrap(usernames).should('deep.equal', sortedUsers.map(({username}) => username));
});
// * Verify no statuses on bots
cy.get('.more-modal__row--bot .status-wrapper .status').should('not.exist');
// * Verify bot badges
cy.get('.more-modal__row--bot .Tag').then(($tags) => {
$tags.toArray().forEach((tagEl) => {
cy.wrap(tagEl).then(() => tagEl.scrollIntoView());
cy.wrap(tagEl).should('be.visible').and('have.text', 'BOT');
});
});
});
});
});

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

@@ -0,0 +1,96 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
import {createChannelPatch} from '../../../support/api/channel';
describe('Managing bots in Teams and Channels', () => {
let team;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'sample.mattermost.com',
},
});
cy.apiInitSetup({loginAfter: true}).then((out) => {
team = out.team;
});
});
it('MM-T1815 Add a BOT to a team that has email restricted', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.getChannelByName(team.id, 'town-square');
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Invite bot to team
const bot = await client.createBot(createBotPatch());
cy.uiInviteMemberToCurrentTeam(bot.username);
// * Verify system message in-channel
cy.uiWaitUntilMessagePostedIncludes(`@${bot.username} added to the team by you.`);
});
});
it('MM-T1816 Add a BOT to a channel', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.createChannel(createChannelPatch(team.id, 'a-chan', 'A Channel'));
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Add bot to team
const bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
// # Add bot to channel in team
cy.uiAddUsersToCurrentChannel([bot.username]);
// * Verify system message in-channel
cy.uiWaitUntilMessagePostedIncludes(`@${bot.username} added to the channel by you.`);
});
});
it('MM-T1817 Add a BOT to a channel that is not on the Team', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.createChannel(createChannelPatch(team.id, 'a-chan', 'A Channel'));
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Invite bot to team
const bot = await client.createBot(createBotPatch());
cy.postMessage(`/invite @${bot.username} `);
// * Verify system message in-channel
cy.uiWaitUntilMessagePostedIncludes(`@${bot.username} is not a member of the team.`);
});
});
it('MM-T1818 No ephemeral post about Adding a bot to a channel When Bot is mentioned', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.createChannel(createChannelPatch(team.id, 'a-chan', 'A Channel'));
cy.visit(`/${team.name}/channels/${channel.name}`);
// # And bot to team
const bot = await client.createBot(createBotPatch());
cy.apiAddUserToTeam(team.id, bot.user_id);
// # Mention bot
const message = `hey @${bot.username}, tell me a rhyme..`;
cy.postMessage(message);
// * Verify no ephemeral post is shown asking if you want to invite the bot to the server
cy.uiGetNthPost(-1).should('contain.text', message);
});
});
});

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

@@ -0,0 +1,101 @@
// 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 @bot_accounts @plugin @not_cloud
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {matterpollPlugin} from '../../../utils/plugins';
describe('Managing bot accounts', () => {
let newTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.shouldHavePluginUploadEnabled();
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
it('MM-T1859 Bot is kept active when owner is disabled', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.DisableBotsWhenOwnerIsDeactivatedfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click();
// # Create another admin account
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
// # Login as the new admin
cy.apiLogin(sysadmin);
// # Create a new bot as the new admin
cy.apiCreateBot({prefix: 'stay-enabled-bot'}).then(({bot}) => {
// # Login again as main admin
cy.apiAdminLogin();
// # Deactivate the newly created admin
cy.apiDeactivateUser(sysadmin.id);
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.display_name);
// * Validate that the plugin is still active, even though its owner is disabled
cy.get('.bot-list__disabled').should('not.exist');
cy.findByText(bot.fullDisplayName).scrollIntoView().should('be.visible');
cy.visit(`/${newTeam.name}/messages/@sysadmin`);
// # Get last post message text
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('postMessageText');
});
// * Verify entire message
cy.get('@postMessageText').
should('be.visible').
and('contain.text', `${sysadmin.username} was deactivated. They managed the following bot accounts`).
and('contain.text', bot.username);
});
});
});
it('MM-T1853 Bots managed plugins can be created when Enable Bot Account Creation is set to false', () => {
// # Upload and enable "matterpoll" plugin
cy.apiUploadAndEnablePlugin(matterpollPlugin);
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.EnableBotAccountCreationfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click();
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Validate that plugin installed ok
cy.contains('Matterpoll (@matterpoll)', {timeout: TIMEOUTS.ONE_MIN});
});
});

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

@@ -0,0 +1,206 @@
// 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 @bot_accounts
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Managing bot accounts', () => {
let newTeam;
before(() => {
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
});
});
beforeEach(() => {
cy.apiAdminLogin();
const newSettings = {
ServiceSettings: {
EnableBotAccountCreation: true,
},
};
cy.apiUpdateConfig(newSettings);
});
it('MM-T1851 No option to create BOT accounts when Enable Bot Account Creation is set to False.', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.EnableBotAccountCreationfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click();
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Assert that adding bots is not possible
cy.get('#addBotAccount', {timeout: TIMEOUTS.ONE_MIN}).should('not.exist');
});
it('MM-T1852 Bot creation via API is not permitted when Enable Bot Account Creation is set to False', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.EnableBotAccountCreationfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click().wait(TIMEOUTS.HALF_SEC);
// * Validate that creating bot fails
cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/api/v4/bots',
method: 'POST',
failOnStatusCode: false,
body: {
username: `bot-${getRandomId()}`,
display_name: 'test bot',
description: 'test bot',
},
}).then((response) => {
expect(response.status).to.equal(403);
expect(response.body.message).to.equal('Bot creation has been disabled.');
return cy.wrap(response);
});
});
it('MM-T1854 Bots can be create when Enable Bot Account Creation is set to True.', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// * Check that creation is enabled
cy.findByTestId('ServiceSettings.EnableBotAccountCreationtrue', {timeout: TIMEOUTS.ONE_MIN}).should('be.checked');
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Assert that adding bots is possible
cy.get('#addBotAccount', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
it('MM-T1856 Disable Bot', () => {
cy.apiCreateBot({prefix: 'test-bot'}).then(({bot}) => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Filter bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// * Check that the previously created bot is listed
cy.findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).scrollIntoView().then((el) => {
// # Click the disable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(3)').should('be.visible').click();
});
// * Check that the bot is in the 'disabled' section
cy.get('.bot-list__disabled').scrollIntoView().findByText(bot.fullDisplayName).should('be.visible');
});
});
it('MM-T1857 Enable Bot', () => {
cy.apiCreateBot({prefix: 'test-bot'}).then(({bot}) => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).scrollIntoView().then((el) => {
// # Click the disable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(3)').should('be.visible').click();
});
// # Filter bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// # Re-enable the bot
cy.get('.bot-list__disabled').scrollIntoView().findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).scrollIntoView().then((el) => {
// # Click the enable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(1)').should('be.visible').click();
});
// * Check that the bot is in the 'enabled' section
cy.findByText(bot.fullDisplayName).scrollIntoView().should('be.visible');
cy.get('.bot-list__disabled').should('not.exist');
});
});
it('MM-T1858 Search active and disabled Bot accounts', () => {
cy.apiCreateBot({prefix: 'hello-bot'}).then(({bot}) => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the disable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(3)').should('be.visible').click();
});
// * Validate that disabled section appears
cy.get('.bot-list__disabled').scrollIntoView().should('be.visible');
// # Search for the other bot
cy.apiCreateBot({prefix: 'other-bot'}).then(({bot: otherBot}) => {
cy.get('#searchInput').type(otherBot.username);
// * Validate that disabled section disappears
cy.get('.bot-list__disabled').should('not.exist');
});
});
});
it('MM-T1860 Bot is disabled when owner is deactivated', () => {
// # Create another admin account
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
// # Login as the new admin
cy.apiLogin(sysadmin);
// # Create a new bot as the new admin
cy.apiCreateBot({prefix: 'stay-enabled-bot'}).then(({bot}) => {
// # Login again as main admin
cy.apiAdminLogin();
// # Deactivate the newly created admin
cy.apiDeactivateUser(sysadmin.id);
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.display_name);
// * Validate that the plugin is disabled since it's owner is deactivate
cy.get('.bot-list__disabled').scrollIntoView().findByText(bot.fullDisplayName).scrollIntoView().should('be.visible');
cy.visit(`/${newTeam.name}/messages/@sysadmin`);
// # Get last post message text
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('postMessageText');
});
// * Verify entire message
cy.get('@postMessageText').
should('be.visible').
and('contain.text', `${sysadmin.username} was deactivated. They managed the following bot accounts`).
and('contain.text', bot.username);
});
});
});
});

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

@@ -0,0 +1,53 @@
// 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 @bot_accounts
describe('Bot post message', () => {
let offTopicChannel;
before(() => {
cy.apiInitSetup().then(({team}) => {
cy.apiGetChannelByName(team.name, 'off-topic').then(({channel}) => {
offTopicChannel = channel;
});
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T1812 Post as a bot when personal access tokens are false', () => {
// # Create a bot and get bot user id
cy.apiCreateBot().then(({bot}) => {
const botUserId = bot.user_id;
const message = 'This is a message from a bot.';
// # Get token from bot's id
cy.apiAccessToken(botUserId, 'Create token').then(({token}) => {
//# Add bot to team
cy.apiAddUserToTeam(offTopicChannel.team_id, botUserId);
// # Post message as bot through api with auth token
const props = {attachments: [{pretext: 'Some Pretext', text: 'Some Text'}]};
cy.postBotMessage({token, message, props, channelId: offTopicChannel.id}).
its('id').
should('exist').
as('botPost');
// * Verify bot message
cy.uiWaitUntilMessagePostedIncludes(message);
cy.get('@botPost').then((postId) => {
cy.get(`#postMessageText_${postId}`).
should('be.visible').
and('have.text', message);
});
});
});
});
});

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

@@ -0,0 +1,58 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
describe('Managing bots in Teams and Channels', () => {
let team;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'sample.mattermost.com',
},
});
cy.apiInitSetup({loginAfter: true}).then((out) => {
team = out.team;
});
});
it('MM-T1819 Promote a BOT to team admin', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.getChannelByName(team.id, 'off-topic');
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Add bot to team
const bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
// # Open team menu and click 'Manage Members'
cy.uiOpenTeamMenu('Manage Members');
// # Find bot
cy.get('.more-modal__list').find('.more-modal__row').its('length').should('be.gt', 0);
cy.get('#searchUsersInput').type(bot.username);
// # Wait for loading screen
cy.get('#teamMembersModal .loading-screen').should('be.visible');
// # Find bot member dropdown
cy.get(`#teamMembersDropdown_${bot.username}`).as('memberDropdown').should('contain.text', 'Member').click();
// # Promote bot to team admin
cy.findByTestId('userListItemActions').find('button').contains('Make Team Admin').click();
// * Verify bot was promoted
cy.get('@memberDropdown').should('contain.text', 'Team Admin');
});
});
});

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

@@ -0,0 +1,94 @@
// 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 @bot_accounts @not_cloud
import {createBotPatch} from '../../../support/api/bots';
import {generateRandomUser} from '../../../support/api/user';
describe('Bot accounts', () => {
let team;
let channel;
let testUser;
let bots;
let createdUsers;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
testUser = out.user;
});
cy.makeClient().then(async (client) => {
// # Create bots
bots = await Promise.all([
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
]);
// # Create users
createdUsers = await Promise.all([
client.createUser(generateRandomUser()),
client.createUser(generateRandomUser()),
]);
await Promise.all([
...bots,
...createdUsers,
].map(async (user) => {
// * Verify username exists
cy.wrap(user).its('username');
// # Add to team and channel
await client.addToTeam(team.id, user.user_id ?? user.id);
await client.addToChannel(user.user_id ?? user.id, channel.id);
}));
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
it('MM-T1836 Bot accounts display', () => {
// # Login as regular user and visit a channel
cy.apiLogin(testUser);
cy.visit(`/${team.name}/messages/@${bots[0].username}`);
cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) .SidebarChannel.active > .SidebarLink').then(($link) => {
// * Verify DM label
cy.wrap($link).find('.SidebarChannelLinkLabel').should('have.text', bots[0].username);
// * Verify bot icon exists
cy.wrap($link).find('.Avatar').should('exist').
and('have.attr', 'src').
then((url) => cy.request({url, encoding: 'binary'})).
then(({body}) => {
// * Verify it matches default bot avatar
cy.fixture('bot-default-avatar.png', 'binary').should('deep.equal', body);
});
});
cy.postMessage('Bump bot chat recency');
// # Open a new DM
cy.visit(`/${team.name}/messages/@${createdUsers[0].username}`);
cy.postMessage('Hello, regular user');
// * Verify Bots and Regular users as siblings in DMs
cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) .SidebarChannel.active').siblings('.SidebarChannel').then(($siblings) => {
cy.wrap($siblings).contains('.SidebarChannelLinkLabel', bots[0].username);
});
});
});

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

@@ -0,0 +1,96 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
describe('Bot tags', () => {
let me;
let team;
let channel;
let postId;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
});
let meId;
cy.getCurrentUserId().then((id) => {
meId = id;
});
cy.makeClient().then(async (client) => {
// # Setup state
me = await client.getUser(meId);
const bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
await client.addToChannel(bot.user_id, channel.id);
const {token} = await client.createUserAccessToken(bot.user_id, 'Create token');
const message = `Message for @${me.username}. Signed, @${bot.username}.`;
// # Post message as bot through api with auth token
const props = {attachments: [{pretext: 'Some Pretext', text: 'Some Text'}]};
cy.postBotMessage({token, message, props, channelId: channel.id}).then(async ({id}) => {
postId = id;
await client.pinPost(postId);
cy.visit(`/${team.name}/channels/${channel.name}`);
cy.clickPostDotMenu(postId);
cy.get(`#CENTER_flagIcon_${postId}`).click();
});
});
});
it('MM-T1831 BOT tag is visible in search results', () => {
// # Open search
cy.uiSearchPosts(`Message for @${me.username}`);
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Search Results');
rhsPostHasBotBadge(postId);
});
it('MM-T1832 BOT tag is visible in Recent Mentions', () => {
// # Open mentions
cy.uiGetRecentMentionButton().click();
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Recent Mentions');
rhsPostHasBotBadge(postId);
});
it('MM-T1833 BOT tag is visible in Pinned Posts', () => {
// # Open pinned posts
cy.uiGetChannelPinButton().click();
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Pinned Posts');
rhsPostHasBotBadge(postId);
});
it('MM-T3659 BOT tag is visible in Saved Posts', () => {
// # Open saved posts
cy.uiGetSavedPostButton().click();
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Saved Posts');
rhsPostHasBotBadge(postId);
});
});
function rhsPostHasBotBadge(postId) {
cy.get(`.post#searchResult_${postId} .Tag`).should('be.visible').and('have.text', 'BOT');
}

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

@@ -0,0 +1,328 @@
// 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 @channel
import {getRandomId} from '../../../utils';
describe('Leave an archived channel', () => {
let testTeam;
let offTopicUrl;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team, offTopicUrl: url}) => {
testTeam = team;
offTopicUrl = url;
cy.visit(offTopicUrl);
// # Post a message to the channel
cy.postMessage('hello');
});
});
it('MM-T1680 Open archived channel from search results with permalink view in another channel is open', () => {
// # Visit the test team
cy.visit(`/${testTeam.name}`);
// # Create a new channel
cy.uiCreateChannel({isNewSidebar: true});
// # Make a post
const archivedPostText = `archived${getRandomId()}`;
cy.postMessage(archivedPostText);
cy.getLastPostId().as('archivedPostId');
// # Archive the newly created channel
cy.uiArchiveChannel();
// # Switch away from the archived channel
cy.visit(offTopicUrl);
// # Make a post outside of the archived channel
const otherPostText = `post${getRandomId()}`;
cy.postMessage(otherPostText);
cy.getLastPostId().as('otherPostId');
// # Search for the new post and jump to it from the search results
cy.uiSearchPosts(otherPostText);
cy.get('@otherPostId').then((otherPostId) => cy.uiJumpToSearchResult(otherPostId));
// # Search for a post in the archived channel
cy.uiSearchPosts(archivedPostText);
// # Open it in the RHS
cy.get('@archivedPostId').then((archivedPostId) => {
cy.clickPostCommentIcon(archivedPostId, 'SEARCH');
// * Verify that the RHS has switched from search results to the thread
cy.get('#searchContainer').should('not.exist');
cy.get('#rhsContainer').should('be.visible');
// * Verify that the thread is visible and marked as archived
cy.get(`#rhsPost_${archivedPostId}`).should('be.visible');
cy.get('#rhsContainer .channel-archived-warning__container').should('be.visible');
cy.get('#rhsContainer .channel-archived-warning__content').should('be.visible');
});
});
it('MM-T1697 - Browse Public channels shows archived channels option', () => {
// # Create public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Click on add channel button
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
// # Click on browse channels
cy.get('#showMoreChannels').click();
// # More channels modal opens
cy.get('#moreChannelsModal').should('be.visible').within(() => {
// # Click on dropdown
cy.findByText('Show: Public Channels').should('be.visible').click();
// # Click archived channels
cy.findByText('Archived Channels').click();
// # Modal should contain created channel
cy.get('#moreChannelsList').should('contain', channel.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
});
it('MM-T1699 - Browse Channels for all channel types shows archived channels option', () => {
let archivedPrivateChannel;
let archivedPublicChannel;
// # Create private channel
cy.uiCreateChannel({isPrivate: true, isNewSidebar: true}).as('channel').then((channel) => {
archivedPrivateChannel = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPrivateChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Create public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedPublicChannel = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPublicChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Click on add channel button
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
// # Click on browse channels
cy.get('#showMoreChannels').click();
// # More channels modal opens
cy.get('.more-modal').should('be.visible').within(() => {
// # Public channel list opens by default
cy.findByText('Show: Public Channels').should('be.visible').click();
// # Click on archived channels
cy.findByText('Archived Channels').click();
// # Channel list should contain newly created channels
cy.get('#moreChannelsList').should('contain', archivedPrivateChannel.name);
cy.get('#moreChannelsList').should('contain', archivedPublicChannel.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1700 - All archived public channels are shown Important', () => {
let archivedPublicChannel1;
let archivedPublicChannel2;
// # Create public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedPublicChannel1 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPublicChannel1.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Create second public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedPublicChannel2 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPublicChannel2.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave channel
cy.uiLeaveChannel();
// # User should be redirected to last viewed channel
cy.url().should('include', `/${testTeam.name}/channels/${archivedPublicChannel1.name}`);
});
// # Click on add channel button
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
// # Click on browse channels
cy.get('#showMoreChannels').click();
// # More channels modal opens
cy.get('.more-modal').should('be.visible').within(() => {
// # Public channels are shown by default
cy.findByText('Show: Public Channels').should('be.visible').click();
// # Go to archived channels
cy.findByText('Archived Channels').click();
// # Channel list should contain both archived public channels
cy.get('#moreChannelsList').should('contain', archivedPublicChannel1.display_name);
cy.get('#moreChannelsList').should('contain', archivedPublicChannel2.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1701 - Only Private channels you are a member of are displayed', () => {
let archivedPrivateChannel1;
let archivedPrivateChannel2;
// # Create private channel
cy.uiCreateChannel({isPrivate: true, isNewSidebar: true}).as('channel').then((channel) => {
archivedPrivateChannel1 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPrivateChannel1.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Create another private channel
cy.uiCreateChannel({isPrivate: true, isNewSidebar: true}).as('channel').then((channel) => {
archivedPrivateChannel2 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPrivateChannel2.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave the channel
cy.uiLeaveChannel();
});
// # Leave channel modal is visible
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModalButton').click();
// # Click on add channel button
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
// # Click on browse channels
cy.get('#showMoreChannels').click();
// # More channels modal opens
cy.get('.more-modal').should('be.visible').within(() => {
// # Show public channels is visible by default
cy.findByText('Show: Public Channels').should('be.visible').click();
// # Go to archived channels
cy.findByText('Archived Channels').click();
// # Channel list should contain only the private channel user is a member of
cy.get('#moreChannelsList').should('contain', archivedPrivateChannel1.name);
cy.get('#moreChannelsList').should('not.contain', archivedPrivateChannel2.name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1703 - User can open archived channels', () => {
let archivedChannel;
// # Create a public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedChannel = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Click on add channel button
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
// # Click on browse channels
cy.get('#showMoreChannels').click();
// # More channels modal opens and lands on public channels
cy.get('#moreChannelsModal').should('be.visible').within(() => {
cy.findByText('Show: Public Channels').should('be.visible').click();
// # Go to archived channels
cy.findByText('Archived Channels').click();
// # More channels list should contain the archived channel
cy.get('#moreChannelsList').should('contain', archivedChannel.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1696 - When clicking Browse Channels no options for archived channels are shown when the feature is disabled', () => {
cy.apiAdminLogin();
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: false,
},
});
// # Create public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Click on add channel button
cy.get('#SidebarContainer .AddChannelDropdown_dropdownButton').click();
// # Click on browse channels
cy.get('#showMoreChannels').click();
// # Modal should not contain the created channel
cy.get('#channelsMoreDropdown').should('not.exist');
cy.get('#moreChannelsList').should('not.contain', channel.name);
});
cy.get('body').typeWithForce('{esc}');
});
});

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

@@ -0,0 +1,110 @@
// 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 @channel
import {getAdminAccount} from '../../../support/env';
import {getRandomId} from '../../../utils';
describe('Leave an archived channel', () => {
let testTeam;
let testUser;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
beforeEach(() => {
// # Login as test user and visit off-topic
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
it('MM-T1687 App does not crash when another user archives a channel', () => {
cy.makeClient({user: getAdminAccount()}).then((client) => {
// # Have another user create a private channel
const channelName = `channel${getRandomId()}`;
cy.wrap(client.createChannel({
display_name: channelName,
name: channelName,
team_id: testTeam.id,
type: 'P',
})).then(async (channel) => {
// # Then invite us to it
await client.addToChannel(testUser.id, channel.id);
cy.wrap(channel);
}).then((channel) => {
// * Verify that the newly created channel is in the sidebar
cy.get(`#sidebarItem_${channel.name}`).should('be.visible');
cy.wrap(channel);
}).then(async (channel) => {
// # Then archive the channel
await client.deleteChannel(channel.id);
// * Verify that the channel is no longer in the sidebar and that the app hasn't crashed
cy.get(`#sidebarItem_${channel.name}`).should('not.exist');
});
});
});
it('MM-T1688 archived channels only appear in search results as long as the user does not leave them', () => {
// # Create a new channel
cy.uiCreateChannel({isNewSidebar: true}).then(({name: channelName}) => {
// # Make a post
const archivedPostText = `archived${getRandomId()}`;
cy.postMessage(archivedPostText);
cy.getLastPostId().then((archivedPostId) => {
// # Archive the newly created channel
cy.uiArchiveChannel();
// # Switch away from the archived channel
cy.get('#sidebarItem_off-topic').click();
// * Verify that the channel is no longer in the sidebar
cy.get(`#sidebarItem_${channelName}`).should('not.exist');
// # Search for the post
cy.uiSearchPosts(archivedPostText);
// * Verify that the post is shown in the search results
cy.get(`#searchResult_${archivedPostId}`).should('be.visible');
// # Switch back to the archived channel through the permalink
cy.uiJumpToSearchResult(archivedPostId);
// * Wait for the permalink URL to disappear
cy.url().should('include', `/${testTeam.name}/channels/${channelName}`);
// # Leave the channel
cy.uiLeaveChannel();
cy.url().should('include', `/${testTeam.name}/channels/off-topic`);
cy.postMessage('hello');
// # Search for the post again
cy.uiSearchPosts(archivedPostText);
// * Verify that the post is no longer shown in the search results
cy.get(`#searchResult_${archivedPostId}`).should('not.exist');
cy.get('#search-items-container .no-results__wrapper').should('be.visible');
});
});
});
});

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

@@ -0,0 +1,465 @@
// 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 @channel @rhs @channel_info
import {stubClipboard} from '../../../utils';
describe('Channel Info RHS', () => {
let testTeam;
let testChannel;
let groupChannel;
let directChannel;
let directUser;
let admin;
let user;
const otherUsers = [];
before(() => {
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, user: newAdmin}) => {
testTeam = team;
admin = newAdmin;
cy.apiCreateChannel(testTeam.id, 'channel', 'Public Channel', 'O').then(({channel}) => {
testChannel = channel;
cy.apiAddUserToChannel(channel.id, newAdmin.id);
});
// User we'll use for our permissions tests
cy.apiCreateUser().then(({user: newUser}) => {
cy.apiAddUserToTeam(team.id, newUser.id);
user = newUser;
});
// Users used for GM/DM
cy.apiCreateUser().then(({user: newUser}) => {
otherUsers.push(newUser);
cy.apiPatchUser(newUser.id, {position: 'Upside down'}).then(({user: patchedUser}) => {
cy.apiCreateDirectChannel([newAdmin.id, newUser.id]).then(({channel}) => {
directChannel = channel;
});
directUser = patchedUser;
});
cy.apiAddUserToTeam(team.id, newUser.id);
cy.apiCreateUser().then(({user: newUser2}) => {
otherUsers.push(newUser2);
cy.apiAddUserToTeam(team.id, newUser.id);
cy.apiCreateGroupChannel([newAdmin.id, newUser.id, newUser2.id]).then(({channel}) => {
groupChannel = channel;
});
});
});
// # Change permission so that regular users can't change channels or add members
cy.apiGetRolesByNames(['channel_user']).then(({roles}) => {
const role = roles[0];
const permissions = role.permissions.filter((permission) => {
return !(['manage_public_channel_members', 'manage_private_channel_members', 'manage_public_channel_properties', 'manage_private_channel_properties'].includes(permission));
});
if (permissions.length !== role.permissions) {
cy.apiPatchRole(role.id, {permissions});
}
});
cy.apiLogin(admin);
});
});
it('should be able to open the RHS', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * RHS Container shoud exist
ensureRHSIsOpenOnChannelInfo(testChannel);
});
describe('regular channel', () => {
describe('top buttons', () => {
it('should be able to toggle favorite on a channel', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the favorite status
cy.uiGetRHS().findByText('Favorite').should('be.visible').click();
cy.uiGetRHS().findByText('Favorited').should('be.visible').click();
cy.uiGetRHS().findByText('Favorite').should('be.visible');
});
it('should be able to toggle mute on a channel', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the mute status
cy.uiGetRHS().findByText('Mute').should('be.visible').click();
cy.uiGetRHS().findByText('Muted').should('be.visible').click();
cy.uiGetRHS().findByText('Mute').should('be.visible');
});
it('should be able to add people', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('be.visible').click();
cy.get('.channel-invite').should('be.visible');
});
it('should NOT be able to add people without permission', () => {
// # Login as simple user
cy.apiLogout().then(() => {
cy.apiLogin(user);
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('not.exist');
// # log back in as admin
cy.apiLogout().then(() => {
cy.apiLogin(admin);
});
});
});
it('should be able to copy link', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
stubClipboard().as('clipboard');
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify initial state
cy.get('@clipboard').its('contents').should('eq', '');
// # Click on "Copy Link"
cy.uiGetRHS().findByText('Copy Link').parent().should('be.visible').trigger('click');
// * Text should change to Copied
cy.uiGetRHS().findByText('Copied').should('be.visible');
// * Verify if it's called with correct link value
cy.get('@clipboard').its('contents').should('eq', `${Cypress.config('baseUrl')}/${testTeam.name}/channels/${testChannel.name}`);
});
});
describe('about area', () => {
it('should display purpose', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(testChannel.id, {
...testChannel,
purpose: 'purpose for the tests',
}).then(() => {
cy.uiGetRHS().findByText('purpose for the tests').should('be.visible');
});
});
it('should display header', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(testChannel.id, {
...testChannel,
header: 'header for the tests',
}).then(() => {
cy.uiGetRHS().findByText('header for the tests').should('be.visible');
});
});
});
describe('bottom menu', () => {
it('should be able to manage notifications', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Notification Preferences"
cy.uiGetRHS().findByText('Notification Preferences').should('be.visible').click();
// * Ensures the modal is there
cy.get('.settings-modal').should('be.visible');
});
it('should be able to view files and come back', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Files"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Files').should('be.visible').click();
// * Ensure we see the files RHS
cy.uiGetRHS().findByText('No files yet').should('be.visible');
// # Click the Back Icon
cy.uiGetRHS().get('[aria-label="Back Icon"]').click();
// * Make sure we are back in the channel info rhs
ensureRHSIsOpenOnChannelInfo(testChannel);
});
it('should be able to view pinned message and come back', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Go to test channel
cy.uiPostMessageQuickly('Hello channel info rhs spec').then(() => {
cy.getNthPostId(-1).then((postId) => {
cy.clickPostDotMenu(postId);
cy.get(`#pin_post_${postId}`).click();
});
});
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Pinned Messages"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Pinned Messages').should('be.visible').click();
// * Ensure we see the Pinned Post RHS
cy.uiGetRHS().findByText('Hello channel info rhs spec').should('be.visible');
// # Click the Back Icon
cy.uiGetRHS().get('[aria-label="Back Icon"]').click();
// * Make sure we are back in the channel info rhs
ensureRHSIsOpenOnChannelInfo(testChannel);
});
it('should be able to view channel members and come back', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Members"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Members').should('be.visible').click();
// * Ensure we see the members
cy.uiGetRHS().contains('sysadmin').should('be.visible');
cy.uiGetRHS().contains(`${admin.username}`).should('be.visible');
// # Click the Back Icon
cy.uiGetRHS().get('[aria-label="Back Icon"]').click();
// * Make sure we are back in the channel info rhs
ensureRHSIsOpenOnChannelInfo(testChannel);
});
});
});
describe('group channel', () => {
describe('top buttons', () => {
it('should be able to toggle favorite', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the favorite status
cy.uiGetRHS().findByText('Favorite').should('be.visible').click();
cy.uiGetRHS().findByText('Favorited').should('be.visible').click();
cy.uiGetRHS().findByText('Favorite').should('be.visible');
});
it('should be able to toggle mute', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the mute status
cy.uiGetRHS().findByText('Mute').should('be.visible').click();
cy.uiGetRHS().findByText('Muted').should('be.visible').click();
cy.uiGetRHS().findByText('Mute').should('be.visible');
});
it('should be able to add people', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('be.visible').click();
cy.get('.more-direct-channels').should('be.visible');
});
it('should NOT be able to copy link', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Copy Link"
cy.uiGetRHS().get('Copy Link').should('not.exist');
});
});
describe('about area', () => {
it('should display other users', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
otherUsers.forEach((otherUser) => {
cy.uiGetRHS().contains(otherUser.username);
});
});
it('should display header', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(groupChannel.id, {
...groupChannel,
header: 'header for the tests',
}).then(() => {
cy.uiGetRHS().findByText('header for the tests').should('be.visible');
});
});
});
describe('bottom menu', () => {
it('should be able to manage notifications', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Notification Preferences"
cy.uiGetRHS().findByText('Notification Preferences').should('be.visible').click();
// * Ensures the modal is there
cy.get('.settings-modal').should('be.visible');
});
});
});
describe('direct channel', () => {
describe('top buttons', () => {
it('should be able to toggle favorite', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the favorite status
cy.uiGetRHS().findByText('Favorite').should('be.visible').click();
cy.uiGetRHS().findByText('Favorited').should('be.visible').click();
cy.uiGetRHS().findByText('Favorite').should('be.visible');
});
it('should be able to toggle mute', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the mute status
cy.uiGetRHS().findByText('Mute').should('be.visible').click();
cy.uiGetRHS().findByText('Muted').should('be.visible').click();
cy.uiGetRHS().findByText('Mute').should('be.visible');
});
it('should NOT be able to add people', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('not.exist');
});
it('should NOT be able to copy link', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Copy Link"
cy.uiGetRHS().get('Copy Link').should('not.exist');
});
});
describe('about area', () => {
it('should display other user name and position', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.uiGetRHS().contains(directUser.username);
cy.uiGetRHS().contains(directUser.position);
});
it('should display header', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(directChannel.id, {
...directChannel,
header: 'header for the tests',
}).then(() => {
cy.uiGetRHS().findByText('header for the tests').should('be.visible');
});
});
});
});
});
function ensureRHSIsOpenOnChannelInfo(testChannel) {
cy.get('#rhsContainer').then((rhsContainer) => {
cy.wrap(rhsContainer).findByText('Info').should('be.visible');
cy.wrap(rhsContainer).findByText(testChannel.display_name).should('be.visible');
});
}

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

@@ -0,0 +1,329 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const {generateRandomUser} = require('../../../support/api/user');
// ***************************************************************
// - [#] 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 @channel @rhs @channel_members
function openChannelMembersRhs(testTeam, testChannel) {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on the Members menu
cy.uiGetRHS().findByText('Members').should('be.visible').click();
}
describe('Channel members RHS', () => {
let testTeam;
let testChannel;
let admin;
let user;
before(() => {
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, user: newAdmin}) => {
testTeam = team;
admin = newAdmin;
// User we'll use for our permissions tests
cy.apiCreateUser().then(({user: newUser}) => {
user = newUser;
cy.apiAddUserToTeam(team.id, newUser.id).then(() => {
cy.apiCreateChannel(testTeam.id, 'channel', 'Public Channel', 'O').then(({channel}) => {
testChannel = channel;
cy.apiAddUserToChannel(channel.id, newAdmin.id);
cy.apiAddUserToChannel(channel.id, newUser.id);
});
});
});
// # Change permission so that regular users can't change channels or add members
cy.apiGetRolesByNames(['channel_user']).then(({roles}) => {
const role = roles[0];
const permissions = role.permissions.filter((permission) => {
return !(['manage_public_channel_members', 'manage_private_channel_members', 'manage_public_channel_properties', 'manage_private_channel_properties'].includes(permission));
});
if (permissions.length !== role.permissions) {
cy.apiPatchRole(role.id, {permissions});
}
});
cy.apiLogin(admin);
});
});
it('should be able to open the RHS from channel info', () => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, testChannel);
// * RHS Container should exist
ensureChannelMembersRHSExists(testChannel);
});
it('should be able to open the RHS from the members icon', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Open the RHS by clicking on the members icon
cy.get('#channelHeaderInfo').within(() => {
cy.get('#member_rhs').should('be.visible').click({force: true});
});
// * RHS Container should exist
ensureChannelMembersRHSExists(testChannel);
});
it('should display the number of members', () => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, testChannel);
// * Verify that we can toggle the favorite status
cy.uiGetRHS().findByText('3 members').should('be.visible');
});
it('should go back to previous RHS when switching from a channel to a DM', () => {
cy.apiCreateUser({}).then(({user: newUser}) => {
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, testChannel);
// * Ensure we are in the members subpanel
cy.uiGetRHS().get('.sidebar--right__title').findByText('Members').should('be.visible');
// # Visit the DM page
cy.uiGotoDirectMessageWithUser(newUser);
// * We should be in Channel Info RHS now
cy.uiGetRHS().get('.sidebar--right__title').findByText('Info').should('be.visible');
});
});
});
it('should close the RHS when switching from a channel to a DM', () => {
cy.apiCreateUser({}).then(({user: newUser}) => {
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
// # Open the Channel Members RHS directly from the channel header button
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.get('#member_rhs').click();
// * Ensure we are in the members subpanel
cy.uiGetRHS().get('.sidebar--right__title').findByText('Members').should('be.visible');
// # Visit the DM page
cy.uiGotoDirectMessageWithUser(newUser);
// * The RHS must not exist
cy.get('#sidebar-right').should('not.exist');
});
});
});
it('should display search when number of members is 20 or above', () => {
cy.apiCreateChannel(testTeam.id, 'search-test-channel', 'Search Test Channel', 'O').then(({channel}) => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, channel);
// * Ensure the search bar is not there
cy.uiGetRHS().findByPlaceholderText('Search members').should('not.exist');
// # Close RHS
cy.uiCloseRHS();
for (let i = 0; i < 20; i++) {
// eslint-disable-next-line no-loop-func
cy.apiCreateUser().then(({user: newUser}) => {
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
cy.apiAddUserToChannel(channel.id, newUser.id);
});
});
}
cy.apiGetUsers({in_channel: channel.id}).then(({users}) => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, channel);
// # Search for first user
cy.uiGetRHS().findByTestId('channel-member-rhs-search').should('be.visible').type(users[0].username);
// * we should see them, but nobody else
cy.uiGetRHS().contains(`${users[0].username}`).should('be.visible');
cy.uiGetRHS().findByText(`${users[1].username}`).should('not.exist');
// # erase the field
cy.uiGetRHS().get('[aria-label="cancel members search"]').should('be.visible').click();
cy.uiGetRHS().contains(`${users[0].username}`).should('exist');
});
});
});
it('should hide deactivated members', () => {
cy.apiCreateChannel(testTeam.id, 'hide-test-channel', 'Hide Test Channel', 'O').then(({channel}) => {
let testUser = null;
cy.apiCreateUser().then(({user: newUser}) => {
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
cy.apiAddUserToChannel(channel.id, newUser.id).then(() => {
testUser = newUser;
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, channel);
// * Ensure the member is visible
cy.uiGetRHS().contains(`${testUser.username}`).should('be.visible');
// # Deactivate the user
cy.apiDeactivateUser(testUser.id);
// * Ensure the user is not visible anymore
cy.uiGetRHS().findByText(`${testUser.username}`).should('not.exist');
});
});
});
});
});
describe('as an admin', () => {
before(() => {
cy.apiLogout();
cy.apiLogin(admin);
});
it('should be able to open the RHS from the channel menu', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.uiOpenChannelMenu('Manage Members');
// * RHS Container should be open in edit mode
cy.get('#rhsContainer').then((rhsContainer) => {
cy.wrap(rhsContainer).findByText('Members').should('be.visible');
cy.wrap(rhsContainer).findByText(testChannel.display_name).should('be.visible');
// Done button should be visible
cy.wrap(rhsContainer).findByText('Done').should('be.visible');
});
});
it('should be able to invite new members', () => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, testChannel);
// # Click on the Add button
cy.uiGetRHS().findByText('Add').should('be.visible').click();
// * The modal should appear
cy.get('.channel-invite').should('be.visible');
});
it('should be able to manage members', () => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, testChannel);
// # Click on the Manage button
cy.uiGetRHS().findByText('Manage').should('be.visible').click();
// * Can see user with their roles, and change it
cy.uiGetRHS().findByTestId(`memberline-${user.id}`).should('be.visible').within(() => {
cy.contains(`${user.username}`).should('be.visible');
cy.findByText('Member').should('be.visible').click();
cy.findByText('Make Channel Admin').should('be.visible').click();
});
// the user line is going to be removed and re-added in another category,
// cypress struggle to realize this so we have to wait a few ms
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
// * Can see the user with his new admin role, and change it back
cy.uiGetRHS().findByTestId(`memberline-${user.id}`).should('be.visible').within(() => {
cy.findByText('Admin').should('be.visible').click();
cy.findByText('Make Channel Member').should('be.visible').click();
});
});
});
describe('as an non-admin', () => {
before(() => {
cy.apiLogout();
cy.apiLogin(user);
});
it('should be able to open the RHS from the channel menu', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.uiOpenChannelMenu('View Members');
// * RHS Container should be open in edit mode
ensureChannelMembersRHSExists(testChannel);
});
it('should not be able to invite new members', () => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, testChannel);
// # Click on the Add button
cy.uiGetRHS().findByText('Add').should('not.exist');
});
it('should not be able to manage members', () => {
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, testChannel);
// # Click on the Manage button
cy.uiGetRHS().findByText('Manage').should('not.exist');
});
});
it('should be able to find users not in the initial list', () => {
cy.apiCreateChannel(testTeam.id, 'big-search-test-channel', 'Big Search Test Channel', 'O').then(({channel}) => {
// # create 100 random users
for (let i = 0; i < 100; i++) {
// eslint-disable-next-line no-loop-func
cy.apiCreateUser().then(({user: newUser}) => {
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
cy.apiAddUserToChannel(channel.id, newUser.id);
});
});
}
// # create a user that will not be listed by default
const lastUser = generateRandomUser();
lastUser.username = 'zzzzzzz' + Date.now();
cy.apiCreateUser({user: lastUser}).then(({user: newUser}) => {
cy.apiAddUserToTeam(testTeam.id, newUser.id).then(() => {
cy.apiAddUserToChannel(channel.id, newUser.id);
});
});
// # Open the Channel Members RHS
openChannelMembersRhs(testTeam, channel);
// # make sure that last user is not present in the list
cy.uiGetRHS().findByText(`${lastUser.username}`).should('not.exist');
// # Search for the user user
cy.uiGetRHS().findByTestId('channel-member-rhs-search').should('be.visible').type(lastUser.username);
// * the user is now existing
cy.uiGetRHS().contains(`${lastUser.username}`).should('be.visible');
});
});
});
function ensureChannelMembersRHSExists(testChannel) {
cy.get('#rhsContainer').then((rhsContainer) => {
cy.wrap(rhsContainer).findByText('Members').should('be.visible');
cy.wrap(rhsContainer).findByText(testChannel.display_name).should('be.visible');
});
}

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

@@ -0,0 +1,114 @@
// 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 @channel
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Channel', () => {
let testTeam;
let ownChannel;
let otherChannel;
let testUser;
let offTopicUrl;
const myChannelsDividerText = 'My Channels';
const otherChannelsDividerText = 'Other Channels';
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup().then(({team, channel, user, offTopicUrl: url}) => {
testTeam = team;
ownChannel = channel;
testUser = user;
offTopicUrl = url;
cy.apiCreateChannel(testTeam.id, 'delta-test', 'Delta Channel').then((out) => {
otherChannel = out.channel;
});
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
it('Channel autocomplete should have both lists populated correctly', () => {
// # Type "~"
cy.uiGetPostTextBox().clear().type('~').wait(TIMEOUTS.HALF_SEC);
cy.get('#loadingSpinner').should('not.exist');
// * Should open up suggestion list for channels
// * Should match each channel item
cy.get('#suggestionList').should('be.visible').children().as('suggestionList');
// * Should render "MY CHANNELS" suggestion list divider
cy.get('@suggestionList').eq(0).contains(myChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').eq(1).should('contain', ownChannel.display_name);
cy.get('@suggestionList').eq(2).should('contain', 'Off-Topic');
cy.get('@suggestionList').eq(3).should('contain', 'Town Square');
// * Should render "OTHER CHANNELS" suggestion list divider
cy.get('@suggestionList').eq(4).contains(otherChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').eq(5).should('contain', otherChannel.display_name);
});
it('Joining a channel should alter channel mention autocomplete lists accordingly', () => {
// # Join a channel by /join slash command
cy.uiGetPostTextBox().clear().wait(TIMEOUTS.HALF_SEC).type(`/join ~${otherChannel.name}`).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Verify that it redirects into the channel
cy.url().should('include', `/${testTeam.name}/channels/${otherChannel.name}`);
// # Type "~"
cy.uiGetPostTextBox().type('~').wait(TIMEOUTS.HALF_SEC);
cy.get('#loadingSpinner').should('not.exist');
// * Should open up suggestion list for channels
// * Should match each channel
cy.get('#suggestionList').should('be.visible').children().as('suggestionList');
// * Should render "MY CHANNELS" suggestion list divider
cy.get('@suggestionList').eq(0).contains(myChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').eq(1).should('contain', ownChannel.display_name);
cy.get('@suggestionList').eq(2).should('contain', otherChannel.display_name);
cy.get('@suggestionList').eq(3).should('contain', 'Off-Topic');
cy.get('@suggestionList').eq(4).should('contain', 'Town Square');
});
it('Getting removed from a channel should alter channel mention autocomplete lists accordingly', () => {
// # Remove test user from the test channel
cy.apiAdminLogin();
cy.removeUserFromChannel(otherChannel.id, testUser.id).then((res) => {
expect(res.status).to.equal(200);
// # Login as test user and visit the test team
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
// # Type "~"
cy.uiGetPostTextBox().clear().type('~').wait(TIMEOUTS.HALF_SEC);
cy.get('#loadingSpinner').should('not.exist');
// * Should open up suggestion list for channels
// * Should match each channel item
cy.get('#suggestionList').should('be.visible').children().as('suggestionList');
// * Should render "MY CHANNELS" suggestion list divider
cy.get('@suggestionList').eq(0).contains(myChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').eq(1).should('contain', ownChannel.display_name);
cy.get('@suggestionList').eq(2).should('contain', 'Off-Topic');
cy.get('@suggestionList').eq(3).should('contain', 'Town Square');
// * Should render "OTHER CHANNELS" suggestion list divider
cy.get('@suggestionList').eq(4).contains(otherChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').eq(5).should('contain', otherChannel.display_name);
});
});
});

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

@@ -0,0 +1,130 @@
// 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 @channel @not_cloud
import * as TIMEOUTS from '../../../fixtures/timeouts';
const timestamp = Date.now();
function verifyChannel(channel, verifyExistence = true) {
// # Wait for Channel to be created
cy.wait(TIMEOUTS.HALF_SEC);
// # Hover on the channel name
cy.get(`#sidebarItem_${channel.name}`).should('be.visible').trigger('mouseover');
// * Verify that the tooltip is displayed
if (verifyExistence) {
cy.get('div.tooltip-inner').
should('be.visible').
and('contain', channel.display_name);
} else {
cy.get('div.tooltip-inner').should('not.exist');
}
// # Move cursor away from channel
cy.get(`#sidebarItem_${channel.name}`).should('be.visible').trigger('mouseout');
}
describe('channel name tooltips', () => {
let loggedUser;
let longUser;
let testTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
// # Login as new user and visit off-topic
cy.apiInitSetup().then(({team, user, offTopicUrl}) => {
testTeam = team;
loggedUser = user;
cy.apiCreateUser({prefix: `thisIsALongUsername${timestamp}`}).then(({user: user1}) => {
longUser = user1;
cy.apiAddUserToTeam(testTeam.id, loggedUser.id);
});
cy.apiLogin(loggedUser);
cy.visit(offTopicUrl);
});
});
it('Should show tooltip on hover - open/public channel with long name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
`Public channel with a long name-${timestamp}`,
).then(({channel}) => {
verifyChannel(channel);
});
});
it('Should show tooltip on hover - private channel with long name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
`Private channel with a long name-${timestamp}`,
'P',
).then(({channel}) => {
verifyChannel(channel);
});
});
it('Should not show tooltip on hover - open/public channel with short name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
'Public channel',
).then(({channel}) => {
verifyChannel(channel, false);
});
});
it('Should not show tooltip on hover - private channel with short name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
'Private channel',
'P',
).then(({channel}) => {
verifyChannel(channel, false);
});
});
it('Should show tooltip on hover - user with a long username', () => {
// # Open a DM with the user
cy.findByRole('button', {name: 'Write a direct message'}).click();
cy.focused().as('searchBox').typeWithForce(longUser.username);
// * Verify that the user is selected in the results list before typing enter
cy.get('div.more-modal__row').
should('have.length', 1).
and('have.class', 'clickable').
and('have.class', 'more-modal__row--selected').
and('contain.text', longUser.username.toLowerCase());
cy.get('@searchBox').typeWithForce('{enter}');
cy.uiGetButton('Go').click();
// # Hover on the channel name
cy.get(`#sidebarItem_${Cypress._.sortBy([loggedUser.id, longUser.id]).join('__')}`).scrollIntoView().should('be.visible').trigger('mouseover');
// * Verify that the tooltip is displayed
cy.get('div.tooltip-inner').should('be.visible');
// # Move cursor away from channel
cy.get(`#sidebarItem_${Cypress._.sortBy([loggedUser.id, longUser.id]).join('__')}`).scrollIntoView().should('be.visible').trigger('mouseout');
});
});

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

@@ -0,0 +1,138 @@
// 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 @channel
describe('Channel routing', () => {
let testUser;
let otherUser1;
let otherUser2;
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser({prefix: 'otherA'}).then(({user: newUser}) => {
otherUser1 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
cy.apiCreateUser({prefix: 'otherB'}).then(({user: newUser}) => {
otherUser2 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('should go to town square channel view', () => {
// * Check if the channel is loaded correctly
cy.get('#channelHeaderTitle').should('be.visible').should('contain', 'Town Square');
});
it('should go to private channel view', () => {
// # Create a private channel
cy.apiCreateChannel(testTeam.id, 'private-channel', 'Private channel', 'P').then(({channel}) => {
// # Go to the newly created channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', 'Private channel');
// # Remove the created channel
cy.apiDeleteChannel(channel.id);
});
});
it('should go to self direct channel using the multiple ways to go', () => {
// # Create a self direct channel
cy.apiCreateDirectChannel([testUser.id, testUser.id]).then(({channel: ownDMChannel}) => {
// # Visit the channel using the channel name
cy.visit(`/${testTeam.name}/channels/${testUser.id}__${testUser.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
// # Visit the channel using the channel id
cy.visit(`/${testTeam.name}/channels/${ownDMChannel.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
// # Visit the channel using the username
cy.visit(`/${testTeam.name}/messages/@${testUser.username}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
// # Visit the channel using the user email
cy.visit(`/${testTeam.name}/messages/${testUser.email}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
});
});
it('should go to other user direct channel using multiple ways to go', () => {
// # Create a direct channel between two users
cy.apiCreateDirectChannel([testUser.id, otherUser1.id]).then(({channel: dmChannel}) => {
// # Visit the channel using the channel name
cy.visit(`/${testTeam.name}/channels/${testUser.id}__${otherUser1.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
// # Visit the channel using the channel id
cy.visit(`/${testTeam.name}/channels/${dmChannel.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
// # Visit the channel using the target username
cy.visit(`/${testTeam.name}/messages/@${otherUser1.username}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
// # Visit the channel using the target user email
cy.visit(`/${testTeam.name}/messages/${otherUser1.email}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
});
});
it('should go group channel using group id', () => {
const userGroupIds = [testUser.id, otherUser1.id, otherUser2.id];
// # Create a group channel for 3 users
cy.apiCreateGroupChannel(userGroupIds).then(({channel: gmChannel}) => {
// # Visit the channel using the name using the channels route
cy.visit(`/${testTeam.name}/channels/${gmChannel.name}`);
// * Check you can go to the channel without problem
const displayName = gmChannel.display_name.split(', ').filter(((username) => username !== testUser.username)).join(', ');
cy.get('#channelHeaderTitle').should('be.visible').should('contain', displayName);
// # Visit the channel using the name using the messages route
cy.visit(`/${testTeam.name}/messages/${gmChannel.name}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', displayName);
});
});
});

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

@@ -0,0 +1,95 @@
// 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 @channel @channel_settings
import {
beMuted,
beUnmuted,
} from '../../../support/assertions';
describe('Channel Settings', () => {
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
cy.apiCreateChannel(testTeam.id, 'channel', 'Private Channel', 'P').then(({channel}) => {
cy.apiAddUserToChannel(channel.id, user.id);
});
cy.apiLogin(user);
// # Visit town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('MM-T882 Channel URL validation works properly', () => {
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Go to channel dropdown > Rename channel
cy.get('#channelHeaderDropdownIcon').click();
cy.findByText('Rename Channel').click();
// # Try to enter existing URL and save
cy.get('#channel_name').clear().type('town-square');
cy.get('#save-button').click();
// # Error is displayed and URL is unchanged
cy.get('.has-error').should('be.visible').and('contain', 'Unable to update the channel');
cy.url().should('include', `/${testTeam.name}/channels/${channel.name}`);
// # Enter a new URL and save
cy.get('#channel_name').clear().type('another-town-square');
cy.get('#save-button').click();
// * URL is updated and no errors are displayed
cy.url().should('include', `/${testTeam.name}/channels/another-town-square`);
});
});
it('MM-T887 Channel dropdown menu - Mute / Unmute', () => {
// # Visit off-topic
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Go to channel dropdown > Mute channel
cy.get('#channelHeaderDropdownIcon').click();
cy.get('#channelHeaderDropdownMenu').should('exist').
findByText('Mute Channel').should('be.visible').click();
// # Verify channel is muted
cy.get('#sidebarItem_off-topic').should(beMuted);
// # Verify mute bell icon is visible
cy.get('#toggleMute').should('be.visible');
// # Verify that off topic is last in the list of channels
cy.uiGetLhsSection('CHANNELS').find('.SidebarChannel').
last().should('contain', 'Off-Topic').
get('a').should('have.class', 'muted');
// # Click Unmute channel while menu is open
cy.get('#channelHeaderDropdownIcon').click();
cy.get('#channelHeaderDropdownMenu').should('exist').
findByText('Unmute Channel').should('be.visible').click();
// # Verify channel is unmuted
cy.get('#sidebarItem_off-topic').should(beUnmuted);
// # Verify mute bell icon is not visible
cy.get('#toggleMute').should('not.exist');
// # Verify that off topic is not last in the list of channels
cy.uiGetLhsSection('CHANNELS').find('.SidebarChannel').
last().should('not.contain', 'Off-Topic');
});
});

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

@@ -0,0 +1,112 @@
// 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 @channel @smoke
describe('Channel Switcher', () => {
let testTeam;
let testChannelName;
let offTopicUrl;
const channelNamePrefix = 'aswitchchannel';
const channelDisplayNamePrefix = 'ASwitchChannel';
before(() => {
cy.apiInitSetup({channelPrefix: {name: `${channelNamePrefix}-a`, displayName: `${channelDisplayNamePrefix} A`}}).then(({team, channel, user, offTopicUrl: url}) => {
testTeam = team;
testChannelName = channel.display_name;
offTopicUrl = url;
// # Add some channels
cy.apiCreateChannel(testTeam.id, `${channelNamePrefix}-b`, `${channelDisplayNamePrefix} B`, 'O');
cy.apiCreateChannel(testTeam.id, `${channelNamePrefix}-c`, `${channelDisplayNamePrefix} C`, 'O');
// # Login as test user and go to off-topic
cy.apiLogin(user);
cy.visit(offTopicUrl);
});
});
it('MM-T2031_1 - should switch channels by keyboard', () => {
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing channel name in the "Switch Channels" modal message box
// # Use up/down arrow keys to highlight second channel
// # Press ENTER
cy.findByRole('textbox', {name: 'quick switch input'}).
type(`${channelDisplayNamePrefix} `).
type('{downarrow}{downarrow}{enter}');
// * Expect channel title to match title
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', testChannelName);
// * Expect url to match url
cy.url().should('contain', `${testChannelName.replace(/ /g, '-').toLowerCase()}`);
});
it('MM-T2031_2 - should switch channels by mouse', () => {
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing channel name in the "Switch Channels" modal message box
cy.findByRole('textbox', {name: 'quick switch input'}).type(`${channelDisplayNamePrefix} `);
cy.get(`[data-testid^=${channelNamePrefix}-c] > span`).click();
// * Expect channel title to match title
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', `${channelDisplayNamePrefix} C`);
// * Expect url to match url
cy.url().should('contain', `${channelNamePrefix}-c`);
});
it('MM-T2031_3 - should show empty result', () => {
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Type invalid channel name in the "Switch Channels" modal message box
cy.findByRole('textbox', {name: 'quick switch input'}).type('there-is-no-spoon');
// * Expect 'nothing found' message
cy.get('.no-results__title > span').should('be.visible');
});
it('MM-T2031_4 - should close on esc and outside click', () => {
cy.visit(offTopicUrl);
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Press ESC
cy.findByRole('textbox', {name: 'quick switch input'}).type('{esc}');
// * Expect the dialog to be closed
cy.findByRole('textbox', {name: 'quick switch input'}).should('not.exist');
// * Expect staying in the same channel
cy.url().should('contain', 'off-topic');
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Click outside of the modal
cy.get('.modal').click({force: true});
// * Expect the dialog to be closed
cy.findByRole('textbox', {name: 'quick switch input'}).should('not.exist');
// * Expect staying in the same channel
cy.url().should('contain', 'off-topic');
});
});

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

@@ -0,0 +1,125 @@
// 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 @channel @channel_settings @not_cloud
// Make sure that the current channel is Town Square and that the
// channel identified by the passed name is no longer in the channel
// sidebar
function verifyChannelWasProperlyClosed(channelName) {
// * Make sure that we have switched channels
cy.get('#channelHeaderTitle').should('contain', 'Town Square');
// * Make sure the old DM no longer exists
cy.get('#sidebarItem_' + channelName).should('not.exist');
}
describe('Close direct messages', () => {
let testUser;
let otherUser;
let testTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser().then(({user: newUser}) => {
otherUser = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('Through channel header dropdown menu', () => {
createAndVisitDMChannel([testUser.id, otherUser.id]).then((channel) => {
// # Open channel header dropdown menu and click on Close Direct Message
cy.get('#channelHeaderDropdownIcon').click();
cy.findByText('Close Direct Message').click();
verifyChannelWasProperlyClosed(channel.name);
});
});
function createAndVisitDMChannel(userIds) {
return cy.apiCreateDirectChannel(userIds).then(({channel}) => {
// # Visit the new channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Verify channel's display name
cy.get('#channelHeaderTitle').should('contain', channel.display_name);
return cy.wrap(channel);
});
}
});
describe('Close group messages', () => {
let testUser;
let otherUser1;
let otherUser2;
let testTeam;
before(() => {
cy.apiAdminLogin();
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser({prefix: 'aaa'}).then(({user: newUser}) => {
otherUser1 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
cy.apiCreateUser({prefix: 'bbb'}).then(({user: newUser}) => {
otherUser2 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('Through channel header dropdown menu', () => {
createAndVisitGMChannel([otherUser1, otherUser2]).then((channel) => {
// # Open channel header dropdown menu and click on Close Direct Message
cy.get('#channelHeaderDropdownIcon').click();
cy.findByText('Close Group Message').click();
verifyChannelWasProperlyClosed(channel.name);
});
});
function createAndVisitGMChannel(users = []) {
const userIds = users.map((user) => user.id);
return cy.apiCreateGroupChannel(userIds).then(({channel}) => {
// # Visit the new channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Verify channel's display name
const displayName = users.
map((member) => member.username).
sort((a, b) => a.localeCompare(b, 'en', {numeric: true})).
join(', ');
cy.get('#channelHeaderTitle').should('contain', displayName);
return cy.wrap(channel);
});
}
});

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

@@ -0,0 +1,158 @@
// 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 @channel
describe('Channels', () => {
let testUser;
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
});
});
it('MM-T3348 - Convert to private channel should only be shown to users with permission', () => {
// # Reset permissions to default
resetPermissionsToDefault();
// # Enable convert public channels to private for all users
enablePermission('all_users-public_channel-convert_public_channel_to_private-checkbox');
saveConfig();
// # Login as a regular user
cy.apiLogin(testUser);
// # Create new test channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Click the channel header dropdown
cy.get('#channelHeaderDropdownIcon').click();
// * Channel convert to private should be visible and confirm
cy.get('#channelConvertToPrivate').should('be.visible').click();
cy.findByTestId('convertChannelConfirm').should('be.visible').click();
// # Click the channel header dropdown
cy.get('#channelHeaderDropdownIcon').click();
// * Channel convert to private should no longer be visible
cy.get('#channelConvertToPrivate').should('not.exist');
});
// # Reset permissions to default
resetPermissionsToDefault();
// # Login as a regular user
cy.apiLogin(testUser);
// # Create new test channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Click the channel header dropdown
cy.get('#channelHeaderDropdownIcon').click();
// * Channel convert to private should no longer be visible
cy.get('#channelConvertToPrivate').should('not.exist');
});
// # Reset permissions to default
resetPermissionsToDefault();
// # Remove permission from team admins
removePermission('team_admin-public_channel-convert_public_channel_to_private-checkbox');
saveConfig();
// # Promote user to team admin
cy.apiUpdateTeamMemberSchemeRole(testTeam.id, testUser.id, {scheme_admin: true, scheme_user: true});
// # Login as the now team admin user
cy.apiLogin(testUser);
// # Create new test channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Click the channel header dropdown
cy.get('#channelHeaderDropdownIcon').click();
// * Channel convert to private should not be visible
cy.get('#channelConvertToPrivate').should('not.exist');
});
// # Reset permissions to default
resetPermissionsToDefault();
// # Login as the team admin user
cy.apiLogin(testUser);
// # Create new test channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Click the channel header dropdown
cy.get('#channelHeaderDropdownIcon').click();
// * Channel convert to private should be visible and confirm
cy.get('#channelConvertToPrivate').should('be.visible').click();
cy.findByTestId('convertChannelConfirm').should('be.visible').click();
// # Click the channel header dropdown
cy.get('#channelHeaderDropdownIcon').click();
// * Channel convert to private should no longer be visible
cy.get('#channelConvertToPrivate').should('not.exist');
});
});
});
const saveConfig = () => {
cy.get('#saveSetting').click();
cy.waitUntil(() => cy.get('#saveSetting').then((el) => {
return el[0].innerText === 'Save';
}));
};
const enablePermission = (permissionCheckBoxTestId) => {
cy.findByTestId(permissionCheckBoxTestId).then((el) => {
if (!el.hasClass('checked')) {
el.click();
}
});
};
const removePermission = (permissionCheckBoxTestId) => {
cy.findByTestId(permissionCheckBoxTestId).then((el) => {
if (el.hasClass('checked')) {
el.click();
}
});
};
const resetPermissionsToDefault = () => {
// # Login as sysadmin and navigate to system scheme page
cy.apiAdminLogin();
cy.visit('/admin_console/user_management/permissions/system_scheme');
// # Click reset to defaults and confirm
cy.findByTestId('resetPermissionsToDefault').click();
cy.get('#confirmModalButton').click();
// # Save
saveConfig();
};

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

@@ -0,0 +1,100 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {UserProfile} from 'mattermost-redux/types/users';
// ***************************************************************
// - [#] 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 @channel
describe('Leave and Archive channel actions display as destructive', () => {
let testUser: UserProfile;
let offTopicUrl: string;
before(() => {
// # Login as test user and visit off-topic channel
cy.apiInitSetup().then(({user, offTopicUrl: url}) => {
testUser = user;
offTopicUrl = url;
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
beforeEach(() => {
cy.reload();
});
it('MM-T4943_1 Leave and Archive channel actions display as destructive in the channel dropdown menu', () => {
// # click on channel drop-down menu
cy.get('#channelHeaderTitle').should('be.visible').click();
// * View Info menu option should be visible
cy.findByText('View Info').should('be.visible');
// * Move to... menu option should be visible
cy.findByText('Move to...').should('be.visible').children().trigger('mouseover');
// * Favorites Sub-menu option should be visible
cy.findByText('Favorites').should('be.visible');
// * New Category Sub-menu option should be visible
cy.findByText('New Category').should('be.visible');
// * Notification Preferences menu option should be visible
cy.get('#channelNotificationPreferences').should('be.visible');
// * Mute Channel menu option should be visible
cy.get('#channelToggleMuteChannel').should('be.visible');
// * Add Members menu option should be visible
cy.get('#channelAddMembers').should('be.visible');
// * Manage Members menu option should be visible
cy.get('#channelManageMembers').should('be.visible');
// * Edit Channel Header menu option should be visible
cy.get('#channelEditHeader').should('be.visible');
// * Edit Channel Purpose menu option should be visible
cy.get('#channelEditPurpose').should('be.visible');
// * Rename Channel menu option should be visible
cy.get('#channelRename').should('be.visible');
// * Archive Channel menu option should be visible and have a background-color (destructive)
cy.get('#channelArchiveChannel').should('be.visible').children().focus().should('have.css', 'background-color', 'rgb(210, 75, 78)');
// * Leave Channel menu option should be visible and hav a background-color (destructive)
cy.get('#channelLeaveChannel').should('be.visible').children().focus().should('have.css', 'background-color', 'rgb(210, 75, 78)');
});
it('MM-T4943_2 Leave channel actions display as destructive in the Edit Channel Menu ', () => {
// # Open Edit Channel Menu and verify menu optin
cy.uiGetChannelSidebarMenu('Off-Topic').within(() => {
// * Favorite menu option should be visible
cy.findByText('Favorite').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Mute Channel menu option should be visible
cy.findByText('Mute Channel').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Copy Link menu option should be visible
cy.findByText('Copy Link').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Add Members menu option should be visible
cy.findByText('Add Members').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Move to... menu option should be visible and is not destructive
cy.findByText('Move to...').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Leave Channel menu option should be visible and have a color (destructive)
cy.findByText('Leave Channel').should('be.visible').should('have.css', 'color', 'rgb(210, 75, 78)');
});
});
});

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

@@ -0,0 +1,110 @@
// 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 @channel
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Leave channel', () => {
let testTeam;
let testUser;
let testChannel;
before(() => {
cy.apiUpdateConfig({
ServiceSettings: {
ThreadAutoFollow: true,
CollapsedThreads: 'default_off',
},
});
// # Login as test user and visit created channel
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiLogin(testUser);
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
testChannel = channel;
});
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
it('MM-T4429_1 Leave a channel while RHS is open', () => {
// # Post a message in the channel
cy.postMessage('Test leave channel while RHS open');
cy.getLastPostId().then((id) => {
// # Open RHS
cy.clickPostCommentIcon(id);
// # Post a reply message
cy.postMessageAs({sender: testUser, message: 'another reply!', channelId: testChannel.id, rootId: id});
// * RHS should be visible
cy.get('#rhsContainer').should('be.visible');
// * RHS text box should be visible
cy.uiGetReplyTextBox();
// # Archive the channel
cy.uiLeaveChannel();
cy.wait(TIMEOUTS.TWO_SEC); // eslint-disable-line cypress/no-unnecessary-waiting
// * RHS should not be visible
cy.get('#rhsContainer').should('not.exist');
// * Assert that user is redirected to townsquare
cy.url().should('include', '/channels/town-square');
cy.get('#channelHeaderTitle').should('be.visible').and('contain', 'Town Square');
});
});
it('MM-T4429_2 Leave a channel while RHS is open and CRT on', () => {
// * Post text box should be visible
cy.uiGetPostTextBox();
// # Set CRT to on
cy.uiChangeCRTDisplaySetting('ON');
// # Post a message in the channel
cy.postMessage('Test leave channel while RHS open');
cy.getLastPostId().then((id) => {
// # Open RHS
cy.clickPostCommentIcon(id);
// # Post a reply message
cy.postMessageAs({sender: testUser, message: 'another reply!', channelId: testChannel.id, rootId: id});
// * RHS should be visible
cy.get('#rhsContainer').should('be.visible');
// * RHS text box should be visible
cy.uiGetReplyTextBox();
// * Close tour tip
cy.get('#tipNextButton').should('be.visible').click();
// # Archive the channel
cy.uiLeaveChannel();
cy.wait(TIMEOUTS.TWO_SEC); // eslint-disable-line cypress/no-unnecessary-waiting
// * RHS should not be visible
cy.get('#rhsContainer').should('not.exist');
// * Assert that user is redirected to townsquare
cy.url().should('include', '/channels/town-square');
cy.get('#channelHeaderTitle').should('be.visible').and('contain', 'Town Square');
});
});
});

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

@@ -0,0 +1,258 @@
// 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 @channel
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {createPrivateChannel} from '../enterprise/elasticsearch_autocomplete/helpers';
describe('Channels', () => {
let testUser;
let otherUser;
let testTeam;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
});
cy.apiLogin(testUser).then(() => {
// # Create new test channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
testChannel = channel;
});
// # Go to town square
cy.visit(`/${team.name}/channels/town-square`);
});
});
});
it('MM-19337 Verify UI of More channels modal with archived selection', () => {
verifyMoreChannelsModalWithArchivedSelection(false, testUser, testTeam);
verifyMoreChannelsModalWithArchivedSelection(true, testUser, testTeam);
});
it('MM-19337 Enable users to view archived channels', () => {
cy.apiAdminLogin();
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
// # Login as new user and go to "/"
cy.apiLogin(otherUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse Channels'
cy.uiBrowseOrCreateChannel('Browse Channels').click();
cy.get('#moreChannelsModal').should('be.visible').within(() => {
// * Dropdown should be visible, defaulting to "Public Channels"
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Show: Public Channels').wait(TIMEOUTS.HALF_SEC);
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1).within(() => {
cy.findByText(testChannel.display_name).should('be.visible');
});
cy.get('#searchChannelsTextbox').clear();
// * Channel test should be visible as a public channel in the list
cy.get('#moreChannelsList').should('be.visible').within(() => {
// # Click to join the channel
cy.findByText(testChannel.display_name).scrollIntoView().should('be.visible').click();
});
});
// # Verify that the modal is closed and it's redirected to the selected channel
cy.get('#moreChannelsModal').should('not.exist');
cy.url().should('include', `/${testTeam.name}/channels/${testChannel.name}`);
// # Login as channel admin and go directly to the channel
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click channel header to open channel menu
cy.get('#channelHeaderTitle').should('contain', testChannel.display_name).click();
// * Verify that the menu is opened
cy.get('.Menu__content').should('be.visible').within(() => {
// # Archive the channel
cy.findByText('Archive Channel').should('be.visible').click();
});
// * Verify that the delete/archive channel modal is opened
cy.get('#deleteChannelModal').should('be.visible').within(() => {
// # Confirm archive
cy.findByText('Archive').should('be.visible').click();
});
// # Go to LHS and click 'Browse Channels'
cy.uiBrowseOrCreateChannel('Browse Channels').click();
cy.get('#moreChannelsModal').should('be.visible').within(() => {
// # CLick dropdown to open selection
cy.get('#channelsMoreDropdown').should('be.visible').click().within((el) => {
// # Click on archived channels item
cy.findByText('Archived Channels').should('be.visible').click();
// * Channel test should be visible as an archived channel in the list
cy.wrap(el).should('contain', 'Show: Archived Channels');
});
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
cy.get('#moreChannelsList').children().should('have.length', 1).within(() => {
cy.findByText(testChannel.display_name).should('be.visible');
});
cy.get('#searchChannelsTextbox').clear();
// * Test channel should be visible as a archived channel in the list
cy.get('#moreChannelsList').should('be.visible').within(() => {
// # Click to view archived channel
cy.findByText(testChannel.display_name).scrollIntoView().should('be.visible').click();
});
});
// * Assert that channel is archived and new messages can't be posted.
cy.get('#channelArchivedMessage').should('contain', 'You are viewing an archived channel. New messages cannot be posted.');
cy.uiGetPostTextBox({exist: false});
// # Switch to another channel
cy.get('#sidebarItem_town-square').click();
// * Assert that archived channel doesn't show up in LHS list
cy.get('#sidebar-left').should('not.contain', testChannel.display_name);
});
it('MM-T1702 Search works when changing public/archived options in the dropdown', () => {
cy.apiAdminLogin();
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: true,
},
});
let newChannel;
let testArchivedChannel;
let testPrivateArchivedChannel;
cy.apiCreateTeam('team', 'Test NoMember').then(({team}) => {
cy.apiCreateChannel(team.id, 'not-archived-channel', 'Not Archived Channel').then(({channel}) => {
newChannel = channel;
cy.visit(`/${team.name}/channels/${newChannel.name}`);
// # Leave the channel
cy.uiLeaveChannel();
// * Verify that we've switched to Town Square
cy.url().should('include', '/channels/town-square');
});
cy.apiCreateChannel(team.id, 'archived-channel', 'Archived Channel').then(({channel}) => {
testArchivedChannel = channel;
// # Visit the channel
cy.visit(`/${team.name}/channels/${testArchivedChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave the channel
cy.uiLeaveChannel();
// * Verify that we've switched to Town Square
cy.url().should('include', '/channels/town-square');
});
createPrivateChannel(team.id).then((channel) => {
testPrivateArchivedChannel = channel;
// # Visit the channel
cy.visit(`/${team.name}/channels/${testPrivateArchivedChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
cy.visit(`/${team.name}/channels/town-square`);
});
});
// # Go to LHS and click 'Browse Channels'
cy.uiBrowseOrCreateChannel('Browse Channels').click();
// * Dropdown should be visible, defaulting to "Public Channels"
cy.get('#channelsMoreDropdown').should('be.visible').within((el) => {
cy.wrap(el).should('contain', 'Show: Public Channels');
});
// * Users should be able to type and search
cy.get('#searchChannelsTextbox').should('be.visible').type('iv').wait(TIMEOUTS.HALF_SEC);
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1).within(() => {
cy.findByText(newChannel.display_name).should('be.visible');
});
cy.get('#moreChannelsModal').should('be.visible').within(() => {
// * Users should be able to switch to "Archived Channels" list
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Show: Public Channels').click().within((el) => {
// # Click on archived channels item
cy.findByText('Archived Channels').should('be.visible').click();
// * Modal should show the archived channels list
cy.wrap(el).should('contain', 'Show: Archived Channels');
}).wait(TIMEOUTS.HALF_SEC);
cy.get('#searchChannelsTextbox').clear();
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 2);
cy.get('#moreChannelsList').within(() => {
cy.findByText(testArchivedChannel.display_name).should('be.visible');
cy.findByText(testPrivateArchivedChannel.display_name).should('be.visible');
});
});
});
});
function verifyMoreChannelsModalWithArchivedSelection(isEnabled, testUser, testTeam) {
// # Login as sysadmin and Update config to enable/disable viewing of archived channels
cy.apiAdminLogin();
cy.apiUpdateConfig({
TeamSettings: {
ExperimentalViewArchivedChannels: isEnabled,
},
});
// * Verify more channels modal
cy.visit(`/${testTeam.name}/channels/town-square`);
verifyMoreChannelsModal(isEnabled);
// # Login as regular user and verify more channels modal
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
verifyMoreChannelsModal(isEnabled);
}
function verifyMoreChannelsModal(isEnabled) {
// # Go to LHS and click 'Browse Channels'
cy.uiBrowseOrCreateChannel('Browse Channels').click();
// * Verify that the more channels modal is open and with or without option to view archived channels
cy.get('#moreChannelsModal').should('be.visible').within(() => {
if (isEnabled) {
cy.get('#channelsMoreDropdown').should('be.visible').and('have.text', 'Show: Public Channels');
} else {
cy.get('#channelsMoreDropdown').should('not.exist');
}
});
}

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

@@ -0,0 +1,97 @@
// 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 @channel
function verifyNoChannelToJoinMessage(isVisible) {
cy.findByText('No more channels to join').should(isVisible ? 'be.visible' : 'not.exist');
cy.findByText('Click \'Create New Channel\' to make a new one').should(isVisible ? 'be.visible' : 'not.exist');
}
describe('more public channels', () => {
let testUser;
let otherUser;
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
});
cy.apiLogin(testUser).then(() => {
// # create 30 channels
for (let i = 0; i < 30; i++) {
cy.apiCreateChannel(testTeam.id, 'public-channel', 'public-channel');
}
// # Go to town square
cy.visit(`/${team.name}/channels/town-square`);
});
});
});
it('MM-T1664 Channels do not disappear from More Channels modal', () => {
// # Login as other user
cy.apiLogin(otherUser);
// # Go to town square
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse Channels'
cy.uiBrowseOrCreateChannel('Browse Channels').click();
// * Assert that the moreChannelsModel is visible
cy.findByRole('dialog', {name: 'More Channels'}).should('be.visible').within(() => {
// * Assert that the moreChannelsList is visible and the number of channels is 31
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 31);
// # scroll to the middle channel
cy.get('#moreChannelsList .more-modal__row').eq(15).scrollIntoView();
// * Assert that the "No more channels to join" message does not exist
verifyNoChannelToJoinMessage(false);
// # scroll to the last channel
cy.get('#moreChannelsList .more-modal__row').last().scrollIntoView();
// * Assert that the "No more channels to join" message does not exist
verifyNoChannelToJoinMessage(false);
// # scroll to the first channel
cy.get('#moreChannelsList .more-modal__row').first().scrollIntoView();
// * Assert that the "No more channels to join" message does not exist
verifyNoChannelToJoinMessage(false);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Go to town square
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse Channels'
cy.uiBrowseOrCreateChannel('Browse Channels').click();
// * Assert the moreChannelsModel is visible
cy.findByRole('dialog', {name: 'More Channels'}).should('be.visible').within(() => {
// * Assert the moreChannelsList does have one child
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1);
// * Assert that the "No more channels to join" message is visible
verifyNoChannelToJoinMessage(true);
});
});
});

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

@@ -0,0 +1,51 @@
// 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 @channel
describe('New Channel modal with Boards enabled', () => {
let testTeam;
before(() => {
cy.apiInitSetup().then(({team}) => {
testTeam = team;
});
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
cy.apiLogin(sysadmin);
cy.visit(`/${testTeam.name}/channels/town-square`);
});
cy.shouldHaveFeatureFlag('BoardsProduct', true);
});
it('MM-T5141 New Channel is created with an associated Board', () => {
// # Create new channel with board
const channelName = 'Test Channel With Board';
cy.uiCreateChannel({
prefix: 'channel-',
isPrivate: false,
purpose: '',
name: channelName,
createBoard: true,
}).then(() => {
// * Verify that new channel is in the sidebar and is active
cy.url().should('include', `/${testTeam.name}/channels/test-channel`);
cy.get('#channelHeaderTitle').should('contain', channelName);
cy.get(`.SidebarChannel.active:contains(${channelName})`).should('be.visible');
// * Verify the board is created - check the message sent
cy.waitUntil(() => cy.getLastPost().then((el) => {
const postedMessageEl = el.find('.post-message__text > p')[0];
return Boolean(postedMessageEl && postedMessageEl.textContent.includes('created the board'));
}));
});
});
});

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

@@ -0,0 +1,207 @@
// 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 @channel @rhs
import * as TIMEOUTS from '../../../fixtures/timeouts';
import * as MESSAGES from '../../../fixtures/messages';
describe('Channel RHS', () => {
let testAdmin: Cypress.UserProfile;
let testTeam: Cypress.Team;
let testChannel: Cypress.Channel;
before(() => {
cy.apiCreateCustomAdmin({loginAfter: true}).then(({sysadmin}) => {
testAdmin = sysadmin;
cy.apiCreateTeam('team1', 'team1').then(({team}) => {
testTeam = team;
cy.apiAddUserToTeam(testTeam.id, testAdmin.id);
cy.apiCreateChannel(testTeam.id, 'channel', 'channel', 'O').then(({channel}) => {
testChannel = channel;
cy.apiAddUserToChannel(channel.id, testAdmin.id);
cy.apiSaveCRTPreference(testAdmin.id, 'off');
cy.apiLogin(testAdmin);
});
});
});
});
beforeEach(() => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
it('MM-44435 - should be able to open channel info, visit the system console and come back without issues -- KNOWN ISSUE: MM-47226', () => {
// # Click on the channel info button
cy.uiGetChannelInfoButton().click();
// * Verify that the channel info is opened in RHS
verifyRHSisOpenAndHasTitle('Info');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the channel info is still opened in RHS
verifyRHSisOpenAndHasTitle('Info');
});
it('MM-T5311 - should be able to open recent mentions, visit the system console and come back without issues', () => {
// # Click on the recent mentions button
cy.uiGetRecentMentionButton().click();
// * Verify that the recent mentions is opened in RHS
verifyRHSisOpenAndHasTitle('Recent Mentions');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the recent mentions is still opened in RHS
verifyRHSisOpenAndHasTitle('Recent Mentions');
});
it('MM-T5312 - should be able to open saved posts, visit the system console and come back without issues', () => {
// # Click on the saved posts button
cy.uiGetSavedPostButton().click();
// * Verify that the saved posts is opened in RHS
verifyRHSisOpenAndHasTitle('Saved Posts');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the saved posts is still opened in RHS
verifyRHSisOpenAndHasTitle('Saved Posts');
});
it('MM-T5313 - should be able to open pinned posts, visit the system console and come back without issues', () => {
// # Click on the pinned posts button
cy.uiGetChannelPinButton().click();
// * Verify that the pinned posts is opened in RHS
verifyRHSisOpenAndHasTitle('Pinned Posts');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the pinned posts is still opened in RHS
verifyRHSisOpenAndHasTitle('Pinned Posts');
});
it('MM-T5314 - should be able to open channel members, visit the system console and come back without issues', () => {
// # Click on the channel members button
cy.uiGetChannelMemberButton().click();
// * Verify that the channel members is opened in RHS
verifyRHSisOpenAndHasTitle('Members');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the channel members is still opened in RHS
verifyRHSisOpenAndHasTitle('Members');
});
it('MM-T5315 - should be able to open channel files, visit the system console and come back without issues', () => {
// # Click on the channel files button
cy.uiGetChannelFileButton().click();
// * Verify that the channel files is opened in RHS
verifyRHSisOpenAndHasTitle('Recent files');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the channel files is still opened in RHS
verifyRHSisOpenAndHasTitle('Recent files');
});
it('MM-T5316 - should be able to open search results, visit the system console and come back without issues', () => {
// # Post a message
cy.postMessage(MESSAGES.SMALL);
// # Enter the search terms and hit enter to start the search
cy.get('#searchBox').clear().type(MESSAGES.TINY).type('{enter}');
// * Verify that the search results is opened in RHS
verifyRHSisOpenAndHasTitle('Search Results');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the search results is still opened in RHS
verifyRHSisOpenAndHasTitle('Search Results');
});
it('MM-T5317 - should be able to open thread reply, visit the system console and come back without issues', () => {
// # Post a message
cy.postMessage(MESSAGES.SMALL);
// # Click on the reply button
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// * Verify that the thread reply is opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the thread reply is still opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
});
it('MM-T5318 - should be able to open thread reply with CRT, visit the system console and come back without issues', () => {
// # Enable CRT
cy.apiSaveCRTPreference(testAdmin.id, 'on');
// # Refresh the page
cy.reload();
// # Post a message
cy.postMessage(MESSAGES.SMALL);
// # Click on the reply button
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// * Verify that the thread reply is opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the thread reply is still opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
});
});
function openSystemConsoleAndLeave() {
// # visit the system console...
cy.uiOpenProductMenu('System Console');
cy.wait(TIMEOUTS.THREE_SEC);
// # ...and leave it
cy.get('.backstage-navbar__back').click();
}
function verifyRHSisOpenAndHasTitle(title: string) {
cy.get('#sidebar-right').should('exist').within(() => {
cy.findByText(title).should('exist').and('be.visible');
});
}

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

@@ -0,0 +1,63 @@
// 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 @channel
import {getAdminAccount} from '../../../support/env';
describe('View Members modal', () => {
const sysadmin = getAdminAccount();
it('MM-20164 - Going from a Member to an Admin should update the modal', () => {
cy.apiInitSetup().then(({team, user}) => {
cy.apiCreateUser().then(({user: user1}) => {
cy.apiAddUserToTeam(team.id, user1.id);
});
// # Promote user as a system admin
// # Visit default channel and verify members modal
cy.apiLogin(user);
promoteToSysAdmin(user, sysadmin);
cy.visit(`/${team.name}/channels/town-square`);
verifyMemberDropdownAction(true);
// # Make user a regular member
// # Reload and verify members modal
demoteToMember(user, sysadmin);
cy.reload();
verifyMemberDropdownAction(false);
});
});
});
const demoteToMember = (user, sysadmin) => {
cy.externalRequest({user: sysadmin, method: 'put', path: `users/${user.id}/roles`, data: {roles: 'system_user'}});
};
const promoteToSysAdmin = (user, sysadmin) => {
cy.externalRequest({user: sysadmin, method: 'put', path: `users/${user.id}/roles`, data: {roles: 'system_user system_admin'}});
};
function verifyMemberDropdownAction(hasActionItem) {
// # Click member count to open member rhs
cy.get('#member_rhs').click();
cy.uiGetRHS().should('be.visible').within(() => {
// # Click "Manage"
cy.findByText('Manage').click();
});
// * Check to see any user has dropdown menu
if (hasActionItem) {
cy.uiGetRHS().findAllByText('Member').should('exist');
} else {
cy.uiGetRHS().findByText('Member').should('not.exist');
}
}

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

@@ -0,0 +1,92 @@
// 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.
// ***************************************************************
// Group: @channels @channel
import {getAdminAccount} from '../../../support/env';
const demoteToMember = (user, admin) => {
cy.externalRequest({user: admin, method: 'put', path: `users/${user.id}/roles`, data: {roles: 'system_user'}});
};
const demoteToChannelMember = (user, channelId, admin) => {
cy.externalRequest({
user: admin,
method: 'put',
path: `channels/${channelId}/members/${user.id}/schemeRoles`,
data: {
scheme_user: true,
scheme_admin: false,
},
});
};
const promoteToChannelAdmin = (user, channelId, admin) => {
cy.externalRequest({
user: admin,
method: 'put',
path: `channels/${channelId}/members/${user.id}/schemeRoles`,
data: {
scheme_user: true,
scheme_admin: true,
},
});
};
describe('Change Roles', () => {
const admin = getAdminAccount();
let testUser;
let testChannelId;
beforeEach(() => {
// # Login as test user and visit test channel
cy.apiInitSetup().then(({team, user, channel}) => {
testUser = user;
testChannelId = channel.id;
cy.apiCreateUser().then(({user: otherUser}) => {
cy.apiAddUserToTeam(team.id, otherUser.id);
});
// # Change permission so that regular users can't change channels or add members
cy.apiGetRolesByNames(['channel_user']).then(({roles}) => {
const role = roles[0];
const permissions = role.permissions.filter((permission) => {
return !(['manage_public_channel_members', 'manage_private_channel_members', 'manage_public_channel_properties', 'manage_private_channel_properties'].includes(permission));
});
if (permissions.length !== role.permissions) {
cy.apiPatchRole(role.id, {permissions});
}
});
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Make user a regular member for channel and system
demoteToMember(testUser, admin);
demoteToChannelMember(testUser, testChannelId, admin);
// # Reload page to ensure no cache or saved information
cy.reload(true);
});
});
it('MM-T4174 User role to channel admin/member updates channel member modal immediately without refresh', () => {
// # Go to member modal
cy.uiGetChannelMemberButton().click();
cy.uiGetRHS().findByText('Manage').should('not.exist');
// Promote user to a channel admin
promoteToChannelAdmin(testUser, testChannelId, admin);
// * Check to see if a dropdown exists now
cy.uiGetRHS().findByText('Manage').should('be.visible');
});
});

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

@@ -0,0 +1,189 @@
// 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 @channel @channel_settings @smoke
describe('Channel Settings', () => {
let testTeam: Cypress.Team;
let firstUser: Cypress.UserProfile;
let addedUsersChannel: Cypress.Channel;
let username: string;
const usernames: string[] = [];
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
firstUser = user;
// # Add 4 users
for (let i = 0; i < 4; i++) {
cy.apiCreateUser().then(({user: newUser}) => { // eslint-disable-line
cy.apiAddUserToTeam(testTeam.id, newUser.id);
});
}
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
addedUsersChannel = channel;
});
cy.apiLogin(firstUser);
});
});
it('MM-T859_1 Single User: Usernames are links, open profile popovers', () => {
// # Create and visit new channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Add users to channel
addNumberOfUsersToChannel(1);
cy.getLastPostId().then((id) => {
// * The system message should contain 'added to the channel by you'
cy.get(`#postMessageText_${id}`).should('contain', 'added to the channel by you');
// # Verify username link
verifyMentionedUserAndProfilePopover(id);
});
});
});
it('MM-T859_2 Combined Users: Usernames are links, open profile popovers', () => {
// # Create and visit new channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
addNumberOfUsersToChannel(3);
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).should('contain', '2 others were added to the channel by you');
// # Click "2 others" to expand more users
cy.get(`#post_${id}`).find('.markdown__paragraph-inline').siblings('a').first().click().then(() => {
// # Verify each username link
verifyMentionedUserAndProfilePopover(id);
});
});
});
});
it('MM-T856_1 Add existing users to public channel from drop-down > Add Members', () => {
// # Visit the add users channel
cy.visit(`/${testTeam.name}/channels/${addedUsersChannel.name}`);
// # Open channel menu and click 'Add Members'
cy.uiOpenChannelMenu('Add Members');
cy.get('#addUsersToChannelModal').should('be.visible');
// # Type into the input box to search for a user
cy.get('#selectItems input').typeWithForce('u');
// # First add one user in order to see them disappearing from the list
cy.get('#multiSelectList > div').first().then((el) => {
const childNodes = Array.from(el[0].childNodes);
childNodes.map((child: HTMLElement) => usernames.push(child.innerText));
// # Get username from text for comparison
username = usernames.toString().match(/\w+/g)[0];
cy.get('#multiSelectList').should('contain', username);
// # Verify status wrapper is present within the modal list
cy.get(el as unknown as string).children().first().should('have.class', 'status-wrapper');
// # Click to add the first user
cy.wrap(el).click();
// # Verify users list is not visible
cy.get('#multiSelectList').should('not.exist');
// # Click 'Add' button
cy.uiGetButton('Add').click();
cy.get('#addUsersToChannelModal').should('not.exist');
});
// # Verify that the last system post also contains the username
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).should('contain', `${username} added to the channel by you.`);
});
// Add two more users
addNumberOfUsersToChannel(2);
// Verify that the system post reflects the number of added users
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).should('contain', 'added to the channel by you');
});
});
it('MM-T856_2 Existing users cannot be added to public channel from drop-down > Add Members', () => {
cy.apiAdminLogin();
// # Visit the add users channel
cy.visit(`/${testTeam.name}/channels/${addedUsersChannel.name}`);
// # Verify that the system message for adding users displays
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).should('contain', `added to the channel by @${firstUser.username}`);
});
// Visit off topic where all users are added
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Open channel menu and click 'Add Members'
cy.uiOpenChannelMenu('Add Members');
cy.get('#addUsersToChannelModal').should('be.visible');
// # Type into the input box to search for already added user
cy.get('#selectItems input').typeWithForce(firstUser.username);
// * Verify user list exist
cy.get('#multiSelectList').should('exist').within(() => {
cy.findByText('Already in channel').should('be.visible');
});
cy.get('body').type('{esc}');
});
});
function verifyMentionedUserAndProfilePopover(postId: string) {
cy.get(`#post_${postId}`).find('.mention-link').each(($el) => {
// # Get username from each mentioned link
const userName = $el[0].innerHTML;
// # Click each username link
cy.wrap($el).click();
// * Profile popover should be visible
cy.get('#user-profile-popover').should('be.visible');
// * The username in the popover the same as the username link for each user
cy.get('#userPopoverUsername').should('contain', userName);
// Click anywhere to close profile popover
cy.get('#channelHeaderInfo').click();
});
}
function addNumberOfUsersToChannel(num = 1) {
// # Open channel menu and click 'Add Members'
cy.uiOpenChannelMenu('Add Members');
cy.get('#addUsersToChannelModal').should('be.visible');
// * Assert that modal appears
// # Click the first row for a number of times
Cypress._.times(num, () => {
cy.get('#selectItems input').typeWithForce('u');
cy.get('#multiSelectList').should('be.visible').first().click();
});
// # Click the button "Add" to add user to a channel
cy.uiGetButton('Add').click();
// # Wait for the modal to disappear
cy.get('#addUsersToChannelModal').should('not.exist');
}

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

@@ -0,0 +1,100 @@
// 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 @channel_settings
// node run_tests.js --group='@channel_settings'
import {getRandomId} from '../../../utils';
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Channel Settings', () => {
let testTeam: Cypress.Team;
let user1: Cypress.UserProfile;
let admin: Cypress.UserProfile;
before(() => {
cy.apiGetMe().then(({user: adminUser}) => {
admin = adminUser;
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
user1 = user;
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
});
it('MM-T1808 Hover effect exists to add a channel description / header (when not already present)', () => {
// # Create a new public channel and then private channel
['O', 'P'].forEach((channelType) => {
cy.apiCreateChannel(testTeam.id, `chan${getRandomId()}`, 'chan', channelType).then(({channel}) => {
// # Go to new channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Test hovering over the header with public and private channel
hoverOnChannelDescriptionAndVerifyBehavior();
});
});
// # Create DM with admin and user 1
cy.apiCreateDirectChannel([user1.id, user1.id]).then(() => {
// # Go to DM
cy.visit(`/${testTeam.name}/messages/@${user1.username}`);
// * Test hovering over the header with DM
hoverOnChannelDescriptionAndVerifyBehavior();
});
// # Create another user and add to the team
cy.apiCreateUser().then(({user: user2}) => {
cy.apiAddUserToTeam(testTeam.id, user2.id).then(() => {
// # Create a GM with admin, user1 and user 2
cy.apiCreateGroupChannel([user2.id, user1.id, admin.id]).then(({channel}) => {
// # Visit the channel using the name using the channels route
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Test hovering over the header with GM
hoverOnChannelDescriptionAndVerifyBehavior();
});
});
});
});
});
function hoverOnChannelDescriptionAndVerifyBehavior() {
const channelDescriptionText = `test description ${getRandomId()}`;
// # Wait a little for channel to load
cy.wait(TIMEOUTS.FIVE_SEC);
// # Scan within channel header description area
cy.get('#channelHeaderDescription').should('be.visible').within(() => {
// * Verify that empty header text is visible and click it
cy.findByText('Add a channel header').should('be.visible').click();
});
// # Scan inside the channel header modal
cy.get('.a11y__modal.modal-dialog').should('be.visible').within(() => {
// # Enter a channel description
cy.findByTestId('edit_textbox').should('exist').clear().type(channelDescriptionText);
// # Click on save
cy.findByText('Save').should('be.visible').click();
});
cy.get('#channelHeaderDescription').should('be.visible').within(() => {
// * Verify that new channel header is set and click it
cy.findAllByText(channelDescriptionText).should('be.visible').click({multiple: true, force: true});
});
// * Check clicking on it doesn't open the edit modal once again
cy.get('.a11y__modal.modal-dialog').should('not.exist');
}

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

@@ -0,0 +1,131 @@
// 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 @channel
// Group: @channels @channel_settings
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Channel routing', () => {
let testTeam: Cypress.Team;
let testUser: Cypress.UserProfile;
let testChannel: Cypress.Channel;
before(() => {
cy.apiInitSetup().then(({team, user, channel}) => {
testTeam = team;
testUser = user;
testChannel = channel;
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('MM-T884_1 Renaming channel name validates against two user IDs being used in URL', () => {
// # Create new test channel
cy.uiCreateChannel({name: 'Test__Channel'});
// # Click on channel menu and press rename channel
cy.get('#channelHeaderDropdownIcon').click();
cy.findByText('Rename Channel').click();
// # Assert if the rename modal present
cy.get('[aria-labelledby="renameChannelModalLabel"').should('be.visible').within(() => {
// # type the two 26 character strings with 2 underscores between them and click on save
cy.get('#channel_name').clear().type('uzsfmtmniifsjgesce4u7yznyh__uzsfmtmniifsjgesce5u7yznyh', {force: true}).wait(TIMEOUTS.HALF_SEC);
cy.get('#save-button').should('be.visible').click();
// # Assert the error occurred with the appropriate message
cy.get('.input__help').should('have.class', 'error');
cy.get('.input__help').should('have.text', 'User IDs are not allowed in channel URLs.');
cy.findByText('Cancel').click();
});
});
it('MM-T884_2 Creating new channel validates against two user IDs being used as channel name', () => {
// # click on create public channel
cy.uiBrowseOrCreateChannel('Create New Channel').click();
// * Verify that the new channel modal is visible
cy.get('#new-channel-modal').should('be.visible').within(() => {
// # Add the new channel name with invalid name and press Create Channel
cy.get('#input_new-channel-modal-name').type('uzsfmtmniifsjgesce4u7yznyh__uzsfmtmniifsjgesce5u7yznyh', {force: true}).wait(TIMEOUTS.HALF_SEC);
cy.findByText('Create channel').should('be.visible').click();
// * Assert the error occurred with the appropriate message
cy.get('.genericModalError').should('be.visible').within(() => {
cy.findByText('Channel names can\'t be in a hexadecimal format. Please enter a different channel name.');
});
// # Close the create channel modal
cy.uiCancelButton().click();
});
});
it('MM-T884_3 Creating a new channel validates against gm-like names being used as channel name', () => {
// # click on create public channel
cy.uiBrowseOrCreateChannel('Create New Channel').click();
// * Verify that the new channel modal is visible
cy.findByRole('dialog', {name: 'Create a new channel'}).within(() => {
// # Add the new channel name with invalid name and press Create Channel
cy.findByPlaceholderText('Enter a name for your new channel').type('71b03afcbb2d503d49f87f057549c43db4e19f92', {force: true}).wait(TIMEOUTS.HALF_SEC);
cy.uiGetButton('Create channel').click();
// * Assert the error occurred with the appropriate message
cy.get('.genericModalError').should('be.visible').within(() => {
cy.findByText('Channel names can\'t be in a hexadecimal format. Please enter a different channel name.');
});
// # Close the create channel modal
cy.uiCancelButton().click();
});
});
it('MM-T883 Channel URL validation for spaces between characters', () => {
const firstWord = getRandomId(26);
const secondWord = getRandomId(26);
// # In a test channel, click the "v" to the right of the channel name in the header
cy.findByText(`${testChannel.display_name}`).click();
cy.get('#channelHeaderDropdownIcon').click();
// # Select "Rename Channel"
cy.findByText('Rename Channel').click();
// # Change the channel name to {26 alphanumeric characters}[insert 2 spaces]{26 alphanumeric characters}
// i.e. a total of 54 characters separated by 2 spaces
cy.get('#display_name').clear().type(`${firstWord}${Cypress._.repeat(' ', 2)}${secondWord}`);
// # Hit Save
cy.findByText('Save').click();
// * The channel name should be updated to the characters you typed with only 1 space between the characters (extra spaces are trimmed)
cy.get('#channelHeaderTitle').contains(`${firstWord} ${secondWord}`);
// # In a test channel, click the "v" to the right of the channel name in the header
cy.get('#channelHeaderDropdownIcon').click();
// # Select "Rename Channel"
cy.findByText('Rename Channel').click();
// # Change the URL to {26 alphanumeric characters}--{26 alphanumeric characters}
cy.get('#channel_name').clear().type(`${firstWord}${Cypress._.repeat('-', 2)}${secondWord}`);
// # Hit Save
cy.findByText('Save').click();
// * The channel URL should be updated to the characters you typed, separated by 2 dashes
cy.url().should('equal', `${Cypress.config('baseUrl')}/${testTeam.name}/channels/${firstWord}${Cypress._.repeat('-', 2)}${secondWord}`);
});
});

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

@@ -0,0 +1,99 @@
// 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 @channel_settings
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Channel settings', () => {
let mainUser: Cypress.UserProfile;
let otherUser: Cypress.UserProfile;
let myTeam: Cypress.Team;
// # Ensure a list of channel names that will be alphabetically sorted
const channelNames = new Array(20).fill(1).map((value, index) => `scroll${index}`);
before(() => {
// # Create a user and a team (done by apiInitSetup)
cy.apiInitSetup().then(({team, user: firstUser}) => {
mainUser = firstUser;
myTeam = team;
// # Create another user and add it to the same team
cy.apiCreateUser().then(({user: secondUser}) => {
otherUser = secondUser;
cy.apiAddUserToTeam(team.id, secondUser.id);
});
// # Create 20 channels (based on length of channelNames array) to ensure that the channels list is scrollable
cy.wrap(channelNames).each((name) => {
const displayName = `channel-${name}`;
cy.apiCreateChannel(team.id, name.toString(), displayName, 'O', '', '', false).then(({channel}) => {
// # Add our 2 created users to each channel so they can both post messages
cy.apiAddUserToChannel(channel.id, mainUser.id);
cy.apiAddUserToChannel(channel.id, otherUser.id);
});
});
});
});
it('MM-T888 Channel sidebar: More unreads', () => {
const firstChannelIndex = 0;
const lastChannelIndex = channelNames.length - 1;
// # Navigate to off-topic channel
cy.apiLogin(mainUser);
cy.visit(`/${myTeam.name}/channels/off-topic`);
// # Post message as the second user, in a channel near the top of the list
cy.apiGetChannelByName(myTeam.name, channelNames[firstChannelIndex]).then(({channel}) => {
cy.postMessageAs({
sender: otherUser,
message: 'Bleep bloop I am a robot',
channelId: channel.id,
});
// # Scroll down in channels list until last created channel is visible
cy.get(`#sidebarItem_${channelNames[lastChannelIndex]}`).scrollIntoView({duration: TIMEOUTS.TWO_SEC});
cy.get('.scrollbar--view').scrollTo('bottom');
});
// * After scrolling is complete, "More Unreads" pill should be visible at the top of the channels list
cy.get('#unreadIndicatorBottom').should('not.be.visible');
// * "More Unreads" pill should be visible at the top of the channels list
// # Click on "More Unreads" pill
cy.get('#unreadIndicatorTop').should('be.visible').click();
// # Post as another user in a channel near the bottom of the list, scroll channels list to view it (should be in bold)
cy.apiGetChannelByName(myTeam.name, channelNames[lastChannelIndex]).then(({channel}) => {
cy.postMessageAs({
sender: otherUser,
message: 'Bleep bloop I am a robot',
channelId: channel.id,
});
// # Scroll down in channels list until last created channel is visible
cy.get(`#sidebarItem_${channelNames[firstChannelIndex]}`).scrollIntoView({duration: TIMEOUTS.TWO_SEC});
cy.get('.scrollbar--view').scrollTo('top');
});
// * After scrolling is complete, "More Unreads" pill should not be visible at the top of the channels list
cy.get('#unreadIndicatorTop').should('not.be.visible');
// * "More Unreads" pill should be visible at the bottom of the channels list
// # Click on "More Unreads" pill
cy.get('#unreadIndicatorBottom').should('be.visible').click();
// * "More Unreads" pill should not be visible at the bottom of the channels list & visible at the top
cy.get('#unreadIndicatorBottom').should('not.be.visible');
cy.get('#unreadIndicatorTop').should('be.visible');
});
});

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше