Этот коммит содержится в:
Mario Vitale
2023-03-27 16:28:42 +02:00
родитель da7a6825ce
Коммит ba6b97fb62
1142 изменённых файлов: 44 добавлений и 44 удалений

Просмотреть файл

@@ -0,0 +1,70 @@
// 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 @incoming_webhook
describe('Integrations/Incoming Webhook', () => {
let incomingWebhook;
let testChannel;
before(() => {
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel}) => {
testChannel = channel;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: true,
description: 'Incoming webhook - attachment does not collapse',
display_name: 'attachment-does-not-collapse',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T642 Attachment does not collapse', () => {
// # Post the incoming webhook with a text attachment (lorem ipsum test text)
const content = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vel convallis arcu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur id convallis lectus. Quisque ut laoreet augue, et suscipit magna. Etiam ut interdum nunc. Nam euismod felis eu ipsum eleifend, eget rhoncus arcu fringilla. Nam id laoreet eros, a bibendum diam. Donec sed augue vel tortor porta pulvinar. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Proin interdum, nunc in tempor molestie, dui erat facilisis tellus, ut faucibus mauris est et felis. Suspendisse pulvinar mauris vel viverra pulvinar. Maecenas iaculis euismod mauris, id pharetra justo rutrum et.' +
'Donec sit amet nulla varius, posuere enim sit amet, venenatis sapien. Morbi venenatis ornare urna id vestibulum. Curabitur efficitur efficitur arcu, vel rhoncus lorem varius sed. Ut venenatis interdum arcu, et rutrum est pretium eu. Nam laoreet tincidunt cursus. Pellentesque feugiat sit amet ipsum a porta. Phasellus nec laoreet nulla. Duis gravida dolor orci, vitae mollis orci consequat at. Sed tincidunt dolor nisi, at fermentum ligula tristique non. Duis pulvinar, eros quis ultrices aliquam, libero ipsum lobortis leo, quis ullamcorper sapien sem vel magna. Etiam sed ligula ut ipsum luctus venenatis. Sed mollis convallis dolor, eu dictum leo condimentum id. Praesent porttitor neque in volutpat iaculis.' +
'Vestibulum fermentum, elit vel vestibulum vestibulum, lectus odio tincidunt leo, quis gravida erat tortor non arcu. Donec condimentum accumsan dolor eget tempus. Pellentesque convallis porta mattis. Aenean pulvinar felis tincidunt, finibus felis at, imperdiet massa. Duis sed pellentesque urna, finibus tristique risus. In quam magna, commodo nec commodo ut, consectetur non tortor. Cras accumsan faucibus arcu, quis suscipit purus posuere ac. Nunc at urna nec massa bibendum posuere. Pellentesque at rhoncus eros. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec a maximus ipsum. Phasellus sed venenatis lacus, a vestibulum massa. Nunc rutrum nunc et dui porta aliquam. In ac eros mattis, congue nisi ut, rhoncus lacus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nec mauris at erat vehicula sollicitudin.' +
'Aliquam ornare sed tortor ut placerat. Fusce posuere a odio nec aliquet. Cras nec maximus metus. In elementum tincidunt orci, at sagittis nisl. Pellentesque scelerisque lorem ultricies ipsum finibus, in iaculis purus tincidunt. Aliquam tempus nunc at elementum vehicula. Integer tempus pretium magna, sed gravida nisl porta at. Donec et imperdiet augue, eget cursus dolor. Sed non magna dui. Phasellus vel massa pulvinar, cursus diam sit amet, vestibulum neque. Vivamus accumsan, mi vitae ultrices pretium, arcu eros sodales enim, et pellentesque quam eros in ligula. In eu justo a quam iaculis consequat. Aenean ornare a velit ac aliquet. Nullam lobortis posuere neque a pretium.' +
'Etiam dignissim sed ante commodo faucibus. Nunc vitae aliquet justo. Proin consequat leo vel libero porttitor, ac vulputate turpis bibendum. In vel libero sed odio euismod tincidunt id non dui. Quisque vitae est quis ante eleifend rutrum sed ut diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Integer rhoncus, leo nec iaculis sagittis, ligula mauris consequat felis, nec finibus odio sem tincidunt dolor. Pellentesque vel purus a sem rhoncus porta eget et erat. Sed interdum, justo ac dictum lacinia, nibh metus posuere arcu, vel euismod eros libero semper ligula. Proin elementum ligula quis ornare auctor. Integer vitae elementum augue, in congue lorem. Sed felis purus, consequat eu lacus in, fermentum accumsan diam. Duis lacus nunc, accumsan varius consequat nec, tincidunt et odio.';
const payload = {
channel: testChannel.name,
attachments: [{fallback: 'testing attachment does not collapse', pretext: 'testing attachment does not collapse', text: content}],
};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload, waitFor: 'attachment-pretext'});
// * Check "show more" button is visible and click
cy.getLastPostId().then((postId) => {
const postMessageId = `#${postId}_message`;
cy.get(postMessageId).within(() => {
cy.get('#showMoreButton').scrollIntoView().should('be.visible').and('have.text', 'Show more').click();
});
});
// # Type /collapse and press Enter
cy.uiGetPostTextBox().type('/collapse {enter}');
// * Check that the post from the webhook has NOT collapsed (verify expanded post)
cy.getNthPostId(-2).then((postId) => {
const postMessageId = `#${postId}_message`;
cy.get(postMessageId).within(() => {
// * Verify "show more" button says "Show less"
cy.get('#showMoreButton').scrollIntoView().should('be.visible').and('have.text', 'Show less');
});
});
});
});

Просмотреть файл

@@ -0,0 +1,128 @@
// 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 @incoming_webhook
describe('Incoming webhook', () => {
let testChannel;
let otherUser;
let incomingWebhook;
before(() => {
cy.apiUpdateConfig({
ServiceSettings: {
EnablePostUsernameOverride: true,
EnablePostIconOverride: true,
},
});
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel, user}) => {
testChannel = channel;
otherUser = user;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: true,
description: 'Incoming webhook - basic formatting',
display_name: 'basic-formatting',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
cy.visit(`/${team.name}/channels/${channel.name}`);
cy.postMessage('Test message');
});
});
it('MM-T619 Webhook with @-mention, username and profile pic, and basic formatting', () => {
const baseUrl = Cypress.config('baseUrl');
const payload = getPayload(testChannel, otherUser);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.waitUntil(() => cy.getLastPost().then((el) => {
const postedMessageEl = el.find('.post-message__text > p')[0];
return Boolean(postedMessageEl && postedMessageEl.textContent.includes('The following escaped characters should appear normally'));
}));
cy.getLastPost().within((el) => {
// * Verify that the username is overridden per webhook payload
cy.get('.post__header').find('.user-popover').should('have.text', payload.username);
// * Verify that the user icon is overridden per webhook payload
const encodedIconUrl = encodeURIComponent(payload.icon_url);
cy.get('.profile-icon > img').should('have.attr', 'src', `${baseUrl}/api/v4/image?url=${encodedIconUrl}`);
// * Verify that the BOT label appears
cy.get('.Tag').should('be.visible').and('have.text', 'BOT');
// * Verify that there's no status indicator
cy.get('.status').should('not.exist');
// # Verify that the elements on posted message matched as expected
cy.get('.post-message__text').within(() => {
cy.wrap(el).should('contain', 'The following escaped characters should appear normally');
cy.wrap(el).should('contain', '(ampersand, open angle, close angle): & < >');
cy.wrap(el).should('contain', 'The following should appear as links:');
cy.get('.markdown__link').eq(0).
should('have.text', 'This is a link to about-dot-mattermost-dot-com').
and('have.attr', 'href', 'https://mattermost.com/');
cy.get('.markdown__link').eq(1).
should('have.text', 'Markdown Link also to About page').
and('have.attr', 'href', 'https://mattermost.com/');
cy.wrap(el).should('contain', 'Normal Link:');
cy.get('.markdown__link').eq(2).
should('have.text', 'https://mattermost.com/').
and('have.attr', 'href', 'https://mattermost.com/');
cy.wrap(el).should('contain', 'Mail Link:');
cy.get('.markdown__link').eq(3).
should('have.text', 'Email').
and('have.attr', 'href', 'mailto:mail@example.com');
cy.wrap(el).should('contain', 'The following should be markdown formatted');
cy.wrap(el).should('contain', '(mouse emoji, strawberry emoji, then formatting as indicated):');
cy.get('.emoticon').eq(0).parent().
should('have.html', `<span alt=":hamster:" class="emoticon" title=":hamster:" style="background-image: url(&quot;${baseUrl}/static/emoji/1f439.png&quot;);">:hamster:</span>`);
cy.get('.emoticon').eq(1).parent().
should('have.html', `<span alt=":strawberry:" class="emoticon" title=":strawberry:" style="background-image: url(&quot;${baseUrl}/static/emoji/1f353.png&quot;);">:strawberry:</span>`);
cy.wrap(el).find('strong').should('have.text', 'bold');
cy.wrap(el).find('em').should('have.text', 'italic');
cy.wrap(el).find('strong').should('have.text', 'bold');
cy.get('.codespan__pre-wrap').should('have.html', '<code>code</code>');
cy.wrap(el).find('del').should('have.text', 'strike');
cy.get('.mention-link').eq(0).should('have.text', '#hashtag');
cy.get('.mention-link').eq(1).should('have.text', `@${otherUser.username}`);
});
});
});
});
function getPayload(channel, user) {
const text = `The following escaped characters should appear normally
(ampersand, open angle, close angle): &amp; &lt; &gt;
The following should appear as links:
<https://mattermost.com/|This is a link to about-dot-mattermost-dot-com>
[Markdown Link also to About page](https://mattermost.com/)
Normal Link: https://mattermost.com/
Mail Link: <mailto:mail@example.com|Email>
The following should be markdown formatted
(mouse emoji, strawberry emoji, then formatting as indicated): 🐹 :strawberry: **bold** _italic_ \`code\` ~~strike~~ #hashtag
The following should turn into a user mention and clicking it should open profile popover
@${user.username}
`;
return {
channel: channel.name,
username: 'new_username',
text,
icon_url: 'https://mattermost.com/wp-content/uploads/2022/02/icon_WS.png',
};
}

