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,284 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import {getRandomId} from '@/utils';
describe('Leave an archived channel', () => {
let testTeam: Team;
let offTopicUrl: string;
const channelType = {
all: 'Channel Type: All',
public: 'Channel Type: Public',
archived: 'Channel Type: Archived',
};
before(() => {
// # Login as test user and visit off-topic
cy.apiInitSetup({loginAfter: true}).then(({team, offTopicUrl: url}) => {
testTeam = team;
offTopicUrl = url;
cy.visit(offTopicUrl);
// # Post a message to the channel
cy.postMessage('hello');
});
});
it('MM-T1680 Open archived channel from search results with permalink view in another channel is open', () => {
// # Visit the test team
cy.visit(`/${testTeam.name}`);
// # Create a new channel
cy.uiCreateChannel({isNewSidebar: true});
// # Make a post
const archivedPostText = `archived${getRandomId()}`;
cy.postMessage(archivedPostText);
cy.getLastPostId().as('archivedPostId');
// # Archive the newly created channel
cy.uiArchiveChannel();
// # Switch away from the archived channel
cy.visit(offTopicUrl);
// # Make a post outside of the archived channel
const otherPostText = `post${getRandomId()}`;
cy.postMessage(otherPostText);
cy.getLastPostId().as('otherPostId');
// # Search for the new post and jump to it from the search results
cy.uiSearchPosts(otherPostText);
cy.get<string>('@otherPostId').then((otherPostId) => cy.uiJumpToSearchResult(otherPostId));
// # Search for a post in the archived channel
cy.uiSearchPosts(archivedPostText);
// # Open it in the RHS
cy.get<string>('@archivedPostId').then((archivedPostId) => {
cy.clickPostCommentIcon(archivedPostId, 'SEARCH');
// * Verify that the RHS has switched from search results to the thread
cy.get('#searchContainer').should('not.exist');
cy.get('#rhsContainer').should('be.visible');
// * Verify that the thread is visible and marked as archived
cy.get(`#rhsPost_${archivedPostId}`).should('be.visible');
cy.get('#rhsContainer .channel-archived-warning__container').should('be.visible');
cy.get('#rhsContainer .channel-archived-warning__content').should('be.visible');
});
});
it('MM-T1697 - Browse Public channels shows archived channels option', () => {
// # Create public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Click on browse channels
cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible');
// # Click on dropdown
cy.findByText(channelType.all).should('be.visible').click();
// # Click archived channels
cy.findByText('Archived channels').click();
// # Modal should contain created channel
cy.get('#moreChannelsList').should('contain', channel.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1699 - Browse Channels for all channel types shows archived channels option', () => {
let archivedPrivateChannel: Channel;
let archivedPublicChannel: Channel;
// # Create private channel
cy.uiCreateChannel({isPrivate: true, isNewSidebar: true}).as('channel').then((channel) => {
archivedPrivateChannel = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPrivateChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Create public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedPublicChannel = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPublicChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Click on browse channels from menu
cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible').then(() => {
// # All channel list opens by default
cy.findByText(channelType.all).should('be.visible').click();
// # Click on archived channels
cy.findByText('Archived channels').click();
// # Channel list should contain newly created channels
cy.get('#moreChannelsList').should('contain', archivedPrivateChannel.name);
cy.get('#moreChannelsList').should('contain', archivedPublicChannel.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1700 - All archived public channels are shown Important', () => {
let archivedPublicChannel1: Channel;
let archivedPublicChannel2: Channel;
// # Create public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedPublicChannel1 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPublicChannel1.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Create second public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedPublicChannel2 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPublicChannel2.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave channel
cy.uiLeaveChannel();
// # User should be redirected to last viewed channel
cy.url().should('include', `/${testTeam.name}/channels/${archivedPublicChannel1.name}`);
});
// # Click on browse channels from menu
cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible').then(() => {
// # All channels are shown by default
cy.findByText(channelType.all).should('be.visible').click();
// # Go to archived channels
cy.findByText('Archived channels').click();
// # Channel list should contain both archived public channels
cy.get('#moreChannelsList').should('contain', archivedPublicChannel1.display_name);
cy.get('#moreChannelsList').should('contain', archivedPublicChannel2.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1701 - Only Private channels you are a member of are displayed', () => {
let archivedPrivateChannel1: Channel;
let archivedPrivateChannel2: Channel;
// # Create private channel
cy.uiCreateChannel({isPrivate: true, isNewSidebar: true}).as('channel').then((channel) => {
archivedPrivateChannel1 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPrivateChannel1.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Create another private channel
cy.uiCreateChannel({isPrivate: true, isNewSidebar: true}).as('channel').then((channel) => {
archivedPrivateChannel2 = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedPrivateChannel2.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave the channel
cy.uiLeaveChannel();
});
// # Leave channel modal is visible
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModalButton').click();
// # Click on browse channels from menu
cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens
cy.get('#browseChannelsModal').should('be.visible').then(() => {
// # Show all channels is visible by default
cy.findByText(channelType.all).should('be.visible').click();
// # Go to archived channels
cy.findByText('Archived channels').click();
// # Channel list should contain only the private channel user is a member of
cy.get('#moreChannelsList').should('contain', archivedPrivateChannel1.name);
cy.get('#moreChannelsList').should('not.contain', archivedPrivateChannel2.name);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-T1703 - User can open archived channels', () => {
let archivedChannel: Channel;
// # Create a public channel
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
archivedChannel = channel;
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${archivedChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
});
// # Click on browse channels from menu
cy.uiBrowseOrCreateChannel('Browse channels');
// # More channels modal opens and lands on all channels
cy.get('#browseChannelsModal').should('be.visible').then(() => {
cy.findByText(channelType.all).should('be.visible').click();
// # Go to archived channels
cy.findByText('Archived channels').click();
// # More channels list should contain the archived channel
cy.get('#moreChannelsList').should('contain', archivedChannel.display_name);
});
cy.get('body').typeWithForce('{esc}');
});
});

View File

@@ -0,0 +1,112 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
import type {Channel, ServerChannel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import {getAdminAccount} from '@/support/env';
import {getRandomId} from '@/utils';
describe('Leave an archived channel', () => {
let testTeam: Team;
let testUser: UserProfile;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
beforeEach(() => {
// # Login as test user and visit off-topic
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/off-topic`);
});
it('MM-T1687 App does not crash when another user archives a channel', () => {
cy.makeClient({user: getAdminAccount()}).then(async (client) => {
// # Have another user create a private channel
const channelName = `channel${getRandomId()}`;
const channelTest = {
display_name: channelName,
name: channelName,
team_id: testTeam.id,
type: 'P',
} as Channel;
cy.wrap(client.createChannel(channelTest)).then(async (channel: unknown) => {
const ch = channel as ServerChannel;
// # Then invite us to it
await client.addToChannel(testUser.id, ch.id);
return ch;
}).then((channel) => {
// * Verify that the newly created channel is in the sidebar
cy.get(`#sidebarItem_${channel.name}`).should('be.visible');
cy.wrap(channel);
}).then(async (channel) => {
const ch = channel as ServerChannel;
// # Then archive the channel
await client.deleteChannel(ch.id);
// * Verify that the channel is no longer in the sidebar and that the app hasn't crashed
cy.get(`#sidebarItem_${ch.name}`).should('not.exist');
});
});
});
it('MM-T1688 archived channels only appear in search results as long as the user does not leave them', () => {
// # Create a new channel
cy.uiCreateChannel({isNewSidebar: true}).then(({name: channelName}) => {
// # Make a post
const archivedPostText = `archived${getRandomId()}`;
cy.postMessage(archivedPostText);
cy.getLastPostId().then((archivedPostId) => {
// # Archive the newly created channel
cy.uiArchiveChannel();
// # Switch away from the archived channel
cy.get('#sidebarItem_off-topic').click();
// * Verify that the channel is no longer in the sidebar
cy.get(`#sidebarItem_${channelName}`).should('not.exist');
// # Search for the post
cy.uiSearchPosts(archivedPostText);
// * Verify that the post is shown in the search results
cy.get(`#searchResult_${archivedPostId}`).should('be.visible');
// # Switch back to the archived channel through the permalink
cy.uiJumpToSearchResult(archivedPostId);
// * Wait for the permalink URL to disappear
cy.url().should('include', `/${testTeam.name}/channels/${channelName}`);
// # Leave the channel
cy.uiLeaveChannel();
cy.url().should('include', `/${testTeam.name}/channels/off-topic`);
cy.postMessage('hello');
// # Search for the post again
cy.uiSearchPosts(archivedPostText);
// * Verify that the post is no longer shown in the search results
cy.get(`#searchResult_${archivedPostId}`).should('not.exist');
cy.get('#search-items-container .no-results__wrapper').should('be.visible');
});
});
});
});

View File

@@ -0,0 +1,284 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import {createPrivateChannel} from '../enterprise/elasticsearch_autocomplete/helpers';
import * as TIMEOUTS from '@/fixtures/timeouts';
const channelType = {
all: 'Channel Type: All',
public: 'Channel Type: Public',
archived: 'Channel Type: Archived',
};
describe('Channels', () => {
let testUser: UserProfile;
let otherUser: UserProfile;
let testTeam: Team;
let testChannel: Channel;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
});
cy.apiLogin(testUser).then(() => {
// # Create new test channel
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
testChannel = channel;
});
// # Go to town square
cy.visit(`/${team.name}/channels/town-square`);
});
});
});
it('MM-19337 Verify UI of Browse channels modal with archived selection as admin', () => {
// * Verify browse channels modal as admin shows archived channels option
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible').within(() => {
cy.get('#menuWrapper').should('be.visible').and('have.text', channelType.all);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-19337 Verify UI of Browse channels modal with archived selection as regular user', () => {
// # Login as regular user and verify browse channels modal shows archived channels option
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible').within(() => {
cy.get('#menuWrapper').should('be.visible').and('have.text', channelType.all);
});
cy.get('body').typeWithForce('{esc}');
});
it('MM-19337 Enable users to view archived channels', () => {
// # Login as new user and go to "/"
cy.apiLogin(otherUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse channels'
cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible').within(() => {
// * Dropdown should be visible, defaulting to "All Channels"
cy.get('#menuWrapper').should('be.visible').and('contain', channelType.all).wait(TIMEOUTS.HALF_SEC);
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1).within(() => {
cy.findByText(testChannel.display_name).should('be.visible');
});
cy.get('#searchChannelsTextbox').clear();
// * Channel test should be visible as a public channel in the list
cy.get('#moreChannelsList').should('be.visible').within(() => {
// # Click to join the channel
cy.findByText(testChannel.display_name).scrollIntoView().should('be.visible').click();
});
});
// # Verify that the modal is not closed
cy.get('#browseChannelsModal').should('exist');
cy.url().should('include', `/${testTeam.name}/channels/${testChannel.name}`);
// # Login as channel admin and go directly to the channel
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click channel header to open channel menu
cy.get('#channelHeaderTitle').should('contain', testChannel.display_name).click();
// * Verify that the menu is opened
cy.get('#channelHeaderDropdownMenu').should('be.visible').within(() => {
// # Archive the channel
cy.findByText('Archive Channel').should('be.visible').click();
});
// * Verify that the delete/archive channel modal is opened
cy.get('#deleteChannelModal').should('be.visible').within(() => {
// # Confirm archive
cy.findByText('Archive').should('be.visible').click();
});
// # Go to LHS and click 'Browse channels'
cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible');
// # CLick dropdown to open selection
cy.get('#menuWrapper').should('be.visible').click();
// # Click on archived channels item
cy.findByText('Archived channels').should('be.visible').click();
// * Menu text should be updated to reflect the selection
cy.get('#menuWrapper').should('contain', channelType.archived);
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
cy.get('#moreChannelsList').children().should('have.length', 1).within(() => {
cy.findByText(testChannel.display_name).should('be.visible');
});
cy.get('#searchChannelsTextbox').clear();
// * Test channel should be visible as a archived channel in the list
cy.get('#moreChannelsList').should('be.visible').within(() => {
// # Click to view archived channel
cy.findByText(testChannel.display_name).scrollIntoView().should('be.visible').click();
});
// * Assert that channel is archived and new messages can't be posted.
cy.get('#channelArchivedMessage').should('contain', 'You are viewing an archived channel. New messages cannot be posted.');
cy.uiGetPostTextBox({exist: false});
// # Switch to another channel
cy.get('#sidebarItem_town-square').click();
// * Assert that archived channel doesn't show up in LHS list
cy.get('#sidebar-left').should('not.contain', testChannel.display_name);
});
it('MM-19337 Increase channel member count when user joins a channel', () => {
// # Login as new user and go to "/"
cy.apiLogin(otherUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
let newChannel: Channel;
cy.apiCreateChannel(testTeam.id, 'channel-to-leave', 'Channel to leave').then(({channel}) => {
newChannel = channel;
cy.visit(`/${testTeam.name}/channels/${newChannel.name}`);
// # Leave the channel
cy.uiLeaveChannel();
// * Verify that we've switched to Town Square
cy.url().should('include', '/channels/town-square');
});
// # Go to LHS and click 'Browse channels'
cy.uiBrowseOrCreateChannel('Browse channels');
cy.get('#browseChannelsModal').should('be.visible').within(() => {
// * Verify that channel has zero members
cy.findByTestId(`channelMemberCount-${newChannel.name}`).should('be.visible').and('contain', 0);
// # Click the channel row to join the channel
cy.findByTestId(`ChannelRow-${newChannel.name}`).scrollIntoView().should('be.visible').click();
// * Verify that channel has one member
cy.findByTestId(`channelMemberCount-${newChannel.name}`).should('be.visible').and('contain', 1);
});
});
it('MM-T1702 Search works when changing public/all options in the dropdown', () => {
cy.apiAdminLogin();
let newChannel: Channel;
let testArchivedChannel: Channel;
let testPrivateArchivedChannel: Channel;
cy.apiCreateTeam('team', 'Test NoMember').then(({team}) => {
cy.apiCreateChannel(team.id, 'not-archived-channel', 'Not Archived Channel').then(({channel}) => {
newChannel = channel;
cy.visit(`/${team.name}/channels/${newChannel.name}`);
// # Leave the channel
cy.uiLeaveChannel();
// * Verify that we've switched to Town Square
cy.url().should('include', '/channels/town-square');
});
cy.apiCreateChannel(team.id, 'archived-channel', 'Archived Channel').then(({channel}) => {
testArchivedChannel = channel;
// # Visit the channel
cy.visit(`/${team.name}/channels/${testArchivedChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
// # Leave the channel
cy.uiLeaveChannel();
// * Verify that we've switched to Town Square
cy.url().should('include', '/channels/town-square');
});
createPrivateChannel(team.id).then((channel) => {
testPrivateArchivedChannel = channel;
// # Visit the channel
cy.visit(`/${team.name}/channels/${testPrivateArchivedChannel.name}`);
// # Archive the channel
cy.uiArchiveChannel();
cy.visit(`/${team.name}/channels/town-square`);
});
});
// # Go to LHS and click 'Browse channels'
cy.uiBrowseOrCreateChannel('Browse channels');
// * Dropdown should be visible, defaulting to "All channels"
cy.get('#menuWrapper').should('be.visible').within((el) => {
cy.wrap(el).should('contain', channelType.all);
});
// * Hide Archived is on by default, so search only returns active matches
cy.get('#hideArchivedPreferenceCheckbox').should('be.visible').and('have.attr', 'aria-checked', 'true');
cy.get('#searchChannelsTextbox').should('be.visible').type('iv').wait(TIMEOUTS.HALF_SEC);
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1);
cy.get('#moreChannelsList').should('be.visible').within(() => {
cy.findByText(newChannel.display_name).should('be.visible');
cy.findByText(testArchivedChannel.display_name).should('not.exist');
});
// # Uncheck Hide Archived so archived matches appear in All search results
cy.get('#hideArchivedPreferenceCheckbox').click();
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 2);
cy.get('#moreChannelsList').should('be.visible').within(() => {
cy.findByText(newChannel.display_name).should('be.visible');
cy.findByText(testArchivedChannel.display_name).should('be.visible');
});
cy.get('#browseChannelsModal').should('be.visible');
// * Users should be able to switch to "Archived Channels" list
cy.get('#menuWrapper').should('be.visible').and('contain', channelType.all).click().wait(TIMEOUTS.HALF_SEC);
// # Click on archived channels item
cy.findByText('Archived channels').should('be.visible').click();
// * Modal menu should be updated accordingly
cy.get('#menuWrapper').should('contain', channelType.archived);
cy.get('#searchChannelsTextbox').clear();
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 2);
cy.get('#moreChannelsList').within(() => {
cy.findByText(testArchivedChannel.display_name).should('be.visible');
cy.findByText(testPrivateArchivedChannel.display_name).should('be.visible');
});
});
});

View File

@@ -0,0 +1,128 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
function verifyNoChannelToJoinMessage(isVisible: boolean) {
cy.findByText('No public channels').should(isVisible ? 'be.visible' : 'not.exist');
}
function ensureHideJoinedCheckboxEnabled(shouldBeChecked: boolean) {
cy.get('#hideJoinedPreferenceCheckbox').then(($checkbox) => {
cy.wrap($checkbox).findByText('Hide Joined').should('be.visible');
cy.wrap($checkbox).find('div.get-app__checkbox').invoke('attr', 'class').then(($classList) => {
if ($classList!.split(' ').includes('checked') !== Boolean(shouldBeChecked)) {
// We click on the button only when the XOR operands do not match
// e.g. checkbox is checked, but should not be checked; and vice-versa
cy.wrap($checkbox).click();
cy.wrap($checkbox).find('div.get-app__checkbox').should(`${shouldBeChecked ? '' : 'not.'}have.class`, 'checked');
}
});
});
}
describe('browse public channels', () => {
let testUser: UserProfile;
let otherUser: UserProfile;
let testTeam: Team;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser().then(({user: user1}) => {
otherUser = user1;
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
});
cy.apiLogin(testUser).then(() => {
// # create 30 channels
for (let i = 0; i < 30; i++) {
cy.apiCreateChannel(testTeam.id, 'public-channel', 'public-channel');
}
// # Go to town square
cy.visit(`/${team.name}/channels/town-square`);
});
});
});
it('MM-T1664 Channels do not disappear from Browse Channels modal', () => {
// # Login as other user
cy.apiLogin(otherUser);
// # Go to town square
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse channels'
cy.uiBrowseOrCreateChannel('Browse channels');
// * Assert that the browse channel modal is visible
cy.findByRole('dialog', {name: 'Browse Channels'}).should('be.visible').then(() => {
// # Click on dropdown
cy.findByText('Channel Type: All').should('be.visible').click();
// # Click archived channels
cy.findByText('Public channels').should('be.visible').click();
// # Click hide joined checkbox if not already checked
ensureHideJoinedCheckboxEnabled(true);
// * Assert that the moreChannelsList is visible and the number of channels is 31
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 31);
// # scroll to the middle channel
cy.get('#moreChannelsList .more-modal__row').eq(15).scrollIntoView();
// * Assert that the "No more channels to join" message does not exist
verifyNoChannelToJoinMessage(false);
// # scroll to the last channel
cy.get('#moreChannelsList .more-modal__row').last().scrollIntoView();
// * Assert that the "No more channels to join" message does not exist
verifyNoChannelToJoinMessage(false);
// # scroll to the first channel
cy.get('#moreChannelsList .more-modal__row').first().scrollIntoView();
// * Assert that the "No more channels to join" message does not exist
verifyNoChannelToJoinMessage(false);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Go to town square
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Go to LHS and click 'Browse channels'
cy.uiBrowseOrCreateChannel('Browse channels');
// * Assert the moreChannelsModel is visible
cy.findByRole('dialog', {name: 'Browse Channels'}).should('be.visible').then(() => {
// # Click on dropdown
cy.findByText('Channel Type: All').should('be.visible').click();
// # Click archived channels
cy.findByText('Public channels').should('be.visible').click();
// # Click hide joined checkbox if not already checked
ensureHideJoinedCheckboxEnabled(true);
// * Assert that the "No more channels to join" message is visible
verifyNoChannelToJoinMessage(true);
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel @channel_settings
describe('Channel Settings - Channel Header', () => {
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
cy.apiLogin(user);
// # Visit town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('Create channel, modify header, and verify preview button functionality', () => {
// # Create a new channel
cy.apiCreateChannel(testTeam.id, 'new-channel', 'New Channel').then(({channel}) => {
// # Visit the newly created channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Select the "Edit Channel Header" option from the dropdown
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// * Verify modal is open
cy.get('.ChannelSettingsModal').should('be.visible');
cy.get('#genericModalLabel').should('contain', 'Channel Settings');
// # Type something in the header edit box
cy.get('#channel_settings_header_textbox').clear().type('This is the new header content');
// * Verify the "Preview" button exists and is not active
cy.get('#channel_settings_header_textbox').
parents('.AdvancedTextbox').
find('#PreviewInputTextButton').
should('not.have.class', 'active');
// * Verify that before hitting the preview button, the style on the textbox is `display: block`
cy.get('#channel_settings_header_textbox').should('have.css', 'display', 'block');
// # Click the "Preview" button
cy.get('#channel_settings_header_textbox').
parents('.AdvancedTextbox').
find('#PreviewInputTextButton').click();
// * Verify that the display is now none on the textbox element
cy.get('#channel_settings_header_textbox').should('have.css', 'display', 'none');
// * Verify the "Preview" button has class active
cy.get('#channel_settings_header_textbox').
parents('.AdvancedTextbox').
find('#PreviewInputTextButton').
should('have.class', 'active');
});
});
});

View File

@@ -0,0 +1,541 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel @rhs @channel_info
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import {stubClipboard} from '@/utils';
describe('Channel Info RHS', () => {
let testTeam: Team;
let testChannel: Channel;
let groupChannel: Channel;
let directChannel: Channel;
let directUser: UserProfile;
let admin: UserProfile;
let user: UserProfile;
const otherUsers: UserProfile[] = [];
before(() => {
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, user: newAdmin}) => {
testTeam = team;
admin = newAdmin;
cy.apiCreateChannel(testTeam.id, 'channel', 'Public Channel', 'O').then(({channel}) => {
testChannel = channel;
cy.apiAddUserToChannel(channel.id, newAdmin.id);
});
// User we'll use for our permissions tests
cy.apiCreateUser().then(({user: newUser}) => {
cy.apiAddUserToTeam(team.id, newUser.id);
user = newUser;
});
// Users used for GM/DM
cy.apiCreateUser().then(({user: newUser}) => {
otherUsers.push(newUser);
cy.apiPatchUser(newUser.id, {position: 'Upside down'} as UserProfile).then(({user: patchedUser}) => {
cy.apiCreateDirectChannel([newAdmin.id, newUser.id]).then(({channel}) => {
directChannel = channel;
});
directUser = patchedUser;
});
cy.apiAddUserToTeam(team.id, newUser.id);
cy.apiCreateUser().then(({user: newUser2}) => {
otherUsers.push(newUser2);
cy.apiAddUserToTeam(team.id, newUser.id);
cy.apiCreateGroupChannel([newAdmin.id, newUser.id, newUser2.id]).then(({channel}) => {
groupChannel = channel;
});
});
});
// # Change permission so that regular users can't change channels or add members
cy.apiGetRolesByNames(['channel_user']).then(({roles}) => {
const role = roles[0];
const permissions = role.permissions.filter((permission) => {
return !(['manage_public_channel_members', 'manage_private_channel_members', 'manage_public_channel_properties', 'manage_private_channel_properties'].includes(permission));
});
if (permissions.length !== role.permissions.length) {
cy.apiPatchRole(role.id, {permissions});
}
});
cy.apiLogin(admin);
});
});
it('should be able to open the RHS', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * RHS Container shoud exist
ensureRHSIsOpenOnChannelInfo(testChannel);
});
describe('regular channel', () => {
describe('top buttons', () => {
it('should show correct tooltips for all buttons', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify tooltips appear with correct text
cy.uiGetRHS().findByText('Favorite').trigger('mouseenter');
cy.findByText('Add this channel to favorites').should('be.visible');
cy.uiGetRHS().findByText('Favorite').trigger('mouseleave');
cy.uiGetRHS().findByText('Mute').trigger('mouseenter');
cy.findByText('Mute notifications for this channel').should('be.visible');
cy.uiGetRHS().findByText('Mute').trigger('mouseleave');
cy.uiGetRHS().findByText('Add People').trigger('mouseenter');
cy.findByText('Add team members to this channel').should('be.visible');
cy.uiGetRHS().findByText('Add People').trigger('mouseleave');
cy.uiGetRHS().findByText('Copy Link').trigger('mouseenter');
cy.findByText('Copy link to this channel').should('be.visible');
cy.uiGetRHS().findByText('Copy Link').trigger('mouseleave');
});
it('should be able to toggle favorite on a channel', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the favorite status
cy.uiGetRHS().findByText('Favorite').should('be.visible').click();
cy.uiGetRHS().findByText('Favorited').should('be.visible').click();
cy.uiGetRHS().findByText('Favorite').should('be.visible');
});
it('should be able to toggle mute on a channel', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the mute status
cy.uiGetRHS().findByText('Mute').should('be.visible').click();
cy.uiGetRHS().findByText('Muted').should('be.visible').click();
cy.uiGetRHS().findByText('Mute').should('be.visible');
});
it('should be able to add people', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('be.visible').click();
cy.get('.channel-invite').should('be.visible');
});
it('should NOT be able to add people without permission', () => {
// # Login as simple user
cy.apiLogout().then(() => {
cy.apiLogin(user);
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('not.exist');
// # log back in as admin
cy.apiLogout().then(() => {
cy.apiLogin(admin);
});
});
});
it('should be able to copy link', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
stubClipboard().as('clipboard');
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify initial state
cy.get('@clipboard').its('contents').should('eq', '');
// # Click on "Copy Link"
cy.uiGetRHS().findByText('Copy Link').parent().should('be.visible').trigger('click');
// * Text should change to Copied
cy.uiGetRHS().findByText('Copied').should('be.visible');
// * Verify if it's called with correct link value
cy.get('@clipboard').its('contents').should('eq', `${Cypress.config('baseUrl')}/${testTeam.name}/channels/${testChannel.name}`);
});
});
describe('about area', () => {
it('should display purpose', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(testChannel.id, {
purpose: 'purpose for the tests',
}).then(() => {
cy.uiGetRHS().findByText('purpose for the tests').should('be.visible');
});
});
it('should display header', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(testChannel.id, {
header: 'header for the tests',
}).then(() => {
cy.uiGetRHS().findByText('header for the tests').should('be.visible');
});
});
it('should be able to rename channel from About area', () => {
// # Create a dedicated channel for renaming to avoid affecting other tests
cy.apiCreateChannel(testTeam.id, 'channel-to-rename', 'Channel To Rename', 'O').then(({channel}) => {
cy.apiAddUserToChannel(channel.id, admin.id);
// # Go to the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open Channel Info RHS
cy.get('#channel-info-btn').click();
// # Click edit on channel name (first Edit in About)
cy.uiGetRHS().findAllByLabelText('Edit').first().click({force: true});
// * Rename Channel modal appears
cy.findByRole('heading', {name: /rename channel/i}).should('be.visible');
// # Fill display name and URL
cy.findByPlaceholderText(/enter display name/i).clear().type('Renamed Channel');
cy.get('.url-input-button').click();
cy.get('.url-input-container input').clear().type('renamed-channel');
cy.get('.url-input-container button.url-input-button').click();
// # Save
cy.findByRole('button', {name: /save/i}).click();
// * URL updated
cy.location('pathname').should('include', `/${testTeam.name}/channels/renamed-channel`);
// * Header shows new name
cy.get('#channelHeaderTitle').should('contain', 'Renamed Channel');
});
});
});
describe('bottom menu', () => {
it('should be able to manage notifications', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Notification Preferences"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Notification Preferences').scrollIntoView().should('be.visible').click();
// * Ensures the modal is there
cy.get('.ChannelNotificationModal').should('be.visible');
});
it('should open Channel Settings from RHS menu', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Close RHS if it's open, then click on the channel info button
cy.get('body').then(($body) => {
if ($body.find('#rhsCloseButton').length > 0) {
cy.get('#rhsCloseButton').click();
}
cy.get('#channel-info-btn').should('be.visible').click();
});
// * Channel Settings item is visible in RHS menu
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Channel Settings').scrollIntoView().should('be.visible').click();
// * Channel Settings modal opens
cy.get('.ChannelSettingsModal').should('be.visible');
});
it('should be able to view files and come back', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Files"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Files').scrollIntoView().should('be.visible').click();
// * Ensure we see the files RHS
cy.uiGetRHS().findByText('No files yet').should('be.visible');
// # Click the Back Icon
cy.uiGetRHS().get('[aria-label="Back Icon"]').click();
// * Make sure we are back in the channel info rhs
ensureRHSIsOpenOnChannelInfo(testChannel);
});
it('should be able to view pinned message and come back', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Go to test channel
cy.uiPostMessageQuickly('Hello channel info rhs spec').then(() => {
cy.getNthPostId(-1).then((postId) => {
cy.clickPostDotMenu(postId);
cy.get(`#pin_post_${postId}`).click();
});
});
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Pinned Messages"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Pinned messages').scrollIntoView().should('be.visible').click();
// * Ensure we see the Pinned Post RHS
cy.uiGetRHS().findByText('Hello channel info rhs spec').first().should('be.visible');
// # Click the Back Icon
cy.uiGetRHS().get('[aria-label="Back Icon"]').click();
// * Make sure we are back in the channel info rhs
ensureRHSIsOpenOnChannelInfo(testChannel);
});
it('should be able to view channel members and come back', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Members"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Members').scrollIntoView().should('be.visible').click();
// * Ensure we see the members
cy.uiGetRHS().contains('sysadmin').should('be.visible');
cy.uiGetRHS().contains(`${admin.username}`).should('be.visible');
// # Click the Back Icon
cy.uiGetRHS().get('[aria-label="Back Icon"]').click();
// * Make sure we are back in the channel info rhs
ensureRHSIsOpenOnChannelInfo(testChannel);
});
});
});
describe('group channel', () => {
describe('top buttons', () => {
it('should be able to toggle favorite', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the favorite status
cy.uiGetRHS().findByText('Favorite').should('be.visible').click();
cy.uiGetRHS().findByText('Favorited').should('be.visible').click();
cy.uiGetRHS().findByText('Favorite').should('be.visible');
});
it('should be able to toggle mute', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the mute status
cy.uiGetRHS().findByText('Mute').should('be.visible').click();
cy.uiGetRHS().findByText('Muted').should('be.visible').click();
cy.uiGetRHS().findByText('Mute').should('be.visible');
});
it('should be able to add people', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('be.visible').click();
cy.get('.more-direct-channels').should('be.visible');
});
it('should NOT be able to copy link', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Copy Link"
cy.uiGetRHS().get('Copy Link').should('not.exist');
});
});
describe('about area', () => {
it('should display other users', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
otherUsers.forEach((otherUser) => {
cy.uiGetRHS().contains(otherUser.username);
});
});
it('should display header', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(groupChannel.id, {
header: 'header for the tests',
}).then(() => {
cy.uiGetRHS().findByText('header for the tests').should('be.visible');
});
});
});
describe('bottom menu', () => {
it('should be able to manage notifications', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/${groupChannel.name}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Notification Preferences"
cy.uiGetRHS().findByTestId('channel_info_rhs-menu').findByText('Notification Preferences').scrollIntoView().should('be.visible').click();
// * Ensures the modal is there
cy.get('.ChannelNotificationModal').should('be.visible');
});
});
});
describe('direct channel', () => {
describe('top buttons', () => {
it('should be able to toggle favorite', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the favorite status
cy.uiGetRHS().findByText('Favorite').should('be.visible').click();
cy.uiGetRHS().findByText('Favorited').should('be.visible').click();
cy.uiGetRHS().findByText('Favorite').should('be.visible');
});
it('should be able to toggle mute', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that we can toggle the mute status
cy.uiGetRHS().findByText('Mute').should('be.visible').click();
cy.uiGetRHS().findByText('Muted').should('be.visible').click();
cy.uiGetRHS().findByText('Mute').should('be.visible');
});
it('should NOT be able to add people', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// * Verify that the modal appears
cy.uiGetRHS().findByText('Add People').should('not.exist');
});
it('should NOT be able to copy link', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
// # Click on "Copy Link"
cy.uiGetRHS().get('Copy Link').should('not.exist');
});
});
describe('about area', () => {
it('should display other user name and position', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.uiGetRHS().contains(directUser.username);
cy.uiGetRHS().contains(directUser.position);
});
it('should display header', () => {
// # Go to test channel
cy.visit(`/${testTeam.name}/messages/@${directUser.username}`);
// # Click on the channel info button
cy.get('#channel-info-btn').click();
cy.apiPatchChannel(directChannel.id, {
header: 'header for the tests',
}).then(() => {
cy.uiGetRHS().findByText('header for the tests').should('be.visible');
});
});
});
});
});
function ensureRHSIsOpenOnChannelInfo(testChannel: Cypress.Channel) {
cy.get('#rhsContainer').then((rhsContainer) => {
cy.wrap(rhsContainer).findByText('Info').should('be.visible');
cy.wrap(rhsContainer).find('.sidebar--right__title__subtitle').should('contain', testChannel.display_name);
});
}

