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

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

@@ -0,0 +1,195 @@
// 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 @bot_accounts @mfa
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Bot accounts ownership and API', () => {
let newTeam;
let newUser;
let newChannel;
let botId;
let botName;
beforeEach(() => {
cy.apiAdminLogin();
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
EnforceMultifactorAuthentication: false,
},
};
cy.apiUpdateConfig(newSettings);
cy.apiInitSetup().then(({team, user, channel, townSquareUrl}) => {
newTeam = team;
newUser = user;
newChannel = channel;
cy.visit(townSquareUrl);
cy.postMessage('hello');
});
// # Create a test bot
cy.apiCreateBot().then(({bot}) => {
({user_id: botId, display_name: botName} = bot);
cy.apiPatchUserRoles(bot.user_id, ['system_admin', 'system_user']);
});
});
it('MM-T1862 Only system admin are able to create bots', () => {
// # Open product switch menu and click "Integrations"
cy.uiOpenProductMenu('Integrations');
// * Confirm integrations are visible
cy.url().should('include', `/${newTeam.name}/integrations`);
cy.get('.backstage-header').findByText('Integrations').should('be.visible');
// # Login as a regular user
cy.apiLogin(newUser);
cy.visit(`/${newTeam.name}/channels/town-square`);
// # Click product switch button
cy.uiOpenProductMenu();
// * Confirm "Integrations" is not visible
cy.uiGetProductMenu().should('not.contain', 'Integrations');
});
it('MM-T1863 Only System Admin are able to create bots (API)', () => {
// # Login as a regular user
cy.apiLogin(newUser);
// # Try to create a new bot as a regular user
const botName3 = 'stay-enabled-bot-' + Date.now();
cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/api/v4/bots',
method: 'POST',
failOnStatusCode: false,
body: {
username: botName3,
display_name: 'some text',
description: 'some text',
},
}).then((response) => {
// * Validate that request was denied
expect(response.status).to.equal(403);
});
});
it('MM-T1864 Create bot (API)', () => {
// * This call will fail if bot was not created
cy.apiCreateBot();
});
it('MM-T1865 Create post as bot', () => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
cy.apiCreatePost(newChannel.id, msg1, '', {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token);
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate post was created
cy.findByText(msg1).should('be.visible');
});
});
it('MM-T1866 Create two posts in a row to the same channel', () => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
const msg2 = 'this is a bot message2 ' + botName;
cy.apiCreatePost(newChannel.id, msg1, '', {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token).then(({body: post1}) => {
cy.apiCreatePost(newChannel.id, msg2, '', {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token).then(({body: post2}) => {
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate posts were created
cy.get(`#postMessageText_${post1.id}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
cy.get(`#postMessageText_${post2.id}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg2);
// * Validate first post has an image
cy.get(`#post_${post1.id}`).find('.Avatar').should('be.visible');
// * Validate that the second one doesn't
cy.get(`#post_${post2.id}`).should('have.class', 'same--user');
});
});
});
});
it('MM-T1867 Post as a bot and include an @ mention', () => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
cy.apiCreatePost(newChannel.id, msg1 + ' to @sysadmin', '', {}, token);
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
// * Assert that the last message posted contains highlighted mention
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).find('.mention--highlight').should('be.visible');
});
});
});
it('MM-T1868 BOT has a member role and is not in target channel and team', () => {
// # Create a test bot (member)
cy.apiCreateBot().then(({bot}) => {
// # Create token for the bot
cy.apiCreateToken(bot.user_id).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
// # Try posting
cy.apiCreatePost(newChannel.id, 'this is a bot message ' + bot.username, '', {}, token, false).then((response) => {
// * Validate that posting was not allowed
expect(response.status).to.equal(403);
});
});
});
});
it('MM-T1869 BOT has a system admin role and is not in target channel and team', () => {
const botName3 = 'stay-enabled-bot-' + Date.now();
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
// # Try posting
cy.apiCreatePost(newChannel.id, 'this is a bot message ' + botName3, '', {}, token).then((response) => {
// * Validate that posting was allowed
expect(response.status).to.equal(201);
});
});
});
});

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