Просмотреть файл

@@ -0,0 +1,52 @@
// 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 @integrations
describe('Integrations', () => {
let newIncomingHook;
before(() => {
// # Create test team, channel, and webhook
cy.apiInitSetup().then(({team, channel}) => {
newIncomingHook = {
channel_id: channel.id,
channel_locked: true,
description: 'Test Webhook Description',
display_name: 'Test Webhook Name',
};
//# Create a new webhook
cy.apiCreateWebhook(newIncomingHook);
// # Visit the webhook page
cy.visit(`/${team.name}/integrations/incoming_webhooks`);
});
});
it('MM-T640 Cancel out of edit', () => {
// # Make an edit to the webhook
cy.findByText('Edit').click();
cy.get('#displayName').type('name changed');
cy.get('#description').type('description changed ');
cy.get('#channelSelect').select('Town Square');
cy.get('#channelLocked').uncheck();
//# Click cancel to cancel the edits
cy.findByText('Cancel').click();
// # Assert the webhook's previous values are present
cy.findAllByText(newIncomingHook.display_name).should('be.visible');
cy.findAllByText(newIncomingHook.description).should('be.visible');
cy.findByText('Delete').should('be.visible');
cy.findByText('Edit').click();
cy.get('#channelLocked').should('be.checked');
});
});

Просмотреть файл

@@ -0,0 +1,71 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
describe('Incoming webhook', () => {
before(() => {
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
EnableIncomingWebhooks: true,
},
};
cy.apiUpdateConfig(newSettings);
cy.apiInitSetup().then(({team}) => {
// # Go to integrations
cy.visit(`/${team.name}/integrations`);
// * Validate that incoming webhooks are enabled
cy.get('#incomingWebhooks').should('be.visible');
});
});
it('MM-T637 Copy icon for Incoming Webhook URL', () => {
const title = 'test-title';
const description = 'test-description';
const channel = 'Town Square';
cy.get('#incomingWebhooks').should('be.visible').click();
// # For this test purpose, fill in "Title", "Description" and select a "channel" with some test data
cy.findByText('Add Incoming Webhook').should('be.visible').click();
cy.findByLabelText('Title').should('be.visible').type(title);
cy.findByLabelText('Description').should('be.visible').type(description);
cy.get('#channelSelect').should('be.visible').select(channel);
// # Scroll down and click "Save"
cy.findByText('Save').should('be.visible').click();
cy.findByText('Setup Successful').should('be.visible');
// * You should see a "copy" icon to the right of the URL in the "Setup Successful" screen
copyIconIsVisible('.backstage-form__confirmation');
// # Click "Done" in the "Setup Successful" screen
cy.findByText('Done').should('be.visible').click();
// # You should see a "copy" icon to the right of the webhook's URL
copyIconIsVisible('.item-details__url');
});
});
function copyIconIsVisible(element) {
cy.get(element).within(() => {
cy.get('.fa.fa-copy').
should('be.visible').
trigger('mouseover').
should('have.attr', 'aria-describedby', 'copy');
});
}

Просмотреть файл

@@ -0,0 +1,122 @@
// 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 @incoming_webhook
import {enableUsernameAndIconOverride} from './helpers';
describe('Incoming webhook', () => {
let testTeam;
let testChannel;
let siteName;
before(() => {
cy.apiGetConfig().then(({config}) => {
siteName = config.TeamSettings.SiteName;
});
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
testChannel = channel;
});
});
it('MM-T645 Delete Incoming Webhook', () => {
// # Enable username and icon override at system console
cy.apiAdminLogin();
enableUsernameAndIconOverride(true);
// # Go to test team/channel, open product menu and click "Integrations"
cy.visit(`${testTeam.name}/channels/${testChannel.name}`);
cy.uiOpenProductMenu('Integrations');
// * Verify that it redirects to integrations URL. Then, click "Incoming Webhooks"
cy.url().should('include', `${testTeam.name}/integrations`);
cy.get('.backstage-sidebar').should('be.visible').findByText('Incoming Webhooks').click();
// * Verify that it redirects to incoming webhooks URL. Then, click "Add Incoming Webhook"
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
cy.findByText('Add Incoming Webhook').click();
// * Verify that it redirects to where it can add incoming webhook
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks/add`);
// # Enter webhook details such as title, description and channel, then save
cy.get('.backstage-form').should('be.visible').within(() => {
cy.get('#displayName').type('Webhook Title');
cy.get('#description').type('Webhook Description');
cy.get('#channelSelect').select(testChannel.display_name);
cy.findByText('Save').scrollIntoView().click();
});
// * Verify that it redirects to incoming webhook confirmation URL
cy.url().should('include', `${testTeam.name}/integrations/confirm?type=incoming_webhooks&id=`).
invoke('toString').then((hookConfirmationUrl) => {
const hookId = hookConfirmationUrl.split('id=')[1];
const hookUrl = `${Cypress.config('baseUrl')}/hooks/${hookId}`;
// * Verify that the hook ID in the URL matches with the one shown in a page
// * Verify that the copy link is shown.
cy.findByText(hookUrl).should('be.visible').
parent().siblings('.fa-copy').should('be.visible');
// # Click "Done" and verify that it redirects to incoming webhooks URL
cy.findByText('Done').click();
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
// # Click back to site name and verify that it redirects to test team/channel
cy.findByText(`Back to ${siteName}`).click();
cy.url().should('include', `${testTeam.name}/channels/${testChannel.name}`);
// # Post an incoming webhook and verify that it is posted in the channel
const payload = {
channel: testChannel.name,
username: 'new-username',
text: 'Setup for incoming webhook.',
};
cy.postIncomingWebhook({url: hookUrl, data: payload, waitFor: 'text'});
// * Verify the message is successfully posted as last post
cy.getLastPostId().then((postId) => {
cy.get(`#${postId}_message`).should('exist').within(() => {
// * Check if message text
cy.findByText('Setup for incoming webhook.').should('exist');
});
});
// # Click 'Integrations' at product menu
cy.uiOpenProductMenu('Integrations');
// * Verify that it redirects to integrations URL. Then, click "Incoming Webhooks"
cy.url().should('include', `${testTeam.name}/integrations`);
cy.get('.backstage-sidebar').should('be.visible').findByText('Incoming Webhooks').click();
// * Verify that it redirects to incoming webhooks URL. Then, click "Delete" and then confirm "Delete"
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
cy.findByText('Delete').click();
cy.get('#confirmModalButton > span').click();
// # Click back to site name and verify that it redirects to test team/channel
cy.findByText(`Back to ${siteName}`).click();
cy.url().should('include', `${testTeam.name}/channels/${testChannel.name}`);
// # Post an incoming webhook and verify that it is posted in the channel
const payload1 = {
channel: testChannel.name,
username: 'new-username',
text: 'after deleting incoming webhook.',
};
cy.task('postIncomingWebhook', {url: hookUrl, data: payload1}).then((res) => {
// * Verify that it failed to post
expect(res.status).equal(400);
expect(res.data.message).equal('Invalid webhook.');
});
});
});
});