View File

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

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Channel', () => {
let testTeam: Team;
let ownChannel: Channel;
let otherChannel: Channel;
let testUser: UserProfile;
let offTopicUrl: string;
const myChannelsDividerText = 'My Channels';
const otherChannelsDividerText = 'Other Channels';
before(() => {
// # Login as new user and visit off-topic
cy.apiInitSetup().then(({team, channel, user, offTopicUrl: url}) => {
testTeam = team;
ownChannel = channel;
testUser = user;
offTopicUrl = url;
cy.apiCreateChannel(testTeam.id, 'delta-test', 'Delta Channel').then((out) => {
otherChannel = out.channel;
});
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
it('Channel autocomplete should have both lists populated correctly', () => {
// # Type "~"
cy.uiGetPostTextBox().clear().type('~').wait(TIMEOUTS.HALF_SEC);
cy.get('#loadingSpinner').should('not.exist');
// * Should open up suggestion list for channels
// * Should match each channel item
cy.get('#suggestionList').should('be.visible').children().as('suggestionList');
// * Should render "MY CHANNELS" suggestion list group
cy.get('@suggestionList').get('[role="presentation"]').contains(myChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').get('[role="option"]').should('contain', ownChannel.display_name);
cy.get('@suggestionList').get('[role="option"]').should('contain', 'Off-Topic');
cy.get('@suggestionList').get('[role="option"]').should('contain', 'Town Square');
// * Should render "OTHER CHANNELS" suggestion list group
cy.get('@suggestionList').get('[role="presentation"]').contains(otherChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').get('[role="option"]').should('contain', otherChannel.display_name);
});
it('Joining a channel should alter channel mention autocomplete lists accordingly', () => {
// # Join a channel by /join slash command
cy.uiGetPostTextBox().clear().wait(TIMEOUTS.HALF_SEC).type(`/join ~${otherChannel.name}`).type('{enter}').wait(TIMEOUTS.HALF_SEC);
// * Verify that it redirects into the channel
cy.url().should('include', `/${testTeam.name}/channels/${otherChannel.name}`);
// # Type "~"
cy.uiGetPostTextBox().type('~').wait(TIMEOUTS.HALF_SEC);
cy.get('#loadingSpinner').should('not.exist');
// * Should open up suggestion list for channels
// * Should match each channel
cy.get('#suggestionList').should('be.visible').children().as('suggestionList');
// * Should render "MY CHANNELS" suggestion list group
cy.get('@suggestionList').get('[role="presentation"]').contains(myChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').get('[role="option"]').should('contain', ownChannel.display_name);
cy.get('@suggestionList').get('[role="option"]').should('contain', otherChannel.display_name);
cy.get('@suggestionList').get('[role="option"]').should('contain', 'Off-Topic');
cy.get('@suggestionList').get('[role="option"]').should('contain', 'Town Square');
});
it('Getting removed from a channel should alter channel mention autocomplete lists accordingly', () => {
// # Remove test user from the test channel
cy.apiAdminLogin();
cy.removeUserFromChannel(otherChannel.id, testUser.id).then((res) => {
expect(res.status).to.equal(200);
// # Login as test user and visit the test team
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
// # Type "~"
cy.uiGetPostTextBox().clear().type('~').wait(TIMEOUTS.HALF_SEC);
cy.get('#loadingSpinner').should('not.exist');
// * Should open up suggestion list for channels
// * Should match each channel item
cy.get('#suggestionList').should('be.visible').children().as('suggestionList');
// * Should render "MY CHANNELS" suggestion list group
cy.get('@suggestionList').get('[role="presentation"]').contains(myChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').get('[role="option"]').should('contain', ownChannel.display_name);
cy.get('@suggestionList').get('[role="option"]').should('contain', 'Off-Topic');
cy.get('@suggestionList').get('[role="option"]').should('contain', 'Town Square');
// * Should render "OTHER CHANNELS" suggestion list group
cy.get('@suggestionList').get('[role="presentation"]').contains(otherChannelsDividerText, {matchCase: false});
cy.get('@suggestionList').get('[role="option"]').should('contain', otherChannel.display_name);
});
});
});

View File

@@ -0,0 +1,132 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel @not_cloud
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import * as TIMEOUTS from '@/fixtures/timeouts';
const timestamp = Date.now();
describe('channel name tooltips', () => {
let loggedUser: UserProfile;
let longUser: UserProfile;
let testTeam: Team;
before(() => {
cy.shouldNotRunOnCloudEdition();
// # Login as new user and visit off-topic
cy.apiInitSetup().then(({team, user, offTopicUrl}) => {
testTeam = team;
loggedUser = user;
cy.apiCreateUser({prefix: `thisIsALongUsername${timestamp}`}).then(({user: user1}) => {
longUser = user1;
cy.apiAddUserToTeam(testTeam.id, loggedUser.id);
});
cy.apiLogin(loggedUser);
cy.visit(offTopicUrl);
});
});
it('Should show tooltip on hover - open/public channel with long name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
`Public channel with a long name-${timestamp}`,
).then(({channel}) => {
verifyChannel(channel);
});
});
it('Should show tooltip on hover - private channel with long name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
`Private channel with a long name-${timestamp}`,
'P',
).then(({channel}) => {
verifyChannel(channel);
});
});
it('Should not show tooltip on hover - open/public channel with short name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
'Public channel',
).then(({channel}) => {
verifyChannel(channel, false);
});
});
it('Should not show tooltip on hover - private channel with short name', () => {
// # Create new test channel
cy.apiCreateChannel(
testTeam.id,
'channel-test',
'Private channel',
'P',
).then(({channel}) => {
verifyChannel(channel, false);
});
});
it('Should show tooltip on hover - user with a long username', () => {
// # Open a DM with the user
cy.findByRole('button', {name: 'Write a direct message'}).click();
cy.get('.more-direct-channels #selectItems input').as('searchBox').typeWithForce(longUser.username);
// * Verify that the user is selected in the results list before typing enter
cy.get('div.more-modal__row').
should('have.length', 1).
and('have.class', 'clickable').
and('have.class', 'more-modal__row--selected').
and('contain.text', longUser.username.toLowerCase());
cy.get('@searchBox').typeWithForce('{enter}');
cy.uiGetButton('Go').click();
// # Hover on the channel name
cy.get(`#sidebarItem_${Cypress._.sortBy([loggedUser.id, longUser.id]).join('__')}`).scrollIntoView().should('be.visible').trigger('mouseenter');
// * Verify that the tooltip is displayed
cy.findByRole('tooltip').should('be.visible');
// # Move cursor away from channel
cy.get(`#sidebarItem_${Cypress._.sortBy([loggedUser.id, longUser.id]).join('__')}`).scrollIntoView().should('be.visible').trigger('mouseleave');
});
});
function verifyChannel(channel: Channel, verifyExistence = true) {
// # Wait for Channel to be created
cy.wait(TIMEOUTS.HALF_SEC);
// # Hover on the channel name
cy.get(`#sidebarItem_${channel.name}`).should('be.visible').trigger('mouseenter');
// * Verify that the tooltip is displayed
if (verifyExistence) {
cy.findByRole('tooltip').should('be.visible').and('have.text', channel.display_name);
} else {
cy.findByRole('tooltip').should('not.exist');
}
// # Move cursor away from channel
cy.get(`#sidebarItem_${channel.name}`).should('be.visible').trigger('mouseleave');
}