@@ -0,0 +1,310 @@
// 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 @bot_accounts @mfa
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Bot accounts ownership and API', () => {
let newTeam;
let newUser;
let newChannel;
let botId;
let botUsername;
let botName;
let adminUser;
beforeEach(() => {
cy.apiAdminLogin().then(({user}) => {
adminUser = user;
});
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
EnforceMultifactorAuthentication: false,
},
};
cy.apiUpdateConfig(newSettings);
cy.apiInitSetup().then(({team, user, channel, townSquareUrl}) => {
newTeam = team;
newUser = user;
newChannel = channel;
cy.visit(townSquareUrl);
cy.postMessage('hello');
});
// # Create a test bot
cy.apiCreateBot().then(({bot}) => {
({user_id: botId, username: botUsername, display_name: botName} = bot);
cy.apiPatchUserRoles(bot.user_id, ['system_admin', 'system_user']);
});
});
it('MM-T1870 BOT has a system admin role and can also post to private channels they do not belong to', () => {
const channelName = 'channel' + Date.now();
// # Create private channel that bot doesn't belong to
cy.apiCreateChannel(newTeam.id, channelName, channelName, 'P').then(({channel}) => {
// # Create token for the bot
cy.apiCreateToken(botId).then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.apiCreatePost(channel.id, msg1 + ' to @sysadmin', '', {}, token);
// # Re-login to validate post presence
cy.apiAdminLogin();
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
});
});
});
it('MM-T1872 Bot can post to DM channel', () => {
// # Create DM channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
const msg1 = 'this is a bot message ' + botName;
// # Post test message
cy.postBotMessage({message: msg1, token, channelId: channel.id});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
});
});
});
it('MM-T1874 Bots can post when MFA is enforced', () => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
const msg1 = 'this is a bot message ' + botName;
cy.postBotMessage({channelId: newChannel.id, message: msg1, props: {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token});
// # Visit test channel
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate post was created
cy.findByText(msg1).should('be.visible');
const newSettings = {
ServiceSettings: {
EnforceMultifactorAuthentication: true,
},
};
cy.apiUpdateConfig(newSettings);
const msg2 = 'this is a bot message2 ' + botName;
cy.postBotMessage({channelId: newChannel.id, message: msg2, props: {attachments: [{pretext: 'Look some text', text: 'This is text'}]}, token});
cy.visit(`/${newTeam.name}/channels/` + newChannel.name);
// * Validate post was created
cy.findByText(msg2).should('be.visible');
});
});
it('MM-T1875 A bot cannot create another bot', () => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
// # Logout to allow posting as bot
cy.apiLogout();
// # Try to create a new bot
cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest', Authorization: `Bearer ${token}`},
url: '/api/v4/bots',
method: 'POST',
failOnStatusCode: false,
body: {
username: botName + '333',
display_name: 'some text',
description: 'some text',
},
}).then((response) => {
// * Validate that request was denied
expect(response.status).to.equal(403);
});
});
});
it('MM-T1877 Reactivate a deactivated bot', () => {
// # Create private channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token}) => {
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg1, token});
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
// # Disable the bot
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().parent().findByText('Disable').click();
// # Try to post again
const msg2 = 'this is a bot message2 ' + botName;
// # Logout to allow posting as bot
cy.apiLogout();
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg2, token, failOnStatus: false}).then(({status}) => {
// * Validate that posting failed
expect(status, 403);
});
cy.apiAdminLogin();
// # Enable the bot again
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().parent().findByText('Enable').click();
// # Try to post again
// * Validate that posting works
cy.postBotMessage({channelId: channel.id, message: msg2, token});
// * Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg2);
});
});
});
});
it('MM-T1878 Disable token can not be used to post', () => {
// # Create DM channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token, id}) => {
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg1, token});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
// # Disable the bot token
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
cy.get(`#${id}_deactivate`).click();
});
// # Try to post again
const msg2 = 'this is a bot message2 ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg2, token, failOnStatus: false}).then(({status}) => {
// * Validate that posting failed
expect(status, 403);
});
// # Enable the bot token again
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findAllByText('Bot Accounts');
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
cy.get(`#${id}_activate`).click().wait(TIMEOUTS.ONE_SEC);
// # Try to post again
// * Validate that posting works
cy.postBotMessage({channelId: channel.id, message: msg2, token});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg2);
});
});
});
});
});
it('MM-T1880 Deleted token can not be used to post', () => {
// # Create private channel that bot doesn't belong to
cy.apiCreateDirectChannel([newUser.id, adminUser.id]).then(({channel}) => {
// # Create token for the bot
cy.apiAccessToken(botId, 'some text').then(({token, id}) => {
const msg1 = 'this is a bot message ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg1, token});
// # Validate post presence
cy.visit(`/${newTeam.name}/channels/` + channel.name);
cy.getLastPostId().then((postId) => {
// * Validate post was created
cy.get(`#postMessageText_${postId}`, {timeout: TIMEOUTS.ONE_MIN}).should('contain', msg1);
});
// # Disable the bot token
cy.visit(`/${newTeam.name}/integrations/bots`);
cy.findByText(`${botName} (@${botUsername})`).scrollIntoView().then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Delete token
cy.get(`#${id}_delete`).click();
cy.get('#confirmModalButton').click();
// # Try to post again
const msg2 = 'this is a bot message2 ' + botName;
// # Create a post
cy.postBotMessage({channelId: channel.id, message: msg2, token, failOnStatus: false}).then(({status}) => {
// * Validate that posting failed
expect(status, 403);
});
});
});
});
});
});

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

@@ -0,0 +1,76 @@
// 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 @not_cloud @bot_accounts
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Bot accounts ownership and API', () => {
let newTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup({
promoteNewUserAsAdmin: true,
loginAfter: true,
}).then(({team}) => {
newTeam = team;
});
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
DisableBotsWhenOwnerIsDeactivated: true,
},
};
cy.apiUpdateConfig(newSettings);
});
it('MM-T1861 Bots do not re-enable if the owner is re-activated', () => {
// # Create another admin account
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
// # Login as the new admin
cy.apiLogin(sysadmin);
// # Create a new bot as the new admin
cy.apiCreateBot({prefix: 'stay-enabled-bot'}).then(({bot}) => {
// # Login again as main admin
cy.apiAdminLogin();
// # Deactivate the newly created admin
cy.apiDeactivateUser(sysadmin.id);
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// * Validate that the plugin is disabled since its owner is deactivated
cy.get('.bot-list__disabled').scrollIntoView().should('be.visible');
// # Re-activate the newly created admin
cy.apiActivateUser(sysadmin.id);
// # Repeat the test to confirm it stays disabled
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// * Validate that the plugin is disabled even though its owner is activated
cy.get('.bot-list__disabled').scrollIntoView().should('be.visible');
});
});
});
});

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

@@ -0,0 +1,54 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
describe('Bot channel intro and avatar', () => {
let team;
let bot;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
});
cy.makeClient().then(async (client) => {
// # Create bot
bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
});
});
it('MM-T1839 Bots have default profile image visible', () => {
// # Open bot DM channel
cy.visit(`/${team.name}/messages/@${bot.username}`);
// # Get channel intro and bot-post Avatars
cy.get(`#channelIntro .profile-icon > img.Avatar, img.Avatar[alt="${bot.username} profile image"]`).
should(($imgs) => {
// * Verify imgs downloaded
expect($imgs[0].naturalWidth).to.be.greaterThan(0);
expect($imgs[1].naturalWidth).to.be.greaterThan(0);
}).
each(($img) => {
// * Verify img visible and has src
cy.wrap($img).
should('be.visible').
and('have.attr', 'src').
then((url) => cy.request({url, encoding: 'binary'})).
then(({body}) => {
// * Verify matches expected default bot avatar
cy.fixture('bot-default-avatar.png', 'binary').should('deep.equal', body);
});
});
});
});

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

