snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 00:29:55 +05:00
commit 240db4e2a7
15309 changed files with 2173406 additions and 0 deletions

View File

@@ -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 @channel
describe('Channel user count', () => {
let testTeam;
let secondUser;
before(() =>
// # Create a new user and assign it to a team
cy.apiInitSetup().then(({team}) => {
testTeam = team;
// # Visit 'off-topic' channel for this team, as sysadmin
cy.visit(`/${testTeam.name}/channels/off-topic`);
}),
);
it('MM-T481 User count is updated if user automatically joins channel', () => {
const initialUserCount = 2;
// * Assert channel user count displays '2' (system admin + main user)
cy.get('#channelMemberCountText').should('be.visible').and('have.text', `${initialUserCount}`);
cy.get('#channelMemberCountText').invoke('text').as('initialUserCountText');
// # Create another user
cy.apiCreateUser().then(({user}) => {
secondUser = user;
// # Add new user (secondUser) to current team
cy.apiAddUserToTeam(testTeam.id, secondUser.id);
});
cy.apiGetChannelByName(testTeam.name, 'off-topic').then(({channel}) => {
// # Add secondUser to 'off-topic' channel
cy.apiAddUserToChannel(channel.id, secondUser.id);
});
// * Assert channel user count now displays '3' (system admin + main user + second user)
cy.get('#channelMemberCountText').should('be.visible').and('have.text', `${initialUserCount + 1}`);
});
});

View File

@@ -0,0 +1,106 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import * as TIMEOUTS from '@/fixtures/timeouts';
import * as MESSAGES from '@/fixtures/messages';
// ***************************************************************
// [#] indicates a test step (e.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 @multi_team_and_dm
describe('Multi-Team + DMs', () => {
let userA;
let userB;
let testChannelUrl;
const away = {name: 'away', ariaLabel: 'Away', message: 'You are now away', className: 'icon-clock'};
const online = {name: 'online', ariaLabel: 'Online', message: 'You are now online', className: 'icon-check', profileClassName: 'icon-check-circle'};
before(() => {
cy.apiInitSetup().then(({team, user}) => {
userA = user;
testChannelUrl = `/${team.name}/channels/town-square`;
cy.apiCreateUser().then(({user: otherUser}) => {
userB = otherUser;
cy.apiAddUserToTeam(team.id, userB.id);
});
});
});
it('MM-T423 Online Status - Statuses update in center, in member icon drop-down, and in DM LHS sidebar', () => {
// # Login with UserB and post a message to UserA
cy.apiLogin(userB);
cy.visit(testChannelUrl);
cy.postMessage(MESSAGES.SMALL);
cy.uiAddDirectMessage().click();
cy.get('#selectItems').typeWithForce(userA.username);
cy.findByText('Loading').should('be.visible');
cy.findByText('Loading').should('not.exist');
cy.get('#multiSelectList').findByText(`@${userA.username}`).click();
cy.findByText('Go').click();
cy.postMessage(MESSAGES.SMALL);
// # Set online status and verify it's changed as the initial status
setStatus(online.name, online.profileClassName);
verifyUserStatus(away);
// # Login with UserA and verify Status of UserB
cy.apiLogout();
cy.apiLogin(userA);
cy.visit(testChannelUrl);
// * Verify status shown at username in the DM list on LHS
cy.get(`[aria-label^="${userB.username}"]`).
children().
find('i').should('have.class', 'status-away');
// * Verify user's status show in the members list on RHS
cy.get('#member_rhs').click();
cy.get('#rhsContainer').should('be.visible');
cy.get(`[data-testid="memberline-${userB.id}"]`).
should('be.visible').
children('div.Avatar-IGMzc').
children().
find('svg').should('have.attr', 'aria-label', 'Away');
});
});
function setStatus(status, icon) {
cy.apiUpdateUserStatus(status);
cy.uiGetProfileHeader().
find('i').
and('have.class', icon);
}
function verifyUserStatus(testCase) {
// # Clear then type '/'
cy.uiGetPostTextBox().clear().type('/');
// * Verify that the suggestion list is visible
cy.get('#suggestionList').should('be.visible');
// # Post slash command to change user status
cy.uiGetPostTextBox().type(`${testCase.name}{enter}`).wait(TIMEOUTS.ONE_HUNDRED_MILLIS).type('{enter}');
// * Get last post and verify system message
cy.getLastPost().within(() => {
cy.findByText(testCase.message);
cy.findByText('(Only visible to you)');
});
// * Verify status shown at user profile in LHS
cy.uiGetProfileHeader().
find('i').
and('have.class', testCase.profileClassName || testCase.className);
// # Post a message
cy.postMessage(testCase.name);
// * Verify that the profile in the posted message shows correct status
cy.get('.post__img').last().findByLabelText(testCase.ariaLabel);
}

View File