Просмотреть файл

@@ -0,0 +1,39 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @integrations
describe('Integrations', () => {
const maxDescription = '1234567890'.repeat(50);
const overMaxDescription = `${maxDescription}123`;
let testTeam;
before(() => {
// # Login as test user and visit the newly created test channel
cy.apiInitSetup().then(({team}) => {
testTeam = team;
// # Visit the Incoming Webhooks add page
cy.visit(`/${team.name}/integrations/incoming_webhooks/add`);
});
});
it('MM-T636 Description field length check', () => {
// * Check incoming description field only accepts 500 characters
cy.get('#description').clear().type(maxDescription).should('have.value', maxDescription);
cy.get('#description').clear().type(overMaxDescription).should('have.value', maxDescription);
// * Check outgoing description field only accepts 500 characters
cy.visit(`/${testTeam.name}/integrations/outgoing_webhooks/add`);
cy.get('#description').clear().type(maxDescription).should('have.value', maxDescription);
cy.get('#description').clear().type(overMaxDescription).should('have.value', maxDescription);
});
});

Просмотреть файл

@@ -0,0 +1,122 @@
// 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 @incoming_webhook
import {getRandomId} from '../../../../utils';
import * as TIMEOUTS from '../../../../fixtures/timeouts';
import {enableUsernameAndIconOverride} from './helpers';
describe('Incoming webhook', () => {
let sysadmin;
let testTeam;
let testChannel;
let testUser;
let incomingWebhook;
before(() => {
cy.apiGetMe().then(({user}) => {
sysadmin = user;
});
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
const newHook = {
channel_id: testChannel.id,
channel_locked: true,
description: 'Incoming webhook - override',
display_name: `incoming-override-${getRandomId()}`,
};
cy.apiCreateWebhook(newHook).then((hook) => {
incomingWebhook = hook;
});
});
});
it('MM-T622 Disallow override of username and profile picture', () => {
const iconUrl = 'https://mattermost.com/wp-content/uploads/2022/02/icon_WS.png';
// # Enable username and icon override
cy.apiAdminLogin();
enableUsernameAndIconOverride(true);
// # Login as test user, visit test channel and post any message
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').and('have.text', testChannel.display_name);
cy.postMessage('a');
// # Post an incoming webhook
const payload1 = getPayload(testChannel, iconUrl);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload1, waitFor: 'text'});
cy.getLastPost().within(() => {
// * Verify that the message is posted via incoming webhook
cy.findByText(payload1.text).should('be.visible');
// * Verify that the username is overridden per webhook payload
cy.get('.post__header').find('.user-popover').should('have.text', payload1.username);
// * Verify that the user icon is overridden per webhook payload
const encodedIconUrl = encodeURIComponent(payload1.icon_url);
cy.get('.profile-icon > img').should('have.attr', 'src', `${Cypress.config('baseUrl')}/api/v4/image?url=${encodedIconUrl}`);
});
// # Disable username and icon override
cy.apiAdminLogin();
enableUsernameAndIconOverride(false);
// # Login as test user, visit test channel and post any message
cy.apiLogin(testUser);
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
cy.get('#channelHeaderTitle', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').and('have.text', testChannel.display_name);
cy.postMessage('b');
// # Post another incoming webhook
const payload2 = getPayload(testChannel, iconUrl);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload2, waitFor: 'text'});
cy.getLastPost().within(() => {
// * Verify that another message is posted via incoming webhook
cy.findByText(payload2.text).should('be.visible');
// * Verify that the username shown is of the webhook creator and override is not allowed.
cy.get('.post__header').find('.user-popover').should('have.text', sysadmin.username);
// * Verify that the user icon shown is of the webhook creator and override is not allowed.
cy.get('.profile-icon > img').should('have.attr', 'src', `${Cypress.config('baseUrl')}/api/v4/users/${sysadmin.id}/image?_=0`);
});
// * Verify previous webhook message if new setting is respected.
cy.uiGetNthPost(-3).within(() => {
// * Verify that the post is from previous webhook message.
cy.findByText(payload1.text).should('be.visible');
// * Verify that the username shown is updated as webhook creator and override didn't take effect.
cy.get('.post__header').find('.user-popover').should('have.text', sysadmin.username);
// * Verify that the user icon shown is updated as webhook creator and override didn't take effect.
cy.get('.profile-icon > img').should('have.attr', 'src', `${Cypress.config('baseUrl')}/api/v4/users/${sysadmin.id}/image?_=0`);
});
});
});
function getPayload(channel, iconUrl) {
return {
channel: channel.name,
username: 'user-overriden',
icon_url: iconUrl,
text: `${getRandomId()} - this is from incoming webhook.`,
};
}

Просмотреть файл

@@ -0,0 +1,79 @@
// 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 @incoming_webhook
describe('Incoming webhook', () => {
let incomingWebhook;
let testTeam;
before(() => {
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: true,
description: 'Incoming webhook - Event: Editing Webhook',
display_name: 'editing-webhook',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
});
});
it('MM-T641 Edit incoming webhook, webhook posts attachment', () => {
cy.intercept('GET', '**api/v4/channels/**').as('channels');
cy.intercept('GET', '**/api/v4/**').as('networkCalls');
// # Go to test team/channel, open product menu and click "Integrations"
cy.visit(`${testTeam.name}/channels/town-square`);
cy.wait('@channels');
cy.wait('@networkCalls');
cy.uiOpenProductMenu('Integrations');
// * Verify that it redirects to integrations URL. Then, click "Incoming Webhooks"
cy.url().should('include', `${testTeam.name}/integrations`);
cy.get('.backstage-sidebar').should('be.visible').findByText('Incoming Webhooks').click();
// * Verify that it redirects to incoming webhooks URL. Then, click "Add Incoming Webhook"
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
cy.findByText('Edit').click();
// # Change the channel from Off Topic to another channel that you have access to, then click "Update"
cy.get('.backstage-form').should('be.visible').within(() => {
cy.get('#channelSelect').select('Town Square');
cy.findByRole('button', {name: 'Update'}).scrollIntoView().click();
});
// # Redirect to test team/channel
cy.visit(`${testTeam.name}/channels/town-square`);
// # Post an incoming webhook and verify that it is posted in the channel
const payload = {
channel: 'town-square',
username: 'new-username',
attachments: [{fallback: 'fallback text', pretext: 'Optional text that appears above the attachment block', author_name: 'Authors Name', author_link: 'http://mattermost.org', author_icon: 'http://www.mattermost.org/wp-content/uploads/2016/04/icon.png', text: 'This is the text of the attachment. It should appear just above the image. \nIts very long, so it makes the text collapse behind a \\"Show More\\" button. If you click \\"Show More\\" the text should expand, and then if you click "Show Less" it should collapse again. The rest of the attachment should include one image of a graph and one thumbnail image of the Mattermost logo on the right hand side of the attachment. It should also include additional fields below the image that are formatted more like a table, in two columns. The left border of the attachment should be colored green. At the top of the attachment, there should be an author name followed by a bolded title. Both the author name and the title should be hyperlinks.', thumb_url: 'http://www.mattermost.org/wp-content/uploads/2016/04/icon.png', title: 'Testing Integration Attachments', title_link: 'https://www.google.com', color: '#00ff00', image_url: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/ScientificGraphSpeedVsTime.svg/2000px-ScientificGraphSpeedVsTime.svg.png', fields: [{short: false, title: 'Area', value: 'Testing with a very long piece of text that will take up the whole width of the table. And then some more space even because it is really not a short field.'}, {short: true, title: 'Iteration', value: 'Testing'}, {short: true, title: 'State', value: 'New'}, {short: false, title: 'Reason', value: 'New defect reported'}]}],
};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.getLastPost().within(() => {
cy.findByRole('link', {name: 'Testing Integration Attachments', hidden: true});
cy.get('.attachment__image').should('be.visible');
cy.get(':nth-child(2) > thead > tr > .attachment-field__caption').should('have.text', 'Area');
cy.get(':nth-child(3) > thead > tr > :nth-child(1)').should('have.text', 'Iteration');
cy.get('thead > tr > :nth-child(2)').should('have.text', 'State');
cy.get(':nth-child(4) > thead > tr > .attachment-field__caption').should('have.text', 'Reason');
});
});
});