@@ -0,0 +1,66 @@
// 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 @bot_accounts
import {getRandomId} from '../../../utils';
describe('Create bot', () => {
it('MM-T1810 Create a Bot via the UI', () => {
cy.apiUpdateConfig({
ServiceSettings: {
EnableUserAccessTokens: true,
},
});
createBot();
});
it('MM-T1811 Create a Bot when personal access tokens are set to False', () => {
cy.apiUpdateConfig({
ServiceSettings: {
EnableUserAccessTokens: false,
},
});
createBot();
});
});
function createBot() {
cy.apiInitSetup().then(({team}) => {
// # Go to town-square channel
cy.visit(`/${team.name}/channels/town-square`);
cy.postMessage('hello');
// # Go to bot integrations page
cy.uiOpenProductMenu('Integrations');
cy.get('a.integration-option[href$="/bots"]').click();
cy.get('#addBotAccount').click();
// # Fill and submit form
cy.get('#username').type(`bot-${getRandomId()}`);
cy.get('#displayName').type('Test Bot');
cy.get('#saveBot').click();
// * Verify confirmation page
cy.url().
should('include', `/${team.name}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
should((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
cy.get('#doneButton').click();
});
}

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

@@ -0,0 +1,78 @@
// 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 @not_cloud @bot_accounts
import {getRandomId} from '../../../utils';
import {createBotInteractive} from './helpers';
describe('Bot accounts - CRUD Testing', () => {
let newTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.shouldHavePluginUploadEnabled();
// # Set ServiceSettings to expected values
const newSettings = {
EmailSettings: {
SMTPServer: '',
},
PluginSettings: {
Enable: true,
},
};
cy.apiUpdateConfig(newSettings);
// # Create a test bot
cy.apiCreateBot();
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
});
});
it('MM-T1849 Create a Personal Access Token when email config is invalid', () => {
// # Create a test bot and validate that token is created
const botUsername = `bot-${getRandomId()}`;
createBotInteractive(newTeam, botUsername);
cy.get('#doneButton').click();
// # Add a new token to the bot
// * Check that the previously created bot is listed
cy.findByText(`Test Bot (@${botUsername})`).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the 'Create token' button
cy.wrap(el[0].parentElement.parentElement).findByText('Create New Token').should('be.visible').click();
// # Add description
cy.wrap(el[0].parentElement.parentElement).find('input').click().type('description!');
// # Save
cy.findByTestId('saveSetting').click();
// # Click Close button
cy.wrap(el[0].parentElement.parentElement).findByText('Close').should('be.visible').click();
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// * Check that token is visible
cy.wrap(el[0].parentElement.parentElement).findAllByText(/Token ID:/).should('have.length', 2);
});
});
});

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

@@ -0,0 +1,198 @@
// 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 @bot_accounts
import {getRandomId} from '../../../utils';
import {createBotInteractive} from './helpers';
describe('Bot accounts - CRUD Testing', () => {
let newTeam;
let testBot;
before(() => {
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
});
cy.apiAdminLogin();
});
beforeEach(() => {
// # Create a test bot
cy.apiCreateBot().then(({bot}) => {
testBot = bot;
});
});
it('MM-T1841 Long description text', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the edit button
cy.wrap(el[0].parentElement.parentElement).findByText('Edit').should('be.visible').click();
// * Validate redirect to edit screen
cy.url().should('include', `/${newTeam.name}/integrations/bots/edit`);
// # type long string
const longDescription = 'A'.repeat(1020); // 1024 is the limit
cy.get('#description').clear().type(longDescription);
// * Validate that it's fully typed
cy.get('#description').should('have.value', longDescription);
// # type some more characters
cy.get('#description').type('{end}12345');
// * Validate that it's partially updated
cy.get('#description').should('have.value', longDescription + '1234');
// # Update the bot
cy.get('#saveBot').click();
// * Validate that bot saved correctly
cy.url().should('include', `/${newTeam.name}/integrations/bots`);
// * Validate that the description exists
cy.findAllByText(longDescription + '1234').should('exist');
});
});
it('MM-T1842 Change BOT role', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the edit button
cy.wrap(el[0].parentElement.parentElement).findByText('Edit').should('be.visible').click();
// * Validate redirect to edit screen
cy.url().should('include', `/${newTeam.name}/integrations/bots/edit`);
// # Select sysadmin
cy.get('select').select('System Admin');
// * Validate that permissions are set and read only
cy.get('#postChannels').should('be.checked').should('be.disabled');
cy.get('#postAll').should('be.checked').should('be.disabled');
// # Update the bot
cy.get('#saveBot').click();
// * Validate that bot saved correctly
cy.url().should('include', `/${newTeam.name}/integrations/bots`);
});
});
it('MM-T1843 ID along with actual token is created', () => {
// # Create the bot and validate token is visible
createBotInteractive(newTeam);
cy.get('#doneButton').click();
});
it('MM-T1844 Token is hidden when you return to the page but ID is still visible', () => {
// # Create the bot and validate token is visible
const botUsername = `bot-${getRandomId()}`;
createBotInteractive(newTeam, botUsername).then((text) => {
// # Close the Add dialog
cy.get('#doneButton').click();
// * Check that the previously created bot is listed
cy.findByText(`Test Bot (@${botUsername})`).then((el) => {
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// * Validate that token is NOT visible on the next page
const token = text.substr(text.indexOf('Token: ') + 7, 26);
cy.findByText(new RegExp(token)).should('not.exist');
});
});
});
it('MM-T1845 Create a new token via the UI', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the 'Create token' button
cy.wrap(el[0].parentElement.parentElement).findByText('Create New Token').should('be.visible').click();
// # Try saving without description
cy.findByTestId('saveSetting').click();
cy.wrap(el[0].parentElement.parentElement).find('input').scrollIntoView();
// * Check for error message
cy.get('#clientError').should('be.visible');
// # Add description
cy.wrap(el[0].parentElement.parentElement).find('input').click().type(testBot.username + 'description!');
// # Save and check that no error is visible
cy.findByTestId('saveSetting').click();
cy.get('#clientError').should('not.exist');
cy.findAllByText(testBot.username + 'description!').should('exist');
});
});
it('MM-T1848 Delete Token', () => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(testBot.fullDisplayName).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the 'Create token' button
cy.wrap(el[0].parentElement.parentElement).findByText('Create New Token').should('be.visible').click();
// # Add description
cy.wrap(el[0].parentElement.parentElement).find('input').click().type('description!');
// # Save
cy.findByTestId('saveSetting').click();
// # Click Close button
cy.wrap(el[0].parentElement.parentElement).findByText('Close').should('be.visible').click();
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// * Check that token is visible
cy.wrap(el[0].parentElement.parentElement).findByText(/Token ID:/).should('be.visible');
// # Click Delete button
cy.wrap(el[0].parentElement.parentElement).findByText('Delete').should('be.visible').click();
// * Validate that confirmation dialog is visible and click the delete button
cy.get('#confirmModalButton').should('be.visible').click();
// * Check that token is not visible
cy.wrap(el[0].parentElement.parentElement).findByText(/Token ID:/).should('not.exist');
});
});
});

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

@@ -0,0 +1,98 @@
// 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 @bot_accounts
describe('Bot display name', () => {
let offTopicChannel;
let otherSysadmin;
before(() => {
cy.intercept('**/api/v4/**').as('resources');
// # Set ServiceSettings to expected values
const newSettings = {
ServiceSettings: {
EnableUserAccessTokens: false,
},
};
cy.apiUpdateConfig(newSettings);
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
otherSysadmin = sysadmin;
cy.apiLogin(otherSysadmin);
cy.apiInitSetup().then(({team}) => {
cy.apiGetChannelByName(team.name, 'off-topic').then(({channel}) => {
offTopicChannel = channel;
});
cy.visit(`/${team.name}/channels/off-topic`);
cy.wait('@resources');
});
});
});
it('MM-T1813 Display name for bots stays current', () => {
cy.makeClient({user: otherSysadmin}).then((client) => {
// # Create a bot and get bot user id
cy.apiCreateBot().then(({bot}) => {
const botUserId = bot.user_id;
const firstMessage = 'This is the first message from a bot that will change its name';
const secondMessage = 'This is the second message from a bot that has changed its name';
// # Get token from bot's id
cy.apiAccessToken(botUserId, 'Create token').then(({token}) => {
//# Add bot to team
cy.apiAddUserToTeam(offTopicChannel.team_id, botUserId);
// # Post message as bot through api with auth token
const props = {attachments: [{pretext: 'Some Pretext', text: 'Some Text'}]};
cy.postBotMessage({token, message: firstMessage, props, channelId: offTopicChannel.id}).
its('id').
should('exist').
as('botPost');
cy.uiWaitUntilMessagePostedIncludes(firstMessage);
// # Go to the channel
cy.get('#sidebarItem_off-topic').click({force: true});
// * Verify bot display name
cy.get('@botPost').then((postIdA) => {
cy.get(`#post_${postIdA} button.user-popover`).click();
cy.get('#user-profile-popover').
should('be.visible');
cy.findByTestId(`popover-fullname-${bot.username}`).
should('have.text', bot.display_name);
}).then(() => {
// # Change display name after prior verification
cy.wrap(client.patchBot(bot.user_id, {display_name: `NEW ${bot.display_name}`})).then((newBot) => {
cy.postBotMessage({token, message: secondMessage, props, channelId: offTopicChannel.id}).
its('id').
should('exist').
as('newBotPost');
cy.uiWaitUntilMessagePostedIncludes(secondMessage);
// * Verify changed display name
cy.get('@newBotPost').then(() => {
cy.get('#user-profile-popover').
should('be.visible');
cy.findByTestId(`popover-fullname-${bot.username}`).
should('have.text', newBot.display_name);
});
});
});
});
});
});
});
});

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

