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

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

@@ -0,0 +1,39 @@
// 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
describe('Mention self', () => {
let testUser;
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl, user}) => {
testUser = user;
cy.visit(offTopicUrl);
});
});
it('should be always highlighted', () => {
[
`@${testUser.username} `,
`@${testUser.username}. `,
`@${testUser.username}_ `,
`@${testUser.username}- `,
`@${testUser.username}, `,
].forEach((message) => {
cy.postMessage(message);
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).find('.mention--highlight');
});
});
});
});

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

@@ -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: @channels @messaging
describe('Identical Message Drafts', () => {
let testTeam;
let testChannelA;
let testChannelB;
before(() => {
// # Login as test user and visit test channel
cy.apiInitSetup({
loginAfter: true,
channelPrefix: {name: 'ca', displayName: 'CB'},
}).then(({team, channel}) => {
testTeam = team;
testChannelA = channel;
cy.apiCreateChannel(testTeam.id, 'cb', 'CB').then((out) => {
testChannelB = out.channel;
});
cy.visit(`/${testTeam.name}/channels/${testChannelA.name}`);
cy.postMessage('hello');
});
});
it('MM-T132 Identical Message Drafts - Autocomplete shown in each channel', () => {
// # Start a draft in Channel A containing just "@"
cy.uiGetPostTextBox().type('@');
// * At mention auto-complete appears in Channel A
cy.get('#suggestionList').should('be.visible');
// # Go to test Channel B on sidebar
cy.get(`#sidebarItem_${testChannelB.name}`).should('be.visible').click();
// * Validate if the newly navigated channel is open
// * autocomplete should not be visible in channel
cy.url().should('include', `/channels/${testChannelB.name}`);
cy.get('#suggestionList').should('not.exist');
// # Start a draft in Channel B containing just "@"
cy.uiGetPostTextBox().type('@');
// * At mention auto-complete appears in Channel B
cy.get('#suggestionList').should('be.visible');
// # Go to Channel C then back to test Channel A on sidebar
cy.get('#sidebarItem_off-topic').should('be.visible').click();
cy.get(`#sidebarItem_${testChannelA.name}`).should('be.visible').click();
// * Validate if the channel has been opened
// * At mention auto-complete is preserved in Channel A
cy.url().should('include', `/channels/${testChannelA.name}`);
cy.uiGetPostTextBox();
cy.get('#suggestionList').should('be.visible');
});
});

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

@@ -0,0 +1,326 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getAdminAccount} from '../../../support/env';
describe('autocomplete', () => {
let testTeam;
let testChannel;
let testUser;
let otherUser;
let otherUser2;
let notInChannelUser;
let sysadmin;
let displayNameTestUser;
let displayNameOtherUser;
before(() => {
sysadmin = getAdminAccount();
// # Login as admin and visit town-square
cy.apiInitSetup({channelPrefix: {name: 'ask-anything', displayName: 'Ask Anything'}}).then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
displayNameTestUser = `${testUser.first_name} ${testUser.last_name} (${testUser.nickname})`;
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
});
displayNameOtherUser = `${otherUser.first_name} ${otherUser.last_name} (${otherUser.nickname})`;
});
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
otherUser2 = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser2.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser2.id);
});
});
cy.apiCreateUser({prefix: 'notinchannel'}).then(({user: user1}) => {
notInChannelUser = user1;
cy.apiAddUserToTeam(testTeam.id, notInChannelUser.id);
});
});
});
it('MM-T2199 @ autocomplete - username', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Clear then type @ and user name
cy.uiGetPostTextBox().clear().type(`@${testUser.username}`);
// * Verify that the item is displayed or not as expected.
cy.get('#suggestionList').should('be.visible').within(() => {
cy.findByText(displayNameTestUser).should('be.visible');
cy.findByText(displayNameOtherUser).should('not.exist');
});
// # Post user mention
cy.uiPostMessageQuickly(`@${testUser.username} `);
cy.uiWaitUntilMessagePostedIncludes(testUser.username);
// # Check that the user name has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', `${testUser.username}`);
});
});
it('MM-T2200 @ autocomplete - nickname', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Clear then type @ and user nickname
cy.uiGetPostTextBox().clear().type(`@${testUser.nickname}`);
// * Verify that the item is displayed or not as expected.
cy.get('#suggestionList').within(() => {
cy.findByText(displayNameTestUser).should('be.visible');
cy.findByText(displayNameOtherUser).should('not.exist');
});
// # Post user mention
cy.uiGetPostTextBox().type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes(testUser.username);
// # Check that the user name has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', `${testUser.username}`);
});
});
it('MM-T2201 @ autocomplete - first name', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Clear then type @ and user first names
cy.uiGetPostTextBox().clear().type(`@${testUser.first_name}`);
// * Verify that the item is displayed or not as expected.
cy.get('#suggestionList').within(() => {
cy.findByText(displayNameTestUser).should('be.visible');
cy.findByText(displayNameOtherUser).should('not.exist');
});
// # Post user mention
cy.uiGetPostTextBox().type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes(testUser.username);
// # Check that the user name has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', `${testUser.username}`);
});
});
it('MM-T2203 @ autocomplete - not email', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Clear then type @ and user email
cy.uiGetPostTextBox().clear().type(`@${testUser.email}`);
// * Verify that the item is displayed or not as expected.
cy.get('#suggestionList').should('not.exist');
});
it('MM-T2206 @ autocomplete - not in channel (center), have permission to add (public channel)', () => {
const message = `@${notInChannelUser.username} did not get notified by this mention because they are not in the channel. Would you like to add them to the channel? They will have access to all message history.`;
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Clear then type @
cy.uiGetPostTextBox().clear().type('@');
// * Verify that the suggestion list is visible
cy.get('#suggestionList').should('be.visible');
// # Type user name
cy.uiGetPostTextBox().type(notInChannelUser.username);
// # Post user mention
cy.uiGetPostTextBox().type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes('They will have access to all message history.');
cy.getLastPostId().then((postId) => {
// * Verify that the correct system message is displayed or not
cy.get(`#postMessageText_${postId}`).should('include.text', message);
// * Click on the link to add the user to the channel
cy.get('a.PostBody_addChannelMemberLink').should('be.visible').click();
cy.uiWaitUntilMessagePostedIncludes('added to the channel by you');
// * Verify that the correct system message is displayed or not
cy.getLastPostId().then((npostId) => {
cy.get(`#postMessageText_${npostId}`).should('include.text', `@${notInChannelUser.username} added to the channel by you`);
});
});
});
it('MM-T2207 Added to channel from autocomplete not in channel', () => {
let tempUser;
// # Create a temporary user
cy.apiCreateUser({prefix: 'temp'}).then(({user: user1}) => {
tempUser = user1;
cy.apiAddUserToTeam(testTeam.id, tempUser.id);
cy.apiLogin(testUser).then(() => {
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Clear then type @
cy.uiGetPostTextBox().clear().type('@');
// * Verify that the suggestion list is visible
cy.get('#suggestionList').should('be.visible');
// # Type user name
cy.uiGetPostTextBox().type(tempUser.username);
// # Post user name mention
cy.uiGetPostTextBox().type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes('They will have access to all message history.');
// * Verify that the correct system message is displayed
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('include.text', `@${tempUser.username} did not get notified by this mention because they are not in the channel. Would you like to add them to the channel? They will have access to all message history.`);
// * Click on the link to add the user to the channel
cy.get('a.PostBody_addChannelMemberLink').should('be.visible').click();
cy.uiWaitUntilMessagePostedIncludes('added to the channel by you');
});
cy.get('#sidebarItem_off-topic', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').click();
cy.apiLogout().then(() => {
cy.apiLogin(tempUser).then(() => {
cy.visit(`/${testTeam.name}`);
// # Verify the mention notification exists
cy.get(`#sidebarItem_${testChannel.name}`).
scrollIntoView().
find('#unreadMentions').
should('be.visible').
and('have.text', '1');
// * Navigate to the channel the user has been added to
cy.get(`#sidebarItem_${testChannel.name}`).click();
cy.uiWaitUntilMessagePostedIncludes('You were added to the channel');
// * Verify that the correct system message is displayed or not
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('include.text', `You were added to the channel by @${testUser.username}`);
});
cy.apiLogout().then(() => {
// # Login as admin to restore the original state
cy.apiLogin(sysadmin);
});
});
});
});
});
});
it('MM-T2209 @ autocomplete - not in DM, GM', () => {
const userGroupIds = [testUser.id, otherUser.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}`);
// # Clear then type @
cy.uiGetPostTextBox().clear().type('@');
// * Verify that the suggestion list is visible
cy.get('#suggestionList').should('be.visible');
// # Type user name
cy.uiGetPostTextBox().type(notInChannelUser.username);
// # Post user name mention
cy.uiGetPostTextBox().type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes(notInChannelUser.username);
// # Check that the user name has been posted
cy.getLastPostId().then((postId) => {
// * Verify that the correct system message is displayed or not
cy.get(`#postMessageText_${postId}`).should('not.include.text', `@${notInChannelUser.username} did not get notified by this mention because they are not in the channel. Would you like to add them to the channel? They will have access to all message history.`);
// # Check that the user name has been posted
cy.get(`#postMessageText_${postId}`).should('contain', `${notInChannelUser.username}`);
// * Verify that the @ mention is a link
cy.get(`#postMessageText_${postId}`).find('.mention-link').should('exist');
});
});
cy.apiCreateDirectChannel([testUser.id, otherUser.id]).then(() => {
// # Visit the channel using the channel name
cy.visit(`/${testTeam.name}/channels/${testUser.id}__${otherUser.id}`);
// # Clear then type @
cy.uiGetPostTextBox().clear().type('@');
// * Verify that the suggestion list is visible
cy.get('#suggestionList').should('be.visible');
// # Type user name
cy.uiGetPostTextBox().type(notInChannelUser.username);
cy.uiGetPostTextBox().type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes(notInChannelUser.username);
cy.getLastPostId().then((postId) => {
// * Verify that the correct system message is displayed or not
cy.get(`#postMessageText_${postId}`).should('not.include.text', `@${notInChannelUser.username} did not get notified by this mention because they are not in the channel. Would you like to add them to the channel? They will have access to all message history.`);
// # Check that the user name has been posted
cy.get(`#postMessageText_${postId}`).should('contain', `${notInChannelUser.username}`);
// * Verify that the @ mention is a link
cy.get(`#postMessageText_${postId}`).find('.mention-link').should('exist');
});
});
});
it('MM-T2212 @ mention followed by dot or underscore should highlight', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Type input suffixed with '.'
cy.uiGetPostTextBox().clear().type(`@${sysadmin.username}.`).type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes(sysadmin.username);
cy.getLastPostId().then((postId) => {
// # Check that the user name has been posted
cy.get(`#postMessageText_${postId}`).should('contain', `${sysadmin.username}`);
// * Verify that the group mention does have colored text
cy.get(`#postMessageText_${postId}`).
findByText(`@${sysadmin.username}`).should('have.class', 'mention-link').
parent().should('have.class', 'mention--highlight');
});
// # Type input suffixed with '_'
cy.uiGetPostTextBox().clear().type(`@${sysadmin.username}_`).type('{enter}{enter}');
cy.uiWaitUntilMessagePostedIncludes(sysadmin.username);
cy.getLastPostId().then((postId) => {
// # Check that the user name has been posted
cy.get(`#postMessageText_${postId}`).should('contain', `${sysadmin.username}`);
// * Verify that the @ mention does have colored text
cy.get(`#postMessageText_${postId}`).
findByText(`@${sysadmin.username}`).should('have.class', 'mention-link').
parent().should('have.class', 'mention--highlight');
});
});
});

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

@@ -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 @messaging
describe('Messaging', () => {
let testTeam;
let testChannel;
let testUser;
before(() => {
// # Login as admin and visit town-square
cy.apiInitSetup({channelPrefix: {name: 'ask-anything', displayName: 'Ask Anything'}}).then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/channels/town-square`);
});
it('MM-T1662_1 Autocomplete should match entries with spaces', () => {
const {name, display_name: displayName} = testChannel;
[
{input: `${name}`, isChannel: true, expected: displayName, case: 'should match on ~channelname'},
{input: `${displayName}`, isChannel: true, expected: displayName, case: 'should match on ~channeldisplayname with space'},
{input: `${displayName.toLowerCase()}`, isChannel: true, expected: displayName, case: 'should match on lowercase ~channeldisplayname'},
{input: 'Ask Any', isChannel: true, expected: displayName, case: 'should match on partial ~channeldisplayname'},
{input: 'Ask Anything ', isChannel: true, expected: displayName, case: 'should match on partial ~channeldisplayname'},
].forEach((testCase) => {
verifySuggestionList(testCase);
});
});
it('MM-T1662_2 Autocomplete should match entries with spaces', () => {
const displayName = `${testUser.first_name} ${testUser.last_name} (${testUser.nickname})`;
[
{input: `${testUser.username}`, expected: displayName, case: 'should match on @username'},
{input: `${testUser.first_name.toLowerCase()}`, expected: displayName, case: 'should match on lowercase @firstname'},
{input: `${testUser.last_name.toLowerCase()}`, expected: displayName, case: 'should match on lowercase @lastname'},
{input: `${testUser.first_name}`, expected: displayName, case: 'should match on @firstname'},
{input: `${testUser.last_name}`, expected: displayName, case: 'should match on @lastname'},
{input: `${testUser.first_name} ${testUser.last_name.substring(0, testUser.last_name.length - 6)}`, expected: displayName, case: 'should match on partial @fullName'},
{input: `${testUser.first_name} ${testUser.last_name}`, expected: displayName, case: 'should match on @displayName'},
{input: `${testUser.first_name} ${testUser.last_name} `, withoutSuggestion: true, case: 'should not match on @displayName with trailing space'},
].forEach((testCase) => {
verifySuggestionList(testCase);
});
});
});
function verifySuggestionList({input, isChannel = false, expected, withoutSuggestion}) {
// # Clear then type ~ or @
cy.uiGetPostTextBox().clear().type(isChannel ? '~' : '@');
// * Verify that the suggestion list is visible
cy.get('#suggestionList').should('be.visible');
// # Type input
cy.uiGetPostTextBox().type(input);
// * Verify that the item is displayed or not as expected.
if (withoutSuggestion) {
cy.get('#suggestionList').should('not.exist');
} else {
cy.get('#suggestionList').should('be.visible').within(() => {
cy.findByText(expected).should('be.visible');
});
}
}

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

@@ -0,0 +1,823 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
import * as MESSAGES from '../../../fixtures/messages';
import {isMac} from '../../../utils';
describe('Messaging', () => {
let testTeam;
let testUser;
let otherUser;
const firstMessage = 'Hello';
const message1 = 'message1';
const message2 = 'message2';
const messageX = 'messageX';
const messageWithCodeblock1 = '```{shift}{enter}codeblock1{shift}{enter}```{shift}{enter}';
const messageWithCodeblockTextOnly1 = 'codeblock1';
const messageWithCodeblockIncomplete2 = '```{shift}{enter}codeblock2';
const messageWithCodeblockTextOnly2 = 'codeblock2';
const messageWithCodeblockIncomplete3 = '```{shift}{enter}codeblock3';
const messageWithCodeblockTextOnly3 = 'codeblock3';
before(() => {
let offTopicUrl;
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
offTopicUrl = `/${team.name}/channels/off-topic`;
});
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
}).then(() => {
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
beforeEach(() => {
cy.apiLogin(testUser);
// # Post a new message to ensure there will be a post to click on
cy.uiGetPostTextBox().type(firstMessage).type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
it('MM-T210 Center channel input box doesn\'t overlap with RHS', () => {
// # Change viewport to tablet
cy.viewport('ipad-2');
const maxReplyCount = 15;
// * Check if center channel post text box is focused
cy.uiGetPostTextBox().should('be.focused');
// # Click "Reply"
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// * Check if center channel post text box is not focused
// Although visually post text box is not visible to user,
// cypress still considers it visible so the assertion
// should('not.exist') will fail
cy.uiGetPostTextBox().should('not.be.focused');
// # Post several replies
cy.uiGetReplyTextBox().clear().should('be.visible').as('replyTextBox');
for (let i = 1; i <= maxReplyCount; i++) {
cy.get('@replyTextBox').type(`post ${i}`).type('{enter}');
}
// * Check if "Reply" button is visible
cy.uiGetReply().should('be.visible');
// # Reset the viewport
cy.viewport(1280, 900);
});
it('MM-T712 Editing a post with Ctrl+Enter on for all messages configured', () => {
// # Enable 'Send Messages on CTRL+ENTER > On for all messages' in Settings > Advanced
setSendMessagesOnCtrlEnter('On for all messages');
// # [1] Post message
cy.uiGetPostTextBox().type(message1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has not been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('not.contain', message1);
});
// # [2] Press CTRL+ENTER
cy.typeCmdOrCtrl().type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', message1);
});
// # [3] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(message2).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Edit Post Input is still visible after typing ENTER
cy.get('#edit_textbox').should('be.visible');
// [4] Press CTRL+ENTER
cy.typeCmdOrCtrlForEdit().type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// # [5] Post message with quotes
cy.uiGetPostTextBox().should('be.visible').clear().type(`${messageWithCodeblock1}{enter}`).wait(TIMEOUTS.HALF_SEC);
// * Check that the message has not been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('not.contain', messageWithCodeblockTextOnly1);
});
// # [6] Press CTRL+ENTER
cy.typeCmdOrCtrl().type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly1);
});
// # [7] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(messageWithCodeblock1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Edit Post Input is still visible after typing ENTER
cy.get('#edit_textbox').should('be.visible');
// [8] Press CTRL+ENTER
cy.typeCmdOrCtrlForEdit().type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// # [9] Post message with quotes
cy.uiGetPostTextBox().should('be.visible').clear().type(`${messageWithCodeblockIncomplete2}{enter}`).wait(TIMEOUTS.HALF_SEC);
// * Check that the message has not been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('not.contain', messageWithCodeblockTextOnly2);
});
// # [10] Press CTRL+ENTER
cy.typeCmdOrCtrl().type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly2);
});
// # [11] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(messageWithCodeblockIncomplete2).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Edit Post Input is still visible after typing ENTER
cy.get('#edit_textbox').should('be.visible');
// [12] Press CTRL+ENTER
cy.typeCmdOrCtrlForEdit().type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// # [13] Post message with quotes with caret in the middle
cy.uiGetPostTextBox().should('be.visible').clear().type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}{enter}`).wait(TIMEOUTS.HALF_SEC);
// * Check that the message has not been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('not.contain', messageWithCodeblockTextOnly3);
});
// # [14] Press CTRL+ENTER
// * Post message again (previous one is broken)
cy.uiGetPostTextBox().should('be.visible').clear().type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}`).wait(TIMEOUTS.HALF_SEC);
cy.typeCmdOrCtrl().type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly3);
});
// # [15] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}{enter}`).wait(TIMEOUTS.HALF_SEC);
// * Edit Post Input is still visible after typing ENTER
cy.get('#edit_textbox').should('be.visible');
// [16] Press CTRL+ENTER
// * Post message again (previous one is broken)
cy.get('#edit_textbox').invoke('val', '').type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}`);
cy.typeCmdOrCtrlForEdit().type('{enter}');
});
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly3);
});
});
it('MM-T3448 Editing a post with Ctrl+Enter only for code blocks starting with ``` configured', () => {
// # Enable 'Send Messages on CTRL+ENTER > On only for code blocks starting with ```' in Settings > Advanced
setSendMessagesOnCtrlEnter('On only for code blocks starting with ```');
// # [17] Post message
cy.uiGetPostTextBox().type(message1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', message1);
});
// # [18] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(message2).type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// * Check that the edited message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', message2);
});
// # [19] Post message with quotes
cy.uiGetPostTextBox().should('be.visible').clear().type(messageWithCodeblock1).wait(TIMEOUTS.HALF_SEC);
cy.uiGetPostTextBox().type('{enter}');
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly1);
});
// # [20] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(messageWithCodeblock1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// * Check that the edited message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly1);
});
// # [21] Post message with quotes incomplete
cy.uiGetPostTextBox().clear().type(messageWithCodeblockIncomplete2).wait(TIMEOUTS.HALF_SEC);
cy.uiGetPostTextBox().type('{enter}');
// * Check that the message has not been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('not.contain', messageWithCodeblockTextOnly2);
});
// # [22] Press CTRL+ENTER
cy.typeCmdOrCtrl().type('{enter}');
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly2);
});
// # [23] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(messageWithCodeblockIncomplete2).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Edit Post Input is still visible after typing ENTER
cy.get('#edit_textbox').should('be.visible');
// [24] Press CTRL+ENTER
cy.typeCmdOrCtrlForEdit().type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// * Check that the edited message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly2);
});
// # [25] Post message with quotes with caret in the middle
cy.uiGetPostTextBox().clear().type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}{enter}`).wait(TIMEOUTS.HALF_SEC);
// * Check that the message has not been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('not.contain', messageWithCodeblockTextOnly3);
});
// # [26] Press CTRL+ENTER
// * Post message again (previous one is broken)
cy.uiGetPostTextBox().clear().type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}`).wait(TIMEOUTS.HALF_SEC);
cy.typeCmdOrCtrl().type('{enter}');
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly3);
});
// # [27] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}`).wait(TIMEOUTS.HALF_SEC);
cy.get('#edit_textbox').type('{enter}');
// * Edit Post Input is still visible after typing ENTER
cy.get('#edit_textbox').should('be.visible');
// [28] Press CTRL+ENTER
// * Post message again (previous one is broken)
cy.get('#edit_textbox').invoke('val', '').type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}`).wait(TIMEOUTS.HALF_SEC);
cy.typeCmdOrCtrlForEdit().type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// * Check that the message has been posted
// Bug? - when clocking CTRL+ENTER from edit box, the code block does not appear in center
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly3);
});
});
it('MM-T3449 Editing a post with Ctrl+Enter off for code blocks configured', () => {
// # Enable 'Send Messages on CTRL+ENTER > Off in Settings > Advanced
setSendMessagesOnCtrlEnter('Off');
// # [29] Post message
cy.uiGetPostTextBox().type(message1).wait(TIMEOUTS.HALF_SEC);
cy.uiGetPostTextBox().type('{enter}');
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', message1);
});
// # [30] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(message2).type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// * Check that the edited message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', message2);
});
// # [31] Post message with quotes
cy.uiGetPostTextBox().should('be.visible').clear().type(messageWithCodeblock1).wait(TIMEOUTS.HALF_SEC);
cy.uiGetPostTextBox().type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly1);
});
// # [32] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(messageWithCodeblock1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
});
// * Check that the edited message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly1);
});
// # [33] Post message with quotes incomplete
cy.uiGetPostTextBox().clear().type(messageWithCodeblockIncomplete2).wait(TIMEOUTS.HALF_SEC);
cy.uiGetPostTextBox().type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly2);
});
// # [34] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(messageWithCodeblockIncomplete2).wait(TIMEOUTS.HALF_SEC);
cy.get('#edit_textbox').type('{enter}');
});
// * Check that the edited message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly2);
});
// # [35] Post message with quotes with caret in the middle
cy.uiGetPostTextBox().clear().type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}`).wait(TIMEOUTS.HALF_SEC);
cy.uiGetPostTextBox().type('{enter}');
// * Check that the message has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly3);
});
// # [36] Edit previous post
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox').invoke('val', '').type(`${messageWithCodeblockIncomplete3}{leftArrow}{leftArrow}{leftArrow}`).wait(TIMEOUTS.HALF_SEC);
cy.get('#edit_textbox').type('{enter}');
});
// * Check that the message has been posted
// Bug? - when clocking CTRL+ENTER from edit box, the code block does not appear in center
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', messageWithCodeblockTextOnly3);
});
});
it('MM-T2139 Canceling out of editing a message makes no changes - Center', () => {
// # Post message
cy.postMessage(message1);
cy.getLastPostId().then((postId) => {
// # Click 'Edit' to last post
cy.uiClickPostDropdownMenu(postId, 'Edit');
// * Edit textbox should be visible
cy.get('#edit_textbox').should('be.visible').wait(TIMEOUTS.ONE_SEC);
// # Make no change then press enter
cy.get('#edit_textbox').type('{enter}');
// # Verify that edit textbox no longer exist and last post does not contain "Edited"
cy.get('#edit_textbox').should('not.exist');
cy.get(`#postMessageText_${postId}`).should('contain', message1).and('not.contain', 'Edited');
});
});
it('MM-T2140 Edited message displays edits and "Edited" in center and RHS', () => {
// # Mobile app
cy.viewport('iphone-6');
cy.reload();
// # Post message
cy.uiGetPostTextBox().type(message1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Edit post by opening modal
cy.getLastPostId().then((postId) => {
cy.clickPostDotMenu(postId);
// * Click edit post
cy.get(`#edit_post_${postId}`).scrollIntoView().should('be.visible').click();
cy.get('#edit_textbox').should('be.visible');
// * Update the message and finish editing
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type(message2).wait(TIMEOUTS.HALF_SEC).type('{enter}');
});
cy.getLastPostId().then((postId) => {
// # Verify that the posted message contains "Edited"
cy.get(`#post_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
// # Open the RHS panel
cy.clickPostCommentIcon(postId);
// # Verify that the updated post message in RHS contains "Edited"
cy.get('#rhsContainer').should('be.visible').within(() => {
cy.get(`#rhsPost_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
});
});
// # Web app
cy.viewport(1280, 900);
// # Post message
cy.uiGetPostTextBox().type(message1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Click "Reply"
cy.getLastPostId().then((postId) => {
// # Open the RHS panel
cy.clickPostCommentIcon(postId);
});
// # Edit post by opening modal
cy.getLastPostId().then((postId) => {
cy.clickPostDotMenu(postId, 'RHS_ROOT');
// * Click edit post
cy.get(`#edit_post_${postId}`).scrollIntoView().should('be.visible').click();
cy.get('#edit_textbox').should('be.visible');
// * Update the message and finish editing
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type(message2).wait(TIMEOUTS.HALF_SEC).type('{enter}');
});
cy.getLastPostId().then((postId) => {
// # Verify that the posted message contains "Edited"
cy.get(`#post_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
// # Verify that the updated post message in RHS contains "Edited"
cy.get('#rhsContainer').should('be.visible').within(() => {
cy.get(`#rhsPost_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
});
});
});
it('MM-T2141 Edit non-list to be numbered list', () => {
const messageText = 'Post';
const numberedListTextPart1Prefix = '1. ';
const numberedListTextPart1 = 'One';
const numberedListTextPart2Prefix = '2. ';
const numberedListTextPart2 = new Array(32).fill('Two').join(' ');
// # Post message
cy.uiGetPostTextBox().type(messageText).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Edit post by opening modal
cy.getLastPostId().then((postId) => {
cy.clickPostDotMenu(postId);
// * Click edit post
cy.get(`#edit_post_${postId}`).scrollIntoView().should('be.visible').click();
cy.get('#edit_textbox').should('be.visible');
// * Update the message
cy.get('#edit_textbox', {timeout: TIMEOUTS.HALF_SEC}).invoke('val', '').type(numberedListTextPart1Prefix + numberedListTextPart1).type('{shift}{enter}').type(numberedListTextPart2Prefix + numberedListTextPart2).wait(TIMEOUTS.HALF_SEC);
// * Close the modal
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type('{enter}');
});
// # Ensure the list and two bullets have been rendered
cy.getLastPostId().then(() => {
cy.get('ol.markdown__list').should('be.visible').within(() => {
cy.contains(numberedListTextPart1);
cy.contains(numberedListTextPart2);
});
});
});
it('MM-T2142 Edit code block', () => {
const codeBlockMessage = ' test';
const updateMessageText = ' update';
// # Post message
cy.uiGetPostTextBox().type(codeBlockMessage).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Edit post by opening modal
cy.getLastPostId().then((postId) => {
cy.clickPostDotMenu(postId);
// * Click edit post
cy.get(`#edit_post_${postId}`).scrollIntoView().should('be.visible').click();
cy.get('#edit_textbox').should('be.visible');
// * Update the message
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type(updateMessageText).wait(TIMEOUTS.HALF_SEC);
// * Close the modal
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type('{enter}');
});
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within((el) => {
// # Verify that the message is in a code block
cy.wrap(el).find('.post-code.post-code--wrap').should('have.text', 'test update');
// # Verify that the updated post contains 'Edited'
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
});
});
it('MM-T2144 Up arrow, edit', () => {
// # Post message
cy.uiGetPostTextBox().type(message1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Edit the post by opening modal
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).invoke('val', '').type(message2).wait(TIMEOUTS.HALF_SEC);
// * Close the modal
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type('{enter}');
});
cy.getLastPostId().then((postId) => {
// # Verify that the updated post contains 'Edited'
cy.get(`#post_${postId}`).within((el) => {
// # Verify that the updated post contains updated message
cy.wrap(el).findByText(message2).should('be.visible');
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
});
});
it('MM-T2145 Other user sees "Edited"', () => {
// # Post message
cy.uiGetPostTextBox().type(message1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Edit the post by opening modal
cy.getLastPostId().then(() => {
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type(message2).wait(TIMEOUTS.HALF_SEC);
// * Close the Edit Post Input
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type('{enter}');
});
// # Login as another user
cy.apiLogin(otherUser);
cy.getLastPostId().then((postId) => {
// # Verify that the updated post contains 'Edited'
cy.get(`#post_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
// # Reply to the message
// * Launch reply post modal
cy.clickPostCommentIcon(postId);
cy.get('#rhsContainer').should('be.visible').within(() => {
// # Verify that the updated post in RHS contains 'Edited'
cy.get(`#rhsPost_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
});
});
});
it('MM-T2149 Edit a message in search results RHS', () => {
// # Post a message
cy.postMessage(messageX);
cy.getLastPostId().then((postId) => {
// # Search for the posted message
cy.get('#searchBox').should('be.visible').type(messageX).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Click on post dot menu so we can edit
cy.clickPostDotMenu(postId, 'SEARCH');
cy.get(`#SEARCH_dropdown_${postId}`).should('be.visible').within(() => {
cy.findByText('Edit').click();
});
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// * Update the post message and type ENTER
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).invoke('val', '').type(message2).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Post appears in RHS search results, displays Pinned badge
cy.get(`#searchResult_${postId}`).findByText('Edited').should('exist');
// # Verify that the updated post contains 'Edited'
cy.get(`#post_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
// # Close the modal
cy.findAllByLabelText('Close').should('be.visible').first().click();
});
});
it('MM-T2152 Edit long message - edit box expands to larger size', () => {
// # Post message
cy.uiGetPostTextBox().
clear().
invoke('val', MESSAGES.HUGE).
wait(TIMEOUTS.HALF_SEC).
type(' {backspace}{enter}');
// # Edit post by opening modal
cy.getLastPostId().then((postId) => {
cy.clickPostDotMenu(postId);
// # Click edit post
cy.get(`#edit_post_${postId}`).scrollIntoView().should('be.visible').click();
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// # Check that a scrollbar exists
cy.get('.post--editing__wrapper.scroll').should('be.visible');
// # Update the message
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type(' test').wait(TIMEOUTS.HALF_SEC);
// # finish editing
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type('{enter}');
});
// # Verify that the updated post contains 'Edited'
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within((el) => {
cy.wrap(el).find('.post-edited__indicator').should('have.text', 'Edited');
});
});
});
it('MM-T2204 @ autocomplete from within edit modal', () => {
// # Post message
cy.uiGetPostTextBox().type(message1).type('{enter}').wait(TIMEOUTS.HALF_SEC);
cy.getLastPostId().then((postId) => {
cy.clickPostDotMenu(postId);
// # Click edit post
cy.get(`#edit_post_${postId}`).scrollIntoView().should('be.visible').click();
// * Edit Post Input should appear
cy.get('#edit_textbox').should('be.visible');
// # Mention first two letters of sysadmin user name
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type(' @sy').wait(TIMEOUTS.HALF_SEC);
cy.get('#suggestionList').within(() => {
// # Verify that the sysadmin user name is mentioned
cy.findByText('@sysadmin').should('be.visible').click();
});
// # Close the modal
cy.get('#edit_textbox', {timeout: TIMEOUTS.FIVE_SEC}).type('{enter}');
});
});
});
function setSendMessagesOnCtrlEnter(name) {
// # Open 'Advanced' section of 'Settings' modal
cy.uiOpenSettingsModal('Advanced').within(() => {
// # Open 'Send Messages on Cmd/Ctrl+Enter' setting
cy.findByRole('heading', {name: `Send Messages on ${isMac() ? '⌘+ENTER' : 'CTRL+ENTER'}`}).should('be.visible').click();
// # Click radio button to select
cy.findByRole('radio', {name}).click();
// # Save and close the modal
cy.uiSaveAndClose();
});
}

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

