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
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:
@@ -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}');
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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)');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user