@@ -0,0 +1,143 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Stage: @prod
// Group: @channels @multi_team_and_dm
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Direct messages: redirections', () => {
let testUser;
let secondDMUser;
let firstDMUser;
let testTeam;
let offTopicChannelUrl;
before(() => {
// # Create a new team
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
offTopicChannelUrl = `/${testTeam.name}/channels/off-topic`;
cy.apiCreateUser().then(({user: createdUser}) => {
firstDMUser = createdUser;
cy.apiAddUserToTeam(testTeam.id, firstDMUser.id);
});
// # Create a second test user
cy.apiCreateUser().then(({user: createdUser}) => {
secondDMUser = createdUser;
cy.apiAddUserToTeam(testTeam.id, secondDMUser.id);
});
// # Login as test user
cy.apiLogin(testUser);
// # View 'off-topic' channel
cy.visit(offTopicChannelUrl);
});
});
it('MM-T453_1 Closing a direct message should redirect to town square channel', () => {
// # From the 'Direct Messages' menu, find a specific user and send 'hi'
sendDirectMessageToUser(firstDMUser, 'hi');
// # Close the direct message via 'x' button right of the username in the direct messages' list
closeDirectMessage(testUser, firstDMUser, testTeam);
// * Expect to be redirected to town square channel, check channel title and url
expectActiveChannelToBe('Town Square', `/${testTeam.name}/channels/town-square`);
// # From the 'Direct Messages' menu, find the same user as before and send 'hi'
sendDirectMessageToUser(firstDMUser, 'hi again');
// # Open channel menu and click Close Direct Message
cy.uiOpenChannelMenu('Close Direct Message');
// * Expect to be redirected to town square channel, check channel title and url
expectActiveChannelToBe('Town Square', `/${testTeam.name}/channels/town-square`);
});
it('MM-T453_2 Closing a different direct message should not affect active direct message', () => {
// # Send a direct message to a first user
sendDirectMessageToUser(firstDMUser, 'hi first');
// # Send a message to a second user
sendDirectMessageToUser(secondDMUser, 'hi second');
// # Close the direct message previously opened with the first user
closeDirectMessage(testUser, firstDMUser, testTeam);
// * Expect channel title and url to secondDMUser's username
expectActiveChannelToBe(secondDMUser.username, `/messages/@${secondDMUser.username}`);
});
it('MM-T453_3 Changing URL to root url when viewing a direct message should redirect to direct message', () => {
// # Send a direct message to a first user
sendDirectMessageToUser(firstDMUser, 'hi');
// # Visit root url
cy.visit('/');
// * Expect channel title and url to firstDMUser's username
expectActiveChannelToBe(firstDMUser.username, `/messages/@${firstDMUser.username}`);
});
});
const expectActiveChannelToBe = (title, url) => {
// * Expect channel title to match title passed in argument
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', title);
// * Expect url to match url passed in argument
cy.url().should('contain', url);
};
const sendDirectMessageToUser = (user, message) => {
// # Open a new direct message with firstDMUser
cy.uiAddDirectMessage().click();
// # Type username
cy.get('#selectItems input').should('be.enabled').typeWithForce(`@${user.username}`).wait(TIMEOUTS.ONE_SEC);
// * Expect user count in the list to be 1
cy.get('#multiSelectList').
should('be.visible').
children().
should('have.length', 1);
// # Select first user in the list
cy.get('body').
type('{downArrow}').
type('{enter}');
// # Click on "Go" in the group message's dialog to begin the conversation
cy.get('#saveItems').click();
// * Expect the channel title to be the user's username
// In the channel header, it seems there is a space after the username, justifying the use of contains.text instead of have.text
cy.get('#channelHeaderTitle').should('be.visible').and('contain.text', user.username);
// # Type message and send it to the user
cy.uiGetPostTextBox().
type(message).
type('{enter}');
};
const closeDirectMessage = (sender, recipient, team) => {
// # Find the username in the 'Direct Messages' list and trigger the 'x' button to appear (hover over the username)
cy.apiGetChannelsForUser(sender.id, team.id).then(({channels}) => {
// Get the name of the channel to build the CSS selector for that specific DM link in the sidebar
const channelDmWithFirstUser = channels.find((channel) =>
channel.type === 'D' && channel.name.includes(recipient.id),
);
// # Close the DM via sidebar channel menu
cy.uiGetChannelSidebarMenu(channelDmWithFirstUser.name, true).within(() => {
cy.findByText('Close Conversation').click();
});
});
};

View File

@@ -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 @multi_team_and_dm
describe('Multi-user group messages', () => {
let testUser;
let testTeam;
before(() => {
// # Create a new team
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
// # Add 3 users to the team
Cypress._.times(3, () => createUserAndAddToTeam(testTeam));
});
});
it('MM-T1799 Should Close Group Message from channel menu', () => {
// # Login as test user
cy.apiLogin(testUser);
// # Go to town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open the 'Direct messages' dialog to create a new direct message
cy.uiAddDirectMessage().should('be.visible').click();
// # Add 3 users to a group message
addUsersToGMViaModal(3);
// * Check that the list is populated
cy.get('.react-select__multi-value').should('have.length', 3);
// # Click on "Go" in the group message's dialog to begin the conversation
cy.get('#saveItems').click();
// # Click channel dropdown icon to open menu
cy.get('#channelHeaderTitle').click();
// # Click 'close' item
cy.findByText('Close Group Message').should('be.visible').click();
// * Validate that GM is closed and we are redirected
expectActiveChannelToBe('Town Square', '/town-square');
});
});
// Helper functions
const expectActiveChannelToBe = (title, url) => {
// * Expect channel title to match title passed in argument
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', title);
// * Expect url to match url passed in argument
cy.url().should('contain', url);
};
const createUserAndAddToTeam = (team) => {
cy.apiCreateUser({prefix: 'gm'}).then(({user}) =>
cy.apiAddUserToTeam(team.id, user.id),
);
};
/**
* Add a given amount of numbers to a direct message group
* @param {number} userCountToAdd
*/
const addUsersToGMViaModal = (userCountToAdd) => {
// # Ensure there are enough selectable users in the list
cy.get('#multiSelectList').
should('be.visible').
children().
should('have.length.gte', userCountToAdd);
// # Add the first user from the top of the list, as many times as requested by 'userCountToAdd'
Cypress._.times(userCountToAdd, () => {
cy.get('#multiSelectList').
children().
first().
click();
});
};