@@ -0,0 +1,231 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Message permalink', () => {
let testTeam;
let testChannel;
let testUser;
let otherUser;
let notInChannelUser;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
});
});
cy.apiCreateUser({prefix: 'notinchannel'}).then(({user: user1}) => {
notInChannelUser = user1;
cy.apiAddUserToTeam(testTeam.id, notInChannelUser.id);
});
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
it('MM-T1630 - "Jump" to convo works every time for a conversation', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Post 25 messages
let index = 0;
for (index = 0; index < 25; index++) {
cy.uiGetPostTextBox().clear().type(String(index)).type('{enter}');
}
// # Search for a message in the current channel
cy.get('#searchBox').clear().type('in:town-square').type('{enter}');
// # Jump to first permalink view (most recent message)
cy.get('.search-item__jump').first().click();
// # Verify that we jumped to the most recent message
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', index - 1);
});
// # Scroll to the first message
cy.getNthPostId(-index).then((postId) => {
// # Scroll into view
cy.get(`#post_${postId}`).scrollIntoView();
});
// # Search for a message in the current channel
cy.get('#searchBox').clear().type('in:town-square').type('{enter}');
// # Jump to first permalink view (most recent message)
cy.get('.search-item__jump').first().click();
// # Verify that we jumped to the last message
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', index - 1);
});
});
it('MM-T2222 - Channel shortlinking - ~ autocomplete', () => {
const publicChannelName = 'town-square';
const publicChannelDisplayName = 'Town Square';
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Clear then type ~ and prefix of channel name
cy.uiGetPostTextBox().clear().type('~' + publicChannelName.substring(0, 3)).wait(TIMEOUTS.HALF_SEC);
// * Verify that the item is displayed or not as expected.
cy.get('#suggestionList').should('be.visible').within(() => {
cy.findByText(publicChannelDisplayName).should('be.visible');
});
// # Post channel mention
cy.uiGetPostTextBox().type('{enter}{enter}');
// # Check that the user name has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', publicChannelDisplayName);
cy.get('a.mention-link').click();
cy.get('#channelHeaderTitle').should('be.visible').should('contain', publicChannelDisplayName);
});
});
it('MM-T2224 - Channel shortlinking - link joins public channel', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Clear then type ~ and prefix of channel name
cy.uiGetPostTextBox().clear().type(`~${testChannel.display_name}`).wait(TIMEOUTS.HALF_SEC);
// * Verify that the item is displayed or not as expected.
cy.get('#suggestionList').within(() => {
cy.findByText(testChannel.display_name).should('be.visible');
});
// # Post channel mention
cy.uiGetPostTextBox().
type('{enter}').
should('contain', testChannel.name).
type('{enter}');
cy.uiWaitUntilMessagePostedIncludes(testChannel.display_name);
cy.apiLogout();
cy.apiLogin(notInChannelUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Check that the channel display name has been posted
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', `${testChannel.display_name}`);
cy.get('a.mention-link').click();
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testChannel.display_name}`);
});
});
it('MM-T2234 - Permalink - auto joins public channel', () => {
cy.visit(`/${testTeam.name}/channels/town-square`);
// # 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);
// # Leave the channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Visit the permalink
cy.visit(permalink);
// # Check that the post message is the correct one
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('contain', 'Test');
});
});
});
it('MM-T2236 - Permalink - does not auto join private channel', () => {
// # Create private channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel', 'P').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);
// # Leave the channel
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.apiLogout();
cy.apiLogin(testUser);
// # Visit the permalink
cy.visit(permalink);
// # Check the error message
cy.findByText('Permalink belongs to a deleted message or to a channel to which you do not have access.').should('be.visible');
});
});
});
it('MM-T3471 - Clicking/tapping channel URL link joins public channel', () => {
let tempUser;
// # Create a temporary user
cy.apiCreateUser({prefix: 'temp'}).then(({user: user1}) => {
tempUser = user1;
cy.apiAddUserToTeam(testTeam.id, tempUser.id);
// # Login as the other user
cy.apiLogout();
cy.apiLogin(otherUser);
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Clear then type channel url
cy.uiGetPostTextBox().clear().type(`${Cypress.config('baseUrl')}/${testTeam.name}/channels/${testChannel.name}`).type('{enter}');
// # Login as the temporary user
cy.apiLogout();
cy.apiLogin(tempUser);
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Check that the channel permalink has been posted
cy.getLastPostId().then(() => {
cy.get('a.markdown__link').click();
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testChannel.display_name}`);
});
});
});
});

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

@@ -0,0 +1,116 @@
// 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 @messaging
import {getAdminAccount} from '../../../support/env';
function demoteUserToGuest(user, admin) {
// # Issue a Request to demote the user to guest
const baseUrl = Cypress.config('baseUrl');
cy.externalRequest({user: admin, method: 'post', baseUrl, path: `users/${user.id}/demote`});
}
function promoteGuestToUser(user, admin) {
// # Issue a Request to promote the guest to user
const baseUrl = Cypress.config('baseUrl');
cy.externalRequest({user: admin, method: 'post', baseUrl, path: `users/${user.id}/promote`});
}
describe('Channel header menu', () => {
const admin = getAdminAccount();
let testUser;
let testTeam;
before(() => {
// # Login as new user, create new team and visit its URL
cy.apiInitSetup({loginAfter: true}).then(({team, user}) => {
testUser = user;
testTeam = team;
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('MM-14490 show/hide properly menu dividers', () => {
// # Go to "/"
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Create new test channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel Test').then(({channel}) => {
// # Select channel on the left hand side
cy.get(`#sidebarItem_${channel.name}`).click();
// * Channel's display name should be visible at the top of the center pane
cy.get('#channelHeaderTitle').should('contain', channel.display_name);
// # Then click it to access the drop-down menu
cy.get('#channelHeaderTitle').click();
// * The dropdown menu of the channel header should be visible;
cy.get('.Menu__content').should('be.visible');
// * The dropdown menu of the channel header should have 3 dividers;
cy.get('.Menu__content').find('.menu-divider:visible').should('have.lengthOf', 3);
// # Demote the user to guest
demoteUserToGuest(testUser, admin);
// # Reload the browser
cy.reload();
// # Then click it to access the drop-down menu
cy.get('#channelHeaderTitle').click();
// * The dropdown menu of the channel header should have 2 dividers because some options have disappeared;
cy.get('.Menu__content').find('.menu-divider:visible').should('have.lengthOf', 2);
// # Promote the guest to user again
promoteGuestToUser(testUser, admin);
// # Reload the browser
cy.reload();
// # Then click it to access the drop-down menu
cy.get('#channelHeaderTitle').click();
// * The dropdown menu of the channel header should have 3 dividers again;
cy.get('.Menu__content').find('.menu-divider:visible').should('have.lengthOf', 3);
});
});
it('MM-24590 should leave channel successfully', () => {
// # Go to "/"
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Create new test channel
cy.apiAdminLogin();
cy.apiCreateChannel(testTeam.id, 'channel-test-leave', 'Channel Test Leave').then(({channel}) => {
// # Reload the browser
cy.reload();
// # Select channel on the left hand side
cy.get(`#sidebarItem_${channel.name}`).click();
// * Channel's display name should be visible at the top of the center pane
cy.get('#channelHeaderTitle').should('contain', channel.display_name);
// # Then click it to access the drop-down menu
cy.get('#channelHeaderTitle').click();
// * The dropdown menu of the channel header should be visible;
cy.get('.Menu__content').should('be.visible');
// # Click the "Leave Channel" option
cy.get('#channelLeaveChannel').click();
// * Should now be in Town Square
cy.get('#channelHeaderInfo').should('be.visible').and('contain', 'Town Square');
});
});
});

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

@@ -0,0 +1,126 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let testTeam;
let testChannel;
let testUser;
let otherUser;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
});
});
it('MM-T179 Channel is removed from Unreads section if user navigates out of it via permalink', () => {
const message = 'Hello' + Date.now();
let permalink;
let postId;
// # Create new DM channel
cy.apiCreateDirectChannel([testUser.id, otherUser.id]).then(() => {
cy.visit(`/${testTeam.name}/messages/@${otherUser.username}`);
// # Post message to use
cy.postMessage(message);
cy.getLastPostId().then((id) => {
postId = id;
permalink = `${Cypress.config('baseUrl')}/${testTeam.name}/pl/${postId}`;
// # Check if ... button is visible in last post right side
cy.get(`#CENTER_button_${postId}`).should('not.be.visible');
// # Click on ... button of last post
cy.clickPostDotMenu(postId);
// # Click on "Copy Link"
cy.uiClickCopyLink(permalink, postId);
// # Post the message on the channel
postMessageOnChannel(testChannel, otherUser, permalink);
// # Change user
cy.apiLogout();
cy.reload();
cy.apiLogin(otherUser);
cy.apiSaveSidebarSettingPreference();
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Check Message is in Unread List
cy.uiGetLhsSection('UNREADS').find('#sidebarItem_' + testChannel.name).
should('be.visible').
and('have.attr', 'aria-label', `${testChannel.display_name.toLowerCase()} public channel 1 mention`);
// # Read the message and click the permalink
clickLink(testChannel);
// * Check if url include the permalink
cy.url().should('include', `/${testTeam.name}/messages/@${testUser.username}/${postId}`);
// * Check if url redirects back to parent path eventually
cy.wait(TIMEOUTS.FIVE_SEC).url().should('include', `/${testTeam.name}/messages/@${testUser.username}`).and('not.include', `/${postId}`);
// # Channel should still be visible
cy.findAllByRole('button', {name: 'CHANNELS'}).first().parent().next().should('be.visible').within(() => {
cy.get('#sidebarItem_' + testChannel.name).
should('be.visible').
and('have.attr', 'aria-label', `${testChannel.display_name.toLowerCase()} public channel`);
});
// * Check the channel is not under the unread channel list
cy.uiGetLhsSection('UNREADS').findByText(testChannel.name).should('not.exist');
// * Check the channel is not marked as unread
cy.uiGetLhsSection('CHANNELS').find('#sidebarItem_' + testChannel.name).invoke('attr', 'aria-label').should('not.include', 'unread');
});
});
});
});
function postMessageOnChannel(channel, user, linkText) {
// # Click on test public channel
cy.get('#sidebarItem_' + channel.name).click({force: true});
cy.wait(TIMEOUTS.HALF_SEC);
// # Paste link on postlist area and mention the other user
cy.postMessage(`@${user.username} ${linkText}`);
// # We add the mentioned user to the channel
cy.findByText('add them to the channel').should('be.visible').click();
}
function clickLink(channel) {
// # Click on test public channel
cy.get('#sidebarItem_' + channel.name).click({force: true});
cy.wait(TIMEOUTS.HALF_SEC);
// # Since the last message is the system message telling us we joined the channel, we take the one previous
cy.getNthPostId(1).then((postId) => {
// # Click on permalink
cy.get(`#postMessageText_${postId} > p > .markdown__link`).scrollIntoView().click();
});
}

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

@@ -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 @messaging
describe('Channel users interactions', () => {
let testTeam;
let testChannel;
let receiver;
let sender;
before(() => {
// # Login as test user
cy.apiInitSetup().then(({team, channel, user}) => {
receiver = user;
testTeam = team;
testChannel = channel;
cy.apiCreateUser().then(({user: user1}) => {
sender = user1;
cy.apiAddUserToTeam(testTeam.id, sender.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, sender.id);
});
});
cy.apiLogin(receiver);
// # Visit a test channel and post a message
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.postMessage('Hello');
});
});
it('MM-T216 Scroll to bottom when sending a message', () => {
// # Go to off-topic channel via LHS
cy.get('#sidebarItem_off-topic').click({force: true});
// # Post a message in test channel by another user
const message = `I\'m messaging!${'\n2'.repeat(30)}`; // eslint-disable-line no-useless-escape
cy.postMessageAs({sender, message, channelId: testChannel.id});
// # Post any message to off-topic channel
const hello = 'Hello';
cy.postMessage(hello);
cy.uiWaitUntilMessagePostedIncludes(hello);
// # Go to test channel where the message is posted
cy.get(`#sidebarItem_${testChannel.name}`).click({force: true});
// * Check that the new message separator is visible
cy.findByTestId('NotificationSeparator').
find('span').
should('be.visible').
and('have.text', 'New Messages');
// # Post a message on current channel
cy.uiGetPostTextBox().clear().type('message123{enter}');
// * Verify that last posted message is visible
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('have.text', 'message123');
});
});
});

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

@@ -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 @messaging
describe('Messaging', () => {
let testUser;
let offTopicUrl;
before(() => {
// # Enable Link Previews
cy.apiUpdateConfig({
ServiceSettings: {
EnableLinkPreviews: true,
},
});
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl: url, user}) => {
testUser = user;
offTopicUrl = url;
// # Save Show Preview Preference to true
// # Save Preview Collapsed Preference to false
cy.apiSaveLinkPreviewsPreference('true');
cy.apiSaveCollapsePreviewsPreference('false');
cy.visit(offTopicUrl);
});
});
it('MM-T199 Link preview - Removing it from my view removes it from other user\'s view', () => {
const message = 'https://www.bbc.com/news/uk-wales-45142614';
// # Post message to use
cy.postMessage(message);
cy.getLastPostId().then((postId) => {
// * Check the preview is shown
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph').should('exist');
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph__image figure').should('exist');
// # Log-in to the other user
cy.apiAdminLogin();
cy.visit(offTopicUrl);
// * Check the preview is shown
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph').should('exist');
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph__image figure').should('exist');
// # Log-in back to the first user
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
// # Collapse the preview
cy.get(`#${postId}_message`).find('.preview-toggle').click({force: true});
// * Check the preview is not shown
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph').should('exist');
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph__image figure').should('not.exist');
// # Log-in to the other user
cy.apiAdminLogin();
cy.visit(offTopicUrl);
// * Check the preview is shown
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph').should('exist');
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph__image figure').should('exist');
// # Log-in back to the first user
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
// # Remove the preview
cy.get(`#${postId}_message`).within(() => {
cy.findByTestId('removeLinkPreviewButton').click({force: true});
});
// * Preview should not exist
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph').should('not.exist');
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph__image figure').should('not.exist');
// # Log-in to the other user
cy.apiAdminLogin();
cy.visit(offTopicUrl);
// * Preview should not exist
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph').should('not.exist');
cy.get(`#${postId}_message`).find('.PostAttachmentOpenGraph__image figure').should('not.exist');
});
});
});

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

@@ -0,0 +1,57 @@
// 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
describe('Long message', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T104 Can `Show More` and `Show Less` on long posts, Markdown in long posts', () => {
// # Post message with kitchen sink markdown text
cy.postMessageFromFile('long_text_post.txt');
// # Get last post
cy.getLastPostId().then((postId) => {
const postMessageId = `#${postId}_message`;
cy.get(postMessageId).within(() => {
// * Verify collapsed post
verifyCollapsedPost(postId);
// # Expand the post
cy.get('#showMoreButton').click();
// * Verify expanded post
verifyExpandedPost(postId);
// # Collapse the post
cy.get('#showMoreButton').click();
// * Verify collapsed post
verifyCollapsedPost(postId);
});
});
});
});
function verifyCollapsedPost() {
// * Verify show more button
cy.get('#showMoreButton').scrollIntoView().should('be.visible').and('have.text', 'Show more');
}
function verifyExpandedPost() {
// * Verify show more button now says 'Show less'
cy.get('#showMoreButton').scrollIntoView().should('be.visible').and('have.text', 'Show less');
}

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

@@ -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 @messaging
import {stubClipboard} from '../../../utils';
describe('Permalink message edit', () => {
before(() => {
cy.apiInitSetup().then(({channelUrl}) => {
// # Go to test channel
cy.visit(channelUrl);
});
});
it('MM-T4688 Copy Text menu item should copy post message to clipboard', () => {
stubClipboard().as('clipboard');
// # Post a message
const message = Date.now().toString();
cy.postMessage(message);
cy.getLastPostId().as('postId1');
// # Post another message
const message2 = Date.now().toString();
cy.postMessage(message2);
cy.getLastPostId().as('postId2');
// # Post several messages so that dropdown menu can be seen when rendered at the bottom (Cypress limitation)
Cypress._.times(10, (n) => {
cy.uiPostMessageQuickly(n);
});
cy.get('@postId1').then((postId) => {
// # Copy message by clicking
cy.uiClickPostDropdownMenu(postId, 'Copy Text');
// * Ensure that the clipboard contents are correct
verifyClipboard(message);
});
cy.get('@postId2').then((postId) => {
// # Copy by keyboard shortcut
cy.clickPostDotMenu(postId, 'CENTER');
cy.get('body').type('c');
// * Ensure that the clipboard contents are correct
verifyClipboard(message2);
});
});
// Instead of external Apps we are testing the clipboard directly, to ensure the copied text is correct
it('MM-T1615 Pasting code block text into external apps does not include line numbers', () => {
stubClipboard().as('clipboard');
const postCodeBlock = '```javascript\nvar foo = "bar"\nfunction doSomething()\nreturn 7;\n}\n```';
const copiedCodeBlockText = 'var foo = "bar"\nfunction doSomething()\nreturn 7;\n}\n';
// # Post the code block
cy.postMessage(postCodeBlock);
cy.getLastPostId().as('postId1');
// # Copy message by clicking 'Copy code Block' icon
cy.get('@postId1').then((postId) => {
cy.get(`#postMessageText_${postId}`).click();
cy.get('i.icon.icon-content-copy').invoke('show').click();
cy.get('@clipboard').its('contents').then((contents) => {
// * Verify clipboard content does not have extra quotes
expect(contents.trim()).to.equal(copiedCodeBlockText.trim());
});
});
});
});
function verifyClipboard(message) {
cy.get('@clipboard').its('wasCalled').should('eq', true);
cy.get('@clipboard').its('contents').should('eq', message);
}

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

@@ -0,0 +1,57 @@
// 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
describe('Messaging', () => {
let testTeam;
let firstUser;
let secondUser;
let thirdUser;
before(() => {
// # Login as test user
cy.apiInitSetup().then(({team, user: user1}) => {
firstUser = user1;
testTeam = team;
// # Create two more users
cy.apiCreateUser().then(({user: user2}) => {
secondUser = user2;
cy.apiAddUserToTeam(testTeam.id, secondUser.id);
});
cy.apiCreateUser().then(({user: user3}) => {
thirdUser = user3;
cy.apiAddUserToTeam(testTeam.id, thirdUser.id);
});
cy.apiLogin(firstUser);
});
});
it('MM-T1226 - CTRL/CMD+K - Find GM by matching username, full name, or nickname, even if that name isn\'t displayed', () => {
// # Create a group channel and add the three users created in the 'before' hook
cy.apiCreateGroupChannel([firstUser.id, secondUser.id, thirdUser.id]).then(({channel}) => {
// # Visit the newly created group message
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Go to off-topic
cy.get('#sidebarItem_off-topic').click();
// # Type either cmd+K / ctrl+K depending on OS and type in the first 7 of the second user's last name
cy.uiGetPostTextBox().cmdOrCtrlShortcut('K');
cy.focused().type(secondUser.last_name.slice(0, 7));
// * The suggestion for the GM channel with the user that was searched for should show the username of the user and verify the suggestion for the GM channel with the user that was searched for should show the 'G' text
cy.get('.suggestion--selected').should('exist').and('contain.text', secondUser.username).findByText('G').should('exist');
});
});
});

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

@@ -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 @messaging @keyboard_shortcuts
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let firstUser;
let secondUser;
let offTopicUrl;
before(() => {
// # Login as test user
cy.apiInitSetup().then(({team, user, offTopicUrl: url}) => {
firstUser = user;
offTopicUrl = url;
// # Create a second user that will be searched
cy.apiCreateUser().then(({user: user1}) => {
secondUser = user1;
cy.apiAddUserToTeam(team.id, secondUser.id);
});
cy.apiLogin(firstUser);
// # Visit created test team
cy.visit(offTopicUrl);
});
});
it('MM-T1224 - CTRL/CMD+K - Open DM using mouse', () => {
// # Type CTRL/CMD+K
cy.uiGetPostTextBox().cmdOrCtrlShortcut('K');
// # In the "Switch Channels" modal type the first 6 characters of the username
cy.findByRole('textbox', {name: 'quick switch input'}).should('be.focused').type(secondUser.username.substring(0, 6)).wait(TIMEOUTS.HALF_SEC);
// # Verify that the list of users and channels suggestions is present
cy.get('#suggestionList').should('be.visible').within(() => {
// * Newly created username should be there in the search list; click it
cy.findByTestId(secondUser.username).scrollIntoView().should('exist').click().wait(TIMEOUTS.HALF_SEC);
});
// # Verify that we are in a DM channel
cy.get('#channelIntro').should('be.visible').within(() => {
cy.get('.channel-intro-profile').
should('be.visible').
and('have.text', secondUser.username);
cy.get('.channel-intro-text').
should('be.visible').
and('contain', `This is the start of your direct message history with ${secondUser.username}.`).
and('contain', 'Direct messages and files shared here are not shown to people outside this area.');
});
// # Verify that the focus is on the message box
cy.uiGetPostTextBox().should('be.focused');
// # Send a DM
cy.postMessage(`Hi there, ${secondUser.username}!`);
// # Logout then login as the second user and visit the team
cy.apiLogout();
cy.reload();
cy.apiLogin(secondUser);
cy.visit(offTopicUrl);
// * Check that the DM exists and receives the message with mention
cy.uiGetLhsSection('DIRECT MESSAGES').findByLabelText(`${firstUser.username} 1 mention`).should('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 @messaging
import {getAdminAccount} from '../../../support/env';
describe('Messaging', () => {
const admin = getAdminAccount();
let newChannel;
before(() => {
// # Create and visit new channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
newChannel = channel;
cy.apiPatchMe({
locale: 'en',
timezone: {automaticTimezone: '', manualTimezone: 'UTC', useAutomaticTimezone: 'false'},
});
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-21482 Date separators should translate correctly', () => {
function verifyDateSeparator(index, match) {
cy.findAllByTestId('basicSeparator').eq(index).within(() => {
cy.findByText(match);
});
}
// # Post a message with old date
const oldDate = Date.UTC(2019, 0, 5, 12, 30); // Jan 5, 2019 12:30pm
cy.postMessageAs({sender: admin, message: 'Hello from Jan 5, 2019 12:30pm', channelId: newChannel.id, createAt: oldDate});
// # Post message from 4 days ago
const ago4 = Cypress.dayjs().subtract(4, 'days').valueOf();
cy.postMessageAs({sender: admin, message: 'Hello from 4 days ago', channelId: newChannel.id, createAt: ago4});
// # Post message from yesterday
const yesterdaysDate = Cypress.dayjs().subtract(1, 'days').valueOf();
cy.postMessageAs({sender: admin, message: 'Hello from yesterday', channelId: newChannel.id, createAt: yesterdaysDate});
// # Post a message for today
cy.postMessage('Hello from today');
// # Reload to re-arrange post order
cy.reload();
// * Verify that the date separators are rendered in English
verifyDateSeparator(0, /^January (04|05), 2019/);
//! cannot test for MMMM DD format as it is current-year dependent, need fixed-time comparison
verifyDateSeparator(1, /^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)$/);
verifyDateSeparator(2, 'Yesterday');
verifyDateSeparator(3, 'Today');
// # Change user locale to "es" and reload
cy.apiPatchMe({locale: 'es'});
cy.reload();
// * Verify that the date separators are rendered in Spanish
verifyDateSeparator(0, /^(04|05) de enero de 2019/);
verifyDateSeparator(1, /^(lunes|martes|miércoles|jueves|viernes|sábado|domingo)$/);
verifyDateSeparator(2, 'Ayer');
verifyDateSeparator(3, 'Hoy');
// # Change user timezone which is not supported by react-intl and reload
cy.apiPatchMe({timezone: {automaticTimezone: '', manualTimezone: 'NZ-CHAT', useAutomaticTimezone: 'false'}});
cy.reload();
// * Verify that it renders in "es" locale
verifyDateSeparator(0, /^(04|05) de enero de 2019/);
});
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {spyNotificationAs} from '../../../support/notification';
describe('Direct Message', () => {
let testTeam;
let testUser;
let otherUser;
let townsquareLink;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
townsquareLink = `/${team.name}/channels/town-square`;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
});
});
});
beforeEach(() => {
cy.apiLogin(testUser);
cy.visit(townsquareLink);
});
it('MM-T449 - Edit a direct message body', () => {
const originalMessage = 'Hello';
const editedMessage = 'Hello World';
// Sent a DM that can be edited
cy.apiCreateDirectChannel([testUser.id, otherUser.id]).then(({channel}) => {
// Have another user send you a DM
cy.postMessageAs({sender: testUser, message: originalMessage, channelId: channel.id}).wait(TIMEOUTS.HALF_SEC);
}).apiLogout().wait(TIMEOUTS.HALF_SEC);
cy.apiLogin(otherUser).then(() => {
// # Visit the DM channel
cy.visit(`/${testTeam.name}/messages/@${testUser.username}`);
// * Verify message is sent and not pending
cy.getLastPostId().then((postId) => {
const postText = `#postMessageText_${postId}`;
cy.get(postText).should('have.text', originalMessage);
});
}).apiLogout().wait(TIMEOUTS.HALF_SEC);
cy.apiLogin(testUser).then(() => {
// # Visit the DM channel
cy.visit(`/${testTeam.name}/messages/@${otherUser.username}`);
// # Edit the last post
cy.uiGetPostTextBox();
cy.uiGetPostTextBox().clear().type('{uparrow}');
// * Edit post Input should appear, and edit the post
cy.get('#edit_textbox').should('be.visible');
cy.get('#edit_textbox').should('have.text', originalMessage).type(' World{enter}');
cy.get('#edit_textbox').should('not.exist');
// * Verify that last post does contain "Edited"
cy.getLastPostId().then((postId) => {
const postEdited = `#postEdited_${postId}`;
cy.get(postEdited).should('be.visible').and('have.text', 'Edited');
});
}).apiLogout().wait(TIMEOUTS.HALF_SEC);
cy.apiLogin(otherUser).then(() => {
// # Visit the DM channel
cy.visit(`/${testTeam.name}/messages/@${testUser.username}`);
// * Should not have unread mentions indicator.
cy.get('#sidebarItem_off-topic').
scrollIntoView().
find('#unreadMentions').
should('not.exist');
// * Verify message is sent and not pending
cy.getLastPostId().then((postId) => {
const postText = `#postMessageText_${postId}`;
const postEdited = `#postEdited_${postId}`;
// * Check the post and verify that it contains new edited message.
cy.get(postText).should('have.text', `${editedMessage} Edited`);
cy.get(postEdited).should('be.visible');
});
}).apiLogout().wait(TIMEOUTS.HALF_SEC);
});
it('MM-T457 - Self direct message', () => {
// # Stub notifications API
spyNotificationAs('withNotification', 'granted');
// # Open DM modal
cy.uiAddDirectMessage().click().wait(TIMEOUTS.HALF_SEC);
// # Search for your username
cy.get('#selectItems input').
typeWithForce(testUser.username).
wait(TIMEOUTS.HALF_SEC);
// * Verify username shows up on search
cy.get(`#displayedUserName${testUser.username}`).should('be.visible');
// # Click on username
cy.get(`#displayedUserName${testUser.username}`).click().wait(TIMEOUTS.HALF_SEC);
// * Verify top header
cy.get('#channelHeaderTitle').should('contain', `${testUser.username} (you)`);
// # Post a message
cy.postMessage('todo list for today: 1,2,3');
// * Desktop notification is not received
cy.wait(TIMEOUTS.HALF_SEC);
cy.get('@withNotification').should('not.have.been.called');
});
it('MM-T458 - Edit direct message channel header', () => {
// # Create a DM channel
cy.apiCreateDirectChannel([testUser.id, otherUser.id]).then(({channel}) => {
// Have another user send you a DM.
cy.postMessageAs({sender: otherUser, message: 'Hello', channelId: channel.id}).wait(TIMEOUTS.HALF_SEC);
});
// # Visit the DM channel
cy.visit(`/${testTeam.name}/messages/@${otherUser.username}`);
// # Click on the channel header
cy.get('#channelHeaderTitle').click().wait(TIMEOUTS.HALF_SEC);
// # Click on 'Edit Channel Header'
cy.get('#channelEditHeader').click().wait(TIMEOUTS.HALF_SEC);
// # Fill and save channel header changes
const message = 'This is a line{shift}{enter}{shift}{enter}This is another line';
const expectedMessage = 'This is a line\n\nThis is another line';
cy.get('#edit_textbox').type(message).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// # Hover on channel header
cy.get('#channelHeaderDescription .header-description__text').trigger('mouseover');
// * Verify changes have been applied on header
cy.wait(TIMEOUTS.HALF_SEC);
cy.get('#header-popover .popover-content').should('be.visible');
cy.get('#header-popover').find('.popover-content').should(($el) => {
expect($el.get(0).innerText).to.eq(expectedMessage);
});
});
it('MM-T1536 - Mute & Unmute', () => {
// # Create a DM channel
cy.apiCreateDirectChannel([testUser.id, otherUser.id]).then(({channel}) => {
// Have another user send you a DM.
cy.postMessageAs({sender: otherUser, message: 'Hello', channelId: channel.id}).wait(TIMEOUTS.HALF_SEC);
});
// # Visit the DM channel
cy.visit(`/${testTeam.name}/messages/@${otherUser.username}`);
// # Open channel menu and click Mute Conversation
cy.uiOpenChannelMenu('Mute Conversation');
// * Assert that channel appears as muted on the LHS
cy.uiGetLhsSection('DIRECT MESSAGES').find('.muted').first().should('contain', otherUser.username);
// # Open channel menu and click Unmute Conversation
cy.uiOpenChannelMenu('Unmute Conversation');
// * Assert that channel does not appear as muted on the LHS
cy.uiGetLhsSection('DIRECT MESSAGES').find('.muted').should('not.exist');
});
});

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

@@ -0,0 +1,83 @@
// 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 @not_cloud
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let testTeam;
let testUser;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
it('MM-T1665_1 Deactivated user is not shown in Direct Messages modal when no previous conversation', () => {
// # Create new user, add to team and then deactivate
cy.apiCreateUser().then(({user: deactivatedUser}) => {
cy.apiAddUserToTeam(testTeam.id, deactivatedUser.id);
cy.externalActivateUser(deactivatedUser.id, false);
// # Login as test user and visit town-square
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Click on '+' sign to open DM modal
cy.uiAddDirectMessage().click().wait(TIMEOUTS.ONE_SEC);
// # Search for the deactivated user
cy.findByRole('dialog', {name: 'Direct Messages'}).should('be.visible').wait(TIMEOUTS.ONE_SEC);
cy.findByRole('textbox', {name: 'Search for people'}).typeWithForce(deactivatedUser.email);
// * Verify that the inactive user is not found
cy.get('.no-channel-message').should('be.visible').and('contain', 'No results found matching');
});
});
it('MM-T1665_2 Deactivated user is shown in Direct Messages modal when had previous conversation', () => {
// # Create new user and then deactivate
cy.apiCreateUser().then(({user: deactivatedUser}) => {
cy.apiAddUserToTeam(testTeam.id, deactivatedUser.id);
// # Login as test user and visit town-square
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/messages/@${deactivatedUser.username}`);
// # Post first message in case it is a new Channel
cy.postMessage(`Hello ${deactivatedUser.username}`);
cy.externalActivateUser(deactivatedUser.id, false);
// # Click on '+' sign to open DM modal
cy.uiAddDirectMessage().click();
// * Verify that the DM modal is open
cy.get('#moreDmModal').should('be.visible').contains('Direct Messages');
// # Search for the deactivated user
cy.get('#selectItems input').should('be.focused').typeWithForce(deactivatedUser.email);
// * Verify that the deactivated user is found
cy.get('#moreDmModal .more-modal__row--selected').should('be.visible').
and('contain', deactivatedUser.username).
and('contain', 'Deactivated');
});
});
});

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

@@ -0,0 +1,57 @@
// 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
describe('Messaging', () => {
let testTeam;
let testChannel;
let receiver;
let lastPostId;
before(() => {
// # Login as test user
cy.apiInitSetup().then(({team, channel, user}) => {
receiver = user;
testTeam = team;
testChannel = channel;
cy.apiLogin(receiver);
// # Visit a test channel and post a message
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.postMessage('휴');
// # Assign lastPostId variable to the id of the last post
cy.getLastPostId().then((postId) => {
lastPostId = postId;
});
});
});
it('MM-T1667 - Post with only 2 byte characters shouldn\'t remain after posting', () => {
testChannel.name = testChannel.name.replace(/\s+/g, '-').toLowerCase();
// * Check that the Korean message got posted in the channel
cy.get(`#postMessageText_${lastPostId}`).should('contain', '휴');
// # Change channels to the Town Square channel
cy.get('#sidebarItem_town-square').click();
// * Check that the draft icon does not exist next to the Test Channel name
cy.get(`#sidebarItem_${testChannel.name}`).findByTestId('draftIcon').should('not.exist');
// # Return to the channel where the 2 byte character was posted
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// * Assert that the message textbox is empty
cy.uiGetPostTextBox().should('have.value', '');
});
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Edit Message', () => {
let offTopicUrl;
before(() => {
// # Login as test user
cy.apiInitSetup({loginAfter: true}).then((out) => {
offTopicUrl = out.offTopicUrl;
});
});
beforeEach(() => {
// # Visit town-square
cy.visit(offTopicUrl);
});
it('MM-T121 Escape should not close modal when an autocomplete drop down is in use', () => {
// # Post a message
cy.postMessage('Hello World!');
// # Hit the up arrow to open the "edit modal"
cy.uiGetPostTextBox().type('{uparrow}');
// # In the modal type @
cy.get('#edit_textbox').type(' @');
// * Assert user autocomplete is visible
cy.get('#suggestionList').should('be.visible');
// # Press the escape key
cy.get('#edit_textbox').wait(TIMEOUTS.HALF_SEC).focus().type('{esc}');
// * Check if the textbox contains expected text
cy.get('#edit_textbox').should('have.value', 'Hello World! @');
// * Assert user autocomplete is not visible
cy.get('#suggestionList').should('not.exist');
// # In the modal type ~
cy.get('#edit_textbox').type(' ~');
// * Assert channel autocomplete is visible
cy.get('#suggestionList').should('be.visible');
// # Press the escape key
cy.get('#edit_textbox').wait(TIMEOUTS.HALF_SEC).type('{esc}');
// * Check if the textbox contains expected text
cy.get('#edit_textbox').should('have.value', 'Hello World! @ ~');
// * Assert channel autocomplete is not visible
cy.get('#suggestionList').should('not.exist');
// # In the modal click the emoji picker icon
cy.get('#editPostEmoji').click();
// * Assert emoji picker is visible
cy.get('#emojiPicker').should('be.visible');
// * Press the escape key
cy.get('#emojiPickerSearch').wait(TIMEOUTS.HALF_SEC).type('{esc}');
// * Assert emoji picker is not visible
cy.get('#emojiPicker').should('not.exist');
});
it('MM-T102 Timestamp on edited post shows original post time', () => {
// # Post a message
cy.postMessage('Checking timestamp');
cy.getLastPostId().then((postId) => {
// # Mouseover post to display the timestamp
cy.get(`#post_${postId}`).trigger('mouseover');
cy.get(`#CENTER_time_${postId}`).find('time').invoke('attr', 'dateTime').then((originalTimeStamp) => {
// # Click dot menu
cy.clickPostDotMenu(postId);
// # Click the edit button
cy.get(`#edit_post_${postId}`).click();
// # Edit the post
cy.get('#edit_textbox').type('Some text {enter}');
// # Mouseover the post again
cy.get(`#post_${postId}`).trigger('mouseover');
// * Current post timestamp should have not been changed by edition
cy.get(`#CENTER_time_${postId}`).find('time').should('have.attr', 'dateTime').and('equal', originalTimeStamp);
// # Open RHS by clicking the post comment icon
cy.clickPostCommentIcon(postId);
// * Check that the RHS is open
cy.get('#rhsContainer').should('be.visible');
// * Check that the RHS timeStamp equals the original post timeStamp
cy.get(`#CENTER_time_${postId}`).find('time').invoke('attr', 'dateTime').should('equal', originalTimeStamp);
});
});
});
it('MM-T97 Open edit modal immediately after making a post', () => {
// # Enter first message
const firstMessage = 'Hello';
cy.postMessage(firstMessage);
// * Verify first message is sent and not pending
cy.getLastPostId().then((postId) => {
const postText = `#postMessageText_${postId}`;
cy.get(postText).should('have.text', firstMessage);
});
// # Enter second message
const secondMessage = 'World!';
cy.postMessage(secondMessage);
// * Verify second message is sent and not pending
cy.getLastPostId().then((postId) => {
const postText = `#postMessageText_${postId}`;
cy.get(postText).should('have.text', secondMessage);
// # Edit the last post
cy.uiGetPostTextBox().type('{uparrow}');
// * Edit Post Input should appear, and edit the post
cy.get('#edit_textbox').should('be.visible');
cy.get('#edit_textbox').should('have.text', secondMessage).type(' Another new message{enter}');
cy.get('#edit_textbox').should('not.exist');
// * Check the second post and verify that it contains new edited message.
cy.get(postText).should('have.text', `${secondMessage} Another new message Edited`);
});
});
});

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

