Move /e2e -> /e2e-tests
Этот коммит содержится в:
76
e2e-tests/cypress/tests/integration/boards/card_badges_spec.ts
Обычный файл
76
e2e-tests/cypress/tests/integration/boards/card_badges_spec.ts
Обычный файл
@@ -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();
|
||||
};
|
||||
});
|
||||
118
e2e-tests/cypress/tests/integration/boards/card_urlproperty_spec.ts
Обычный файл
118
e2e-tests/cypress/tests/integration/boards/card_urlproperty_spec.ts
Обычный файл
@@ -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');
|
||||
};
|
||||
});
|
||||
222
e2e-tests/cypress/tests/integration/boards/create_board_spec.ts
Обычный файл
222
e2e-tests/cypress/tests/integration/boards/create_board_spec.ts
Обычный файл
@@ -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');
|
||||
});
|
||||
});
|
||||
104
e2e-tests/cypress/tests/integration/boards/manage_groups_spec.ts
Обычный файл
104
e2e-tests/cypress/tests/integration/boards/manage_groups_spec.ts
Обычный файл
@@ -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('You’re 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);
|
||||
});
|
||||
});
|
||||
300
e2e-tests/cypress/tests/integration/channels/autocomplete/helpers.ts
Обычный файл
300
e2e-tests/cypress/tests/integration/channels/autocomplete/helpers.ts
Обычный файл
@@ -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');
|
||||
});
|
||||
});
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user