View File

@@ -0,0 +1,141 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
describe('Channel routing', () => {
let testUser: UserProfile;
let otherUser1: UserProfile;
let otherUser2: UserProfile;
let testTeam: Team;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser({prefix: 'otherA'}).then(({user: newUser}) => {
otherUser1 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
cy.apiCreateUser({prefix: 'otherB'}).then(({user: newUser}) => {
otherUser2 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('should go to town square channel view', () => {
// * Check if the channel is loaded correctly
cy.get('#channelHeaderTitle').should('be.visible').should('contain', 'Town Square');
});
it('should go to private channel view', () => {
// # Create a private channel
cy.apiCreateChannel(testTeam.id, 'private-channel', 'Private channel', 'P').then(({channel}) => {
// # Go to the newly created channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', 'Private channel');
// # Remove the created channel
cy.apiDeleteChannel(channel.id);
});
});
it('should go to self direct channel using the multiple ways to go', () => {
// # Create a self direct channel
cy.apiCreateDirectChannel([testUser.id, testUser.id]).then(({channel: ownDMChannel}) => {
// # Visit the channel using the channel name
cy.visit(`/${testTeam.name}/channels/${testUser.id}__${testUser.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
// # Visit the channel using the channel id
cy.visit(`/${testTeam.name}/channels/${ownDMChannel.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
// # Visit the channel using the username
cy.visit(`/${testTeam.name}/messages/@${testUser.username}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
// # Visit the channel using the user email
cy.visit(`/${testTeam.name}/messages/${testUser.email}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', `${testUser.username} (you)`);
});
});
it('should go to other user direct channel using multiple ways to go', () => {
// # Create a direct channel between two users
cy.apiCreateDirectChannel([testUser.id, otherUser1.id]).then(({channel: dmChannel}) => {
// # Visit the channel using the channel name
cy.visit(`/${testTeam.name}/channels/${testUser.id}__${otherUser1.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
// # Visit the channel using the channel id
cy.visit(`/${testTeam.name}/channels/${dmChannel.id}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
// # Visit the channel using the target username
cy.visit(`/${testTeam.name}/messages/@${otherUser1.username}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
// # Visit the channel using the target user email
cy.visit(`/${testTeam.name}/messages/${otherUser1.email}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', otherUser1.username);
});
});
it('should go group channel using group id', () => {
const userGroupIds = [testUser.id, otherUser1.id, otherUser2.id];
// # Create a group channel for 3 users
cy.apiCreateGroupChannel(userGroupIds).then(({channel: gmChannel}) => {
// # Visit the channel using the name using the channels route
cy.visit(`/${testTeam.name}/channels/${gmChannel.name}`);
// * Check you can go to the channel without problem
const displayName = gmChannel.display_name.split(', ').filter(((username) => username !== testUser.username)).join(', ');
cy.get('#channelHeaderTitle').should('be.visible').should('contain', displayName);
// # Visit the channel using the name using the messages route
cy.visit(`/${testTeam.name}/messages/${gmChannel.name}`);
// * Check you can go to the channel without problem
cy.get('#channelHeaderTitle').should('be.visible').should('contain', displayName);
});
});
});

View File

@@ -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 @channel @channel_settings @permissions
import type {Team} from '@mattermost/types/teams';
import type {Channel} from '@mattermost/types/channels';
import type {UserProfile} from '@mattermost/types/users';
describe('Channel Settings Menu Permissions', () => {
let testTeam: Team;
let testChannel: Channel;
let admin: UserProfile;
let user: UserProfile;
before(() => {
// # Create a test team, channel, and admin user
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, user: newAdmin, channel}) => {
testTeam = team;
admin = newAdmin;
testChannel = channel;
// # Create a regular user
cy.apiCreateUser().then(({user: newUser}) => {
user = newUser;
cy.apiAddUserToTeam(team.id, newUser.id).then(() => {
cy.apiAddUserToChannel(channel.id, newUser.id);
});
});
// # Change permission so that regular users can't access channel settings
cy.apiGetRolesByNames(['channel_user']).then(({roles}) => {
const role = roles[0];
const permissions = role.permissions.filter((permission) => {
return !(['manage_public_channel_properties', 'manage_private_channel_properties', 'manage_public_channel_banner', 'manage_private_channel_banner', 'delete_public_channel', 'delete_private_channel'].includes(permission));
});
if (permissions.length !== role.permissions.length) {
cy.apiPatchRole(role.id, {permissions});
}
});
cy.apiLogin(admin);
});
});
it('MM-T1001: Channel Settings menu is visible for users with permissions', () => {
// # Login as the admin user
cy.apiLogin(admin);
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Open channel header menu
cy.get('#channelHeaderDropdownButton').click();
// * Verify Channel Settings option is visible
cy.findByText('Channel Settings').should('be.visible');
});
it('MM-T1002: Channel Settings menu is hidden for users without permissions', () => {
// # Login as the regular user
cy.apiLogin(user);
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Open channel header menu
cy.get('#channelHeaderDropdownButton').click();
// * Verify Channel Settings option is not visible
cy.findByText('Channel Settings').should('not.exist');
});
});

View File

@@ -0,0 +1,460 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel @channel_settings
import type {Team} from '@mattermost/types/teams';
import type {Channel} from '@mattermost/types/channels';
import type {UserProfile} from '@mattermost/types/users';
describe('Channel Settings Modal', () => {
let testTeam: Team;
let testUser: UserProfile;
let testChannel: Channel;
let originalTestChannel: Channel;
before(() => {
// Setup test data
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create a test channel
cy.apiCreateChannel(testTeam.id, 'test-channel', 'Test Channel').then(({channel}) => {
testChannel = channel;
originalTestChannel = {...channel};
cy.apiAddUserToChannel(channel.id, user.id);
});
cy.apiLogin(testUser);
});
});
beforeEach(() => {
// Visit the channel before each test
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
// 2DO: update tests ids once QA has defined the test plan and zephyr tests
it('MM-T1: Can open and close the channel settings modal', () => {
// # Open channel settings modal from channel header dropdown
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// * Verify modal is open
cy.get('.ChannelSettingsModal').should('be.visible');
cy.get('#genericModalLabel').should('contain', 'Channel Settings');
// # Close the modal
cy.get('.GenericModal .modal-header button[aria-label="Close"]').click();
// * Verify modal is closed
cy.get('.ChannelSettingsModal').should('not.exist');
});
it('MM-T2: Can navigate between tabs', () => {
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// * Verify Info tab is active by default
cy.get('#infoButton').should('have.class', 'active');
cy.get('.ChannelSettingsModal__infoTab').should('be.visible');
// # Click on Archive tab
cy.get('#archiveButton').click();
// * Verify Archive tab is active
cy.get('#archiveButton').should('have.class', 'active');
cy.get('.ChannelSettingsModal__archiveTab').should('be.visible');
// # Click back on Info tab
cy.get('#infoButton').click();
// * Verify Info tab is active again
cy.get('#infoButton').should('have.class', 'active');
cy.get('.ChannelSettingsModal__infoTab').should('be.visible');
});
it('MM-T3: Can edit channel name and URL', () => {
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Get original channel name, url and alias it
cy.get('#input_channel-settings-name').invoke('val').as('originalName');
cy.get('.url-input-label').invoke('val').as('originalUrl');
// # Edit channel name
cy.get('#input_channel-settings-name').clear().type('Updated Channel Name');
// * Verify URL remains unchanged (does not auto-update when editing existing channels)
cy.get('@originalUrl').then((originalUrl) => {
cy.get('.url-input-label').should('contain', originalUrl);
});
// # Click Save
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * Verify changes are saved
cy.get('.SaveChangesPanel').should('contain', 'Settings saved');
// # Close the modal
cy.get('.GenericModal .modal-header button[aria-label="Close"]').click();
// * Verify channel header shows updated name
cy.get('#channelHeaderTitle').should('contain', 'Updated Channel Name');
// # Open channel settings modal to restore original values
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// * Verify the channel name is reset to the original value
cy.get('#input_channel-settings-name').clear().type(originalTestChannel.display_name);
// # Try to change URL to match the original url
cy.get('.url-input-button').click();
cy.get('.url-input-container input').clear().type(originalTestChannel.name);
cy.get('.url-input-container button.url-input-button').click();
// # Save changes
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * Verify changes are saved
cy.get('.SaveChangesPanel').should('contain', 'Settings saved');
// # Close the modal
cy.get('.GenericModal .modal-header button[aria-label="Close"]').click();
// * Verify channel header shows updated name
cy.get('#channelHeaderTitle').should('contain', originalTestChannel.display_name);
});
it('MM-T31: Channel name changes do not auto-update URL, URL only changes when explicitly modified', () => {
// # Create a new channel for this test
cy.apiCreateChannel(testTeam.id, 'url-behavior-test', 'URL Behavior Test').then(({channel}) => {
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Store original values
cy.get('#input_channel-settings-name').invoke('val').as('originalName');
cy.get('.url-input-label').invoke('val').as('originalUrl');
// # Verify changing channel name does NOT auto-update URL
// # Change the channel name
cy.get('#input_channel-settings-name').clear().type('New Channel Name');
// * Verify URL remains unchanged after name change
cy.get('@originalUrl').then((originalUrl) => {
cy.get('.url-input-label').should('contain', originalUrl);
});
// # Save the name change
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * Verify changes are saved
cy.get('.SaveChangesPanel').should('contain', 'Settings saved');
// * Verify URL is still the original URL after saving
cy.get('@originalUrl').then((originalUrl) => {
cy.get('.url-input-label').should('contain', originalUrl);
});
// # Verify URL only changes when explicitly modified
// # Click the URL edit button to enable URL editing
cy.get('.url-input-button').click();
// # Modify the URL explicitly
cy.get('.url-input-container input').clear().type('explicitly-changed-url');
// # Confirm the URL change
cy.get('.url-input-container button.url-input-button').click();
// * Verify URL has been updated to the new value
cy.get('.url-input-label').should('contain', 'explicitly-changed-url');
// # Save the URL change
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * Verify changes are saved
cy.get('.SaveChangesPanel').should('contain', 'Settings saved');
// # Close the modal
cy.get('.GenericModal .modal-header button[aria-label="Close"]').click();
// * Verify the URL has actually changed by checking the browser URL
cy.url().should('include', 'explicitly-changed-url');
// # Verify subsequent name changes still don't affect the explicitly set URL
// # Open channel settings modal again
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Store the current URL (which should be the explicitly changed one)
cy.get('.url-input-label').invoke('val').as('currentUrl');
// # Change the channel name again
cy.get('#input_channel-settings-name').clear().type('Another Name Change');
// * Verify URL remains the explicitly set URL, not auto-generated from new name
cy.get('@currentUrl').then((currentUrl) => {
cy.get('.url-input-label').should('contain', currentUrl);
});
// * Verify URL does NOT contain auto-generated value from new name
cy.get('.url-input-label').should('not.contain', 'another-name-change');
// # Save the second name change
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * Verify changes are saved
cy.get('.SaveChangesPanel').should('contain', 'Settings saved');
// * Final verification: URL should still be the explicitly set one
cy.get('@currentUrl').then((currentUrl) => {
cy.get('.url-input-label').should('contain', currentUrl);
});
});
});
it('MM-T4: Shows error for invalid channel name', () => {
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Set empty channel name
cy.get('#input_channel-settings-name').clear();
// # Try Save changes
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * Verify error is shown
cy.get('.Input_fieldset').should('have.class', 'Input_fieldset___error');
cy.get('.SaveChangesPanel').should('contain', 'There are errors in the form above');
});
it('MM-T6: Can edit channel purpose and header', () => {
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Edit channel purpose
cy.get('#channel_settings_purpose_textbox').clear().type('This is a test purpose');
// # Edit channel header
cy.get('#channel_settings_header_textbox').clear().type('This is a test header');
// # Save changes
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * Verify changes are saved
cy.get('.SaveChangesPanel').should('contain', 'Settings saved');
// # Close the modal
cy.get('.GenericModal .modal-header button[aria-label="Close"]').click();
// * Verify channel header shows updated header
cy.get('#channelHeaderDescription').should('contain', 'This is a test header');
});
it('MM-T7: Shows error when purpose exceeds character limit', () => {
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Enter purpose that exceeds character limit (250 characters)
const longPurpose = 'a'.repeat(260);
cy.get('#channel_settings_purpose_textbox').clear().type(longPurpose);
// * Verify error is shown
cy.findAllByTestId('channel_settings_purpose_textbox').should('have.class', 'textarea--has-errors');
cy.get('.SaveChangesPanel').should('contain', 'There are errors in the form above');
});
it('MM-T8: Shows error when header exceeds character limit', () => {
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Enter header that exceeds character limit (1024 characters)
const longHeader = 'a'.repeat(1050);
cy.findByTestId('channel_settings_header_textbox').clear().type(longHeader);
// * Verify error is shown
cy.findByTestId('channel_settings_header_textbox').should('have.class', 'textarea--has-errors');
cy.get('.SaveChangesPanel').should('contain', 'There are errors in the form above');
});
it('MM-T9: Can archive a channel and stay in archived channel view', () => {
// # Create a new channel for this test
cy.apiCreateChannel(testTeam.id, 'archived-channel-test', 'Archived Channel Test').then(({channel}) => {
// # Visit the channel to be archived
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Click on Archive tab
cy.get('#archiveButton').click();
// # Click Archive button
cy.get('#channelSettingsArchiveChannelButton').click();
// * Verify confirmation modal appears
cy.get('#archiveChannelConfirmModal').should('be.visible');
// # Confirm archive
cy.findByRole('button', {name: 'Confirm'}).click();
// * Verify we stay in the archived channel (no redirect)
cy.url().should('include', channel.name);
// * Verify archived channel message is displayed
cy.contains('You are viewing an archived channel').should('be.visible');
// * Verify channel is still visible in sidebar while viewing it
cy.get('.SidebarChannel').contains('Archived Channel Test').should('exist');
// # Navigate to a different channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify archived channel disappears from sidebar after navigating away
cy.get('.SidebarChannel').contains('Archived Channel Test').should('not.exist');
});
});
it('MM-T10: Warns when switching tabs with unsaved changes', () => {
// # Create a new channel for this test
cy.apiCreateChannel(testTeam.id, 'unsaved-test', 'Unsaved Test').then(({channel}) => {
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Make a change to the channel name
cy.get('#input_channel-settings-name').clear().type('Changed Name');
// # Try to switch to Archive tab
cy.get('#archiveButton').click();
// * Verify we're still on the Info tab due to unsaved changes
cy.get('#infoButton').should('have.class', 'active');
// * Verify save changes panel shows error
cy.get('.SaveChangesPanel').should('have.class', 'error');
});
});
// MM-T11: Can reset changes without saving
it('MM-T11: Can reset changes without saving', () => {
// # Create a new channel for this test
cy.apiCreateChannel(testTeam.id, 'reset-test', 'Reset Test').then(({channel}) => {
// # Visit the channel page using the channel name returned from the API
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Get original channel name and alias it
cy.get('#input_channel-settings-name').invoke('val').as('originalName');
// # Change the channel name
cy.get('#input_channel-settings-name').clear().type('Temporary Name');
// # Click the Reset button in the SaveChangesPanel
cy.get('.SaveChangesPanel button').contains('Reset').click();
// * Verify the channel name is reset to the original value
cy.get('@originalName').then((originalName) => {
cy.get('#input_channel-settings-name').should('have.value', originalName);
});
// * Verify the SaveChangesPanel is no longer visible
cy.get('.SaveChangesPanel').should('not.exist');
});
});
// MM-T12: Can preview purpose and header with markdown
it('MM-T12: Can preview purpose and header with markdown', () => {
// # Create a new channel for this test
cy.apiCreateChannel(testTeam.id, 'markdown-test', 'Markdown Test').then(({channel}) => {
// # Visit the channel page using the channel name returned from the API
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Add markdown to purpose
cy.get('#channel_settings_purpose_textbox').clear().type('This is **bold** and _italic_ text');
// # Click preview button for purpose (assumes preview button is inside the AdvancedTextbox container)
cy.get('#channel_settings_purpose_textbox').
parents('.AdvancedTextbox').
find('#PreviewInputTextButton').
click();
// * Verify that markdown is rendered in preview: check for bold and italic formatting
cy.get('.textbox-preview-area').
should('contain', 'This is').
find('strong').
should('contain', 'bold');
cy.get('.textbox-preview-area').
find('em').
should('contain', 'italic');
// # Add markdown to header
cy.get('#channel_settings_header_textbox').clear().type('Visit [Mattermost](https://mattermost.com)');
// # Click preview button for header
cy.get('#channel_settings_header_textbox').
parents('.AdvancedTextbox').
find('#PreviewInputTextButton').
click();
// * Verify that markdown is rendered in preview: check for a link with text "Mattermost"
cy.get('.textbox-preview-area').
should('contain', 'Visit').
find('a').
should('contain', 'Mattermost');
});
});
it('MM-T13: Validates URL when editing channel name', () => {
// # Create two channels to test URL conflict error
cy.apiCreateChannel(testTeam.id, 'first-channel', 'First Channel').then(({channel: channel1}) => {
cy.apiCreateChannel(testTeam.id, 'second-channel', 'Second Channel').then(({channel}) => {
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Try to change URL to match the first channel
cy.get('.url-input-button').click();
cy.get('.url-input-container input').clear().type(channel1.name);
cy.get('.url-input-container button.url-input-button').click();
// * Verify error is shown
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
cy.get('.SaveChangesPanel').should('contain', 'There are errors in the form above');
});
});
});
});

View File

@@ -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 @channel @channel_settings
import type {Team} from '@mattermost/types/teams';
import {
beMuted,
beUnmuted,
} from '@/support/assertions';
describe('Channel Settings', () => {
let testTeam: Team;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
cy.apiCreateChannel(testTeam.id, 'channel', 'Private Channel', 'P').then(({channel}) => {
cy.apiAddUserToChannel(channel.id, user.id);
});
cy.apiLogin(user);
// # Visit town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('MM-T882 Channel URL validation works properly', () => {
cy.apiCreateChannel(testTeam.id, 'channel-test', 'Channel').then(({channel}) => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Go to channel dropdown > Channel Settings Modal
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
// # Change channel name (this should NOT affect the URL anymore)
cy.get('#input_channel-settings-name').clear().type('town-square');
// # Now explicitly change the URL to trigger the validation error
cy.get('.url-input-button').click();
cy.get('.url-input-container input').clear().type('town-square');
cy.get('.url-input-container button.url-input-button').click();
// # Try to save
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// # Error is displayed and URL is unchanged
cy.get('.SaveChangesPanel').should('contain', 'There are errors in the form above');
cy.get('.url-input-error').should('be.visible').and('contain.text', 'A channel with that name already exists on the same team.');
cy.url().should('include', `/${testTeam.name}/channels/${channel.name}`);
// # Enter a new URL and save
cy.get('.url-input-container input').clear().type('another-town-square');
cy.get('.url-input-button').click();
cy.get('.url-input-container button.url-input-button').click();
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
// * URL is updated and no errors are displayed
cy.url().should('include', `/${testTeam.name}/channels/another-town-square`);
});
});
it('MM-T887 Channel dropdown menu - Mute / Unmute', () => {
// # Visit off-topic
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Go to channel dropdown > Mute channel
cy.get('#channelHeaderTitle').click();
cy.get('#channelHeaderDropdownMenu').should('exist').
findByText('Mute Channel').should('be.visible').click();
// # Verify channel is muted
cy.get('#sidebarItem_off-topic').should(beMuted);
// # Verify mute bell icon is visible
cy.get('#toggleMute').should('be.visible');
// # Verify that off topic is last in the list of channels
cy.uiGetLhsSection('CHANNELS').find('.SidebarChannel').
last().should('contain', 'Off-Topic').
get('a').should('have.class', 'muted');
// # Click Unmute channel while menu is open
cy.get('#channelHeaderTitle').click();
cy.get('#channelHeaderDropdownMenu').should('exist').
findByText('Unmute Channel').should('be.visible').click();
// # Verify channel is unmuted
cy.get('#sidebarItem_off-topic').should(beUnmuted);
// # Verify mute bell icon is not visible
cy.get('#toggleMute').should('not.exist');
// # Verify that off topic is not last in the list of channels
cy.uiGetLhsSection('CHANNELS').find('.SidebarChannel').
last().should('not.contain', 'Off-Topic');
});
});

View File

@@ -0,0 +1,120 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel @smoke
describe('Channel Switcher', () => {
let testTeam: Team;
let offTopicUrl: string;
const channelNamePrefix = 'aswitchchannel';
const channelDisplayNamePrefix = 'ASwitchChannel';
let channelB: Channel;
let channelC: Channel;
before(() => {
cy.apiInitSetup({channelPrefix: {name: `${channelNamePrefix}-a`, displayName: `${channelDisplayNamePrefix} A`}}).then(({team, user, offTopicUrl: url}) => {
testTeam = team;
offTopicUrl = url;
// # Add some channels
cy.apiCreateChannel(testTeam.id, `${channelNamePrefix}-b`, `${channelDisplayNamePrefix} B`, 'O').then(({channel}) => {
channelB = channel;
});
cy.apiCreateChannel(testTeam.id, `${channelNamePrefix}-c`, `${channelDisplayNamePrefix} C`, 'O').then(({channel}) => {
channelC = channel;
});
// # Login as test user and go to off-topic
cy.apiLogin(user);
cy.visit(offTopicUrl);
});
});
it('MM-T2031_1 - should switch channels by keyboard', () => {
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing channel name in the "Switch Channels" modal message box
// # Use up/down arrow keys to highlight second channel
// # Press ENTER
cy.findByRole('combobox', {name: 'quick switch input'}).type(`${channelDisplayNamePrefix}`);
cy.findByRole('option', {name: channelB.display_name}).should('be.visible');
cy.findByRole('combobox', {name: 'quick switch input'}).type('{downarrow}{downarrow}{enter}');
// * Expect channel title to match title
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', channelB.display_name);
// * Expect url to match url
cy.url().should('contain', `${channelB.name}`);
});
it('MM-T2031_2 - should switch channels by mouse', () => {
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Start typing channel name in the "Switch Channels" modal message box
cy.findByRole('combobox', {name: 'quick switch input'}).type(`${channelDisplayNamePrefix} `);
cy.get(`[data-testid^=${channelC.name}] > span`).click();
// * Expect channel title to match title
cy.get('#channelHeaderTitle').
should('be.visible').
and('contain.text', channelC.display_name);
// * Expect url to match url
cy.url().should('contain', `${channelC.name}`);
});
it('MM-T2031_3 - should show empty result', () => {
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Type invalid channel name in the "Switch Channels" modal message box
cy.findByRole('combobox', {name: 'quick switch input'}).type('there-is-no-spoon');
// * Expect 'nothing found' message
cy.get('.no-results__title > span').should('be.visible');
});
it('MM-T2031_4 - should close on esc and outside click', () => {
cy.visit(offTopicUrl);
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Press ESC
cy.findByRole('combobox', {name: 'quick switch input'}).type('{esc}');
// * Expect the dialog to be closed
cy.findByRole('combobox', {name: 'quick switch input'}).should('not.exist');
// * Expect staying in the same channel
cy.url().should('contain', 'off-topic');
// # Press CTRL+K (Windows) or CMD+K(Mac)
cy.typeCmdOrCtrl().type('K', {release: true});
// # Click outside of the modal
cy.get('.modal').click({force: true});
// * Expect the dialog to be closed
cy.findByRole('combobox', {name: 'quick switch input'}).should('not.exist');
// * Expect staying in the same channel
cy.url().should('contain', 'off-topic');
});
});

View File

@@ -0,0 +1,131 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import type {ChainableT} from '@/types';
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel @channel_settings @not_cloud
// Make sure that the current channel is Town Square and that the
// channel identified by the passed name is no longer in the channel
// sidebar
function verifyChannelWasProperlyClosed(channelName: string) {
// * Make sure that we have switched channels
cy.get('#channelHeaderTitle').should('contain', 'Town Square');
// * Make sure the old DM no longer exists
cy.get('#sidebarItem_' + channelName).should('not.exist');
}
describe('Close direct messages', () => {
let testUser: UserProfile;
let otherUser: UserProfile;
let testTeam: Team;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser().then(({user: newUser}) => {
otherUser = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/town-square`);
});
});
it('Through channel header dropdown menu', () => {
createAndVisitDMChannel([testUser.id, otherUser.id]).then((channel) => {
// # Open channel header dropdown menu and click on Close Direct Message
cy.get('#channelHeaderTitle').click();
cy.findByText('Close Direct Message').click();
verifyChannelWasProperlyClosed(channel.name);
});
});
function createAndVisitDMChannel(userIds: string[]): ChainableT<Channel> {
return cy.apiCreateDirectChannel(userIds).then(({channel}) => {
// # Visit the new channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Verify channel's display name
cy.get('#channelHeaderTitle').should('contain', channel.display_name);
return cy.wrap(channel);
});
}
});
describe('Close group messages', () => {
let testUser: UserProfile;
let otherUser1: UserProfile;
let otherUser2: UserProfile;
let testTeam: Team;
before(() => {
cy.apiAdminLogin();
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiCreateUser({prefix: 'aaa'}).then(({user: newUser}) => {
otherUser1 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
cy.apiCreateUser({prefix: 'bbb'}).then(({user: newUser}) => {
otherUser2 = newUser;
cy.apiAddUserToTeam(team.id, newUser.id);
});
// # Login as test user and go to town square
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/town-square`);
});
});
it('Through channel header dropdown menu', () => {
createAndVisitGMChannel([otherUser1, otherUser2]).then((channel) => {
// # Open channel header dropdown menu and click on Close Direct Message
cy.get('#channelHeaderTitle').click();
cy.findByText('Close Group Message').click();
verifyChannelWasProperlyClosed(channel.name);
});
});
function createAndVisitGMChannel(users: UserProfile[] = []) {
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);
});
}
});