@@ -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 @messaging
function emojiVerification(postId) {
// # set the postMessageTextId var
const postMessageTextId = `#postMessageText_${postId}`;
// * Check for the emoji attr of :) is exists
cy.get(`${postMessageTextId} p span span.emoticon`).should('have.attr', 'title', ':slightly_smiling_face:');
// * Check for the punctuation('=') is exists without space
cy.get(`${postMessageTextId} p`).should('same.text', ':slightly_smiling_face:=');
}
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T222 Emoji characters followed by punctuation', () => {
// # Post a message
const messageText = ':)=';
cy.postMessage(messageText);
// # Get Last Post ID
cy.getLastPostId().then((postId) => {
emojiVerification(postId);
});
});
});

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

@@ -0,0 +1,33 @@
// 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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T1539 Gendered emojis are rendered with the correct gender', () => {
// # Post a man-gesturing-ok emoji
cy.postMessage('🙆‍♂️');
// # Assert posted emoji was rendered as man
cy.findByTitle(':man-gesturing-ok:').should('be.visible');
// # Post a man-gesturing-ok emoji
cy.postMessage('🙆‍♀️');
// # Assert posted emoji was rendered as man
cy.findByTitle(':woman-gesturing-ok:').should('be.visible');
});
});

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

@@ -0,0 +1,41 @@
// 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
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T95 Selecting an emoji from emoji picker should insert it at the cursor position', () => {
// # Write some text in the send box.
cy.uiGetPostTextBox().type('HelloWorld!');
// # Move the cursor to the middle of the text.
cy.uiGetPostTextBox().type('{leftarrow}{leftarrow}{leftarrow}{leftarrow}{leftarrow}{leftarrow}');
// # Open emoji picker
cy.uiOpenEmojiPicker();
// # Select the grinning emoji from the emoji picker.
cy.clickEmojiInEmojiPicker('grinning');
// * The emoji should be inserted where the cursor is at the time of selection.
cy.uiGetPostTextBox().should('have.value', 'Hello :grinning: World!');
cy.uiGetPostTextBox().type('{enter}');
// * The emoji should be displayed in the post at the position inserted.
cy.getLastPost().find('p').should('have.html', `Hello <span data-emoticon="grinning"><span alt=":grinning:" class="emoticon" title=":grinning:" style="background-image: url(&quot;${Cypress.config('baseUrl')}/static/emoji/1f600.png&quot;);">:grinning:</span></span> World!`);
});
});

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

@@ -0,0 +1,111 @@
// 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
describe('MM-T154 Use keyboard navigation in emoji picker', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
beforeEach(() => {
// # Open emoji picker
cy.uiOpenEmojiPicker();
// # Wait for emoji picker to load
cy.get('#emojiPicker').should('be.visible');
});
['right', 'down'].forEach((dir) => {
it(`${dir} keypress should select the first emoji on start without prior selection`, () => {
// # Type arrow key
cy.get('#emojiPickerSearch').type(`{${dir}arrow}`);
// * The first emoji in the "People" category should be selected
testSelectedIndex((idx) => expect(idx).to.equal(0));
// # Close emoji picker
cy.get('#emojiPickerButton').click();
});
});
['left', 'up'].forEach((dir) => {
it(`${dir} keypress should select nothing on start without prior selection`, () => {
// # Type arrow key
cy.get('#emojiPickerSearch').type(`{${dir}arrow}`);
// * There should be no selection
cy.get('.emoji-picker__item.selected').should('not.exist');
// # Close emoji picker
cy.get('#emojiPickerButton').click();
});
});
it('should select emoji on continuous keypress', () => {
// # Move around with arrow key and verify selected emojis
let lastSelected = null;
const pressCounts = {
right: 5,
down: 5,
left: 4,
up: 5,
};
const checkSelectionId = (selectedEmoji) => {
expect(selectedEmoji).to.not.equal(lastSelected);
lastSelected = selectedEmoji;
};
const checkSelectionSrc = (selectedEmoji) => {
cy.get('#emojiPickerSpritePreview').invoke('attr', 'src').should('equal', selectedEmoji);
};
for (const direction of Object.keys(pressCounts)) {
for (let i = 0; i < pressCounts[direction]; i += 1) {
// # Press arrow key
cy.get('#emojiPickerSearch').type(`{${direction}arrow}`);
// * id of selected emoji should be different from last iteration
cy.get('.emoji-picker__item.selected img').invoke('attr', 'id').then(checkSelectionId);
// * Verify that the selected emoji matches the sprite preview
cy.get('.emoji-picker__item.selected img').invoke('attr', 'src').then(checkSelectionSrc);
}
}
// # Close emoji picker
cy.get('#emojiPickerButton').click();
});
it('should post selected emoji', () => {
// # Press arrow keys
cy.get('#emojiPickerSearch').type('{rightarrow}{downarrow}');
cy.get('.emoji-picker__preview').invoke('text').then((selectedEmoji) => {
// # Select chosen emoji
cy.get('#emojiPickerSearch').type('{enter}');
// # Post message with keyboard
cy.uiGetPostTextBox().type('{enter}');
// * Compare selected emoji with last post
cy.getLastPost().find('.emoticon').should('have.attr', 'title', selectedEmoji);
});
});
});
const testSelectedIndex = (done) => {
cy.get('.emoji-picker__item.selected').then((selectedEmoji) => {
done(selectedEmoji.index());
});
};

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

@@ -0,0 +1,38 @@
// 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
describe('Messaging', () => {
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({townSquareUrl}) => {
cy.visit(townSquareUrl);
});
});
it("MM-T165 Windows: Custom emoji don't overlap", () => {
// # Post a message
const emojis = '😈🤣👘😘😋😋😛🤨😎😏😛🤓😋😖🤨😫😫😚😒😋☹️🤨😒😒🤪😖😋😒😋🤨😏😩🤨😀🤨😇🧐🙃🤨🙃😟😛😔🧐☹️🤬😱😳🤫🤫😥😳🤔😨🤗😢😑🤢🤢🤢🤮🤮😪😑😑🤔😴🤭😵😑😷🤐🤐👙👨‍👧‍👧👨‍👨‍👧‍👦👚👩‍👦‍👦👔👩‍👧‍👦👠👩‍👦‍👦👨‍👦‍👦';
cy.postMessage(emojis);
cy.get('.emoticon').then((allEmoticons) => {
for (let index = 0; index < allEmoticons.length - 1; index++) {
const emoticon = allEmoticons[index];
const emoticonToCompare = allEmoticons[index + 1];
// * Compare emojis on same row not to have overlap
if (emoticon.getBoundingClientRect().top === emoticonToCompare.getBoundingClientRect().top) {
cy.wrap(emoticon.getBoundingClientRect().right).should('be.lte', emoticonToCompare.getBoundingClientRect().left);
}
}
});
});
});

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

@@ -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 @messaging
describe('MM-13064 - Emoji picker keyboard usability', () => {
let offTopicUrl;
before(() => {
// # Login as test user
cy.apiInitSetup({loginAfter: true}).then((out) => {
offTopicUrl = out.offTopicUrl;
});
});
beforeEach(() => {
// # Visit the Off-Topic channel
cy.visit(offTopicUrl);
// # Open emoji picker
cy.uiOpenEmojiPicker();
// # Wait for emoji picker to load
cy.get('#emojiPicker').should('be.visible');
});
it('If the left arrow key is pressed while focus is on the emoji picker text box, the cursor should move left in the text', () => {
cy.findByLabelText('Search for an emoji').
should('be.visible').
and('have.focus').
type('si').
should('have.value', 'si').
type('{leftarrow}m').
should('have.value', 'smi');
});
it('If the right arrow key is pressed when the cursor is at the end of the word in the text box, move the selection to the right in the emoji picker, otherwise move the cursor to the right in the text', () => {
// * Should highlight and preview the first emoji ("smile") on right arrow key
cy.findByLabelText('Search for an emoji').
should('be.visible').
and('have.focus').
type('si').
should('have.value', 'si').
type('{leftarrow}m').
should('have.value', 'smi').
type('{rightarrow}l').
should('have.value', 'smil').
type('{rightarrow}');
cy.get('.emoji-picker__item.selected').should('exist').within(() => {
cy.findByTestId('smile').should('exist');
});
cy.get('.emoji-picker__preview').should('have.text', ':smile:');
// * Should highlight and preview the second emoji ("smile_cat") on second right arrow key
cy.findByLabelText('Search for an emoji').
should('have.focus').
and('have.value', 'smil').
type('{rightarrow}').
should('have.value', 'smil');
cy.get('.emoji-picker__item.selected').should('exist').within(() => {
cy.findByTestId('smile_cat').should('exist');
});
cy.get('.emoji-picker__preview').should('have.text', ':smile_cat:');
});
it('On up or down arrow key press, move the selection up or down the emoji items', () => {
// * Check initial state of emoji preview
cy.get('.emoji-picker__preview').should('have.text', 'Select an Emoji');
// # Press down arrow and verify selected emoji
cy.get('body').type('{downarrow}');
cy.get('.emoji-picker__preview').should('have.text', ':grinning:');
// # Again, press down arrow and verify next selected emoji
cy.get('body').type('{downarrow}');
cy.get('.emoji-picker__preview').should('have.text', ':upside_down_face:');
// # Press up arrow and verify previously selected emoji
cy.get('body').type('{uparrow}');
cy.get('.emoji-picker__preview').should('have.text', ':grinning:');
// # Again, press up arrow and nothing is selected
cy.get('body').type('{uparrow}');
cy.get('.emoji-picker__preview').should('have.text', 'Select an Emoji');
});
it('On up arrow key, move around search input or highlight the text when holding shift key', () => {
// # Type "mi" and verify search text input
cy.get('body').type('mi');
cy.findByLabelText('Search for an emoji').should('have.value', 'mi');
// # Move cursor to the beginning of search text, type "s", and then verify search text input
cy.get('body').type('{uparrow}').type('s');
cy.findByLabelText('Search for an emoji').should('have.value', 'smi');
// # Move cursor to the end of the text
cy.findByLabelText('Search for an emoji').type('{downarrow}{uparrow}');
// * Verify that nothing is initially selected
verifySelectedTextAs('');
// # Highlight the text by holding shift key + up arrow, and verify that all characters are highlighted
cy.findByLabelText('Search for an emoji').type('{shift}{uparrow}');
verifySelectedTextAs('smi');
});
it('On down arrow key, move around the search input or highlight the text when holding shift key', () => {
// # Type "sm" and verify search text input
cy.get('body').type('sm');
cy.findByLabelText('Search for an emoji').should('have.value', 'sm');
// # Move cursor to the beginning then end of search text, type "i", and then verify search text input
cy.findByLabelText('Search for an emoji').type('{uparrow}{downarrow}i').should('have.value', 'smi');
// # Move cursor to the beginning of search text
cy.findByLabelText('Search for an emoji').type('{uparrow}');
// * Verify that nothing is initially selected
verifySelectedTextAs('');
// # Highlight the text by holding shift + arrow down, and verify that all characters are highlighted
cy.findByLabelText('Search for an emoji').type('{shift}{downarrow}');
verifySelectedTextAs('smi');
});
});
function verifySelectedTextAs(text) {
cy.document().then((doc) => {
expect(doc.getSelection().toString()).to.eq(text);
});
}

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

@@ -0,0 +1,199 @@
// 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
import timeouts from '../../../fixtures/timeouts';
describe('Messaging', () => {
let offTopicPath;
let testTeam;
const defaultEmojis = ['+1 emoji', 'grinning emoji', 'white check mark emoji'];
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl, team}) => {
testTeam = team;
offTopicPath = offTopicUrl;
cy.visit(offTopicUrl);
});
});
it('MM-T4261_1 One-click reactions on a post', () => {
// # Ensure emoji picker is enabled in System console
cy.apiAdminLogin();
cy.visit('/admin_console/site_config/emoji');
cy.get('.admin-console__header').should('be.visible').and('have.text', 'Emoji');
cy.findByTestId('ServiceSettings.EnableEmojiPickertrue').check();
cy.get('#saveSetting').then((btn) => {
if (btn.is(':enabled')) {
btn.click();
cy.waitUntil(() => cy.get('#saveSetting').then((el) => {
return el[0].innerText === 'Save';
}));
}
});
// Login with the test user to ensure the recently used emojis are defaulted
cy.apiCreateUser({prefix: 'other'}).then(({user: user1}) => {
cy.apiAddUserToTeam(testTeam.id, user1.id);
cy.apiLogin(user1);
});
// # Create a post and hover over post menu and verify One click reactions are not part of the post menu on hover
cy.visit(offTopicPath);
// # Toggle One-click reactions option in Account Settings>Display>One-click reactions on messages to ON
cy.uiOpenSettingsModal('Display').within(() => {
cy.findByText('Display', {timeout: timeouts.ONE_MIN}).click();
cy.findByText('Quick reactions on messages').click();
cy.findByLabelText('On').click();
cy.uiSaveAndClose();
});
cy.postMessage('Test post for recent reactions.');
cy.getLastPostId().then((postId) => {
// # Hover over the post menu
// * Verify One click reactions are part of the post menu on hover
// * Verify +1, grinning and white check mark are 3 default emoji shown
validateQuickReactions(postId, 'CENTER', defaultEmojis);
// # Open the same post in RHS and hover over the post
cy.get(`#post_${postId}`).click();
// * Verify only one emoji is part of the post menu, +1
validateQuickReactions(postId, 'RHS_ROOT', defaultEmojis);
// # Click on Expand Sidebar to expand RHS
cy.uiExpandRHS();
// * Verify all 3 default emoji are part of the post menu on hover
validateQuickReactions(postId, 'RHS_ROOT', defaultEmojis);
// # Close RHS
cy.uiCloseRHS();
// # React to post with new emoji e.g "wave"
// # Click the add reaction icon
cy.clickPostReactionIcon(postId);
cy.get('#emojiPickerSearch').type('wave');
cy.clickEmojiInEmojiPicker('wave');
let recentEmojis = ['wave emoji', '+1 emoji', 'grinning emoji'];
// # Hover over post again
// * Verify most recent emoji is shown slotted in the 1st place, pushing white check mark emoji out
validateQuickReactions(postId, 'CENTER', recentEmojis);
// Use 2 more new emoji reactions (other then default) on the same post and hover
cy.clickPostReactionIcon(postId);
cy.clickEmojiInEmojiPicker('blush');
cy.clickPostReactionIcon(postId);
cy.clickEmojiInEmojiPicker('innocent');
recentEmojis = ['innocent emoji', 'blush emoji', 'wave emoji'];
// # Hover over post again
// * Verify 3 most recent emoji are shown as one click reaction options, with the last used in the 1st slot and all default emoji have been pushed out, no longer visible.
validateQuickReactions(postId, 'CENTER', recentEmojis);
});
cy.postMessage('Another post for recent reactions.');
cy.getLastPostId().then((postId) => {
cy.clickPostReactionIcon(postId);
// # Use :wave: reaction again few times on different posts
cy.get('#emojiPickerSearch').type('wave');
cy.clickEmojiInEmojiPicker('wave');
// * Verify :wave: is sorted on the left as most frequently used
let recentEmojis = ['wave emoji', 'innocent emoji', 'blush emoji'];
validateQuickReactions(postId, 'CENTER', recentEmojis);
// # Use +1 reaction again
cy.clickPostReactionIcon(postId);
cy.get('#emojiPickerSearch').type('+1');
cy.findByTestId('+1,thumbsup').parent().click();
// * Verify :wave: is still in the far left spot as the most frequently used reaction
recentEmojis = ['wave emoji', '+1 emoji', 'innocent emoji'];
validateQuickReactions(postId, 'CENTER', recentEmojis);
});
});
it('MM-T4261_2 One-Click Reactions setting with Emoji Picker OFF', () => {
// # Navigate to System Console>Site Configuration>Emoji>Enable Emoji Picker, set it to FALSE and save
// # Ensure emoji picker is enabled in System console
cy.apiAdminLogin();
cy.visit('/admin_console/site_config/emoji');
cy.get('.admin-console__header').should('be.visible').and('have.text', 'Emoji');
cy.findByTestId('ServiceSettings.EnableEmojiPickerfalse').check();
cy.get('#saveSetting').then((btn) => {
if (btn.is(':enabled')) {
btn.click();
cy.waitUntil(() => cy.get('#saveSetting').then((el) => {
return el[0].innerText === 'Save';
}));
}
});
cy.visit(offTopicPath).wait(timeouts.HALF_SEC);
// # Open Account Settings > Display
// * Verify One-click reactions on messages option is not available
cy.uiOpenSettingsModal('Display').within(() => {
cy.findByText('Display', {timeout: timeouts.ONE_MIN}).click();
cy.findByText('Quick reactions on messages').should('not.exist');
cy.uiClose();
});
// # Create a post and hover to reveal post menu
cy.postMessage('Test post for recent reactions. (2)');
// * Verify one click reactions are not shown on the post menu
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).trigger('mouseover', {force: true}).within(() => {
cy.wait(timeouts.HALF_SEC).get(`#recent_reaction_${0}`).should('not.exist');
});
});
});
function validateQuickReactions(postId, location, emojis) {
let idPrefix;
let numReactions = 3;
if (location === 'CENTER') {
idPrefix = 'post';
} else if (location === 'RHS_ROOT' || location === 'RHS_COMMENT') {
idPrefix = 'rhsPost';
numReactions = 1;
} else if (location === 'RHS_EXPANDED') {
idPrefix = 'rhsPost';
}
for (let i = 0; i < numReactions; i++) {
cy.get(`#${idPrefix}_${postId}`).trigger('mouseover', {force: true}).within(() => {
cy.wait(timeouts.HALF_SEC).get(`#recent_reaction_${i}`).should('have.attr', 'aria-label', emojis[i]);
});
}
}
});

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

@@ -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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T164 Whitespace with emojis does not affect size', () => {
const emojis = [':book:', ':key:', ':gem:'];
// # Post a message beginning with a new line and followed by emojis
cy.postMessage('hello');
cy.uiGetPostTextBox().type('\n' + emojis.join(' ')).type('{enter}');
// # Get last post message text
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('newLineMessage');
});
// * Verify message is visible and does not have a new line
cy.get('@newLineMessage').
should('be.visible').
and('not.contain', '\n');
// * Verify emoji size
checkEmojiSize('@newLineMessage', emojis, true);
// # Post a message beginning with three spaces and followed by emojis
cy.postMessage(' ' + emojis.join(' '));
// # Get last post message text
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('spacesMessage');
});
// * Verify message is visible and starts with three spaces
cy.get('@spacesMessage').
should('be.visible');
cy.get('@spacesMessage').
should((message) => {
expect(message.find('span.all-emoji p').html()).to.match(/^[ ]{3}/);
});
// * Verify emoji size
checkEmojiSize('@spacesMessage', emojis, true);
});
it('MM-T163 Emojis are not jumbo when accompanied by text', () => {
const emojis = [':book:', ':key:', ':gem:'];
// # Post a message
const messageText = 'This is a message from the future';
cy.postMessage(messageText + ' ' + emojis.join(' '));
// # Get last post message text
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('newLineMessage');
});
// # Making sure Emojis from last post message are 21px size
checkEmojiSize('@newLineMessage', emojis, false);
});
it('MM-T160 Emojis show as jumbo in main thread - Multi emoji, no text, including unicode and emoticon', () => {
// # Create list of emojis we want to post
const emojis = [':smiley:', ':thumbsup:', '🤟'];
// # Post Emojis list
cy.postMessage(emojis.join(''));
// # Get last post message
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('lastMessage');
// * Expect emoji list to have emoji jumbo size
checkEmojiSize('@lastMessage', emojis, true);
});
});
});
/**
* [checkEmojiSizeInPost: this function is going to check the correct size of emojis when they're inside messages]
* @param message {string[]} [this is the message we send along with some emojis attached to it ]
* @param emojis {string[]} [array of emojis]
* @param isJumbo {boolean} [This parameter is used to verify what kind of matcher and size we need to compare in the emojis]
*/
function checkEmojiSize(message, emojis, isJumbo) {
const [height, width, size] = isJumbo ? ['min-Height', 'min-Width', '32px'] : ['height', 'width', '21px'];
emojis.forEach((emoji) => {
cy.get(message).
find('span[alt="' + (emoji === '🤟' ? ':i_love_you_hand_sign:' : emoji) + '"]').
and('have.css', height, size).
and('have.css', width, size);
});
}

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

@@ -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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T3014 Skin tone emoji', () => {
const gestures = [
':wave',
':point_up',
':clap',
':+1',
];
const skinTones = [
'_light_skin_tone:',
'_medium_light_skin_tone:',
'_medium_skin_tone:',
'_medium_dark_skin_tone:',
'_dark_skin_tone:',
];
// # Post emojis and check if they are visible on desktop and mobile viewports
gestures.forEach((gesture) => {
skinTones.forEach((skinTone) => {
// # Set viewport to desktop and post gesture with skin tone
cy.viewport('macbook-13');
cy.postMessage(gesture + skinTone);
// * Check if gesture with skin tone is visible
cy.findByTitle(gesture + skinTone).should('be.visible');
// # Set viewport to mobile
cy.viewport('iphone-se2');
// * Check if gesture with skin tone is visible
cy.findByTitle(gesture + skinTone).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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T198 Emojis preceeded by 4 or more spaces are always treated as markdown', () => {
[
' :taco:',
' :taco:',
' :D',
' :D',
].forEach((message) => {
createAndVerifyMessage(message, true);
});
[
' :taco:',
' :D',
].forEach((message) => {
createAndVerifyMessage(message, false);
});
});
});
function createMessages(message, aliases) {
cy.postMessage(message);
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as(aliases[0]);
cy.clickPostCommentIcon(postId);
cy.wait(TIMEOUTS.HALF_SEC);
cy.postMessageReplyInRHS(message);
cy.getLastPostId().then((lastPostId) => {
cy.get(`#postMessageText_${lastPostId}`).as(aliases[1]);
});
});
}
function createAndVerifyMessage(message, isCode) {
const aliases = ['newLineMessage', 'aliasLineMessageReplyInRHS'];
createMessages(message, aliases);
if (isCode) {
aliases.forEach((alias) => {
cy.get('@' + alias).
find('.post-code').should('be.visible').
find('code').should('be.visible').
contains(message.trim());
});
} else {
aliases.forEach((alias) => {
cy.get('@' + alias).
children().should('have.class', 'all-emoji').
children().find('span').last().should('have.class', 'emoticon').
and('have.attr', 'title', message.trim() === ':D' ? ':smile:' : ':taco:');
});
}
}

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

@@ -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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T4861 Show single image thumbnails in standard mode', () => {
// # Set the messages display setting to standard i.e not compact
cy.apiSaveMessageDisplayPreference('clean');
// # upload an image
const IMAGE_NAME = 'huge-image.jpg';
cy.get('#fileUploadInput').attachFile(IMAGE_NAME);
cy.uiWaitForFileUploadPreview();
// # post it with a message
const IMAGE_WITH_POST_TEXT = `image in compact display setting ${Date.now()}`;
cy.postMessage(IMAGE_WITH_POST_TEXT);
cy.getLastPostId().then((lastPostId) => {
// # Get to last post's message
cy.get(`#${lastPostId}_message`).should('exist').within(() => {
// * Check if typed message appeared in the post
cy.findByText(IMAGE_WITH_POST_TEXT).should('exist');
// * Check that we don't show the image name
cy.findByText(IMAGE_NAME).should('not.exist');
// * Check if image appeared
cy.findByLabelText(`file thumbnail ${IMAGE_NAME}`);
// * Check if collapse/expand button appeared, since its an icon button without text,
// finding it by Aria Label, as thats what screen readers will call out
cy.findByLabelText('Toggle Embed Visibility').should('exist');
// * Since last post was image upload, it should contain img with height property of 350px
cy.get('img').should('exist').and('have.css', 'max-height', '350px');
});
});
});
});

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

@@ -0,0 +1,171 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let offTopicUrl;
let testChannelName;
before(() => {
// # Login as test user
cy.apiInitSetup({loginAfter: true}).then((out) => {
offTopicUrl = out.offTopicUrl;
testChannelName = out.channel.display_name;
});
});
beforeEach(() => {
cy.visit(offTopicUrl);
});
it('MM-T200 Focus move to main input box when a character key is selected', () => {
// # Post a message
cy.postMessage('Hello');
// # Click the save icon to move focus out of the main input box
cy.uiGetSavedPostButton().click();
cy.uiGetPostTextBox().should('not.be.focused');
// # Push a character key such as "A"
// # Expect to have "A" value in main input
cy.get('body').type('A');
cy.uiGetPostTextBox().should('be.focused');
// # Click the @-mention icon to move focus out of the main input box
cy.uiGetRecentMentionButton().click();
cy.uiGetPostTextBox().should('not.be.focused');
// # Push a character key such as "B"
// # Expect to have "B" value in main input
cy.get('body').type('B');
cy.uiGetPostTextBox().should('be.focused');
});
it('MM-T204 Focus will move to main input box after a new channel has been opened', () => {
// # Post a new message
cy.postMessage('new post');
// # Open the reply thread on the most recent post
cy.clickPostCommentIcon();
// # Place the focus inside the RHS input box
cy.uiGetReplyTextBox().focus().should('be.focused');
// # Use CTRL+K or CMD+K to open the channel switcher depending on OS
cy.typeCmdOrCtrl().type('K', {release: true});
// * Verify channel switcher is visible
cy.get('#quickSwitchHint').should('be.visible');
// # Type channel name and select it
cy.findByRole('textbox', {name: 'quick switch input'}).type(testChannelName).wait(TIMEOUTS.HALF_SEC).type('{enter}');
// * Verify that it redirected into selected channel
cy.get('#channelHeaderTitle').should('be.visible').should('contain', testChannelName);
// * Verify focus is moved to main input box when the channel is opened
cy.uiGetPostTextBox().should('be.focused');
});
it('MM-T205 Focus to remain in RHS textbox each time Reply arrow is clicked', () => {
// # Post a new message
cy.postMessage('new post');
// # Open the reply thread on the most recent post
cy.clickPostCommentIcon();
// * Verify RHS textbox is focused the first time Reply arrow is clicked
cy.uiGetReplyTextBox().should('be.focused');
// # Focus away from RHS textbox
cy.get('#rhsContainer .post-right__content').click();
// # Click reply arrow on post in same thread
cy.clickPostCommentIcon();
// * Verify RHS textbox is again focused the second time, when already open
cy.uiGetReplyTextBox().should('be.focused');
});
it('MM-T203 Focus does not move when it has already been set elsewhere', () => {
// # Verify Focus in add channel member modal
verifyFocusInAddChannelMemberModal();
});
it('MM-T202 Focus does not move for non-character keys', () => {
// # Post a message
cy.postMessage('Hello');
// # Click the save icon to move focus out of the main input box
cy.uiGetSavedPostButton().click();
cy.uiGetPostTextBox().should('not.be.focused');
// Keycodes for keys that don't have a special character sequence for cypress.type()
const numLockKeycode = 144;
const f7Keycode = 118;
const windowsKeycode = 91;
[numLockKeycode, f7Keycode, windowsKeycode].forEach((keycode) => {
// # Trigger keydown event using keycode
cy.get('body').trigger('keydown', {keyCode: keycode, which: keycode});
// # Make sure main input is not focused
cy.uiGetPostTextBox().should('not.be.focused');
});
// For other keys we can use cypress.type() with a special character sequence.
['{downarrow}', '{pagedown}', '{shift}', '{pageup}', '{enter}'].forEach((key) => {
// # Type special character key using Cypress special character sequence
cy.get('body').type(key);
// # Make sure main input is not focused
cy.uiGetPostTextBox().should('not.be.focused');
});
});
});
function verifyFocusInAddChannelMemberModal() {
// # Click to access the Channel menu
cy.get('#channelHeaderTitle').click();
// * The dropdown menu of the channel header should be visible;
cy.get('#channelLeaveChannel').should('be.visible');
// # Click 'Add Members'
cy.get('#channelAddMembers').click();
// * Assert that modal appears
cy.get('#addUsersToChannelModal').should('be.visible');
// * Assert that the input box is in focus
cy.get('#selectItems input').should('be.focused');
// # Push a character key such as "A"
cy.focused().typeWithForce('A');
// * Check that input box has character A
cy.get('#selectItems input').should('have.value', 'A');
// # Click anywhere in the modal that is not on a field that can take focus
cy.get('.channel-invite__header').click();
// * Note the focus has been removed from the search box
cy.get('#selectItems input').should('not.be.focused');
// # Push a character key such as "A"
cy.get('body').type('A');
// * Focus is not moved anywhere. Neither the search box or main input box has the focus
cy.get('#selectItems input').should('not.be.focused').and('have.value', 'A');
cy.uiGetPostTextBox().should('not.be.focused');
}

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