@@ -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 @bot_accounts
import * as MESSAGES from '../../../fixtures/messages';
import {getRandomId} from '../../../utils';
describe('Edit bot', () => {
let testTeam;
before(() => {
cy.apiInitSetup().then(({team, townSquareUrl}) => {
testTeam = team;
cy.visit(townSquareUrl);
cy.postMessage('hello');
});
});
it('MM-T1840 Description allows for special character', () => {
const userName = `bot-${getRandomId()}`;
const description = MESSAGES.LARGE.concat('!@#$%&*');
// # Create bot
createBot(userName, testTeam.name);
// * Set alias for bot entry in bot list, this also checks that the bot entry exists
cy.get('.backstage-list__item').contains('.backstage-list__item', userName).as('botEntry');
cy.get('@botEntry').then((el) => {
// # Find the edit link for the bot
const editLink = el.find('.item-actions>a');
if (editLink.text() === 'Edit') {
// # Click the edit link for the bot
cy.wrap(editLink).click();
// * Check that user name is as expected
cy.get('#username').should('have.value', userName);
// * Check that details are empty
cy.get('#displayName').should('have.value', '');
cy.get('#description').should('have.value', '');
// # Set long description
cy.get('#description').clear().type(description);
// # Click update button
cy.get('#saveBot').click();
}
});
// * Get bot entry in bot list by username
cy.get('@botEntry').then((el) => {
cy.wrap(el).scrollIntoView();
// * Confirm long description is as expected
cy.wrap(el.find('.bot-details__description')).should('have.text', description);
});
});
function createBot(userName, teamName) {
// # Go to bot integrations page
cy.uiOpenProductMenu('Integrations');
cy.get('a.integration-option[href$="/bots"]').click();
cy.get('#addBotAccount').click();
// # Fill and submit form
cy.get('#username').type(userName);
cy.get('#saveBot').click();
// * Verify confirmation page
cy.url().
should('include', `/${teamName}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
should((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
cy.get('#doneButton').click();
}
});

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

@@ -0,0 +1,132 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @bot_accounts
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Edit bot username', () => {
let team;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
});
});
it('MM-T2923 Edit bot username.', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Verify that the setting is enabled
cy.findByTestId('ServiceSettings.EnableBotAccountCreationtrue', {timeout: TIMEOUTS.ONE_MIN}).should('be.checked');
// # Visit the integrations
goToCreateBot();
const initialBotName = `bot-${getRandomId()}`;
// # Fill and submit form
cy.get('#username').clear().type(initialBotName);
cy.get('#displayName').clear().type('Test Bot');
cy.get('#saveBot').click();
cy.get('#doneButton').click();
// * Set alias for bot entry in bot list, this also checks that the bot entry exists
cy.get('.backstage-list__item').contains('.backstage-list__item', initialBotName).as('botEntry');
cy.get('@botEntry').then((el) => {
// # Find the edit link for the bot
const editLink = el.find('.item-actions>a');
if (editLink.text() === 'Edit') {
// # Click the edit link for the bot
cy.wrap(editLink).click();
// * Check that user name is as expected
cy.get('#username').should('have.value', initialBotName);
// * Check that the display name is correct
cy.get('#displayName').should('have.value', 'Test Bot');
// * Check that description is empty
cy.get('#description').should('have.value', '');
const newBotName = `bot-${getRandomId()}`;
// * Enter the new user name
cy.get('#username').clear().type(newBotName);
// # Click update button
cy.get('#saveBot').click();
cy.wrap(newBotName);
}
}).then((newBotName) => {
// * Set alias for bot entry in bot list, this also checks that the bot entry exists
cy.get('.backstage-list__item').contains('.backstage-list__item', newBotName).as('newbotEntry');
// * Get bot entry in bot list by username
cy.get('@newbotEntry').then((el) => {
cy.wrap(el).scrollIntoView();
});
});
});
it('MM-T1838 Bot naming convention is enforced', () => {
goToCreateBot();
// # Attempt invalid bot usernames
tryUsername('be', NAMING_WARNING_STANDARD);
tryUsername('@be', NAMING_WARNING_STANDARD);
tryUsername('abe.', NAMING_WARNING_ENDING_PERIOD);
// # Attempt valid bot username
const validBotName = `abe-the-bot-${getRandomId()}`;
tryUsername(validBotName);
});
const NAMING_WARNING_STANDARD = 'Usernames have to begin with a lowercase letter and be 3-22 characters long. You can use lowercase letters, numbers, periods, dashes, and underscores.';
const NAMING_WARNING_ENDING_PERIOD = 'Bot usernames cannot have a period as the last character';
function tryUsername(name, warningMessage) {
cy.get('#username').clear().type(name);
cy.get('#saveBot').click();
if (warningMessage) {
// * Verify expected warning
cy.get('.backstage-form__footer .has-error').should('have.text', warningMessage);
} else {
// * Verify confirmation page
cy.url().
should('include', `/${team.name}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
and('include.text', name).
and((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
// # back to start
goToCreateBot();
}
}
function goToCreateBot() {
cy.visit(`/${team.name}/integrations/bots`);
// * Assert that adding bots possible
cy.get('#addBotAccount', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible').click();
}
});

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

@@ -0,0 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {getRandomId} from '../../../utils';
import * as TIMEOUTS from '../../../fixtures/timeouts';
export function createBotInteractive(team, username = `bot-${getRandomId()}`) {
// # Visit the Integrations > Bot Accounts page
cy.visit(`/${team.name}/integrations/bots`);
// # Click add bot
cy.get('#addBotAccount').click();
// # Fill and submit form
cy.get('#username').type(username);
cy.get('#displayName').type('Test Bot');
cy.get('#saveBot').click();
// * Verify confirmation page
cy.url({timeout: TIMEOUTS.ONE_MIN}).
should('include', `/${team.name}/integrations/confirm`).
should('match', /token=[a-zA-Z0-9]{26}/);
// * Verify confirmation form/token
cy.get('div.backstage-form').
should('include.text', 'Setup Successful').
should((confirmation) => {
expect(confirmation.text()).to.match(/Token: [a-zA-Z0-9]{26}/);
});
return cy.get('div.backstage-form').invoke('text');
}

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

@@ -0,0 +1,71 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @channels @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
import {generateRandomUser} from '../../../support/api/user';
describe('Bots in lists', () => {
let team;
let channel;
let bots;
let createdUsers;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
});
cy.makeClient().then(async (client) => {
// # Create bots
bots = await Promise.all([
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
]);
// # Create users
createdUsers = await Promise.all([
client.createUser(generateRandomUser()),
client.createUser(generateRandomUser()),
]);
await Promise.all([
...bots,
...createdUsers,
].map(async (user) => {
// * Verify username exists
cy.wrap(user).its('username');
// # Add to team and channel
await client.addToTeam(team.id, user.user_id ?? user.id);
await client.addToChannel(user.user_id ?? user.id, channel.id);
}));
});
});
it('MM-T1834 Bots are not listed on “Users” list in System Console > Users', () => {
// # Go to system console > users
cy.visit('/admin_console/user_management/users');
bots.forEach(({username}) => {
// # Search for bot
cy.get('#searchUsers').clear().type(`@${username}`);
// * Verify bot not in list
cy.findByTestId('noUsersFound').should('have.text', 'No users found');
// * Verify pseudo checksum total of non bot users
cy.get('#searchableUserListTotal').contains('0 users of').should('be.visible');
});
});
});

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