Просмотреть файл

@@ -0,0 +1,23 @@
// 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.
// ***************************************************************
export function enableUsernameAndIconOverride(enable) {
enableUsernameAndIconOverrideInt(enable, enable);
}
export function enableUsernameAndIconOverrideInt(enableUsername, enableIcon) {
// # Visit integration management at system console and change override values
cy.visit('/admin_console/integrations/integration_management');
cy.findByTestId('ServiceSettings.EnablePostUsernameOverride' + enableUsername).check({force: true});
cy.findByTestId('ServiceSettings.EnablePostIconOverride' + enableIcon).check({force: true});
// # Save the settings
cy.get('#saveSetting').should('be.enabled').click({force: true});
cy.get('#saveSetting').should('be.disabled');
}

Просмотреть файл

@@ -0,0 +1,173 @@
// 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 @incoming_webhook
import {getRandomId} from '../../../../utils';
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('Incoming webhook', () => {
const inAppUsername = 'in-app';
const inAppIconURL = 'https://pbs.twimg.com/profile_images/3303520670/4da3468b30495a5d73e6f31df068e5c9.jpeg';
let testTeam;
let testChannel;
let sysadmin;
let incomingWebhook;
before(() => {
cy.apiGetMe().then(({user}) => {
sysadmin = user;
});
cy.apiUpdateConfig({
ServiceSettings: {
EnablePostUsernameOverride: true,
EnablePostIconOverride: true,
},
});
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
testChannel = channel;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: true,
description: 'Incoming webhook - in-app override',
display_name: 'in-app-override',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
editIncomingWebhook(incomingWebhook.id, team.name, inAppUsername, inAppIconURL);
});
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/channels/town-square`);
});
it('MM-T620 Payload username and profile picture override in-app settings', () => {
// # Post an incoming webhook with username and profile icon URL
const payload = getPayload(testChannel, true);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
// # Click test channel on sidebar
cy.get(`#sidebarItem_${testChannel.name}`).should('be.visible').click({force: true});
// # Wait for the webhook message to get posted
cy.waitUntil(() => cy.getLastPost().then((el) => {
const postedMessageEl = el.find('.post-message__text > p')[0];
return Boolean(postedMessageEl && postedMessageEl.textContent.includes(payload.text));
}));
// * Verify that the username and profile icon are overridden per webhook payload
verifyLastPost(sysadmin, payload.username, payload.icon_url);
});
it('MM-T621 Override username and profile picture - remove overrides from payload', () => {
// # Post an incoming webhook without username and profile icon URL
const payload = getPayload(testChannel, false);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
// # Click test channel on sidebar
cy.get(`#sidebarItem_${testChannel.name}`).should('be.visible').click({force: true});
// # Wait for the webhook message to get posted
cy.waitUntil(() => cy.getLastPost().then((el) => {
const postedMessageEl = el.find('.post-message__text > p')[0];
return Boolean(postedMessageEl && postedMessageEl.textContent.includes(payload.text));
}));
// * Verify that the username and profile icon are based from webhook settings
verifyLastPost(sysadmin, inAppUsername, inAppIconURL);
});
});
function editIncomingWebhook(incomingWebhookId, teamName, inAppUsername, inAppIconURL) {
// # Edit incoming webhook
cy.visit(`/${teamName}/integrations/incoming_webhooks/edit?id=${incomingWebhookId}`);
cy.get('.backstage-header', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').within(() => {
cy.findByText('Incoming Webhooks').should('be.visible');
cy.findByText('Edit').should('be.visible');
});
// # Enter username and profile icon URL
cy.findByLabelText('Username').should('exist').type(inAppUsername);
cy.findByLabelText('Profile Picture').should('exist').type(inAppIconURL);
// # Click update and verify it redirects to incoming webhook page
cy.findByText('Update').click();
cy.url().should('include', `/${teamName}/integrations/incoming_webhooks`).wait(TIMEOUTS.ONE_SEC);
}
function getPayload(channel, withUsernameAndProfileIcon) {
const payload = {
channel: channel.name,
text: `${getRandomId()} - this is from incoming webhook`,
};
if (!withUsernameAndProfileIcon) {
return payload;
}
return {
...payload,
username: 'payload_username',
icon_url: 'https://mattermost.com/wp-content/uploads/2022/02/icon_WS.png',
};
}
function verifyLastPost(owner, username, iconUrl) {
cy.getLastPost().within(() => {
// * Verify username in post header
cy.get('.post__header').find('.user-popover').as('usernameForPopover').should('have.text', username);
// * Verify profile icon in post header
const baseUrl = Cypress.config('baseUrl');
const encodedIconUrl = encodeURIComponent(iconUrl);
cy.get('.profile-icon > img').as('profileIconForPopover').should('have.attr', 'src', `${baseUrl}/api/v4/image?url=${encodedIconUrl}`);
// * Verify that the BOT label appears
cy.get('.Tag').should('be.visible').and('have.text', 'BOT');
// * Verify that there's no status indicator
cy.get('.status').should('not.exist');
});
// # Click on username and verify profile popover
cy.get('@usernameForPopover').click();
verifyProfilePopover(owner, username, iconUrl);
// # Press escape key to close profile popover
cy.get('body').typeWithForce('{esc}');
// # Click on profile icon and verify profile popover
cy.get('@profileIconForPopover').click();
verifyProfilePopover(owner, username, iconUrl);
}
function verifyProfilePopover(owner, username, iconUrl) {
// * Verify that the profile popover is shown
cy.get('#user-profile-popover').should('be.visible').within(() => {
// * Verify username from payload
cy.get('.user-profile-popover__heading').should('be.visible').and('have.text', username);
// * Verify icon URL from payload
cy.get('.Avatar').should('have.attr', 'src', iconUrl);
// * Verify that it matches with correct footer
cy.get('.popover__row').should('be.visible').and('have.text', `This post was created by an integration from @${owner.username}`);
});
}

Просмотреть файл