@@ -0,0 +1,282 @@
// 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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {spyNotificationAs} from '../../../support/notification';
describe('Group Message', () => {
let testTeam;
let testUser;
let townsquareLink;
var users = [];
const groupUsersCount = 3;
before(() => {
cy.apiInitSetup({}).then(({team, user, townSquareUrl}) => {
testTeam = team;
testUser = user;
townsquareLink = townSquareUrl;
});
});
beforeEach(() => {
cy.apiAdminLogin();
// Add users on the testTeam
Cypress._.times(groupUsersCount, (i) => {
cy.apiCreateUser().then(({user: newUser}) => {
cy.apiAddUserToTeam(testTeam.id, newUser.id);
users.push(newUser);
if (i === groupUsersCount - 1) {
cy.apiLogin(testUser);
cy.visit(townsquareLink);
}
});
});
});
it('MM-T3319 Add GM', () => {
const otherUser1 = users[0];
const otherUser2 = users[1];
// # Click on '+' sign to open DM modal
cy.uiAddDirectMessage().click();
// * Verify that the DM modal is open
cy.get('#moreDmModal').should('be.visible').contains('Direct Messages');
// # Search for the user otherA
cy.get('#selectItems input').should('be.enabled').typeWithForce(`@${otherUser1.username}`);
// * Verify that the user is found and add to GM
cy.get('#moreDmModal .more-modal__row').should('be.visible').and('contain', otherUser1.username).click({force: true});
// # Search for the user otherB
cy.get('#selectItems input').should('be.enabled').typeWithForce(`@${otherUser2.username}`);
// * Verify that the user is found and add to GM
cy.get('#moreDmModal .more-modal__row').should('be.visible').and('contain', otherUser2.username).click({force: true});
// # Search for the current user
cy.get('#selectItems input').should('be.enabled').typeWithForce(`@${testUser.username}`);
// * Assert that it's not found
cy.get('.no-channel-message').should('be.visible').and('contain', 'No results found matching');
// # Start GM
cy.findByText('Go').click();
// # Post something to create a GM
cy.uiGetPostTextBox().type('Hi!').type('{enter}');
// # Click on '+' sign to open DM modal
cy.uiAddDirectMessage().click();
// * Verify that the DM modal is open
cy.get('#moreDmModal').should('be.visible').contains('Direct Messages');
// # Search for the user otherB
cy.get('#selectItems input').should('be.enabled').typeWithForce(`@${otherUser2.username}`);
// * Verify that the user is found and is part of the GM together with the other user
cy.get('#moreDmModal .more-modal__row').should('be.visible').and('contain', otherUser2.username).and('contain', otherUser1.username);
});
it('MM-T460 - Add and Remove users whilst creating a group message', () => {
// # Create a group message with two other users
createGroupMessageWith(users.slice(0, 2));
// # Open channel menu and click Add Members
cy.uiOpenChannelMenu('Add Members');
// # Filter user by username
cy.get('#selectItems input').typeWithForce(users[2].username).wait(TIMEOUTS.HALF_SEC);
// # Click the first user on a filtered list
cy.get('#multiSelectList .clickable').first().click();
// * Assert that member info updates to reflect the new addition
cy.get('#multiSelectHelpMemberInfo').should('contain', 'You can add 4 more people');
// # Click the first user on an unfiltered list
cy.get('#multiSelectList .clickable').first().click();
// * Assert that member info updates to reflect the new addition
cy.get('#multiSelectHelpMemberInfo').should('contain', 'You can add 3 more people');
// # Remove user by clicking the remove(x) button
cy.get('#selectItems .react-select__multi-value__remove').first().click();
// * Assert that member info updates to reflect the new addition
cy.get('#multiSelectHelpMemberInfo').should('contain', 'You can add 4 more people');
// # Remove last user on the list by typing backspace
cy.get('#selectItems input').typeWithForce('{backspace}').wait(TIMEOUTS.HALF_SEC);
// * Assert that member info updates to reflect the new addition
cy.get('#multiSelectHelpMemberInfo').should('contain', 'You can add 5 more people');
});
it('MM-T465 - Assert that group message participant sees', () => {
// # Create a group message with two other users
const participants = users.slice(0, 2);
createGroupMessageWith(participants);
cy.wait(TIMEOUTS.HALF_SEC);
const sortedParticipants = participants.sort((a, b) => {
return a.username > b.username ? 1 : -1;
});
// * Assert that intro message includes the right copy
const expectedChannelInfo = `This is the start of your group message history with ${sortedParticipants[0].username}, ${sortedParticipants[1].username}.Messages and files shared here are not shown to people outside this area.`;
cy.get('#channelIntro p.channel-intro-text').first().should('contain', expectedChannelInfo);
cy.get('#channelIntro .profile-icon').should('have.length', '2');
cy.location().then((loc) => {
const channelId = loc.pathname.split('/').slice(-1)[0];
// * Assert that sidebar displays the group channel
cy.get(`#sidebarItem_${channelId}`).should('contain', `${sortedParticipants[0].username}, ${sortedParticipants[1].username}`);
// * Assert that member count shows next to the channel name
cy.get(`#sidebarItem_${channelId} .status`).eq(0).should('contain', '2');
});
});
it('MM-T469 - Post an @mention on a group channel', () => {
spyNotificationAs('withNotification', 'granted');
// # Create a group message with two other users
const participants = users.slice(0, 2);
createGroupMessageWith(participants);
cy.wait(TIMEOUTS.HALF_SEC);
// # Post a message as a different user
cy.getCurrentChannelId().then((channelId) => {
cy.postMessageAs({sender: participants[0], message: `@${testUser.username} Hello!!!`, channelId});
// * Assert that user receives notification
cy.wait(TIMEOUTS.HALF_SEC);
cy.get('@withNotification').should('have.been.called');
});
});
it('MM-T475 - Channel preferences, mute channel', () => {
spyNotificationAs('withNotification', 'granted');
// # Create a group message with two other users
const participants = users.slice(0, 2);
createGroupMessageWith(participants);
cy.wait(TIMEOUTS.HALF_SEC);
// # Clicks on Mute Channel through Notification Preferences
cy.uiOpenChannelMenu().within(() => {
// # Set Mute Channel to On
cy.get('#markUnreadEdit').click();
cy.get('#channelNotificationUnmute').click();
cy.get('#saveSetting').click();
// * Assert that channel is muted
cy.get('#toggleMute').should('be.visible');
});
// # Post a message as a different user
cy.getCurrentChannelId().then((channelId) => {
let channelName;
cy.location().then((loc) => {
channelName = loc.pathname.split('/').slice(-1)[0];
});
cy.postMessageAs({sender: participants[0], message: 'Hello all', channelId}).then(() => {
cy.visit(townsquareLink);
// * Assert that user does not receives a notification
cy.get('@withNotification').should('not.have.been.called');
// * Should not have unread mentions indicator.
cy.get(`#sidebarItem_${channelName}`).
scrollIntoView().
find('#unreadMentions').
should('not.exist');
});
cy.postMessageAs({sender: participants[0], message: `@${testUser.username} Hello!!!`, channelId}).then(() => {
cy.apiLogin(testUser);
cy.visit(townsquareLink);
// * Assert that user does not receives a notification
cy.get('@withNotification').should('not.have.been.called');
// * Should have unread mentions indicator.
cy.get(`#sidebarItem_${channelName}`).
scrollIntoView().
get('#unreadMentions').
should('exist');
});
});
});
it('MM-T478 - Open existing group message from More... section', () => {
// # Create a group message with two other users
const participants = users.slice(0, 2);
const sortedParticipants = participants.sort((a, b) => {
return a.username > b.username ? 1 : -1;
});
createGroupMessageWith(participants);
cy.wait(TIMEOUTS.HALF_SEC);
cy.location().then((loc) => {
const channelName = loc.pathname.split('/').slice(-1)[0];
// # Remove GM from the LHS
cy.uiGetChannelSidebarMenu(channelName).within(() => {
cy.findByText('Close Conversation').click();
});
// # Open DM modal
cy.uiAddDirectMessage().click().wait(TIMEOUTS.HALF_SEC);
// # Open previously closed group message
cy.get('#selectItems input').typeWithForce(participants[0].username).wait(TIMEOUTS.HALF_SEC);
cy.get('#multiSelectList .suggestion-list__item').last().click().wait(TIMEOUTS.HALF_SEC);
// * Verify that participants are listed in the input field
cy.get('#selectItems').should('contain', `${sortedParticipants[0].username}${sortedParticipants[1].username}`);
// # Open group message
cy.get('#saveItems').click().wait(TIMEOUTS.HALF_SEC);
// * Verify that page renders with the right information
cy.get('#channelHeaderTitle').should('contain', `${sortedParticipants[0].username}, ${sortedParticipants[1].username}`);
});
});
});
const createGroupMessageWith = (users) => {
const defaultUserLimit = 7;
cy.uiAddDirectMessage().click().wait(TIMEOUTS.HALF_SEC);
cy.get('#multiSelectHelpMemberInfo').should('contain', 'You can add 7 more people');
users.forEach((user, index) => {
cy.get('#selectItems input').typeWithForce(user.username).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Assert that member info updates whilst adding new members
cy.get('#multiSelectHelpMemberInfo').should('contain', `You can add ${defaultUserLimit - (index + 1)} more people`);
});
// # Save group message member changes
cy.get('#saveItems').click().wait(TIMEOUTS.HALF_SEC);
};

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

@@ -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 @messaging @plugin @not_cloud
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {matterpollPlugin} from '../../../utils/plugins';
describe('Header', () => {
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.shouldHavePluginUploadEnabled();
// # Setup and visit town-square
cy.apiInitSetup().then(({team}) => {
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('MM-T1837_1 - DM channel with bot displays a normal header', () => {
// # Create a bot
cy.apiCreateBot().then(({bot}) => {
// # Open a DM with the bot
cy.uiAddDirectMessage().click();
cy.get('.more-modal__list .more-modal__row');
cy.get('#moreDmModal input').
type(bot.username, {force: true}).
type('{enter}', {force: true});
cy.get('#selectItems').contains(bot.username);
cy.get('#saveItems').click();
// * Verify Channel Header is visible
cy.get('#channelHeaderInfo').should('be.visible');
// * Verify header content
cy.get('#channelHeaderDescription > .header-description__text').find('p').should('have.text', bot.description);
});
});
it('MM-T1837_2 - DM channel with bot from plugin displays a normal header', () => {
// # Upload and enable "matterpoll" plugin
cy.apiUploadAndEnablePlugin(matterpollPlugin);
// # Open a DM with the bot
cy.uiAddDirectMessage().click().wait(TIMEOUTS.ONE_SEC);
cy.findByRole('dialog', {name: 'Direct Messages'}).should('be.visible').wait(TIMEOUTS.ONE_SEC);
cy.findByRole('textbox', {name: 'Search for people'}).
typeWithForce('matterpoll').wait(TIMEOUTS.ONE_SEC).
typeWithForce('{enter}');
cy.get('#selectItems').contains('matterpoll');
cy.get('#saveItems').click();
// * Verify Channel Header is visible
cy.get('#channelHeaderInfo').should('be.visible');
// * Verify header content
cy.get('#channelHeaderDescription > .header-description__text').find('p').should('have.text', 'Poll Bot');
});
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Header', () => {
let otherUser;
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup().then(({team, user}) => {
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(team.id, otherUser.id);
});
cy.apiLogin(user);
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T88 An ellipsis indicates the channel header is too long - public or private channel Quote icon displays at beginning of channel header', () => {
// * Verify with short channel header
updateAndVerifyChannelHeader('>', 'newheader');
// * Verify with long channel header
updateAndVerifyChannelHeader('>', 'newheader'.repeat(20));
});
it('MM-T881_1 - Header: Markdown quote', () => {
// # Update channel header text
const header = 'This is a quote in the header';
updateAndVerifyChannelHeader('>', header);
});
it('MM-T89 An ellipsis indicates the channel header is too long - DM', () => {
// # Open Account Setting and enable Compact View on the Display tab
cy.uiChangeMessageDisplaySetting('COMPACT');
// # Open a DM with other user
cy.uiAddDirectMessage().click().wait(TIMEOUTS.HALF_SEC);
cy.focused().
type(otherUser.username, {force: true}).wait(TIMEOUTS.HALF_SEC).
type('{enter}', {force: true}).wait(TIMEOUTS.HALF_SEC);
cy.get('#saveItems').click().wait(TIMEOUTS.HALF_SEC);
// # Update DM channel header
const header = `quote ${'newheader'.repeat(15)}`;
updateAndVerifyChannelHeader('>', header);
// # Click the header to see the whole text
cy.get('#channelHeaderDescription').click();
// * Check that no ellipsis is present
cy.get('#header-popover > div.popover-content').
should('have.html', `<span><blockquote>\n<p>${header}</p>\n</blockquote></span>`);
cy.apiSaveMessageDisplayPreference('clean');
});
});
function updateAndVerifyChannelHeader(prefix, header) {
// # Update channel header
cy.updateChannelHeader(prefix + header);
// * Should render blockquote if it starts with ">"
if (prefix === '>') {
cy.get('#channelHeaderDescription > span > blockquote').should('be.visible');
}
// * Check if channel header description has ellipsis
cy.get('#channelHeaderDescription > .header-description__text').find('p').
should('have.text', header).
and('have.css', 'overflow', 'hidden').
and('have.css', 'text-overflow', 'ellipsis');
}

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

@@ -0,0 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export function verifyDraftIcon(name, isVisible) {
cy.uiGetLhsSection('CHANNELS').find(`#sidebarItem_${name}`).
should('be.visible').
findByTestId('draftIcon').
should(isVisible ? 'be.visible' : 'not.exist');
}

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

@@ -0,0 +1,186 @@
// 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
describe('Image attachment', () => {
before(() => {
// # Login as new user
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('Image smaller than 48px in both width and height', () => {
const filename = 'small-image.png';
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile(filename);
verifyImageInPostFooter();
// # Post message
cy.postMessage('Image upload');
verifyImageInPostFooter(false);
// # File thumbnail should have correct dimensions
verifyFileThumbnail({
filename,
actualImage: {height: 24, width: 24},
container: {height: 45, width: 45},
});
});
it('Image with height smaller than 48px', () => {
const filename = 'image-small-height.png';
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile(filename);
verifyImageInPostFooter();
// # Post message
cy.postMessage('Image upload');
verifyImageInPostFooter(false);
// # File thumbnail should have correct dimensions
verifyFileThumbnail({
filename,
actualImage: {height: 24, width: 340},
container: {height: 45, width: 339},
});
});
it('Image with width smaller than 48px', () => {
const filename = 'image-small-width.png';
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile(filename);
verifyImageInPostFooter();
// # Post message
cy.postMessage('Image upload');
verifyImageInPostFooter(false);
// # File thumbnail should have correct dimensions
verifyFileThumbnail({
filename,
actualImage: {height: 350, width: 21},
container: {height: 348, width: 46},
});
});
it('Image with width and height bigger than 48px', () => {
const filename = 'MM-logo-horizontal.png';
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile(filename);
verifyImageInPostFooter();
// # Post message
cy.postMessage('Image upload');
verifyImageInPostFooter(false);
// # File thumbnail should have correct dimensions
verifyFileThumbnail({
filename,
actualImage: {height: 151, width: 955},
});
});
it('opens image preview window when image is clicked', () => {
const filename = 'MM-logo-horizontal.png';
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile(filename);
verifyImageInPostFooter();
// # Post message
cy.postMessage('Image upload');
verifyImageInPostFooter(false);
// # File thumbnail should have correct dimensions
verifyFileThumbnail({
filename,
actualImage: {height: 151, width: 955},
clickPreview: () => cy.uiGetFileThumbnail(filename).click(),
});
// * Verify that the preview modal open up
cy.uiGetFilePreviewModal();
// # Close the modal
cy.uiCloseFilePreviewModal();
});
it('opens image preview window when small image is clicked', () => {
const filename = 'small-image.png';
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile(filename);
verifyImageInPostFooter();
// # Post message
cy.postMessage('Image upload');
verifyImageInPostFooter(false);
// # File thumbnail should have correct dimensions
verifyFileThumbnail({
filename,
actualImage: {height: 24, width: 24},
container: {height: 45, width: 45},
clickPreview: () => cy.uiGetFileThumbnail(filename).click(),
});
// * Verify that the preview modal open up
cy.uiGetFilePreviewModal();
});
});
function verifyImageInPostFooter(verifyExistence = true) {
// * Verify that the image exists or not
cy.get('#advancedTextEditorCell').find('.file-preview').should(verifyExistence ? 'be.visible' : 'not.exist');
}
function verifyFileThumbnail({filename, actualImage = {}, container = {}, clickPreview}) {
// # File thumbnail should have correct dimensions
cy.getLastPostId().then((postId) => {
// * Verify that the image is inside a container div
cy.get(`#${postId}_message`).within(() => {
cy.uiGetFileThumbnail(filename).
should((img) => {
expect(img.height()).to.be.closeTo(actualImage.height, 2.0);
expect(img.width()).to.be.closeTo(actualImage.width, 2.0);
}).
parent().
should((img) => {
if (container.width || container.height) {
expect(img.height()).to.be.closeTo(container.height, 2.0);
expect(img.width()).to.be.closeTo(container.width, 2.0);
}
});
if (clickPreview) {
// # Open file preview
clickPreview();
}
});
});
}

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

@@ -0,0 +1,36 @@
// 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
describe('Messaging', () => {
before(() => {
// # Login as test user and visit the newly created test channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
// # Visit a test channel
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T187 Inline markdown images open preview window', () => {
// # Post the image link to the channel
cy.postMessage('Hello ![test image](https://raw.githubusercontent.com/furqanmlk/furqanmlk.github.io/main/images/image-small-height.png)');
// * Confirm the image container is visible
cy.uiWaitUntilMessagePostedIncludes('Hello');
cy.get('.markdown-inline-img__container').should('be.visible');
// # Hover over image then click to open preview image
cy.get('.file-preview__button').trigger('mouseover').click();
// * Confirm image is visible
cy.findByTestId('imagePreview').should('be.visible');
});
});

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

@@ -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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit the newly created test channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
// # Visit a test channel
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T188 - Inline markdown image that is a link, opens the link', () => {
const linkUrl = 'https://www.google.com';
const imageUrl = 'https://docs.mattermost.com/_images/icon-76x76.png';
const label = 'Build Status';
const baseUrl = Cypress.config('baseUrl');
// # Post the provided Markdown text in the test channel
cy.postMessage(`[![${label}](${imageUrl})](${linkUrl})`);
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).find('a').then(($a) => {
// * Check that the newly created post contains an a tag with the correct href link and target
cy.wrap($a).
should('have.attr', 'href', linkUrl).
and('have.attr', 'target', '_blank');
// * Check that the newly created post has an image
cy.wrap($a).find('img').should('be.visible').
and('have.attr', 'src', `${baseUrl}/api/v4/image?url=${encodeURIComponent(imageUrl)}`).
and('have.attr', 'alt', label);
// # Assign the value of the a tag href to the 'href' variable and assert the link is valid
const href = $a.prop('href');
cy.request(href).its('body').should('include', '</html>');
});
});
});
});

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

@@ -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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit test channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel, user}) => {
cy.visit(`/${team.name}/channels/${channel.name}`);
Cypress._.times(30, (i) => {
cy.postMessageAs({sender: user, message: `[${i}]`, channelId: channel.id});
});
});
});
it('MM-T207 Input box on main thread can expand with RHS closed', () => {
// # Check whether the RHS Close button exist, and click it in case it exist.
cy.get('body').then((body) => {
if (body.find('rhsCloseButton').length) {
cy.get('#rhsCloseButton').click({force: true});
}
});
// * Confirm the RHS is not shown since there is no Close Button
cy.get('#rhsCloseButton').should('not.exist');
// # Get initial height of the post textbox
cy.uiGetPostTextBox().clear();
cy.uiGetPostTextBox().then((post) => {
cy.wrap(parseInt(post[0].clientHeight, 10)).as('previousHeight');
});
// # Scroll to latest post
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).scrollIntoView();
});
// # Write lines until maximum height
for (let i = 0; i < 15; i++) {
// # Post the line
cy.uiGetPostTextBox().type('{shift}{enter}');
cy.uiGetPostTextBox().then((post) => {
const height = parseInt(post[0].clientHeight, 10);
// * Previous height should be lower than the current height
cy.get('@previousHeight').should('be.most', height);
// # Store the current height as the previous height for the next loop
cy.wrap(height).as('previousHeight');
});
}
// * Check that height does not keep increasing.
cy.uiGetPostTextBox().type('{shift}{enter}');
cy.uiGetPostTextBox().then((post) => {
const height = parseInt(post[0].clientHeight, 10);
cy.get('@previousHeight').should('equal', height);
});
cy.getNthPostId(-1).then((postId) => {
cy.get(`#postMessageText_${postId}`).should('be.visible');
});
// # Clear textbox to test from a different post
cy.uiGetPostTextBox().clear();
// # Scroll to a previous post
cy.getNthPostId(-29).then((postId) => {
cy.get(`#postMessageText_${postId}`).scrollIntoView();
});
// # Again, write lines until the textbox reaches the maximum height
for (let i = 0; i < 15; i++) {
cy.uiGetPostTextBox().type('{shift}{enter}');
}
// * Previous post should be visible
cy.getNthPostId(-29).then((postId) => {
cy.get(`#postMessageText_${postId}`).should('be.visible');
});
});
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getAdminAccount} from '../../../support/env';
describe('Messaging', () => {
const admin = getAdminAccount();
before(() => {
// # Log in as test user, go to test channel and post several messages
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
cy.visit(`/${team.name}/channels/${channel.name}`);
Cypress._.times(30, (i) => {
cy.postMessageAs({sender: admin, message: `[${i}]`, channelId: channel.id});
});
});
});
it('MM-T208 Input box on main thread can expand with RHS open', () => {
// # Wait until site is loaded
cy.wait(TIMEOUTS.FIVE_SEC);
// # Open RHS
cy.clickPostCommentIcon();
// * Confirm the RHS is shown
cy.get('#rhsCloseButton').should('exist');
// # Get initial height of the post textbox
cy.uiGetPostTextBox().clear();
cy.uiGetPostTextBox().then((post) => {
cy.wrap(parseInt(post[0].clientHeight, 10)).as('previousHeight');
});
// # Scroll to latest post
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).scrollIntoView();
});
// # Write lines until maximum height
for (let i = 0; i < 13; i++) {
// # Post the line
cy.uiGetPostTextBox().type('{shift}{enter}');
cy.uiGetPostTextBox().then((post) => {
const height = parseInt(post[0].clientHeight, 10);
// * Previous height should be lower than the current heigh
cy.get('@previousHeight').should('be.lessThan', height);
// # Store the current height as the previous height for the next loop
cy.wrap(height).as('previousHeight');
});
}
// * Check that height does not keep increasing.
cy.uiGetPostTextBox().type('{shift}{enter}');
cy.uiGetPostTextBox().then((post) => {
const height = parseInt(post[0].clientHeight, 10);
cy.get('@previousHeight').should('equal', height);
});
cy.getNthPostId(-1).then((postId) => {
cy.get(`#postMessageText_${postId}`).should('be.visible');
});
// # Clear textbox to test from a different post
cy.uiGetPostTextBox().clear();
// # Scroll to a previous post
cy.getNthPostId(-29).then((postId) => {
cy.get(`#postMessageText_${postId}`).scrollIntoView();
});
// # Write again all the long message
for (let i = 0; i < 14; i++) {
cy.uiGetPostTextBox().type('{shift}{enter}');
}
// * Previous post should be visible
cy.getNthPostId(-29).then((postId) => {
cy.get(`#postMessageText_${postId}`).should('be.visible');
});
});
});

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

@@ -0,0 +1,34 @@
// 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 @emoji
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T167 Terms that are not valid emojis render as plain text', () => {
// # Post message to use
cy.postMessage(':pickle:');
// * Post contains the text
cy.getLastPost().should('contain', ':pickle:');
// # Post message to use
cy.postMessage('on Mon Jun 03 16:15:11 +0000 2019');
// * Post contains the text
cy.getLastPost().should('contain', 'on Mon Jun 03 16:15:11 +0000 2019');
});
});

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

@@ -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 @messaging
import {getAdminAccount} from '../../../support/env';
describe('Messaging', () => {
const sysadmin = getAdminAccount();
before(() => {
// # Enable Timezone
cy.apiUpdateConfig({
DisplaySettings: {
ExperimentalTimezone: true,
},
});
// # Create and visit new channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Post messages from the past
[
Date.UTC(2020, 0, 5, 4, 30), // Jan 5, 2020 04:30
Date.UTC(2020, 0, 5, 12, 30), // Jan 5, 2020 12:30
Date.UTC(2020, 0, 5, 20, 30), // Jan 5, 2020 20:30
Date.UTC(2020, 0, 6, 0, 30), // Jan 6, 2020 00:30
].forEach((createAt, index) => {
cy.postMessageAs({sender: sysadmin, message: `Hello from ${index}`, channelId: channel.id, createAt});
});
// # Post messages from now
cy.postMessage('Hello from now');
// # Reload to re-arrange posts
cy.reload();
});
});
it('MM-T713 Post time should render correct format and locale', () => {
const testCases = [
{
name: 'in English',
publicChannel: 'CHANNELS',
locale: 'en',
manualTimezone: 'UTC',
localTimes: [
{postIndex: 0, standard: '4:30 AM', military: '04:30'},
{postIndex: 1, standard: '12:30 PM', military: '12:30'},
{postIndex: 2, standard: '8:30 PM', military: '20:30'},
{postIndex: 3, standard: '12:30 AM', military: '00:30'},
],
},
{
name: 'in Spanish',
publicChannel: 'CANALES',
locale: 'es',
manualTimezone: 'UTC',
localTimes: [
{postIndex: 0, standard: '4:30 a. m.', military: '4:30'},
{postIndex: 1, standard: '12:30 p. m.', military: '12:30'},
{postIndex: 2, standard: '8:30 p. m.', military: '20:30'},
{postIndex: 3, standard: '12:30 a. m.', military: '0:30'},
],
},
{
name: 'in react-intl unsupported timezone',
publicChannel: 'CHANNELS',
locale: 'en',
manualTimezone: 'NZ-CHAT',
localTimes: [
{postIndex: 0, standard: '6:15 PM', military: '18:15'},
{postIndex: 1, standard: '2:15 AM', military: '02:15'},
{postIndex: 2, standard: '10:15 AM', military: '10:15'},
{postIndex: 3, standard: '2:15 PM', military: '14:15'},
],
},
];
testCases.forEach((testCase) => {
// # Standard time
testCase.localTimes.forEach((localTime, index) => {
// # Change user preference to 12-hour format
cy.apiSaveClockDisplayModeTo24HourPreference(false);
// # Set user locale and timezone
setLocaleAndTimezone(testCase.locale, testCase.manualTimezone);
// * Verify that the channel is loaded correctly based on locale
cy.findByText(testCase.publicChannel).should('be.visible');
// * Verify that the local time of each post is rendered in 12-hour format based on locale
cy.findAllByTestId('postView').eq(index).find('.post__time', {timeout: 500}).should('have.text', localTime.standard);
});
// # Military time
testCase.localTimes.forEach((localTime, index) => {
// # Change user preference to 24-hour format
cy.apiSaveClockDisplayModeTo24HourPreference(true);
// # Set user locale and timezone
setLocaleAndTimezone(testCase.locale, testCase.manualTimezone);
// * Verify that the channel is loaded correctly based on locale
cy.findByText(testCase.publicChannel).should('be.visible');
// * Verify that the local time of each post is rendered in 24-hour format based on locale
cy.findAllByTestId('postView').eq(index).find('.post__time', {timeout: 500}).should('have.text', localTime.military);
});
});
});
});
function setLocaleAndTimezone(locale, manualTimezone) {
cy.apiPatchMe({
locale,
timezone: {
manualTimezone,
automaticTimezone: '',
useAutomaticTimezone: 'false',
},
});
}

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let otherChannelName;
let otherChannelUrl;
let initialHeight = 0;
before(() => {
// # Make sure the viewport is the expected one, so written lines always create new lines
cy.viewport(1000, 660);
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then((out) => {
otherChannelName = out.channel.name;
otherChannelUrl = out.channelUrl;
cy.visit(out.offTopicUrl);
cy.postMessage('hello');
cy.uiGetPostTextBox().invoke('height').then((height) => {
initialHeight = height;
// # Get the height before starting to write
// Setting alias based on reference to element seemed to be problematic with Cypress (regression)
// Quick hack to reference based on value
cy.wrap(initialHeight).as('initialHeight');
cy.wrap(initialHeight).as('previousHeight');
});
});
});
it('MM-T211 Leave a long draft in the main input box', () => {
const lines = [
'Lorem ipsum dolor sit amet,',
'consectetur adipiscing elit.',
'Nulla ac consectetur quam.',
'Phasellus libero lorem,',
'facilisis in purus sed, auctor.',
];
// # Write all lines
writeLinesToPostTextBox(lines);
// # Visit a different channel and verify textbox
cy.get(`#sidebarItem_${otherChannelName}`).click({force: true}).wait(TIMEOUTS.THREE_SEC);
verifyPostTextbox('@initialHeight', '');
// # Return to the channel and verify textbox
cy.get('#sidebarItem_off-topic').click({force: true}).wait(TIMEOUTS.THREE_SEC);
verifyPostTextbox('@previousHeight', lines.join('\n'));
// # Clear the textbox
cy.uiGetPostTextBox().clear();
cy.postMessage('World!');
// # Write all lines again
cy.wrap(initialHeight).as('previousHeight');
writeLinesToPostTextBox(lines);
// # Visit a different channel by URL and verify textbox
cy.visit(otherChannelUrl).wait(TIMEOUTS.THREE_SEC);
verifyPostTextbox('@initialHeight', '');
// # Should have returned to the channel by URL. However, Cypress is clearing storage for some reason.
// # Does not happened on actual user interaction.
// * Verify textbox
cy.get('#sidebarItem_off-topic').click({force: true}).wait(TIMEOUTS.THREE_SEC);
verifyPostTextbox('@previousHeight', lines.join('\n'));
});
});
function writeLinesToPostTextBox(lines) {
Cypress._.forEach(lines, (line, i) => {
// # Add the text
cy.uiGetPostTextBox().type(line, {delay: TIMEOUTS.ONE_HUNDRED_MILLIS}).wait(TIMEOUTS.HALF_SEC);
if (i < lines.length - 1) {
// # Add new line
cy.uiGetPostTextBox().type('{shift}{enter}').wait(TIMEOUTS.HALF_SEC);
// * Verify new height
cy.uiGetPostTextBox().invoke('height').then((height) => {
// * Verify previous height should be lower than the current height
cy.get('@previousHeight').should('be.lessThan', parseInt(height, 10));
// # Store the current height as the previous height for the next loop
cy.wrap(parseInt(height, 10)).as('previousHeight');
});
}
});
cy.wait(TIMEOUTS.THREE_SEC);
}
function verifyPostTextbox(heightSelector, text) {
cy.uiGetPostTextBox().and('have.text', text).invoke('height').then((currentHeight) => {
cy.get(heightSelector).should('be.gte', currentHeight);
});
}

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
before(() => {
// # Create new team and new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T105 Long post with multiple attachments', () => {
// * Attachment previews/thumbnails display as expected, when viewing full or partial post':
// # Post attachments
postAttachments();
// * Verify show more button
cy.get('#showMoreButton').scrollIntoView().should('be.visible').and('have.text', 'Show more');
// * Verify the total 4 attached items are present
cy.getLastPostId().then((postID) => {
cy.get(`#${postID}_message`).findByTestId('fileAttachmentList').children().should('have.length', '4');
// * Verify the preview attachment are present
[...Array(4)].forEach((value, index) => {
cy.get(`#${postID}_message`).findByTestId('fileAttachmentList').children().eq(index).
find('.post-image__name').contains('small-image.png').should('exist');
});
});
// * Can click one of the attachments and cycle through the multiple attachment previews as usual:
// # Post attachments
postAttachments();
// * Verify the attached items can be cycled through
cy.getLastPostId().then((postId) => {
cy.get(`#${postId}_message`).within(() => {
cy.uiOpenFilePreviewModal();
});
// * Verify image preview is visible
cy.uiGetFilePreviewModal();
// * Verify the header with the count of the file exists
cy.uiGetHeaderFilePreviewModal().contains('1 of 4');
for (var index = 2; index <= 4; index++) {
// # click on right arrow to preview next attached image
cy.get('#previewArrowRight').should('be.visible').click();
// * Verify the header counter
cy.uiGetHeaderFilePreviewModal().contains(`${index} of 4`);
}
// # Close the modal
cy.uiCloseFilePreviewModal();
});
});
});
function verifyImageInPostFooter(verifyExistence = true) {
// * Verify that the image exists or not
cy.get('#advancedTextEditorCell').find('.file-preview').should(verifyExistence ? 'be.visible' : 'not.exist').wait(TIMEOUTS.THREE_SEC);
}
function postAttachments() {
// Add 4 attachments to a post
[...Array(4)].forEach(() => {
cy.get('#fileUploadInput').attachFile('small-image.png');
});
// # verify the attachment at the footer
verifyImageInPostFooter();
// # Copy and paste the text below into the message box and post
cy.fixture('long_text_post.txt', 'utf-8').then((text) => {
cy.uiGetPostTextBox().then((textbox) => {
textbox.val(text);
}).type(' {backspace}{enter}');
});
}

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

@@ -0,0 +1,57 @@
// 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 @markdown
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.apiSaveShowMarkdownPreviewPreference();
cy.visit(offTopicUrl);
cy.postMessage('hello');
});
});
it('MM-T186 Markdown preview: inline image', () => {
const message = '![make it so](https://i.stack.imgur.com/MNeE7.jpg)';
// # Get the height before starting to write
cy.uiGetPostTextBox().clear().then(() => {
cy.get('#post-create').then((postArea) => {
cy.wrap(parseInt(postArea[0].clientHeight, 10)).as('initialHeight');
});
});
// # Post first line to use
cy.uiGetPostTextBox().type(message).type('{shift}{enter}').type(message);
// # Click on Preview button
cy.findByLabelText('preview').click();
cy.get('#post-list').then((postList) => {
cy.get('#create_post').within(() => {
cy.get('.markdown-inline-img').then((img) => {
// * Images do not overlap Post List
expect(postList[0].getBoundingClientRect().bottom).lessThan(img[0].getBoundingClientRect().top);
expect(postList[0].getBoundingClientRect().bottom).lessThan(img[1].getBoundingClientRect().top);
// * Images do not overlap among themselves
expect(img[0].getBoundingClientRect().bottom <= img[1].getBoundingClientRect().top).equals(true);
});
});
});
// * Post Create area should be higher than before
cy.get('#post-create').then((postArea) => {
cy.get('@initialHeight').should('be.lessThan', parseInt(postArea[0].clientHeight, 10));
});
});
});

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

@@ -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 @messaging @markdown
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
cy.postMessage('hello');
});
});
it('MM-T189 Markdown quotation paragraphs', () => {
const messageParts = ['this is', 'really', 'three quote lines'];
// # Post message to use
cy.uiGetPostTextBox().clear().type('>' + messageParts[0]).type('{shift}{enter}{enter}');
cy.uiGetPostTextBox().type('>' + messageParts[1]).type('{shift}{enter}{enter}');
cy.uiGetPostTextBox().type('>' + messageParts[2]).type('{enter}');
var firstPartLeft;
cy.getLastPostId().then((postId) => {
// * There is only one blockquote, and therefore only one Quote icon
cy.get(`#postMessageText_${postId} > blockquote`).should('have.length', 1);
// * There are three distinct paragraphs, and therefore the three of them are separated by a space
cy.get(`#postMessageText_${postId} > blockquote > p`).should('have.length', 3);
cy.get(`#postMessageText_${postId} > blockquote > p`).each((el, i) => {
// * Each paragraph contains the content we put on the message
expect(messageParts[i]).equals(el.html());
// # We save the alignment of the first paragraph
if (i === 0) {
firstPartLeft = el[0].getBoundingClientRect().left;
}
// * All paragraph are aligned on their left border
expect(firstPartLeft).equals(el[0].getBoundingClientRect().left);
});
});
});
});

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