View File

@@ -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 @multi_team_and_dm
describe('Multi Team and DM', () => {
let testChannel;
let testTeam;
let testUser;
let searchTerm;
const prefix = 'testuser';
before(() => {
// # Setup with the new team, channel and user
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
searchTerm = user.username;
// # Create 52 users so the user must page forward in the dm list
Cypress._.times(52, () => {
cy.apiCreateUser({prefix}).then(() => {
cy.apiAddUserToTeam(testTeam.id, user.id);
});
});
// # Login with testUser and visit test channel
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
});
it('MM-T446 DM More... searching from page 2 of user list', () => {
// # Open the Direct Message modal
cy.uiAddDirectMessage().click();
// # Move to the next page of users
cy.findByText('Next').click();
cy.findByText('Previous').should('exist');
// # Enter a search term
cy.findByRole('combobox', {name: 'Search for people'}).typeWithForce(searchTerm);
// * Assert that the previous / next links do not appear since there should only be 1 record displayed
cy.findByText('Next').should('not.exist');
cy.findByText('Previous').should('not.exist');
// * Assert that the search term does not return wrong user(s)
cy.findAllByText(prefix).should('not.exist');
// * Assert that the search term returns the correct user and is visible
cy.findByText(`${searchTerm}@sample.mattermost.com`).should('be.visible');
});
});

View File

@@ -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 @multi_team_and_dm
import {getRandomId} from '@/utils';
describe('Multi Team and DM', () => {
let testChannel;
let testTeam;
let testUser;
const unique = `${getRandomId(4)}`;
before(() => {
// # Setup with the new team, channel and user
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
// # Create 4 test users with prefix 'common'
Cypress._.times(4, () => {
cy.apiCreateUser({prefix: 'common'}).then(() => {
cy.apiAddUserToTeam(testTeam.id, user.id);
});
});
// # Create 2 test users with random prefix
Cypress._.times(2, () => {
cy.apiCreateUser({prefix: unique}).then(() => {
cy.apiAddUserToTeam(testTeam.id, user.id);
});
});
// # Login with testUser and visit test channel
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
});
it('MM-T444 DM More... show user count', () => {
// # Open the Direct Message modal
cy.uiAddDirectMessage().click();
cy.get('#multiSelectHelpMemberInfo > :nth-child(2)').then((number) => {
// # Grab total number of users before filter applied
const totalUsers = number.text().split(' ').slice(2, 3);
// * Assert that 2 unique users are displayed
cy.findByRole('combobox', {name: 'Search for people'}).typeWithForce(unique).then(() => {
cy.get('#multiSelectList').within(() => {
cy.get('.more-modal__details').should('have.length', 2);
});
// * Assert that total number of users is displayed correctly
cy.get('#multiSelectHelpMemberInfo').should('contain', 'Use ↑↓ to browse, ↵ to select. You can add 7 more people. ').should('contain', `2 of ${totalUsers} members`);
});
});
});
});

View File