@@ -0,0 +1,105 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
import {generateRandomUser} from '../../../support/api/user';
describe('Bots in lists', () => {
let team;
let channel;
let testUser;
const STATUS_PRIORITY = {
online: 0,
away: 1,
dnd: 2,
offline: 3,
ooo: 3,
};
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
testUser = out.user;
});
cy.makeClient().then(async (client) => {
// # Create bots
const bots = await Promise.all([
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
]);
// # Create users
const createdUsers = await Promise.all([
client.createUser(generateRandomUser()),
client.createUser(generateRandomUser()),
]);
await Promise.all([
...bots,
...createdUsers,
].map(async (user) => {
// * Verify username exists
cy.wrap(user).its('username');
// # Add to team and channel
await client.addToTeam(team.id, user.user_id ?? user.id);
await client.addToChannel(user.user_id ?? user.id, channel.id);
}));
});
});
it('MM-T1835 Channel Members list for BOTs', () => {
cy.makeClient({user: testUser}).then((client) => {
// # Login as regular user and visit a channel
cy.apiLogin(testUser);
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Open channel members
cy.get('.channel-header__trigger').click();
cy.findByText('Manage Members').click();
cy.get('.more-modal__row .more-modal__name').then(async ($query) => {
// # Extract usernames from jQuery collection
const usernames = $query.toArray().map(({innerText}) => innerText.split('\n')[0]);
// # Get users
const profiles = await client.getProfilesByUsernames(usernames);
const statuses = await client.getStatusesByIds(profiles.map((user) => user.id));
const users = Cypress._.zip(profiles, statuses).map(([profile, status]) => ({...profile, ...status}));
// # Sort 'em
const sortedUsers = Cypress._.sortBy(users, [
({is_bot: isBot}) => (isBot ? 1 : 0), // users first
({status}) => STATUS_PRIORITY[status],
({username}) => username,
]);
// * Verify order of member-dropdown users against API-sourced/data-sorted version
cy.wrap(usernames).should('deep.equal', sortedUsers.map(({username}) => username));
});
// * Verify no statuses on bots
cy.get('.more-modal__row--bot .status-wrapper .status').should('not.exist');
// * Verify bot badges
cy.get('.more-modal__row--bot .Tag').then(($tags) => {
$tags.toArray().forEach((tagEl) => {
cy.wrap(tagEl).then(() => tagEl.scrollIntoView());
cy.wrap(tagEl).should('be.visible').and('have.text', 'BOT');
});
});
});
});
});

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