@@ -0,0 +1,133 @@
// 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 @not_cloud @messaging @markdown
import * as TIMEOUTS from '../../../fixtures/timeouts';
const testCases = [
{name: 'Markdown - typescript', fileKey: 'markdown_typescript'},
{name: 'Markdown - postgres', fileKey: 'markdown_postgres'},
{name: 'Markdown - latex', fileKey: 'markdown_latex'},
{name: 'Markdown - python', fileKey: 'markdown_python'},
{name: 'Markdown - shell', fileKey: 'markdown_shell'},
];
describe('Markdown', () => {
before(() => {
cy.shouldNotRunOnCloudEdition();
// # Enable latex
cy.apiUpdateConfig({
ServiceSettings: {
EnableLatex: true,
EnableTesting: true,
},
});
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
cy.postMessage('hello');
});
});
testCases.forEach((testCase, i) => {
it(`MM-T1734_${i + 1} Code highlighting - ${testCase.name}`, () => {
// # Post markdown message
cy.postMessageFromFile(`markdown/${testCase.fileKey}.md`).wait(TIMEOUTS.ONE_SEC);
// * Verify that HTML Content is correct
cy.compareLastPostHTMLContentFromFile(`markdown/${testCase.fileKey}.html`);
});
});
it('MM-T2241 Markdown basics', () => {
// # Post markdown message
postMarkdownTest('/test url test-markdown-basics.md');
let postId;
let expectedHtml;
cy.getNthPostId(-2).then((id) => {
postId = id;
return cy.fixture('markdown/markdown_test_basic.html', 'utf-8');
}).then((html) => {
expectedHtml = html;
const postMessageTextId = `#postMessageText_${postId}`;
return cy.get(postMessageTextId).invoke('html');
}).then((res) => {
// * Verify that HTML Content is correct
assert(res === expectedHtml.replace(/\n$/, ''));
});
});
it('MM-T2242 Markdown lists', () => {
// # Post markdown message
postMarkdownTest('/test url test-markdown-lists.md');
let postId;
let expectedHtml;
cy.getNthPostId(-2).then((id) => {
postId = id;
return cy.fixture('markdown/markdown_list.html', 'utf-8');
}).then((html) => {
expectedHtml = html;
const postMessageTextId = `#postMessageText_${postId}`;
return cy.get(postMessageTextId).invoke('html');
}).then((res) => {
// * Verify that HTML Content is correct
assert(res === expectedHtml.replace(/\n$/, ''));
});
});
it('MM-T2244 Markdown tables', () => {
// # Post markdown message
postMarkdownTest('/test url test-tables.md');
let postId;
let expectedHtml;
cy.getNthPostId(-2).then((id) => {
postId = id;
return cy.fixture('markdown/markdown_tables.html', 'utf-8');
}).then((html) => {
expectedHtml = html;
const postMessageTextId = `#postMessageText_${postId}`;
return cy.get(postMessageTextId).invoke('html');
}).then((res) => {
// * Verify that HTML Content is correct
assert(res === expectedHtml.replace(/\n$/, ''));
});
});
it('MM-T2246 Markdown code syntax', () => {
// # Post markdown message
postMarkdownTest('/test url test-syntax-highlighting');
let postId;
let expectedHtml;
cy.getNthPostId(-2).then((id) => {
postId = id;
return cy.fixture('markdown/markdown_code_syntax.html', 'utf-8');
}).then((html) => {
expectedHtml = html;
const postMessageTextId = `#postMessageText_${postId}`;
return cy.get(postMessageTextId).invoke('html');
}).then((res) => {
// * Verify that HTML Content is correct
assert(res === expectedHtml.replace(/\n$/, ''));
});
});
});
function postMarkdownTest(slashCommand) {
// # Post markdown message
cy.postMessage(slashCommand).wait(TIMEOUTS.ONE_SEC);
cy.uiWaitUntilMessagePostedIncludes('Loaded data');
cy.wait(TIMEOUTS.ONE_SEC);
}

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

@@ -0,0 +1,120 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
import * as MESSAGES from '../../../fixtures/messages';
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('At-mention user autocomplete should open below the textbox in RHS when only one message is present -- KNOWN ISSUE: MM-45597', () => {
// # Add a single message to center textbox
cy.postMessage(MESSAGES.TINY);
// # Mouseover the last post and click post comment icon to open in RHS
cy.clickPostCommentIcon();
// * Check that the RHS is open
cy.get('#rhsContainer').should('be.visible');
// # Enter @ to allow opening of autocomplete box
cy.uiGetReplyTextBox().clear().type('@');
cy.uiGetReplyTextBox().then((replyTextbox) => {
cy.get('#suggestionList').then((suggestionList) => {
// * Verify that the suggestion box opened below the textbox
expect(replyTextbox[0].getBoundingClientRect().top).to.be.lessThan(suggestionList[0].getBoundingClientRect().top);
});
});
// # Close the RHS
cy.uiCloseRHS();
});
it('At-mention user autocomplete should open above the textbox in RHS is filled with messages', () => {
// # Add a single message to center textbox
cy.postMessage(MESSAGES.TINY);
// # Mouseover the last post and click post comment icon to open in RHS
cy.clickPostCommentIcon();
// * Check that the RHS is open
cy.get('#rhsContainer').should('be.visible');
// # Make a series of post so we are way past the first message in terms of scroll
Cypress._.times(2, () => {
cy.uiGetReplyTextBox().
clear().
invoke('val', MESSAGES.HUGE).
wait(TIMEOUTS.ONE_SEC).
type(' {backspace}{enter}');
});
// # Enter @ to allow opening of autocomplete box
cy.uiGetReplyTextBox().clear().type('@');
cy.uiGetReplyTextBox().then((replyTextbox) => {
cy.get('#suggestionList').then((suggestionList) => {
// * Verify that the suggestion box opened above the textbox
expect(replyTextbox[0].getBoundingClientRect().top).to.be.greaterThan(suggestionList[0].getBoundingClientRect().top);
});
});
// # Close the RHS
cy.uiCloseRHS();
});
it('MM-T70_1 At-mention user autocomplete is legible when it overlaps with channel header when drafting a long message containing a file attachment', () => {
// # Upload file, add message, add mention, verify no overlap
uploadFileAndAddAutocompleteThenVerifyNoOverlap();
});
it('MM-T70_2 At-mention user autocomplete is legible when it overlaps with channel header when drafting a long message containing a file attachment (1280x900 viewport)', () => {
// # Set to different viewport
cy.viewport(1280, 900);
// # Upload file, add message, add mention, verify no overlap
uploadFileAndAddAutocompleteThenVerifyNoOverlap();
});
});
function uploadFileAndAddAutocompleteThenVerifyNoOverlap() {
// # Upload file
cy.get('#fileUploadInput').attachFile('mattermost-icon.png');
// # Create and then type message to use
cy.uiGetPostTextBox().clear();
let message = 'h{shift}';
for (let i = 0; i < 12; i++) {
message += '{enter}h';
}
cy.uiGetPostTextBox().type(message);
// # Add the mention
cy.uiGetPostTextBox().type('{shift}{enter}').type('@');
cy.get('#channel-header').should('be.visible').then((header) => {
cy.get('#suggestionList').should('be.visible').then((list) => {
// # Wait for suggestions to be fully loaded
cy.wait(TIMEOUTS.HALF_SEC).then(() => {
// * Suggestion list should visibly render just within the channel header
cy.wrap(header[0].getBoundingClientRect().top).should('be.lt', list[0].getBoundingClientRect().top);
cy.wrap(header[0].getBoundingClientRect().bottom).should('be.lt', list[0].getBoundingClientRect().top);
});
});
});
}

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

@@ -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.
// ***************************************************************
// Stage: @prod
// Group: @channels @auto_response @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Auto Response In DMs', () => {
const AUTO_RESPONSE_MESSAGE = 'Out of Office';
const MESSAGES = ['Message1', 'Message2', 'Message3'];
let userA;
let userB;
let testTeam;
let offTopicUrl;
before(() => {
// # Enable ExperimentalEnableAutomaticReplies setting
cy.apiUpdateConfig({TeamSettings: {ExperimentalEnableAutomaticReplies: true}});
// # Create a new team
cy.apiInitSetup().then((out) => {
userA = out.user;
testTeam = out.team;
offTopicUrl = out.offTopicUrl;
// # Create a second user
cy.apiCreateUser().then(({user}) => {
userB = user;
cy.apiAddUserToTeam(testTeam.id, userB.id);
});
});
});
it('MM-T4004 Out-of-office automatic reply sends only one in a direct message within one calendar day', () => {
// # Login as userB
cy.apiLogin(userB);
// # Visit off topic channel
cy.visit(offTopicUrl);
// # Open 'Settings' modal and view 'Notifications'
cy.uiOpenSettingsModal().within(() => {
// # Click on 'Edit' for 'Automatic Direct Message Replies
cy.get('#auto-responderEdit').should('be.visible').click();
// # Click on 'Enabled' checkbox
cy.get('#autoResponderActive').should('be.visible').click();
// # Clear default auto response message
cy.get('#autoResponderMessageInput').should('be.visible').clear();
// # Enter new Auto Response Message
cy.get('#autoResponderMessageInput').should('be.visible').clear().type(AUTO_RESPONSE_MESSAGE);
// # Save and close the modal
cy.uiSaveAndClose();
});
// # Logout userB
cy.apiLogout();
// # Login as userA
cy.apiLogin(userA);
// # Visit off topic channel
cy.visit(offTopicUrl);
// # Send direct message to userB
cy.uiAddDirectMessage().click();
cy.get('#selectItems input').typeWithForce(userB.username);
cy.findByText('Loading', {timeout: TIMEOUTS.FIVE_SEC}).should('be.visible');
cy.findByText('Loading').should('not.exist');
cy.get('#multiSelectList').findByText(`@${userB.username}`).click();
cy.findByText('Go').click();
cy.postMessage(MESSAGES[0]);
// * Verify if auto response message in last post is displayed
cy.getLastPostId().then((replyId) => {
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', AUTO_RESPONSE_MESSAGE);
});
// # Send another direct message to userB
cy.postMessage(MESSAGES[1]);
// * Verify if recent direct message and not the auto response in last post is displayed
cy.getLastPostId().then((replyId) => {
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', MESSAGES[1]);
});
// # Send another direct message to userB
cy.postMessage(MESSAGES[2]);
// * Verify if recent direct message and not the auto response in last post is displayed
cy.getLastPostId().then((replyId) => {
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', MESSAGES[2]);
});
});
});

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

@@ -0,0 +1,108 @@
// 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
describe('Message', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T87 Text in bullet points is the same size as text above and below it', () => {
// # Post a message
cy.uiGetPostTextBox().clear().
type('This is a normal sentence.').
type('{shift}{enter}{enter}').
type('1. this is point 1').
type('{shift}{enter}').
type(' - this is a bullet under 1').
type('{shift}{enter}{enter}').
type('This is more normal text.').
type('{enter}');
// # Get last postId
cy.getLastPostId().then((postId) => {
const postMessageTextId = `#postMessageText_${postId}`;
// * Verify text sizes
cy.get(postMessageTextId).within(() => {
const expectedSize = '13.5px';
cy.get('p').first().should('have.text', 'This is a normal sentence.').and('have.css', 'font-size', expectedSize);
cy.get('ol li').first().should('have.text', 'this is point 1\nthis is a bullet under 1').and('have.css', 'font-size', expectedSize);
cy.get('ol li ul li').should('have.text', 'this is a bullet under 1').and('have.css', 'font-size', expectedSize);
cy.get('p').last().should('have.text', 'This is more normal text.').and('have.css', 'font-size', expectedSize);
});
});
});
it('MM-T1321 WebApp: Truncated Numbered List on Chat History Panel', () => {
const bulletMessages = [
{
text:
'9. firstBullet{shift}{enter}10. secondBullet{shift}{enter}11. thirdBullet',
counter: 9,
},
{
text:
'9999. firstBullet{shift}{enter}10000. secondBullet{shift}{enter}10001. thirdBullet',
counter: 9999,
},
{
text:
'999999. firstBullet{shift}{enter}1000000. secondBullet{shift}{enter}1000001. thirdBullet',
counter: 999999,
},
];
bulletMessages.forEach((bulletMessage) => {
// # Post the message containing bullets
cy.uiGetPostTextBox().
clear().
type(bulletMessage.text).
type('{enter}');
// # Get the last posted message
cy.getLastPost().within(() => {
// * Verify that messages are wrapped in li tags
cy.findByText('firstBullet').
should('be.visible').
parents('li').
should('exist');
cy.findByText('secondBullet').
should('be.visible').
parents('li').
should('exist');
cy.findByText('thirdBullet').
should('be.visible').
parents('li').
should('exist');
// * Verify that li tags have ol as their parent
cy.findByText('firstBullet').
parents('ol').
should('exist').
as('olParent');
// * Verify that ol tag starts from the start number
cy.get('@olParent').
should(
'have.css',
'counter-reset',
`list ${bulletMessage.counter - 1}`,
).
and('have.class', 'markdown__list');
});
});
});
});

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

@@ -0,0 +1,40 @@
// 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 @markdown
describe('Messaging', () => {
before(() => {
// # resize window to mobile view
cy.viewport('iphone-6');
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T73 Mobile view: Clicking on airplane icon does not open file attachment modal but sends the message', () => {
// # type some characters in the message box
const message = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ';
cy.uiGetPostTextBox().clear().type(message);
// # click send
cy.findByTestId('SendMessageButton').click();
// # check if message sent correctly
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('be.visible').and('have.text', message);
});
// # attachment modal does not show up
cy.get('.a11y__popup').should('not.exist');
});
});

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

@@ -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 @not_cloud @messaging @plugin
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
import {drawPlugin} from '../../../utils/plugins';
describe('M17448 Does not post draft message', () => {
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.shouldHavePluginUploadEnabled();
// # Update config
cy.apiUpdateConfig({
PluginSettings: {
Enable: true,
},
});
// # Upload and enable "Draw" plugin
cy.apiUploadAndEnablePlugin(drawPlugin);
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('on successful upload via "Draw" plugin', () => {
const draft = `Draft message ${getRandomId()}`;
cy.uiGetPostTextBox().clear().type(draft);
// # Open file upload options and select draw plugin
cy.get('#fileUploadButton').click();
cy.get('#fileUploadOptions').findByText('Draw').click();
// * Upload a file and verify drafted message still exist in textbox
cy.get('canvas').trigger('pointerdown').trigger('pointerup').click();
cy.findByText('Upload').should('be.visible').click();
cy.uiGetPostTextBox().
wait(TIMEOUTS.HALF_SEC).
should('have.text', draft);
});
it('on upload cancel via "Draw" plugin', () => {
const draft = `Draft message ${getRandomId()}`;
cy.uiGetPostTextBox().clear().type(draft);
// # Open file upload options and select draw plugin
cy.get('#fileUploadButton').click();
cy.get('#fileUploadOptions').findByText('Draw').click();
// * Cancel file upload process and verify drafted message still exist in textbox
cy.findByText('Cancel').should('be.visible').click();
cy.uiGetPostTextBox().
wait(TIMEOUTS.HALF_SEC).
should('have.text', draft);
});
it('on upload attempt via "Your Computer', () => {
const draft = `Draft message ${getRandomId()}`;
cy.uiGetPostTextBox().clear().type(draft);
// # Open file upload options and select "Your Computer"
cy.get('#fileUploadButton').click();
cy.get('#fileUploadOptions').findByText('Your computer').click();
// * Verify drafted message still exist in textbox
cy.uiGetPostTextBox().wait(TIMEOUTS.HALF_SEC).
should('have.text', draft);
});
});

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

@@ -0,0 +1,47 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T174 Autocomplete should close if tildes are deleted using backspace', () => {
const msg = 'foo';
// # Make a post
cy.postMessage(msg);
// # Hit the "up" arrow to open the edit modal
cy.uiGetPostTextBox().type('{uparrow}');
// # Insert a tilde (~) at the beginning of the post to be edited
cy.get('#edit_textbox').should('be.visible').wait(TIMEOUTS.HALF_SEC).type('{home}~');
// * autocomplete opens
cy.get('#suggestionList').should('be.visible');
// # Delete the tilde by backspacing
cy.get('#edit_textbox').type('{home}{rightarrow}{backspace}');
// * autocomplete closes
cy.get('#suggestionList').should('not.exist');
// finish editing
cy.get('#edit_textbox').should('be.visible').wait(TIMEOUTS.HALF_SEC).type('{enter}');
});
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getAdminAccount} from '../../../support/env';
describe('Messaging', () => {
const admin = getAdminAccount();
let testChannelId;
let testChannelUrl;
before(() => {
// # Login as test user and visit test channel
cy.apiInitSetup({loginAfter: true}).then((out) => {
testChannelId = out.channel.id;
testChannelUrl = out.channelUrl;
cy.visit(testChannelUrl);
});
});
it('MM-T113 Delete a Message during reply, other user sees "(message deleted)"', () => {
const message = 'aaa';
const draftMessage = 'draft';
// # Type message to use
cy.postMessageAs({sender: admin, message, channelId: testChannelId});
// # Click Reply button
cy.clickPostCommentIcon();
// # Write message on reply box
cy.uiGetReplyTextBox().type(draftMessage);
// # Remove message from the other user
cy.getLastPostId().then((postId) => {
cy.externalRequest({user: admin, method: 'DELETE', path: `posts/${postId}`});
// # Wait for the message to be deleted
cy.wait(TIMEOUTS.HALF_SEC);
// * Aria labels should not contain original message
cy.get(`#post_${postId}, #rhsPost_${postId}`).each((el) => {
cy.wrap(el).
should('have.attr', 'aria-label').
and('not.contain', message);
});
// # Send message
cy.uiGetReplyTextBox().type('{enter}');
// * Post Deleted Modal should be visible
cy.findAllByTestId('postDeletedModal').should('be.visible');
// # Close the modal
cy.findAllByTestId('postDeletedModalOkButton').click();
// // * The message should not have been sent
cy.uiGetRHS().find('.post__content').each((content) => {
cy.wrap(content).findByText(draftMessage).should('not.exist');
});
// * Textbox should still have the draft message
cy.uiGetReplyTextBox().should('contain', draftMessage);
// # Try to post the message one more time pressing enter
cy.uiGetReplyTextBox().type('{enter}');
// * The modal should have appeared again
cy.findAllByTestId('postDeletedModal').should('be.visible');
// # Close the modal by hitting the OK button
cy.findAllByTestId('postDeletedModalOkButton').click();
// // * The message should not have been sent
cy.uiGetRHS().find('.post__content').each((content) => {
cy.wrap(content).findByText(draftMessage).should('not.exist');
});
// * Textbox should still have the draft message
cy.uiGetReplyTextBox().should('contain', draftMessage);
// # Change to the other user and go to test channel
cy.apiAdminLogin();
cy.visit(testChannelUrl);
// * Post should not exist
cy.get(`#post_${postId}`).should('not.exist');
});
});
});

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

@@ -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 @messaging
describe('Message deletion', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({offTopicUrl}) => {
cy.visit(offTopicUrl);
});
});
it('MM-T112 Delete a parent message that has a reply - reply thread', () => {
// # Post message in center.
cy.postMessage('test message deletion');
cy.getLastPostId().then((parentMessageId) => {
// # Mouseover the post and click post comment icon.
cy.clickPostCommentIcon();
// * Check that the RHS is open
cy.uiGetRHS();
// # Post a reply in RHS.
cy.postMessageReplyInRHS('test message reply in RHS');
cy.getLastPostId().then((replyMessageId) => {
// # Click post dot menu in center.
cy.clickPostDotMenu(parentMessageId);
// # Click delete button.
cy.get(`#delete_post_${parentMessageId}`).click();
// * Check that confirmation dialog is open.
cy.get('#deletePostModal').should('be.visible');
// * Check that confirmation dialog contains correct text
cy.get('#deletePostModal').should('contain', 'Are you sure you want to delete this Post?');
// * Check that confirmation dialog shows that the post has one comment on it
cy.get('#deletePostModal').should('contain', 'This post has 1 comment on it.');
// # Confirm deletion.
cy.get('#deletePostModalButton').click();
// * Check that the modal is closed
cy.get('#deletePostModal').should('not.exist');
// * Check that the RHS is closed.
cy.uiGetRHS({exist: false});
// * Check that parent message is no longer visible.
cy.get(`#post_${parentMessageId}`).should('not.exist');
// * Check that reply message is no longer visible.
cy.get(`#post_${replyMessageId}`).should('not.exist');
});
cy.getLastPostId().then((replyMessageId) => {
// * Check that last message do not contain (message deleted)
cy.get(`#post_${replyMessageId}`).should('not.contain', '(message deleted)');
});
});
});
});

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

@@ -0,0 +1,121 @@
// 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
import localForage from 'localforage';
import {verifyDraftIcon} from './helpers';
describe('Message Draft Persistance', () => {
let testChannel;
let offTopicUrl;
before(() => {
// # Create new team and new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then((out) => {
testChannel = out.channel;
offTopicUrl = out.offTopicUrl;
});
});
beforeEach(() => {
localForage.clear();
});
it('MM-T4639 Persisting a draft in the current channel', () => {
const testText = 'this is a test';
// # Go to Off-Topic
cy.visit(offTopicUrl);
// # Type some text into the post textbox
cy.uiGetPostTextBox().type(testText);
// # Reload the page
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500).reload();
// * Ensure the draft is back in the post textbox
cy.uiGetPostTextBox().should('have.text', testText);
});
it('MM-T4640 Persisting a draft in another channel', () => {
const testText = 'this is another test';
// # Go to Off-Topic
cy.visit(offTopicUrl);
// # Type some text into the post textbox
cy.uiGetPostTextBox().clear().type(testText);
// # Switch to another channel
cy.get(`#sidebarItem_${testChannel.name}`).click();
// * Ensure the post textbox was cleared
cy.uiGetPostTextBox().should('be.empty');
// * Ensure Off-Topic has the draft icon
verifyDraftIcon('off-topic', true);
// # Reload the page
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500).reload();
// * Ensure the post textbox is still empty
cy.uiGetPostTextBox().should('be.empty');
// * Ensure Off-Topic still has the draft icon
cy.get('#sidebarItem_off-topic').
should('be.visible').
findByTestId('draftIcon').
should('be.visible');
// # Switch back to Off-Topic
cy.get('#sidebarItem_off-topic').click();
// * Ensure the draft is back in the post textbox
cy.uiGetPostTextBox().should('have.text', testText);
});
it('MM-T4641 Migration of drafts from redux-persist@4.0.0', () => {
const testText = 'this is a migration test';
// # Go to Off-Topic
cy.visit(offTopicUrl);
// # Add a fake old draft to storage
cy.then(() => {
localForage.setItem(`reduxPersist:storage:draft_${testChannel.id}`, JSON.stringify({
timestamp: new Date(),
value: {
message: testText,
fileInfos: [],
uploadsInProgress: [],
},
}));
});
// # Refresh the app to trigger migration
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500).reload();
// * Ensure the other channel has the draft icon
verifyDraftIcon(testChannel.name, true);
// # Switch to that channel
cy.get(`#sidebarItem_${testChannel.name}`).click();
// * Ensure the draft is in the post textbox
cy.uiGetPostTextBox().should('have.text', testText);
});
});

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

@@ -0,0 +1,40 @@
// 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
describe('Message Draft', () => {
let testTeam;
before(() => {
// # Create new team and new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team, offTopicUrl}) => {
testTeam = team;
cy.visit(offTopicUrl);
});
});
it('MM-T130 Message Draft Pencil Icon- Text', () => {
// * Validate if the draft icon is not visible on the sidebar before making a draft
cy.get('#sidebarItem_off-topic').findByTestId('draftIcon').should('not.exist');
// # Type in some text into the text area of the opened channel
cy.uiGetPostTextBox().type('comm');
// # Go to another test channel without submitting the draft in the previous channel
cy.get('#sidebarItem_town-square').click({force: true});
// * Validate if the newly navigated channel is open
cy.url().should('include', `/${testTeam.name}/channels/town-square`);
// * Validate if the draft icon is visible on side bar on the previous channel with a draft
cy.get('#sidebarItem_off-topic').findByTestId('draftIcon').should('be.visible');
});
});

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

@@ -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 @messaging
import {verifyDraftIcon} from './helpers';
describe('Message Draft and Switch Channels', () => {
let testTeam;
let testChannel;
before(() => {
// # Create new team and new user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
testTeam = team;
testChannel = channel;
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
});
it('MM-T131 Message Draft Pencil Icon - CTRL/CMD+K & "Jump to"', () => {
const {name, display_name: displayName} = testChannel;
const message = 'message draft test';
// * Validate if the draft icon is not visible at LHS before making a draft
verifyDraftIcon(name, false);
// # Go to test channel and check if it opened correctly
openChannelFromLhs(testTeam.name, displayName, name);
// # Type a message in the input box but do not send
cy.findByRole('textbox', `write to ${displayName.toLowerCase()}`).should('be.visible').type(message);
// # Switch to another channel and check if it opened correctly
openChannelFromLhs(testTeam.name, 'Off-Topic', 'off-topic');
// * Validate if the draft icon is visible at LHS
verifyDraftIcon(name, true);
// # Press CTRL/CMD+K shortcut to open Quick Channel Switch modal
cy.typeCmdOrCtrl().type('K', {release: true});
// * Verify that the switch model is shown
cy.findAllByRole('dialog').first().findByText('Find Channels').should('be.visible');
// # Type the first few letters of the channel name you typed the message draft in
cy.findByRole('textbox', {name: 'quick switch input'}).type(displayName.substring(0, 3));
// * Suggestion list is visible
cy.get('#suggestionList').should('be.visible').within(() => {
// * A pencil icon before the channel name in the filtered list is visible
cy.get(`#switchChannel_${name}`).find('.icon-pencil-outline').should('be.visible');
// # Click to switch back to the test channel
cy.get(`#switchChannel_${name}`).click({force: true});
});
// * Draft is saved in the text input box of the test channel
cy.findByRole('textbox', `write to ${displayName.toLowerCase()}`).should('be.visible').and('have.text', message);
});
});
function openChannelFromLhs(teamName, channelName, name) {
// # Go to test channel and check if it opened correctly
cy.uiGetLhsSection('CHANNELS').findByText(channelName).click();
cy.url().should('include', `/${teamName}/channels/${name}`);
}

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

@@ -0,0 +1,50 @@
// 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
describe('Message Draft with attachment and Switch Channels', () => {
let testChannel1;
let testChannel2;
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
testChannel1 = channel;
cy.apiCreateChannel(team.id, 'channel', 'Channel').then((out) => {
testChannel2 = out.channel;
});
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('MM-T129 Message Draft Pencil Icon - No text, only file attachment', () => {
cy.get(`#sidebarItem_${testChannel1.name}`).click({force: true});
// # Validate if the channel has been opened
cy.url().should('include', '/channels/' + testChannel1.name);
// # Validate if the draft icon is not visible on the sidebar before making a draft
cy.get(`#sidebarItem_${testChannel1.name}`).findByTestId('draftIcon').should('not.exist');
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile('mattermost-icon.png');
// # Go to test channel without submitting the draft in the previous channel
cy.get(`#sidebarItem_${testChannel2.name}`).click({force: true});
// # Validate if the newly navigated channel is open
cy.url().should('include', '/channels/' + testChannel2.name);
// # Validate if the draft icon is visible in side bar for the previous channel
cy.get(`#sidebarItem_${testChannel1.name}`).findByTestId('draftIcon').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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Delete the post on text clear', () => {
let offtopiclink;
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
offtopiclink = `/${team.name}/channels/off-topic`;
cy.visit(offtopiclink);
});
});
it('MM-T2146 Remove all text from a post (no attachment)', () => {
// # Go to a test channel on the side bar
cy.get('#sidebarItem_off-topic').click({force: true});
// * Validate if the channel has been opened
cy.url().should('include', offtopiclink);
// # Type 'This is sample text' and submit
cy.postMessage('This is sample text');
// # Get last post ID
cy.getLastPostId().then((postID) => {
// # click dot menu button
cy.clickPostDotMenu();
// # click edit post
cy.get(`#edit_post_${postID}`).click();
// # clear Message and enter
cy.get('#edit_textbox').should('be.visible').and('be.focused').wait(TIMEOUTS.HALF_SEC).clear().type('{enter}');
// # Press Enter to confirm deleting the post
cy.focused().click(); // pressing Enter on buttons is not supported in Cypress, so we use click instead
// * Assert post message disappears
cy.get(`#postMessageText_${postID}`).should('not.exist');
});
});
});

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

@@ -0,0 +1,151 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Post Edit History', () => {
let offtopiclink: string;
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
offtopiclink = `/${team.name}/channels/off-topic`;
cy.visit(offtopiclink);
});
});
beforeEach(() => {
// * Validate if the channel has been opened
cy.url().should('include', offtopiclink);
// # Post a message
cy.postMessage('This is a sample message');
cy.getLastPostId().then((postId) => {
editMessage(postId);
});
});
it('MM-T5381_1 Show and restore older versions of a message', () => {
// # Get the last post id
cy.getLastPostId().then((postId) => {
openEditHistory(postId);
// * Check if the current version of the message is visible
cy.get(`#rhsPostMessageText_${postId}`).should('have.text', 'This is the final version of the sample message');
// * Check if the previous versions of the message are visible and correct in the history
cy.get('#rhsContainer').find('.edit-post-history__container').should('have.length', 3);
cy.get('#rhsContainer').find('.edit-post-history__container').eq(1).click();
cy.get('#rhsContainer').find('.post-message__text').eq(1).should('have.text', 'This is an edited sample message');
cy.get('#rhsContainer').find('.edit-post-history__container').eq(2).click();
cy.get('#rhsContainer').find('.post-message__text').eq(2).should('have.text', 'This is a sample message');
// # Click the restore button on the first version of the message
cy.get('#rhsContainer').find('.restore-icon').eq(0).click();
// # Confirm the restore
cy.get('.confirm').click();
// # Wait for the message to be updated
cy.wait(TIMEOUTS.HALF_SEC);
// * Check if the message has been updated to the first version
cy.get(`#postMessageText_${postId}`).should('have.text', 'This is an edited sample message Edited');
});
});
it('MM-T5381_2 Show, restore older versions of a message and click undo in toast', () => {
// # Get the last post id
cy.getLastPostId().then((postId) => {
openEditHistory(postId);
// # Click the restore button on the first version of the message
cy.get('#rhsContainer').find('.restore-icon').eq(0).click();
// # Confirm the restore
cy.get('.confirm').click();
// # Wait for the message to be updated
cy.wait(TIMEOUTS.HALF_SEC);
// # Click the undo button on the toast
cy.get('.info-toast__undo').click();
// # Wait for the message to be updated
cy.wait(TIMEOUTS.HALF_SEC);
// * Check if the message has been updated to the first version
cy.get(`#postMessageText_${postId}`).should('have.text', 'This is the final version of the sample message Edited');
});
});
it('MM-T5381_3 Edit history should not be available when user lacks edit own posts permissions', () => {
// # Login as sysadmin and update edit own posts permissions
cy.apiAdminLogin();
cy.apiUpdateConfig({
ServiceSettings: {
PostEditTimeLimit: -1,
},
});
cy.reload();
cy.getLastPostId().then((postId) => {
// # Click the edited indicator on the post
cy.get(`#postEdited_${postId}`).click();
// * Confirm edit history is not visible
cy.get('#rhsContainer').find('.sidebar--right__title').should('not.contain.text', 'Edit History');
});
});
});
const editMessage = (postId: string) => {
// # click dot menu button
cy.clickPostDotMenu();
// # click edit post
cy.get(`#edit_post_${postId}`).click();
// # Edit the message
cy.get('#edit_textbox').
should('be.visible').
and('be.focused').
wait(TIMEOUTS.HALF_SEC).
clear().
type('This is an edited sample message').
type('{enter}');
// # click dot menu button
cy.clickPostDotMenu();
// # click edit post
cy.get(`#edit_post_${postId}`).click();
// # Edit the message again
cy.get('#edit_textbox').
should('be.visible').
and('be.focused').
wait(TIMEOUTS.HALF_SEC).
clear().
type('This is the final version of the sample message').
type('{enter}');
};
const openEditHistory = (postId: string) => {
// # Click the edited indicator on the post
cy.get(`#postEdited_${postId}`).click();
// # Check if the edit history is visible
cy.get('#rhsContainer').should('exist');
};

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let offtopiclink;
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
offtopiclink = `/${team.name}/channels/off-topic`;
cy.visit(offtopiclink);
});
});
it('MM-T99 Edit Post with attachment, paste text in middle', () => {
// # Got to a test channel on the side bar
cy.get('#sidebarItem_off-topic').click({force: true});
// * Validate if the channel has been opened
cy.url().should('include', offtopiclink);
// # Upload a file on center view
cy.get('#fileUploadInput').attachFile('mattermost-icon.png');
// # Type 'This is sample text' and submit
cy.postMessage('This is sample text');
// # Get last post ID
cy.getLastPostId().then((postID) => {
// # click dot menu button
cy.clickPostDotMenu();
// # click edit post
cy.get(`#edit_post_${postID}`).click();
// # Edit message and finish by hitting `enter`
cy.get('#edit_textbox').
should('be.visible').
and('be.focused').
wait(TIMEOUTS.HALF_SEC).
type('{leftarrow}{leftarrow}{leftarrow}{leftarrow}').type('add ').type('{enter}');
// * Assert post message should contain 'This is sample add text'
cy.get(`#postMessageText_${postID}`).should('have.text', 'This is sample add text Edited');
cy.get(`#${postID}_message`).within(() => {
// * Assert file attachment should still exist
cy.get('.file-view--single').should('be.visible');
});
});
});
});

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

@@ -0,0 +1,64 @@
// 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 @emoji
const normalSize = '21px';
const jumboSize = '32px';
const testCases = [
{message: 'This is a normal reply with emoji :smile:', emojiSize: normalSize},
{message: ':smile:', emojiSize: jumboSize},
{message: ':smile: :yum:', emojiSize: jumboSize},
];
function verifyLastPostStyle(expectedSize) {
// # Get Last Post ID
cy.getLastPostId().then((postId) => {
const postMessageTextId = `#rhsPostMessageText_${postId}`;
// # Get Each Emoji from Reply Window RHS for the postId
cy.get(`#rhsContainer .post-right__content ${postMessageTextId} span.emoticon`).each(($el) => {
cy.wrap($el).as('message');
// * Verify the size of the emoji
cy.get('@message').should('have.css', 'height', expectedSize).and('have.css', 'width', expectedSize);
});
});
}
describe('Message', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T162 Emojis show as jumbo in reply thread', () => {
// # Post a message
const messageText = 'This is a test message';
cy.postMessage(messageText);
// # Get Last Post ID
cy.getLastPostId().then((postId) => {
// # Mouseover the post and click post comment icon.
cy.clickPostCommentIcon(postId);
testCases.forEach((testCase) => {
// # Post a reply
cy.postMessageReplyInRHS(testCase.message);
// * Verify the size of the emoji
verifyLastPostStyle(testCase.emojiSize);
});
});
});
});

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