@@ -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 @multi_team_and_dm
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Channel', () => {
let testTeamId;
before(() => {
// # Login as test user and visit town-square
cy.apiInitSetup({loginAfter: true}).then(({team}) => {
testTeamId = team.id;
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('MM-T422_1 Channel name already taken for public channel', () => {
// # Create a new public channel
createNewChannel('unique-public', false, testTeamId).as('channel');
cy.reload();
cy.get('@channel').then((channel) => {
// * Verify new public channel cannot be created with existing public channel name
verifyChannel(channel);
});
});
it('MM-T422_2 Channel name already taken for private channel', () => {
// # Create a new private channel
createNewChannel('unique-private', true, testTeamId).as('channel');
cy.reload();
cy.get('@channel').then((channel) => {
// * Verify new private channel cannot be created with existing private channel name
verifyChannel(channel);
});
});
it('MM-43881 Channel name already taken for public channel and changed allows to create', () => {
// # Create a new public channel
createNewChannel('other-public', false, testTeamId).as('channel');
cy.reload();
cy.get('@channel').then((channel) => {
// * Verify new public channel cannot be created with existing public channel name
verifyExistingChannelError(channel.name);
// Update channel name in the input field for new channel
cy.get('#input_new-channel-modal-name').should('be.visible').click().type(`${channel.name}1`);
cy.wait(TIMEOUTS.HALF_SEC);
// * Error message saying "A channel with that name already exists" should be removed
cy.get('.url-input-error').should('not.exist');
// * 'Create Channel' button should be enabled
cy.findByText('Create channel').should('not.have.class', 'disabled');
// # Click on Cancel button to move out of New Channel Modal
cy.findByText('Cancel').click();
});
});
});
/**
* Creates a channel with existing name and verify that error is shown
* @param {String} newChannelName - New channel name to assign
* @param {boolean} makePrivate - Set to false to make public channel (default), otherwise true as private channel
*/
function verifyExistingChannelError(newChannelName, makePrivate = false) {
// Click on '+' button for Public or Private Channel
cy.uiBrowseOrCreateChannel('Create new channel');
if (makePrivate) {
cy.get('#public-private-selector-button-P').click();
} else {
cy.get('#public-private-selector-button-O').click();
}
// Type `newChannelName` in the input field for new channel
cy.get('#input_new-channel-modal-name').should('be.visible').click().type(`${newChannelName}{enter}`);
// * User gets a message saying "A channel with that name already exists"
cy.get('.url-input-error').contains('A channel with that URL already exists');
// * 'Create Channel' button should be disabled
cy.findByText('Create channel').should('have.class', 'disabled');
}
/**
* Attempts to create public and private channels with existing `channelName` and verifies error
* @param {String} channelName - Existing channel name that is also being tested for error
*/
function verifyChannel(channel) {
// # Find current number of channels
cy.uiGetLhsSection('CHANNELS').find('.SidebarChannel').its('length').as('origChannelLength');
// * Verify channel `channelName` exists
cy.uiGetLhsSection('CHANNELS').should('contain', channel.display_name);
// * Verify new public channel cannot be created with existing public channel name
verifyExistingChannelError(channel.name);
// # Click on Cancel button to move out of New Channel Modal
cy.findByText('Cancel').click();
// * Verify new private channel cannot be created with existing public channel name
verifyExistingChannelError(channel.name, true);
// # Click on Cancel button to move out of New Channel Modal
cy.findByText('Cancel').click();
// * Verify the number of channels is still the same as before
cy.get('@origChannelLength').then((origChannelLength) => {
cy.uiGetLhsSection('CHANNELS').find('.SidebarChannel').its('length').should('equal', origChannelLength);
});
}
/**
* Create new channel via API
* @param {String} name Name of the channel. This will be used for both name and display_name
* @param {Boolean} isPrivate Should the channel be private
* @param {String} testTeamId Team where to create a channel
* @returns body of request
*/
function createNewChannel(name, isPrivate = false, testTeamId) {
const makePrivate = isPrivate ? 'P' : 'O';
return cy.apiCreateChannel(testTeamId, name, name, makePrivate, 'Let us chat here').its('channel');
}

View File

@@ -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 @multi_team_and_dm
describe('Close group messages', () => {
let testUser;
let otherUser1;
let otherUser2;
let testTeam;
before(() => {
cy.apiAdminLogin();
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser({prefix: 'aaa'}).then(({user: newUser}) => {
otherUser1 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
cy.apiCreateUser({prefix: 'bbb'}).then(({user: newUser}) => {
otherUser2 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('MM-T474 - GM: Favorite, and close', () => {
createAndVisitGMChannel([otherUser1, otherUser2]).then((channel) => {
// # Favorite the channel
cy.get('#toggleFavorite').click();
// * Check that the channel is on top of favorites list
cy.uiGetLhsSection('FAVORITES').find('.SidebarChannel').first().should('contain', channel.display_name.replace(`, ${testUser.username}`, ''));
// # Click on the x button on the sidebar channel item
cy.uiGetChannelSidebarMenu(channel.name, true).within(() => {
cy.findByText('Close Conversation').click();
});
verifyChannelWasProperlyClosed(channel.name);
});
});
function createAndVisitGMChannel(users = []) {
const userIds = users.map((user) => user.id);
return cy.apiCreateGroupChannel(userIds).then(({channel}) => {
// # Visit the new channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Verify channel's display name
const displayName = users.
map((member) => member.username).
sort((a, b) => a.localeCompare(b, 'en', {numeric: true})).
join(', ');
cy.get('#channelHeaderTitle').should('contain', displayName);
return cy.wrap(channel);
});
}
// Make sure that the current channel is Town Square and that the
// channel identified by the passed name is no longer in the channel
// sidebar
function verifyChannelWasProperlyClosed(channelName) {
// * Make sure that we have switched channels
cy.get('#channelHeaderTitle').should('contain', 'Town Square');
// * Make sure the old DM no longer exists
cy.get('#sidebarItem_' + channelName).should('not.exist');
}
});

View File

@@ -0,0 +1,197 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Stage: @prod
// Group: @channels @multi_team_and_dm
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Multi-user group messages', () => {
let testUser;
let testTeam;
const userIds = [];
const userList = [];
let groupChannel;
before(() => {
// # Create a new team
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
// # Add 3 users to the team that should be alphabetically sorted
['0aadam', '0aabadam', 'beatrice'].forEach((prefix) => {
createTestUser(prefix, team);
});
// # Create a GM with at least 3 users
['charlie', 'diana', 'eddie'].forEach((name) => {
cy.apiCreateUser({prefix: name, bypassTutorial: true}).then(({user: groupUser}) => {
cy.apiAddUserToTeam(testTeam.id, groupUser.id);
userIds.push(groupUser.id);
userList.push(groupUser);
});
});
// # Add test user to the list of group members
userIds.push(testUser.id);
cy.apiCreateGroupChannel(userIds).then(({channel}) => {
groupChannel = channel;
});
});
});
it('MM-T459 Group Messaging: Add first user', () => {
const searchTerm = '0aa';
// # Login as test user
cy.apiLogin(testUser);
// # Go to town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.contains('#channelHeaderTitle', 'Town Square');
// # Open the 'Direct messages' dialog
cy.uiAddDirectMessage().click().wait(TIMEOUTS.ONE_SEC);
cy.findByRole('dialog', {name: 'Direct Messages'}).should('be.visible').wait(TIMEOUTS.ONE_SEC);
// # Start typing part of a username that matches previously created users
cy.findByRole('combobox', {name: 'Search for people'}).
typeWithForce(searchTerm).
wait(TIMEOUTS.ONE_SEC);
// * Expect user list to only contain usernames matching the query term and to be sorted alphabetically
expectUserListSortedAlphabetically(searchTerm);
// # With the arrow and enter keys, select the first user that matches our search query
cy.get('body').
type('{downarrow}').
type('{enter}');
// * Expect to have the selected user's username to be displayed inside the search input
cy.get('.react-select__multi-value').
should('be.visible').
and('have.length', 1).
then(($selectedUser) => {
// # Get the selected user's username
const selectedUserName = $selectedUser.text();
// * Expect user list to not contain selected user, not be filtered, but still in alphabetical order
expectUserListSortedAlphabetically(selectedUserName, true);
});
// * Expect to have a remove button ('x') present next to the selected username (previous step)
cy.get('.react-select__multi-value__remove').
should('be.visible').
and('have.length', 1);
// * Expect search bar to have focus
cy.get('.react-select__multi-value').next().find('input').should('have.focus');
// * Expect previous search term to be cleared from search bar
cy.get('#selectItems input').should('have.text', '');
// * Expect the help section showing that we 6 more people can be added to the message
cy.get('#multiSelectHelpMemberInfo').
should('be.visible').
and('contain.text', 'You can add 6 more people');
});
it('MM-T468 Group Messaging: Add member to existing GM', () => {
cy.apiLogin(testUser);
// # Add some messages to the channel
cy.visit(`/${testTeam.name}/channels/${groupChannel.name}`);
cy.postMessage('some');
cy.postMessage('historical');
cy.postMessage('messages');
// # Open channel menu and click Add Members
cy.uiOpenChannelMenu('Members');
cy.uiGetButton('Add').click();
// * Verify message says: "This will start a new conversation. If you're adding a lot of people, consider creating a private channel instead."
cy.get('#moreDmModal').should('be.visible');
const warnMessage = 'This will start a new conversation. If you\'re adding a lot of people, consider creating a private channel instead.';
cy.contains(warnMessage).should('be.visible');
// * Verify users are listed in the GM box
userList.forEach((user) => {
cy.get('.react-select__multi-value div').should('contain', user.username);
});
// # Type a search term and select an autocomplete option.
cy.get('#selectItems input').typeWithForce('beatrice');
cy.get('.loading-screen').should('not.exist');
cy.contains('#multiSelectList .clickable', 'beatrice').should('be.visible'); // .click(); runs into detached dom element
cy.get('#selectItems input').typeWithForce('{enter}');
// # Click Go
cy.get('button#saveItems').click({force: true});
// * Modal closes
cy.get('#moreDmModal').should('not.exist');
cy.wait(TIMEOUTS.ONE_SEC);
// * Original messages does not exist
cy.contains('.post-message__text', 'historical').should('not.exist');
cy.contains('p.channel-intro__text', 'This is the start of your group message history with');
// * New user is added to the GM
cy.uiGetRHS().contains(testUser.username).should('be.visible');
// * Other users are still there
userList.forEach((user) => {
cy.uiGetRHS().contains(user.username).scrollIntoView().should('be.visible');
});
});
});
// Helper functions
/**
* Check that the user list inside the direct messages dialog is sorted alphabetically
* - Numbers are included in the sorting logic
* - Lowercase letters come before uppercase letters
* @param {string?} filterString Optional, ensure all results in the list match a certain string, an autocomplete filter
*/
const expectUserListSortedAlphabetically = (filterString, excludeFilter = false) => {
return cy.get('#multiSelectList').
should('be.visible').
children().
each(($child) => {
// To limit the amount of text fetched, only get the text content from the displayed name and username (without email)
const currentChildText = $child.find('[id*="displayedUserName"]').text();
const immediateNextSibling = $child.next();
// * Expect all usernames displayed in the in user list to be sorted alphabetically
if (immediateNextSibling.length) {
const siblingText = immediateNextSibling.find('[id*="displayedUserName"]').text();
const stringComparison = currentChildText.localeCompare(siblingText, 'en');
// Both strings equal -> 0, currentChildText comes before siblingText -> -1 (could vary by browser but should not be negative)
expect(stringComparison, `${currentChildText} should be before ${siblingText}`).to.be.lte(0);
}
if (excludeFilter) {
// * If a user is selected, ensure remaining users list does not contain the selected user
expect(currentChildText).to.not.contain(filterString);
} else {
// * Optionally, ensure all usernames match the autocomplete input
expect(currentChildText).to.contain(filterString);
}
});
};
/**
* Create a user and assign it to a team
* @param {string} prefix Prefix to add to the username
* @param {string} team Team object, reference to the team we want to add the user to
*/
const createTestUser = (prefix, team) => {
cy.apiCreateUser({prefix}).then(({user}) =>
cy.apiAddUserToTeam(team.id, user.id),
);
};

View File

@@ -0,0 +1,164 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @multi_team_and_dm
import {beRead, beUnread} from '@/support/assertions';
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Multi-user group header', () => {
let testUser;
let testTeam;
const userIds = [];
const userList = [];
let groupChannel;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
// # create a GM with at least 3 users
['charlie', 'diana', 'eddie'].forEach((name) => {
cy.apiCreateUser({prefix: name, bypassTutorial: true}).then(({user: groupUser}) => {
cy.apiAddUserToTeam(testTeam.id, groupUser.id);
userIds.push(groupUser.id);
userList.push(groupUser);
});
});
// # add test user to the list of group members
userIds.push(testUser.id);
cy.apiCreateGroupChannel(userIds).then(({channel}) => {
groupChannel = channel;
});
});
});
it('MM-T472 Add a channel header to a GM', () => {
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${groupChannel.name}`);
const header = 'peace and progress';
// * Verify that no channel header is set
cy.get('#channel-header').within(() => {
cy.findByText('Add a channel header').should('not.be.visible');
});
// # Force click on button which is hidden and shows on hover
cy.findByText('Add a channel header').click({force: true});
// * Verify the modal open to add header
cy.get('#editChannelHeaderModalLabel').should('be.visible').wait(TIMEOUTS.ONE_SEC);
// # Add the header in the modal
cy.findByPlaceholderText('Enter the Channel Header').should('be.visible').type(`${header}{enter}`);
// # Wait for modal to disappear
cy.waitUntil(() => cy.get('#editChannelHeaderModalLabel').should('not.be.visible'));
// * text appears in the top center panel
cy.get('#channel-header').within(() => {
cy.findByText(header).should('be.visible');
});
checkSystemMessage('updated the channel header');
// * Channel is marked as read for the current user
cy.get(`#sidebarItem_${groupChannel.name}`).should(beRead);
// * Channel is marked as unread for other user
cy.apiLogout();
cy.apiLogin(userList[0]);
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.get(`#sidebarItem_${groupChannel.name}`).should(beUnread);
cy.apiLogout();
});
it('MM-T473_1 Edit GM channel header', () => {
// # open existing GM
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${groupChannel.name}`);
// * Verify that channel header is set
cy.get('#channel-header').within(() => {
cy.findByText('Add a channel header').should('not.exist');
});
const header = 'In pursuit of peace and progress';
editHeader(header);
// * Header text appears at the top
cy.get('#channel-header').within(() => {
cy.findByText(header).should('be.visible');
});
checkSystemMessage('updated the channel header');
// * Channel is marked as unread for other users
cy.apiLogout();
cy.apiLogin(userList[0]);
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.get(`#sidebarItem_${groupChannel.name}`).should(beUnread);
cy.apiLogout();
});
it('MM-T473_2 Edit GM channel header', () => {
// # Open existing GM
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${groupChannel.name}`);
// * Verify that channel header is set
cy.get('#channel-header').within(() => {
cy.findByText('Add a channel header').should('not.exist');
});
const header = `In pursuit of peace and progress by @${testUser.username}`;
editHeader(header);
// * Header text appears at the top
cy.get('#channel-header').within(() => {
// * Verify mention is present
cy.get('.mention-link').should('be.visible').and('have.text', `@${testUser.username}`);
// * Verify its not highlighted
cy.get('.mention--highlight').should('not.exist');
});
});
function editHeader(header) {
// # Click edit header
cy.uiOpenChannelMenu('');
cy.findByRole('menuitem', {name: 'Settings'}).trigger('mouseover');
cy.findByText('Edit Header').click();
// * Verify the modal open to add header
cy.get('#editChannelHeaderModalLabel').should('be.visible').wait(TIMEOUTS.ONE_SEC);
// # Add the header in the modal
cy.get('textarea#edit_textbox').should('be.visible').clear().type(`${header}{enter}`);
// # Wait for modal to disappear
cy.waitUntil(() => cy.get('#editChannelHeaderModalLabel').should('not.be.visible'));
}
function checkSystemMessage(message) {
// * system message is posted notifying of the change
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).should('contain', message);
cy.clickPostDotMenu(id).then(() => {
// * system message can be deleted
cy.get(`#delete_post_${id}`);
});
});
}
});