@@ -0,0 +1,96 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
import {createChannelPatch} from '../../../support/api/channel';
describe('Managing bots in Teams and Channels', () => {
let team;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'sample.mattermost.com',
},
});
cy.apiInitSetup({loginAfter: true}).then((out) => {
team = out.team;
});
});
it('MM-T1815 Add a BOT to a team that has email restricted', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.getChannelByName(team.id, 'town-square');
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Invite bot to team
const bot = await client.createBot(createBotPatch());
cy.uiInviteMemberToCurrentTeam(bot.username);
// * Verify system message in-channel
cy.uiWaitUntilMessagePostedIncludes(`@${bot.username} added to the team by you.`);
});
});
it('MM-T1816 Add a BOT to a channel', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.createChannel(createChannelPatch(team.id, 'a-chan', 'A Channel'));
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Add bot to team
const bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
// # Add bot to channel in team
cy.uiAddUsersToCurrentChannel([bot.username]);
// * Verify system message in-channel
cy.uiWaitUntilMessagePostedIncludes(`@${bot.username} added to the channel by you.`);
});
});
it('MM-T1817 Add a BOT to a channel that is not on the Team', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.createChannel(createChannelPatch(team.id, 'a-chan', 'A Channel'));
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Invite bot to team
const bot = await client.createBot(createBotPatch());
cy.postMessage(`/invite @${bot.username} `);
// * Verify system message in-channel
cy.uiWaitUntilMessagePostedIncludes(`@${bot.username} is not a member of the team.`);
});
});
it('MM-T1818 No ephemeral post about Adding a bot to a channel When Bot is mentioned', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.createChannel(createChannelPatch(team.id, 'a-chan', 'A Channel'));
cy.visit(`/${team.name}/channels/${channel.name}`);
// # And bot to team
const bot = await client.createBot(createBotPatch());
cy.apiAddUserToTeam(team.id, bot.user_id);
// # Mention bot
const message = `hey @${bot.username}, tell me a rhyme..`;
cy.postMessage(message);
// * Verify no ephemeral post is shown asking if you want to invite the bot to the server
cy.uiGetNthPost(-1).should('contain.text', message);
});
});
});

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

@@ -0,0 +1,101 @@
// 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 @bot_accounts @plugin @not_cloud
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {matterpollPlugin} from '../../../utils/plugins';
describe('Managing bot accounts', () => {
let newTeam;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.shouldHavePluginUploadEnabled();
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
it('MM-T1859 Bot is kept active when owner is disabled', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.DisableBotsWhenOwnerIsDeactivatedfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click();
// # Create another admin account
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
// # Login as the new admin
cy.apiLogin(sysadmin);
// # Create a new bot as the new admin
cy.apiCreateBot({prefix: 'stay-enabled-bot'}).then(({bot}) => {
// # Login again as main admin
cy.apiAdminLogin();
// # Deactivate the newly created admin
cy.apiDeactivateUser(sysadmin.id);
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.display_name);
// * Validate that the plugin is still active, even though its owner is disabled
cy.get('.bot-list__disabled').should('not.exist');
cy.findByText(bot.fullDisplayName).scrollIntoView().should('be.visible');
cy.visit(`/${newTeam.name}/messages/@sysadmin`);
// # Get last post message text
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('postMessageText');
});
// * Verify entire message
cy.get('@postMessageText').
should('be.visible').
and('contain.text', `${sysadmin.username} was deactivated. They managed the following bot accounts`).
and('contain.text', bot.username);
});
});
});
it('MM-T1853 Bots managed plugins can be created when Enable Bot Account Creation is set to false', () => {
// # Upload and enable "matterpoll" plugin
cy.apiUploadAndEnablePlugin(matterpollPlugin);
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.EnableBotAccountCreationfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click();
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Validate that plugin installed ok
cy.contains('Matterpoll (@matterpoll)', {timeout: TIMEOUTS.ONE_MIN});
});
});

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