@@ -0,0 +1,50 @@
// 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
describe('Hide ephemeral message on refresh', () => {
let offtopiclink;
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
offtopiclink = `/${team.name}/channels/off-topic`;
cy.visit(offtopiclink);
});
});
it('MM-T2197 Ephemeral message disappears in center after refresh', () => {
// # Got to a test channel on the side bar
cy.get('#sidebarItem_off-topic').click({force: true});
// * Validate if the channel has been opened
cy.url().should('include', offtopiclink);
// # Set initial status to online
cy.apiUpdateUserStatus('online');
// # Cause an ephemeral message to appear
cy.postMessage('/offline ');
// # Get last post ID
cy.getLastPostId().then((postID) => {
// * Assert message presence
cy.get(`#postMessageText_${postID}`).should('have.text', 'You are now offline');
// # Refresh the page
cy.reload();
cy.visit(offtopiclink);
// * Assert message disappearing
cy.get(`#postMessageText_${postID}`).should('not.exist');
});
});
});

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

@@ -0,0 +1,40 @@
// 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
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T182 Typing using CJK keyboard', () => {
const msg = '안녕하세요';
const msg2 = '닥터 카레브';
// # Make a post
cy.postMessage(msg);
// * Check that last message do contain right message
cy.getLastPost().should('contain', msg);
// # Mouseover the post and click post comment icon.
cy.clickPostCommentIcon();
// # Post a reply in RHS.
cy.postMessageReplyInRHS(msg2);
// * Check that last message do contain right message
cy.getLastPost().should('contain', msg2);
});
});

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

@@ -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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T196 Markdown correctly parses "://///" and doesn\'t break the channel', () => {
// # Go to Off-topic as test channel
cy.get('#sidebarItem_off-topic').click({force: true});
// # Validate if the channel has been opened
cy.url().should('include', '/channels/off-topic');
// # type in the message "://///"
const message = '://///';
const textAfterParsed = `:confused:${message.substr(2)}`;
cy.postMessage(message);
// # check if message sent correctly, it should parse it as 😕////"
cy.getLastPostId().then((postId) => {
verifyPostedMessage(postId, textAfterParsed);
// # check if message still correctly parses after reload
cy.reload();
verifyPostedMessage(postId, textAfterParsed);
});
});
function verifyPostedMessage(postId, text) {
cy.get(`#postMessageText_${postId}`).should('be.visible').within((el) => {
cy.wrap(el).should('have.text', text);
cy.get('.emoticon').should('be.visible').and('have.attr', 'title', ':confused:');
});
}
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Message permalink', () => {
let testTeam;
let testChannel;
let testUser;
let otherUser;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id).then(() => {
// # Login as test user and create DM with other user
cy.apiLogin(testUser);
cy.apiCreateDirectChannel([testUser.id, otherUser.id]);
});
});
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/messages/@${otherUser.username}`);
});
it('MM-T177 Copy a permalink and paste into another channel', () => {
// # Post message to use
const message = 'Hello' + Date.now();
cy.postMessage(message);
cy.getLastPostId().then((postId) => {
const permalink = `${Cypress.config('baseUrl')}/${testTeam.name}/pl/${postId}`;
// # Check if ... button is visible in last post right side
cy.get(`#CENTER_button_${postId}`).should('not.be.visible');
// # Click on ... button of last post
cy.clickPostDotMenu(postId);
// # Click on "Copy Link"
cy.uiClickCopyLink(permalink, postId);
const dmChannelLink = `/${testTeam.name}/messages/@${otherUser.username}`;
cy.apiSaveMessageDisplayPreference('compact');
verifyPermalink(message, testChannel, permalink, postId, dmChannelLink);
cy.apiSaveMessageDisplayPreference('clean');
verifyPermalink(message, testChannel, permalink, postId, dmChannelLink);
});
});
it('Permalink highlight should fade after timeout and change to channel url', () => {
// # Post message to use
const message = 'Hello' + Date.now();
cy.postMessage(message);
cy.getLastPostId().then((postId) => {
const link = `/${testTeam.name}/messages/@${otherUser.username}/${postId}`;
cy.visit(link);
cy.url().should('include', link);
cy.get(`#post_${postId}`, {timeout: TIMEOUTS.HALF_MIN}).should('have.class', 'post--highlight');
cy.clock();
cy.tick(6000);
cy.get(`#post_${postId}`).should('not.have.class', 'post--highlight');
cy.url().should('not.include', postId);
});
});
});
function verifyPermalink(message, testChannel, permalink, postId, dmChannelLink) {
// # Click on test public channel
cy.get('#sidebarItem_' + testChannel.name).click({force: true});
cy.wait(TIMEOUTS.HALF_SEC);
// # Paste link on postlist area
cy.postMessage(permalink);
// # Get last post id from that postlist area
cy.getLastPostId().then((id) => {
// # Click on permalink
cy.get(`#postMessageText_${id} > p > .markdown__link`).scrollIntoView().click();
// # Check if url include the permalink
cy.url().should('include', `${dmChannelLink}/${postId}`);
// * Check if url redirects back to parent path eventually
cy.wait(TIMEOUTS.FIVE_SEC).url().should('include', dmChannelLink).and('not.include', `/${postId}`);
});
// # Get last post id from open channel
cy.getLastPostId().then((clickedpostid) => {
// # Check the sent message
cy.get(`#postMessageText_${clickedpostid}`).should('be.visible').and('have.text', message);
});
}

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

@@ -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: @channels @messaging
const pinnedPosts = [];
/**
* Pin post by cliking on [...] then 'Pin to channel', and add the pinned post to pinnedPosts
* @param {String} postId - post ID of the post to pin
*/
function pinPost(index) {
cy.getNthPostId(index).then((postId) => {
cy.clickPostDotMenu(postId);
cy.get(`#pin_post_${postId}`).click();
pinnedPosts.push(postId);
});
}
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T142 Pinning or un-pinning older message does not cause it to display at bottom of channel Pinned posts display in RHS with newest at top', () => {
// * Ensure that the channel view is loaded
cy.uiGetPostTextBox();
// # Post messages
const olderPost = 7;
for (let i = olderPost; i > 0; --i) {
cy.postMessage(i);
}
// # Ensure there are a couple of pinned posts in the channel already:
// # Pin 4th and 6th posts from latest
pinPost(-4);
pinPost(-6);
// # Click on Pinned Posts channel header button
cy.get('#channelHeaderPinButton').click();
// * Verify the pinned posts (4 & 6) are added to the Pinned Posts list on the right hand side
cy.get('#search-items-container').children().should('have.length', 2);
// # Close out of the Pinned Post side bar
cy.get('#searchResultsCloseButton').click();
// # Get last post (7th)
cy.getNthPostId(-olderPost).then((postId) => {
// # Scroll into view
cy.get(`#post_${postId}`).scrollIntoView();
// # Click [...] > Pin to channel
pinPost(-olderPost);
// # Store the post message of the 20th post as pinnedPostText
cy.get(`#postMessageText_${postId}`).invoke('text').then((pinnedPostText) => {
// # Store the last post ID as lastPostId
cy.getLastPostId().then((lastPostId) => {
// # Scroll down to the last post
cy.get(`#post_${lastPostId}`).scrollIntoView();
// * Verify that message just pinned does not display at bottom in center channel as newest in channel
cy.get(`#postMessageText_${lastPostId}`).should('not.contain', pinnedPostText);
// # Click pin icon to view pinned posts in right-hand-side
cy.get('#channelHeaderPinButton').click();
// * Verify that there are now 3 pinned messages in the right-hand-side
cy.get('#search-items-container').children().should('have.length', 3);
// * Verify sorted by newest at top: 1st post is the newest, and 3rd post is the oldest
cy.get('#search-items-container').children().eq(0).get(`#postMessageText_${lastPostId}`);
cy.get('#search-items-container').children().eq(2).get(`#postMessageText_${postId}`).and('contain', pinnedPostText);
// # Scroll back up to the last pinned post.
cy.get(`#post_${postId}`).scrollIntoView();
// * When un-pinned in center, post disappears from pinned posts list in right-hand-side:
// # Click [...] > Un-pin from channel
cy.clickPostDotMenu(postId);
cy.get(`#unpin_post_${postId}`).click();
// * Right-hand-side only has 2 initially pinned posts
cy.get('#search-items-container').children().should('have.length', 2);
// * Right-hand-side does not have the last pinned post.
cy.get('#search-items-container').children().should('not.contain', `#rhsPostMessageText_${postId}`);
});
});
});
});
});

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

@@ -0,0 +1,85 @@
// 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
describe('Emoji reactions to posts/messages in GM channels', () => {
let userOne;
let userTwo;
let testTeam;
let testGroupChannel;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
userOne = user;
cy.apiCreateUser().then((data) => {
userTwo = data.user;
cy.apiAddUserToTeam(testTeam.id, userTwo.id);
cy.apiCreateGroupChannel([userOne.id, userTwo.id]).then(({channel}) => {
testGroupChannel = channel;
});
// # Login as userOne and town-square
cy.apiLogin(userOne);
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
});
it('MM-T471 add a reaction to a message in a GM', () => {
// # Switch to the GM
cy.visit(`/${testTeam.name}/messages/${testGroupChannel.name}`);
// # Post a message
cy.postMessage('This is a post');
// * Verify that the Add a Reaction button works
cy.getLastPostId().then((postId) => {
// # Click the add reaction icon
cy.clickPostReactionIcon(postId);
// # Choose "slightly_frowning_face" emoji
cy.clickEmojiInEmojiPicker('slightly_frowning_face');
// * The number shown on the reaction is incremented by 1
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).
should('have.text', '1').
should('be.visible');
});
// * Verify that the Add Reaction button is visible at the right times
cy.getLastPostId().then((postId) => {
// * Verify that the Add Reaction button isn't visible
cy.findByLabelText('Add a reaction').should('not.be.visible');
// # Focus on the post since we can't hover with Cypress
cy.get(`#post_${postId}`).focus().tab().tab();
// * Verify that the Add Reaction button is now visible
cy.findByLabelText('Add a reaction').should('be.visible');
// # Click somewhere to clear the focus
cy.get('#channelIntro').click();
// * Verify that the Add Reaction button is no longer visible
cy.findByLabelText('Add a reaction').should('not.be.visible');
// # Resize window to mobile view
cy.viewport('iphone-6');
// * Verify that the Add Reaction button is once again visible
cy.findByLabelText('Add a reaction').should('be.visible');
});
});
});

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

@@ -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 @smoke
// Group: @channels @messaging
describe('Emoji reactions to posts/messages', () => {
let userOne;
let userTwo;
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
userOne = user;
cy.apiCreateUser().then((data) => {
userTwo = data.user;
cy.apiAddUserToTeam(testTeam.id, userTwo.id);
// # Login as userOne and Off-Topic
cy.apiLogin(userOne);
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
});
});
it('adding a reaction to a post is visible to another user in the channel', () => {
// # Post a message
cy.postMessage('The reaction to this post should be visible to user-2');
// # Mouseover the last post
cy.getLastPost().trigger('mouseover');
cy.getLastPostId().then((postId) => {
// # Click the add reaction icon
cy.clickPostReactionIcon(postId);
// # Choose "slightly_frowning_face" emoji
cy.clickEmojiInEmojiPicker('slightly_frowning_face');
// * The number shown on the reaction is incremented by 1
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).
should('have.text', '1').
should('be.visible');
});
// # Logout
cy.apiLogout();
// # Login as userTwo and off-topic
cy.apiLogin(userTwo);
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.getLastPostId().then((postId) => {
// * userOne's reaction "slightly_frowning_face" is visible and is equal to 1
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).
should('have.text', '1').
should('be.visible');
});
});
it('another user adding to existing reaction increases reaction count', () => {
cy.getLastPostId().then((postId) => {
// # Click on the "slightly_frowning_face" emoji
cy.get(`#postReaction-${postId}-slightly_frowning_face`).click();
// * The number shown on the "slightly_frowning_face" reaction is incremented by 1
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).
should('have.text', '2').
should('be.visible');
});
});
it('a reaction added by current user has highlighted background color', () => {
cy.getLastPostId().then((postId) => {
// # The "slightly_frowning_face" emoji of the last post and the background color changes
cy.get(`#postReaction-${postId}-slightly_frowning_face`).
should('be.visible').
should('have.css', 'background-color').
and('eq', 'rgba(28, 88, 217, 0.08)');
});
});
it("can click another user's reaction to detract from it", () => {
cy.getLastPostId().then((postId) => {
// * The number shown on the "slightly_frowning_face" reaction is currently at 2
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).
should('have.text', '2').
should('be.visible');
// # Click on the "slightly_frowning_face" emoji
cy.get(`#postReaction-${postId}-slightly_frowning_face`).click();
// * The number shown on the "slightly_frowning_face" reaction is decremented by 1
cy.get(`#postReaction-${postId}-slightly_frowning_face .Reaction__number--display`).
should('have.text', '1').
should('be.visible');
});
});
it('can add a reaction to a post with an existing reaction', () => {
cy.getLastPostId().then((postId) => {
// # Click on the + icon
cy.get(`#addReaction-${postId}`).click({force: true});
// * The reaction emoji picker is open
cy.get('#emojiPicker').should('be.visible');
// # Select the "sweat_smile" emoji
cy.clickEmojiInEmojiPicker('sweat_smile');
// * The emoji picker is no longer open
cy.get('#emojiPicker').should('not.exist');
// * The "sweat_smile" emoji is added to the post
cy.get(`#postReaction-${postId}-sweat_smile`).should('be.visible');
});
});
it('can remove a reaction to a post with an existing reaction', () => {
cy.getLastPostId().then((postId) => {
// * The "sweat_smile" should exist on the post
cy.get(`#postReaction-${postId}-sweat_smile`).should('be.visible');
// # Click on the "sweat_smile" emoji
cy.get(`#postReaction-${postId}-sweat_smile`).click();
// * The "sweat_smile" emoji is removed
cy.get(`#postReaction-${postId}-sweat_smile`).should('not.exist');
});
});
});

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

@@ -0,0 +1,161 @@
// 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
import {getAdminAccount} from '../../../support/env';
describe('Messaging', () => {
const sysadmin = getAdminAccount();
let newChannel;
before(() => {
// # Create and visit new channel
cy.apiInitSetup().then(({team, channel}) => {
newChannel = channel;
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T93 Replying to an older bot post that has no post content and no attachment pretext', () => {
// # Get yesterdays date in UTC
const yesterdaysDate = Cypress.dayjs().subtract(1, 'days').valueOf();
// # Create a bot and get userID
cy.apiCreateBot().then(({bot}) => {
const botUserId = bot.user_id;
cy.externalRequest({user: sysadmin, method: 'put', path: `users/${botUserId}/roles`, data: {roles: 'system_user system_post_all system_admin'}});
// # Get token from bots id
cy.apiAccessToken(botUserId, 'Create token').then(({token}) => {
//# Add bot to team
cy.apiAddUserToTeam(newChannel.team_id, botUserId);
// # Post message with auth token
const props = {attachments: [{text: 'Some Text posted by bot that has no content and no attachment pretext'}]};
cy.postBotMessage({token, props, channelId: newChannel.id, createAt: yesterdaysDate}).
its('id').
should('exist').
as('yesterdaysBotPost');
});
// # Add two subsequent posts
cy.postMessage('First posting');
cy.postMessage('Another one Posted');
const replyMessage = 'A reply to an older post bot post';
cy.get('@yesterdaysBotPost').then((postId) => {
// # Open RHS comment menu
cy.clickPostCommentIcon(postId);
// # Reply to message
cy.postMessageReplyInRHS(replyMessage);
// # Get the latest reply post
cy.getLastPostId().then((replyId) => {
// * Verify that the reply is in the RHS with matching text
cy.get(`#rhsPost_${replyId}`).within(() => {
cy.findByTestId('post-link').should('not.exist');
cy.get(`#rhsPostMessageText_${replyId}`).should('be.visible').and('have.text', replyMessage);
});
cy.get(`#CENTER_time_${postId}`).find('time').invoke('attr', 'dateTime').then((originalTimeStamp) => {
// * Verify the first post timestamp equals the RHS timestamp
cy.get(`#RHS_ROOT_time_${postId}`).find('time').invoke('attr', 'dateTime').should('equal', originalTimeStamp);
// * Verify the first post timestamp was not modified by the reply
cy.get(`#CENTER_time_${replyId}`).find('time').should('have.attr', 'dateTime').and('not.equal', originalTimeStamp);
});
//# Close RHS
cy.uiCloseRHS();
// * Verify that the reply is in the channel view with matching text
cy.get(`#post_${replyId}`).within(() => {
cy.findByTestId('post-link').should('be.visible').and('have.text', 'Commented on ' + bot.username + 'BOT\'s message: Some Text posted by bot that has no content and no attachment pretext');
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', replyMessage);
});
});
});
// # Verify RHS is closed
cy.get('#rhsContainer').should('not.exist');
});
});
it('MM-T91 Replying to an older post by a user that has no content (only file attachments)', () => {
// # Get yesterdays date in UTC
const yesterdaysDate = Cypress.dayjs().subtract(1, 'days').valueOf();
// # Create a bot and get userID
cy.apiCreateBot().then(({bot}) => {
const botUserId = bot.user_id;
cy.externalRequest({user: sysadmin, method: 'put', path: `users/${botUserId}/roles`, data: {roles: 'system_user system_post_all system_admin'}});
// # Get token from bots id
cy.apiAccessToken(botUserId, 'Create token').then(({token}) => {
//# Add bot to team
cy.apiAddUserToTeam(newChannel.team_id, botUserId);
// # Post message with auth token
const message = 'Hello message from ' + bot.username;
const props = {attachments: [{pretext: 'Some Pretext', text: 'Some Text'}]};
cy.postBotMessage({token, message, props, channelId: newChannel.id, createAt: yesterdaysDate}).
its('id').
should('exist').
as('yesterdaysPost');
});
// # Add two subsequent posts
cy.postMessage('First post');
cy.postMessage('Another Post');
const replyMessage = 'A reply to an older post with message attachment';
cy.get('@yesterdaysPost').then((postId) => {
// # Open RHS comment menu
cy.clickPostCommentIcon(postId);
// # Reply to message
cy.postMessageReplyInRHS(replyMessage);
// # Get the latest reply post
cy.getLastPostId().then((replyId) => {
// * Verify that the reply is in the RHS with matching text
cy.get(`#rhsPost_${replyId}`).within(() => {
cy.findByTestId('post-link').should('not.exist');
cy.get(`#rhsPostMessageText_${replyId}`).should('be.visible').and('have.text', replyMessage);
});
cy.get(`#CENTER_time_${postId}`).find('time').invoke('attr', 'dateTime').then((originalTimeStamp) => {
// * Verify the first post timestamp equals the RHS timestamp
cy.get(`#RHS_ROOT_time_${postId}`).find('time').invoke('attr', 'dateTime').should('equal', originalTimeStamp);
// * Verify the first post timestamp was not modified by the reply
cy.get(`#CENTER_time_${replyId}`).find('time').should('have.attr', 'dateTime').and('not.equal', originalTimeStamp);
});
//# Close RHS
cy.uiCloseRHS();
// * Verify that the reply is in the channel view with matching text
cy.get(`#post_${replyId}`).within(() => {
cy.findByTestId('post-link').should('be.visible').and('have.text', 'Commented on ' + bot.username + 'BOT\'s message: Hello message from ' + bot.username);
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', replyMessage);
});
});
});
// # Verify RHS is closed
cy.get('#rhsContainer').should('not.exist');
});
});
});

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

@@ -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 @messaging
import {getRandomId} from '../../../utils';
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Reply in existing GM', () => {
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('MM-T470 Reply in existing GM', () => {
const userGroupIds = [testUser.id, otherUser1.id, otherUser2.id];
// # Create a group channel for 3 users
cy.apiCreateGroupChannel(userGroupIds).then(({channel: gmChannel}) => {
// # Go to the group message channel
cy.visit(`/${testTeam.name}/channels/${gmChannel.name}`);
const rootPostMessage = `this is test message from user: ${otherUser1.id}`;
// # Post message as otherUser1
cy.postMessageAs({sender: otherUser1, message: rootPostMessage, channelId: gmChannel.id}).then((post) => {
cy.uiWaitUntilMessagePostedIncludes(rootPostMessage);
const rootPostId = post.id;
const rootPostMessageId = `#rhsPostMessageText_${rootPostId}`;
// # Click comment icon to open RHS
cy.clickPostCommentIcon(rootPostId);
// * Check that the RHS is open
cy.get('#rhsContainer').should('be.visible');
// * Verify that the original message is in the RHS
cy.get('#rhsContainer').find(rootPostMessageId).should('have.text', `${rootPostMessage}`);
const replyMessage = `A reply ${getRandomId()}`;
// # Post a reply
cy.postMessageReplyInRHS(replyMessage);
cy.getLastPostId().then((replyId) => {
// * Verify that the reply is in the RHS with matching text
cy.wait(TIMEOUTS.TWO_SEC);
cy.get(`#rhsPostMessageText_${replyId}`).should('be.visible').and('have.text', replyMessage);
// * Verify that the reply is in the center channel with matching text
cy.get(`#postMessageText_${replyId}`).should('be.visible').should('have.text', replyMessage);
// # Login as otherUser
cy.apiLogin(otherUser1);
// # Go to the group message channel
cy.visit(`/${testTeam.name}/channels/${gmChannel.name}`);
// * Verify that the reply is in the center channel with matching text
cy.get(`#postMessageText_${replyId}`).should('be.visible').should('have.text', replyMessage);
});
});
});
});
});

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

@@ -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.
// ***************************************************************
// Group: @channels @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
before(() => {
// # Login as test user and visit off-topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
// # Post a new message to ensure there will be a post to click on
cy.postMessage('Hello ' + Date.now());
});
});
it('MM-T209 Input box on reply thread can expand', () => {
const maxReplyCount = 15;
const halfViewportHeight = Cypress.config('viewportHeight') / 2;
const padding = 8;
const postCreateContainerDefaultHeight = 188;
const replyTextBoxDefaultHeight = 100;
const postCreateContainerClassName = 'post-create__container';
const replyTextBoxId = 'reply_textbox';
let newLinesCount;
// # Click "Reply"
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// # Post several replies and verify last reply
cy.get(`#${replyTextBoxId}`).clear().should('be.visible').as('replyTextBox');
for (let i = 1; i <= maxReplyCount; i++) {
cy.postMessageReplyInRHS(`post ${i}`);
}
verifyLastReply(maxReplyCount);
// # Get post create container and reply text box
cy.document().then((doc) => {
const postCreateContainer = doc.getElementsByClassName(postCreateContainerClassName)[0];
const replyTextBox = doc.getElementById(replyTextBoxId);
// * Check if post create container has default offset height and less than 50% of viewport height
expect(postCreateContainer.offsetHeight).to.eq(postCreateContainerDefaultHeight).and.lessThan(halfViewportHeight);
// * Check if reply text box has default offset height and less than post create container default offset height
expect(replyTextBox.offsetHeight).to.eq(replyTextBoxDefaultHeight).and.lessThan(postCreateContainerDefaultHeight);
});
// # Enter new lines into RHS so that box should reach max height, verify last reply, and verify heights
newLinesCount = 25;
enterNewLinesAndVerifyLastReplyAndHeights(newLinesCount, maxReplyCount, postCreateContainerClassName, replyTextBoxId, padding, halfViewportHeight, postCreateContainerDefaultHeight);
// # Enter more new lines into RHS, verify last reply, and verify heights
newLinesCount *= 2;
enterNewLinesAndVerifyLastReplyAndHeights(newLinesCount, maxReplyCount, postCreateContainerClassName, replyTextBoxId, padding, halfViewportHeight, postCreateContainerDefaultHeight);
// # Get first reply and scroll into view
cy.getNthPostId(-maxReplyCount).then((replyId) => {
cy.get(`#postMessageText_${replyId}`).scrollIntoView();
cy.wait(TIMEOUTS.HALF_SEC);
});
// # Type new message to reply text box and verify last reply
cy.get('@replyTextBox').type('new message');
verifyLastReply(maxReplyCount);
});
function enterNewLinesAndVerifyLastReplyAndHeights(newLinesCount, maxReplyCount, postCreateContainerClassName, replyTextBoxId, padding, halfViewportHeight, postCreateContainerDefaultHeight) {
const newLines = '{shift}{enter}'.repeat(newLinesCount);
cy.get('@replyTextBox').type(newLines);
verifyLastReply(maxReplyCount);
verifyHeights(postCreateContainerClassName, replyTextBoxId, padding, halfViewportHeight, postCreateContainerDefaultHeight);
}
function verifyLastReply(maxReplyCount) {
// * Check last reply is visible
cy.getLastPostId().then((replyId) => {
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', `post ${maxReplyCount}`);
});
}
function verifyHeights(postCreateContainerClassName, replyTextBoxId, padding, halfViewportHeight, postCreateContainerDefaultHeight) {
// # Get post create container and reply text box
cy.document().then((doc) => {
const postCreateContainer = doc.getElementsByClassName(postCreateContainerClassName)[0];
const replyTextBox = doc.getElementById(replyTextBoxId);
// * Check if post create container offset height is 50% of viewport height
expect(postCreateContainer.offsetHeight - padding).to.eq(halfViewportHeight);
// * Check if reply text box offset height is greater than post create container default height
expect(replyTextBox.offsetHeight).to.be.greaterThan(postCreateContainerDefaultHeight);
// * Check if reply text box height attribute is greater than reply text box offset height
cy.get(`#${replyTextBoxId}`).should('have.attr', 'height').then((height) => {
expect(Number(height)).to.be.greaterThan(replyTextBox.offsetHeight);
});
});
}
});

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

@@ -0,0 +1,156 @@
// 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
const TIMEOUTS = require('../../../fixtures/timeouts');
describe('Message Reply', () => {
let mainChannel;
let otherChannel;
let rootId;
before(() => {
// # Create main channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
mainChannel = channel;
// # Create other channel
cy.apiCreateChannel(team.id, 'other', 'other').then(({channel: newChannel}) => {
otherChannel = newChannel;
});
// # Visit main channel
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T2132 - Message sends: just text', () => {
// # Type `Hello` in center message box
const msg = 'Hello';
cy.uiGetPostTextBox().type(msg);
// # Press `Enter`
cy.uiGetPostTextBox().type('{enter}');
// * Message displays in center
cy.getLastPostId().then((postId) => {
rootId = postId;
cy.get(`#postMessageText_${postId}`).should('be.visible').and('have.text', msg);
});
});
it('MM-T2133 - Reply arrow opens RHS with Reply button disabled until text entered', () => {
// # Click on the `...` menu icon and click on `Reply`
cy.uiClickPostDropdownMenu(rootId, 'Reply', 'CENTER');
// * RHS is open
cy.get('#rhsContainer').should('be.visible');
// * Reply button is disabled
cy.uiGetReply().should('be.disabled');
// # Type a character in the comment box
cy.uiGetReplyTextBox().type('A');
// * Reply button is not disabled
cy.uiGetReply().should('not.be.disabled');
// # Clear comment box
cy.uiGetReplyTextBox().clear();
// # Close RHS
cy.uiCloseRHS();
});
it('MM-T2134 - Reply to message displays in RHS and center and shows reply count', () => {
// # Open RHS comment menu
cy.clickPostCommentIcon(rootId);
const msg = 'reply1';
// # Type message
cy.uiGetReplyTextBox().type(msg);
// # Post reply
cy.uiReply();
cy.getLastPostId().then((replyId) => {
// * Message displays in center
cy.get(`#post_${replyId}`).within(() => {
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', msg);
});
// * Message displays in RHS
cy.get(`#rhsPost_${replyId}`).within(() => {
cy.get(`#rhsPostMessageText_${replyId}`).should('be.visible').and('have.text', msg);
});
// * Reply count is visible and shows expected value
cy.get(`#CENTER_commentIcon_${rootId} .post-menu__comment-count`).should('be.visible').and('have.text', '1');
});
// # Close RHS
cy.uiCloseRHS();
});
it('MM-T2135 - Can open reply thread from reply count arrow and reply', () => {
// # Click reply icon
cy.clickPostCommentIcon(rootId);
const msg = 'reply2';
// # Type message
cy.uiGetReplyTextBox().type(msg);
// # Press `Enter`
cy.uiGetReplyTextBox().type('{enter}');
cy.getLastPostId().then((replyId) => {
// * Message displays in center
cy.get(`#post_${replyId}`).within(() => {
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', msg);
});
// * Message displays in RHS
cy.get(`#rhsPost_${replyId}`).within(() => {
cy.get(`#rhsPostMessageText_${replyId}`).should('be.visible').and('have.text', msg);
});
});
// # Close RHS
cy.uiCloseRHS();
});
it('MM-T2136 - Reply in RHS with different channel open in center', () => {
// # Click on the `...` menu icon and click on `Reply`
cy.uiClickPostDropdownMenu(rootId, 'Reply', 'CENTER');
// # Switch to a different channel
cy.get(`#sidebarItem_${otherChannel.name}`).click().wait(TIMEOUTS.FIVE_SEC);
const msg = 'reply3';
// # Type message
cy.uiGetReplyTextBox().type(msg);
// # Post reply
cy.uiReply().wait(TIMEOUTS.HALF_SEC);
// * Center channel has not changed
cy.get('#channelHeaderTitle').should('contain', otherChannel.display_name);
// * Main channel is not unread
cy.get(`#sidebarItem_${mainChannel.name}`).should('not.have.class', 'unread-title');
// # Close RHS
cy.uiCloseRHS();
});
});

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

@@ -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 @messaging
describe('Message reply scrollable', () => {
before(() => {
// # Login as test user and visit off-topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
// # Post a new message to ensure there will be a post to click on
cy.postMessage('Hello ' + Date.now());
});
// # Click "Reply"
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// # Post several replies and verify last reply
for (let i = 1; i <= 15; i++) {
cy.postMessageReplyInRHS(`post ${i}`);
}
});
it('MM-T4083_1 correctly scrolls to the bottom when a thread is opened', () => {
// # Scroll RHS to top and close it
cy.get('.post-right__content > div > div').first().scrollIntoView();
cy.uiCloseRHS();
// # Open RHS
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// * Check that after opening RHS it's scrolled to bottom
cy.uiGetRHS().findByLabelText('Send a message');
});
it('MM-T4083_2 correctly scrolls to the bottom when the user types in the comment box', () => {
// # Scroll RHS to top
cy.get('.post-right__content > div > div').first().scrollIntoView();
// # Type into comment box
cy.uiGetReplyTextBox().type('foo', {scrollBehavior: false}); // without scrollBehavior=false cypress automatically scrolls to replyTextBox. We need to check if application does that.
// * Check if RHS scrolled to bottom
cy.uiGetRHS().findByLabelText('Send a message');
});
});

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

@@ -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 @messaging
import {getAdminAccount} from '../../../support/env';
describe('Message Reply', () => {
const sysadmin = getAdminAccount();
let newChannel;
before(() => {
// # Create and visit new channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
newChannel = channel;
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T90 Reply to older message', () => {
// # Get yesterdays date in UTC
const yesterdaysDate = Cypress.dayjs().subtract(1, 'days').valueOf();
// # Post a day old message
cy.postMessageAs({sender: sysadmin, message: 'Hello from yesterday', channelId: newChannel.id, createAt: yesterdaysDate}).
its('id').
should('exist').
as('yesterdaysPost');
// # Add two subsequent posts
cy.postMessage('One');
cy.postMessage('Two');
cy.get('@yesterdaysPost').then((postId) => {
// # Open RHS comment menu
cy.clickPostCommentIcon(postId);
// # Reply with the attachment
cy.postMessageReplyInRHS('A reply to an older post with attachment');
// # Get the latest reply post
cy.getLastPostId().then((replyId) => {
// * Verify that the reply is in the channel view with matching text
cy.get(`#post_${replyId}`).within(() => {
cy.findByTestId('post-link').should('be.visible').and('have.text', 'Commented on sysadmin\'s message: Hello from yesterday');
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', 'A reply to an older post with attachment');
});
// * Verify that the reply is in the RHS with matching text
cy.get(`#rhsPost_${replyId}`).within(() => {
cy.findByTestId('post-link').should('not.exist');
cy.get(`#rhsPostMessageText_${replyId}`).should('be.visible').and('have.text', 'A reply to an older post with attachment');
});
cy.get(`#CENTER_time_${postId}`).find('time').invoke('attr', 'dateTime').then((originalTimeStamp) => {
// * Verify the first post timestamp equals the RHS timestamp
cy.get(`#RHS_ROOT_time_${postId}`).find('time').invoke('attr', 'dateTime').should('equal', originalTimeStamp);
// * Verify the first post timestamp was not modified by the reply
cy.get(`#CENTER_time_${replyId}`).find('time').should('have.attr', 'dateTime').and('not.equal', originalTimeStamp);
});
});
});
// # Close RHS
cy.uiCloseRHS();
// # Verify RHS is open
cy.get('#rhsContainer').should('not.exist');
});
});

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