View File

@@ -0,0 +1,117 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Stage: @prod
// Group: @channels @multi_team_and_dm
describe('Join an open team from a direct message link', () => {
let openTeam;
let testUserInOpenTeam;
let publicChannelInOpenTeam;
let secondTestTeam;
let testUserOutsideOpenTeam;
before(() => {
cy.apiCreateTeam('mmt452-second-team', 'mmt452-second-team', 'I', true).then(({team}) => {
secondTestTeam = team;
// # Create test user in closed team
cy.apiCreateUser().then(({user}) => {
testUserOutsideOpenTeam = user;
cy.apiAddUserToTeam(secondTestTeam.id, testUserOutsideOpenTeam.id);
});
});
cy.apiCreateTeam('mmt452-open-team', 'mmt452-open-team', 'O', true).then(({team}) => {
openTeam = team;
// # Allow any user with an account on this server to join this team
cy.apiPatchTeam(openTeam.id, {
allow_open_invite: true,
});
// # Create a public channel inside the open team
cy.apiCreateChannel(openTeam.id, 'open-team-channel', 'open-team-channel').then(({channel}) => {
publicChannelInOpenTeam = channel;
});
// # Create test user
cy.apiCreateUser().then(({user}) => {
testUserInOpenTeam = user;
// # Add user to open team
cy.apiAddUserToTeam(openTeam.id, testUserInOpenTeam.id);
// # Login as test user
cy.apiLogin(testUserInOpenTeam);
});
});
});
it('MM-T452 User with no teams should be able to join an open team from a link in direct messages', () => {
// # View 'off-topic' channel
cy.visit(`/${openTeam.name}/channels/${publicChannelInOpenTeam.name}`);
// * Expect channel title to match title passed in argument
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', publicChannelInOpenTeam.display_name);
// # Copy full url to channel
cy.url().then((publicChannelUrl) => {
// # From the 'Direct Messages' menu, send the URL of the public channel to the user outside the team
sendDirectMessageToUser(testUserOutsideOpenTeam, openTeam, publicChannelUrl);
// # Logout as test user
cy.apiLogout();
// # Login as user outside team
cy.apiLogin(testUserOutsideOpenTeam);
// # Reload the page to ensure the new session is active
cy.reload();
// * Expect page to reload with new user session, username should display in the header
cy.uiOpenUserMenu().findByText(`@${testUserOutsideOpenTeam.username}`);
// # Open direct message from the user in the open team (testUserInOpenTeam)
cy.visit(`/${secondTestTeam.name}/messages/@${testUserInOpenTeam.username}`);
// * Expect channel title to contain the username (ensures we opened the right DM)
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', `${testUserInOpenTeam.username}`);
// # Click on URL sent by the user in the open team
cy.findByTestId('postContent').
first().
find('a.theme.markdown__link').
should('have.attr', 'href', publicChannelUrl);
cy.visit(publicChannelUrl);
// * Expect URL to equal what was sent to the user outside the team
cy.url().should('equal', publicChannelUrl);
});
// * Expect the current team's display name to match the open team's display name
cy.uiGetLHSHeader().findByText(openTeam.display_name);
// * Expect channel title to match the display name of our previously created channel
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', publicChannelInOpenTeam.display_name);
});
});
const sendDirectMessageToUser = (user, team, message) => {
// # Open the the direct messages channel with the target user
cy.visit(`/${team.name}/messages/@${user.username}`);
// * Expect the channel title to be the user's username
// In the channel header, it seems there is a space after the username, justifying the use of contains.text instead of have.text
cy.get('#channelHeaderTitle').should('be.visible').and('contain.text', user.username);
// # Type message and send it to the user
cy.postMessage(message);
};