View File

@@ -0,0 +1,571 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import type {Channel} from '@mattermost/types/channels';
import {getAdminAccount} from '@/support/env';
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a 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 Type Conversion (Public to Private Only)', () => {
let testUser: UserProfile;
let testTeam: Team;
const admin = getAdminAccount();
// Helper functions for permission management
const saveConfig = () => {
cy.waitUntil(() =>
cy.get('#saveSetting').then((button) => {
// Check if the button text is exactly "Save"
if (button.text().trim() === 'Save') {
button.click();
return true; // signals waitUntil that it succeeded
}
return false;
}),
);
};
const enablePermission = (permissionCheckBoxTestId: string) => {
cy.findByTestId(permissionCheckBoxTestId).then((el) => {
if (!el.hasClass('checked')) {
el.click();
}
});
};
const removePermission = (permissionCheckBoxTestId: string) => {
cy.findByTestId(permissionCheckBoxTestId).then((el) => {
if (el.hasClass('checked')) {
el.click();
}
});
};
const promoteToChannelAdmin = (userId: string, channelId: string, admin: Pick<UserProfile, 'username' | 'password'>) => {
cy.externalRequest({
user: admin,
method: 'put',
path: `channels/${channelId}/members/${userId}/schemeRoles`,
data: {
scheme_user: true,
scheme_admin: true,
},
});
};
const resetPermissionsToDefault = () => {
// # Login as sysadmin and navigate to system scheme page
cy.apiAdminLogin();
cy.visit('/admin_console/user_management/permissions/system_scheme');
// # Click reset to defaults and confirm
cy.findByTestId('resetPermissionsToDefault').click();
cy.get('#confirmModalButton').click();
// # Save
saveConfig();
cy.wait(1000);
cy.visit('/admin_console/user_management/permissions/system_scheme');
};
// Helper functions for permission setup
const setupPermissions = (config: {
resetToDefault?: boolean;
publicToPrivate?: boolean;
removeFromTeamAdmin?: boolean;
}) => {
if (config.resetToDefault) {
resetPermissionsToDefault();
}
// Public to private conversion permissions
if (config.publicToPrivate) {
enablePermission('all_users-public_channel-convert_public_channel_to_private-checkbox');
} else if (config.publicToPrivate === false) {
removePermission('all_users-public_channel-convert_public_channel_to_private-checkbox');
}
// Remove public to private conversion from team admin if specified
if (config.removeFromTeamAdmin) {
removePermission('team_admin-public_channel-convert_public_channel_to_private-checkbox');
}
cy.wait(1000);
saveConfig();
};
// Helper functions for channel management
const createAndVisitPublicChannel = (teamName: string, channelId: string, displayName: string): Cypress.Chainable<Channel> => {
return cy.apiCreateChannel(testTeam.id, channelId, displayName).then(({channel}) => {
cy.apiAddUserToChannel(channel.id, testUser.id);
cy.visit(`/${teamName}/channels/${channel.name}`);
return cy.wrap(channel);
});
};
const createAndVisitPrivateChannel = (teamName: string, channelId: string, displayName: string): Cypress.Chainable<Channel> => {
return cy.apiCreateChannel(testTeam.id, channelId, displayName, 'P').then(({channel}) => {
cy.apiAddUserToChannel(channel.id, testUser.id);
cy.visit(`/${teamName}/channels/${channel.name}`);
return cy.wrap(channel);
});
};
const visitChannel = (teamName: string, channelName: string) => {
cy.visit(`/${teamName}/channels/${channelName}`);
};
// Helper functions for UI interaction
const openChannelSettingsModal = () => {
cy.get('#channelHeaderDropdownButton').click();
cy.findByText('Channel Settings').click();
cy.get('.ChannelSettingsModal').should('be.visible');
};
const closeChannelSettingsModal = () => {
cy.get('.GenericModal .modal-header button[aria-label="Close"]').click();
cy.get('.ChannelSettingsModal').should('not.exist');
};
// Helper functions for UI interaction
const saveChannelSettings = () => {
cy.get('[data-testid="SaveChangesPanel__save-btn"]').click();
};
const verifySettingsSaved = () => {
cy.get('.SaveChangesPanel').should('contain', 'Settings saved');
};
// Helper functions for channel conversion
const convertChannelToPrivate = () => {
// Select private channel type
cy.get('#public-private-selector-button-P').click();
cy.get('#public-private-selector-button-P').should('have.class', 'selected');
// Save changes - this will trigger the confirmation modal
saveChannelSettings();
// Handle the confirmation modal that appears when converting from public to private
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModalButton').click();
// Verify settings were saved
verifySettingsSaved();
// Verify the modal completely closed to avoid flakiness
cy.get('#confirmModal').should('not.exist');
};
// Function kept for potential future use but not used in current tests
// since private to public conversion is no longer allowed
// const convertChannelToPublic = () => {
// cy.get('#public-private-selector-button-O').click();
// cy.get('#public-private-selector-button-O').should('have.class', 'selected');
// };
// Helper functions for verification
const verifyChannelIsPrivate = (channelName: string) => {
cy.get('.SidebarChannel').contains(channelName).parent().find('.icon-lock-outline').should('exist');
};
// Function kept for potential future use but not used in current tests
// since private to public conversion is no longer allowed
// const verifyChannelIsPublic = (channelName: string) => {
// cy.get('.SidebarChannel').contains(channelName).parent().find('.icon-lock-outline').should('not.exist');
// };
const verifyConversionOptionDisabled = (toPrivate = true) => {
if (toPrivate) {
// Wait for the UI to fully load and stabilize
cy.wait(500);
// Check if the button is disabled - it might have a disabled attribute or a disabled class
cy.get('#public-private-selector-button-P').then(($el) => {
const isDisabled = $el.hasClass('disabled') || $el.prop('disabled') === true || $el.attr('aria-disabled') === 'true';
expect(isDisabled).to.be.true;
});
} else {
// Wait for the UI to fully load and stabilize
cy.wait(500);
// Check if the button is disabled - it might have a disabled attribute or a disabled class
cy.get('#public-private-selector-button-O').then(($el) => {
const isDisabled = $el.hasClass('disabled') || $el.prop('disabled') === true || $el.attr('aria-disabled') === 'true';
expect(isDisabled).to.be.true;
});
}
};
const verifyConversionOptionEnabled = (toPrivate = true) => {
if (toPrivate) {
// Wait for the UI to fully load and stabilize
cy.wait(500);
// Check if the button is enabled - it should not have disabled attributes or classes
cy.get('#public-private-selector-button-P').then(($el) => {
const isDisabled = $el.hasClass('disabled') || $el.prop('disabled') === true || $el.attr('aria-disabled') === 'true';
expect(isDisabled).to.be.false;
});
} else {
// Wait for the UI to fully load and stabilize
cy.wait(500);
// Check if the button is enabled - it should not have disabled attributes or classes
cy.get('#public-private-selector-button-O').then(($el) => {
const isDisabled = $el.hasClass('disabled') || $el.prop('disabled') === true || $el.attr('aria-disabled') === 'true';
expect(isDisabled).to.be.false;
});
}
};
// Helper function to make user a channel admin
const makeUserChannelAdmin = (channelId: string, userId: string) => {
cy.apiAddUserToChannel(channelId, userId);
promoteToChannelAdmin(userId, channelId, admin);
};
// Helper function to make user a team admin
const makeUserTeamAdmin = (teamId: string, userId: string) => {
cy.apiUpdateTeamMemberSchemeRole(teamId, userId, {scheme_admin: true, scheme_user: true});
};
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
});
});
beforeEach(() => {
// Reset to a clean state before each test
cy.apiLogin(testUser);
});
describe('Basic Conversion Functionality', () => {
it('MM-T3348-1 - Can convert a public channel to private', () => {
// # Setup permissions
setupPermissions({resetToDefault: true, publicToPrivate: true});
// # Create and visit a public channel
createAndVisitPublicChannel(testTeam.name, 'public-to-private', 'Public To Private').then((channel) => {
// # Open channel settings modal
openChannelSettingsModal();
// # Convert to private
convertChannelToPrivate();
// # Close the modal
closeChannelSettingsModal();
// * Verify channel is now private
verifyChannelIsPrivate(channel.display_name);
});
});
it('MM-T3348-5 - Cannot convert a private channel back to public', () => {
// # Setup permissions
setupPermissions({resetToDefault: true, publicToPrivate: true});
// # Create and visit a public channel
createAndVisitPublicChannel(testTeam.name, 'private-stays-private', 'Private Stays Private').then((channel) => {
// # Open channel settings modal
openChannelSettingsModal();
// # Convert to private
convertChannelToPrivate();
// # Close the modal
closeChannelSettingsModal();
// * Verify channel is now private
verifyChannelIsPrivate(channel.display_name);
// # Open channel settings modal again
openChannelSettingsModal();
// * Verify conversion option to public is disabled
verifyConversionOptionDisabled(false);
// # Close the modal
closeChannelSettingsModal();
});
});
});
describe('Role-Based Channel Type Conversion', () => {
it('MM-T3350-1 - System admin cannot convert a private channel to public', () => {
// # Reset permissions to default
resetPermissionsToDefault();
// # Login as system admin
cy.apiAdminLogin();
// # Create and visit a private channel
createAndVisitPrivateChannel(testTeam.name, 'sysadmin-private-stays-private', 'SysAdmin Private Channel').then(() => {
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option to public is disabled
verifyConversionOptionDisabled(false);
// # Close the modal
closeChannelSettingsModal();
});
});
it('MM-T3350-2 - Channel admin cannot convert a private channel to public', () => {
// # Reset permissions to default
setupPermissions({resetToDefault: true});
// # Login as regular user
cy.apiLogin(testUser);
// # Create and visit a private channel
createAndVisitPrivateChannel(testTeam.name, 'channel-admin-private', 'Channel Admin Private').then((channel) => {
// # Make test user a channel admin
makeUserChannelAdmin(channel.id, testUser.id);
// # Visit the channel again after role change
visitChannel(testTeam.name, channel.name);
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option to public is disabled
verifyConversionOptionDisabled(false);
// # Close the modal
closeChannelSettingsModal();
});
});
it('MM-T3350-3 - Team admin cannot convert a private channel to public', () => {
// # Reset permissions to default
setupPermissions({resetToDefault: true});
// # Make test user a team admin
makeUserTeamAdmin(testTeam.id, testUser.id);
// # Login as team admin
cy.apiLogin(testUser);
// # Create and visit a private channel
createAndVisitPrivateChannel(testTeam.name, 'team-admin-private', 'Team Admin Private').then(() => {
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option to public is disabled
verifyConversionOptionDisabled(false);
// # Close the modal
closeChannelSettingsModal();
});
});
it('MM-T3350-4 - Regular user cannot convert a private channel to public', () => {
// # Reset permissions to default
resetPermissionsToDefault();
// # Create a new regular user specifically for this test
cy.apiCreateUser().then(({user: regularUser}) => {
// # Add user to the team
cy.apiAddUserToTeam(testTeam.id, regularUser.id);
// # Have admin create a private channel
cy.apiAdminLogin();
cy.apiCreateChannel(testTeam.id, 'admin-created-private', 'Admin Created Private', 'P').then(({channel}) => {
// # Add the regular user to the channel (as a regular member, not admin)
cy.apiAddUserToChannel(channel.id, regularUser.id);
// # Login as the regular user
cy.apiLogin(regularUser);
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option is disabled
verifyConversionOptionDisabled(false);
// # Close the modal
closeChannelSettingsModal();
});
});
});
});
describe('Permission-Based Tests', () => {
it('MM-T3348-2 - Regular user without permission cannot convert public to private', () => {
// # Setup permissions - remove public to private conversion permission
setupPermissions({resetToDefault: true, publicToPrivate: false});
// # Create a new regular user specifically for this test
cy.apiCreateUser().then(({user: regularUser}) => {
// # Add user to the team
cy.apiAddUserToTeam(testTeam.id, regularUser.id);
// # Have admin create a public channel
cy.apiAdminLogin();
cy.apiCreateChannel(testTeam.id, 'admin-created-public', 'Admin Created Public').then(({channel}) => {
// # Add the regular user to the channel (as a regular member, not admin)
cy.apiAddUserToChannel(channel.id, regularUser.id);
// # Login as the regular user
cy.apiLogin(regularUser);
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option is disabled
verifyConversionOptionDisabled(true);
// # Close the modal
closeChannelSettingsModal();
});
});
});
it('MM-T3348-3 - Team admin can convert public to private by default', () => {
// # Setup permissions - reset to default
setupPermissions({resetToDefault: true});
// # Make test user a team admin
makeUserTeamAdmin(testTeam.id, testUser.id);
// # Create and visit a public channel
createAndVisitPublicChannel(testTeam.name, 'team-admin-convert', 'Team Admin Convert').then((channel) => {
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option is enabled
verifyConversionOptionEnabled(true);
// # Convert to private
convertChannelToPrivate();
// # Close the modal
closeChannelSettingsModal();
// * Verify channel is now private
verifyChannelIsPrivate(channel.display_name);
});
});
it('MM-T3348-4 - Team admin cannot convert when permission is removed', () => {
// # Setup permissions - remove permission from team admin
setupPermissions({
resetToDefault: true,
removeFromTeamAdmin: true,
});
// # Make test user a team admin
makeUserTeamAdmin(testTeam.id, testUser.id);
// # Have admin create a public channel
cy.apiAdminLogin();
cy.apiCreateChannel(testTeam.id, 'admin-created-team-admin-no-perm', 'Admin Created Team Admin No Permission').then(({channel}) => {
// # Add the team admin user to the channel (as a regular member, not channel admin)
cy.apiAddUserToChannel(channel.id, testUser.id);
// # Login as the team admin user
cy.apiLogin(testUser);
// # Visit the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option is disabled
verifyConversionOptionDisabled(true);
// # Close the modal
closeChannelSettingsModal();
});
});
});
describe('Channel Admin Tests', () => {
it('MM-T3349-1 - Channel admin can convert public to private when permission is enabled', () => {
// # Setup permissions - enable public to private conversion permission
setupPermissions({resetToDefault: true, publicToPrivate: true});
// # Create and visit a public channel
createAndVisitPublicChannel(testTeam.name, 'channel-admin-pub', 'Channel Admin Public').then((channel) => {
// # Make test user a channel admin
makeUserChannelAdmin(channel.id, testUser.id);
// # Visit the channel again after role change
visitChannel(testTeam.name, channel.name);
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option is enabled
verifyConversionOptionEnabled(true);
// # Convert to private
convertChannelToPrivate();
// # Close the modal
closeChannelSettingsModal();
// * Verify channel is now private
verifyChannelIsPrivate(channel.display_name);
});
});
it('MM-T3349-2 - Channel admin cannot convert public to private when permission is removed', () => {
// Generate a unique channel name with timestamp to avoid conflicts
const timestamp = Date.now();
const channelId = `channel-admin-no-perm-${timestamp}`;
const displayName = `Channel Admin No Permission ${timestamp}`;
// # Setup permissions - remove public to private conversion permission
setupPermissions({resetToDefault: true, publicToPrivate: false, removeFromTeamAdmin: true});
// # Create and visit a public channel
createAndVisitPublicChannel(testTeam.name, channelId, displayName).then((channel) => {
// # Make test user a channel admin
makeUserChannelAdmin(channel.id, testUser.id);
// # Log back in as the test user after making channel admin
cy.apiLogin(testUser);
// # Wait for login to complete
cy.wait(500);
// # Visit the channel again after role change
visitChannel(testTeam.name, channel.name);
// # Wait for channel to load
cy.wait(500);
// # Open channel settings modal
openChannelSettingsModal();
// * Verify conversion option is disabled
verifyConversionOptionDisabled(true);
// # Close the modal
closeChannelSettingsModal();
});
});
});
});