@@ -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 @messaging
describe('Message Reply too long', () => {
before(() => {
// # Login as test user and visit off-topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
// # Post a new message to ensure there will be a post to click on
cy.postMessage('Hello ' + Date.now());
});
});
it('MM-T106 Webapp: "Message too long" warning text', () => {
// # Click "Reply"
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// # Enter valid text into RHS
const replyValid = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ';
cy.postMessageReplyInRHS(replyValid);
// * Check no warning
cy.get('.post-error').should('not.exist');
// # Enter too long text into RHS
const maxReplyLength = 16383;
const replyTooLong = replyValid.repeat((maxReplyLength / replyValid.length) + 1);
cy.uiGetReplyTextBox().invoke('val', replyTooLong).trigger('input');
// * Check warning doesn't overlap textbox
cy.get('.post-error').should('be.visible');
cy.uiGetReplyTextBox();
// # Type "enter" into RHS
cy.uiGetReplyTextBox().type('{enter}');
// * Check warning
cy.get('.post-error').should('be.visible').and('have.text', `Your message is too long. Character count: ${replyTooLong.length}/${maxReplyLength}`);
cy.uiGetReplyTextBox();
// * Check last reply is the last valid one
cy.getLastPostId().then((replyId) => {
cy.get(`#postMessageText_${replyId}`).should('be.visible').and('have.text', replyValid);
});
});
});

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

@@ -0,0 +1,47 @@
// 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
describe('Message', () => {
let testChannel;
let testTeam;
before(() => {
// # Login as test user and go to off-topic
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
testChannel = channel;
testTeam = team;
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
});
it('MM-T175 Channel shortlinking still works when placed in brackets', () => {
// # Post a shortlink of channel
const shortLink = `(~${testChannel.name})`;
const longLink = `~${testChannel.display_name}`;
cy.postMessage('hello');
cy.uiGetPostTextBox().type(shortLink).type('{enter}');
cy.getLastPostId().then((postId) => {
// # Grab last message with the long link url and go to the link
const divPostId = `#postMessageText_${postId}`;
cy.get(divPostId).contains(longLink).click();
// * verify that the url is the same as what was just clicked on
cy.location('pathname').should('contain', `${testTeam.name}/channels/${testChannel.name}`);
// * verify that the channel title represents the same channel that was clicked on
cy.get('#channelHeaderTitle').should('contain', testChannel.display_name);
});
});
});

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

@@ -0,0 +1,187 @@
// 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 @smoke
import {getAdminAccount} from '../../../support/env';
import * as MESSAGES from '../../../fixtures/messages';
import timeouts from '../../../fixtures/timeouts';
describe('Message', () => {
const admin = getAdminAccount();
let testTeam;
let testChannel;
before(() => {
// # Create new team and new user and visit Off-Topic channel
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
testTeam = team;
testChannel = channel;
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
});
it('MM-T77 Consecutive message does not repeat profile info', () => {
// # Wait for posts to load
cy.get('#postListContent').should('be.visible');
// # Post a message to force next user message to display a message
cy.postMessageAs({sender: admin, message: 'Hello', channelId: testChannel.id});
// # Post message "One"
cy.postMessage('One');
// * Check profile image is visible
shouldHavePostProfileImageVisible(true);
// # Post message "Two"
cy.postMessage('Two');
// * Check profile image is not visible
shouldHavePostProfileImageVisible(false);
// # Post message "Three"
cy.postMessage('Three');
// * Check profile image is not visible
shouldHavePostProfileImageVisible(false);
});
it('MM-T201 Focus move to main input box when a character key is selected', () => {
// # Post message
cy.postMessage('Message');
cy.getLastPostId().then((postId) => {
const divPostId = `#post_${postId}`;
// # Left click on post to move the focus out of the main input box
cy.get(divPostId).click().wait(timeouts.ONE_SEC);
// # Push a character key such as "A"
cy.uiGetPostTextBox().type('A');
// # Open the "..." menu on a post in the main to move the focus out of the main input box
cy.clickPostDotMenu(postId);
cy.get(`#CENTER_dropdown_${postId}`).should('be.visible').type('{esc}');
// # Push a character key such as "A"
cy.uiGetPostTextBox().type('A');
// * Focus is moved back to the main input and the keystroke is captured
cy.focused().should('have.id', 'post_textbox');
cy.focused().should('contain', 'AA');
});
});
it('MM-T72 @here. @all. @channel. (containing a period) still highlight', () => {
// # Post message
cy.postMessage('@here. @all. @channel.');
cy.getLastPostId().then((postId) => {
const divPostId = `#postMessageText_${postId}`;
// * Check that the message contains the whole content sent ie. mentions with dots.
cy.get(divPostId).find('p').should('have.text', '@here. @all. @channel.');
// * Check that only the at-mention are inside span.mention--highlight
cy.get(divPostId).find('.mention--highlight').
first().should('have.text', '@here').should('not.have.text', '.').
next().should('have.text', '@all').should('not.have.text', '.').
next().should('have.text', '@channel').should('not.have.text', '.');
});
});
it('MM-2954 /me message should be formatted like a system message', () => {
// # Post message
const message = 'hello there';
cy.postMessage(`/me ${message}`);
cy.uiWaitUntilMessagePostedIncludes(message);
cy.getLastPostId().then((postId) => {
const divPostId = `#post_${postId}`;
// * Check that message has the css class needed for system message styling
cy.get(divPostId).should('have.class', 'post--system');
});
});
it('message with emoji contains hidden shortcode text', () => {
const message = 'This post has a shortcode emoji :raising_hand_man: within it.';
// # Post a message with a shortcode emoji
cy.postMessage(message);
cy.getLastPostId().then((postId) => {
const divPostId = `#post_${postId}`;
// * Check that message matches what was posted with the emoji omitted
cy.get(`#postMessageText_${postId}`).should('have.text', message);
// * Check that the emoji image contains the hidden shortcode text
cy.get(divPostId).find('span.emoticon').should('have.text', ':raising_hand_man:');
});
});
it('message with unicode emoji displays the unicode and not a span with background image', () => {
// # Post a message with a shortcode emoji
cy.postMessage('This post a unicode emoji in a code snippet: `😉`');
cy.getLastPostId().then((postId) => {
const divPostId = `#post_${postId}`;
cy.get(divPostId).find('span.emoticon').should('not.exist');
cy.get(divPostId).find('span.codespan__pre-wrap code').should('have.text', '😉');
});
});
it('MM-T3307 Focus remains in the RHS text box', () => {
cy.apiSaveShowMarkdownPreviewPreference();
cy.postMessage(MESSAGES.MEDIUM);
// # Open reply thread (RHS)
cy.clickPostCommentIcon();
// # Add some text to RHS text box
cy.uiGetReplyTextBox().type(MESSAGES.TINY);
// # Click on Preview
cy.get('#PreviewInputTextButton').click();
// # Click on Reply
cy.uiReply();
// * Focus to remain in the RHS text box
cy.uiGetReplyTextBox().should('be.focused');
});
});
function shouldHavePostProfileImageVisible(isVisible = true) {
cy.getLastPostId().then((postID) => {
const target = `#post_${postID}`;
if (isVisible) {
cy.get(target).invoke('attr', 'class').
should('contain', 'current--user').
and('contain', 'other--root');
cy.get(`${target} > div[data-testid='postContent'] > .post__img`).should('be.visible');
} else {
cy.get(target).invoke('attr', 'class').
should('contain', 'current--user').
and('contain', 'same--user').
and('contain', 'same--root');
cy.get(`${target} > div[data-testid='postContent'] > .post__img`).
should('be.visible').
and('be.empty');
}
});
}

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

@@ -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 @messaging
describe('Show GIF images properly', () => {
let offtopiclink;
before(() => {
// # Set the configuration on Link Previews
cy.apiUpdateConfig({
ServiceSettings: {
EnableLinkPreviews: true,
},
});
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
offtopiclink = `/${team.name}/channels/off-topic`;
cy.visit(offtopiclink);
});
});
it('MM-T3318 Posting GIFs', () => {
// # Got to a test channel on the side bar
cy.get('#sidebarItem_off-topic').click({force: true});
// * Validate if the channel has been opened
cy.url().should('include', offtopiclink);
// # Post tenor GIF
cy.postMessage('https://c.tenor.com/Ztva2YFROSkAAAAi/duck-searching.gif');
cy.getLastPostId().as('postId').then((postId) => {
// * Validate image size
cy.get(`#post_${postId}`).find('.attachment__image').should('have.css', 'width', '137px');
});
// # Post giphy GIF
cy.postMessage('https://media.giphy.com/media/XIqCQx02E1U9W/giphy.gif');
cy.getLastPostId().as('postId').then((postId) => {
// * Validate image size
cy.get(`#post_${postId}`).find('.attachment__image').invoke('outerWidth').should('be.gte', 480);
});
});
});

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

@@ -0,0 +1,64 @@
// 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
describe('Delete Parent Message', () => {
before(() => {
// # Set view port to mobile
cy.viewport('iphone-6');
// # Login as test user and visit town-square channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T110 Delete a parent message that has a reply: Reply RHS', () => {
// # Close Hamburger menu, post a message, and add replies
cy.uiGetPostTextBox().click({force: true});
cy.postMessage('Parent Message');
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
// * Check that the RHS is open
cy.get('#rhsContainer').should('be.visible');
// * Add 2 replies
const replyCount = 2;
for (var i = 0; i < replyCount; i++) {
cy.postMessageReplyInRHS('Reply');
}
cy.getLastPostId().then((replyPostId) => {
// * No delete modal should be visible yet
cy.get('#deletePostModal').should('not.exist');
// #Close RHS view, open delete confirmation modal for the parent message from the center screen
cy.get('#sbrSidebarCollapse').click();
cy.clickPostDotMenu(postId);
cy.get(`#delete_post_${postId}`).click();
// * Modal should now be visible and warning message should match the number of replies
cy.get('#deletePostModal').should('be.visible');
cy.get('#deletePostModal').contains(replyCount).should('be.visible');
// # Delete the parent message
cy.get('#deletePostModalButton').click({force: true});
// * Post is deleted from both center and RHS is not visible to the user who deleted it
cy.get('#rhsContainer').should('not.exist');
cy.get(`#post_${postId}`).should('not.exist');
cy.get(`#post_${replyPostId}`).should('not.exist');
});
});
});
});

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

@@ -0,0 +1,56 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Profile popover', () => {
before(() => {
// # Set view port to mobile
cy.viewport('iphone-6');
// # Login as test user and visit off-topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
// # Post a new message to ensure there will be a post to click on
cy.postMessage('Test message');
});
});
it('MM-T145_1 Mobile view: View profile popover from profile pic (standard mode)', () => {
// # Setting posts to standard mode
cy.apiSaveMessageDisplayPreference();
cy.getLastPostId().then((postId) => {
// add wait time to ensure image is rendered and can be clicked
cy.wait(TIMEOUTS.HALF_SEC);
// # Click on user profile image
cy.get(`#post_${postId}`).find('.profile-icon > img').click({force: true});
// * Popover should have rendered to screen
cy.get('#user-profile-popover').should('be.visible');
cy.get('body').type('{esc}');
});
});
it('MM-T145_2 Mobile view: View profile popover from profile pic (compact mode)', () => {
// # Setting posts to compact mode
cy.apiSaveMessageDisplayPreference('compact');
cy.getLastPostId().then((postId) => {
// # Click on username
cy.get(`#post_${postId}`).find('.user-popover').click({force: true});
// * Popover should have rendered to screen
cy.get('#user-profile-popover').should('be.visible');
});
});
});

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

@@ -0,0 +1,56 @@
// 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
describe('No Matches for Autocomplete', () => {
before(() => {
cy.apiInitSetup().then(({team}) => {
// # Visit town-square
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('MM-T270 No matches for user autocomplete', () => {
// # Type non-existent user name
const nonExistentUser = 'nonExistentUser';
cy.uiGetPostTextBox().clear().type(`@${nonExistentUser}`);
// * Verify suggestion list does not exist
cy.get('#suggestionList').should('not.exist');
// # Hit enter to post non-existent user name
cy.uiGetPostTextBox().type('{enter}');
// * Verify that the last message posted contains the user name and is not linked
cy.getLastPost().within(() => {
cy.get(`[data-mention=${nonExistentUser}]`).should('include.text', `@${nonExistentUser}`);
cy.get('.mention-link').should('not.exist');
});
});
it('MM-T269 No matches for channel autocomplete', () => {
// # Type non-existent channel name
const nonExistentChannel = 'nonExistentChannel';
cy.uiGetPostTextBox().clear().clear().type(`~${nonExistentChannel}`);
// * Verify suggestion list does not exist
cy.get('#suggestionList').should('not.exist');
// # Hit enter to post non-existent channel name
cy.uiGetPostTextBox().type('{enter}');
// * Verify that the last message posted contains the channel name and is not linked
cy.getLastPost().should('include.text', `~${nonExistentChannel}`).within(() => {
cy.get(`[data-channel-mention=${nonExistentChannel}]`).should('not.exist');
cy.get('.mention-link').should('not.exist');
});
});
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getAdminAccount} from '../../../support/env';
describe('Permalink message edit', () => {
let testTeam;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
testChannel = channel;
});
});
/**
* 1. Should leave the team (if joined while trying to access private channel) if user decides not to join the private channel.
* 2. Should show prompts when opened directly from address bar.
* 3. Should show prompts without leaving the screen upon pressing on channel url/permalink.
*/
it('MM-T3830 System admins prompted before joining private channel via permalink', () => {
// # Go to test channel
gotoChannel(testTeam, testChannel);
// # Create Private Channel 1
cy.uiCreateChannel({isPrivate: true}).then((channel1) => {
// # Create Private Channel 2
cy.uiCreateChannel({isPrivate: true}).then((channel2) => {
// # Post a message
cy.postMessage(Date.now().toString());
cy.getLastPostId().then((postId) => {
// # Go to test channel
gotoChannel(testTeam, testChannel);
// # Post Private Channel 1 Permalink
const message1 = `${Cypress.config('baseUrl')}/${testTeam.name}/channels/${channel1.name}`;
cy.postMessage(message1);
// # Post Private Channel 2's POST Permalink
const message2 = `${Cypress.config('baseUrl')}/${testTeam.name}/pl/${postId}`;
cy.postMessage(message2);
// # Logout and login as sysadmin
cy.apiLogout().wait(TIMEOUTS.ONE_SEC).then(() => {
cy.apiLogin(getAdminAccount());
// # Leave private channel 1 & 2 to test the prompts
gotoChannel(testTeam, channel1);
cy.leaveTeam();
// # Go to channel 1 from url
cy.visit(`/${testTeam.name}/channels/${channel1.name}`);
// * Prompt should be shown upon going to the screen
verifyPrivateChannelJoinPromptIsVisible(channel1);
// # Cancel the prompt
cy.get('#cancelModalButton').should('be.visible').click();
// * Verify that we've left the team and switched to other team
cy.uiGetLHSHeader().findByText(testTeam.display_name);
// # Go to public channel
gotoChannel(testTeam, testChannel);
// # Click on link 1 - channel url
cy.get(`a[href="${message1}"]`).should('be.visible').click();
// * Prompt should be shown after the click
verifyPrivateChannelJoinPromptIsVisible(channel1);
// # Cancel the prompt
cy.get('#cancelModalButton').should('be.visible').click();
// * Verify we are still in the same channel
cy.get('#channelHeaderTitle').should('be.visible').should('contain', testChannel.display_name);
// # Click on link 2 - permalink
cy.get(`a[href="${message2}"]`).should('be.visible').click();
// * Prompt should be shown after the click for permalink as well
verifyPrivateChannelJoinPromptIsVisible(channel2);
// # Join channel 2
joinPrivateChannel(channel2);
// # Leave channel
cy.uiLeaveChannel(true);
// * Prompt should be shown even if we navigate back
cy.go('back').wait(TIMEOUTS.THREE_SEC);
verifyPrivateChannelJoinPromptIsVisible(channel2);
});
});
});
});
});
});
function gotoChannel(team, channel) {
// # Visit town-square
cy.visit(`/${team.name}/channels/${channel.name}`);
cy.get('#channelHeaderTitle').should('be.visible').should('contain', channel.display_name || channel.name);
}
function joinPrivateChannel(channel) {
// # Join the private channel
cy.get('#confirmModalButton').should('be.visible').click();
// * Verify private channel screen is open
cy.get('#channelHeaderTitle').should('be.visible').should('contain', channel.name);
}
function verifyPrivateChannelJoinPromptIsVisible(channel) {
// * Verify modal is shown before joining the private channel
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModalLabel').should('be.visible').and('have.text', 'Join private channel');
cy.get('#confirmModalBody').should('be.visible').and('have.text', `You are about to join ${channel.name} without explicitly being added by the channel admin. Are you sure you wish to join this private channel?`);
}

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

@@ -0,0 +1,91 @@
// 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
import {getRandomId} from '../../../utils';
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let testTeam;
let testPrivateChannel;
let testPublicChannel;
before(() => {
cy.apiInitSetup({loginAfter: true}).then(({team, channel}) => {
testTeam = team;
testPublicChannel = channel;
cy.apiCreateChannel(testTeam.id, 'private', 'Private', 'P').then((out) => {
testPrivateChannel = out.channel;
});
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('MM-T3308 Permalink to first post in channel does not show endless loading indicator above', () => {
const message = getRandomId();
const maxMessageCount = 10;
// # Click on test private channel
cy.get('#sidebarItem_' + testPrivateChannel.name).click({force: true});
// # Post several messages
for (let i = 1; i <= maxMessageCount; i++) {
cy.postMessage(`${message}-${i}`);
}
// # Get first post id from that postlist area
cy.getNthPostId(-maxMessageCount).then((permalinkPostId) => {
const permalink = `${Cypress.config('baseUrl')}/${testTeam.name}/pl/${permalinkPostId}`;
// # Click on ... button of last post
cy.clickPostDotMenu(permalinkPostId);
// # Click on "Copy Link"
cy.uiClickCopyLink(permalink, permalinkPostId);
// # Click on test public channel
cy.get('#sidebarItem_' + testPublicChannel.name).click({force: true});
// # Paste link on postlist area
cy.postMessage(permalink);
cy.uiWaitUntilMessagePostedIncludes(permalink);
// # Get last post id from that postlist area
cy.getLastPostId().then((postId) => {
// # Click on permalink
cy.get(`#postMessageText_${postId} > p > .markdown__link`).scrollIntoView().click();
// * Check if url include the permalink
cy.url().should('include', `/${testTeam.name}/channels/${testPrivateChannel.name}/${permalinkPostId}`);
// * Check if url redirects back to parent path eventually
cy.wait(TIMEOUTS.FIVE_SEC).url().should('include', `/${testTeam.name}/channels/${testPrivateChannel.name}`).and('not.include', `/${permalinkPostId}`);
// * Check if the matching channel intro title is visible
cy.get('#channelIntro').contains('.channel-intro__title', `Beginning of ${testPrivateChannel.display_name}`).scrollIntoView().should('be.visible');
});
// # Get last post id from open channel
cy.getLastPostId().then((clickedPostId) => {
// * Check the sent message
cy.get(`#postMessageText_${clickedPostId}`).scrollIntoView().should('be.visible').and('have.text', `${message}-${maxMessageCount}`);
// * Check if the loading indicator is not visible
cy.get('.loading-screen').should('not.exist');
// * Check if the more messages text is not visible
cy.get('.more-messages-text').should('not.exist');
});
});
});
});

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

@@ -0,0 +1,90 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Permalink message edit', () => {
let testTeam;
let testUser;
let otherUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
});
});
});
it('MM-T180 Edit a message in permalink view', () => {
// # Login as test user and visit town-square
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
const searchWord = `searchtest ${Date.now()}`;
// # Post message
cy.postMessage(searchWord);
// # Search for searchWord
cy.get('#searchBox').type(searchWord).type('{enter}');
// # Jump to permalink view
cy.get('.search-item__jump').first().click();
cy.getLastPostId().then((postId) => {
// # Check if url include the permalink
cy.url().should('include', `/${testTeam.name}/channels/town-square/${postId}`);
// # Click on ... button of last post matching the searchWord
cy.clickPostDotMenu(postId);
// # Click on edit post
cy.get(`#edit_post_${postId}`).click();
const editedText = `edited - ${searchWord}`;
// # Add new text in edit box and finish by hitting enter
cy.get('#edit_textbox').should('be.visible').type('any').click().focused().clear({force: true}).type(editedText).type('{enter}');
// # Check edited post
verifyEditedPermalink(postId, editedText, testTeam);
// # Login as other user, visit town-square and post any message
cy.apiLogin(otherUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.postMessage('hello');
// # Find searchWord and verify edited post
cy.get('#searchBox').should('be.visible').type(searchWord).type('{enter}');
cy.get('.search-item__jump').first().click();
// # Check if url include the permalink
cy.url().should('include', `/${testTeam.name}/channels/town-square/${postId}`);
verifyEditedPermalink(postId, editedText, testTeam);
});
});
function verifyEditedPermalink(permalinkId, text, team) {
// * Check if url redirects back to parent path eventually
cy.wait(TIMEOUTS.FIVE_SEC).url().should('include', `/${team.name}/channels/town-square`).and('not.include', `/${permalinkId}`);
// * Verify edited post
cy.get(`#postMessageText_${permalinkId}`).should('have.text', `${text} Edited`);
cy.get(`#postEdited_${permalinkId}`).should('have.text', 'Edited');
}
});

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

@@ -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: @channels @messaging
describe('Messaging', () => {
let testTeam;
let testChannel;
let receiver;
let sender;
before(() => {
// # Login as test user
cy.apiInitSetup().then(({team, channel, user}) => {
receiver = user;
testTeam = team;
testChannel = channel;
cy.apiCreateUser().then(({user: user1}) => {
sender = user1;
cy.apiAddUserToTeam(testTeam.id, sender.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, sender.id);
});
});
cy.apiLogin(receiver);
// # Visit a test channel and post a message
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.postMessage('Hello');
});
});
it('MM-T123 Pinned parent post: reply count remains in center channel and is correct', () => {
// # Login as the other user
cy.apiLogin(sender);
// # Reload the page
cy.reload();
cy.getLastPostId().then((postId) => {
// # Click the reply button, and post a reply four times and close the thread rhs tab
cy.get(`#post_${postId}`).trigger('mouseover');
cy.get(`#CENTER_commentIcon_${postId}`).click({force: true});
for (let i = 0; i < 5; i++) {
cy.uiGetReplyTextBox().click().should('be.visible').type(`Hello to you too ${i}`);
cy.uiGetReply().should('be.enabled').click();
}
cy.get('#rhsCloseButton').click();
// # Pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Find the 'Pinned' span in the post pre-header to verify that the post was actually pinned
cy.get(`#post_${postId}`).findByText('Pinned').should('exist');
// * Assert that the reply count exists and is correct
cy.get(`#CENTER_commentIcon_${postId}`).find('span').eq(0).find('span').eq(1).should('have.text', '5');
});
});
});

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

@@ -0,0 +1,193 @@
// 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
describe('Messaging', () => {
let testTeam;
let testChannel;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testUser = user;
testTeam = team;
testChannel = channel;
cy.apiLogin(testUser);
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
it('MM-T2167 Pin a post, view pinned posts', () => {
// # Post a message
cy.postMessage('This is a post that is going to be pinned.');
cy.getLastPostId().then((postId) => {
// # On a message in center channel, click then pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Click pin icon next to search box
cy.uiGetChannelPinButton().click();
// * RHS title displays as "Pinned Posts" and "[channel name]"
cy.get('#sidebar-right').should('be.visible').and('contain', 'Pinned Posts').and('contain', `${testChannel.display_name}`);
// * Pinned post appear in RHS
cy.get(`#rhsPostMessageText_${postId}`).should('exist');
// * Message has Pinned badge in center but not in "Pinned Posts" RHS
cy.get(`#post_${postId}`).findByText('Pinned').should('exist');
cy.get(`#rhsPostMessageText_${postId}`).findByText('Pinned').should('not.exist');
});
});
it('MM-T2168 Un-pin a post, disappears from pinned posts RHS', () => {
// # Post a message
cy.postMessage('This is a post that is going to be pinned then removed.');
cy.getLastPostId().then((postId) => {
// # On a message in center channel, click then pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Find the 'Pinned' span in the post pre-header to verify that the post was actually pinned
cy.get(`#post_${postId}`).findByText('Pinned').should('exist');
// # Click pin icon next to search box
cy.uiGetChannelPinButton().click();
// # View pinned posts RHS
cy.get(`#rhsPostMessageText_${postId}`).should('exist');
// # On a message in center channel, Click [...] > Un-pin from channel
cy.uiClickPostDropdownMenu(postId, 'Unpin from Channel');
// * Post disappears from RHS
cy.get(`#rhsPostMessageText_${postId}`).should('not.exist');
// * Pinned badge is removed from post in center
cy.get(`#post_${postId}`).findByText('Pinned').should('not.exist');
});
});
it('MM-T2169 Un-pinning a post in center also removes badge from *search results* RHS', () => {
// # Post a message
cy.postMessage('Hello');
cy.getLastPostId().then((postId) => {
// # On a message in center channel, click then pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Search for "Hello"
cy.uiGetSearchBox().type('Hello').type('{enter}');
// * Post appears in RHS search results, displays Pinned badge
cy.get(`#searchResult_${postId}`).findByText('Pinned').should('exist');
// # On a message in center channel, Click [...] > Un-pin from channel
cy.uiClickPostDropdownMenu(postId, 'Unpin from Channel');
// * Post still appears in RHS search results, but Pinned badge is removed
cy.get(`#searchResult_${postId}`).findByText('Pinned').should('not.exist');
});
});
it('MM-T2170 Un-pinning a post in *permalink* view also removes badge from saved posts RHS', () => {
// # Post a message
cy.postMessage('Permalink post.');
cy.getLastPostId().then((postId) => {
// # On a message in center channel, click then pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Click save icon
cy.clickPostSaveIcon(postId);
// # In RHS, click Jump to view permalink view
cy.uiGetSavedPostButton().should('exist').click();
cy.get(`#searchResult_${postId}`).should('exist');
cy.get('a.search-item__jump').first().click();
// * Message is displayed in center channel and highlighted (permalink view)
cy.get(`#post_${postId}`).
and('have.class', 'post--highlight');
// # In permalink view, click [...] > Un-pin from channel
cy.uiClickPostDropdownMenu(postId, 'Unpin from Channel');
// * Pinned badge is removed in both center and RHS
cy.get(`#post_${postId}`).findByText('Pinned').should('not.exist');
cy.get(`#searchResult_${postId}`).findByText('Pinned').should('not.exist');
});
});
it('MM-T2171 Un-pinning and pinning a post in center also removes and adds badge in *saved posts* RHS', () => {
// # Post a message
cy.postMessage('This is a post that is going to be pinned then removed, then pinned again.');
cy.getLastPostId().then((postId) => {
// # On a message in center channel, click then pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # And also save the message
cy.clickPostSaveIcon(postId);
// # Open Saved posts
cy.uiGetSavedPostButton().click();
// * Post appears in saved posts list, and displays Pinned badge
cy.get(`#searchResult_${postId}`).findByText('Pinned').should('exist');
// # In center channel, click [...] > Un-pin from channel
cy.uiClickPostDropdownMenu(postId, 'Unpin from Channel');
// * Post still appears in saved posts list, and Pinned badge is removed in both center and RHS
cy.get(`#post_${postId}`).findByText('Pinned').should('not.exist');
cy.get(`#searchResult_${postId}`).findByText('Pinned').should('not.exist');
// # In center channel, click [...] > Pin to channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// * Pinned badge returns on message in both center and RHS
cy.get(`#post_${postId}`).findByText('Pinned').should('exist');
cy.get(`#searchResult_${postId}`).findByText('Pinned').should('exist');
});
});
it('MM-T2172 Non-pinned replies do not appear with parent post in pinned posts RHS', () => {
// # Post a message
cy.postMessage('This is a post that is going to be pinned and replied.');
cy.getLastPostId().then((postId) => {
// # On a message in center channel, click then pin the post to the channel
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Open RHS comment menu
cy.clickPostCommentIcon(postId);
// # Enter comment in RHS
cy.postMessageReplyInRHS('This is a reply');
// # Click to reply to message
cy.getLastPostId().then((replyPostId) => {
// # Click pin icon next to search box
cy.uiGetChannelPinButton().click();
// * Pinned post appear in RHS
cy.get(`#rhsPostMessageText_${postId}`).should('exist');
// * Reply does no appear in RHS
cy.get(`#rhsPostMessageText_${replyPostId}`).should('not.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 @messaging
import {getAdminAccount} from '../../../support/env';
describe('Pinned posts', () => {
let testTeam;
let testChannel;
let testUser;
let otherUser;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testUser = user;
testTeam = team;
testChannel = channel;
return cy.apiCreateUser();
}).then(({user: user1}) => {
otherUser = user1;
return cy.apiAddUserToTeam(testTeam.id, otherUser.id);
}).then(() => {
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
});
});
it('MM-T2173 Un-pinning a post from reply RHS also removes badge in center', () => {
// # Login
cy.apiLogin(testUser);
// # Visit a test channel and post a message
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.postMessage('Hello');
cy.getLastPostId().then((postId) => {
// # Pin the post
pinPost(postId);
// # Now open RHS
cy.get(`#post_${postId}`).trigger('mouseover');
cy.get(`#CENTER_commentIcon_${postId}`).click({force: true});
// * Verify that pinned icon is there both in center and RHS
cy.get(`#rhsPost_${postId}`).find('.post-pre-header').should('be.visible').and('have.text', 'Pinned');
cy.get(`#post_${postId}`).find('.post-pre-header').should('be.visible').and('have.text', 'Pinned');
// # Un-pin the post from RHS
unpinPost(postId);
// * Verify that pinned icon is gone from center and RHS
cy.get(`#rhsPost_${postId}`).find('.post-pre-header').should('not.exist');
cy.get(`#post_${postId}`).find('.post-pre-header').should('not.exist');
// # Click pin icon
cy.uiGetChannelPinButton().click();
// * Should not have any pinned posts
cy.get('#search-items-container .no-results__title').should('have.text', 'No pinned posts yet');
});
});
it('MM-T2174 Switching channels in center also changes pinned posts RHS', () => {
// # Login
cy.apiLogin(testUser);
// # Visit a test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.getLastPostId().then((postId) => {
// # Pin the post
pinPost(postId);
// # Click pin icon
cy.uiGetChannelPinButton().click();
// * Number of pinned posts in RHS should be 1
cy.findByTestId('search-item-container').should('have.length', 1);
// # Go to town square
cy.get('#sidebarItem_town-square').should('be.visible').click();
// * Should not have any pinned posts
cy.get('#search-items-container .no-results__title').should('have.text', 'No pinned posts yet');
// * Number of pinned posts in RHS should be 0
cy.findByTestId('search-item-container').should('not.exist');
// # Visit a test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Unpin the post again
unpinPost(postId);
});
});
it('MM-T2175 Pin a post in a DM channel Pin a post while viewing empty pinned post RHS', () => {
// # Create DM Channel
cy.apiCreateDirectChannel([testUser.id, otherUser.id]).then(() => {
cy.apiLogin(testUser);
// # Visit the DM channel
cy.visit(`/${testTeam.name}/channels/${testUser.id}__${otherUser.id}`);
// # Click pin icon
cy.uiGetChannelPinButton().click();
// # Post a message
cy.postMessage('Hello');
return cy.getLastPostId();
}).then((postId) => {
// * Number of pinned posts in RHS should be 0
cy.findByTestId('search-item-container').should('not.exist');
// # Pin the post
pinPost(postId);
// * Verify that the pinned post appears in RHS.
cy.get(`#rhsPostMessageText_${postId}`).should('exist');
// * Verify that pinned icon is there in center.
cy.get(`#post_${postId} .post-pre-header`).should('have.text', 'Pinned');
});
});
it('MM-T2177 Deleting pinned post while viewing RHS pinned posts removes post from RHS', () => {
// # Login
cy.apiLogin(testUser);
// # Visit a test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click pin icon
cy.uiGetChannelPinButton().click();
// * Should not have any pinned posts
cy.get('#search-items-container .no-results__title').should('have.text', 'No pinned posts yet');
// # Post a message
cy.postMessage('Hello again');
cy.getLastPostId().then((postId) => {
// # Pin the post
pinPost(postId);
// * Verify that the pinned post appears in RHS.
cy.get(`#rhsPostMessageText_${postId}`).should('exist');
// * Verify that pinned icon is there in center.
cy.get(`#post_${postId} .post-pre-header`).should('have.text', 'Pinned');
// # Now delete the post
cy.get(`#CENTER_button_${postId}`).click({force: true});
cy.get(`#delete_post_${postId}`).scrollIntoView().should('be.visible').click();
cy.get('#deletePostModalButton').should('be.visible').click();
// * Should not have any pinned posts
cy.get('#search-items-container .no-results__title').should('have.text', 'No pinned posts yet');
// * Post should be deleted
cy.get(`#post_${postId}`).should('not.exist');
cy.get(`#rhsPost_${postId}`).should('not.exist');
// # Make another post now
cy.postMessage('To be deleted by another user');
return cy.getLastPostId();
}).then((postId) => {
// # Pin the post
pinPost(postId);
// * Verify that the pinned post appears in RHS
cy.get(`#rhsPostMessageText_${postId}`).should('exist');
// * Verify that pinned icon is there in center
cy.get(`#post_${postId} .post-pre-header`).should('have.text', 'Pinned');
// # Now delete the post by sysadmin
deletePost(postId);
// * Verify that message delete text is there in RHS
cy.get('#search-items-container .post__body').should('have.text', '(message deleted)');
// * Verify that pinned icon is there in center
cy.get(`#post_${postId} .post-pre-header`).should('have.text', 'Pinned');
// * Verify that message delete text is there in center
cy.get(`#post_${postId} #${postId}_message`).should('have.text', '(message deleted)');
cy.reload();
// # Click pin icon
cy.uiGetChannelPinButton().click();
// * Should not have any pinned posts
cy.get('#search-items-container .no-results__title').should('have.text', 'No pinned posts yet');
// * The old post should be gone
cy.get(`#post_${postId}`).should('not.exist');
});
});
it('MM-T2922 Pinned post count indicates the number of pinned posts in currently viewed channel', () => {
// # Login
cy.apiLogin(testUser);
// # Visit town square
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Pinned count should be zero
cy.get('#channelPinnedPostCountText').should('not.exist');
// # Post a message
cy.postMessage('Town-square post');
cy.getLastPostId().then((postId) => {
// # Pin the post
pinPost(postId);
// * Pinned count should be 1
cy.get('#channelPinnedPostCountText').should('have.text', '1');
// # Visit a test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// * Pinned count should be zero
cy.get('#channelPinnedPostCountText').should('not.exist');
// # Post a message
cy.postMessage('Hello pinned post');
return cy.getLastPostId();
}).then((postId) => {
// # Pin the post
pinPost(postId);
// * Pinned count should be 1
cy.get('#channelPinnedPostCountText').should('have.text', '1');
// # Unpin the post
unpinPost(postId);
// * Pinned count should be zero
cy.get('#channelPinnedPostCountText').should('not.exist');
// # Pin the post
pinPost(postId);
// # Post a message
cy.postMessage('Hello second post');
return cy.getLastPostId();
}).then((postId) => {
// # Pin the post
pinPost(postId);
// * Pinned count should be 2
cy.get('#channelPinnedPostCountText').should('have.text', '2');
// # Click pin icon
cy.uiGetChannelPinButton().should('be.visible').click();
// # Go to town square
cy.get('#sidebarItem_town-square').should('be.visible').click();
// * Pinned count should be 1
cy.get('#channelPinnedPostCountText').should('have.text', '1');
// * Number of pinned posts in RHS should also be 1
cy.findByTestId('search-item-container').should('have.length', 1);
});
});
});
function pinPost(postId) {
cy.get(`#post_${postId}`).trigger('mouseover');
cy.get(`#CENTER_button_${postId}`).click({force: true});
cy.get(`#pin_post_${postId}`).scrollIntoView().should('be.visible').click();
}
function unpinPost(postId) {
cy.get(`#post_${postId}`).trigger('mouseover');
cy.get(`#CENTER_button_${postId}`).click({force: true});
cy.get(`#unpin_post_${postId}`).scrollIntoView().should('be.visible').click();
}
function deletePost(postId) {
cy.externalRequest({
user: getAdminAccount(),
method: 'delete',
path: `posts/${postId}`,
});
}

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Post Header', () => {
let testTeam;
before(() => {
// # Login as test user and visit town-square channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
testTeam = team;
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
it('should render permalink view on click of post timestamp at center view', () => {
// # Post a message
cy.postMessage('test for permalink');
cy.getLastPostId().then((postId) => {
const divPostId = `#post_${postId}`;
// * Check initial state that the first message posted is not highlighted
cy.get(divPostId).should('be.visible').should('not.have.class', 'post--highlight');
// # Click timestamp of a post
cy.clickPostTime(postId);
// * Check if url include the permalink
cy.url().should('include', `/${testTeam.name}/channels/off-topic/${postId}`);
// * Check if url redirects back to parent path eventually
cy.wait(TIMEOUTS.FIVE_SEC).url().should('include', `/${testTeam.name}/channels/off-topic`).and('not.include', `/${postId}`);
// * Check that the post is highlighted on permalink view
cy.get(divPostId).should('be.visible').and('have.class', 'post--highlight');
// * Check that the highlight is removed after a period of time
cy.wait(TIMEOUTS.HALF_SEC).get(divPostId).should('be.visible').and('not.have.class', 'post--highlight');
// * Check the said post not highlighted
cy.get(divPostId).should('be.visible').should('not.have.class', 'post--highlight');
});
});
it('should open dropdown menu on click of dot menu icon', () => {
// # Post a message
cy.postMessage('test for dropdown menu');
cy.getLastPostId().then((postId) => {
// * Check that the center dot menu' button and dropdown are hidden
cy.get(`#post_${postId}`).should('be.visible');
cy.get(`#CENTER_button_${postId}`).should('not.be.visible');
cy.get(`#CENTER_dropdown_${postId}`).should('not.exist');
// # Click dot menu of a post
cy.clickPostDotMenu(postId);
// * Check that the center dot menu button and dropdown are visible
cy.get(`#post_${postId}`).should('be.visible');
cy.get(`#CENTER_button_${postId}`).should('be.visible');
cy.get(`#CENTER_dropdown_${postId}`).should('be.visible').type('{esc}');
// # Click to other location like post textbox
cy.uiGetPostTextBox().click();
// * Check that the center dot menu and dropdown are hidden
cy.get(`#post_${postId}`).should('be.visible');
cy.get(`#CENTER_button_${postId}`).should('not.be.visible');
cy.get(`#CENTER_dropdown_${postId}`).should('not.exist');
});
});
it('should open and close Emoji Picker on click of reaction icon', () => {
// # Post a message
cy.postMessage('test for reaction and emoji picker');
cy.getLastPostId().then((postId) => {
// * Check that the center post reaction icon and emoji picker are not visible
cy.get(`#CENTER_reaction_${postId}`).should('not.be.visible');
cy.get('#emojiPicker').should('not.exist');
// # Click the center post reaction icon of the post
cy.clickPostReactionIcon(postId);
// * Check that the center post reaction icon of the post becomes visible
cy.get(`#CENTER_reaction_${postId}`).should('be.visible').and('have.class', 'post-menu__item--active').and('have.class', 'post-menu__item--reactions');
// * Check that the emoji picker becomes visible as well
cy.get('#emojiPicker').should('be.visible');
// # Click again the center post reaction icon of the post
cy.clickPostReactionIcon(postId);
// # Click on textbox to focus away from emoji area
cy.uiGetPostTextBox().click();
// * Check that the center post reaction icon and emoji picker are not visible
cy.get(`#CENTER_reaction_${postId}`).should('not.be.visible');
cy.get('#emojiPicker').should('not.exist');
});
});
it('should open RHS on click of comment icon and close on RHS\' close button', () => {
// # Post a message
cy.postMessage('test for opening and closing RHS');
// # Open RHS on hover to a post and click to its comment icon
cy.clickPostCommentIcon();
// * Check that the RHS is open
cy.get('#rhsContainer').should('be.visible');
// # Close RHS on click of close button
cy.uiCloseRHS();
// * Check that the RHS is close
cy.get('#rhsContainer').should('not.exist');
});
it('MM-T122 Visual verification of "Searching" animation for Saved and Pinned posts', () => {
cy.delayRequestToRoutes(['pinned', 'flagged'], 5000);
cy.reload();
// Pin and save last post before clicking on Pinned and Saved post icons
cy.postMessage('Post');
//Pin and save the posted message
cy.getLastPostId().then((postId) => {
cy.clickPostDotMenu(postId);
cy.get(`#pin_post_${postId}`).click();
cy.clickPostSaveIcon(postId);
});
// # Click on the "Pinned Posts" icon to the left of the "Search" box
cy.uiGetChannelPinButton().click();
// * Verify that the RHS for pinned posts is opened.
cy.get('#searchContainer').should('be.visible').within(() => {
// * Check that searching indicator appears before the pinned posts are loaded
cy.get('#loadingSpinner', {timeout: TIMEOUTS.FIVE_SEC}).should('be.visible').and('have.text', 'Searching...');
cy.get('#search-items-container').should('be.visible');
// # Close the RHS
cy.get('#searchResultsCloseButton').should('be.visible').click();
});
// # Click on the "Saved Posts" icon to the right of the "Search" box
cy.uiGetSavedPostButton().click();
// * Verify that the RHS for saved posts is opened.
cy.get('#searchContainer').should('be.visible').within(() => {
// * Check that searching indicator appears before the pinned posts are loaded
cy.get('#loadingSpinner').should('be.visible').and('have.text', 'Searching...');
cy.get('#search-items-container').should('be.visible');
// # Close the RHS
cy.get('#searchResultsCloseButton').should('be.visible').click();
});
});
});

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

@@ -0,0 +1,37 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Post HTML', () => {
before(() => {
// # Create new team and new user and visit Town Square channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('Pasting HTML table in message box should not trigger CSP violation', () => {
cy.document().then(($document) => {
$document.addEventListener('securitypolicyviolation', () => {
throw new Error('should not have triggered violation');
});
});
// # Paste HTML data from clipboard to the message box.
cy.uiGetPostTextBox().trigger('paste', {clipboardData: {
items: [1],
types: ['text/html'],
getData: () => '<table><img src="null" onerror="alert(\'xss\')" /></table>',
}}).wait(TIMEOUTS.TEN_SEC);
});
});

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

@@ -0,0 +1,62 @@
// 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
describe('Messaging', () => {
before(() => {
// # resize window to mobile view
cy.viewport('iphone-6');
// # Login as test user and visit Off-Topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
// # Post a new message to ensure there will be a post to click on
cy.postMessage('Hello ' + Date.now());
// # Click "Reply"
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// # Enter valid text into RHS
const replyValid = 'Hi ' + Date.now();
cy.postMessageReplyInRHS(replyValid);
});
});
it('MM-T74 Mobile view: Post options menu (3-dots) is present on a reply post in RHS', () => {
// # Get the last entered RHS post
cy.getLastPostId().then((lastPostId) => {
const dotMenuButtonID = `#RHS_COMMENT_button_${lastPostId}`;
// * Check to see if button is visible and click it
cy.get(dotMenuButtonID).should('be.visible').click();
const dropDownMenuOfPostOptionsID = `#RHS_COMMENT_dropdown_${lastPostId}`;
// * Check if modal for post options have opened
cy.get(dropDownMenuOfPostOptionsID).should('be.visible').within(() => {
// * Check if at least 1 item is present
cy.get('li').should('have.length.greaterThan', 0);
// * Check if one of the options are as follows
cy.findByText('Add Reaction').should('be.visible');
cy.findByText('Mark as Unread').should('be.visible');
cy.findByText('Copy Link').should('be.visible');
cy.findByText('Save').should('be.visible');
cy.findByText('Pin to Channel').should('be.visible');
cy.findByText('Edit').should('be.visible');
cy.findByText('Delete').should('be.visible');
});
});
});
});

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