@@ -0,0 +1,206 @@
// 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 @bot_accounts
import * as TIMEOUTS from '../../../fixtures/timeouts';
import {getRandomId} from '../../../utils';
describe('Managing bot accounts', () => {
let newTeam;
before(() => {
// # Create and visit new channel
cy.apiInitSetup().then(({team}) => {
newTeam = team;
});
});
beforeEach(() => {
cy.apiAdminLogin();
const newSettings = {
ServiceSettings: {
EnableBotAccountCreation: true,
},
};
cy.apiUpdateConfig(newSettings);
});
it('MM-T1851 No option to create BOT accounts when Enable Bot Account Creation is set to False.', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.EnableBotAccountCreationfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click();
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Assert that adding bots is not possible
cy.get('#addBotAccount', {timeout: TIMEOUTS.ONE_MIN}).should('not.exist');
});
it('MM-T1852 Bot creation via API is not permitted when Enable Bot Account Creation is set to False', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// # Click 'false' to disable
cy.findByTestId('ServiceSettings.EnableBotAccountCreationfalse', {timeout: TIMEOUTS.ONE_MIN}).click();
// # Save
cy.findByTestId('saveSetting').should('be.enabled').click().wait(TIMEOUTS.HALF_SEC);
// * Validate that creating bot fails
cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/api/v4/bots',
method: 'POST',
failOnStatusCode: false,
body: {
username: `bot-${getRandomId()}`,
display_name: 'test bot',
description: 'test bot',
},
}).then((response) => {
expect(response.status).to.equal(403);
expect(response.body.message).to.equal('Bot creation has been disabled.');
return cy.wrap(response);
});
});
it('MM-T1854 Bots can be create when Enable Bot Account Creation is set to True.', () => {
// # Visit bot config
cy.visit('/admin_console/integrations/bot_accounts');
// * Check that creation is enabled
cy.findByTestId('ServiceSettings.EnableBotAccountCreationtrue', {timeout: TIMEOUTS.ONE_MIN}).should('be.checked');
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Assert that adding bots is possible
cy.get('#addBotAccount', {timeout: TIMEOUTS.ONE_MIN}).should('be.visible');
});
it('MM-T1856 Disable Bot', () => {
cy.apiCreateBot({prefix: 'test-bot'}).then(({bot}) => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Filter bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// * Check that the previously created bot is listed
cy.findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).scrollIntoView().then((el) => {
// # Click the disable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(3)').should('be.visible').click();
});
// * Check that the bot is in the 'disabled' section
cy.get('.bot-list__disabled').scrollIntoView().findByText(bot.fullDisplayName).should('be.visible');
});
});
it('MM-T1857 Enable Bot', () => {
cy.apiCreateBot({prefix: 'test-bot'}).then(({bot}) => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).scrollIntoView().then((el) => {
// # Click the disable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(3)').should('be.visible').click();
});
// # Filter bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.username);
// # Re-enable the bot
cy.get('.bot-list__disabled').scrollIntoView().findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).scrollIntoView().then((el) => {
// # Click the enable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(1)').should('be.visible').click();
});
// * Check that the bot is in the 'enabled' section
cy.findByText(bot.fullDisplayName).scrollIntoView().should('be.visible');
cy.get('.bot-list__disabled').should('not.exist');
});
});
it('MM-T1858 Search active and disabled Bot accounts', () => {
cy.apiCreateBot({prefix: 'hello-bot'}).then(({bot}) => {
// # Visit the integrations
cy.visit(`/${newTeam.name}/integrations/bots`);
// * Check that the previously created bot is listed
cy.findByText(bot.fullDisplayName, {timeout: TIMEOUTS.ONE_MIN}).then((el) => {
// # Make sure it's on the screen
cy.wrap(el[0].parentElement.parentElement).scrollIntoView();
// # Click the disable button
cy.wrap(el[0].parentElement.parentElement).find('button:nth-child(3)').should('be.visible').click();
});
// * Validate that disabled section appears
cy.get('.bot-list__disabled').scrollIntoView().should('be.visible');
// # Search for the other bot
cy.apiCreateBot({prefix: 'other-bot'}).then(({bot: otherBot}) => {
cy.get('#searchInput').type(otherBot.username);
// * Validate that disabled section disappears
cy.get('.bot-list__disabled').should('not.exist');
});
});
});
it('MM-T1860 Bot is disabled when owner is deactivated', () => {
// # Create another admin account
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
// # Login as the new admin
cy.apiLogin(sysadmin);
// # Create a new bot as the new admin
cy.apiCreateBot({prefix: 'stay-enabled-bot'}).then(({bot}) => {
// # Login again as main admin
cy.apiAdminLogin();
// # Deactivate the newly created admin
cy.apiDeactivateUser(sysadmin.id);
// # Get bot list
cy.visit(`/${newTeam.name}/integrations/bots`);
// # Search for the other bot
cy.get('#searchInput', {timeout: TIMEOUTS.ONE_MIN}).type(bot.display_name);
// * Validate that the plugin is disabled since it's owner is deactivate
cy.get('.bot-list__disabled').scrollIntoView().findByText(bot.fullDisplayName).scrollIntoView().should('be.visible');
cy.visit(`/${newTeam.name}/messages/@sysadmin`);
// # Get last post message text
cy.getLastPostId().then((postId) => {
cy.get(`#postMessageText_${postId}`).as('postMessageText');
});
// * Verify entire message
cy.get('@postMessageText').
should('be.visible').
and('contain.text', `${sysadmin.username} was deactivated. They managed the following bot accounts`).
and('contain.text', bot.username);
});
});
});
});

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

@@ -0,0 +1,53 @@
// 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 @bot_accounts
describe('Bot post message', () => {
let offTopicChannel;
before(() => {
cy.apiInitSetup().then(({team}) => {
cy.apiGetChannelByName(team.name, 'off-topic').then(({channel}) => {
offTopicChannel = channel;
});
cy.visit(`/${team.name}/channels/off-topic`);
});
});
it('MM-T1812 Post as a bot when personal access tokens are false', () => {
// # Create a bot and get bot user id
cy.apiCreateBot().then(({bot}) => {
const botUserId = bot.user_id;
const message = 'This is a message from a bot.';
// # Get token from bot's id
cy.apiAccessToken(botUserId, 'Create token').then(({token}) => {
//# Add bot to team
cy.apiAddUserToTeam(offTopicChannel.team_id, botUserId);
// # Post message as bot through api with auth token
const props = {attachments: [{pretext: 'Some Pretext', text: 'Some Text'}]};
cy.postBotMessage({token, message, props, channelId: offTopicChannel.id}).
its('id').
should('exist').
as('botPost');
// * Verify bot message
cy.uiWaitUntilMessagePostedIncludes(message);
cy.get('@botPost').then((postId) => {
cy.get(`#postMessageText_${postId}`).
should('be.visible').
and('have.text', message);
});
});
});
});
});

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