View File

@@ -0,0 +1,171 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Channel} from '@mattermost/types/channels';
import type {Team, TeamMembership} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
describe('Group Message Conversion To Private Channel', () => {
let testTeam1: Team;
let testTeam2: Team;
let testUser1: UserProfile;
let testUser2: UserProfile;
let testUser3: UserProfile;
let gm: Channel;
before(() => {
// we need two teams, and a set of users belonging to both teams
cy.apiInitSetup().then(({team, user}) => {
testTeam1 = team;
testUser1 = user;
cy.apiCreateTeam('gmconversionteam2', 'GM Conversion Team 2').then(({team: team2}) => {
testTeam2 = team2;
cy.apiCreateUser({prefix: 'other'}).then(({user: user2}) => {
testUser2 = user2;
cy.apiCreateUser({prefix: 'other'}).then(({user: user3}) => {
testUser3 = user3;
const teamMembers1 = [testUser1, testUser2, testUser3].map((u) => ({team_id: testTeam1.id, user_id: u.id}));
cy.apiAddUsersToTeam(testTeam1.id, teamMembers1 as TeamMembership[]).then(() => {
const teamMembers2 = [testUser1, testUser2, testUser3].map((u) => ({team_id: testTeam2.id, user_id: u.id}));
cy.apiAddUsersToTeam(testTeam2.id, teamMembers2 as TeamMembership[]).then(() => {
cy.apiCreateGroupChannel([testUser1.id, testUser2.id, testUser3.id]).then(({channel}) => {
gm = channel;
});
});
});
});
});
});
});
});
it('GM conversion', () => {
// Open the GM
cy.visit(`/${testTeam1.name}/messages/${gm.name}`);
// Open the GM conversion dialog
cy.get('#channelHeaderDropdownButton').click();
cy.findByRole('menuitem', {name: 'Settings'}).trigger('mouseover');
cy.findByText('Convert to Private Channel').click();
// the dialog has a animation, so we're waiting for it to finish
// by waiting for the (currently disabled) confirm button to appear
cy.get('.GenericModal__button.delete.disabled').wait(2000);
// Open the team dropdown and select a team
cy.findByText('Select Team').click({force: true});
cy.findByText(testTeam2.display_name).click();
// Enter the new channel name and confirm
cy.findByPlaceholderText('Channel name').type('Converted Channel');
cy.get('.GenericModal__button.delete').click();
// verify the channel is in a team, as indicated by the presence
// of `/channels/` in the URL us `/messages/` in case of GM
cy.url().should('contain', `/${testTeam2.name}/channels/converted-channel`);
// verify the channel created is a private channel by verifying
// the presence of the lock icon next to the channel name in LHS
cy.get('.SidebarChannel:contains("Converted Channel")').get('.icon.icon-lock-outline').should('be.visible');
});
it('When users belong to only one common team', () => {
let testUser4: Cypress.UserProfile;
let gm2;
cy.apiCreateUser({prefix: 'other'}).then(({user}) => {
testUser4 = user;
const teamMembers = [testUser1, testUser2, testUser3, testUser4].map((u) => ({
team_id: testTeam2.id,
user_id: u.id,
}));
cy.apiAddUsersToTeam(testTeam2.id, teamMembers as TeamMembership[]).then(() => {
cy.apiCreateGroupChannel([testUser1.id, testUser2.id, testUser3.id, testUser4.id]).then(({channel}) => {
gm2 = channel;
// Open the GM
cy.visit(`/${testTeam1.name}/messages/${gm2.name}`);
cy.get('#channelHeaderDropdownButton').click();
cy.findByRole('menuitem', {name: 'Settings'}).trigger('mouseover');
cy.findByText('Convert to Private Channel').click();
cy.get('.GenericModal__button.delete.disabled').wait(2000);
cy.contains('Select Team').should('not.exist');
cy.findByPlaceholderText('Channel name').type('Converted Channel 2');
cy.get('.GenericModal__button.delete').click();
cy.url().should('contain', `/${testTeam2.name}/channels/converted-channel-2`);
cy.get('.SidebarChannel:contains("Converted Channel 2")').get('.icon.icon-lock-outline').should('be.visible');
});
});
});
});
it('When users have no common team', () => {
let testUser5: Cypress.UserProfile;
let gm3;
let testTeam3;
cy.apiCreateUser({prefix: 'other'}).then(({user}) => {
testUser5 = user;
cy.apiCreateTeam('gmconversionteam3', 'GM Conversion Team 3').then(({team}) => {
testTeam3 = team;
const teamMembers = [{
team_id: testTeam3.id,
user_id: testUser5.id,
}];
cy.apiAddUsersToTeam(testTeam3.id, teamMembers as TeamMembership[]).then(() => {
cy.apiCreateGroupChannel([testUser1.id, testUser2.id, testUser3.id, testUser5.id]).then(({channel}) => {
gm3 = channel;
// Open the GM
cy.visit(`/${testTeam1.name}/messages/${gm3.name}`);
cy.get('#channelHeaderDropdownButton').click();
cy.findByRole('menuitem', {name: 'Settings'}).trigger('mouseover');
cy.findByText('Convert to Private Channel').click();
cy.findByText('Unable to convert to a channel because group members are part of different teams').wait(2000);
});
});
});
});
});
it('websocket event should update channel category correctly when in channel', () => {
cy.apiCreateGroupChannel([testUser1.id, testUser2.id, testUser3.id]).then(({channel}) => {
gm = channel;
// Open the GM
cy.visit(`/${testTeam1.name}/messages/${gm.name}`);
// Wait until the channel is loaded
cy.get('#channelHeaderDropdownButton').should('be.visible');
// convert via API call
const timestamp = Date.now();
cy.apiConvertGMToPrivateChannel(gm.id, testTeam2.id, `Channel ${timestamp}`, `c-${timestamp}`).then(() => {
cy.url().should('contain', `${testTeam2.name}/channels/c-${timestamp}`);
});
});
});
});

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 @channel
describe('Leave and Archive channel actions display as destructive', () => {
let testUser: Cypress.UserProfile;
let offTopicUrl: string;
before(() => {
// # Login as test user and visit off-topic channel
cy.apiInitSetup().then(({user, offTopicUrl: url}) => {
testUser = user;
offTopicUrl = url;
cy.apiLogin(testUser);
cy.visit(offTopicUrl);
});
});
beforeEach(() => {
cy.reload();
});
it('MM-T4943_1 Leave and Archive channel actions display as destructive in the channel dropdown menu', () => {
// # click on channel drop-down menu
cy.get('#channelHeaderTitle').should('be.visible').click();
// * View Info menu option should be visible
cy.findByText('View Info').should('be.visible');
// * Move to... menu option should be visible
cy.findByText('Move to...').should('be.visible').trigger('mouseover');
// * Favorites Sub-menu option should be visible
cy.findByText('Favorites').should('be.visible');
// * New Category Sub-menu option should be visible
cy.findByText('New Category').should('be.visible');
// * Move to... close menu option
cy.findByText('Move to...').should('be.visible').trigger('mouseout');
cy.findByRole('menuitem', {name: 'Move to...'}).should('have.attr', 'aria-expanded', 'false');
// * Notification Preferences menu option should be visible
cy.get('#channelNotificationPreferences').should('be.visible');
// * Mute Channel menu option should be visible
cy.get('#channelToggleMuteChannel').should('be.visible');
// * Manage Members menu option should be visible
cy.get('#channelMembers').should('be.visible');
// * Channel Settings menu option should be visible
cy.findByText('Channel Settings').should('be.visible');
// * Archive Channel menu option should be visible and have a background-color (destructive)
cy.get('#channelArchiveChannel').should('be.visible').focus().should('have.css', 'background-color', 'rgb(210, 75, 78)');
// // * Leave Channel menu option should be visible and hav a background-color (destructive)
cy.get('#channelLeaveChannel').should('be.visible').focus().should('have.css', 'background-color', 'rgb(210, 75, 78)');
});
it('MM-T4943_2 Leave channel actions display as destructive in the Edit Channel Menu ', () => {
// # Open Edit Channel Menu and verify menu optin
cy.uiGetChannelSidebarMenu('Off-Topic').within(() => {
// * Favorite menu option should be visible
cy.findByText('Favorite').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Mute Channel menu option should be visible
cy.findByText('Mute Channel').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Copy Link menu option should be visible
cy.findByText('Copy Link').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Add Members menu option should be visible
cy.findByText('Add Members').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Move to... menu option should be visible and is not destructive
cy.findByText('Move to...').should('be.visible').should('not.have.css', 'color', 'rgb(210, 75, 78)');
// * Leave Channel menu option should be visible and have a color (destructive)
cy.findByText('Leave Channel').should('be.visible').should('have.css', 'color', 'rgb(210, 75, 78)');
});
});
});

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
import type {Channel} from '@mattermost/types/channels';
import type {Team} from '@mattermost/types/teams';
import type {UserProfile} from '@mattermost/types/users';
import * as TIMEOUTS from '@/fixtures/timeouts';
describe('Leave channel', () => {
let testTeam: Team;
let testUser: UserProfile;
let testChannel: Channel;
before(() => {
cy.apiUpdateConfig({
ServiceSettings: {
ThreadAutoFollow: true,
CollapsedThreads: 'default_off',
},
});
// # Login as test user and visit created channel
cy.apiInitSetup().then(({team, user}) => {
testUser = user;
testTeam = team;
cy.apiSaveUserPreference([{
user_id: user.id,
category: 'crt_thread_pane_step',
name: user.id,
value: '0',
}], user.id);
cy.apiLogin(testUser);
cy.apiCreateChannel(testTeam.id, 'channel', 'channel').then(({channel}) => {
testChannel = channel;
});
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
it('MM-T4429_1 Leave a channel while RHS is open', () => {
// # Post a message in the channel
cy.postMessage('Test leave channel while RHS open');
cy.getLastPostId().then((id) => {
// # Open RHS
cy.clickPostCommentIcon(id);
// # Post a reply message
cy.postMessageAs({sender: testUser, message: 'another reply!', channelId: testChannel.id, rootId: id});
// * RHS should be visible
cy.get('#rhsContainer').should('be.visible');
// * RHS text box should be visible
cy.uiGetReplyTextBox();
// # Archive the channel
cy.uiLeaveChannel();
cy.wait(TIMEOUTS.TWO_SEC);
// * RHS should not be visible
cy.get('#rhsContainer').should('not.exist');
// * Assert that user is redirected to townsquare
cy.url().should('include', '/channels/town-square');
cy.get('#channelHeaderTitle').should('be.visible').and('contain', 'Town Square');
});
});
it('MM-T4429_2 Leave a channel while RHS is open and CRT on', () => {
// * Post text box should be visible
cy.uiGetPostTextBox();
// # Set CRT to on
cy.uiChangeCRTDisplaySetting('ON');
// # Post a message in the channel
cy.postMessage('Test leave channel while RHS open');
cy.getLastPostId().then((id) => {
// # Open RHS
cy.clickPostCommentIcon(id);
// # Post a reply message
cy.postMessageAs({sender: testUser, message: 'another reply!', channelId: testChannel.id, rootId: id});
// * RHS should be visible
cy.get('#rhsContainer').should('be.visible');
// * RHS text box should be visible
cy.uiGetReplyTextBox();
// # Archive the channel
cy.uiLeaveChannel();
cy.wait(TIMEOUTS.TWO_SEC);
// * RHS should not be visible
cy.get('#rhsContainer').should('not.exist');
// * Assert that user is redirected to townsquare
cy.url().should('include', '/channels/town-square');
cy.get('#channelHeaderTitle').should('be.visible').and('contain', 'Town Square');
});
});
});

View File

@@ -0,0 +1,207 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Group: @channels @channel @rhs
import * as TIMEOUTS from '@/fixtures/timeouts';
import * as MESSAGES from '@/fixtures/messages';
describe('Channel RHS', () => {
let testAdmin: Cypress.UserProfile;
let testTeam: Cypress.Team;
let testChannel: Cypress.Channel;
before(() => {
cy.apiCreateCustomAdmin({loginAfter: true}).then(({sysadmin}) => {
testAdmin = sysadmin;
cy.apiCreateTeam('team1', 'team1').then(({team}) => {
testTeam = team;
cy.apiAddUserToTeam(testTeam.id, testAdmin.id);
cy.apiCreateChannel(testTeam.id, 'channel', 'channel', 'O').then(({channel}) => {
testChannel = channel;
cy.apiAddUserToChannel(channel.id, testAdmin.id);
cy.apiSaveCRTPreference(testAdmin.id, 'off');
cy.apiLogin(testAdmin);
});
});
});
});
beforeEach(() => {
// # Go to test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
it('MM-44435 - should be able to open channel info, visit the system console and come back without issues -- KNOWN ISSUE: MM-47226', () => {
// # Click on the channel info button
cy.uiGetChannelInfoButton().click();
// * Verify that the channel info is opened in RHS
verifyRHSisOpenAndHasTitle('Info');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the channel info is still opened in RHS
verifyRHSisOpenAndHasTitle('Info');
});
it('MM-T5311 - should be able to open recent mentions, visit the system console and come back without issues', () => {
// # Click on the recent mentions button
cy.uiGetRecentMentionButton().click();
// * Verify that the recent mentions is opened in RHS
verifyRHSisOpenAndHasTitle('Recent Mentions');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the recent mentions is still opened in RHS
verifyRHSisOpenAndHasTitle('Recent Mentions');
});
it('MM-T5312 - should be able to open saved messages, visit the system console and come back without issues', () => {
// # Click on the saved messages button
cy.uiGetSavedPostButton().click();
// * Verify that the saved messages is opened in RHS
verifyRHSisOpenAndHasTitle('Saved messages');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the saved messages is still opened in RHS
verifyRHSisOpenAndHasTitle('Saved messages');
});
it('MM-T5313 - should be able to open Pinned messages, visit the system console and come back without issues', () => {
// # Click on the Pinned messages button
cy.uiGetChannelPinButton().click();
// * Verify that the Pinned messages is opened in RHS
verifyRHSisOpenAndHasTitle('Pinned messages');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the Pinned messages is still opened in RHS
verifyRHSisOpenAndHasTitle('Pinned messages');
});
it('MM-T5314 - should be able to open channel members, visit the system console and come back without issues', () => {
// # Click on the channel members button
cy.uiGetChannelMemberButton().click();
// * Verify that the channel members is opened in RHS
verifyRHSisOpenAndHasTitle('Members');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the channel members is still opened in RHS
verifyRHSisOpenAndHasTitle('Members');
});
it('MM-T5315 - should be able to open channel files, visit the system console and come back without issues', () => {
// # Click on the channel files button
cy.uiGetChannelFileButton().click();
// * Verify that the channel files is opened in RHS
verifyRHSisOpenAndHasTitle('Recent files');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the channel files is still opened in RHS
verifyRHSisOpenAndHasTitle('Recent files');
});
it('MM-T5316 - should be able to open search results, visit the system console and come back without issues', () => {
// # Post a message
cy.postMessage(MESSAGES.SMALL);
// # Enter the search terms and hit enter to start the search
cy.uiGetSearchContainer().click();
cy.uiGetSearchBox().clear().type(MESSAGES.TINY).type('{enter}');
// * Verify that the search results is opened in RHS
verifyRHSisOpenAndHasTitle('Search Results');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the search results is still opened in RHS
verifyRHSisOpenAndHasTitle('Search Results');
});
it('MM-T5317 - should be able to open thread reply, visit the system console and come back without issues', () => {
// # Post a message
cy.postMessage(MESSAGES.SMALL);
// # Click on the reply button
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// * Verify that the thread reply is opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the thread reply is still opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
});
it('MM-T5318 - should be able to open thread reply with CRT, visit the system console and come back without issues', () => {
// # Enable CRT
cy.apiSaveCRTPreference(testAdmin.id, 'on');
// # Refresh the page
cy.reload();
// # Post a message
cy.postMessage(MESSAGES.SMALL);
// # Click on the reply button
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
});
// * Verify that the thread reply is opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
// # visit the system console and leave it
openSystemConsoleAndLeave();
// * Verify that the thread reply is still opened in RHS
verifyRHSisOpenAndHasTitle('Thread');
});
});
function openSystemConsoleAndLeave() {
// # visit the system console...
cy.uiOpenProductMenu('System Console');
cy.wait(TIMEOUTS.THREE_SEC);
// # ...and leave it
cy.get('.backstage-navbar__back').click();
}
function verifyRHSisOpenAndHasTitle(title: string) {
cy.get('#sidebar-right').should('exist').within(() => {
cy.findByText(title).should('exist').and('be.visible');
});
}

View File

@@ -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 @channel
describe('Restrict Direct Message Channels', () => {
let testTeam: Cypress.Team;
let testUser: Cypress.UserProfile;
let otherUser: Cypress.UserProfile;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
RestrictDirectMessage: 'any',
},
});
// # Initialize setup
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create another user
cy.apiCreateUser().then(({user}) => {
otherUser = user;
cy.apiLogin(testUser);
});
});
});
it('should allow direct messages between any users when RestrictDirectMessage is set to "any"', () => {
// # Visit the town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open the direct messages modal
cy.findByLabelText('DIRECT MESSAGES').parents('.SidebarChannelGroup').within(() => {
cy.get('.SidebarChannelGroupHeader_addButton').click();
});
cy.get('#moreDmModal').should('exist');
// # Type the username of the other user in the search box
cy.get('#selectItems input').should('be.enabled').typeWithForce(otherUser.username);
// * Verify that the otherUser is specifically listed in the direct messages modal
cy.get('.more-modal__row').contains(otherUser.username).should('exist');
});
it('should restrict direct messages to team members when RestrictDirectMessage is set to "team"', () => {
// # set RestrictDirectMessage to 'team'
cy.apiAdminLogin().then(() => {
cy.apiUpdateConfig({
TeamSettings: {
RestrictDirectMessage: 'team',
},
}).then(() => {
cy.apiLogin(testUser);
});
});
// # Visit the town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open the direct messages modal
cy.findByLabelText('DIRECT MESSAGES').parents('.SidebarChannelGroup').within(() => {
cy.get('.SidebarChannelGroupHeader_addButton').click();
});
cy.get('#moreDmModal').should('exist');
// # Type the username of the other user in the search box
cy.get('#selectItems input').should('be.enabled').typeWithForce(otherUser.username);
// * Verify that no users are listed in the direct messages modal
cy.get('.more-modal__row').should('not.exist');
// # Add the other user back to the team
cy.apiAdminLogin();
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
// # Login as the test user again
cy.apiLogin(testUser);
// # Visit the town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open the direct messages modal
cy.findByLabelText('DIRECT MESSAGES').parents('.SidebarChannelGroup').within(() => {
cy.get('.SidebarChannelGroupHeader_addButton').click();
});
cy.get('#moreDmModal').should('exist');
// # Type the username of the other user in the search box
cy.get('#selectItems input').should('be.enabled').typeWithForce(otherUser.username);
// * Verify that the otherUser is specifically listed in the direct messages modal
cy.get('.more-modal__row').contains(otherUser.username).should('exist');
});
it('should not allow direct messages to users that no longer share a team', () => {
// # Login as sysadmin and set RestrictDirectMessage to 'team'
cy.apiAdminLogin();
cy.apiUpdateConfig({
TeamSettings: {
RestrictDirectMessage: 'team',
},
});
cy.apiAddUserToTeam(testTeam.id, otherUser.id);
// # Login as the test user
cy.apiLogin(testUser);
// # Visit the town-square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open a direct message with another user
cy.apiCreateDirectChannel([otherUser.id, testUser.id]).then((channel) => {
// # Visit the direct message channel
cy.visit(`/${testTeam.name}/messages/@${otherUser.username}`);
// # Verify that the channel is open and the user can send a message
cy.get('.channel-header').should('exist');
cy.get('#post_textbox').should('be.enabled');
// # Post a message in the direct message channel
cy.get('#post_textbox').type('Hello, this is a test message{enter}');
// # Verify the message is posted
cy.getLastPost().should('contain', 'Hello, this is a test message');
// # Remove the other user from the team
cy.apiAdminLogin();
cy.removeUserFromTeam(testTeam.id, otherUser.id);
// # Login as the test user again
cy.apiLogin(testUser);
// # Check if the direct message channel is still in the sidebar
cy.get(`#sidebarItem_${channel.channel.name}`).should('exist').click();
// # Verify that the channel is open and the user cannot send a message
cy.get('#post_textbox').should('not.exist');
cy.get('#noSharedTeamMessage').should('exist');
});
});
});

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @channel
describe('View Members modal', () => {
it('MM-20164 - Going from a Member to an Admin should update the modal', () => {
cy.apiInitSetup().then(({team, user}) => {
cy.apiCreateUser().then(({user: user1}) => {
cy.apiAddUserToTeam(team.id, user1.id);
});
// # Promote user as a system admin
// # Visit default channel and verify members modal
cy.apiLogin(user);
cy.externalUpdateUserRoles(user.id, 'system_user system_admin');
cy.visit(`/${team.name}/channels/town-square`);
verifyMemberDropdownAction(true);
// # Make user a regular member
// # Reload and verify members modal
cy.externalUpdateUserRoles(user.id, 'system_user');
cy.reload();
verifyMemberDropdownAction(false);
});
});
});
function verifyMemberDropdownAction(hasActionItem: boolean) {
// # Click member count to open member rhs
cy.get('#member_rhs').click();
cy.uiGetRHS().should('be.visible').within(() => {
// # Click "Manage"
cy.findByText('Manage').click();
});
// * Check to see any user has dropdown menu
if (hasActionItem) {
cy.uiGetRHS().findAllByText('Member').should('exist');
} else {
cy.uiGetRHS().findByText('Member').should('not.exist');
}
}

View File

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