Move /e2e -> /e2e-tests
Этот коммит содержится в:
@@ -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("${Cypress.config('baseUrl')}/static/emoji/1f600.png");">: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 ');
|
||||
|
||||
// * 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(`[](${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 = '';
|
||||
|
||||
// # 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user