Feature: Wrangler (#23602)
* Migrate feature/wrangler to mono-repo * Add wrangler files * Fix linters, types, etc * Fix snapshots * Fix playwright * Fix pipelines * Fix more pipeline * Fixes for pipelines * More changes for pipeline * Fix types * Add support for a feature flag, but leave it defaulted on for spinwick usage for now * Update snapshot * fix js error when removing last value of multiselect, support CSV marshaling to string array for textsetting * Fix linter * Remove TODO * Remove another TODO * fix tests * Fix i18n * Add server tests * Fix linter * Fix linter * Use proper icon for dot menu * Update snapshot * Add Cypress UI tests for various entrypoints to move thread modal, split SCSS out from forward post into its own thing * clean up * fix linter * More cleanup * Revert files to master * Fix linter for e2e tests * Make ForwardPostChannelSelect channel types configurable with a prop * Add missing return * Fixes from PR feedback * First batch of PR Feedback * Another batch of PR changes * Fix linter * Update snapshots * Wrangler system messages are translated to each user's locale * Initially translate Wrangler into system locale rather than initiating user * More fixes for PR Feedback * Fix some server tests * More updates with master. Fixes around pipelines. Enforce Enterprise license on front/back end * Add tests for dot_menu * More pipeline fixes * Fix e2etests prettier * Update cypress tests, change occurrences of 'Wrangler' with 'Move Thread' * Fix linter * Remove enterprise lock * A couple more occurrences of wrangler strings, and one more enterprise lock * Fix server tests * Fix i18n * Fix e2e linter * Feature flag shouldn't be on by default * Enable move threads feature in smoke tests (#25657) * enable move threads feature * add @prod tag * Fix move_thread_from_public_channel e2e test * Fix e2e style --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: yasserfaraazkhan <attitude3cena.yf@gmail.com>
Этот коммит содержится в:
@@ -66,6 +66,7 @@ services:
|
||||
MM_SERVICESETTINGS_ENABLEONBOARDINGFLOW: "false"
|
||||
MM_FEATUREFLAGS_ONBOARDINGTOURTIPS: "false"
|
||||
MM_SERVICEENVIRONMENT: "test"
|
||||
MM_FEATUREFLAGS_MOVETHREADSENABLED: "true"
|
||||
network_mode: host
|
||||
depends_on:
|
||||
$(for service in $ENABLED_DOCKER_SERVICES; do
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
// 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 @enterprise @messaging
|
||||
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
describe('Move Thread', () => {
|
||||
let user1;
|
||||
let user2;
|
||||
let testTeam;
|
||||
let dmChannel;
|
||||
let testPost;
|
||||
let replyPost;
|
||||
|
||||
const message = 'Move this message';
|
||||
const replyMessage = 'Move this reply';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.shouldHaveFeatureFlag('MoveThreadsEnabled', true);
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_on',
|
||||
},
|
||||
WranglerSettings: {
|
||||
MoveThreadFromDirectMessageChannelEnable: true,
|
||||
},
|
||||
});
|
||||
|
||||
// # Login as new user, create new team and visit its URL
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({
|
||||
user,
|
||||
team,
|
||||
}) => {
|
||||
user1 = user;
|
||||
testTeam = team;
|
||||
|
||||
// # enable CRT for the user
|
||||
cy.apiSaveCRTPreference(user.id, 'on');
|
||||
|
||||
// # Create another user
|
||||
return cy.apiCreateUser({prefix: 'second_'});
|
||||
}).then(({user}) => {
|
||||
user2 = user;
|
||||
|
||||
// # Add other user to team
|
||||
return cy.apiAddUserToTeam(testTeam.id, user2.id);
|
||||
}).then(() => {
|
||||
// # Create new DM channel
|
||||
return cy.apiCreateDirectChannel([user1.id, user2.id]);
|
||||
}).then(({channel}) => {
|
||||
dmChannel = channel;
|
||||
|
||||
// # Post a sample message
|
||||
return cy.postMessageAs({sender: user1, message, channelId: dmChannel.id});
|
||||
}).then((post) => {
|
||||
testPost = post.data;
|
||||
|
||||
// # Post a reply
|
||||
return cy.postMessageAs({sender: user1, message: replyMessage, channelId: dmChannel.id, rootId: testPost.id});
|
||||
}).then((post) => {
|
||||
replyPost = post.data;
|
||||
|
||||
// # Got to Test channel
|
||||
cy.visit(`/${testTeam.name}/channels/${dmChannel.name}`);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// # Go to 1. public channel
|
||||
cy.visit(`/${testTeam.name}/channels/${dmChannel.name}`);
|
||||
});
|
||||
|
||||
it('Move root post from DM', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # move thread
|
||||
movePostFromDM();
|
||||
|
||||
// * Assert switch to DM channel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', dmChannel.display_name);
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedThread({post: testPost});
|
||||
});
|
||||
|
||||
it('Move thread reply from DM', () => {
|
||||
// # Open the RHS with replies to the root post
|
||||
cy.uiClickPostDropdownMenu(testPost.id, 'Reply', 'CENTER');
|
||||
|
||||
// * Assert RHS is open
|
||||
cy.get('#rhsContainer').should('be.visible');
|
||||
|
||||
// # Click on ... button of reply post
|
||||
cy.clickPostDotMenu(replyPost.id, 'RHS_COMMENT');
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # move thread
|
||||
movePostFromDM();
|
||||
|
||||
// * Assert switch to DM channel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', dmChannel.display_name);
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedThread({post: testPost});
|
||||
});
|
||||
|
||||
it('Move post from DM - Cancel using escape key', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # move thread
|
||||
movePostFromDM({cancel: true});
|
||||
|
||||
// * Assert still in the DM channel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', dmChannel.display_name);
|
||||
|
||||
// * Assert last post id is identical with testPost
|
||||
cy.getLastPostId((id) => {
|
||||
assert.isEqual(id, testPost.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not be able to move post from DM if configured off', () => {
|
||||
cy.apiUpdateConfig({
|
||||
WranglerSettings: {
|
||||
MoveThreadFromDirectMessageChannelEnable: false,
|
||||
},
|
||||
});
|
||||
|
||||
// # Open the RHS with replies to the root post
|
||||
cy.uiClickPostDropdownMenu(testPost.id, 'Reply', 'CENTER');
|
||||
|
||||
// * Assert RHS is open
|
||||
cy.get('#rhsContainer').should('be.visible');
|
||||
|
||||
// # Click on ... button of reply post
|
||||
cy.clickPostDotMenu(replyPost.id, 'RHS_COMMENT');
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').should('not.exist');
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that the post has been moved
|
||||
*
|
||||
* @param {Post} post
|
||||
*/
|
||||
const verifyMovedThread = ({post}) => {
|
||||
// * Assert post has been moved
|
||||
cy.getLastPostId().then((id) => {
|
||||
// * Assert last post is visible
|
||||
cy.get(`#${id}_message`).should('be.visible').within(() => {
|
||||
// * Assert the text in the preview matches the original post message
|
||||
cy.get(`#postMessageText_${post.id}`).should('be.visible').should('contain.text', post.message);
|
||||
});
|
||||
|
||||
// # Cleanup
|
||||
cy.apiDeletePost(id);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* move thread
|
||||
*
|
||||
* @param {object?} options
|
||||
* @param {boolean?} options.cancel
|
||||
*/
|
||||
const movePostFromDM = ({cancel = false} = {}) => {
|
||||
// * Assert visibility of the move thread modal
|
||||
cy.get('#move-thread-modal').should('be.visible').within(() => {
|
||||
// * Assert channel select is not existent
|
||||
cy.get('.move-thread__select').should('not.exist');
|
||||
|
||||
// * Assert if button is enabled
|
||||
cy.get('.GenericModal__button.confirm').should('not.be.disabled');
|
||||
|
||||
// * Assert Notification is shown
|
||||
cy.findByTestId('notification-text').should('be.visible').should('contain.text', 'Moving this thread changes who has access');
|
||||
|
||||
if (cancel) {
|
||||
// * Assert if button is active
|
||||
cy.get('.MoveThreadModal__cancel-button').should('not.be.disabled').type('{esc}', {force: true});
|
||||
} else {
|
||||
// * Assert if button is active
|
||||
cy.get('.GenericModal__button.confirm').should('not.be.disabled').type('{enter}', {force: true});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,238 @@
|
||||
// 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 @enterprise @messaging
|
||||
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
describe('Move thread', () => {
|
||||
let user1;
|
||||
let user2;
|
||||
let user3;
|
||||
let testTeam;
|
||||
let gmChannel;
|
||||
let gmChannelName;
|
||||
let testPost;
|
||||
let replyPost;
|
||||
|
||||
const message = 'Move this message';
|
||||
const replyMessage = 'Move this reply';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.shouldHaveFeatureFlag('MoveThreadsEnabled', true);
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_on',
|
||||
},
|
||||
WranglerSettings: {
|
||||
MoveThreadFromGroupMessageChannelEnable: true,
|
||||
},
|
||||
});
|
||||
|
||||
// # Login as new user, create new team and visit its URL
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({
|
||||
user,
|
||||
team,
|
||||
}) => {
|
||||
user1 = user;
|
||||
testTeam = team;
|
||||
|
||||
// # enable CRT for the user
|
||||
cy.apiSaveCRTPreference(user.id, 'on');
|
||||
|
||||
// # Create another user
|
||||
return cy.apiCreateUser({prefix: 'second_'});
|
||||
}).then(({user}) => {
|
||||
user2 = user;
|
||||
|
||||
// # Add other user to team
|
||||
return cy.apiAddUserToTeam(testTeam.id, user2.id);
|
||||
}).then(() => {
|
||||
// # Create another user
|
||||
return cy.apiCreateUser({prefix: 'third_'});
|
||||
}).then(({user}) => {
|
||||
user3 = user;
|
||||
|
||||
// # Add other user to team
|
||||
return cy.apiAddUserToTeam(testTeam.id, user3.id);
|
||||
}).then(() => {
|
||||
// # Create new GM channel
|
||||
return cy.apiCreateGroupChannel([user1.id, user2.id, user3.id]);
|
||||
}).then(({channel}) => {
|
||||
gmChannel = channel;
|
||||
gmChannelName = gmChannel.display_name.split(', ').filter(((username) => username !== user1.username)).join(', ');
|
||||
|
||||
// # Post a sample message
|
||||
return cy.postMessageAs({sender: user1, message, channelId: gmChannel.id});
|
||||
}).then((post) => {
|
||||
testPost = post.data;
|
||||
|
||||
// # Post a reply
|
||||
return cy.postMessageAs({sender: user1, message: replyMessage, channelId: gmChannel.id, rootId: testPost.id});
|
||||
}).then((post) => {
|
||||
replyPost = post.data;
|
||||
|
||||
// # Got to Test channel
|
||||
cy.visit(`/${testTeam.name}/channels/${gmChannel.name}`);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// # Go to 1. public channel
|
||||
cy.visit(`/${testTeam.name}/channels/${gmChannel.name}`);
|
||||
});
|
||||
|
||||
it('Move post from GM (with at least 2 other users)', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # Move Post
|
||||
moveThreadFromGM();
|
||||
|
||||
// * Assert switch to GM channel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', gmChannelName);
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move thread with replies from GM (with at least 2 other users)', () => {
|
||||
// # Open the RHS with replies to the root post
|
||||
cy.uiClickPostDropdownMenu(testPost.id, 'Reply', 'CENTER');
|
||||
|
||||
// * Assert RHS is open
|
||||
cy.get('#rhsContainer').should('be.visible');
|
||||
|
||||
// # Click on ... button of reply post
|
||||
cy.clickPostDotMenu(replyPost.id, 'RHS_COMMENT');
|
||||
|
||||
// * Assert availability of the Move menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # Move Post
|
||||
moveThreadFromGM();
|
||||
|
||||
// * Assert switch to GM channel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', gmChannelName);
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move thread from GM (with at least 2 other users) - Cancel using X', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # Move Post
|
||||
moveThreadFromGM({cancel: true});
|
||||
|
||||
// * Assert switch to GM channel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', gmChannelName);
|
||||
|
||||
// * Assert last post id is identical with testPost
|
||||
cy.getLastPostId((id) => {
|
||||
assert.isEqual(id, testPost.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not be able to move post from GM if configured off', () => {
|
||||
cy.apiUpdateConfig({
|
||||
WranglerSettings: {
|
||||
MoveThreadFromGroupMessageChannelEnable: false,
|
||||
},
|
||||
});
|
||||
|
||||
// # Open the RHS with replies to the root post
|
||||
cy.uiClickPostDropdownMenu(testPost.id, 'Reply', 'CENTER');
|
||||
|
||||
// * Assert RHS is open
|
||||
cy.get('#rhsContainer').should('be.visible');
|
||||
|
||||
// # Click on ... button of reply post
|
||||
cy.clickPostDotMenu(replyPost.id, 'RHS_COMMENT');
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').should('not.exist');
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that the post has been moved
|
||||
*
|
||||
* @param {string?} comment
|
||||
* @param {boolean?} showMore
|
||||
* @param {Post} post
|
||||
*/
|
||||
const verifyMovedMessage = ({post, comment, showMore}) => {
|
||||
const permaLink = `${Cypress.config('baseUrl')}/${testTeam.name}/pl/${post.id}`;
|
||||
|
||||
// * Assert post has been moved
|
||||
cy.getLastPostId().then((id) => {
|
||||
// * Assert last post is visible
|
||||
cy.get(`#${id}_message`).should('be.visible').within(() => {
|
||||
if (comment) {
|
||||
// * Assert the text in the post body is the permalink only
|
||||
cy.get(`#postMessageText_${id}`).should('be.visible').should('contain.text', permaLink).should('contain.text', comment);
|
||||
|
||||
if (showMore) {
|
||||
// * Assert show more button is rendered and works as expected
|
||||
cy.get('#showMoreButton').should('be.visible').should('contain.text', 'Show more').click().should('contain.text', 'Show less').click();
|
||||
}
|
||||
}
|
||||
|
||||
// * Assert the text in the preview matches the original post message
|
||||
cy.get(`#postMessageText_${post.id}`).should('be.visible').should('contain.text', post.message);
|
||||
});
|
||||
|
||||
// # Cleanup
|
||||
cy.apiDeletePost(id);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Move thread
|
||||
*
|
||||
* @param {object?} options
|
||||
* @param {boolean?} options.cancel
|
||||
*/
|
||||
const moveThreadFromGM = ({cancel = false} = {}) => {
|
||||
// * Assert visibility of the move thread modal
|
||||
cy.get('#move-thread-modal').should('be.visible').within(() => {
|
||||
// * Assert channel select is not existent
|
||||
cy.get('.move-thread__select').should('not.exist');
|
||||
|
||||
// * Assert if button is enabled
|
||||
cy.get('.GenericModal__button.confirm').should('not.be.disabled');
|
||||
|
||||
// * Assert Notification is shown
|
||||
cy.findByTestId('notification-text').should('be.visible').should('contain.text', 'Moving this thread changes who has access');
|
||||
|
||||
if (cancel) {
|
||||
// * Assert if button is active
|
||||
cy.uiCloseModal('Move thread');
|
||||
} else {
|
||||
// * Assert if button is active
|
||||
cy.get('.GenericModal__button.confirm').should('not.be.disabled').type('{enter}', {force: true});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @channels @enterprise @messaging
|
||||
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
describe('Move thread', () => {
|
||||
let user1;
|
||||
let testTeam;
|
||||
let privateChannel;
|
||||
let testPost;
|
||||
let replyPost;
|
||||
|
||||
const message = 'Move this message';
|
||||
const replyMessage = 'Move this reply';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.apiRequireLicense();
|
||||
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_on',
|
||||
},
|
||||
WranglerSettings: {
|
||||
MoveThreadFromPrivateChannelEnable: true,
|
||||
},
|
||||
});
|
||||
|
||||
// # Login as new user, create new team and visit its URL
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({
|
||||
user,
|
||||
team,
|
||||
}) => {
|
||||
user1 = user;
|
||||
testTeam = team;
|
||||
|
||||
// # enable CRT for the user
|
||||
cy.apiSaveCRTPreference(user.id, 'on');
|
||||
|
||||
// # Create a private channel
|
||||
return cy.apiCreateChannel(testTeam.id, 'private', 'Private', 'P');
|
||||
}).then(({channel}) => {
|
||||
privateChannel = channel;
|
||||
|
||||
// # Post a sample message
|
||||
return cy.postMessageAs({sender: user1, message, channelId: privateChannel.id});
|
||||
}).then((post) => {
|
||||
testPost = post.data;
|
||||
|
||||
// # Post a reply
|
||||
return cy.postMessageAs({sender: user1, message: replyMessage, channelId: privateChannel.id, rootId: testPost.id});
|
||||
}).then((post) => {
|
||||
replyPost = post.data;
|
||||
|
||||
// # Got to Private channel
|
||||
cy.visit(`/${testTeam.name}/channels/${privateChannel.name}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('Move root post from private channel', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # Move Thread
|
||||
moveThreadFromPrivateChannel();
|
||||
|
||||
// * Assert switch to testchannel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', privateChannel.display_name);
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move thread with replies from private channel', () => {
|
||||
// # Open the RHS with replies to the root post
|
||||
cy.uiClickPostDropdownMenu(testPost.id, 'Reply', 'CENTER');
|
||||
|
||||
// * Assert RHS is open
|
||||
cy.get('#rhsContainer').should('be.visible');
|
||||
|
||||
// # Click on ... button of reply post
|
||||
cy.clickPostDotMenu(replyPost.id, 'RHS_COMMENT');
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # Move Thread
|
||||
moveThreadFromPrivateChannel();
|
||||
|
||||
// * Assert switch to testchannel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', privateChannel.display_name);
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move post from private channel - Cancel', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').type('{shift}W');
|
||||
|
||||
// # Move Thread
|
||||
moveThreadFromPrivateChannel(true);
|
||||
|
||||
// * Assert switch to testchannel
|
||||
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.HALF_MIN}).should('be.visible').should('contain', privateChannel.display_name);
|
||||
|
||||
// * Assert last post id is identical with testPost
|
||||
cy.getLastPostId((id) => {
|
||||
assert.isEqual(id, testPost.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should not be able to move post from private channel if configured off', () => {
|
||||
cy.apiUpdateConfig({
|
||||
WranglerSettings: {
|
||||
MoveThreadFromPrivateChannelEnable: false,
|
||||
},
|
||||
});
|
||||
|
||||
// # Open the RHS with replies to the root post
|
||||
cy.uiClickPostDropdownMenu(testPost.id, 'Reply', 'CENTER');
|
||||
|
||||
// * Assert RHS is open
|
||||
cy.get('#rhsContainer').should('be.visible');
|
||||
|
||||
// # Click on ... button of reply post
|
||||
cy.clickPostDotMenu(replyPost.id, 'RHS_COMMENT');
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').should('not.exist');
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that the post has been moved
|
||||
*
|
||||
* @param {Post} post
|
||||
*/
|
||||
const verifyMovedMessage = ({post}) => {
|
||||
// * Assert post has been moved
|
||||
cy.getLastPostId().then((id) => {
|
||||
// * Assert last post is visible
|
||||
cy.get(`#${id}_message`).should('be.visible').within(() => {
|
||||
// * Assert the text in the preview matches the original post message
|
||||
cy.get(`#postMessageText_${post.id}`).should('be.visible').should('contain.text', post.message);
|
||||
});
|
||||
|
||||
// # Cleanup
|
||||
cy.apiDeletePost(id);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Move thread
|
||||
*
|
||||
* @param {boolean?} cancel
|
||||
*/
|
||||
const moveThreadFromPrivateChannel = (cancel = false) => {
|
||||
// * Assert visibility of the move thread modal
|
||||
cy.get('#move-thread-modal').should('be.visible').within(() => {
|
||||
// * Assert channel select is not existent
|
||||
cy.get('.move-thread__select').should('not.exist');
|
||||
|
||||
// * Assert if button is enabled
|
||||
cy.get('.GenericModal__button.confirm').should('not.be.disabled');
|
||||
|
||||
// * Assert Notification is shown
|
||||
cy.findByTestId('notification-text').should('be.visible').should('contain.text', 'Moving this thread changes who has access');
|
||||
|
||||
if (cancel) {
|
||||
// * Assert if button is active
|
||||
cy.get('.MoveThreadModal__cancel-button').should('not.be.disabled').click();
|
||||
} else {
|
||||
// * Assert if button is active
|
||||
cy.get('.GenericModal__button.confirm').should('not.be.disabled').click();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,230 @@
|
||||
// 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 @enterprise @messaging
|
||||
|
||||
describe('Move Thread', () => {
|
||||
let user1;
|
||||
let user2;
|
||||
let user3;
|
||||
let testTeam;
|
||||
let testChannel;
|
||||
let otherChannel;
|
||||
let privateChannel;
|
||||
let dmChannel;
|
||||
let gmChannel;
|
||||
let testPost;
|
||||
let replyPost;
|
||||
|
||||
const message = 'Move this message';
|
||||
const replyMessage = 'Move this reply';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.apiUpdateConfig({
|
||||
ServiceSettings: {
|
||||
ThreadAutoFollow: true,
|
||||
CollapsedThreads: 'default_on',
|
||||
},
|
||||
});
|
||||
|
||||
// # Login as new user, create new team and visit its URL
|
||||
cy.apiInitSetup({loginAfter: true, promoteNewUserAsAdmin: true}).then(({
|
||||
user,
|
||||
team,
|
||||
channel,
|
||||
}) => {
|
||||
user1 = user;
|
||||
testTeam = team;
|
||||
testChannel = channel;
|
||||
|
||||
// # enable CRT for the user
|
||||
cy.apiSaveCRTPreference(user.id, 'on');
|
||||
|
||||
// # Create another user
|
||||
return cy.apiCreateUser({prefix: 'second_'});
|
||||
}).then(({user}) => {
|
||||
user2 = user;
|
||||
|
||||
// # Add other user to team
|
||||
return cy.apiAddUserToTeam(testTeam.id, user2.id);
|
||||
}).then(() => {
|
||||
// # Create another user
|
||||
return cy.apiCreateUser({prefix: 'third_'});
|
||||
}).then(({user}) => {
|
||||
user3 = user;
|
||||
|
||||
// # Add other user to team
|
||||
return cy.apiAddUserToTeam(testTeam.id, user3.id);
|
||||
}).then(() => {
|
||||
cy.apiAddUserToChannel(testChannel.id, user2.id);
|
||||
cy.apiAddUserToChannel(testChannel.id, user3.id);
|
||||
|
||||
// # Post a sample message
|
||||
return cy.postMessageAs({sender: user1, message, channelId: testChannel.id});
|
||||
}).then((post) => {
|
||||
testPost = post.data;
|
||||
|
||||
// # Post a reply
|
||||
return cy.postMessageAs({sender: user1, message: replyMessage, channelId: testChannel.id, rootId: testPost.id});
|
||||
}).then((post) => {
|
||||
replyPost = post.data;
|
||||
|
||||
// # Create new DM channel
|
||||
return cy.apiCreateDirectChannel([user1.id, user2.id]);
|
||||
}).then(({channel}) => {
|
||||
dmChannel = channel;
|
||||
|
||||
// # Create new DM channel
|
||||
return cy.apiCreateGroupChannel([user1.id, user2.id, user3.id]);
|
||||
}).then(({channel}) => {
|
||||
gmChannel = channel;
|
||||
console.log('this one');
|
||||
|
||||
// # Create a private channel to Move Thread to
|
||||
return cy.apiCreateChannel(testTeam.id, 'private', 'Private');
|
||||
}).then(({channel}) => {
|
||||
privateChannel = channel;
|
||||
console.log('no, this one');
|
||||
|
||||
// # Create a second channel to Move Thread to
|
||||
return cy.apiCreateChannel(testTeam.id, 'movethread', 'Move Thread');
|
||||
}).then(({channel}) => {
|
||||
otherChannel = channel;
|
||||
|
||||
// # Got to Test channel
|
||||
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('Move root post from public channel to another public channel', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').click();
|
||||
|
||||
// # Move Thread
|
||||
moveThread({channelId: otherChannel.id});
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move reply post from public channel to another public channel', () => {
|
||||
// # Open the RHS with replies to the root post
|
||||
cy.uiClickPostDropdownMenu(testPost.id, 'Reply', 'CENTER');
|
||||
|
||||
// * Assert RHS is open
|
||||
cy.get('#rhsContainer').should('be.visible');
|
||||
|
||||
// # Click on ... button of reply post
|
||||
cy.clickPostDotMenu(replyPost.id, 'RHS_COMMENT');
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').click();
|
||||
|
||||
// * Move Thread
|
||||
moveThread({channelId: otherChannel.id});
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move public channel post to Private channel', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').click();
|
||||
|
||||
// # Move Thread
|
||||
moveThread({channelId: privateChannel.id});
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move public channel post to GM', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').click();
|
||||
|
||||
// # Move Thread
|
||||
moveThread({channelId: gmChannel.id});
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
it('Move public channel post to DM', () => {
|
||||
// # Check if ... button is visible in last post right side
|
||||
cy.get(`#CENTER_button_${testPost.id}`).should('not.be.visible');
|
||||
|
||||
// # Click on ... button of last post
|
||||
cy.clickPostDotMenu(testPost.id);
|
||||
|
||||
// * Assert availability of the Move Thread menu-item
|
||||
cy.findByText('Move Thread').click();
|
||||
|
||||
// # Move Thread
|
||||
moveThread({channelId: dmChannel.id});
|
||||
|
||||
// * Assert post has been moved
|
||||
verifyMovedMessage({post: testPost});
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that the post has been moved
|
||||
*
|
||||
* @param {Post} post
|
||||
*/
|
||||
const verifyMovedMessage = ({post}) => {
|
||||
// * Assert post has been moved
|
||||
cy.getLastPostId().then((id) => {
|
||||
// * Assert last post is visible
|
||||
cy.get(`#${id}_message`).should('be.visible').within(() => {
|
||||
// * Assert the text in the preview matches the original post message
|
||||
cy.get(`#postMessageText_${post.id}`).should('be.visible').should('contain.text', post.message);
|
||||
});
|
||||
|
||||
// # Cleanup
|
||||
cy.apiDeletePost(id);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Move Thread with optional comment.
|
||||
*
|
||||
*/
|
||||
const moveThread = () => {
|
||||
// * Assert visibility of the Move Thread modal
|
||||
cy.get('#move-thread-modal').should('be.visible').within(() => {
|
||||
// * Assert channel select is not existent
|
||||
cy.get('.move-thread__select').should('not.exist');
|
||||
|
||||
// * Assert if button is enabled
|
||||
cy.get('.GenericModal__button.confirm').should('not.be.disabled');
|
||||
|
||||
// * Assert Notification is shown
|
||||
cy.findByTestId('notification-text').should('be.visible').should('contain.text', 'Moving this thread changes who has access');
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -714,4 +714,13 @@ const defaultServerConfig: AdminConfig = {
|
||||
Directory: './export',
|
||||
RetentionDays: 30,
|
||||
},
|
||||
WranglerSettings: {
|
||||
PermittedWranglerRoles: [],
|
||||
AllowedEmailDomain: [],
|
||||
MoveThreadMaxCount: 30,
|
||||
MoveThreadToAnotherTeamEnable: true,
|
||||
MoveThreadFromPrivateChannelEnable: true,
|
||||
MoveThreadFromDirectMessageChannelEnable: true,
|
||||
MoveThreadFromGroupMessageChannelEnable: true,
|
||||
},
|
||||
};
|
||||
|
||||
Ссылка в новой задаче
Block a user