@@ -0,0 +1,63 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Stage: @dev
// Group: @channels @incoming_webhook
import {enableUsernameAndIconOverride} from './helpers';
describe('Incoming webhook', () => {
const incomingWebhookText = 'This is a message to a newly created direct message channel';
let incomingWebhookConfiguration;
let incomingWebhook;
let generatedTeam;
let generatedChannel;
let generatedUser;
before(() => {
// # Enable username override
enableUsernameAndIconOverride(true, false);
// # Create a new user, their team, and a channel to tie the webhook to
cy.apiInitSetup({userPrefix: 'mm-t639-'}).then(({team, channel, user}) => {
generatedTeam = team;
generatedChannel = channel;
generatedUser = user;
incomingWebhookConfiguration = {
channel_id: channel.id,
channel_locked: false,
display_name: 'webhook',
};
cy.apiCreateWebhook(incomingWebhookConfiguration).then((hook) => {
incomingWebhook = hook;
});
}).then(() => {
// # Send webhook notification
const webhookPayload = {channel: `@${generatedUser.username}`, text: incomingWebhookText};
cy.postIncomingWebhook({url: incomingWebhook.url, data: webhookPayload});
}).then(() => {
// # Open any page to get to the sidebar
cy.visit(`/${generatedTeam.name}/channels/${generatedChannel.name}`);
});
});
it('MM-T639 🚀 incoming Webhook creates DM', () => {
// # Verify that the channel was created correctly with an unread message, and open it
cy.uiGetLHS().
contains(generatedUser.username).
should('have.class', 'unread-title').
click();
cy.getLastPost().within(($post) => {
cy.wrap($post).contains(incomingWebhookConfiguration.display_name).should('be.visible');
cy.wrap($post).contains('BOT').should('be.visible');
cy.wrap($post).contains(incomingWebhookText).should('be.visible');
});
});
});

Просмотреть файл

@@ -0,0 +1,85 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @incoming_webhook
import {enableUsernameAndIconOverride} from './helpers';
describe('Incoming webhook', () => {
let testTeam;
let siteName;
before(() => {
cy.apiGetConfig().then(({config}) => {
siteName = config.TeamSettings.SiteName;
});
cy.apiInitSetup().then(({team}) => {
testTeam = team;
});
});
it('MM-T625 Incoming webhook is image only', () => {
// # Enable username and icon override at system console
cy.apiAdminLogin();
enableUsernameAndIconOverride(true);
// # Go to test team/channel, open product menu and click "Integrations"
cy.visit(`${testTeam.name}/channels/off-topic`);
cy.uiOpenProductMenu('Integrations');
// * Verify that it redirects to integrations URL. Then, click "Incoming Webhooks"
cy.url().should('include', `${testTeam.name}/integrations`);
cy.get('.backstage-sidebar').should('be.visible').findByText('Incoming Webhooks').click();
// * Verify that it redirects to incoming webhooks URL. Then, click "Add Incoming Webhook"
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
cy.findByText('Add Incoming Webhook').click();
// * Verify that it redirects to where it can add incoming webhook
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks/add`);
// # Enter webhook details such as title, description and channel, then save
cy.get('.backstage-form').should('be.visible').within(() => {
cy.get('#displayName').type('Webhook Title');
cy.get('#description').type('Webhook Description');
cy.get('#channelSelect').select('Off-Topic');
cy.findByText('Save').scrollIntoView().click();
});
// * Verify that it redirects to incoming webhook confirmation URL
cy.url().should('include', `${testTeam.name}/integrations/confirm?type=incoming_webhooks&id=`).
invoke('toString').then((hookConfirmationUrl) => {
const hookId = hookConfirmationUrl.split('id=')[1];
const hookUrl = `${Cypress.config('baseUrl')}/hooks/${hookId}`;
// * Verify that the hook ID in the URL matches with the one shown in a page
// * Verify that the copy link is shown.
cy.findByText(hookUrl).should('be.visible').
parent().siblings('.fa-copy').should('be.visible');
// # Click "Done" and verify that it redirects to incoming webhooks URL
cy.findByText('Done').click();
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
// # Click back to site name and verify that it redirects to test team/channel
cy.findByText(`Back to ${siteName}`).click();
cy.url().should('include', `${testTeam.name}/channels/off-topic`);
// # Post an incoming webhook and verify that it is posted in the channel
const payload = {
channel: 'off-topic',
username: 'new-username',
attachments: [{image_url: 'https://cdn.pixabay.com/photo/2017/10/10/22/24/wide-format-2839089_960_720.jpg'}],
};
cy.postIncomingWebhook({url: hookUrl, data: payload});
cy.get('.attachment__image').should('be.visible');
});
});
});

Просмотреть файл

@@ -0,0 +1,57 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @integrations
describe('Integrations', () => {
let teamA;
let teamB;
let newIncomingHook;
before(() => {
// # Login, create incoming webhook for Team A
cy.apiInitSetup().then(({team, channel}) => {
teamA = team.name;
newIncomingHook = {
channel_id: channel.id,
display_name: 'Team A Webhook',
};
//# Create a new webhook for Team A
cy.apiCreateWebhook(newIncomingHook);
});
// # Login, create incoming webhook for Team B
cy.apiInitSetup().then(({team, channel}) => {
teamB = team.name;
newIncomingHook = {
channel_id: channel.id,
display_name: 'Team B Webhook',
};
// # Create a new webhook for Team B
cy.apiCreateWebhook(newIncomingHook);
});
});
it('MM-T644 Integrations display on team where they were created', () => {
// # Visit Test Team B Incoming Webhooks page
cy.visit(`/${teamB}/integrations/incoming_webhooks`);
// * Assert the page contains only Team B Outgoing Webhook
cy.findByText('Team B Webhook').and('does.not.contain', 'Team A Webhook');
// # Visit Team A Incoming Webhooks page
cy.visit(`/${teamA}/integrations/incoming_webhooks`);
// * Assert the page contains only Team A Outgoing Webhook
cy.findByText('Team A Webhook').and('does.not.contain', 'Team B Webhook');
});
});

Просмотреть файл

@@ -0,0 +1,68 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Group: @channels @incoming_webhook
describe('Integrations/Incoming Webhook', () => {
let incomingWebhook;
let testChannel;
before(() => {
// # Let the webhook have its own icon
cy.apiUpdateConfig({
ServiceSettings: {
EnablePostIconOverride: true,
},
});
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel}) => {
testChannel = channel;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: true,
description: 'Incoming webhook - Viewing attachments with invalid URL does not cause the application to crash',
display_name: 'invalid_attachment_URL_webhook',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
it('MM-T624 Viewing attachments with invalid URL does not cause the application to crash', () => {
// # Post the incoming webhook with bad image URL
const payload = {
channel: testChannel.name,
text: 'The image below should be broken due to the invalid URL in the payload text you just sent.',
attachments: [{
fallback: 'Testing viewing attachments with invalid URL does not cause the application to crash.',
pretext: 'Testing viewing attachments with invalid URL does not cause the application to crash.',
image_url: 'https://example.com',
}],
icon_url: 'http://www.mattermost.org/wp-content/uploads/2016/04/icon_WS.png',
};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload, waitFor: 'attachment-pretext'});
// * Check that the message has sent and that the body is viewable
cy.waitUntil(() => cy.getLastPost().then((el) => {
const postedMessageEl = el.find('.post-message__text > p')[0];
return Boolean(postedMessageEl && postedMessageEl.textContent.includes('The image below should be broken due to the invalid URL in the payload text you just sent.'));
}));
cy.getLastPostId().then((postId) => {
const postMessageId = `#${postId}_message`;
cy.get(postMessageId).within(() => {
cy.get('.attachment__image').should('be.visible');
});
});
});
});

Просмотреть файл