View File

@@ -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.
// ***************************************************************
// Group: @channels @multi_team_and_dm
describe('Multi-user group messages', () => {
let testUser;
let testTeam;
before(() => {
// # Create a new team
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
// # Add 10 users to the team
Cypress._.times(10, () => createUserAndAddToTeam(testTeam));
});
});
it('MM-T463 Should not be able to create a group message with more than 8 users', () => {
// # Login as test user
cy.apiLogin(testUser);
// # Go to town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open the 'Direct messages' dialog to create a new direct message
cy.uiAddDirectMessage().click();
// # Add the maximum amount of users to a group message (7)
addUsersToGMViaModal(7);
// * Check that the user count in the group message modal equals 7
cy.get('.react-select__multi-value').should('have.length', 7);
expectCannotAddUsersMessage(0);
// # Try to add one more user
addUsersToGMViaModal(1);
// * Check that the list of users in the multi-select is still 7
cy.get('.react-select__multi-value').should('have.length', 7);
// # Click on "Go" in the group message's dialog to begin the conversation
cy.get('#saveItems').click();
// * Check that the number of users in the group message is 8
cy.get('#channelMemberCountText').
should('be.visible').
and('have.text', '8');
// * Check that the direct message text input's placeholder is truncated if the user's list is too long
cy.get('[data-testid="post_textbox_placeholder"]').
should('be.visible').
and('have.css', 'overflow', 'hidden').
and('have.css', 'text-overflow', 'ellipsis');
// # From the group message's window, click on the user list's dropdown
cy.get('#channelHeaderTitle').click();
// # From the dropdown menu, click on "Add members"
cy.get('#channelMembers').click();
cy.uiGetButton('Add').click();
// # Try to add one more user from the group message's list
addUsersToGMViaModal(1);
// # Check that the appropriate information & warnings are displayed in the group message's dialog
expectCannotAddUsersMessage(0);
// * Check that the count of selected number of users in the group message's dialog is 7
cy.get('.react-select__multi-value').
should('be.visible').
and('have.length', 7);
// # Remove the last user from the group message's dialog input section
cy.get('.react-select__multi-value__remove').
should('be.visible').
and('have.length', 7).
last().
click();
// # Check that the appropriate information & warnings are displayed in the group message's dialog
expectCannotAddUsersMessage(1);
});
});
// Helper functions
const createUserAndAddToTeam = (team) => {
cy.apiCreateUser({prefix: 'gm'}).then(({user}) =>
cy.apiAddUserToTeam(team.id, user.id),
);
};
/**
* Add a given amount of numbers to a direct message group
* @param {number} userCountToAdd
*/
const addUsersToGMViaModal = (userCountToAdd) => {
// # Ensure there are enough selectable users in the list
cy.get('#multiSelectList').
should('be.visible').
children().
should('have.length.gte', userCountToAdd);
// # Add the first user from the top of the list, as many times as requested by 'userCountToAdd'
Cypress._.times(userCountToAdd, () => {
cy.get('#multiSelectList').
children().
first().
click();
});
};
/**
* In the "Direct messages" dialog, assert against help section that appropriate messages are displayed
* - The number of people that still can be added to the group message
* - The information message advising to start a private channel when the maximum number of users is reached (if applicable)
* @param {number} expectedUsersLeftToAdd
*/
const expectCannotAddUsersMessage = (expectedUsersLeftToAdd) => {
const maxUsersGMNote = "You've reached the maximum number of people for this conversation. Consider creating a private channel instead.";
// * Check that the help section indicates we cannot add anymore users
cy.get('#multiSelectHelpMemberInfo').
should('be.visible').
and('contain.text', `You can add ${expectedUsersLeftToAdd} more ${expectedUsersLeftToAdd === 1 ? 'person' : 'people'}`);
if (expectedUsersLeftToAdd === 0) {
// * Check that a note in the help section suggests creating a private channel instead
cy.get('#multiSelectMessageNote').should('contain.text', maxUsersGMNote);
}
};