@@ -0,0 +1,58 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
describe('Managing bots in Teams and Channels', () => {
let team;
before(() => {
cy.apiUpdateConfig({
TeamSettings: {
RestrictCreationToDomains: 'sample.mattermost.com',
},
});
cy.apiInitSetup({loginAfter: true}).then((out) => {
team = out.team;
});
});
it('MM-T1819 Promote a BOT to team admin', () => {
cy.makeClient().then(async (client) => {
// # Go to channel
const channel = await client.getChannelByName(team.id, 'off-topic');
cy.visit(`/${team.name}/channels/${channel.name}`);
// # Add bot to team
const bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
// # Open team menu and click 'Manage Members'
cy.uiOpenTeamMenu('Manage Members');
// # Find bot
cy.get('.more-modal__list').find('.more-modal__row').its('length').should('be.gt', 0);
cy.get('#searchUsersInput').type(bot.username);
// # Wait for loading screen
cy.get('#teamMembersModal .loading-screen').should('be.visible');
// # Find bot member dropdown
cy.get(`#teamMembersDropdown_${bot.username}`).as('memberDropdown').should('contain.text', 'Member').click();
// # Promote bot to team admin
cy.findByTestId('userListItemActions').find('button').contains('Make Team Admin').click();
// * Verify bot was promoted
cy.get('@memberDropdown').should('contain.text', 'Team Admin');
});
});
});

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

@@ -0,0 +1,94 @@
// 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 @bot_accounts @not_cloud
import {createBotPatch} from '../../../support/api/bots';
import {generateRandomUser} from '../../../support/api/user';
describe('Bot accounts', () => {
let team;
let channel;
let testUser;
let bots;
let createdUsers;
before(() => {
cy.shouldNotRunOnCloudEdition();
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
testUser = out.user;
});
cy.makeClient().then(async (client) => {
// # Create bots
bots = await Promise.all([
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
client.createBot(createBotPatch()),
]);
// # Create users
createdUsers = await Promise.all([
client.createUser(generateRandomUser()),
client.createUser(generateRandomUser()),
]);
await Promise.all([
...bots,
...createdUsers,
].map(async (user) => {
// * Verify username exists
cy.wrap(user).its('username');
// # Add to team and channel
await client.addToTeam(team.id, user.user_id ?? user.id);
await client.addToChannel(user.user_id ?? user.id, channel.id);
}));
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
it('MM-T1836 Bot accounts display', () => {
// # Login as regular user and visit a channel
cy.apiLogin(testUser);
cy.visit(`/${team.name}/messages/@${bots[0].username}`);
cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) .SidebarChannel.active > .SidebarLink').then(($link) => {
// * Verify DM label
cy.wrap($link).find('.SidebarChannelLinkLabel').should('have.text', bots[0].username);
// * Verify bot icon exists
cy.wrap($link).find('.Avatar').should('exist').
and('have.attr', 'src').
then((url) => cy.request({url, encoding: 'binary'})).
then(({body}) => {
// * Verify it matches default bot avatar
cy.fixture('bot-default-avatar.png', 'binary').should('deep.equal', body);
});
});
cy.postMessage('Bump bot chat recency');
// # Open a new DM
cy.visit(`/${team.name}/messages/@${createdUsers[0].username}`);
cy.postMessage('Hello, regular user');
// * Verify Bots and Regular users as siblings in DMs
cy.get('.SidebarChannelGroup:contains(DIRECT MESSAGES) .SidebarChannel.active').siblings('.SidebarChannel').then(($siblings) => {
cy.wrap($siblings).contains('.SidebarChannelLinkLabel', bots[0].username);
});
});
});

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

@@ -0,0 +1,96 @@
// 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 @bot_accounts
import {createBotPatch} from '../../../support/api/bots';
describe('Bot tags', () => {
let me;
let team;
let channel;
let postId;
before(() => {
cy.apiInitSetup().then((out) => {
team = out.team;
channel = out.channel;
});
let meId;
cy.getCurrentUserId().then((id) => {
meId = id;
});
cy.makeClient().then(async (client) => {
// # Setup state
me = await client.getUser(meId);
const bot = await client.createBot(createBotPatch());
await client.addToTeam(team.id, bot.user_id);
await client.addToChannel(bot.user_id, channel.id);
const {token} = await client.createUserAccessToken(bot.user_id, 'Create token');
const message = `Message for @${me.username}. Signed, @${bot.username}.`;
// # Post message as bot through api with auth token
const props = {attachments: [{pretext: 'Some Pretext', text: 'Some Text'}]};
cy.postBotMessage({token, message, props, channelId: channel.id}).then(async ({id}) => {
postId = id;
await client.pinPost(postId);
cy.visit(`/${team.name}/channels/${channel.name}`);
cy.clickPostDotMenu(postId);
cy.get(`#CENTER_flagIcon_${postId}`).click();
});
});
});
it('MM-T1831 BOT tag is visible in search results', () => {
// # Open search
cy.uiSearchPosts(`Message for @${me.username}`);
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Search Results');
rhsPostHasBotBadge(postId);
});
it('MM-T1832 BOT tag is visible in Recent Mentions', () => {
// # Open mentions
cy.uiGetRecentMentionButton().click();
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Recent Mentions');
rhsPostHasBotBadge(postId);
});
it('MM-T1833 BOT tag is visible in Pinned Posts', () => {
// # Open pinned posts
cy.uiGetChannelPinButton().click();
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Pinned Posts');
rhsPostHasBotBadge(postId);
});
it('MM-T3659 BOT tag is visible in Saved Posts', () => {
// # Open saved posts
cy.uiGetSavedPostButton().click();
// * Verify bot badge
cy.get('.sidebar--right__title').should('contain.text', 'Saved Posts');
rhsPostHasBotBadge(postId);
});
});
function rhsPostHasBotBadge(postId) {
cy.get(`.post#searchResult_${postId} .Tag`).should('be.visible').and('have.text', 'BOT');
}