@@ -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 @integrations
describe('Integrations', () => {
let testUser;
let testTeam;
let testChannel;
let newIncomingHook;
let incomingWebhook;
before(() => {
// # Create new setup
cy.apiInitSetup().then(({user}) => {
testUser = user;
// # Login as the new user
cy.apiLogin(testUser).then(() => {
// # Create a new team with the new user
cy.apiCreateTeam('test-team', 'Team Testers').then(({team}) => {
testTeam = team;
// # Create a new test channel for the team
cy.apiCreateChannel(testTeam.id, 'test-channel', 'Testers Channel').then(({channel}) => {
testChannel = channel;
// # Declare web-hook values
newIncomingHook = {
channel_id: testChannel.id,
channel_locked: true,
description: 'Test Webhook Description',
display_name: 'Test Webhook Name',
};
//# Create a new webhook
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
// # Visit the test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
});
});
});
});
it('MM-T643 Incoming webhook:Long URL for embedded image', () => {
const letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const queries = letters.split('').reduce((acc, letter) => {
const newValue = acc + `&${letter}=${letters}`;
return newValue;
}, '');
const url = `http://via.placeholder.com/300.png?expires=213134234234234234234234${queries}`;
const payload = getPayload(testChannel, url);
// # Post the webhook message
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
// * Assert that the message was posted
cy.uiWaitUntilMessagePostedIncludes('Hey attachments');
cy.getLastPostId().then(() => {
const baseUrl = Cypress.config('baseUrl');
const encodedUrl = `${baseUrl}/api/v4/image?url=${encodeURIComponent(url)}`;
// * Assert that file image is present
cy.findByLabelText('file thumbnail').should('be.visible').and('have.attr', 'src', encodedUrl);
// * Assert that the Show More button is visible
cy.findByText('Show more').should('be.visible').click();
// * Assert that the Show less button is visible
cy.findByText('Show less').scrollIntoView().should('be.visible');
});
});
});
function getPayload(channel, url) {
const text = `Hey attachments ![graph](${url}).${'Lorem ipsum dolor '.repeat(240)}.`;
return {
channel: channel.name,
text,
};
}

Просмотреть файл

@@ -0,0 +1,110 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @incoming_webhook
import {getRandomId} from '../../../../utils';
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('Incoming webhook', () => {
let testTeam;
let incomingWebhook;
before(() => {
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: false,
description: 'Incoming webhook - setting',
display_name: 'webhook-setting',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
});
});
it('MM-T623 Lock to this channel on webhook configuration works', () => {
cy.apiCreateChannel(testTeam.id, 'other-channel', 'Other Channel').then(({channel}) => {
// # Post the first incoming webhook
const payload1 = getPayload(channel);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload1});
// # Switch to other channel and wait for the first webhook message to get posted successfully
switchToChannel(testTeam.name, channel.name);
waitUntilWebhookPosted(payload1.text);
// # Edit webhook to lock into the test channel
editIncomingWebhook(incomingWebhook.id, testTeam.name, true);
const payload2 = getPayload(channel);
// # Try to post a second incoming webhook
cy.task('postIncomingWebhook', {url: incomingWebhook.url, data: payload2}).then((res) => {
// * Verify that it failed to post
expect(res.status).equal(403);
expect(res.data.message).equal('This webhook is not permitted to post to the requested channel.');
});
// # Edit webhook to not lock into the test channel
editIncomingWebhook(incomingWebhook.id, testTeam.name, false);
// # Retry posting the second incoming webhook
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload2});
// # Switch to other channel and wait for the second webhook message to get posted
switchToChannel(testTeam.name, channel.name);
waitUntilWebhookPosted(payload2.text);
});
});
});
function switchToChannel(teamName, channelName) {
cy.visit(`/${teamName}/channels/town-square`);
cy.get(`#sidebarItem_${channelName}`, {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').click({force: true});
}
function editIncomingWebhook(incomingWebhookId, teamName, lockToChannel) {
cy.visit(`/${teamName}/integrations/incoming_webhooks/edit?id=${incomingWebhookId}`);
cy.get('.backstage-header', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').within(() => {
cy.findByText('Incoming Webhooks').should('be.visible');
cy.findByText('Edit').should('be.visible');
});
// # Check or uncheck "Lock to this channel"
cy.findByLabelText('Lock to this channel').should('exist').as('lockChannel');
if (lockToChannel) {
cy.get('@lockChannel').check();
} else {
cy.get('@lockChannel').uncheck();
}
// # Click update and verify it redirects to incoming webhook page
cy.findByText('Update').click();
cy.url().should('include', `/${teamName}/integrations/incoming_webhooks`).wait(TIMEOUTS.ONE_SEC);
}
function getPayload(channel) {
return {
channel: channel.name,
text: `${getRandomId()} - this is from incoming webhook`,
};
}
function waitUntilWebhookPosted(text) {
cy.waitUntil(() => cy.getLastPost().then((el) => {
const postedMessageEl = el.find('.post-message__text > p')[0];
return Boolean(postedMessageEl && postedMessageEl.textContent.includes(text));
}));
}

Просмотреть файл

@@ -0,0 +1,86 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @incoming_webhook
import {enableUsernameAndIconOverride} from './helpers';
describe('Incoming webhook', () => {
let testTeam;
let testChannel;
let siteName;
before(() => {
cy.apiGetConfig().then(({config}) => {
siteName = config.TeamSettings.SiteName;
});
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
testChannel = channel;
});
});
it('MM-T2029 Setup for incoming webhook', () => {
// # Enable username and icon override at system console
cy.apiAdminLogin();
enableUsernameAndIconOverride(true);
// # Go to test team/channel, open product menu and click "Integrations"
cy.visit(`${testTeam.name}/channels/${testChannel.name}`);
cy.uiOpenProductMenu('Integrations');
// * Verify that it redirects to integrations URL. Then, click "Incoming Webhooks"
cy.url().should('include', `${testTeam.name}/integrations`);
cy.get('.backstage-sidebar').should('be.visible').findByText('Incoming Webhooks').click();
// * Verify that it redirects to incoming webhooks URL. Then, click "Add Incoming Webhook"
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
cy.findByText('Add Incoming Webhook').click();
// * Verify that it redirects to where it can add incoming webhook
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks/add`);
// # Enter webhook details such as title, description and channel, then save
cy.get('.backstage-form').should('be.visible').within(() => {
cy.get('#displayName').type('Webhook Title');
cy.get('#description').type('Webhook Description');
cy.get('#channelSelect').select(testChannel.display_name);
cy.findByText('Save').scrollIntoView().click();
});
// * Verify that it redirects to incoming webhook confirmation URL
cy.url().should('include', `${testTeam.name}/integrations/confirm?type=incoming_webhooks&id=`).
invoke('toString').then((hookConfirmationUrl) => {
const hookId = hookConfirmationUrl.split('id=')[1];
const hookUrl = `${Cypress.config('baseUrl')}/hooks/${hookId}`;
// * Verify that the hook ID in the URL matches with the one shown in a page
// * Verify that the copy link is shown.
cy.findByText(hookUrl).should('be.visible').
parent().siblings('.fa-copy').should('be.visible');
// # Click "Done" and verify that it redirects to incoming webhooks URL
cy.findByText('Done').click();
cy.url().should('include', `${testTeam.name}/integrations/incoming_webhooks`);
// # Click back to site name and verify that it redirects to test team/channel
cy.findByText(`Back to ${siteName}`).click();
cy.url().should('include', `${testTeam.name}/channels/${testChannel.name}`);
// # Post an incoming webhook and verify that it is posted in the channel
const payload = {
channel: testChannel.name,
username: 'new-username',
text: 'Setup for incoming webhook.',
};
cy.postIncomingWebhook({url: hookUrl, data: payload, waitFor: 'text'});
});
});
});

Просмотреть файл

@@ -0,0 +1,329 @@
// 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 @incoming_webhook
describe('Incoming webhook', () => {
let testTeam;
let testChannel;
let incomingWebhook;
let offTopicLink;
let sysadminUser;
before(() => {
cy.apiUpdateConfig({
ServiceSettings: {
EnablePostUsernameOverride: true,
EnablePostIconOverride: true,
},
});
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel}) => {
testTeam = team;
testChannel = channel;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: false,
description: 'Incoming webhook - basic formatting',
display_name: 'basic-formatting',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
offTopicLink = `/${team.name}/channels/off-topic`;
});
cy.apiGetMe().then((me) => {
sysadminUser = me.user;
});
});
beforeEach(() => {
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
describe('MM-T626 Incoming webhook is only image and fallback text', () => {
const id = 'MM-T626';
const baseUrl = Cypress.config('baseUrl');
const imageUrl = 'https://cdn.pixabay.com/photo/2017/10/10/22/24/wide-format-2839089_960_720.jpg';
const imageSrc = `${baseUrl}/api/v4/image?url=${encodeURIComponent(imageUrl)}`;
it('first payload', () => {
const search = id + '-1';
const payload = {text: search, attachments: [{fallback: 'fallback text', image_url: imageUrl}]};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.getLastPost().within(() => {
cy.get('.post-message__text').should('have.text', search);
cy.get('.attachment__image').should('have.attr', 'src', imageSrc);
});
});
it('second payload', () => {
const search = id + '-2';
const payload = {channel: 'off-topic', text: search, username: 'new_username', attachments: [{fallback: 'fallback text', image_url: imageUrl}], icon_url: 'https://mattermost.com/wp-content/uploads/2022/02/icon_WS.png'};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.visit(offTopicLink);
cy.getLastPost().within(() => {
cy.get('.post-message__text').should('have.text', search);
cy.get('.attachment__image').should('have.attr', 'src', imageSrc);
});
});
});
describe('MM-T627 Images with tall and wide aspect ratios appear correctly', () => {
const id = 'MM-T627';
it('wide image', () => {
const search = id + '-wide';
const payload = {text: search, attachments: [{image_url: 'https://cdn.pixabay.com/photo/2017/10/10/22/24/wide-format-2839089_960_720.jpg'}]};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
// Original image is 960x246. 960/246 = ~3.9. Let's make sure image is rendered with 3.9 +/- 0.1
cy.getLastPost().within(() => {
cy.get('.post-message__text').should('have.text', search);
const originalWidth = 960;
const originalHeight = 246;
const aspectRatio = originalWidth / originalHeight;
cy.get('img.attachment__image').should('be.visible').and((img) => {
expect(img.width() / img.height()).to.be.closeTo(aspectRatio, 0.05);
});
});
});
it('tall image', () => {
const search = id + '-tall';
const payload = {text: search, attachments: [{image_url: 'https://media.npr.org/programs/atc/features/2009/may/short/abetall3-0483922b5fb40887fc9fbe20a606e256cbbd10ee-s800-c85.jpg'}]};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.getLastPost().within(() => {
cy.get('.post-message__text').should('have.text', search);
const originalWidth = 385;
const originalHeight = 916;
const aspectRatio = originalWidth / originalHeight;
cy.get('img.attachment__image').should('be.visible').and((img) => {
expect(img.width() / img.height()).to.be.closeTo(aspectRatio, 0.05);
});
});
});
});
it('MM-T628 Incoming webhook supports Slack-style mentions', () => {
const id = 'MM-T628';
const text = `${id}: <!here> <!channel>`;
const payload = {
channel: testChannel.name,
username: 'new_username',
text,
icon_url: 'https://mattermost.com/wp-content/uploads/2022/02/icon_WS.png',
};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.waitUntil(() => cy.getLastPost().then((el) => {
const postedMessageEl = el.find('.post-message__text > p')[0];
return Boolean(postedMessageEl && postedMessageEl.textContent.includes(id));
}));
cy.getLastPost().within(() => {
cy.get('.post-message__text').within(() => {
cy.get('.mention--highlight').eq(0).should('have.text', '@here');
cy.get('.mention--highlight').eq(1).should('have.text', '@channel');
});
});
});
it('MM-T629 Incoming webhook with Slack attachment, mention in `pretext`', () => {
const id = 'MM-T629';
const payload = {
channel: testChannel.name,
attachments: [{type: 'slack_attachment',
color: '#7CD197',
fields: [{short: false, title: 'Area', value: "Testing with a very long piece of text that will take up the whole width of the table (stopping short of the space where the thumbnail image is displayed). This is one more sentence to really make it a long field, and let's add a taco emoji :taco:."}, {short: true, title: 'Iteration', value: 'Testing'}, {short: true, title: 'State', value: 'New'}, {short: false, title: 'Reason', value: 'New defect reported'}, {short: false, title: 'Random field', value: 'This is a field which is not marked as short so it should be rendered on a separate row'}, {short: true, title: 'Short 1', value: 'Short field'}, {short: true, title: 'Short 2', value: 'Another one'}, {short: true, title: 'Field with link', value: '<http://example.com|Link>'}],
mrkdwn_in: ['pretext'],
pretext: `${id} <@${sysadminUser.id}> Some text here to look at (verify eyes emoji) :eyes:`,
text: 'This is the text of the attachment. There should be a small Jenkins thumbnail off to the right.',
thumb_url: 'https://slack.global.ssl.fastly.net/7bf4/img/services/jenkins-ci_128.png',
title: 'A slack attachment',
title_link: 'https://www.google.com'}],
};
cy.visit(offTopicLink);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.get(`#sidebarItem_${testChannel.name}`).find('#unreadMentions').should('have.text', '1');
cy.get(`#sidebarItem_${testChannel.name}`).click({force: true});
cy.getLastPost().within(() => {
cy.get('.attachment__thumb-pretext').should('contain', id);
cy.get('.attachment__thumb-pretext a.mention-link').should('have.text', '@sysadmin');
cy.get('.attachment__thumb-pretext span[data-emoticon="eyes"]').should('exist');
cy.get('a.attachment__title-link[href="https://www.google.com"]').should('have.text', 'A slack attachment');
cy.get('.attachment-field a.markdown__link[href="http://example.com"]').should('have.text', 'Link');
cy.get('.attachment-field span[data-emoticon="taco"]').should('exist');
cy.get('.attachment__thumb-container .file-preview__button img').should('exist');
});
});
it('MM-T630 Incoming webhook with Slack attachment, mention in attachment `text`', () => {
const id = 'MM-T630';
const payload = {
channel: testChannel.name,
attachments: [{type: 'slack_attachment',
color: '#7CD197',
fields: [{short: false, title: 'Area', value: "Testing with a very long piece of text that will take up the whole width of the table (stopping short of the space where the thumbnail image is displayed). This is one more sentence to really make it a long field, and let's add a taco emoji :taco:."}, {short: true, title: 'Iteration', value: 'Testing'}, {short: true, title: 'State', value: 'New'}, {short: false, title: 'Reason', value: 'New defect reported'}, {short: false, title: 'Random field', value: 'This is a field which is not marked as short so it should be rendered on a separate row'}, {short: true, title: 'Short 1', value: 'Short field'}, {short: true, title: 'Short 2', value: 'Another one'}, {short: true, title: 'Field with link', value: '<http://example.com|Link>'}],
mrkdwn_in: ['pretext'],
pretext: `${id} Some text here to look at (verify eyes emoji) :eyes:`,
text: `This is the text of the attachment. <@${sysadminUser.id}>, There should be a small Jenkins thumbnail off to the right.`,
thumb_url: 'https://slack.global.ssl.fastly.net/7bf4/img/services/jenkins-ci_128.png',
title: 'A slack attachment',
title_link: 'https://www.google.com'}],
};
cy.visit(offTopicLink);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.get(`#sidebarItem_${testChannel.name}`).find('#unreadMentions').should('have.text', '1');
cy.get(`#sidebarItem_${testChannel.name}`).click({force: true});
cy.getLastPost().within(() => {
cy.get('.attachment__thumb-pretext').should('contain', id);
cy.get('.attachment__thumb-pretext span[data-emoticon="eyes"]').should('exist');
cy.get('.attachment__body .post-message__text-container a.mention-link').should('have.text', '@sysadmin');
cy.get('a.attachment__title-link[href="https://www.google.com"]').should('have.text', 'A slack attachment');
cy.get('.attachment-field a.markdown__link[href="http://example.com"]').should('have.text', 'Link');
cy.get('.attachment-field span[data-emoticon="taco"]').should('exist');
cy.get('.attachment__thumb-container .file-preview__button img').should('exist');
});
});
describe('MM-T631 Short field in payload can accept strings text in quotes for true and false', () => {
const id = 'MM-T631';
const makePayloadFromShortValue = (short, currentID) => ({
channel: testChannel.name,
attachments: [{type: 'slack_attachment',
color: '#7CD197',
fields: [{short: false, title: 'Area', value: "Testing with a very long piece of text that will take up the whole width of the table (stopping short of the space where the thumbnail image is displayed). This is one more sentence to really make it a long field, and let's add a taco emoji :taco:."}, {short: true, title: 'Iteration', value: 'Testing'}, {short: true, title: 'State', value: 'New'}, {short: false, title: 'Reason', value: 'New defect reported'}, {short: false, title: 'Random field', value: 'This is a field which is not marked as short so it should be rendered on a separate row'}, {short: true, title: 'Short 1', value: 'Short field'},
{short, title: 'Short 2', value: 'Another one'}, {short: true, title: 'Field with link', value: '<http://example.com|Link>'}],
mrkdwn_in: ['pretext'],
pretext: 'Some text here to look at (verify eyes emoji) :eyes:',
text: `${currentID} ${short} This is the text of the attachment. <@${sysadminUser.id}>, there should be a small Jenkins thumbnail off to the right.`,
thumb_url: 'https://slack.global.ssl.fastly.net/7bf4/img/services/jenkins-ci_128.png',
title: 'A slack attachment',
title_link: 'https://www.google.com',
}]});
const testCases = [
{short: true, shouldShowShort: true, desc: 'true boolean'},
{short: false, shouldShowShort: false, desc: 'false boolean'},
{short: 'true', shouldShowShort: true, desc: 'true string'},
{short: 'false', shouldShowShort: false, desc: 'false string'},
];
testCases.forEach((testCase, i) => {
it(`should show table elements based on short value ${testCase.desc}`, () => {
const currentID = `${id} - ${i}`;
const payload = makePayloadFromShortValue(testCase.short, currentID);
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.getLastPost().within(() => {
cy.get('.attachment__body .post-message__text-container p').should('contain', currentID);
if (testCase.shouldShowShort) {
cy.get('table:nth-child(6) > thead > tr > th:nth-child(2)').should('have.text', 'Short 2');
cy.get('table:nth-child(6) > tbody > tr > td:nth-child(2) > p').should('have.text', 'Another one');
} else {
cy.get('table:nth-child(7) > thead > tr > th:nth-child(1)').should('have.text', 'Short 2');
cy.get('table:nth-child(7) > tbody > tr > td:nth-child(1) > p').should('have.text', 'Another one');
}
});
});
});
});
it('MM-T632 Slack compatibility code shouldn\'t mess up characters', () => {
const id = 'MM-T632';
const text = `${id} <>|<>|`;
const payload = {
channel: testChannel.name,
text,
};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.getLastPost().within(() => {
cy.get('.post__body p').should('have.text', text);
});
});
it('MM-T634 Action buttons in Slack-style attachment post', () => {
const id = 'MM-T634';
const payload = {text: id, attachments: [{pretext: 'This is the attachment pretext.', text: 'This is the attachment text.', actions: [{name: 'Select an option...', integration: {url: 'http://127.0.0.1:7357/action_options', context: {action: 'do_something'}}, type: 'select', data_source: 'channels'}, {name: 'Select an option...', integration: {url: 'http://127.0.0.1:7357/action_options', context: {action: 'do_something'}}, type: 'select', options: [{text: 'Option1', value: 'opt1'}, {text: 'Option2', value: 'opt2'}, {text: 'Option3', value: 'opt3'}]}, {name: 'Ephemeral Message', integration: {url: 'http://127.0.0.1:7357', context: {action: 'do_something_ephemeral'}}}, {name: 'Update', integration: {url: 'http://127.0.0.1:7357', context: {action: 'do_something_update'}}}]}]};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.getLastPost().within(() => {
cy.get('.post-message__text').should('have.text', id);
cy.get('.attachment-actions > :nth-child(1)').should('have.attr', 'data-testid', 'autoCompleteSelector');
cy.get('.attachment-actions > :nth-child(1) input').should('have.attr', 'placeholder', 'Select an option...');
cy.get('.attachment-actions > :nth-child(2)').should('have.attr', 'data-testid', 'autoCompleteSelector');
cy.get('.attachment-actions > :nth-child(2) input').should('have.attr', 'placeholder', 'Select an option...');
cy.get('.attachment-actions > button:nth-child(3)').should('have.attr', 'data-action-id');
cy.get('.attachment-actions > button:nth-child(3)').should('have.text', 'Ephemeral Message');
cy.get('.attachment-actions > button:nth-child(4)').should('have.attr', 'data-action-id');
cy.get('.attachment-actions > button:nth-child(4)').should('have.text', 'Update');
});
});
it('MM-T635 Initial selection on post action dropdown', () => {
const id = 'MM-T635';
const payload = {text: id, attachments: [{pretext: 'This is the attachment pretext.', text: 'This is the attachment text.', actions: [{name: 'Select an option...', integration: {url: 'http://127.0.0.1:7357/action_options', context: {action: 'do_something'}}, type: 'select', data_source: 'channels'}, {name: 'Select an option...', integration: {url: 'http://127.0.0.1:7357/action_options', context: {action: 'do_something'}}, type: 'select', options: [{text: 'Option1', value: 'opt1'}, {text: 'Option2', value: 'opt2'}, {text: 'Option3', value: 'opt3'}]}, {name: 'Ephemeral Message', integration: {url: 'http://127.0.0.1:7357', context: {action: 'do_something_ephemeral'}}}, {name: 'Update', integration: {url: 'http://127.0.0.1:7357', context: {action: 'do_something_update'}}}]}]};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
cy.getLastPost().within(() => {
cy.get('.post-message__text').should('have.text', id);
cy.get('.attachment-actions > :nth-child(1)').should('have.attr', 'data-testid', 'autoCompleteSelector');
cy.get('.attachment-actions > :nth-child(1) input').should('have.attr', 'placeholder', 'Select an option...');
cy.get('.attachment-actions > :nth-child(2)').should('have.attr', 'data-testid', 'autoCompleteSelector');
cy.get('.attachment-actions > :nth-child(2) input').should('have.attr', 'placeholder', 'Select an option...');
cy.get('.attachment-actions > button:nth-child(3)').should('have.attr', 'data-action-id');
cy.get('.attachment-actions > button:nth-child(3)').should('have.text', 'Ephemeral Message');
cy.get('.attachment-actions > button:nth-child(4)').should('have.attr', 'data-action-id');
cy.get('.attachment-actions > button:nth-child(4)').should('have.text', 'Update');
});
});
});