View File

@@ -0,0 +1,59 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @multi_team_and_dm
import {DEFAULT_TEAM} from '@/support/constants';
const NUMBER_OF_TEAMS = 3;
describe('Multi-Team + DMs', () => {
before(() => {
// # Create an account
cy.apiCreateUser().its('user').as('user');
// # Delete all existing teams to clean-up
cy.apiGetAllTeams().then(({teams}) => {
teams.forEach((team) => {
if (team.name !== DEFAULT_TEAM.name) {
cy.apiDeleteTeam(team.id, true);
}
});
});
// # Create teams for user to join
for (let i = 0; i < NUMBER_OF_TEAMS; i++) {
cy.apiCreateTeam('team', 'Team', 'O', true, {allow_open_invite: true});
}
});
it('MM-T1805 No infinite loading spinner on Select Team page', function() {
// # Log-in with created user
cy.apiLogin(this.user);
// # Join all available teams and go to {servername}/select_team
joinAllTeams();
// * Check that no infinite loading spinner is shown
cy.get('.loading-screen').should('not.exist');
});
});
function joinAllTeams() {
cy.visit('/select_team');
cy.findByText('All team communication in one place, searchable and accessible anywhere');
cy.findAllByText(/Team\s/).then(([firstTeam, nextTeam]) => {
firstTeam.click();
if (nextTeam) {
joinAllTeams();
}
});
}