@@ -0,0 +1,192 @@
// 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
import {verifySavedPost} from '../../../support/ui/post';
describe('Post PreHeader', () => {
let testTeam;
before(() => {
// # Login as test user and visit off-topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
testTeam = team;
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
});
it('MM-T3352 Properly handle Saved Posts', () => {
const message = 'test for saved post';
// # Post a message
cy.postMessage(message);
cy.getLastPostId().then((postId) => {
// * Check that the post pre-header is not visible
cy.get('div.post-pre-header').should('not.exist');
// # Click the center save icon of the post
cy.clickPostSaveIcon(postId);
// * Assert the preHeader is displayed and works as expected
verifySavedPost(postId, message);
// # Click again the center save icon of a post
cy.clickPostSaveIcon(postId);
// * Check that the post pre-header is not visible
cy.get('div.post-pre-header').should('not.exist');
// * Check that the post is not highlighted
cy.get(`#post_${postId}`).should('not.have.class', 'post--pinned-or-flagged');
});
});
it('MM-T3353 Unpinning and pinning a post removes and adds badge', () => {
// # Post a message
cy.postMessage('test for pinning/unpinning a post');
cy.getLastPostId().then((postId) => {
// * Check that the post pre-header is not visible
cy.get('div.post-pre-header').should('not.exist');
// # Pin the post.
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// * Check that the post is highlighted
cy.get(`#post_${postId}`).
should('have.class', 'post--pinned-or-flagged').
within(() => {
// * Check that the post pre-header is visible
cy.get('div.post-pre-header').should('be.visible');
// * Check that the post pre-header has the pinned icon
cy.get('span.icon--post-pre-header.icon-pin').should('be.visible');
// * Check that the post pre-header has the pinned post link
cy.get('div.post-pre-header__text-container').
should('be.visible').
and('have.text', 'Pinned').
within(() => {
cy.get('a').as('pinnedLink').should('be.visible');
});
});
// * Check that the pinned posts list is not open in RHS before clicking the link in the post pre-header
cy.get('#searchContainer').should('not.exist');
// # Click the link
cy.get('@pinnedLink').click();
// * Check that the pinned posts list is open in RHS
cy.get('#searchContainer').should('be.visible').within(() => {
cy.get('.sidebar--right__title').
should('be.visible').
and('contain', 'Pinned Posts').
and('contain', 'Off-Topic');
// * Check that the post pre-header is not shown for the pinned message in RHS
cy.findByTestId('search-item-container').within(() => {
cy.get('div.post__content').should('be.visible');
cy.get(`#rhsPostMessageText_${postId}`).contains('test for pinning/unpinning a post');
cy.get('div.post-pre-header').should('not.exist');
});
});
// # Close the RHS
cy.get('#searchResultsCloseButton').should('be.visible').click();
// # Unpin the post
cy.uiClickPostDropdownMenu(postId, 'Unpin from Channel');
// * Check that the post pre-header is not visible
cy.get('div.post-pre-header').should('not.exist');
// * Check that the post is not highlighted
cy.get(`#post_${postId}`).should('not.have.class', 'post--pinned-or-flagged');
});
});
it('MM-T3354 Handle posts that are both pinned and saved', () => {
// # Post a message
cy.postMessage('test both pinned and saved');
cy.getLastPostId().then((postId) => {
// # Pin the post.
cy.uiClickPostDropdownMenu(postId, 'Pin to Channel');
// # Save the post
cy.clickPostSaveIcon(postId);
// * Check that the post is highlighted
cy.get(`#post_${postId}`).
should('have.class', 'post--pinned-or-flagged').
within(() => {
// * Check that the post pre-header is visible
cy.get('div.post-pre-header').should('be.visible');
// * Check that the post pre-header has the saved icon
cy.get('span.icon--post-pre-header').should('be.visible').
find('svg').should('have.attr', 'aria-label', 'Saved Icon');
// * Check that the post pre-header has the pinned icon
cy.get('span.icon--post-pre-header.icon-pin').should('be.visible');
// * Check that the post pre-header has both the saved and pinned links
cy.get('div.post-pre-header__text-container').
should('be.visible').
and('have.text', `Pinned${'\u2B24'}Saved`).
within(() => {
cy.get('a').should('have.length', 2);
cy.get('a').first().as('pinnedLink').should('be.visible');
cy.get('a').last().as('savedLink').should('be.visible');
});
});
// # Click the saved link
cy.get('@savedLink').click();
// * Check that the post pre-header only shows the pinned link in RHS
cy.findByTestId('search-item-container').within(() => {
cy.get('div.post-pre-header__text-container').
should('be.visible').
and('have.text', 'Pinned');
});
// # Click the pinned link
cy.get('@pinnedLink').click();
// * Check that the post pre-header only shows the saved link in RHS
cy.findByTestId('search-item-container').within(() => {
cy.get('div.post-pre-header__text-container').
should('be.visible').
and('have.text', 'Saved');
});
// # Search for the channel.
cy.get('#searchBox').type('test both pinned and saved {enter}');
// * Check that the post pre-header has both pinned and saved links in RHS search results
cy.get('#searchContainer').should('be.visible').within(() => {
cy.get('.sidebar--right__title').
should('be.visible').
and('have.text', 'Search Results');
cy.findByTestId('search-item-container').within(() => {
cy.get('div.post-pre-header__text-container').
should('be.visible').
and('have.text', `Pinned${'\u2B24'}Saved`);
});
});
});
});
});

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

@@ -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 @messaging
describe('Messaging', () => {
before(() => {
// # Login as test user and visit Off-Topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
// # Add two posts
cy.postMessage('test post 1');
cy.postMessage('test post 2');
});
});
it('MM-T212 Leave a long draft in reply input box', () => {
// # Get latest post id
cy.getLastPostId().then((latestPostId) => {
// # Click reply icon
cy.clickPostCommentIcon(latestPostId);
cy.uiGetReplyTextBox().should('have.css', 'height', '100px').invoke('height').then((height) => {
// # Get the initial height of the textbox
// Setting alias based on reference to element seemed to be problematic with Cypress (regression)
// Quick hack to reference based on value
cy.wrap(height).as('originalHeight1');
cy.wrap(height).as('originalHeight2');
});
// # Write a long text in text box
cy.uiGetReplyTextBox().type('test{shift}{enter}{enter}{enter}{enter}{enter}{enter}{enter}test');
// # Check that input box is taller than before
cy.get('@originalHeight1').then((originalHeight1) => {
cy.uiGetReplyTextBox().invoke('height').should('be.gt', originalHeight1 * 2);
});
// # Get second latest post id
const secondLatestPostIndex = -2;
cy.getNthPostId(secondLatestPostIndex).then((secondLatestPostId) => {
// # Click reply icon on the second latest post
cy.clickPostCommentIcon(secondLatestPostId);
// # Click again reply icon on the latest post
cy.clickPostCommentIcon(latestPostId);
// # Check that input box is taller again
cy.get('@originalHeight2').then((originalHeight2) => {
cy.uiGetReplyTextBox().invoke('height').should('be.gt', originalHeight2 * 2);
});
});
});
});
});

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

@@ -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 @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging - Opening a private channel using keyboard shortcuts', () => {
let testTeam;
before(() => {
// # Create new team and new user and visit Off-Topic channel
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
testTeam = team;
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
});
it('MM-T1225 CTRL/CMD+K - Open private channel using arrow keys and Enter', () => {
cy.apiCreateChannel(testTeam.id, 'private-channel', 'Private channel', 'P').then(() => {
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.wait(TIMEOUTS.HALF_SEC);
cy.typeCmdOrCtrl().type('K', {release: true});
// # Type the first letter of a private channel in the "Switch Channels" modal message box
// # Use up/down arrow keys to highlight a private channel
// # Press ENTER
cy.findByRole('textbox', {name: 'quick switch input'}).type('Pr').type('{downarrow}').type('{enter}');
// * Private channel opens
cy.get('#channelHeaderTitle').should('be.visible').should('contain', 'Private channel').wait(TIMEOUTS.HALF_SEC);
// * Focus in the message box
cy.uiGetPostTextBox().should('be.focused');
});
});
});

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

@@ -0,0 +1,36 @@
// 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
describe('Messaging', () => {
before(() => {
// # Login as test user and visit Off-Topic
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
cy.visit(`/${team.name}/channels/off-topic`);
cy.postMessage('hello');
});
});
it('MM-T3309 Posts do not change order when being sent quickly', () => {
// # Build a message and send
for (let i = 9; i >= 0; i--) {
const message = i + '{enter}';
cy.uiGetPostTextBox().type(message, {delay: 0});
}
for (let i = 10; i > 0; i--) {
cy.getNthPostId(-i).then((postId) => {
// * Check if the text is the correct one
cy.get(`#postMessageText_${postId} > p`).should('have.text', String(i - 1));
});
}
});
});

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

@@ -0,0 +1,88 @@
// 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
describe('Compact view: Markdown quotation', () => {
let testTeam;
let userOne;
let userTwo;
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
userOne = user;
cy.apiCreateUser().then(({user: user2}) => {
userTwo = user2;
cy.apiAddUserToTeam(team.id, userTwo.id);
});
});
});
it('MM-T185 Compact view: Markdown quotation', () => {
cy.apiLogin(userOne);
cy.apiCreateDirectChannel([userOne.id, userTwo.id]).then(() => {
cy.visit(`/${testTeam.name}/messages/@${userTwo.username}`);
// # Post first message in case it is a new Channel
cy.postMessage('Hello' + Date.now());
// # Make sure we are on compact mode
cy.apiSaveMessageDisplayPreference('compact');
resetRoot(testTeam, userOne, userTwo);
// # Post message to use and check
const message = '>Hello' + Date.now();
cy.postMessage(message);
checkQuote(message);
resetRoot(testTeam, userOne, userTwo);
// # Post two messages and check
cy.postMessage('Hello' + Date.now());
cy.postMessage(message);
checkQuote(message);
});
});
function resetRoot(team, user1, user2) {
// # Write a message from user2 to make sure we have the root on compact mode
cy.apiLogout();
cy.apiLogin(user2);
cy.visit(`/${testTeam.name}/messages/@${user1.username}`);
cy.postMessage('Hello' + Date.now());
// # Get back to user1
cy.apiLogout();
cy.apiLogin(user1);
cy.visit(`/${testTeam.name}/messages/@${user2.username}`);
}
function checkQuote(message) {
cy.getLastPostId().then((postId) => {
// * Check if the message is the same sent
cy.get(`#postMessageText_${postId} > blockquote > p`).should('be.visible').and('have.text', message.slice(1));
cy.findAllByTestId('postView').filter('.other--root').last().find('.user-popover').then((userElement) => {
// # Get the username bounding rect
const userRect = userElement[0].getBoundingClientRect();
cy.get(`#postMessageText_${postId}`).find('blockquote').then((quoteElement) => {
// # Get the quote block bounding rect
const blockQuoteRect = quoteElement[0].getBoundingClientRect();
// * We check the username rect does not collide with the quote rect
expect(userRect.right < blockQuoteRect.left || userRect.bottom < blockQuoteRect.top).to.equal(true);
});
});
});
}
});

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

@@ -0,0 +1,187 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
cy.apiLogin(user);
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T2189 Emoji reaction - type +:+1:', () => {
// # Post a message
cy.postMessage('Hello');
cy.getLastPostId().then((postId) => {
// # Click reply to open the post in the right hand side
cy.clickPostCommentIcon(postId);
// # Type "+:+1:" in comment box to react to the post with a thumbs-up and post
cy.postMessageReplyInRHS('+:+1:');
// * Thumbs-up reaction displays as reaction on post
cy.get(`#${postId}_message`).within(() => {
cy.findByLabelText('reactions').should('be.visible');
cy.findByLabelText('remove reaction +1').should('be.visible');
});
// # Close RHS
cy.uiCloseRHS();
});
});
it('MM-T2190 Emoji reaction - click `+` next to existing reaction (center)', () => {
// # Post a message
cy.postMessage('Hello to yourself');
cy.getLastPostId().then((postId) => {
// # Click the add reaction icon
cy.clickPostReactionIcon(postId);
// # Add a reaction to the post
cy.clickEmojiInEmojiPicker('smiley');
// # Click the `+` button next to the existing reactions (visible on hover)
cy.get(`#addReaction-${postId}`).should('exist').click({force: true});
// # Click to select an emoji from the picker
cy.clickEmojiInEmojiPicker('upside_down_face');
// * Emoji reaction is added to the post
cy.get(`#${postId}_message`).within(() => {
cy.findByLabelText('reactions').should('exist');
cy.findByLabelText('remove reaction upside down face').should('exist');
});
// * Reaction appears in recently used section of emoji picker
cy.uiOpenEmojiPicker().then(() => {
cy.findAllByTestId('emojiItem').first().within(($el) => {
cy.wrap($el).findByTestId('upside_down_face').should('exist');
});
});
});
});
it('MM-T2192 RHS (reply) shows emoji picker for reactions - Reply box and plus icon', () => {
// # Post a message
cy.postMessage('Hello to you all');
cy.getLastPostId().then((postId) => {
// # Click a reply arrow to open reply RHS
cy.clickPostCommentIcon(postId).then(() => {
// # Click the expand arrows in top right to expand RHS
cy.uiExpandRHS();
// # mouse ove the root post
cy.get(`#rhsPost_${postId}`).trigger('mouseover');
// # Hover over the message, observe emoji picker icon
cy.get(`#RHS_ROOT_reaction_${postId}`).should('exist').click({force: true});
// # Click the emoji picker icon to react to the message, select a reaction
cy.clickEmojiInEmojiPicker('smiley');
// # Hover over the post again and observe the `+` icon next to your reaction
cy.get(`#addReaction-${postId}`).should('exist').click({force: true});
// # Click the `+` icon and select a different reaction
cy.clickEmojiInEmojiPicker('upside_down_face');
// * Two reactions are added to the message in the expanded RHS
cy.get(`#rhsPost_${postId}`).within(() => {
cy.findByLabelText('reactions').should('be.visible');
cy.findByLabelText('remove reaction smiley').should('be.visible');
cy.findByLabelText('remove reaction upside down face').should('be.visible');
});
// # Close RHS
cy.uiCloseRHS();
});
});
});
it('MM-T2195 Emoji reaction - not available on system message Save - not available on system message Pin - not available on system message Can delete your own system message', () => {
// # Click add a channel header
cy.findByRoleExtended('button', {name: 'Add a channel header'}).should('be.visible').click();
// # Add or update a channel header
cy.get('#editChannelHeaderModalLabel').should('be.visible');
cy.get('textarea#edit_textbox').should('be.visible').type('This is a channel description{enter}');
cy.getLastPostId().then((postId) => {
// * Emoji reaction - not available on system message
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
cy.wait(TIMEOUTS.HALF_SEC).get(`#CENTER_reaction_${postId}`).should('not.exist');
// * Save - not available on system message
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
cy.wait(TIMEOUTS.HALF_SEC).get(`#CENTER_flagIcon_${postId}`).should('not.exist');
// * Pin - not available on system message
cy.get(`#post_${postId}`).should('be.visible').within(() => {
cy.get('.post-menu').should('be.visible').within(() => {
return cy.findByText('Pin to Channel').should('not.exist');
});
});
// * If permissions allow, can click [...] >
cy.clickPostDotMenu(postId);
// # Delete to delete system message
cy.get(`#delete_post_${postId}`).click();
// * Check that confirmation dialog is open.
cy.get('#deletePostModal').should('be.visible');
// # Confirm deletion.
cy.get('#deletePostModalButton').click();
});
});
it('MM-T2196 Emoji reaction - not available on ephemeral message Save - not available on ephemeral message Pin - not available on ephemeral message Timestamp - not a link on ephemeral message Can close ephemeral message', () => {
// # Post `/away` to create an ephemeral message
cy.postMessage('/away ');
cy.getLastPostId().then((postId) => {
// * (Only visible to you) displays next to timestamp (standard view) or after message text (compact view)
cy.get(`#post_${postId}`).should('be.visible').within(() => {
cy.findByText('(Only visible to you)').should('exist');
});
// * Emoji reactions are not available on ephemeral messages
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
cy.wait(TIMEOUTS.HALF_SEC).get(`#CENTER_reaction_${postId}`).should('not.exist');
// * Save not available on ephemeral messages
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
cy.wait(TIMEOUTS.HALF_SEC).get(`#CENTER_flagIcon_${postId}`).should('not.exist');
// * Pin not available on ephemeral messages
cy.get(`#post_${postId}`).within(() => {
cy.wait(TIMEOUTS.HALF_SEC).get('.post-menu').should('not.exist');
});
// * Timestamp is not a link on ephemeral messages
cy.get(`#post_${postId}`).then((post) => {
cy.wrap(post).find('time.post__time').invoke('text');
cy.url().should('not.include', postId);
});
// * Can click `x` to close ephemeral message
cy.wait(TIMEOUTS.HALF_SEC).get('button.post__remove').click({force: true});
cy.get(`#post_${postId}`).should('not.exist');
});
});
});

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

@@ -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.
// ***************************************************************
// Group: @channels @messaging
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Messaging', () => {
let testTeam;
let testChannel;
let testUser;
let userOne;
before(() => {
// # Wrap websocket to be able to connect and close connections on demand
cy.mockWebsockets();
// # Update config to enable "EnableReliableWebSockets"
cy.apiUpdateConfig({ServiceSettings: {EnableReliableWebSockets: true}});
// # Login as test user and go to town-square
cy.apiInitSetup().then(({team, channel, user}) => {
testUser = user;
testTeam = team;
testChannel = channel;
cy.apiCreateUser().then(({user: user1}) => {
userOne = user1;
cy.apiAddUserToTeam(testTeam.id, userOne.id).then(() => {
cy.apiAddUserToChannel(testChannel.id, userOne.id);
});
});
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Post several messages to establish websocket connection
Cypress._.times(5, (i) => {
cy.postMessage(i);
});
});
});
it('MM-T94 RHS fetches messages on socket reconnect when a different channel is in center', () => {
// # Connect all sockets
window.mockWebsockets.forEach((value) => {
value.connect();
});
// # Post a message as another user
cy.postMessageAs({sender: userOne, message: 'abc', channelId: testChannel.id}).wait(TIMEOUTS.FIVE_SEC);
// # Click "Reply"
cy.getLastPostId().then((rootPostId) => {
cy.clickPostCommentIcon(rootPostId);
// # Post a message
cy.postMessageReplyInRHS('def');
// # Change channel
cy.uiGetLhsSection('CHANNELS').findByText('Town Square').click().then(() => {
// # Close all sockets
window.mockWebsockets.forEach((value) => {
if (value.close) {
value.close();
}
});
// # Post message as a different user
cy.postMessageAs({sender: userOne, message: 'ghi', channelId: testChannel.id, rootId: rootPostId});
// # Wait a short time to check whether the message appears or not
cy.wait(TIMEOUTS.FIVE_SEC);
// * Verify that only "def" is posted and not "ghi"
cy.get('#rhsContainer .post-right-comments-container').should('be.visible').children().should('have.length', 1);
cy.get('#rhsContainer .post-right-comments-container').within(() => {
cy.findByText('def').should('be.visible');
cy.findByText('ghi').should('not.exist');
}).then(() => {
// * Connect all sockets one more time
window.mockWebsockets.forEach((value) => {
value.connect();
});
// # Wait for sockets to be connected
cy.wait(TIMEOUTS.THREE_SEC);
cy.uiGetLhsSection('CHANNELS').findByText('Off-Topic').click();
cy.postMessage('any');
cy.uiGetLhsSection('CHANNELS').findByText('Town Square').click();
cy.postMessage('any');
cy.wait(TIMEOUTS.THREE_SEC);
// * Verify that both "def" and "ghi" are posted on websocket reconnect
cy.get('#rhsContainer .post-right-comments-container').should('be.visible').children().should('have.length', 2);
cy.get('#rhsContainer .post-right-comments-container').within(() => {
cy.findByText('def').should('be.visible');
cy.findByText('ghi').should('be.visible');
});
});
});
});
});
});

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

@@ -0,0 +1,156 @@
// 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
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getAdminAccount} from '../../../support/env';
describe('Messaging', () => {
const admin = getAdminAccount();
let testUser;
let testTeam;
before(() => {
// # Set the configuration on Link Previews
cy.apiUpdateConfig({
ServiceSettings: {
EnableLinkPreviews: true,
},
});
// # Login as test user and go to town-square
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('MM-T114_1 Delete a GIF from RHS reply thread, other user viewing in center and RHS sees GIF preview disappear from both', () => {
// # Type message to use
cy.postMessage('123');
// # Click Reply button
cy.clickPostCommentIcon();
// # Write message on reply box
cy.postMessageReplyInRHS('https://media1.giphy.com/media/l41lM6sJvwmZNruLe/giphy.gif');
// # Change user and go to Town Square
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Click Reply button to open the RHS
cy.clickPostCommentIcon();
// # Remove message from the other user
cy.getLastPostId().as('postId').then((postId) => {
// * Can view the gif on main view
cy.get(`#post_${postId}`).find('.attachment__image').should('exist');
// * Can view the gif on RHS
cy.get(`#rhsPost_${postId}`).find('.attachment__image').should('exist');
// # Delete the message
cy.externalRequest({user: admin, method: 'DELETE', path: `posts/${postId}`});
// # Wait for the message to be deleted
cy.wait(TIMEOUTS.HALF_SEC);
// * Cannot view the gif on main channel
cy.get(`#post_${postId}`).find('.attachment__image').should('not.exist');
// * Should see (message deleted)
cy.get(`#post_${postId}`).should('contain', '(message deleted');
// * Cannot view the gif on RHS
cy.get(`#rhsPost_${postId}`).find('.attachment__image').should('not.exist');
// * Should see (message deleted)
cy.get(`#rhsPost_${postId}`).should('contain', '(message deleted');
// # Refresh the website and wait for it to be loaded
cy.reload();
cy.wait(TIMEOUTS.FIVE_SEC);
// * The RHS is closed
cy.get('#rhsCloseButton').should('not.exist');
// * Should see (message deleted)
cy.get(`#post_${postId}`).should('not.exist');
// # Log in as the other user and go to town square
cy.apiAdminLogin();
cy.visit(`/${testTeam.name}/channels/town-square`);
// * The post should not exist
cy.get(`#post_${postId}`).should('not.exist');
});
});
it('MM-T114_2 Delete a GIF from RHS reply thread, other user viewing in center and RHS sees GIF preview disappear from both (mobile view)', () => {
cy.apiAdminLogin();
// # Type message to use
cy.postMessage('123');
// # Click Reply button
cy.clickPostCommentIcon();
// # Write message on reply box
cy.postMessageReplyInRHS('https://media1.giphy.com/media/l41lM6sJvwmZNruLe/giphy.gif');
// # Change user and go to Town Square
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Change viewport so it has mobile view
cy.viewport('iphone-6');
// # Click Reply button to open the Message Details
cy.clickPostCommentIcon();
// # Remove message from the other user
cy.getLastPostId().as('postId').then((postId) => {
// * Can view the gif on Message Details
cy.get(`#rhsPost_${postId}`).find('.attachment__image').should('exist').and('be.visible');
// # Close Message Details
cy.get('#sbrSidebarCollapse').click();
// * Can view the gif on main view
cy.get(`#post_${postId}`).find('.attachment__image').should('exist').and('be.visible');
// # Click Reply button to open the Message Details
cy.clickPostCommentIcon();
// # Delete the message
cy.externalRequest({user: admin, method: 'DELETE', path: `posts/${postId}`});
// # Wait for the message to be deleted
cy.wait(TIMEOUTS.HALF_SEC);
// * Cannot view the gif on main channel
cy.get(`#post_${postId}`).find('.attachment__image').should('not.exist');
// * Cannot view the gif on RHS
cy.get(`#rhsPost_${postId}`).find('.attachment__image').should('not.exist');
// # Log in as the other user and go to town square
cy.apiAdminLogin();
cy.visit(`/${testTeam.name}/channels/town-square`);
// * The post should not exist
cy.get(`#post_${postId}`).should('not.exist');
});
});
});

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