Просмотреть файл

@@ -0,0 +1,95 @@
// 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 @integrations
import {getRandomId} from '../../../../utils';
describe('Integrations', () => {
let testUser;
let secondUser;
let testTeam;
let testChannel;
let incomingWebhook;
before(() => {
// # Create new setup
cy.apiInitSetup().then(({user}) => {
testUser = user;
// # Create a second user
cy.apiCreateUser().then(({user: user2}) => {
secondUser = user2;
});
// # Login as the new user
cy.apiLogin(testUser).then(() => {
// # Create a new team with the new user
cy.apiCreateTeam('test-team', 'Team Testers').then(({team}) => {
testTeam = team;
// # Add second user to the test team
cy.apiAddUserToTeam(testTeam.id, secondUser.id);
// # Create a new test channel for the team
cy.apiCreateChannel(testTeam.id, 'test-channel', 'Testers Channel').then(({channel}) => {
testChannel = channel;
const newIncomingHook = {
channel_id: testChannel.id,
channel_locked: true,
description: 'Test Webhook Description',
display_name: 'Test Webhook Name',
};
//# Create a new webhook
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
// # Add second user to the test channel
cy.apiAddUserToChannel(testChannel.id, secondUser.id).then(() => {
// # Remove the first user from the channel
cy.apiDeleteUserFromTeam(testTeam.id, testUser.id).then(({data}) => {
expect(data.status).to.equal('OK');
});
// # Login as the second user
cy.apiLogin(secondUser);
// # Visit the test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
});
});
});
});
});
});
it('MM-T638 Webhook posts when webhook creator is not a member of the channel', () => {
const payload = getPayload(testChannel);
// # Post the webhook message
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload});
// * Assert that the message was posted even though webhook author has been removed
cy.uiWaitUntilMessagePostedIncludes(payload.text);
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).should('have.text', `${payload.text}`);
});
});
});
function getPayload(testChannel) {
return {
channel: testChannel.name,
text: `${getRandomId()} - this webhook was set up by a user that is no longer in this channel`,
};
}