View File

@@ -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 @multi_team_and_dm
import * as TIMEOUTS from '@/fixtures/timeouts';
import * as MESSAGES from '@/fixtures/messages';
describe('Send a DM', () => {
let userA;
let userB;
let team1;
let testChannelUrl;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
userA = user;
team1 = team;
testChannelUrl = `/${team.name}/channels/town-square`;
cy.apiCreateUser().then(({user: otherUser}) => {
userB = otherUser;
cy.apiAddUserToTeam(team.id, userB.id);
});
});
});
it('MM-T451 Send a DM to someone on no team', () => {
// # Log in as UserA and leave all teams
cy.apiLogin(userA);
cy.visit(testChannelUrl);
cy.get('#postListContent', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible');
cy.uiGetLHSHeader().click();
cy.findByText('Leave team').click();
cy.findByText('Yes').click();
cy.url().should('include', '/select_team');
// # Log in as User B and send User A a direct message
cy.apiLogout();
cy.apiLogin(userB);
cy.visit(testChannelUrl);
cy.uiAddDirectMessage().click();
cy.get('#selectItems input').typeWithForce(userA.username).wait(TIMEOUTS.HALF_SEC);
cy.get('#multiSelectList').findByText(`@${userA.username}`).click();
cy.findByText('Go').click();
cy.postMessage(MESSAGES.SMALL);
// * From User B's perspective, message sends.
cy.uiWaitUntilMessagePostedIncludes(MESSAGES.SMALL);
// * The DM appears in your LHS / channel drawer even though other user isn't on any teams
cy.uiGetLhsSection('DIRECT MESSAGES').findByText(userA.username).should('be.visible');
// # Have User A re-join one of the teams
cy.apiAddUserToTeam(team1.id, userA.id);
// * After User A rejoins a team, the DM channel is visible to them in their LHS / channel drawer
cy.apiLogout();
cy.apiLogin(userA);
cy.visit(testChannelUrl);
cy.get('#postListContent', {timeout: TIMEOUTS.TWO_MIN}).should('be.visible');
cy.uiGetLhsSection('DIRECT MESSAGES').findByText(userB.username).should('be.visible');
});
});

View File

@@ -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 @multi_team_and_dm
describe('Multi Team and DM', () => {
let testChannel;
let testTeam;
let testUser;
let otherUser;
before(() => {
// # Setup with the new team, channel and user
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
// # Create otherUser
cy.apiCreateUser().then(({user: user2}) => {
otherUser = user2;
});
// # Login with testUser and visit test channel
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
});
it('MM-T439 Town Square is not marked as unread for existing users when a new user is added to the team', () => {
// # Open 'Advanced' section of 'Settings' modal
cy.uiOpenSettingsModal('Advanced').within(() => {
// # Open 'Enable Join/Leave Messages' and turn it off
cy.findByRole('heading', {name: 'Enable Join/Leave Messages'}).click();
cy.findByRole('radio', {name: 'Off'}).click();
// # Save and close the modal
cy.uiSaveAndClose();
});
// # Confirm Town Square is marked as read
cy.findByLabelText('town square public channel').should('be.visible');
// # Remove focus from Town Square
cy.findByLabelText('off-topic public channel').click();
// # Add second user to team in external session
cy.externalAddUserToTeam(otherUser.id, testTeam.id);
// * Assert that Town Square is still marked as read after second user added to team
cy.findByLabelText('town square public channel').should('be.visible');
// * Switch to different channel and assert that Town Square is still marked as read
cy.findByText(testChannel.display_name).click();
cy.findByLabelText('town square public channel').should('not.have.css', 'font-weight', '600');
});
});