Move /e2e -> /e2e-tests
Этот коммит содержится в:
67
e2e-tests/cypress/tests/support/api/bots.d.ts
поставляемый
Обычный файл
67
e2e-tests/cypress/tests/support/api/bots.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Create a bot.
|
||||
* See https://api.mattermost.com/#tag/bots/paths/~1bots/post
|
||||
* @param {string} options.bot - predefined `bot` object instead of random bot
|
||||
* @param {string} options.prefix - 'bot' (default) or any prefix to easily identify a bot
|
||||
* @returns {Bot} out.bot: `Bot` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateBot().then(({bot}) => {
|
||||
* // do something with bot
|
||||
* });
|
||||
*/
|
||||
apiCreateBot({bot: BotPatch, prefix: string}?): Chainable<{bot: Bot & {fullDisplayName: string}}>;
|
||||
|
||||
/**
|
||||
* Get bots.
|
||||
* See https://api.mattermost.com/#tag/bots/paths/~1bots/get
|
||||
* @param {number} options.page - The page to select
|
||||
* @param {number} options.perPage - The number of users per page. There is a maximum limit of 200 users per page
|
||||
* @param {boolean} options.includeDeleted - If deleted bots should be returned
|
||||
* @returns {Bot[]} out.bots: `Bot[]` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetBots();
|
||||
*/
|
||||
apiGetBots(page: number, perPage: number, includeDeleted: boolean): Chainable<{bots: Bot[]}>;
|
||||
|
||||
/**
|
||||
* Disable bot.
|
||||
* See https://api.mattermost.com/#tag/bots/operation/DisableBot
|
||||
* @param {string} userId - User ID
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDisableBot('user-id);
|
||||
*/
|
||||
apiDisableBot(userId: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Deactivate test bots.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeactivateTestBots();
|
||||
*/
|
||||
apiDeactivateTestBots(): Chainable<>;
|
||||
}
|
||||
}
|
||||
73
e2e-tests/cypress/tests/support/api/bots.js
Обычный файл
73
e2e-tests/cypress/tests/support/api/bots.js
Обычный файл
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getRandomId} from '../../utils';
|
||||
|
||||
// *****************************************************************************
|
||||
// Bots
|
||||
// https://api.mattermost.com/#tag/bots
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiCreateBot', ({prefix, bot = createBotPatch(prefix)} = {}) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/bots',
|
||||
method: 'POST',
|
||||
body: bot,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
const {body} = response;
|
||||
return cy.wrap({
|
||||
bot: {
|
||||
...body,
|
||||
fullDisplayName: `${body.display_name} (@${body.username})`,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetBots', (page = 0, perPage = 200, includeDeleted = false) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/bots?page=${page}&per_page=${perPage}&include_deleted=${includeDeleted}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({bots: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDisableBot', (userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/bots/${userId}/disable`,
|
||||
method: 'POST',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
export function createBotPatch(prefix = 'bot') {
|
||||
const randomId = getRandomId();
|
||||
|
||||
return {
|
||||
username: `${prefix}-${randomId}`,
|
||||
display_name: `Test Bot ${randomId}`,
|
||||
description: `Test bot description ${randomId}`,
|
||||
};
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiDeactivateTestBots', () => {
|
||||
return cy.apiGetBots().then(({bots}) => {
|
||||
bots.forEach((bot) => {
|
||||
if (bot?.display_name?.includes('Test Bot') || bot?.username.startsWith('bot-')) {
|
||||
cy.apiDisableBot(bot.user_id);
|
||||
cy.apiDeactivateUser(bot.user_id);
|
||||
|
||||
// Log for debugging
|
||||
cy.log(`Deactivated Bot: "${bot.username}"`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
32
e2e-tests/cypress/tests/support/api/brand.d.ts
поставляемый
Обычный файл
32
e2e-tests/cypress/tests/support/api/brand.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Delete the custom brand image.
|
||||
* See https://api.mattermost.com/#tag/brand/paths/~1brand~1image/delete
|
||||
* @returns {Response} response: Cypress-chainable response which should have either a successful HTTP status of 200 OK
|
||||
* or a 404 Not Found in case that the image didn't exists to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeleteBrandImage();
|
||||
*/
|
||||
apiDeleteBrandImage(): Chainable<Record<string, any>>;
|
||||
}
|
||||
}
|
||||
20
e2e-tests/cypress/tests/support/api/brand.js
Обычный файл
20
e2e-tests/cypress/tests/support/api/brand.js
Обычный файл
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// Brand
|
||||
// https://api.mattermost.com/#tag/brand
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiDeleteBrandImage', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/brand/image',
|
||||
method: 'DELETE',
|
||||
failOnStatusCode: false,
|
||||
}).then((response) => {
|
||||
// both deleted and not existing responses are valid
|
||||
expect(response.status).to.be.oneOf([200, 404]);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
216
e2e-tests/cypress/tests/support/api/channel.d.ts
поставляемый
Обычный файл
216
e2e-tests/cypress/tests/support/api/channel.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Create a new channel.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels/post
|
||||
* @param {String} teamId - Unique handler for a team, will be present in the team URL
|
||||
* @param {String} name - Unique handler for a channel, will be present in the team URL
|
||||
* @param {String} displayName - Non-unique UI name for the channel
|
||||
* @param {String} type - 'O' for a public channel (default), 'P' for a private channel
|
||||
* @param {String} purpose - A short description of the purpose of the channel
|
||||
* @param {String} header - Markdown-formatted text to display in the header of the channel
|
||||
* @param {Boolean} [unique=true] - if true (default), it will create with unique/random channel name.
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateChannel('team-id', 'test-channel', 'Test Channel').then(({channel}) => {
|
||||
* // do something with channel
|
||||
* });
|
||||
*/
|
||||
apiCreateChannel(
|
||||
teamId: string,
|
||||
name: string,
|
||||
displayName: string,
|
||||
type?: string,
|
||||
purpose?: string,
|
||||
header?: string,
|
||||
unique: boolean = true
|
||||
): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Create a new direct message channel between two users.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1direct/post
|
||||
* @param {string[]} userIds - The two user ids to be in the direct message
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateDirectChannel(['user-1-id', 'user-2-id']).then(({channel}) => {
|
||||
* // do something with channel
|
||||
* });
|
||||
*/
|
||||
apiCreateDirectChannel(userIds: string[]): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Create a new group message channel to group of users via API. If the logged in user's id is not included in the list, it will be appended to the end.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1group/post
|
||||
* @param {string[]} userIds - User ids to be in the group message channel
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateGroupChannel(['user-1-id', 'user-2-id', 'current-user-id']).then(({channel}) => {
|
||||
* // do something with channel
|
||||
* });
|
||||
*/
|
||||
apiCreateGroupChannel(userIds: string[]): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Update a channel.
|
||||
* The fields that can be updated are listed as parameters. Omitted fields will be treated as blanks.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}/put
|
||||
* @param {string} channelId - The channel ID to be updated
|
||||
* @param {Channel} channel - Channel object to be updated
|
||||
* @param {string} channel.name - The unique handle for the channel, will be present in the channel URL
|
||||
* @param {string} channel.display_name - The non-unique UI name for the channel
|
||||
* @param {string} channel.purpose - A short description of the purpose of the channel
|
||||
* @param {string} channel.header - Markdown-formatted text to display in the header of the channel
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiUpdateChannel('channel-id', {name: 'new-name', display_name: 'New Display Name'. 'purpose': 'Updated purpose', 'header': 'Updated header'});
|
||||
*/
|
||||
apiUpdateChannel(channelId: string, channel: Channel): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Partially update a channel by providing only the fields you want to update.
|
||||
* Omitted fields will not be updated.
|
||||
* The fields that can be updated are defined in the request body, all other provided fields will be ignored.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}~1patch/put
|
||||
* @param {string} channelId - The channel ID to be patched
|
||||
* @param {Channel} channel - Channel object to be patched
|
||||
* @param {string} channel.name - The unique handle for the channel, will be present in the channel URL
|
||||
* @param {string} channel.display_name - The non-unique UI name for the channel
|
||||
* @param {string} channel.purpose - A short description of the purpose of the channel
|
||||
* @param {string} channel.header - Markdown-formatted text to display in the header of the channel
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiPatchChannel('channel-id', {name: 'new-name', display_name: 'New Display Name'});
|
||||
*/
|
||||
apiPatchChannel(channelId: string, channel: Partial<Channel>): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Updates channel's privacy allowing changing a channel from Public to Private and back.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}~1privacy/put
|
||||
* @param {string} channelId - The channel ID to be patched
|
||||
* @param {string} privacy - The privacy the channel should be set too. P = Private, O = Open
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiPatchChannelPrivacy('channel-id', 'P');
|
||||
*/
|
||||
apiPatchChannelPrivacy(channelId: string, privacy: string): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Get channel from the provided channel id string.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}/get
|
||||
* @param {string} channelId - Channel ID
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetChannel('channel-id').then(({channel}) => {
|
||||
* // do something with channel
|
||||
* });
|
||||
*/
|
||||
apiGetChannel(channelId: string): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Gets a channel from the provided team name and channel name strings.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1teams~1name~1{team_name}~1channels~1name~1{channel_name}/get
|
||||
* @param {string} teamName - Team name
|
||||
* @param {string} channelName - Channel name
|
||||
* @returns {Channel} `out.channel` as `Channel`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetChannelByName('team-name', 'channel-name').then(({channel}) => {
|
||||
* // do something with channel
|
||||
* });
|
||||
*/
|
||||
apiGetChannelByName(teamName: string, channelName: string): Chainable<{channel: Channel}>;
|
||||
|
||||
/**
|
||||
* Get a list of all channels.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels/get
|
||||
* @returns {Channel[]} `out.channels` as `Channel[]`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetAllChannels().then(({channels}) => {
|
||||
* // do something with channels
|
||||
* });
|
||||
*/
|
||||
apiGetAllChannels(): Chainable<{channels: Channel[]}>;
|
||||
|
||||
/**
|
||||
* Get channels for user.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1users~1{user_id}~1teams~1{team_id}~1channels/get
|
||||
* @returns {Channel[]} `out.channels` as `Channel[]`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetChannelsForUser().then(({channels}) => {
|
||||
* // do something with channels
|
||||
* });
|
||||
*/
|
||||
apiGetChannelsForUser(): Chainable<{channels: Channel[]}>;
|
||||
|
||||
/**
|
||||
* Soft deletes a channel, by marking the channel as deleted in the database.
|
||||
* Soft deleted channels will not be accessible in the user interface.
|
||||
* Direct and group message channels cannot be deleted.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}/delete
|
||||
* @param {string} channelId - The channel ID to be deleted
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeleteChannel('channel-id');
|
||||
*/
|
||||
apiDeleteChannel(channelId: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Add a user to a channel by creating a channel member object.
|
||||
* See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}~1members/post
|
||||
* @param {string} channelId - Channel ID
|
||||
* @param {string} userId - User ID to add to the channel
|
||||
* @returns {ChannelMembership} `out.member` as `ChannelMembership`
|
||||
*
|
||||
* @example
|
||||
* cy.apiAddUserToChannel('channel-id', 'user-id').then(({member}) => {
|
||||
* // do something with member
|
||||
* });
|
||||
*/
|
||||
apiAddUserToChannel(channelId: string, userId: string): Chainable<ChannelMembership>;
|
||||
|
||||
/**
|
||||
* Convenient command that create, post into and then archived a channel.
|
||||
* @param {string} name - name of channel to be created
|
||||
* @param {string} displayName - display name of channel to be created
|
||||
* @param {string} type - type of channel
|
||||
* @param {string} teamId - team Id where the channel will be added
|
||||
* @param {string[]} [messages] - messages to be posted before archiving a channel
|
||||
* @param {UserProfile} [user] - user who will be posting the messages
|
||||
* @returns {Channel} archived channel
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateArchivedChannel('channel-name', 'channel-display-name', 'team-id', messages, user).then((channel) => {
|
||||
* // do something with channel
|
||||
* });
|
||||
*/
|
||||
apiCreateArchivedChannel(name: string, displayName: string, type: string, teamId: string, messages?: string[], user?: UserProfile): Chainable<Channel>;
|
||||
}
|
||||
}
|
||||
188
e2e-tests/cypress/tests/support/api/channel.js
Обычный файл
188
e2e-tests/cypress/tests/support/api/channel.js
Обычный файл
@@ -0,0 +1,188 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getRandomId} from '../../utils';
|
||||
|
||||
// *****************************************************************************
|
||||
// Channels
|
||||
// https://api.mattermost.com/#tag/channels
|
||||
// *****************************************************************************
|
||||
|
||||
export function createChannelPatch(teamId, name, displayName, type = 'O', purpose = '', header = '', unique = true) {
|
||||
const randomSuffix = getRandomId();
|
||||
|
||||
return {
|
||||
team_id: teamId,
|
||||
name: unique ? `${name}-${randomSuffix}` : name,
|
||||
display_name: unique ? `${displayName} ${randomSuffix}` : displayName,
|
||||
type,
|
||||
purpose,
|
||||
header,
|
||||
};
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiCreateChannel', (...args) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels',
|
||||
method: 'POST',
|
||||
body: createChannelPatch(...args),
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateDirectChannel', (userIds = []) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels/direct',
|
||||
method: 'POST',
|
||||
body: userIds,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateGroupChannel', (userIds = []) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels/group',
|
||||
method: 'POST',
|
||||
body: userIds,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUpdateChannel', (channelId, channelData) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels/' + channelId,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
id: channelId,
|
||||
...channelData,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiPatchChannel', (channelId, channelData) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'PUT',
|
||||
url: `/api/v4/channels/${channelId}/patch`,
|
||||
body: channelData,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiPatchChannelPrivacy', (channelId, privacy = 'O') => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'PUT',
|
||||
url: `/api/v4/channels/${channelId}/privacy`,
|
||||
body: {privacy},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetChannel', (channelId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/channels/${channelId}`,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetChannelByName', (teamName, channelName) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/teams/name/${teamName}/channels/name/${channelName}`,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({channel: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetAllChannels', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({channels: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetChannelsForUser', (userId, teamId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/teams/${teamId}/channels`,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({channels: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeleteChannel', (channelId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels/' + channelId,
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiAddUserToChannel', (channelId, userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels/' + channelId + '/members',
|
||||
method: 'POST',
|
||||
body: {
|
||||
user_id: userId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({member: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiRemoveUserFromChannel', (channelId, userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/channels/' + channelId + '/members/' + userId,
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({member: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateArchivedChannel', (name, displayName, type = 'O', teamId, messages = [], user) => {
|
||||
return cy.apiCreateChannel(teamId, name, displayName, type).then(({channel}) => {
|
||||
Cypress._.forEach(messages, (message) => {
|
||||
cy.postMessageAs({
|
||||
sender: user,
|
||||
message,
|
||||
channelId: channel.id,
|
||||
});
|
||||
});
|
||||
|
||||
cy.apiDeleteChannel(channel.id);
|
||||
return cy.wrap(channel);
|
||||
});
|
||||
});
|
||||
41
e2e-tests/cypress/tests/support/api/cloud.d.ts
поставляемый
Обычный файл
41
e2e-tests/cypress/tests/support/api/cloud.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get products.
|
||||
* See https://api.mattermost.com/#operation/GetCloudProducts
|
||||
* @returns {Product[]} out.Products: `Product[]` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetCloudProducts();
|
||||
*/
|
||||
apiGetCloudProducts(): Chainable<{products: Product[]}>;
|
||||
|
||||
/**
|
||||
* Get subscriptions.
|
||||
* See https://api.mattermost.com/#operation/GetSubscription
|
||||
* @returns {Subscription} out.subscription: `Subscription` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetCloudSubscription();
|
||||
*/
|
||||
apiGetCloudSubscription(): Chainable<{subscription: Subscription}>;
|
||||
}
|
||||
}
|
||||
24
e2e-tests/cypress/tests/support/api/cloud.js
Обычный файл
24
e2e-tests/cypress/tests/support/api/cloud.js
Обычный файл
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('apiGetCloudProducts', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/cloud/products',
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({products: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetCloudSubscription', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/cloud/subscription',
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({subscription: response.body});
|
||||
});
|
||||
});
|
||||
276
e2e-tests/cypress/tests/support/api/cloud_default_config.json
Обычный файл
276
e2e-tests/cypress/tests/support/api/cloud_default_config.json
Обычный файл
@@ -0,0 +1,276 @@
|
||||
{
|
||||
"ServiceSettings": {
|
||||
"EnableOAuthServiceProvider": false,
|
||||
"EnableIncomingWebhooks": true,
|
||||
"EnableOutgoingWebhooks": true,
|
||||
"EnableCommands": true,
|
||||
"EnablePostUsernameOverride": false,
|
||||
"EnablePostIconOverride": false,
|
||||
"EnableLinkPreviews": false,
|
||||
"EnableMultifactorAuthentication": false,
|
||||
"EnforceMultifactorAuthentication": false,
|
||||
"EnableUserAccessTokens": false,
|
||||
"EnableCustomEmoji": false,
|
||||
"EnableEmojiPicker": true,
|
||||
"EnableGifPicker": false,
|
||||
"GfycatAPIKey": "2_KtH_W5",
|
||||
"GfycatAPISecret": "3wLVZPiswc3DnaiaFoLkDvB4X0IV6CpMkj4tf2inJRsBY6-FnkT08zGmppWFgeof",
|
||||
"PostEditTimeLimit": -1,
|
||||
"EnablePreviewFeatures": true,
|
||||
"EnableTutorial": true,
|
||||
"EnableOnboardingFlow": false,
|
||||
"ExperimentalEnableDefaultChannelLeaveJoinMessages": true,
|
||||
"ExperimentalGroupUnreadChannels": "disabled",
|
||||
"EnableAPITeamDeletion": true,
|
||||
"ExperimentalEnableHardenedMode": false,
|
||||
"EnableEmailInvitations": true,
|
||||
"EnableBotAccountCreation": true,
|
||||
"EnableSVGs": true,
|
||||
"EnableLatex": false,
|
||||
"EnableLegacySidebar": false,
|
||||
"ThreadAutoFollow": true,
|
||||
"CollapsedThreads": "disabled"
|
||||
},
|
||||
"TeamSettings": {
|
||||
"SiteName": "Mattermost",
|
||||
"MaxUsersPerTeam": 2000,
|
||||
"EnableUserCreation": true,
|
||||
"EnableOpenServer": true,
|
||||
"EnableUserDeactivation": false,
|
||||
"RestrictCreationToDomains": "",
|
||||
"EnableCustomUserStatuses": true,
|
||||
"EnableCustomBrand": false,
|
||||
"CustomBrandText": "",
|
||||
"CustomDescriptionText": "",
|
||||
"RestrictDirectMessage": "any",
|
||||
"UserStatusAwayTimeout": 300,
|
||||
"MaxChannelsPerTeam": 2000,
|
||||
"MaxNotificationsPerChannel": 1000,
|
||||
"EnableConfirmNotificationsToChannel": true,
|
||||
"TeammateNameDisplay": "username",
|
||||
"ExperimentalViewArchivedChannels": false,
|
||||
"ExperimentalEnableAutomaticReplies": false,
|
||||
"LockTeammateNameDisplay": false,
|
||||
"ExperimentalPrimaryTeam": "",
|
||||
"ExperimentalDefaultChannels": []
|
||||
},
|
||||
"PasswordSettings": {
|
||||
"MinimumLength": 5,
|
||||
"Lowercase": false,
|
||||
"Number": false,
|
||||
"Uppercase": false,
|
||||
"Symbol": false
|
||||
},
|
||||
"EmailSettings": {
|
||||
"EnableSignUpWithEmail": true,
|
||||
"EnableSignInWithEmail": true,
|
||||
"EnableSignInWithUsername": true,
|
||||
"SendEmailNotifications": true,
|
||||
"UseChannelInEmailNotifications": false,
|
||||
"RequireEmailVerification": false,
|
||||
"FeedbackName": "",
|
||||
"FeedbackOrganization": "",
|
||||
"SendPushNotifications": true,
|
||||
"PushNotificationServer": "https://push-test.mattermost.com",
|
||||
"PushNotificationContents": "generic",
|
||||
"EnableEmailBatching": false,
|
||||
"EmailBatchingBufferSize": 256,
|
||||
"EmailBatchingInterval": 30,
|
||||
"EnablePreviewModeBanner": true,
|
||||
"EmailNotificationContentsType": "full",
|
||||
"LoginButtonColor": "#0000",
|
||||
"LoginButtonBorderColor": "#2389D7",
|
||||
"LoginButtonTextColor": "#2389D7"
|
||||
},
|
||||
"PrivacySettings": {
|
||||
"ShowEmailAddress": true,
|
||||
"ShowFullName": true
|
||||
},
|
||||
"SupportSettings": {
|
||||
"SupportEmail": "",
|
||||
"CustomTermsOfServiceEnabled": false,
|
||||
"CustomTermsOfServiceReAcceptancePeriod": 365,
|
||||
"EnableAskCommunityLink": true
|
||||
},
|
||||
"AnnouncementSettings": {
|
||||
"EnableBanner": false,
|
||||
"BannerText": "",
|
||||
"BannerColor": "#f2a93b",
|
||||
"BannerTextColor": "#333333",
|
||||
"AllowBannerDismissal": true,
|
||||
"AdminNoticesEnabled": false,
|
||||
"UserNoticesEnabled": false
|
||||
},
|
||||
"ThemeSettings": {
|
||||
"EnableThemeSelection": true,
|
||||
"DefaultTheme": "default",
|
||||
"AllowCustomThemes": true,
|
||||
"AllowedThemes": []
|
||||
},
|
||||
"GitLabSettings": {
|
||||
"Enable": false,
|
||||
"Secret": "",
|
||||
"Id": "",
|
||||
"Scope": "",
|
||||
"AuthEndpoint": "",
|
||||
"TokenEndpoint": "",
|
||||
"UserAPIEndpoint": ""
|
||||
},
|
||||
"GoogleSettings": {
|
||||
"Enable": false,
|
||||
"Secret": "",
|
||||
"Id": "",
|
||||
"Scope": "profile email",
|
||||
"AuthEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"TokenEndpoint": "https://www.googleapis.com/oauth2/v4/token",
|
||||
"UserAPIEndpoint": "https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses,nicknames,metadata"
|
||||
},
|
||||
"Office365Settings": {
|
||||
"Enable": false,
|
||||
"Secret": "",
|
||||
"Id": "",
|
||||
"Scope": "User.Read",
|
||||
"AuthEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||
"TokenEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
||||
"UserAPIEndpoint": "https://graph.microsoft.com/v1.0/me",
|
||||
"DirectoryId": ""
|
||||
},
|
||||
"LdapSettings": {
|
||||
"Enable": true,
|
||||
"EnableSync": false,
|
||||
"LdapServer": "localhost",
|
||||
"LdapPort": 389,
|
||||
"ConnectionSecurity": "",
|
||||
"BaseDN": "dc=mm,dc=test,dc=com",
|
||||
"BindUsername": "cn=admin,dc=mm,dc=test,dc=com",
|
||||
"BindPassword": "mostest",
|
||||
"UserFilter": "",
|
||||
"GroupFilter": "",
|
||||
"GuestFilter": "",
|
||||
"EnableAdminFilter": false,
|
||||
"AdminFilter": "",
|
||||
"GroupDisplayNameAttribute": "cn",
|
||||
"GroupIdAttribute": "entryUUID",
|
||||
"FirstNameAttribute": "cn",
|
||||
"LastNameAttribute": "sn",
|
||||
"EmailAttribute": "mail",
|
||||
"UsernameAttribute": "uid",
|
||||
"NicknameAttribute": "cn",
|
||||
"IdAttribute": "uid",
|
||||
"PositionAttribute": "sAMAccountType",
|
||||
"LoginIdAttribute": "uid",
|
||||
"PictureAttribute": "",
|
||||
"SyncIntervalMinutes": 10000,
|
||||
"SkipCertificateVerification": true,
|
||||
"QueryTimeout": 60,
|
||||
"MaxPageSize": 500,
|
||||
"LoginFieldName": "",
|
||||
"LoginButtonColor": "#0000",
|
||||
"LoginButtonBorderColor": "#2389D7",
|
||||
"LoginButtonTextColor": "#2389D7",
|
||||
"Trace": false
|
||||
},
|
||||
"ComplianceSettings": {
|
||||
"Enable": false,
|
||||
"EnableDaily": false
|
||||
},
|
||||
"LocalizationSettings": {
|
||||
"DefaultServerLocale": "en",
|
||||
"DefaultClientLocale": "en",
|
||||
"AvailableLocales": ""
|
||||
},
|
||||
"SamlSettings": {
|
||||
"Enable": false,
|
||||
"EnableSyncWithLdap": false,
|
||||
"EnableSyncWithLdapIncludeAuth": false,
|
||||
"Verify": true,
|
||||
"Encrypt": true,
|
||||
"SignRequest": false,
|
||||
"IdpURL": "",
|
||||
"IdpDescriptorURL": "",
|
||||
"IdpMetadataURL": "",
|
||||
"AssertionConsumerServiceURL": "",
|
||||
"SignatureAlgorithm": "RSAwithSHA1",
|
||||
"CanonicalAlgorithm": "Canonical1.0",
|
||||
"ScopingIDPProviderId": "",
|
||||
"ScopingIDPName": "",
|
||||
"IdpCertificateFile": "saml-idp.crt",
|
||||
"PublicCertificateFile": "saml-public.crt",
|
||||
"PrivateKeyFile": "saml-private.key",
|
||||
"IdAttribute": "",
|
||||
"GuestAttribute": "",
|
||||
"EnableAdminAttribute": false,
|
||||
"AdminAttribute": "",
|
||||
"FirstNameAttribute": "",
|
||||
"LastNameAttribute": "",
|
||||
"EmailAttribute": "Email",
|
||||
"UsernameAttribute": "Username",
|
||||
"NicknameAttribute": "",
|
||||
"LocaleAttribute": "",
|
||||
"PositionAttribute": "",
|
||||
"LoginButtonText": "SAML",
|
||||
"LoginButtonColor": "#34a28b",
|
||||
"LoginButtonBorderColor": "#2389D7",
|
||||
"LoginButtonTextColor": "#ffffff"
|
||||
},
|
||||
"ClusterSettings": {
|
||||
"Enable": false
|
||||
},
|
||||
"ExperimentalSettings": {
|
||||
"RestrictSystemAdmin": true
|
||||
},
|
||||
"DataRetentionSettings": {
|
||||
"EnableMessageDeletion": false,
|
||||
"EnableFileDeletion": false,
|
||||
"MessageRetentionDays": 365,
|
||||
"FileRetentionDays": 365,
|
||||
"DeletionJobStartTime": "02:00"
|
||||
},
|
||||
"MessageExportSettings": {
|
||||
"EnableExport": false,
|
||||
"ExportFormat": "actiance",
|
||||
"DailyRunTime": "01:00",
|
||||
"ExportFromTimestamp": 0,
|
||||
"BatchSize": 10000,
|
||||
"GlobalRelaySettings": {
|
||||
"CustomerType": "A9",
|
||||
"SMTPUsername": "",
|
||||
"SMTPPassword": "",
|
||||
"EmailAddress": ""
|
||||
}
|
||||
},
|
||||
"PluginSettings": {
|
||||
"Enable": true,
|
||||
"Plugins": {},
|
||||
"PluginStates": {
|
||||
"com.mattermost.nps": {
|
||||
"Enable": false
|
||||
},
|
||||
"com.mattermost.plugin-incident-response": {
|
||||
"Enable": false
|
||||
},
|
||||
"com.mattermost.plugin-incident-management": {
|
||||
"Enable": false
|
||||
},
|
||||
"focalboard": {
|
||||
"Enable": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"DisplaySettings": {
|
||||
"CustomURLSchemes": [],
|
||||
"ExperimentalTimezone": false
|
||||
},
|
||||
"GuestAccountsSettings": {
|
||||
"Enable": true,
|
||||
"AllowEmailAccounts": true,
|
||||
"EnforceMultifactorAuthentication": false,
|
||||
"RestrictCreationToDomains": ""
|
||||
},
|
||||
"ImageProxySettings": {
|
||||
"Enable": true,
|
||||
"ImageProxyType": "local",
|
||||
"RemoteImageProxyURL": "",
|
||||
"RemoteImageProxyOptions": ""
|
||||
}
|
||||
}
|
||||
31
e2e-tests/cypress/tests/support/api/cluster.d.ts
поставляемый
Обычный файл
31
e2e-tests/cypress/tests/support/api/cluster.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get cluster status
|
||||
* See https://api.mattermost.com/#tag/cluster/operation/GetClusterStatus
|
||||
* @returns {ClusterInfo[]} out.clusterInfo: `ClusterInfo[]` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetClusterStatus();
|
||||
*/
|
||||
apiGetClusterStatus(): Chainable<{clusterInfo: ClusterInfo[]}>;
|
||||
}
|
||||
}
|
||||
13
e2e-tests/cypress/tests/support/api/cluster.js
Обычный файл
13
e2e-tests/cypress/tests/support/api/cluster.js
Обычный файл
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
Cypress.Commands.add('apiGetClusterStatus', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/cluster/status',
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({clusterInfo: response.body});
|
||||
});
|
||||
});
|
||||
42
e2e-tests/cypress/tests/support/api/common.d.ts
поставляемый
Обычный файл
42
e2e-tests/cypress/tests/support/api/common.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Upload file directly via API.
|
||||
* @param {String} name - name of form
|
||||
* @param {String} filePath - path of the file to upload; can be relative or absolute
|
||||
* @param {Object} options - request options
|
||||
* @param {String} options.url - HTTP resource URL
|
||||
* @param {String} options.method - HTTP request method
|
||||
* @param {Number} options.successStatus - HTTP status code
|
||||
*
|
||||
* @example
|
||||
* cy.apiUploadFile('certificate', filePath, {url: '/api/v4/saml/certificate/public', method: 'POST', successStatus: 200});
|
||||
*/
|
||||
apiUploadFile(name: string, filePath: string, options: Record<string, unknown>): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Verify export file content-type
|
||||
* @param {String} fileURL - Export file URL
|
||||
* @param {String} contentType - File content-Type
|
||||
*/
|
||||
apiDownloadFileAndVerifyContentType(fileURL: string, contentType: string): Chainable;
|
||||
}
|
||||
}
|
||||
68
e2e-tests/cypress/tests/support/api/common.js
Обычный файл
68
e2e-tests/cypress/tests/support/api/common.js
Обычный файл
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
// *****************************************************************************
|
||||
// Common / Helper commands
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiUploadFile', (name, filePath, options = {}) => {
|
||||
const formData = new FormData();
|
||||
const filename = path.basename(filePath);
|
||||
|
||||
cy.fixture(filePath, 'binary', {timeout: TIMEOUTS.TWENTY_MIN}).
|
||||
then(Cypress.Blob.binaryStringToBlob).
|
||||
then((blob) => {
|
||||
formData.set(name, blob, filename);
|
||||
formRequest(options.method, options.url, formData, options.successStatus);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDownloadFileAndVerifyContentType', (fileURL, contentType = 'application/zip') => {
|
||||
cy.request(fileURL).then((response) => {
|
||||
// * Verify the download
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
// * Confirm its content type
|
||||
expect(response.headers['content-type']).to.equal(contentType);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Process binary file HTTP form request.
|
||||
* @param {String} method - HTTP request method
|
||||
* @param {String} url - HTTP resource URL
|
||||
* @param {FormData} formData - Key value pairs representing form fields and value
|
||||
* @param {Number} successStatus - HTTP status code
|
||||
*/
|
||||
function formRequest(method, url, formData, successStatus) {
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(method, url, false);
|
||||
let cookies = '';
|
||||
cy.getCookie('MMCSRF', {log: false}).then((token) => {
|
||||
//get MMCSRF cookie value
|
||||
const csrfToken = token.value;
|
||||
cy.getCookies({log: false}).then((cookieValues) => {
|
||||
//prepare cookie string
|
||||
cookieValues.forEach((cookie) => {
|
||||
cookies += cookie.name + '=' + cookie.value + '; ';
|
||||
});
|
||||
|
||||
//set headers
|
||||
xhr.setRequestHeader('Access-Control-Allow-Origin', baseUrl);
|
||||
xhr.setRequestHeader('Access-Control-Allow-Methods', 'GET, POST, PUT');
|
||||
xhr.setRequestHeader('X-CSRF-Token', csrfToken);
|
||||
xhr.setRequestHeader('Cookie', cookies);
|
||||
xhr.send(formData);
|
||||
if (xhr.readyState === 4) {
|
||||
expect(xhr.status, 'Expected form request to be processed successfully').to.equal(successStatus);
|
||||
} else {
|
||||
expect(xhr.status, 'Form request process delayed').to.equal(successStatus);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
33
e2e-tests/cypress/tests/support/api/data_retention.d.ts
поставляемый
Обычный файл
33
e2e-tests/cypress/tests/support/api/data_retention.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Delete all custom retention policies
|
||||
*/
|
||||
apiDeleteAllCustomRetentionPolicies(): Chainable;
|
||||
|
||||
/**
|
||||
* Create a post with create_at prop via API
|
||||
* @param {string} channelId - Channel ID
|
||||
* @param {string} message - Post a message
|
||||
* @param {string} token - token
|
||||
* @param {number} createat - epoch date
|
||||
*/
|
||||
apiPostWithCreateDate(channelId: string, message: string, token: string, createat: number): Chainable;
|
||||
}
|
||||
}
|
||||
157
e2e-tests/cypress/tests/support/api/data_retention.js
Обычный файл
157
e2e-tests/cypress/tests/support/api/data_retention.js
Обычный файл
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// Data Retention
|
||||
// https://api.mattermost.com/#tag/data-retention
|
||||
// *****************************************************************************
|
||||
|
||||
/**
|
||||
* Get all Custom Retention Policies
|
||||
* @param {Integer} page - The page to select
|
||||
* @param {Integer} perPage - The number of policies per page
|
||||
*/
|
||||
Cypress.Commands.add('apiGetCustomRetentionPolicies', (page = 0, perPage = 100) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/data_retention/policies?page=${page}&per_page=${perPage}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a Custom Retention Policy
|
||||
* @param {string} id - The id of the policy
|
||||
*/
|
||||
Cypress.Commands.add('apiGetCustomRetentionPolicy', (id) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/data_retention/policies/${id}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete Custom Retention Policy
|
||||
* @param {string} id - The id of the policy
|
||||
*/
|
||||
Cypress.Commands.add('apiDeleteCustomRetentionPolicy', (id) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/data_retention/policies/${id}`,
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get Custom Retention Policy teams
|
||||
* @param {string} id - The id of the policy
|
||||
* @param {Integer} page - The page to select
|
||||
* @param {Integer} perPage - The number of policy teams per page
|
||||
*/
|
||||
Cypress.Commands.add('apiGetCustomRetentionPolicyTeams', (id, page = 0, perPage = 100) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/data_retention/policies/${id}/teams?page=${page}&per_page=${perPage}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get Custom Retention Policy channels
|
||||
* @param {string} id - The id of the policy
|
||||
* @param {Integer} page - The page to select
|
||||
* @param {Integer} perPage - The number of policy channels per page
|
||||
*/
|
||||
Cypress.Commands.add('apiGetCustomRetentionPolicyChannels', (id, page = 0, perPage = 100) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/data_retention/policies/${id}/channels?page=${page}&per_page=${perPage}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Search Custom Retention Policy teams
|
||||
* @param {string} id - The id of the policy
|
||||
* @param {string} term - The team search term
|
||||
*/
|
||||
Cypress.Commands.add('apiSearchCustomRetentionPolicyTeams', (id, term) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/data_retention/policies/${id}/teams/search`,
|
||||
method: 'POST',
|
||||
body: {term},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Search Custom Retention Policy teams
|
||||
* @param {string} id - The id of the policy
|
||||
* @param {string} term - The channel search term
|
||||
*/
|
||||
Cypress.Commands.add('apiSearchCustomRetentionPolicyChannels', (id, term) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/data_retention/policies/${id}/channels/search`,
|
||||
method: 'POST',
|
||||
body: {term},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete all custom retention policies
|
||||
*/
|
||||
Cypress.Commands.add('apiDeleteAllCustomRetentionPolicies', () => {
|
||||
cy.apiGetCustomRetentionPolicies().then((result) => {
|
||||
result.body.policies.forEach((policy) => {
|
||||
cy.apiDeleteCustomRetentionPolicy(policy.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a post with create_at prop via API
|
||||
* @param {string} channelId - Channel ID
|
||||
* @param {string} message - Post a message
|
||||
* @param {string} token - token
|
||||
* @param {number} createAt - epoch date
|
||||
*/
|
||||
Cypress.Commands.add('apiPostWithCreateDate', (channelId, message, token, createAt) => {
|
||||
const headers = {'X-Requested-With': 'XMLHttpRequest'};
|
||||
if (token !== '') {
|
||||
headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return cy.request({
|
||||
headers,
|
||||
url: '/api/v4/posts',
|
||||
method: 'POST',
|
||||
body: {
|
||||
channel_id: channelId,
|
||||
create_at: createAt,
|
||||
message,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
15
e2e-tests/cypress/tests/support/api/helpers.js
Обычный файл
15
e2e-tests/cypress/tests/support/api/helpers.js
Обычный файл
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export function buildQueryString(queryParams = {}) {
|
||||
let queryString = '';
|
||||
Object.entries(queryParams).forEach(([k, v], index) => {
|
||||
if (index > 0) {
|
||||
queryString += '&';
|
||||
}
|
||||
|
||||
queryString += `${k}=${encodeURIComponent(v)}`;
|
||||
});
|
||||
|
||||
return queryString;
|
||||
}
|
||||
24
e2e-tests/cypress/tests/support/api/index.js
Обычный файл
24
e2e-tests/cypress/tests/support/api/index.js
Обычный файл
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import './brand';
|
||||
import './bots';
|
||||
import './channel';
|
||||
import './cloud';
|
||||
import './cluster';
|
||||
import './common';
|
||||
import './data_retention';
|
||||
import './keycloak';
|
||||
import './ldap';
|
||||
import './playbooks';
|
||||
import './preference';
|
||||
import './plugin';
|
||||
import './role';
|
||||
import './saml';
|
||||
import './scheme';
|
||||
import './setup';
|
||||
import './status';
|
||||
import './system';
|
||||
import './team';
|
||||
import './user';
|
||||
import './webhooks';
|
||||
65
e2e-tests/cypress/tests/support/api/keycloak.d.ts
поставляемый
Обычный файл
65
e2e-tests/cypress/tests/support/api/keycloak.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiKeycloakGetAccessToken`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get access token from Keycloak
|
||||
* See https://www.keycloak.org/documentation
|
||||
* @returns {string} token
|
||||
*
|
||||
* @example
|
||||
* cy.apiKeycloakGetAccessToken();
|
||||
*/
|
||||
apiKeycloakGetAccessToken(): Chainable<string>;
|
||||
|
||||
/**
|
||||
* Save realm to Keycloak
|
||||
* See https://www.keycloak.org/documentation
|
||||
* @param {string} options.accessToken - valid token to authorize a request
|
||||
* @param {Boolean} options.failOnStatusCode - whether to fail on status code, default is true
|
||||
* @returns {Response} response: Cypress-chainable response
|
||||
*
|
||||
* @example
|
||||
* cy.apiKeycloakSaveRealm('access-token');
|
||||
*/
|
||||
apiKeycloakSaveRealm(accessToken: string, failOnStatusCode: boolean): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Get realm from Keycloak
|
||||
* See https://www.keycloak.org/documentation
|
||||
* @param {string} options.accessToken - valid token to authorize a request
|
||||
* @param {Boolean} options.failOnStatusCode - whether to fail on status code, default is true
|
||||
* @returns {Response} response: Cypress-chainable response
|
||||
*
|
||||
* @example
|
||||
* cy.apiKeycloakGetRealm('access-token');
|
||||
*/
|
||||
apiKeycloakGetRealm(accessToken: string, failOnStatusCode: boolean): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Verify Keycloak is reachable and has realm setup
|
||||
* See https://www.keycloak.org/documentation
|
||||
* @returns {Response} response: Cypress-chainable response
|
||||
*
|
||||
* @example
|
||||
* cy.apiRequireKeycloak();
|
||||
*/
|
||||
apiRequireKeycloak(): Chainable<string>;
|
||||
}
|
||||
}
|
||||
89
e2e-tests/cypress/tests/support/api/keycloak.js
Обычный файл
89
e2e-tests/cypress/tests/support/api/keycloak.js
Обычный файл
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// Keycloak Admin REST API
|
||||
// https://www.keycloak.org/documentation
|
||||
// *****************************************************************************
|
||||
|
||||
import realmJson from './keycloak_realm.json';
|
||||
|
||||
const {
|
||||
keycloakBaseUrl,
|
||||
keycloakAppName,
|
||||
keycloakUsername,
|
||||
keycloakPassword,
|
||||
} = Cypress.env();
|
||||
|
||||
Cypress.Commands.add('apiKeycloakGetAccessToken', () => {
|
||||
return cy.task('keycloakRequest', {
|
||||
baseUrl: `${keycloakBaseUrl}/auth/realms/master/protocol/openid-connect/token`,
|
||||
method: 'POST',
|
||||
headers: {'Content-type': 'application/x-www-form-urlencoded'},
|
||||
data: `grant_type=password&username=${keycloakUsername}&password=${keycloakPassword}&client_id=admin-cli`,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
const token = response.data.access_token;
|
||||
return cy.wrap(token);
|
||||
});
|
||||
});
|
||||
|
||||
function getRealmJson() {
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
const {ldapServer, ldapPort} = Cypress.env();
|
||||
|
||||
const realm = JSON.stringify(realmJson).
|
||||
replace(/localhost:389/g, `${ldapServer}:${ldapPort}`).
|
||||
replace(/http:\/\/localhost:8065/g, baseUrl);
|
||||
return JSON.parse(realm);
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiKeycloakSaveRealm', (accessToken, failOnStatusCode = true) => {
|
||||
const realm = getRealmJson();
|
||||
|
||||
return cy.task('keycloakRequest', {
|
||||
baseUrl: `${keycloakBaseUrl}/auth/admin/realms`,
|
||||
method: 'POST',
|
||||
data: realm,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
}).then((response) => {
|
||||
if (failOnStatusCode) {
|
||||
expect(response.status).to.equal(201);
|
||||
}
|
||||
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiKeycloakGetRealm', (accessToken, failOnStatusCode = true) => {
|
||||
return cy.task('keycloakRequest', {
|
||||
baseUrl: `${keycloakBaseUrl}/auth/admin/realms/${keycloakAppName}`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
},
|
||||
failOnStatusCode,
|
||||
}).then((response) => {
|
||||
if (failOnStatusCode) {
|
||||
expect(response.status).to.equal(200);
|
||||
}
|
||||
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiRequireKeycloak', () => {
|
||||
cy.apiKeycloakGetAccessToken().then((token) => {
|
||||
cy.apiKeycloakGetRealm(token, false).then((response) => {
|
||||
if (response.status !== 200) {
|
||||
return cy.apiKeycloakSaveRealm(token);
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
});
|
||||
});
|
||||
1966
e2e-tests/cypress/tests/support/api/keycloak_realm.json
Обычный файл
1966
e2e-tests/cypress/tests/support/api/keycloak_realm.json
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
48
e2e-tests/cypress/tests/support/api/ldap.d.ts
поставляемый
Обычный файл
48
e2e-tests/cypress/tests/support/api/ldap.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLDAPSync`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Synchronize any user attribute changes in the configured AD/LDAP server with Mattermost.
|
||||
* See https://api.mattermost.com/#operation/SyncLdap
|
||||
*
|
||||
* @example
|
||||
* cy.apiLDAPSync();
|
||||
*/
|
||||
apiLDAPSync(): Chainable;
|
||||
|
||||
/**
|
||||
* Test the current AD/LDAP configuration to see if the AD/LDAP server can be contacted successfully.
|
||||
* See https://api.mattermost.com/#operation/TestLdap
|
||||
*
|
||||
* @example
|
||||
* cy.apiLDAPTest();
|
||||
*/
|
||||
apiLDAPTest(): Chainable;
|
||||
|
||||
/**
|
||||
* Sync LDAP user
|
||||
* @returns {UserProfile} user - user object
|
||||
*
|
||||
* @example
|
||||
* cy.apiSyncLDAPUser();
|
||||
*/
|
||||
apiSyncLDAPUser(): Chainable<UserProfile>;
|
||||
}
|
||||
}
|
||||
50
e2e-tests/cypress/tests/support/api/ldap.js
Обычный файл
50
e2e-tests/cypress/tests/support/api/ldap.js
Обычный файл
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// LDAP
|
||||
// https://api.mattermost.com/#tag/LDAP
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiLDAPSync', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/ldap/sync',
|
||||
method: 'POST',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiLDAPTest', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/ldap/test',
|
||||
method: 'POST',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSyncLDAPUser', ({
|
||||
ldapUser = {},
|
||||
bypassTutorial = true,
|
||||
}) => {
|
||||
// # Test LDAP connection and synchronize user
|
||||
cy.apiLDAPTest();
|
||||
cy.apiLDAPSync();
|
||||
|
||||
// # Login to sync LDAP user
|
||||
return cy.apiLogin(ldapUser).then(({user}) => {
|
||||
if (bypassTutorial) {
|
||||
cy.apiAdminLogin();
|
||||
}
|
||||
if (bypassTutorial) {
|
||||
cy.apiSaveTutorialStep(user.id, '999');
|
||||
}
|
||||
|
||||
return cy.wrap(user);
|
||||
});
|
||||
});
|
||||
470
e2e-tests/cypress/tests/support/api/on_prem_default_config.json
Обычный файл
470
e2e-tests/cypress/tests/support/api/on_prem_default_config.json
Обычный файл
@@ -0,0 +1,470 @@
|
||||
{
|
||||
"ServiceSettings": {
|
||||
"SiteURL": "http://localhost:8065",
|
||||
"WebsocketURL": "",
|
||||
"LicenseFileLocation": "",
|
||||
"ListenAddress": ":8065",
|
||||
"ConnectionSecurity": "",
|
||||
"TLSCertFile": "",
|
||||
"TLSKeyFile": "",
|
||||
"TLSMinVer": "1.2",
|
||||
"TLSStrictTransport": false,
|
||||
"TLSStrictTransportMaxAge": 63072000,
|
||||
"TLSOverwriteCiphers": [],
|
||||
"UseLetsEncrypt": false,
|
||||
"Forward80To443": false,
|
||||
"TrustedProxyIPHeader": [],
|
||||
"ReadTimeout": 300,
|
||||
"WriteTimeout": 300,
|
||||
"IdleTimeout": 300,
|
||||
"GoroutineHealthThreshold": -1,
|
||||
"GoogleDeveloperKey": "",
|
||||
"EnableOAuthServiceProvider": false,
|
||||
"EnableIncomingWebhooks": true,
|
||||
"EnableOutgoingWebhooks": true,
|
||||
"EnableCommands": true,
|
||||
"EnablePostUsernameOverride": false,
|
||||
"EnablePostIconOverride": false,
|
||||
"EnableLinkPreviews": false,
|
||||
"EnableTesting": false,
|
||||
"EnableDeveloper": false,
|
||||
"EnableOpenTracing": false,
|
||||
"EnableSecurityFixAlert": true,
|
||||
"EnableInsecureOutgoingConnections": false,
|
||||
"AllowedUntrustedInternalConnections": "localhost",
|
||||
"EnableMultifactorAuthentication": false,
|
||||
"EnforceMultifactorAuthentication": false,
|
||||
"EnableUserAccessTokens": false,
|
||||
"AllowCorsFrom": "",
|
||||
"CorsExposedHeaders": "",
|
||||
"CorsAllowCredentials": false,
|
||||
"CorsDebug": false,
|
||||
"AllowCookiesForSubdomains": false,
|
||||
"ExtendSessionLengthWithActivity": true,
|
||||
"SessionLengthWebInHours": 720,
|
||||
"SessionLengthMobileInHours": 720,
|
||||
"SessionLengthSSOInHours": 720,
|
||||
"SessionCacheInMinutes": 10,
|
||||
"SessionIdleTimeoutInMinutes": 43200,
|
||||
"WebsocketSecurePort": 443,
|
||||
"WebsocketPort": 80,
|
||||
"WebserverMode": "gzip",
|
||||
"EnableCustomEmoji": false,
|
||||
"EnableEmojiPicker": true,
|
||||
"EnableGifPicker": false,
|
||||
"GfycatAPIKey": "2_KtH_W5",
|
||||
"GfycatAPISecret": "3wLVZPiswc3DnaiaFoLkDvB4X0IV6CpMkj4tf2inJRsBY6-FnkT08zGmppWFgeof",
|
||||
"PostEditTimeLimit": -1,
|
||||
"TimeBetweenUserTypingUpdatesMilliseconds": 5000,
|
||||
"EnablePostSearch": true,
|
||||
"MinimumHashtagLength": 3,
|
||||
"EnableUserTypingMessages": true,
|
||||
"EnableChannelViewedMessages": true,
|
||||
"EnableUserStatuses": true,
|
||||
"ExperimentalEnableAuthenticationTransfer": true,
|
||||
"ClusterLogTimeoutMilliseconds": 2000,
|
||||
"EnablePreviewFeatures": true,
|
||||
"EnableTutorial": true,
|
||||
"EnableOnboardingFlow": false,
|
||||
"ExperimentalEnableDefaultChannelLeaveJoinMessages": true,
|
||||
"ExperimentalGroupUnreadChannels": "disabled",
|
||||
"EnableAPITeamDeletion": true,
|
||||
"ExperimentalEnableHardenedMode": false,
|
||||
"ExperimentalStrictCSRFEnforcement": false,
|
||||
"EnableEmailInvitations": true,
|
||||
"DisableBotsWhenOwnerIsDeactivated": true,
|
||||
"EnableBotAccountCreation": true,
|
||||
"EnableSVGs": true,
|
||||
"EnableLatex": false,
|
||||
"EnableLegacySidebar": false,
|
||||
"ThreadAutoFollow": true,
|
||||
"CollapsedThreads": "disabled"
|
||||
},
|
||||
"TeamSettings": {
|
||||
"SiteName": "Mattermost",
|
||||
"MaxUsersPerTeam": 2000,
|
||||
"EnableUserCreation": true,
|
||||
"EnableOpenServer": true,
|
||||
"EnableUserDeactivation": false,
|
||||
"RestrictCreationToDomains": "",
|
||||
"EnableCustomUserStatuses": true,
|
||||
"EnableCustomBrand": false,
|
||||
"CustomBrandText": "",
|
||||
"CustomDescriptionText": "",
|
||||
"RestrictDirectMessage": "any",
|
||||
"UserStatusAwayTimeout": 300,
|
||||
"MaxChannelsPerTeam": 2000,
|
||||
"MaxNotificationsPerChannel": 1000,
|
||||
"EnableConfirmNotificationsToChannel": true,
|
||||
"TeammateNameDisplay": "username",
|
||||
"ExperimentalViewArchivedChannels": false,
|
||||
"ExperimentalEnableAutomaticReplies": false,
|
||||
"LockTeammateNameDisplay": false,
|
||||
"ExperimentalPrimaryTeam": "",
|
||||
"ExperimentalDefaultChannels": []
|
||||
},
|
||||
"ClientRequirements": {
|
||||
"AndroidLatestVersion": "",
|
||||
"AndroidMinVersion": "",
|
||||
"IosLatestVersion": "",
|
||||
"IosMinVersion": ""
|
||||
},
|
||||
"SqlSettings": {
|
||||
"DataSourceReplicas": [],
|
||||
"DataSourceSearchReplicas": [],
|
||||
"MaxIdleConns": 20,
|
||||
"ConnMaxLifetimeMilliseconds": 3600000,
|
||||
"MaxOpenConns": 300,
|
||||
"Trace": false,
|
||||
"AtRestEncryptKey": "",
|
||||
"QueryTimeout": 30
|
||||
},
|
||||
"LogSettings": {
|
||||
"EnableConsole": true,
|
||||
"ConsoleLevel": "DEBUG",
|
||||
"ConsoleJson": true,
|
||||
"EnableFile": true,
|
||||
"FileLevel": "INFO",
|
||||
"FileJson": true,
|
||||
"FileLocation": "",
|
||||
"EnableWebhookDebugging": true,
|
||||
"EnableDiagnostics": true,
|
||||
"EnableSentry": false
|
||||
},
|
||||
"ExperimentalAuditSettings": {
|
||||
"FileEnabled": false,
|
||||
"FileName": "",
|
||||
"FileMaxSizeMB": 100,
|
||||
"FileMaxAgeDays": 0,
|
||||
"FileMaxBackups": 0,
|
||||
"FileCompress": false,
|
||||
"FileMaxQueueSize": 1000
|
||||
},
|
||||
"NotificationLogSettings": {
|
||||
"EnableConsole": true,
|
||||
"ConsoleLevel": "DEBUG",
|
||||
"ConsoleJson": true,
|
||||
"EnableFile": true,
|
||||
"FileLevel": "INFO",
|
||||
"FileJson": true,
|
||||
"FileLocation": ""
|
||||
},
|
||||
"PasswordSettings": {
|
||||
"MinimumLength": 5,
|
||||
"Lowercase": false,
|
||||
"Number": false,
|
||||
"Uppercase": false,
|
||||
"Symbol": false,
|
||||
"Enable": false
|
||||
},
|
||||
"FileSettings": {
|
||||
"EnableFileAttachments": true,
|
||||
"EnableMobileUpload": true,
|
||||
"EnableMobileDownload": true,
|
||||
"MaxFileSize": 104857600,
|
||||
"DriverName": "local",
|
||||
"Directory": "./data/",
|
||||
"EnablePublicLink": false,
|
||||
"PublicLinkSalt": "",
|
||||
"InitialFont": "nunito-bold.ttf",
|
||||
"AmazonS3AccessKeyId": "",
|
||||
"AmazonS3SecretAccessKey": "",
|
||||
"AmazonS3Bucket": "",
|
||||
"AmazonS3Region": "",
|
||||
"AmazonS3Endpoint": "s3.amazonaws.com",
|
||||
"AmazonS3SSL": true,
|
||||
"AmazonS3SignV2": false,
|
||||
"AmazonS3SSE": false,
|
||||
"AmazonS3Trace": false
|
||||
},
|
||||
"EmailSettings": {
|
||||
"EnableSignUpWithEmail": true,
|
||||
"EnableSignInWithEmail": true,
|
||||
"EnableSignInWithUsername": true,
|
||||
"SendEmailNotifications": true,
|
||||
"UseChannelInEmailNotifications": false,
|
||||
"RequireEmailVerification": false,
|
||||
"FeedbackName": "",
|
||||
"FeedbackEmail": "test@example.com",
|
||||
"ReplyToAddress": "test@example.com",
|
||||
"FeedbackOrganization": "",
|
||||
"EnableSMTPAuth": false,
|
||||
"SMTPUsername": "",
|
||||
"SMTPPassword": "",
|
||||
"SMTPServer": "localhost",
|
||||
"SMTPPort": "10025",
|
||||
"SMTPServerTimeout": 10,
|
||||
"ConnectionSecurity": "",
|
||||
"SendPushNotifications": true,
|
||||
"PushNotificationServer": "https://push-test.mattermost.com",
|
||||
"PushNotificationContents": "generic",
|
||||
"EnableEmailBatching": false,
|
||||
"EmailBatchingBufferSize": 256,
|
||||
"EmailBatchingInterval": 30,
|
||||
"EnablePreviewModeBanner": true,
|
||||
"SkipServerCertificateVerification": false,
|
||||
"EmailNotificationContentsType": "full",
|
||||
"LoginButtonColor": "#0000",
|
||||
"LoginButtonBorderColor": "#2389D7",
|
||||
"LoginButtonTextColor": "#2389D7"
|
||||
},
|
||||
"RateLimitSettings": {
|
||||
"Enable": false,
|
||||
"PerSec": 10,
|
||||
"MaxBurst": 100,
|
||||
"MemoryStoreSize": 10000,
|
||||
"VaryByRemoteAddr": true,
|
||||
"VaryByUser": false,
|
||||
"VaryByHeader": ""
|
||||
},
|
||||
"PrivacySettings": {
|
||||
"ShowEmailAddress": true,
|
||||
"ShowFullName": true
|
||||
},
|
||||
"SupportSettings": {
|
||||
"TermsOfServiceLink": "https://mattermost.com/terms-of-use/",
|
||||
"PrivacyPolicyLink": "https://mattermost.com/privacy-policy/",
|
||||
"AboutLink": "https://docs.mattermost.com/about/product.html",
|
||||
"HelpLink": "https://mattermost.com/default-help/",
|
||||
"ReportAProblemLink": "https://mattermost.com/default-report-a-problem/",
|
||||
"SupportEmail": "",
|
||||
"CustomTermsOfServiceEnabled": false,
|
||||
"CustomTermsOfServiceReAcceptancePeriod": 365,
|
||||
"EnableAskCommunityLink": true
|
||||
},
|
||||
"AnnouncementSettings": {
|
||||
"EnableBanner": false,
|
||||
"BannerText": "",
|
||||
"BannerColor": "#f2a93b",
|
||||
"BannerTextColor": "#333333",
|
||||
"AllowBannerDismissal": true,
|
||||
"AdminNoticesEnabled": false,
|
||||
"UserNoticesEnabled": false
|
||||
},
|
||||
"ThemeSettings": {
|
||||
"EnableThemeSelection": true,
|
||||
"DefaultTheme": "default",
|
||||
"AllowCustomThemes": true,
|
||||
"AllowedThemes": []
|
||||
},
|
||||
"GitLabSettings": {
|
||||
"Enable": false,
|
||||
"Secret": "",
|
||||
"Id": "",
|
||||
"Scope": "",
|
||||
"AuthEndpoint": "",
|
||||
"TokenEndpoint": "",
|
||||
"UserAPIEndpoint": ""
|
||||
},
|
||||
"GoogleSettings": {
|
||||
"Enable": false,
|
||||
"Secret": "",
|
||||
"Id": "",
|
||||
"Scope": "profile email",
|
||||
"AuthEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"TokenEndpoint": "https://www.googleapis.com/oauth2/v4/token",
|
||||
"UserAPIEndpoint": "https://people.googleapis.com/v1/people/me?personFields=names,emailAddresses,nicknames,metadata"
|
||||
},
|
||||
"Office365Settings": {
|
||||
"Enable": false,
|
||||
"Secret": "",
|
||||
"Id": "",
|
||||
"Scope": "User.Read",
|
||||
"AuthEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||
"TokenEndpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
||||
"UserAPIEndpoint": "https://graph.microsoft.com/v1.0/me",
|
||||
"DirectoryId": ""
|
||||
},
|
||||
"LdapSettings": {
|
||||
"Enable": true,
|
||||
"EnableSync": false,
|
||||
"LdapServer": "localhost",
|
||||
"LdapPort": 389,
|
||||
"ConnectionSecurity": "",
|
||||
"BaseDN": "dc=mm,dc=test,dc=com",
|
||||
"BindUsername": "cn=admin,dc=mm,dc=test,dc=com",
|
||||
"BindPassword": "mostest",
|
||||
"UserFilter": "",
|
||||
"GroupFilter": "",
|
||||
"GuestFilter": "",
|
||||
"EnableAdminFilter": false,
|
||||
"AdminFilter": "",
|
||||
"GroupDisplayNameAttribute": "cn",
|
||||
"GroupIdAttribute": "entryUUID",
|
||||
"FirstNameAttribute": "cn",
|
||||
"LastNameAttribute": "sn",
|
||||
"EmailAttribute": "mail",
|
||||
"UsernameAttribute": "uid",
|
||||
"NicknameAttribute": "cn",
|
||||
"IdAttribute": "uid",
|
||||
"PositionAttribute": "sAMAccountType",
|
||||
"LoginIdAttribute": "uid",
|
||||
"PictureAttribute": "",
|
||||
"SyncIntervalMinutes": 10000,
|
||||
"SkipCertificateVerification": true,
|
||||
"QueryTimeout": 60,
|
||||
"MaxPageSize": 500,
|
||||
"LoginFieldName": "",
|
||||
"LoginButtonColor": "#0000",
|
||||
"LoginButtonBorderColor": "#2389D7",
|
||||
"LoginButtonTextColor": "#2389D7",
|
||||
"Trace": false
|
||||
},
|
||||
"ComplianceSettings": {
|
||||
"Enable": false,
|
||||
"Directory": "./data/",
|
||||
"EnableDaily": false
|
||||
},
|
||||
"LocalizationSettings": {
|
||||
"DefaultServerLocale": "en",
|
||||
"DefaultClientLocale": "en",
|
||||
"AvailableLocales": ""
|
||||
},
|
||||
"SamlSettings": {
|
||||
"Enable": false,
|
||||
"EnableSyncWithLdap": false,
|
||||
"EnableSyncWithLdapIncludeAuth": false,
|
||||
"Verify": true,
|
||||
"Encrypt": true,
|
||||
"SignRequest": false,
|
||||
"IdpURL": "",
|
||||
"IdpDescriptorURL": "",
|
||||
"IdpMetadataURL": "",
|
||||
"AssertionConsumerServiceURL": "",
|
||||
"SignatureAlgorithm": "RSAwithSHA1",
|
||||
"CanonicalAlgorithm": "Canonical1.0",
|
||||
"ScopingIDPProviderId": "",
|
||||
"ScopingIDPName": "",
|
||||
"IdpCertificateFile": "saml-idp.crt",
|
||||
"PublicCertificateFile": "saml-public.crt",
|
||||
"PrivateKeyFile": "saml-private.key",
|
||||
"IdAttribute": "",
|
||||
"GuestAttribute": "",
|
||||
"EnableAdminAttribute": false,
|
||||
"AdminAttribute": "",
|
||||
"FirstNameAttribute": "",
|
||||
"LastNameAttribute": "",
|
||||
"EmailAttribute": "Email",
|
||||
"UsernameAttribute": "Username",
|
||||
"NicknameAttribute": "",
|
||||
"LocaleAttribute": "",
|
||||
"PositionAttribute": "",
|
||||
"LoginButtonText": "SAML",
|
||||
"LoginButtonColor": "#34a28b",
|
||||
"LoginButtonBorderColor": "#2389D7",
|
||||
"LoginButtonTextColor": "#ffffff"
|
||||
},
|
||||
"NativeAppSettings": {
|
||||
"AppDownloadLink": "https://mattermost.com/download/#mattermostApps",
|
||||
"AndroidAppDownloadLink": "https://mattermost.com/mattermost-android-app/",
|
||||
"IosAppDownloadLink": "https://mattermost.com/mattermost-ios-app/"
|
||||
},
|
||||
"MetricsSettings": {
|
||||
"Enable": false,
|
||||
"BlockProfileRate": 0,
|
||||
"ListenAddress": ":8067"
|
||||
},
|
||||
"ExperimentalSettings": {
|
||||
"ClientSideCertEnable": false,
|
||||
"ClientSideCertCheck": "secondary",
|
||||
"LinkMetadataTimeoutMilliseconds": 5000,
|
||||
"RestrictSystemAdmin": false,
|
||||
"UseNewSAMLLibrary": false,
|
||||
"EnableAppBar": true
|
||||
},
|
||||
"AnalyticsSettings": {
|
||||
"MaxUsersForStatistics": 2500
|
||||
},
|
||||
"ElasticsearchSettings": {
|
||||
"ConnectionURL": "http://localhost:9200",
|
||||
"Username": "elastic",
|
||||
"Password": "changeme",
|
||||
"EnableIndexing": false,
|
||||
"EnableSearching": false,
|
||||
"EnableAutocomplete": false,
|
||||
"Sniff": false,
|
||||
"PostIndexReplicas": 1,
|
||||
"PostIndexShards": 1,
|
||||
"ChannelIndexReplicas": 1,
|
||||
"ChannelIndexShards": 1,
|
||||
"UserIndexReplicas": 1,
|
||||
"UserIndexShards": 1,
|
||||
"AggregatePostsAfterDays": 365,
|
||||
"PostsAggregatorJobStartTime": "03:00",
|
||||
"IndexPrefix": "",
|
||||
"LiveIndexingBatchSize": 1,
|
||||
"BulkIndexingTimeWindowSeconds": 3600,
|
||||
"RequestTimeoutSeconds": 30,
|
||||
"SkipTLSVerification": false,
|
||||
"Trace": ""
|
||||
},
|
||||
"DataRetentionSettings": {
|
||||
"EnableMessageDeletion": false,
|
||||
"EnableFileDeletion": false,
|
||||
"MessageRetentionDays": 365,
|
||||
"FileRetentionDays": 365,
|
||||
"DeletionJobStartTime": "02:00"
|
||||
},
|
||||
"MessageExportSettings": {
|
||||
"EnableExport": false,
|
||||
"ExportFormat": "actiance",
|
||||
"DailyRunTime": "01:00",
|
||||
"ExportFromTimestamp": 0,
|
||||
"BatchSize": 10000,
|
||||
"GlobalRelaySettings": {
|
||||
"CustomerType": "A9",
|
||||
"SMTPUsername": "",
|
||||
"SMTPPassword": "",
|
||||
"EmailAddress": ""
|
||||
}
|
||||
},
|
||||
"JobSettings": {
|
||||
"RunJobs": true,
|
||||
"RunScheduler": true
|
||||
},
|
||||
"PluginSettings": {
|
||||
"Enable": true,
|
||||
"EnableUploads": true,
|
||||
"AllowInsecureDownloadURL": false,
|
||||
"EnableHealthCheck": true,
|
||||
"Directory": "./plugins",
|
||||
"ClientDirectory": "./client/plugins",
|
||||
"Plugins": {},
|
||||
"PluginStates": {
|
||||
"com.mattermost.nps": {
|
||||
"Enable": false
|
||||
},
|
||||
"com.mattermost.plugin-incident-response": {
|
||||
"Enable": false
|
||||
},
|
||||
"com.mattermost.plugin-incident-management": {
|
||||
"Enable": false
|
||||
},
|
||||
"focalboard": {
|
||||
"Enable": false
|
||||
}
|
||||
},
|
||||
"EnableMarketplace": true,
|
||||
"EnableRemoteMarketplace": true,
|
||||
"AutomaticPrepackagedPlugins": true,
|
||||
"RequirePluginSignature": false,
|
||||
"MarketplaceURL": "https://api.integrations.mattermost.com",
|
||||
"SignaturePublicKeyFiles": []
|
||||
},
|
||||
"DisplaySettings": {
|
||||
"CustomURLSchemes": [],
|
||||
"ExperimentalTimezone": false
|
||||
},
|
||||
"GuestAccountsSettings": {
|
||||
"Enable": true,
|
||||
"AllowEmailAccounts": true,
|
||||
"EnforceMultifactorAuthentication": false,
|
||||
"RestrictCreationToDomains": ""
|
||||
},
|
||||
"ImageProxySettings": {
|
||||
"Enable": true,
|
||||
"ImageProxyType": "local",
|
||||
"RemoteImageProxyURL": "",
|
||||
"RemoteImageProxyOptions": ""
|
||||
}
|
||||
}
|
||||
483
e2e-tests/cypress/tests/support/api/playbooks.js
Обычный файл
483
e2e-tests/cypress/tests/support/api/playbooks.js
Обычный файл
@@ -0,0 +1,483 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
const playbookRunsEndpoint = '/plugins/playbooks/api/v0/runs';
|
||||
|
||||
const StatusOK = 200;
|
||||
const StatusCreated = 201;
|
||||
|
||||
/**
|
||||
* Get all playbook runs directly via API
|
||||
*/
|
||||
Cypress.Commands.add('apiGetAllPlaybookRuns', (teamId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/plugins/playbooks/api/v0/runs',
|
||||
qs: {team_id: teamId, per_page: 10000},
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all InProgress playbook runs directly via API
|
||||
*/
|
||||
Cypress.Commands.add('apiGetAllInProgressPlaybookRuns', (teamId, userId = '') => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/plugins/playbooks/api/v0/runs',
|
||||
qs: {team_id: teamId, status: 'InProgress', participant_id: userId},
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get playbook run by name directly via API
|
||||
*/
|
||||
Cypress.Commands.add('apiGetPlaybookRunByName', (teamId, name) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/plugins/playbooks/api/v0/runs',
|
||||
qs: {team_id: teamId, search_term: name},
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a playbook run directly via API
|
||||
* @param {String} playbookRunId
|
||||
* All parameters required
|
||||
*/
|
||||
Cypress.Commands.add('apiGetPlaybookRun', (playbookRunId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `${playbookRunsEndpoint}/${playbookRunId}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Start a playbook run directly via API.
|
||||
*/
|
||||
Cypress.Commands.add('apiRunPlaybook', (
|
||||
{
|
||||
teamId,
|
||||
playbookId,
|
||||
playbookRunName,
|
||||
ownerUserId,
|
||||
channelId,
|
||||
description,
|
||||
}, options) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: playbookRunsEndpoint,
|
||||
method: 'POST',
|
||||
body: {
|
||||
name: playbookRunName,
|
||||
owner_user_id: ownerUserId,
|
||||
team_id: teamId,
|
||||
playbook_id: playbookId,
|
||||
channel_id: channelId,
|
||||
description,
|
||||
},
|
||||
failOnStatusCode: !(options?.expectedStatusCode),
|
||||
}).then((response) => {
|
||||
const statusCode = options?.expectedStatusCode || StatusCreated;
|
||||
expect(response.status).to.equal(statusCode);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
// Finish a playbook's run programmaticially. Uses currently logged in user, so that user must
|
||||
// have edit permissions on the run
|
||||
Cypress.Commands.add('apiFinishRun', (playbookRunId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `${playbookRunsEndpoint}/${playbookRunId}/finish`,
|
||||
method: 'PUT',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
// Update a playbook run's status programmatically.
|
||||
Cypress.Commands.add('apiUpdateStatus', (
|
||||
{
|
||||
playbookRunId,
|
||||
message,
|
||||
reminder = 300,
|
||||
}) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `${playbookRunsEndpoint}/${playbookRunId}/status`,
|
||||
method: 'POST',
|
||||
body: {
|
||||
message,
|
||||
reminder,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Change the owner of a playbook run directly via API
|
||||
* @param {String} playbookRunId
|
||||
* @param {String} userId
|
||||
* All parameters required
|
||||
*/
|
||||
Cypress.Commands.add('apiChangePlaybookRunOwner', (playbookRunId, userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: playbookRunsEndpoint + '/' + playbookRunId + '/owner',
|
||||
method: 'POST',
|
||||
body: {
|
||||
owner_id: userId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Change the assignee of a checklist item directly via API
|
||||
* @param {String} playbookRunId
|
||||
* @param {String} checklistId
|
||||
* @param {String} itemId
|
||||
* @param {String} userId
|
||||
* All parameters required
|
||||
*/
|
||||
Cypress.Commands.add('apiChangeChecklistItemAssignee', (playbookRunId, checklistId, itemId, userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: playbookRunsEndpoint + `/${playbookRunId}/checklists/${checklistId}/item/${itemId}/assignee`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
assignee_id: userId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Check a checklist item directly via API
|
||||
* @param {String} playbookRunId
|
||||
* @param {String} checklistId
|
||||
* @param {String} itemId
|
||||
* @param {String} state ('' or 'closed')
|
||||
*/
|
||||
Cypress.Commands.add('apiSetChecklistItemState', (playbookRunId, checklistId, itemId, state) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: playbookRunsEndpoint + `/${playbookRunId}/checklists/${checklistId}/item/${itemId}/state`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
new_state: state,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
// Verify playbook run is created
|
||||
Cypress.Commands.add('verifyPlaybookRunActive', (teamId, playbookRunName, playbookRunDescription) => {
|
||||
cy.apiGetPlaybookRunByName(teamId, playbookRunName).then((response) => {
|
||||
const returnedPlaybookRuns = response.body;
|
||||
const playbookRun = returnedPlaybookRuns.items.find((inc) => inc.name === playbookRunName);
|
||||
assert.isDefined(playbookRun);
|
||||
assert.equal(playbookRun.end_at, 0);
|
||||
assert.equal(playbookRun.name, playbookRunName);
|
||||
|
||||
cy.log('test 1');
|
||||
|
||||
// Only check the description if provided. The server may supply a default depending
|
||||
// on how the playbook run was started.
|
||||
if (playbookRunDescription) {
|
||||
assert.equal(playbookRun.description, playbookRunDescription);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Verify playbook run exists but is not active
|
||||
Cypress.Commands.add('verifyPlaybookRunEnded', (teamId, playbookRunName) => {
|
||||
cy.apiGetPlaybookRunByName(teamId, playbookRunName).then((response) => {
|
||||
const returnedPlaybookRuns = response.body;
|
||||
const playbookRun = returnedPlaybookRuns.items.find((inc) => inc.name === playbookRunName);
|
||||
assert.isDefined(playbookRun);
|
||||
assert.notEqual(playbookRun.end_at, 0);
|
||||
});
|
||||
});
|
||||
|
||||
// Create a playbook programmatically.
|
||||
Cypress.Commands.add('apiCreatePlaybook', (
|
||||
{
|
||||
teamId,
|
||||
title,
|
||||
description,
|
||||
createPublicPlaybookRun,
|
||||
createChannelMemberOnNewParticipant = true,
|
||||
checklists,
|
||||
memberIDs,
|
||||
makePublic = true,
|
||||
broadcastEnabled,
|
||||
broadcastChannelIds,
|
||||
reminderMessageTemplate,
|
||||
reminderTimerDefaultSeconds = 24 * 60 * 60, // 24 hours
|
||||
statusUpdateEnabled = true,
|
||||
retrospectiveReminderIntervalSeconds,
|
||||
retrospectiveTemplate,
|
||||
retrospectiveEnabled = true,
|
||||
invitedUserIds,
|
||||
inviteUsersEnabled,
|
||||
defaultOwnerId,
|
||||
defaultOwnerEnabled,
|
||||
announcementChannelId,
|
||||
announcementChannelEnabled,
|
||||
webhookOnCreationURLs,
|
||||
webhookOnCreationEnabled,
|
||||
webhookOnStatusUpdateURLs,
|
||||
webhookOnStatusUpdateEnabled,
|
||||
messageOnJoin,
|
||||
messageOnJoinEnabled,
|
||||
signalAnyKeywords,
|
||||
signalAnyKeywordsEnabled,
|
||||
channelNameTemplate,
|
||||
runSummaryTemplate,
|
||||
runSummaryTemplateEnabled,
|
||||
channelMode = 'create_new_channel',
|
||||
channelId = '',
|
||||
metrics,
|
||||
}) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/plugins/playbooks/api/v0/playbooks',
|
||||
method: 'POST',
|
||||
body: {
|
||||
title,
|
||||
description,
|
||||
team_id: teamId,
|
||||
create_public_playbook_run: createPublicPlaybookRun,
|
||||
create_channel_member_on_new_participant: createChannelMemberOnNewParticipant,
|
||||
checklists,
|
||||
public: makePublic,
|
||||
members: memberIDs?.map((val) => ({user_id: val, roles: ['playbook_member', 'playbook_admin']})),
|
||||
broadcast_enabled: broadcastEnabled,
|
||||
broadcast_channel_ids: broadcastChannelIds,
|
||||
reminder_message_template: reminderMessageTemplate,
|
||||
reminder_timer_default_seconds: reminderTimerDefaultSeconds,
|
||||
status_update_enabled: statusUpdateEnabled,
|
||||
retrospective_reminder_interval_seconds: retrospectiveReminderIntervalSeconds,
|
||||
retrospective_template: retrospectiveTemplate,
|
||||
retrospective_enabled: retrospectiveEnabled,
|
||||
invited_user_ids: invitedUserIds,
|
||||
invite_users_enabled: inviteUsersEnabled,
|
||||
default_owner_id: defaultOwnerId,
|
||||
default_owner_enabled: defaultOwnerEnabled,
|
||||
announcement_channel_id: announcementChannelId,
|
||||
announcement_channel_enabled: announcementChannelEnabled,
|
||||
webhook_on_creation_urls: webhookOnCreationURLs,
|
||||
webhook_on_creation_enabled: webhookOnCreationEnabled,
|
||||
webhook_on_status_update_urls: webhookOnStatusUpdateURLs,
|
||||
webhook_on_status_update_enabled: webhookOnStatusUpdateEnabled,
|
||||
message_on_join: messageOnJoin,
|
||||
message_on_join_enabled: messageOnJoinEnabled,
|
||||
signal_any_keywords: signalAnyKeywords,
|
||||
signal_any_keywords_enabled: signalAnyKeywordsEnabled,
|
||||
channel_name_template: channelNameTemplate,
|
||||
run_summary_template: runSummaryTemplate,
|
||||
run_summary_template_enabled: runSummaryTemplateEnabled,
|
||||
channel_mode: channelMode,
|
||||
channel_id: channelId,
|
||||
metrics,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
cy.wrap(response.headers.location);
|
||||
}).then((location) => {
|
||||
cy.request({
|
||||
url: location,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Create a test playbook programmatically.
|
||||
Cypress.Commands.add('apiCreateTestPlaybook', (
|
||||
{
|
||||
teamId,
|
||||
title,
|
||||
userId,
|
||||
broadcastEnabled,
|
||||
broadcastChannelIds,
|
||||
reminderMessageTemplate,
|
||||
checklists,
|
||||
inviteUsersEnabled,
|
||||
reminderTimerDefaultSeconds = 24 * 60 * 60, // 24 hours
|
||||
otherMembers = [],
|
||||
invitedUserIds = [],
|
||||
channelNameTemplate = '',
|
||||
}) => (
|
||||
cy.apiCreatePlaybook({
|
||||
teamId,
|
||||
title,
|
||||
checklists: checklists || [{
|
||||
title: 'Stage 1',
|
||||
items: [
|
||||
{title: 'Step 1'},
|
||||
{title: 'Step 2'},
|
||||
],
|
||||
}],
|
||||
memberIDs: [
|
||||
userId,
|
||||
...otherMembers,
|
||||
],
|
||||
broadcastEnabled,
|
||||
broadcastChannelIds,
|
||||
reminderMessageTemplate,
|
||||
reminderTimerDefaultSeconds,
|
||||
invitedUserIds,
|
||||
inviteUsersEnabled,
|
||||
channelNameTemplate,
|
||||
createChannelMemberOnNewParticipant: true,
|
||||
removeChannelMemberOnRemovedParticipant: true,
|
||||
})
|
||||
));
|
||||
|
||||
// Verify that the playbook was created
|
||||
Cypress.Commands.add('verifyPlaybookCreated', (teamId, playbookTitle) => (
|
||||
cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/plugins/playbooks/api/v0/playbooks',
|
||||
qs: {team_id: teamId, sort: 'title', direction: 'asc'},
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
const playbookResults = response.body;
|
||||
const playbook = playbookResults.items.find((p) => p.title === playbookTitle);
|
||||
assert.isDefined(playbook);
|
||||
})
|
||||
));
|
||||
|
||||
// Get a playbook
|
||||
Cypress.Commands.add('apiGetPlaybook', (playbookId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/plugins/playbooks/api/v0/playbooks/${playbookId}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
// Update a playbook
|
||||
Cypress.Commands.add('apiUpdatePlaybook', (playbook, expectedHttpCode = StatusOK) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/plugins/playbooks/api/v0/playbooks/${playbook.id}`,
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(playbook),
|
||||
failOnStatusCode: false,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(expectedHttpCode);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
// Archive a playbook
|
||||
Cypress.Commands.add('apiArchivePlaybook', (playbookId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/plugins/playbooks/api/v0/playbooks/${playbookId}`,
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(204);
|
||||
});
|
||||
});
|
||||
|
||||
// Follow a playbook run
|
||||
Cypress.Commands.add('apiFollowPlaybookRun', (playbookRunId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/plugins/playbooks/api/v0/runs/${playbookRunId}/followers`,
|
||||
method: 'PUT',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
// Unfollow a playbook run
|
||||
Cypress.Commands.add('apiUnfollowPlaybookRun', (playbookRunId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/plugins/playbooks/api/v0/runs/${playbookRunId}/followers`,
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
//addUsersToRun
|
||||
Cypress.Commands.add('apiAddUsersToRun', (playbookRunId, usersIds) => {
|
||||
const query = `
|
||||
mutation AddRunParticipants($runID: String!, $userIDs: [String!]!) {
|
||||
addRunParticipants(runID: $runID, userIDs: $userIDs)
|
||||
}
|
||||
`;
|
||||
const vars = {
|
||||
runID: playbookRunId,
|
||||
userIDs: usersIds,
|
||||
};
|
||||
return doGraphqlQuery(query, 'AddRunParticipants', vars).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
//updateRun
|
||||
Cypress.Commands.add('apiUpdateRun', (playbookRunId, updates) => {
|
||||
const query = `
|
||||
mutation UpdateRun($id: String!, $updates: RunUpdates!) {
|
||||
updateRun(id: $id, updates: $updates)
|
||||
}
|
||||
`;
|
||||
const vars = {
|
||||
id: playbookRunId,
|
||||
updates,
|
||||
};
|
||||
return doGraphqlQuery(query, 'UpdateRun', vars).then((response) => {
|
||||
expect(response.status).to.equal(StatusOK);
|
||||
cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
const doGraphqlQuery = (query, operationName, variables) => {
|
||||
const payload = {query, operationName, variables};
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/plugins/playbooks/api/v0/query',
|
||||
body: JSON.stringify(payload),
|
||||
method: 'POST',
|
||||
});
|
||||
};
|
||||
152
e2e-tests/cypress/tests/support/api/plugin.d.ts
поставляемый
Обычный файл
152
e2e-tests/cypress/tests/support/api/plugin.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,152 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
interface PluginStatus {
|
||||
isInstalled: boolean;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
interface PluginTestInfo {
|
||||
id: string;
|
||||
version: string;
|
||||
url: string;
|
||||
filename: string;
|
||||
}
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get plugins.
|
||||
* See https://api.mattermost.com/#tag/plugins/paths/~1plugins/get
|
||||
* @returns {PluginsResponse} `out.plugins` as `PluginsResponse`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetAllPlugins().then(({plugins}) => {
|
||||
* // do something with plugins
|
||||
* });
|
||||
*/
|
||||
apiGetAllPlugins(): Chainable<PluginsResponse>;
|
||||
|
||||
/**
|
||||
* Get plugins.
|
||||
* @param {string} pluginId - plugin ID
|
||||
* @param {string} version - plugin version
|
||||
*
|
||||
* @returns {PluginStatus} - plugin status if upload and active
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetPluginStatus(pluginId, version).then((status) => {
|
||||
* // do something with status
|
||||
* });
|
||||
*/
|
||||
apiGetPluginStatus(pluginId: string, version?: string): Chainable<PluginStatus>;
|
||||
|
||||
/**
|
||||
* Upload plugin.
|
||||
* See https://api.mattermost.com/#tag/plugins/paths/~1plugins/post
|
||||
* @param {string} filename - name of the plugin to upload
|
||||
* @returns {Response} response: Cypress-chainable response
|
||||
*
|
||||
* @example
|
||||
* cy.apiUploadPlugin('filename');
|
||||
*/
|
||||
apiUploadPlugin(filename: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Upload a plugin and enable.
|
||||
* - If a plugin is already active, then it will immediately return.
|
||||
* - If a plugin is inactive, then it will be enabled only.
|
||||
* - If a plugin is not found in the server, then it will be uploaded
|
||||
* and the enabled.
|
||||
* - On plugin upload, if `pluginTestInfo` includes a `url` field, then
|
||||
* the plugin will be installed via URL. Otherwise if `filename` field
|
||||
* is present, then it will look at such filename under fixtures folder
|
||||
* and then use the file to upload.
|
||||
*
|
||||
* @param {PluginTestInfo} pluginTestInfo - plugin test info
|
||||
* @returns {Response} response: Cypress-chainable response
|
||||
*
|
||||
* @example
|
||||
* cy.apiUploadAndEnablePlugin(pluginTestInfo);
|
||||
*/
|
||||
apiUploadAndEnablePlugin(pluginTestInfo: PluginTestInfo): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Install plugin from url.
|
||||
* See https://api.mattermost.com/#tag/plugins/paths/~1plugins~1install_from_url/post
|
||||
* @param {string} pluginDownloadUrl - URL used to download the plugin
|
||||
* @param {string} force - Set to 'true' to overwrite a previously installed plugin with the same ID, if any
|
||||
* @returns {PluginManifest} `out.plugin` as `PluginManifest`
|
||||
*
|
||||
* @example
|
||||
* cy.apiInstallPluginFromUrl('url', 'true').then(({plugin}) => {
|
||||
* // do something with plugin
|
||||
* });
|
||||
*/
|
||||
apiInstallPluginFromUrl(pluginDownloadUrl: string, force: string): Chainable<PluginManifest>;
|
||||
|
||||
/**
|
||||
* Enable plugin.
|
||||
* See https://api.mattermost.com/#tag/plugins/paths/~1plugins~1{plugin_id}~1enable/post
|
||||
* @param {string} pluginId - Id of the plugin to enable
|
||||
* @returns {string} `out.status`
|
||||
*
|
||||
* @example
|
||||
* cy.apiEnablePluginById('pluginId');
|
||||
*/
|
||||
apiEnablePluginById(pluginId: string): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Disable plugin.
|
||||
* See https://api.mattermost.com/#tag/plugins/paths/~1plugins~1{plugin_id}~disable/post
|
||||
* @param {string} pluginId - Id of the plugin to disable
|
||||
* @returns {string} `out.status`
|
||||
*
|
||||
* @example
|
||||
* cy.apiDisablePluginById('pluginId');
|
||||
*/
|
||||
apiDisablePluginById(pluginId: string): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Disable all plugins installed that are not prepackaged.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDisableNonPrepackagedPlugins();
|
||||
*/
|
||||
apiDisableNonPrepackagedPlugins(): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Remove plugin.
|
||||
* See https://api.mattermost.com/#tag/plugins/paths/~1plugins~1{plugin_id}/delete
|
||||
* @param {string} pluginId - Id of the plugin to uninstall
|
||||
* @returns {string} `out.status`
|
||||
*
|
||||
* @example
|
||||
* cy.apiRemovePluginById('url');
|
||||
*/
|
||||
apiRemovePluginById(pluginId: string, force: string): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Removes all active and inactive plugins.
|
||||
*
|
||||
* @example
|
||||
* cy.apiUninstallAllPlugins();
|
||||
*/
|
||||
apiUninstallAllPlugins(): Chainable;
|
||||
}
|
||||
}
|
||||
198
e2e-tests/cypress/tests/support/api/plugin.js
Обычный файл
198
e2e-tests/cypress/tests/support/api/plugin.js
Обычный файл
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import * as TIMEOUTS from '../../fixtures/timeouts';
|
||||
|
||||
// *****************************************************************************
|
||||
// Plugins
|
||||
// https://api.mattermost.com/#tag/plugins
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiGetAllPlugins', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/plugins',
|
||||
method: 'GET',
|
||||
failOnStatusCode: false,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({plugins: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
function getPlugin(plugins, pluginId, version) {
|
||||
return Cypress._.find(plugins, (plugin) => {
|
||||
return version ? plugin.id === pluginId && plugin.version === version : plugin.id === pluginId;
|
||||
});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiGetPluginStatus', (pluginId, version) => {
|
||||
return cy.apiGetAllPlugins().then(({plugins}) => {
|
||||
const active = getPlugin(plugins.active, pluginId, version);
|
||||
const inactive = getPlugin(plugins.inactive, pluginId, version);
|
||||
|
||||
if (active) {
|
||||
return cy.wrap({isInstalled: true, isActive: true});
|
||||
}
|
||||
|
||||
if (inactive) {
|
||||
return cy.wrap({isInstalled: true, isActive: false});
|
||||
}
|
||||
|
||||
return cy.wrap({isInstalled: false, isActive: false});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUploadPlugin', (filename) => {
|
||||
const options = {
|
||||
url: '/api/v4/plugins',
|
||||
method: 'POST',
|
||||
successStatus: 201,
|
||||
};
|
||||
return cy.apiUploadFile('plugin', filename, options).then(() => {
|
||||
return cy.wait(TIMEOUTS.THREE_SEC);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUploadAndEnablePlugin', ({filename, url, id, version}) => {
|
||||
return cy.apiGetPluginStatus(id, version).then((data) => {
|
||||
// # If already active, then only return the data
|
||||
if (data.isActive) {
|
||||
cy.log(`${id}: Plugin is active.`);
|
||||
return cy.wrap(data);
|
||||
}
|
||||
|
||||
// # If already installed, then only enable the plugin
|
||||
if (data.isInstalled) {
|
||||
cy.log(`${id}: Plugin is inactive. Only going to enable.`);
|
||||
return cy.apiEnablePluginById(id).then(() => {
|
||||
cy.wait(TIMEOUTS.ONE_SEC);
|
||||
return cy.wrap(data);
|
||||
});
|
||||
}
|
||||
|
||||
if (url) {
|
||||
// # Upload plugin by URL then enable
|
||||
cy.log(`${id}: Plugin is to be uploaded via URL and then enable.`);
|
||||
return cy.apiInstallPluginFromUrl(url).then(() => {
|
||||
cy.wait(TIMEOUTS.FIVE_SEC);
|
||||
return cy.apiEnablePluginById(id).then(() => {
|
||||
cy.wait(TIMEOUTS.ONE_SEC);
|
||||
return cy.wrap({isInstalled: true, isActive: true});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// # Upload plugin by file then enable
|
||||
cy.log(`${id}: Plugin is to be uploaded by filename and then enable.`);
|
||||
return cy.apiUploadPlugin(filename).then(() => {
|
||||
return cy.apiEnablePluginById(id).then(() => {
|
||||
return cy.wrap({isInstalled: true, isActive: true});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiInstallPluginFromUrl', (url, force = true) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/plugins/install_from_url?plugin_download_url=${encodeURIComponent(url)}&force=${force}`,
|
||||
method: 'POST',
|
||||
timeout: TIMEOUTS.TWO_MIN,
|
||||
failOnStatusCode: false,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
|
||||
cy.wait(TIMEOUTS.THREE_SEC);
|
||||
return cy.wrap({plugin: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiEnablePluginById', (pluginId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/plugins/${encodeURIComponent(pluginId)}/enable`,
|
||||
method: 'POST',
|
||||
timeout: TIMEOUTS.TWO_MIN,
|
||||
failOnStatusCode: false,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDisablePluginById', (pluginId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/plugins/${encodeURIComponent(pluginId)}/disable`,
|
||||
method: 'POST',
|
||||
timeout: TIMEOUTS.ONE_MIN,
|
||||
failOnStatusCode: false,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
const prepackagedPlugins = [
|
||||
'antivirus',
|
||||
'mattermost-autolink',
|
||||
'com.mattermost.aws-sns',
|
||||
'com.mattermost.plugin-channel-export',
|
||||
'com.mattermost.custom-attributes',
|
||||
'github',
|
||||
'com.github.manland.mattermost-plugin-gitlab',
|
||||
'com.mattermost.plugin-incident-management',
|
||||
'jenkins',
|
||||
'jira',
|
||||
'com.mattermost.nps',
|
||||
'com.mattermost.welcomebot',
|
||||
'zoom',
|
||||
];
|
||||
|
||||
Cypress.Commands.add('apiDisableNonPrepackagedPlugins', () => {
|
||||
cy.apiGetAllPlugins().then(({plugins}) => {
|
||||
plugins.active.forEach((plugin) => {
|
||||
if (!prepackagedPlugins.includes(plugin.id)) {
|
||||
cy.apiDisablePluginById(plugin.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiRemovePluginById', (pluginId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/plugins/${encodeURIComponent(pluginId)}`,
|
||||
method: 'DELETE',
|
||||
timeout: TIMEOUTS.TWO_MIN,
|
||||
failOnStatusCode: false,
|
||||
}).then((response) => {
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUninstallAllPlugins', () => {
|
||||
// # Uninstall all plugins
|
||||
cy.apiGetAllPlugins().then(({plugins}) => {
|
||||
const {active, inactive} = plugins;
|
||||
inactive.forEach((plugin) => cy.apiRemovePluginById(plugin.id));
|
||||
active.forEach((plugin) => cy.apiRemovePluginById(plugin.id));
|
||||
});
|
||||
|
||||
// * Check that all plugins are uninstalled
|
||||
cy.apiGetAllPlugins().then(({plugins}) => {
|
||||
const {active, inactive} = plugins;
|
||||
|
||||
// # Log all uninstalled plugins for debugging
|
||||
if (active.length) {
|
||||
cy.log(JSON.stringify(active));
|
||||
}
|
||||
if (inactive.length) {
|
||||
cy.log(JSON.stringify(active));
|
||||
}
|
||||
|
||||
expect(active.length).to.equal(0);
|
||||
expect(inactive.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
166
e2e-tests/cypress/tests/support/api/preference.d.ts
поставляемый
Обычный файл
166
e2e-tests/cypress/tests/support/api/preference.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
// *******************************************************************************
|
||||
// Preferences
|
||||
// https://api.mattermost.com/#tag/preferences
|
||||
// *******************************************************************************
|
||||
|
||||
/**
|
||||
* Save a list of the user's preferences.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {PreferenceType[]} preferences - List of preference objects
|
||||
* @param {string} userId - User ID
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveUserPreference([{user_id: 'user-id', category: 'display_settings', name: 'channel_display_mode', value: 'full'}], 'user-id');
|
||||
*/
|
||||
apiSaveUserPreference(preferences: PreferenceType[], userId: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Get the full list of the user's preferences.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/get
|
||||
* @param {string} userId - User ID
|
||||
* @returns {Response} response: Cypress-chainable response which should have a list of preference objects
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetUserPreference('user-id');
|
||||
*/
|
||||
apiGetUserPreference(userId: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save clock display mode to 24-hour preference.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {boolean} is24Hour - true (default) or false for 12-hour
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveClockDisplayModeTo24HourPreference(true);
|
||||
*/
|
||||
apiSaveClockDisplayModeTo24HourPreference(is24Hour: boolean): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save onboarding tasklist preference.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} name - options are complete_profile, team_setup, invite_members or hide
|
||||
* @param {string} value - options are 'true' or 'false'
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveOnboardingTaskListPreference('user-id', 'hide', 'true');
|
||||
*/
|
||||
apiSaveOnboardingTaskListPreference(userId: string, name: string, value: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save DM channel show preference.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} otherUserId - Other user in a DM channel
|
||||
* @param {string} value - options are 'true' or 'false'
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveDirectChannelShowPreference('user-id', 'other-user-id', 'false');
|
||||
*/
|
||||
apiSaveDirectChannelShowPreference(userId: string, otherUserId: string, value: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save Collapsed Reply Threads preference.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} value - options are 'on' or 'off'
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveCRTPreference('user-id', 'on');
|
||||
*/
|
||||
apiSaveCRTPreference(userId: string, value: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Saves tutorial step of a user
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} value - value of tutorial step, e.g. '999' (default, completed tutorial)
|
||||
*/
|
||||
apiSaveTutorialStep(userId: string, value: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save cloud trial banner preference.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} name - options are trial or hide
|
||||
* @param {string} value - options are 'max_days_banner' or '3_days_banner' for trial, and 'true' or 'false' for hide
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveCloudTrialBannerPreference('user-id', 'hide', 'true');
|
||||
*/
|
||||
apiSaveCloudTrialBannerPreference(userId: string, name: string, value: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save actions menu preference.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} value - true (default) or false
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveActionsMenuPreference('user-id', true);
|
||||
*/
|
||||
apiSaveActionsMenuPreference(userId: string, value: boolean): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save show trial modal.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} name - trial_modal_auto_shown
|
||||
* @param {string} value - values are 'true' or 'false'
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveStartTrialModal('user-id', 'true');
|
||||
*/
|
||||
apiSaveStartTrialModal(userId: string, value: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Save drafts tour tip preference.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @param {string} value - values are 'true' or 'false'
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiSaveDraftsTourTipPreference('user-id', 'true');
|
||||
*/
|
||||
apiSaveDraftsTourTipPreference(userId: string, value: boolean): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Mark Boards welcome page as viewed.
|
||||
* See https://api.mattermost.com/#tag/preferences/paths/~1users~1{user_id}~1preferences/put
|
||||
* @param {string} userId - User ID
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiBoardsWelcomePageViewed('user-id');
|
||||
*/
|
||||
apiBoardsWelcomePageViewed(userId: string): Chainable<Response>;
|
||||
}
|
||||
}
|
||||
444
e2e-tests/cypress/tests/support/api/preference.js
Обычный файл
444
e2e-tests/cypress/tests/support/api/preference.js
Обычный файл
@@ -0,0 +1,444 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import theme from '../../fixtures/theme.json';
|
||||
|
||||
// *****************************************************************************
|
||||
// Preferences
|
||||
// https://api.mattermost.com/#tag/preferences
|
||||
// *****************************************************************************
|
||||
|
||||
/**
|
||||
* Saves user's preference directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {Array} preference - a list of user's preferences
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveUserPreference', (preferences = [], userId = 'me') => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/preferences`,
|
||||
method: 'PUT',
|
||||
body: preferences,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves clock display mode 24-hour preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {Boolean} is24Hour - Either true (default) or false
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveClockDisplayModeTo24HourPreference', (is24Hour = true) => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'display_settings',
|
||||
name: 'use_military_time',
|
||||
value: is24Hour.toString(),
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves channel display mode preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {String} value - Either "full" (default) or "centered"
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveChannelDisplayModePreference', (value = 'full') => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'display_settings',
|
||||
name: 'channel_display_mode',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves message display preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {String} value - Either "clean" (default) or "compact"
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveMessageDisplayPreference', (value = 'clean') => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'display_settings',
|
||||
name: 'message_display',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves show markdown preview option preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {String} value - Either "true" to show the options (default) or "false"
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveShowMarkdownPreviewPreference', (value = 'true') => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'advanced_settings',
|
||||
name: 'feature_enabled_markdown_preview',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves teammate name display preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {String} value - Either "username" (default), "nickname_full_name" or "full_name"
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveTeammateNameDisplayPreference', (value = 'username') => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'display_settings',
|
||||
name: 'name_format',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves theme preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {Object} value - theme object. Will pass default value if none is provided.
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveThemePreference', (value = JSON.stringify(theme.default)) => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'theme',
|
||||
name: '',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
const defaultSidebarSettingPreference = {
|
||||
grouping: 'by_type',
|
||||
unreads_at_top: 'true',
|
||||
favorite_at_top: 'true',
|
||||
sorting: 'alpha',
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves theme preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {Object} value - sidebar settings object. Will pass default value if none is provided.
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveSidebarSettingPreference', (value = {}) => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const newValue = {
|
||||
...defaultSidebarSettingPreference,
|
||||
...value,
|
||||
};
|
||||
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'sidebar_settings',
|
||||
name: '',
|
||||
value: JSON.stringify(newValue),
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves the preference on whether to show link and image previews
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {boolean} show - Either "true" to show link and images previews (default), or "false"
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveLinkPreviewsPreference', (show = 'true') => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'display_settings',
|
||||
name: 'link_previews',
|
||||
value: show,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves the preference on whether to show link and image previews expanded
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {boolean} collapse - Either "true" to show previews collapsed (default), or "false"
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveCollapsePreviewsPreference', (collapse = 'true') => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const preference = {
|
||||
user_id: cookie.value,
|
||||
category: 'display_settings',
|
||||
name: 'collapse_previews',
|
||||
value: collapse,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference]);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves tutorial step of a user
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {string} value - value of tutorial step, e.g. '999' (default, completed tutorial)
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveTutorialStep', (userId, value = '999') => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'tutorial_step',
|
||||
name: userId,
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveOnboardingPreference', (userId, name, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'recommended_next_steps',
|
||||
name,
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveDirectChannelShowPreference', (userId, otherUserId, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'direct_channel_show',
|
||||
name: otherUserId,
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiHideSidebarWhatsNewModalPreference', (userId, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'whats_new_modal',
|
||||
name: 'has_seen_sidebar_whats_new_modal',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetUserPreference', (userId) => {
|
||||
return cy.request(`/api/v4/users/${userId}/preferences`).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveCRTPreference', (userId, value = 'on') => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'display_settings',
|
||||
name: 'collapsed_reply_threads',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveCloudTrialBannerPreference', (userId, name, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'cloud_trial_banner',
|
||||
name,
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveActionsMenuPreference', (userId, value = true) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'actions_menu',
|
||||
name: 'actions_menu_tutorial_state',
|
||||
value: JSON.stringify({actions_menu_modal_viewed: value}),
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveStartTrialModal', (userId, value = 'true') => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'start_trial_modal',
|
||||
name: 'trial_modal_auto_shown',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveOnboardingTaskListPreference', (userId, name, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'onboarding_task_list',
|
||||
name,
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveSkipStepsPreference', (userId, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'recommended_next_steps',
|
||||
name: 'skip',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveUnreadScrollPositionPreference', (userId, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'advanced_settings',
|
||||
name: 'unread_scroll_position',
|
||||
value,
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSaveDraftsTourTipPreference', (userId, value) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'drafts',
|
||||
name: 'drafts_tour_tip_showed',
|
||||
value: JSON.stringify({drafts_tour_tip_showed: value}),
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiBoardsWelcomePageViewed', (userId) => {
|
||||
const preferences = [{
|
||||
user_id: userId,
|
||||
category: 'boards',
|
||||
name: 'welcomePageViewed',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'boards',
|
||||
name: 'version72MessageCanceled',
|
||||
value: 'true',
|
||||
}];
|
||||
|
||||
return cy.apiSaveUserPreference(preferences, userId);
|
||||
});
|
||||
|
||||
/**
|
||||
* Saves Join/Leave messages preference of a user directly via API
|
||||
* This API assume that the user is logged in and has cookie to access
|
||||
* @param {Boolean} enable - Either true (default) or false
|
||||
*/
|
||||
Cypress.Commands.add('apiSaveJoinLeaveMessagesPreference', (userId, enable = true) => {
|
||||
const preference = {
|
||||
user_id: userId,
|
||||
category: 'advanced_settings',
|
||||
name: 'join_leave',
|
||||
value: enable.toString(),
|
||||
};
|
||||
|
||||
return cy.apiSaveUserPreference([preference], userId);
|
||||
});
|
||||
|
||||
/**
|
||||
* Disables tutorials for user by marking them finished
|
||||
*/
|
||||
Cypress.Commands.add('apiDisableTutorials', (userId) => {
|
||||
const preferences = [
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'playbook_edit',
|
||||
name: userId,
|
||||
value: '999',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'tutorial_pb_run_details',
|
||||
name: userId,
|
||||
value: '999',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'crt_thread_pane_step',
|
||||
name: userId,
|
||||
value: '999',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'playbook_preview',
|
||||
name: userId,
|
||||
value: '999',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'tutorial_step',
|
||||
name: userId,
|
||||
value: '999',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'crt_tutorial_triggered',
|
||||
name: userId,
|
||||
value: '999',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'crt_thread_pane_step',
|
||||
name: userId,
|
||||
value: '999',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'actions_menu',
|
||||
name: 'actions_menu_tutorial_state',
|
||||
value: '{"actions_menu_modal_viewed":true}',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'insights',
|
||||
name: 'insights_tutorial_state',
|
||||
value: '{"insights_modal_viewed":true}',
|
||||
},
|
||||
{
|
||||
user_id: userId,
|
||||
category: 'drafts',
|
||||
name: 'drafts_tour_tip_showed',
|
||||
value: '{"drafts_tour_tip_showed":true}',
|
||||
},
|
||||
];
|
||||
|
||||
return cy.apiSaveUserPreference(preferences, userId);
|
||||
});
|
||||
69
e2e-tests/cypress/tests/support/api/role.d.ts
поставляемый
Обычный файл
69
e2e-tests/cypress/tests/support/api/role.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get a role from the provided role name.
|
||||
* See https://api.mattermost.com/#tag/roles/paths/~1roles~1name~1{role_name}/get
|
||||
* @param {string} name - role name, e.g. 'system_user'
|
||||
* @returns {Role} `out.role` as `Role`
|
||||
*
|
||||
* @example
|
||||
* cy.getRoleByName('system_user').then(({role}) => {
|
||||
* // do something with role
|
||||
* });
|
||||
*/
|
||||
getRoleByName(name: string): Chainable<Role>;
|
||||
|
||||
/**
|
||||
* Get a list of roles by name.
|
||||
* See https://api.mattermost.com/#tag/roles/paths/~1roles~1names/post
|
||||
* @param {string[]} names - list of role names, e.g. ['system_user']
|
||||
* @returns {Role[]} `out.roles` as list of `Role` objects
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetRolesByNames(['system_user']).then(({roles}) => {
|
||||
* // do something with roles
|
||||
* });
|
||||
*/
|
||||
apiGetRolesByNames(names: string[]): Chainable<Role[]>;
|
||||
|
||||
/**
|
||||
* Patch a role by ID.
|
||||
* See https://api.mattermost.com/#tag/roles/paths/~1roles~1{role_id}~1patch/put
|
||||
* @param {string} id - role ID
|
||||
* @param {Permissions} patch.permissions - permissions
|
||||
* @returns {Role} `out.role` as `Role`
|
||||
*
|
||||
* @example
|
||||
* cy.apiPatchRole('role_id', patch).then(({role}) => {
|
||||
* // do something with role
|
||||
* });
|
||||
*/
|
||||
apiPatchRole(id: string, patch: Record<string, any>): Chainable<Role>;
|
||||
|
||||
/**
|
||||
* Reset roles to default values.
|
||||
*
|
||||
* @example
|
||||
* cy.apiResetRoles();
|
||||
*/
|
||||
apiResetRoles();
|
||||
}
|
||||
}
|
||||
83
e2e-tests/cypress/tests/support/api/role.js
Обычный файл
83
e2e-tests/cypress/tests/support/api/role.js
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
78
e2e-tests/cypress/tests/support/api/saml.d.ts
поставляемый
Обычный файл
78
e2e-tests/cypress/tests/support/api/saml.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get the status of the uploaded certificates and keys in use by your SAML configuration.
|
||||
* See https://api.mattermost.com/#tag/SAML/paths/~1saml~1certificate~1status/get
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetSAMLCertificateStatus();
|
||||
*/
|
||||
apiGetSAMLCertificateStatus(): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Get SAML metadata from the Identity Provider. SAML must be configured properly.
|
||||
* See https://api.mattermost.com/#tag/SAML/paths/~1saml~1metadatafromidp/post
|
||||
* @param {String} samlMetadataUrl - SAML metadata URL
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetMetadataFromIdp(samlMetadataUrl);
|
||||
*/
|
||||
apiGetMetadataFromIdp(samlMetadataUrl: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Upload the IDP certificate to be used with your SAML configuration. The server will pick a hard-coded filename for the IdpCertificateFile setting in your config.json.
|
||||
* See https://api.mattermost.com/#tag/SAML/paths/~1saml~1certificate~1idp/post
|
||||
* @param {String} filePath - path of the IDP certificate file relative to fixture folder
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* const filePath = 'saml-idp.crt';
|
||||
* cy.apiUploadSAMLIDPCert(filePath);
|
||||
*/
|
||||
apiUploadSAMLIDPCert(filePath: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Upload the public certificate to be used for encryption with your SAML configuration. The server will pick a hard-coded filename for the PublicCertificateFile setting in your config.json.
|
||||
* See https://api.mattermost.com/#tag/SAML/paths/~1saml~1certificate~1public/post
|
||||
* @param {String} filePath - path of the public certificate file relative to fixture folder
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* const filePath = 'saml-public.crt';
|
||||
* cy.apiUploadSAMLPublicCert(filePath);
|
||||
*/
|
||||
apiUploadSAMLPublicCert(filePath: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Upload the private key to be used for encryption with your SAML configuration. The server will pick a hard-coded filename for the PrivateKeyFile setting in your config.json.
|
||||
* See https://api.mattermost.com/#tag/SAML/paths/~1saml~1certificate~1private/post
|
||||
* @param {String} filePath - path of the private certificate file relative to fixture folder
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* const filePath = 'saml-private.crt';
|
||||
* cy.apiUploadSAMLPublicCert(filePath);
|
||||
*/
|
||||
apiUploadSAMLPrivateKey(filePath: string): Chainable<Response>;
|
||||
}
|
||||
}
|
||||
42
e2e-tests/cypress/tests/support/api/saml.js
Обычный файл
42
e2e-tests/cypress/tests/support/api/saml.js
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// SAML
|
||||
// https://api.mattermost.com/#tag/SAML
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiGetSAMLCertificateStatus', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/saml/certificate/status',
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.be.oneOf([200, 201]);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetMetadataFromIdp', (samlMetadataUrl) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/saml/metadatafromidp',
|
||||
method: 'POST',
|
||||
body: {saml_metadata_url: samlMetadataUrl},
|
||||
}).then((response) => {
|
||||
expect(response.status, 'Failed to obtain metadata from Identity Provider URL').to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUploadSAMLIDPCert', (filePath) => {
|
||||
cy.apiUploadFile('certificate', filePath, {url: '/api/v4/saml/certificate/idp', method: 'POST', successStatus: 200});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUploadSAMLPublicCert', (filePath) => {
|
||||
cy.apiUploadFile('certificate', filePath, {url: '/api/v4/saml/certificate/public', method: 'POST', successStatus: 200});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUploadSAMLPrivateKey', (filePath) => {
|
||||
cy.apiUploadFile('certificate', filePath, {url: '/api/v4/saml/certificate/private', method: 'POST', successStatus: 200});
|
||||
});
|
||||
45
e2e-tests/cypress/tests/support/api/scheme.d.ts
поставляемый
Обычный файл
45
e2e-tests/cypress/tests/support/api/scheme.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get the schemes.
|
||||
* See https://api.mattermost.com/#tag/schemes/paths/~1schemes/get
|
||||
* @param {string} scope - Limit the results returned to the provided scope, either team or channel.
|
||||
* @returns {Scheme[]} `out.schemes` as `Scheme[]`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetSchemes('team').then(({schemes}) => {
|
||||
* // do something with schemes
|
||||
* });
|
||||
*/
|
||||
apiGetSchemes(scope: string): Chainable<{schemes: Scheme[]}>;
|
||||
|
||||
/**
|
||||
* Delete a scheme.
|
||||
* See https://api.mattermost.com/#tag/schemes/paths/~1schemes~1{scheme_id}/delete
|
||||
* @param {string} schemeId - ID of the scheme to delete
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeleteScheme('scheme_id');
|
||||
*/
|
||||
apiDeleteScheme(schemeId: string): Chainable<Response>;
|
||||
}
|
||||
}
|
||||
41
e2e-tests/cypress/tests/support/api/scheme.js
Обычный файл
41
e2e-tests/cypress/tests/support/api/scheme.js
Обычный файл
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// Schemes
|
||||
// https://api.mattermost.com/#tag/schemes
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiGetSchemes', (scope) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/schemes?scope=${scope}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({schemes: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateScheme', (name, scope, description) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/schemes',
|
||||
method: 'POST',
|
||||
body: {display_name: name, scope, description},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({scheme: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeleteScheme', (schemeId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/schemes/' + schemeId,
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
114
e2e-tests/cypress/tests/support/api/setup.ts
Обычный файл
114
e2e-tests/cypress/tests/support/api/setup.ts
Обычный файл
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {ChainableT} from '../../types';
|
||||
|
||||
interface SetupResult {
|
||||
user: Cypress.UserProfile;
|
||||
team: Cypress.Team;
|
||||
channel: Cypress.Channel;
|
||||
channelUrl: string;
|
||||
offTopicUrl: string;
|
||||
townSquareUrl: string;
|
||||
}
|
||||
interface SetupParam {
|
||||
loginAfter?: boolean;
|
||||
promoteNewUserAsAdmin?: boolean;
|
||||
hideAdminTrialModal?: boolean;
|
||||
userPrefix?: string;
|
||||
teamPrefix?: {name: string; displayName: string};
|
||||
channelPrefix?: {name: string; displayName: string};
|
||||
skipBoardsWelcomePage?: boolean;
|
||||
}
|
||||
function apiInitSetup(arg: SetupParam = {}): ChainableT<SetupResult> {
|
||||
const {
|
||||
loginAfter = false,
|
||||
promoteNewUserAsAdmin = false,
|
||||
hideAdminTrialModal = true,
|
||||
userPrefix,
|
||||
teamPrefix = {name: 'team', displayName: 'Team'},
|
||||
channelPrefix = {name: 'channel', displayName: 'Channel'},
|
||||
skipBoardsWelcomePage = true,
|
||||
} = arg;
|
||||
|
||||
return (cy.apiCreateTeam(teamPrefix.name, teamPrefix.displayName) as any).then(({team}) => {
|
||||
// # Add public channel
|
||||
return (cy.apiCreateChannel(team.id, channelPrefix.name, channelPrefix.displayName) as any).then(({channel}) => {
|
||||
return (cy.apiCreateUser({prefix: userPrefix || (promoteNewUserAsAdmin ? 'admin' : 'user')}) as any).then(({user}) => {
|
||||
if (promoteNewUserAsAdmin) {
|
||||
(cy as any).apiPatchUserRoles(user.id, ['system_admin', 'system_user']);
|
||||
|
||||
// Only hide start trial modal for admin since it's not applicable to other users
|
||||
cy.apiSaveStartTrialModal(user.id, hideAdminTrialModal.toString());
|
||||
}
|
||||
|
||||
if (skipBoardsWelcomePage) {
|
||||
cy.apiBoardsWelcomePageViewed(user.id);
|
||||
}
|
||||
|
||||
return cy.apiAddUserToTeam(team.id, user.id).then(() => {
|
||||
return cy.apiAddUserToChannel(channel.id, user.id).then(() => {
|
||||
const getUrl = (channelName: string) => `/${team.name}/channels/${channelName}`;
|
||||
|
||||
const data = {
|
||||
channel,
|
||||
team,
|
||||
user,
|
||||
channelUrl: getUrl(channel.name),
|
||||
offTopicUrl: getUrl('off-topic'),
|
||||
townSquareUrl: getUrl('town-square'),
|
||||
};
|
||||
|
||||
if (loginAfter) {
|
||||
return cy.apiLogin(user).then(() => {
|
||||
return cy.wrap(data);
|
||||
});
|
||||
}
|
||||
|
||||
return cy.wrap(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiInitSetup', apiInitSetup);
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Creates a new user and make it a member of the new public team and its channels - one public channel, town-square and off-topic.
|
||||
* Created user has an option to log in after all are setup.
|
||||
* Requires sysadmin session to initiate this command.
|
||||
* @param {boolean} options.loginAfter - false (default) or true if wants to login as the new user after setting up. Note that when true, succeeding API request will be limited to access/permission of a regular system user.
|
||||
* @param {boolean} options.promoteNewUserAsAdmin - false (default) or true if wants to promote the newly created user as sysadmin.
|
||||
* @param {boolean} options.hideAdminTrialModal - true (default) or false if wants to hide Start Enterprise Trial modal.
|
||||
* @param {string} options.userPrefix - 'user' (default) or any prefix to easily identify a user
|
||||
* @param {string} options.teamPrefix - {name: 'team', displayName: 'Team'} (default) or any prefix to easily identify a team
|
||||
* @param {string} options.channelPrefix - {name: 'team', displayName: 'Team'} (default) or any prefix to easily identify a channel
|
||||
* @returns {Object} `out` Cypress-chainable, yielded with element passed into .wrap().
|
||||
* @returns {Cypress.UserProfile} `out.user` as `UserProfile` object
|
||||
* @returns {Cypress.Team} `out.team` as `Team` object
|
||||
* @returns {Cypress.Channel} `out.channel` as `Channel` object
|
||||
* @returns {string} `out.channelUrl` as channel URL
|
||||
* @returns {string} `out.offTopicUrl` as off-topic URL
|
||||
* @returns {string} `out.townSquareUrl` as town-square URL
|
||||
*
|
||||
* @example
|
||||
* let testUser;
|
||||
* let testTeam;
|
||||
* let testChannel;
|
||||
* cy.apiInitSetup(options).then(({team, channel, user}) => {
|
||||
* testUser = user;
|
||||
* testTeam = team;
|
||||
* testChannel = channel;
|
||||
* });
|
||||
*/
|
||||
apiInitSetup: typeof apiInitSetup;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
e2e-tests/cypress/tests/support/api/status.d.ts
поставляемый
Обычный файл
67
e2e-tests/cypress/tests/support/api/status.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Update status of a current user.
|
||||
* See https://api.mattermost.com/#tag/status/paths/~1users~1{user_id}~1status/put
|
||||
* @param {String} status - "online" (default), "offline", "away" or "dnd"
|
||||
* @returns {UserStatus} `out.status` as `UserStatus`
|
||||
*
|
||||
* @example
|
||||
* cy.apiUpdateUserStatus('offline').then(({status}) => {
|
||||
* // do something with status
|
||||
* });
|
||||
*/
|
||||
apiUpdateUserStatus(status: string): Chainable<UserStatus>;
|
||||
|
||||
/**
|
||||
* Get status of a current user.
|
||||
* See https://api.mattermost.com/#tag/status/paths/~1users~1{user_id}~1status/get
|
||||
* @param {String} userId - ID of a given user
|
||||
* @returns {UserStatus} `out.status` as `UserStatus`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetUserStatus('userId').then(({status}) => {
|
||||
* // examine the status information of the user
|
||||
* });
|
||||
*/
|
||||
apiGetStatus(userId: string): Chainable<UserStatus>;
|
||||
|
||||
/**
|
||||
* Update custom status of current user.
|
||||
* See https://api.mattermost.com/#tag/custom_status/paths/~1users~1{user_id}~1status/custom/put
|
||||
* @param {UserCustomStatus} customStatus - custom status to be updated
|
||||
*
|
||||
* @example
|
||||
* cy.apiUpdateUserCustomStatus({emoji: 'calendar', text: 'In a meeting'});
|
||||
*/
|
||||
apiUpdateUserCustomStatus(customStatus: UserCustomStatus);
|
||||
|
||||
/**
|
||||
* Clear custom status of the current user.
|
||||
* See https://api.mattermost.com/#tag/custom_status/paths/~1users~1{user_id}~1status/custom/delete
|
||||
* @param {UserCustomStatus} customStatus - custom status to be updated
|
||||
*
|
||||
* @example
|
||||
* cy.apiClearUserCustomStatus();
|
||||
*/
|
||||
apiClearUserCustomStatus();
|
||||
}
|
||||
}
|
||||
55
e2e-tests/cypress/tests/support/api/status.js
Обычный файл
55
e2e-tests/cypress/tests/support/api/status.js
Обычный файл
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// Status
|
||||
// https://api.mattermost.com/#tag/status
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiUpdateUserStatus', (status = 'online') => {
|
||||
return cy.getCookie('MMUSERID').then((cookie) => {
|
||||
const data = {user_id: cookie.value, status};
|
||||
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/me/status',
|
||||
method: 'PUT',
|
||||
body: data,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({status: response.body});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetUserStatus', (userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/status`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({status: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUpdateUserCustomStatus', (customStatus) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/me/status/custom',
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(customStatus),
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiClearUserCustomStatus', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/me/status/custom',
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
203
e2e-tests/cypress/tests/support/api/system.d.ts
поставляемый
Обычный файл
203
e2e-tests/cypress/tests/support/api/system.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,203 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get a subset of the server license needed by the client.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1license~1client/get
|
||||
* @returns {ClientLicense} `out.license` as `ClientLicense`
|
||||
* @returns {Boolean} `out.isLicensed`
|
||||
* @returns {Boolean} `out.isCloudLicensed`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetClientLicense().then(({license}) => {
|
||||
* // do something with license
|
||||
* });
|
||||
*/
|
||||
apiGetClientLicense(): Chainable<ClientLicense>;
|
||||
|
||||
/**
|
||||
* Verify if server has license for a certain feature and fail test if not found.
|
||||
* Upload a license if it does not exist.
|
||||
* @param {string[]} ...features - accepts multiple arguments of features to check, e.g. 'LDAP'
|
||||
* @returns {ClientLicense} `out.license` as `ClientLicense`
|
||||
*
|
||||
* @example
|
||||
* cy.apiRequireLicenseForFeature('LDAP');
|
||||
* cy.apiRequireLicenseForFeature('LDAP', 'SAML');
|
||||
*/
|
||||
apiRequireLicenseForFeature(...features: string[]): Chainable<ClientLicense>;
|
||||
|
||||
/**
|
||||
* Verify if server has license and fail test if not found.
|
||||
* Upload a license if it does not exist.
|
||||
* @returns {ClientLicense} `out.license` as `ClientLicense`
|
||||
*
|
||||
* @example
|
||||
* cy.apiRequireLicense();
|
||||
*/
|
||||
apiRequireLicense(): Chainable<ClientLicense>;
|
||||
|
||||
/**
|
||||
* Upload a license to enable enterprise features.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1license/post
|
||||
* @param {String} filePath - path of the license file relative to fixtures folder
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* const filePath = 'mattermost-license.txt';
|
||||
* cy.apiUploadLicense(filePath);
|
||||
*/
|
||||
apiUploadLicense(filePath: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Request and install a trial license for your server.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1trial-license/post
|
||||
* @returns {Object} `out.data` as response status
|
||||
*
|
||||
* @example
|
||||
* cy.apiInstallTrialLicense();
|
||||
*/
|
||||
apiInstallTrialLicense(): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Remove the license file from the server. This will disable all enterprise features.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1license/delete
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeleteLicense();
|
||||
*/
|
||||
apiDeleteLicense(): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Update configuration.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1config/put
|
||||
* @param {AdminConfig} newConfig - new config
|
||||
* @returns {AdminConfig} `out.config` as `AdminConfig`
|
||||
*
|
||||
* @example
|
||||
* cy.apiUpdateConfig().then(({config}) => {
|
||||
* // do something with config
|
||||
* });
|
||||
*/
|
||||
apiUpdateConfig(newConfig: DeepPartial<AdminConfig>): Chainable<{config: AdminConfig}>;
|
||||
|
||||
/**
|
||||
* Reload the configuration file to pick up on any changes made to it.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1config~1reload/post
|
||||
* @returns {AdminConfig} `out.config` as `AdminConfig`
|
||||
*
|
||||
* @example
|
||||
* cy.apiReloadConfig().then(({config}) => {
|
||||
* // do something with config
|
||||
* });
|
||||
*/
|
||||
apiReloadConfig(): Chainable<AdminConfig>;
|
||||
|
||||
/**
|
||||
* Get configuration.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1config/get
|
||||
* @param {Boolean} old - false (default) or true to return old format of client config
|
||||
* @returns {AdminConfig} `out.config` as `AdminConfig`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetConfig().then(({config}) => {
|
||||
* // do something with config
|
||||
* });
|
||||
*/
|
||||
apiGetConfig(): Chainable<{config: AdminConfig}>;
|
||||
|
||||
/**
|
||||
* Get analytics.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1analytics~1old/get
|
||||
* @returns {AnalyticsRow[]} `out.analytics` as `AnalyticsRow[]`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetAnalytics().then(({analytics}) => {
|
||||
* // do something with analytics
|
||||
* });
|
||||
*/
|
||||
apiGetAnalytics(): Chainable<AnalyticsRow[]>;
|
||||
|
||||
/**
|
||||
* Invalidate all the caches.
|
||||
* See https://api.mattermost.com/#tag/system/paths/~1caches~1invalidate/post
|
||||
* @returns {Object} `out.data` as response status
|
||||
*
|
||||
* @example
|
||||
* cy.apiInvalidateCache();
|
||||
*/
|
||||
apiInvalidateCache(): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Allow test for server other than Cloud edition or with Cloud license.
|
||||
* Otherwise, fail fast.
|
||||
* @example
|
||||
* cy.shouldNotRunOnCloudEdition();
|
||||
*/
|
||||
shouldNotRunOnCloudEdition(): Chainable;
|
||||
|
||||
/**
|
||||
* Allow test for server on Team edition or without license.
|
||||
* Otherwise, fail fast.
|
||||
* @example
|
||||
* cy.shouldRunOnTeamEdition();
|
||||
*/
|
||||
shouldRunOnTeamEdition(): Chainable;
|
||||
|
||||
/**
|
||||
* Allow test for server with Plugin upload enabled.
|
||||
* Otherwise, fail fast.
|
||||
* @example
|
||||
* cy.shouldHavePluginUploadEnabled();
|
||||
*/
|
||||
shouldHavePluginUploadEnabled(): Chainable;
|
||||
|
||||
/**
|
||||
* Allow test for server running with subpath.
|
||||
* Otherwise, fail fast.
|
||||
* @example
|
||||
* cy.shouldRunWithSubpath();
|
||||
*/
|
||||
shouldRunWithSubpath(): Chainable;
|
||||
|
||||
/**
|
||||
* Allow test if matches feature flag setting
|
||||
* Otherwise, fail fast.
|
||||
*
|
||||
* @param {string} feature - feature name
|
||||
* @param {string} expectedValue - expected value
|
||||
*
|
||||
* @example
|
||||
* cy.shouldHaveFeatureFlag('feature', 'expected-value');
|
||||
*/
|
||||
shouldHaveFeatureFlag(feature: string, expectedValue: any): Chainable;
|
||||
|
||||
/**
|
||||
* Require email service to be reachable by the server
|
||||
* thru "/api/v4/email/test" if sysadmin account has
|
||||
* permission to do so. Otherwise, skip email test.
|
||||
*
|
||||
* @example
|
||||
* cy.shouldHaveEmailEnabled();
|
||||
*/
|
||||
shouldHaveEmailEnabled(): Chainable;
|
||||
}
|
||||
}
|
||||
332
e2e-tests/cypress/tests/support/api/system.js
Обычный файл
332
e2e-tests/cypress/tests/support/api/system.js
Обычный файл
@@ -0,0 +1,332 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import merge from 'deepmerge';
|
||||
|
||||
import {Constants} from '../../utils';
|
||||
|
||||
import onPremDefaultConfig from './on_prem_default_config.json';
|
||||
import cloudDefaultConfig from './cloud_default_config.json';
|
||||
|
||||
// *****************************************************************************
|
||||
// System
|
||||
// https://api.mattermost.com/#tag/system
|
||||
// *****************************************************************************
|
||||
|
||||
function hasLicenseForFeature(license, key) {
|
||||
let hasLicense = false;
|
||||
|
||||
for (const [k, v] of Object.entries(license)) {
|
||||
if (k === key && v === 'true') {
|
||||
hasLicense = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hasLicense;
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiGetClientLicense', () => {
|
||||
return cy.request('/api/v4/license/client?format=old').then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
const license = response.body;
|
||||
const isLicensed = license.IsLicensed === 'true';
|
||||
const isCloudLicensed = hasLicenseForFeature(license, 'Cloud');
|
||||
|
||||
return cy.wrap({
|
||||
license: response.body,
|
||||
isLicensed,
|
||||
isCloudLicensed,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiRequireLicenseForFeature', (...keys) => {
|
||||
Cypress.log({name: 'EE License', message: `Checking if server has license for feature: __${Object.values(keys).join(', ')}__.`});
|
||||
|
||||
return uploadLicenseIfNotExist().then((data) => {
|
||||
const {license, isLicensed} = data;
|
||||
const hasLicenseMessage = `Server ${isLicensed ? 'has' : 'has no'} EE license.`;
|
||||
expect(isLicensed, hasLicenseMessage).to.equal(true);
|
||||
|
||||
Object.values(keys).forEach((key) => {
|
||||
const hasLicenseKey = hasLicenseForFeature(license, key);
|
||||
const hasLicenseKeyMessage = `Server ${hasLicenseKey ? 'has' : 'has no'} EE license for feature: __${key}__`;
|
||||
expect(hasLicenseKey, hasLicenseKeyMessage).to.equal(true);
|
||||
});
|
||||
|
||||
return cy.wrap(data);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiRequireLicense', () => {
|
||||
Cypress.log({name: 'EE License', message: 'Checking if server has license.'});
|
||||
|
||||
return uploadLicenseIfNotExist().then((data) => {
|
||||
const hasLicenseMessage = `Server ${data.isLicensed ? 'has' : 'has no'} EE license.`;
|
||||
expect(data.isLicensed, hasLicenseMessage).to.equal(true);
|
||||
|
||||
return cy.wrap(data);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUploadLicense', (filePath) => {
|
||||
cy.apiUploadFile('license', filePath, {url: '/api/v4/license', method: 'POST', successStatus: 200});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiInstallTrialLicense', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/trial-license',
|
||||
method: 'POST',
|
||||
body: {
|
||||
trialreceive_emails_accepted: true,
|
||||
terms_accepted: true,
|
||||
users: Cypress.env('numberOfTrialUsers'),
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeleteLicense', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/license',
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({response});
|
||||
});
|
||||
});
|
||||
|
||||
export const getDefaultConfig = () => {
|
||||
const cypressEnv = Cypress.env();
|
||||
|
||||
const fromCypressEnv = {
|
||||
ElasticsearchSettings: {
|
||||
ConnectionURL: cypressEnv.elasticsearchConnectionURL,
|
||||
},
|
||||
LdapSettings: {
|
||||
LdapServer: cypressEnv.ldapServer,
|
||||
LdapPort: cypressEnv.ldapPort,
|
||||
},
|
||||
ServiceSettings: {
|
||||
AllowedUntrustedInternalConnections: cypressEnv.allowedUntrustedInternalConnections,
|
||||
SiteURL: Cypress.config('baseUrl'),
|
||||
},
|
||||
};
|
||||
|
||||
const isCloud = cypressEnv.serverEdition === Constants.ServerEdition.CLOUD;
|
||||
|
||||
if (isCloud) {
|
||||
fromCypressEnv.CloudSettings = {
|
||||
CWSURL: cypressEnv.cwsURL,
|
||||
CWSAPIURL: cypressEnv.cwsAPIURL,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultConfig = isCloud ? cloudDefaultConfig : onPremDefaultConfig;
|
||||
|
||||
return merge(defaultConfig, fromCypressEnv);
|
||||
};
|
||||
|
||||
const expectConfigToBeUpdatable = (currentConfig, newConfig) => {
|
||||
function errorMessage(name) {
|
||||
return `${name} is restricted or not available to update. You may check user/sysadmin access, license requirement, server version or edition (on-prem/cloud) compatibility.`;
|
||||
}
|
||||
|
||||
Object.entries(newConfig).forEach(([newMainKey, newSubSetting]) => {
|
||||
const setting = currentConfig[newMainKey];
|
||||
|
||||
if (setting) {
|
||||
Object.keys(newSubSetting).forEach((newSubKey) => {
|
||||
const isAvailable = setting.hasOwnProperty(newSubKey);
|
||||
const name = `${newMainKey}.${newSubKey}`;
|
||||
expect(isAvailable, isAvailable ? `${name} setting can be updated.` : errorMessage(name)).to.equal(true);
|
||||
});
|
||||
} else {
|
||||
const withSetting = Boolean(setting);
|
||||
expect(withSetting, withSetting ? `${newMainKey} setting can be updated.` : errorMessage(newMainKey)).to.equal(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Cypress.Commands.add('apiUpdateConfig', (newConfig = {}) => {
|
||||
// # Get current config
|
||||
return cy.apiGetConfig().then(({config: currentConfig}) => {
|
||||
// * Check if config can be updated
|
||||
expectConfigToBeUpdatable(currentConfig, newConfig);
|
||||
|
||||
const config = merge.all([currentConfig, getDefaultConfig(), newConfig]);
|
||||
|
||||
// # Set the modified config
|
||||
return cy.request({
|
||||
url: '/api/v4/config',
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'PUT',
|
||||
body: config,
|
||||
}).then((updateResponse) => {
|
||||
expect(updateResponse.status).to.equal(200);
|
||||
return cy.apiGetConfig();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiReloadConfig', () => {
|
||||
// # Reload the config
|
||||
return cy.request({
|
||||
url: '/api/v4/config/reload',
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'POST',
|
||||
}).then((reloadResponse) => {
|
||||
expect(reloadResponse.status).to.equal(200);
|
||||
return cy.apiGetConfig();
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetConfig', (old = false) => {
|
||||
// # Get current settings
|
||||
return cy.request(`/api/v4/config${old ? '/client?format=old' : ''}`).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({config: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiEnsureFeatureFlag', (key, value) => {
|
||||
cy.apiGetConfig().then(({config}) => {
|
||||
cy.log(JSON.stringify(config.PluginSettings.Plugins.playbooks));
|
||||
const currentValue = config.PluginSettings.Plugins.playbooks[key];
|
||||
if (currentValue !== value) {
|
||||
cy.apiUpdateConfig({
|
||||
PluginSettings: {Plugins: {playbooks: {[key]: value}}},
|
||||
}).then(() => {
|
||||
return cy.wrap({prevValue: currentValue, value});
|
||||
});
|
||||
}
|
||||
return cy.wrap({prevValue: currentValue, value});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetAnalytics', () => {
|
||||
cy.apiAdminLogin();
|
||||
|
||||
return cy.request('/api/v4/analytics/old').then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({analytics: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiInvalidateCache', () => {
|
||||
return cy.request({
|
||||
url: '/api/v4/caches/invalidate',
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'POST',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
function isCloudEdition() {
|
||||
return cy.apiGetClientLicense().then(({isCloudLicensed}) => {
|
||||
return cy.wrap(isCloudLicensed);
|
||||
});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('shouldNotRunOnCloudEdition', () => {
|
||||
isCloudEdition().then((isCloud) => {
|
||||
expect(isCloud, isCloud ? 'Should not run on Cloud server' : '').to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
function isTeamEdition() {
|
||||
return cy.apiGetClientLicense().then(({isLicensed}) => {
|
||||
return cy.wrap(!isLicensed);
|
||||
});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('shouldRunOnTeamEdition', () => {
|
||||
isTeamEdition().then((isTeam) => {
|
||||
expect(isTeam, isTeam ? '' : 'Should run on Team edition only').to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
function isElasticsearchEnabled() {
|
||||
return cy.apiGetConfig().then(({config}) => {
|
||||
let isEnabled = false;
|
||||
|
||||
if (config.ElasticsearchSettings) {
|
||||
const {EnableAutocomplete, EnableIndexing, EnableSearching} = config.ElasticsearchSettings;
|
||||
|
||||
isEnabled = EnableAutocomplete && EnableIndexing && EnableSearching;
|
||||
}
|
||||
|
||||
return cy.wrap(isEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('shouldHaveElasticsearchDisabled', () => {
|
||||
isElasticsearchEnabled().then((data) => {
|
||||
expect(data, data ? 'Should have Elasticsearch disabled' : '').to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('shouldHavePluginUploadEnabled', () => {
|
||||
return cy.apiGetConfig().then(({config}) => {
|
||||
const isUploadEnabled = config.PluginSettings.EnableUploads;
|
||||
expect(isUploadEnabled, isUploadEnabled ? '' : 'Should have Plugin upload enabled').to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('shouldHaveClusterEnabled', () => {
|
||||
return cy.apiGetConfig().then(({config}) => {
|
||||
const {Enable, ClusterName} = config.ClusterSettings;
|
||||
expect(Enable, Enable ? '' : 'Should have cluster enabled').to.equal(true);
|
||||
|
||||
const sameClusterName = ClusterName === Cypress.env('serverClusterName');
|
||||
expect(sameClusterName, sameClusterName ? '' : `Should have cluster name set and as expected. Got "${ClusterName}" but expected "${Cypress.env('serverClusterName')}"`).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('shouldRunWithSubpath', () => {
|
||||
return cy.apiGetConfig().then(({config}) => {
|
||||
const isSubpath = Boolean(config.ServiceSettings.SiteURL.replace(/^https?:\/\//, '').split('/')[1]);
|
||||
expect(isSubpath, isSubpath ? '' : 'Should run on server running with subpath only').to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('shouldHaveFeatureFlag', (key, expectedValue) => {
|
||||
return cy.apiGetConfig().then(({config}) => {
|
||||
const actualValue = config.FeatureFlags[key];
|
||||
const message = actualValue === expectedValue ?
|
||||
`Matches feature flag - "${key}: ${expectedValue}"` :
|
||||
`Expected feature flag "${key}" to be "${expectedValue}", but was "${actualValue}"`;
|
||||
expect(actualValue, message).to.equal(expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('shouldHaveEmailEnabled', () => {
|
||||
return cy.apiGetConfig().then(({config}) => {
|
||||
if (!config.ExperimentalSettings.RestrictSystemAdmin) {
|
||||
cy.apiEmailTest();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Upload a license if it does not exist.
|
||||
*/
|
||||
function uploadLicenseIfNotExist() {
|
||||
return cy.apiGetClientLicense().then((data) => {
|
||||
if (data.isLicensed) {
|
||||
return cy.wrap(data);
|
||||
}
|
||||
|
||||
return cy.apiInstallTrialLicense().then(() => {
|
||||
return cy.apiGetClientLicense();
|
||||
});
|
||||
});
|
||||
}
|
||||
185
e2e-tests/cypress/tests/support/api/team.d.ts
поставляемый
Обычный файл
185
e2e-tests/cypress/tests/support/api/team.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Create a team.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams/post
|
||||
* @param {String} name - Unique handler for a team, will be present in the team URL
|
||||
* @param {String} displayName - Non-unique UI name for the team
|
||||
* @param {String} type - 'O' for open (default), 'I' for invite only
|
||||
* @param {Boolean} unique - if true (default), it will create with unique/random team name.
|
||||
* @param {Partial<Team>} options - other fields of team to include
|
||||
* @returns {Team} `out.team` as `Team`
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateTeam('test-team', 'Test Team').then(({team}) => {
|
||||
* // do something with team
|
||||
* });
|
||||
*/
|
||||
apiCreateTeam(name: string, displayName: string, type?: string, unique?: boolean, options?: Partial<Team>): Chainable<{team: Team}>;
|
||||
|
||||
/**
|
||||
* Delete a team.
|
||||
* Soft deletes a team, by marking the team as deleted in the database.
|
||||
* Optionally use the permanent query parameter to hard delete the team.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1{team_id}/delete
|
||||
* @param {String} teamId - The team ID to be deleted
|
||||
* @param {Boolean} permanent - false (default) as soft delete and true as permanent delete
|
||||
* @returns {Object} `out.data` as response status
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeleteTeam('test-id');
|
||||
*/
|
||||
apiDeleteTeam(teamId: string, permanent?: boolean): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Delete the team member object for a user, effectively removing them from a team.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1{team_id}~1members~1{user_id}/delete
|
||||
* @param {String} teamId - The team ID which the user is to be removed from
|
||||
* @param {String} userId - The user ID to be removed from team
|
||||
* @returns {Object} `out.data` as response status
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeleteUserFromTeam('team-id', 'user-id');
|
||||
*/
|
||||
apiDeleteUserFromTeam(teamId: string, userId: string): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Patch a team.
|
||||
* Partially update a team by providing only the fields you want to update.
|
||||
* Omitted fields will not be updated.
|
||||
* The fields that can be updated are defined in the request body, all other provided fields will be ignored.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams/post
|
||||
* @param {String} teamId - The team ID to be patched
|
||||
* @param {String} patch.display_name - Display name
|
||||
* @param {String} patch.description - Description
|
||||
* @param {String} patch.company_name - Company name
|
||||
* @param {String} patch.allowed_domains - Allowed domains
|
||||
* @param {Boolean} patch.allow_open_invite - Allow open invite
|
||||
* @param {Boolean} patch.group_constrained - Group constrained
|
||||
* @returns {Team} `out.team` as `Team`
|
||||
*
|
||||
* @example
|
||||
* cy.apiPatchTeam('test-team', {display_name: 'New Team', group_constrained: true}).then(({team}) => {
|
||||
* // do something with team
|
||||
* });
|
||||
*/
|
||||
apiPatchTeam(teamId: string, patch: Partial<Team>): Chainable<Team>;
|
||||
|
||||
/**
|
||||
* Get a team based on provided name string.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1name~1{name}/get
|
||||
* @param {String} name - Name of a team
|
||||
* @returns {Team} `out.team` as `Team`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetTeamByName('team-name').then(({team}) => {
|
||||
* // do something with team
|
||||
* });
|
||||
*/
|
||||
apiGetTeamByName(name: string): Chainable<Team>;
|
||||
|
||||
/**
|
||||
* Get teams.
|
||||
* For regular users only returns open teams.
|
||||
* Users with the "manage_system" permission will return teams regardless of type.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams/get
|
||||
* @param {String} queryParams.page - Page to select, 0 (default)
|
||||
* @param {String} queryParams.perPage - The number of teams per page, 60 (default)
|
||||
* @returns {Team[]} `out.teams` as `Team[]`
|
||||
* @returns {number} `out.totalCount` as `number`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetAllTeams().then(({teams}) => {
|
||||
* // do something with teams
|
||||
* });
|
||||
*/
|
||||
apiGetAllTeams(queryParams?: Record<string, any>): Chainable<{teams: Team[]}>;
|
||||
|
||||
/**
|
||||
* Get a list of teams that a user is on.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1users~1{user_id}~1teams/get
|
||||
* @param {String} userId - User ID to get teams, or 'me' (default)
|
||||
* @returns {Team[]} `out.teams` as `Team[]`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetTeamsForUser().then(({teams}) => {
|
||||
* // do something with teams
|
||||
* });
|
||||
*/
|
||||
apiGetTeamsForUser(userId: string): Chainable<Team[]>;
|
||||
|
||||
/**
|
||||
* Add user to the team by user_id.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1{team_id}~1members/post
|
||||
* @param {String} teamId - Team ID
|
||||
* @param {String} userId - User ID to be added into a team
|
||||
* @returns {TeamMembership} `out.member` as `TeamMembership`
|
||||
*
|
||||
* @example
|
||||
* cy.apiAddUserToTeam('team-id', 'user-id').then(({member}) => {
|
||||
* // do something with member
|
||||
* });
|
||||
*/
|
||||
apiAddUserToTeam(teamId: string, userId: string): Chainable<TeamMembership>;
|
||||
|
||||
/**
|
||||
* Get team members.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1{team_id}~1members/get
|
||||
* @param {string} teamId - team ID
|
||||
* @returns {TeamMembership[]} `out.members` as `TeamMembership[]`
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetTeamMembers(teamId).then(({members}) => {
|
||||
* // do something with members
|
||||
* });
|
||||
*/
|
||||
apiGetTeamMembers(teamId: string): Chainable<TeamMembership[]>;
|
||||
|
||||
/**
|
||||
* Add a number of users to the team.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1{team_id}~1members~1batch/post
|
||||
* @param {string} teamId - team ID
|
||||
* @param {TeamMembership[]} teamMembers - users to add
|
||||
* @returns {TeamMembership[]} `out.members` as `TeamMembership[]`
|
||||
*
|
||||
* @example
|
||||
* cy.apiAddUsersToTeam(teamId, [{team_id: 'team-id', user_id: 'user-id'}]).then(({members}) => {
|
||||
* // do something with members
|
||||
* });
|
||||
*/
|
||||
apiAddUsersToTeam(teamId: string, teamMembers: TeamMembership[]): Chainable<TeamMembership[]>;
|
||||
|
||||
/**
|
||||
* Update the scheme-derived roles of a team member.
|
||||
* Requires sysadmin session to initiate this command.
|
||||
* See https://api.mattermost.com/#tag/teams/paths/~1teams~1{team_id}~1members~1{user_id}~1schemeRoles/put
|
||||
* @param {string} teamId - team ID
|
||||
* @param {string} userId - user ID
|
||||
* @param {Object} schemeRoles.scheme_admin - false (default) or true to change into team admin
|
||||
* @param {Object} schemeRoles.scheme_user - true (default) or false to change not to be a team user
|
||||
* @returns {Object} `out.data` as response status
|
||||
*
|
||||
* @example
|
||||
* cy.apiUpdateTeamMemberSchemeRole(teamId, userId, {scheme_admin: false, scheme_user: true});
|
||||
*/
|
||||
apiUpdateTeamMemberSchemeRole(teamId: string, userId: string, schemeRoles: Record<string, any>): Chainable<Record<string, any>>;
|
||||
}
|
||||
}
|
||||
163
e2e-tests/cypress/tests/support/api/team.js
Обычный файл
163
e2e-tests/cypress/tests/support/api/team.js
Обычный файл
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {getRandomId} from '../../utils';
|
||||
|
||||
// *****************************************************************************
|
||||
// Teams
|
||||
// https://api.mattermost.com/#tag/teams
|
||||
// *****************************************************************************
|
||||
|
||||
export function createTeamPatch(name = 'team', displayName = 'Team', type = 'O', unique = true) {
|
||||
const randomSuffix = getRandomId();
|
||||
|
||||
return {
|
||||
name: unique ? `${name}-${randomSuffix}` : name,
|
||||
display_name: unique ? `${displayName} ${randomSuffix}` : displayName,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiCreateTeam', (name, displayName, type, unique, options) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/teams',
|
||||
method: 'POST',
|
||||
body: {
|
||||
...createTeamPatch(name, displayName, type, unique),
|
||||
...options,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({team: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeleteTeam', (teamId, permanent = false) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/teams/' + teamId + (permanent ? '?permanent=true' : ''),
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({data: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeleteUserFromTeam', (teamId, userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/teams/' + teamId + '/members/' + userId,
|
||||
method: 'DELETE',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({data: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiPatchTeam', (teamId, teamData) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/teams/${teamId}/patch`,
|
||||
method: 'PUT',
|
||||
body: teamData,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({team: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetTeamByName', (name) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/teams/name/' + name,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({team: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetAllTeams', ({page = 0, perPage = 60} = {}) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `api/v4/teams?page=${page}&per_page=${perPage}`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({teams: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetTeamsForUser', (userId = 'me') => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `api/v4/users/${userId}/teams`,
|
||||
method: 'GET',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({teams: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiAddUserToTeam', (teamId, userId) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v4/teams/${teamId}/members`,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
body: {team_id: teamId, user_id: userId},
|
||||
qs: {team_id: teamId},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({member: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiAddUsersToTeam', (teamId, teamMembers) => {
|
||||
return cy.request({
|
||||
method: 'POST',
|
||||
url: `/api/v4/teams/${teamId}/members/batch`,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
body: teamMembers,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(201);
|
||||
return cy.wrap({members: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetTeamMembers', (teamId) => {
|
||||
return cy.request({
|
||||
method: 'GET',
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/teams/${teamId}/members`,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({members: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUpdateTeamMemberSchemeRole', (teamId, userId, schemeRoles = {}) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/teams/${teamId}/members/${userId}/schemeRoles`,
|
||||
method: 'PUT',
|
||||
body: schemeRoles,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({data: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiSetTeamScheme', (teamId, schemeId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/teams/${teamId}/scheme`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
scheme_id: schemeId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({data: response.body});
|
||||
});
|
||||
});
|
||||
378
e2e-tests/cypress/tests/support/api/user.d.ts
поставляемый
Обычный файл
378
e2e-tests/cypress/tests/support/api/user.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,378 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Specific link to https://api.mattermost.com
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Login to server via API.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1login/post
|
||||
* @param {string} user.username - username of a user
|
||||
* @param {string} user.password - password of user
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiLogin({username: 'sysadmin', password: 'secret'});
|
||||
*/
|
||||
apiLogin(user: UserProfile): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Login to server via API.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1login/post
|
||||
* @param {string} user.username - username of a user
|
||||
* @param {string} user.password - password of user
|
||||
* @param {string} token - MFA token for the session
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiLoginWithMFA({username: 'sysadmin', password: 'secret', token: '123456'});
|
||||
*/
|
||||
apiLoginWithMFA(user: UserProfile, token: string): Chainable<{user: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Login as admin via API.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1login/post
|
||||
* @param {Object} requestOptions - cypress' request options object, see https://docs.cypress.io/api/commands/request#Arguments
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiAdminLogin();
|
||||
*/
|
||||
apiAdminLogin(requestOptions?: Record<string, any>): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Login as admin via API.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1login/post
|
||||
* @param {string} token - MFA token for the session
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiAdminLoginWithMFA(token);
|
||||
*/
|
||||
apiAdminLoginWithMFA(token: string): Chainable<{user: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Logout a user's active session from server via API.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1logout/post
|
||||
* Clears all cookies especially `MMAUTHTOKEN`, `MMUSERID` and `MMCSRF`.
|
||||
*
|
||||
* @example
|
||||
* cy.apiLogout();
|
||||
*/
|
||||
apiLogout();
|
||||
|
||||
/**
|
||||
* Get current user.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}/get
|
||||
* @returns {user: UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetMe().then(({user}) => {
|
||||
* // do something with user
|
||||
* });
|
||||
*/
|
||||
apiGetMe(): Chainable<{user: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Get a user by ID.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}/get
|
||||
* @param {String} userId - ID of a user to get profile
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetUserById('user-id').then(({user}) => {
|
||||
* // do something with user
|
||||
* });
|
||||
*/
|
||||
apiGetUserById(userId: string): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Get a user by email.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1email~1{email}/get
|
||||
* @param {String} email - email address of a user to get profile
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetUserByEmail('email').then(({user}) => {
|
||||
* // do something with user
|
||||
* });
|
||||
*/
|
||||
apiGetUserByEmail(email: string): Chainable<{user: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Get users by usernames.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1usernames/post
|
||||
* @param {String[]} usernames - list of usernames to get profiles
|
||||
* @returns {UserProfile[]} out.users: list of `UserProfile` objects
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetUsersByUsernames().then(({users}) => {
|
||||
* // do something with users
|
||||
* });
|
||||
*/
|
||||
apiGetUsersByUsernames(usernames: string[]): Chainable<UserProfile[]>;
|
||||
|
||||
/**
|
||||
* Patch a user.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1patch/put
|
||||
* @param {String} userId - ID of user to patch
|
||||
* @param {UserProfile} userData - user profile to be updated
|
||||
* @param {string} userData.email
|
||||
* @param {string} userData.username
|
||||
* @param {string} userData.first_name
|
||||
* @param {string} userData.last_name
|
||||
* @param {string} userData.nickname
|
||||
* @param {string} userData.locale
|
||||
* @param {Object} userData.timezone
|
||||
* @param {string} userData.position
|
||||
* @param {Object} userData.props
|
||||
* @param {Object} userData.notify_props
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiPatchUser('user-id', {locale: 'en'}).then(({user}) => {
|
||||
* // do something with user
|
||||
* });
|
||||
*/
|
||||
apiPatchUser(userId: string, userData: UserProfile): Chainable<{user: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Convenient command to patch a current user.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1patch/put
|
||||
* @param {UserProfile} userData - user profile to be updated
|
||||
* @param {string} userData.email
|
||||
* @param {string} userData.username
|
||||
* @param {string} userData.first_name
|
||||
* @param {string} userData.last_name
|
||||
* @param {string} userData.nickname
|
||||
* @param {string} userData.locale
|
||||
* @param {Object} userData.timezone
|
||||
* @param {string} userData.position
|
||||
* @param {Object} userData.props
|
||||
* @param {Object} userData.notify_props
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiPatchMe({locale: 'en'}).then(({user}) => {
|
||||
* // do something with user
|
||||
* });
|
||||
*/
|
||||
apiPatchMe(userData: UserProfile): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Create an admin account based from the env variables defined in Cypress env.
|
||||
* @param {string} options.namePrefix - 'user' (default) or any prefix to easily identify a user
|
||||
* @param {boolean} options.bypassTutorial - true (default) or false for user to go thru tutorial steps
|
||||
* @param {boolean} options.showOnboarding - false (default) to hide or true to show Onboarding steps
|
||||
* @returns {UserProfile} `out.sysadmin` as `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateAdmin(options);
|
||||
*/
|
||||
apiCreateAdmin(options: Record<string, any>): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Create a randomly named admin account
|
||||
*
|
||||
* @param {boolean} options.loginAfter - false (default) or true if wants to login as the new admin.
|
||||
* @param {boolean} options.hideAdminTrialModal - true (default) or false if wants to hide Start Enterprise Trial modal.
|
||||
*
|
||||
* @returns {UserProfile} `out.sysadmin` as `UserProfile` object
|
||||
*/
|
||||
apiCreateCustomAdmin(options: {loginAfter: boolean; hideAdminTrialModal?: boolean}): Chainable<{sysadmin: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Create a new user with an options to set name prefix and be able to bypass tutorial steps.
|
||||
* @param {string} options.user - predefined `user` object instead on random user
|
||||
* @param {string} options.prefix - 'user' (default) or any prefix to easily identify a user
|
||||
* @param {boolean} options.bypassTutorial - true (default) or false for user to go thru tutorial steps
|
||||
* @param {boolean} options.showOnboarding - false (default) to hide or true to show Onboarding steps
|
||||
* @returns {UserProfile} `out.user` as `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateUser(options);
|
||||
*/
|
||||
apiCreateUser(options?: {
|
||||
user?: Partial<UserProfile>;
|
||||
prefix?: string;
|
||||
bypassTutorial?: boolean;
|
||||
showOnboarding?: boolean;
|
||||
}): Chainable<{user: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Create a new guest user with an options to set name prefix and be able to bypass tutorial steps.
|
||||
* @param {string} options.prefix - 'guest' (default) or any prefix to easily identify a guest
|
||||
* @param {boolean} options.bypassTutorial - true (default) or false for guest to go thru tutorial steps
|
||||
* @param {boolean} options.showOnboarding - false (default) to hide or true to show Onboarding steps
|
||||
* @returns {UserProfile} `out.guest` as `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiCreateGuestUser(options);
|
||||
*/
|
||||
apiCreateGuestUser(options: Record<string, any>): Chainable<{guest: UserProfile}>;
|
||||
|
||||
/**
|
||||
* Revoke all active sessions for a user.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1sessions~1revoke~1all/post
|
||||
* @param {String} userId - ID of a user
|
||||
* @returns {Object} `out.data` as response status
|
||||
*
|
||||
* @example
|
||||
* cy.apiRevokeUserSessions('user-id');
|
||||
*/
|
||||
apiRevokeUserSessions(userId: string): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Get list of users based on query parameters
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users/get
|
||||
* @param {String} queryParams - see link on available query parameters
|
||||
* @returns {UserProfile[]} `out.users` as `UserProfile[]` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetUsers().then(({users}) => {
|
||||
* // do something with users
|
||||
* });
|
||||
*/
|
||||
apiGetUsers(queryParams: Record<string, any>): Chainable<UserProfile[]>;
|
||||
|
||||
/**
|
||||
* Get list of users that are not team members.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users/get
|
||||
* @param {String} queryParams.teamId - Team ID
|
||||
* @param {String} queryParams.page - Page to select, 0 (default)
|
||||
* @param {String} queryParams.perPage - The number of users per page, 60 (default)
|
||||
* @returns {UserProfile[]} `out.users` as `UserProfile[]` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetUsersNotInTeam({teamId: 'team-id'}).then(({users}) => {
|
||||
* // do something with users
|
||||
* });
|
||||
*/
|
||||
apiGetUsersNotInTeam(queryParams: Record<string, any>): Chainable<UserProfile[]>;
|
||||
|
||||
/**
|
||||
* Reactivate a user account.
|
||||
* @param {string} userId - User ID
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiActivateUser('user-id');
|
||||
*/
|
||||
apiActivateUser(userId: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Deactivate a user account.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}/delete
|
||||
* @param {string} userId - User ID
|
||||
* @returns {Response} response: Cypress-chainable response which should have successful HTTP status of 200 OK to continue or pass.
|
||||
*
|
||||
* @example
|
||||
* cy.apiDeactivateUser('user-id');
|
||||
*/
|
||||
apiDeactivateUser(userId: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Convert a regular user into a guest. This will convert the user into a guest for the whole system while retaining their existing team and channel memberships.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1demote/post
|
||||
* @param {string} userId - User ID
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiDemoteUserToGuest('user-id');
|
||||
*/
|
||||
apiDemoteUserToGuest(userId: string): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Convert a guest into a regular user. This will convert the guest into a user for the whole system while retaining any team and channel memberships and automatically joining them to the default channels.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1promote/post
|
||||
* @param {string} userId - User ID
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiPromoteGuestToUser('user-id');
|
||||
*/
|
||||
apiPromoteGuestToUser(userId: string): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Verifies a user's email via userId without having to go to the user's email inbox.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1email~1verify~1member/post
|
||||
* @param {string} userId - User ID
|
||||
* @returns {UserProfile} out.user: `UserProfile` object
|
||||
*
|
||||
* @example
|
||||
* cy.apiVerifyUserEmailById('user-id').then(({user}) => {
|
||||
* // do something with user
|
||||
* });
|
||||
*/
|
||||
apiVerifyUserEmailById(userId: string): Chainable<UserProfile>;
|
||||
|
||||
/**
|
||||
* Update a user MFA.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1mfa/put
|
||||
* @param {String} userId - ID of user to patch
|
||||
* @param {boolean} activate - Whether MFA is going to be enabled or disabled
|
||||
* @param {string} token - MFA token/code
|
||||
* @example
|
||||
* cy.apiActivateUserMFA('user-id', activate: false);
|
||||
*/
|
||||
apiActivateUserMFA(userId: string, activate: boolean, token: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Create a user access token
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1tokens/post
|
||||
* @param {String} userId - ID of user for whom to generate token
|
||||
* @param {String} description - The description of the token usage
|
||||
* @example
|
||||
* cy.apiAccessToken('user-id', 'token for cypress tests');
|
||||
*/
|
||||
apiAccessToken(userId: string, description: string): Chainable<UserAccessToken>;
|
||||
|
||||
/**
|
||||
* Revoke a user access token
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1tokens~1revoke/post
|
||||
* @param {String} tokenId - The id of the token to revoke
|
||||
* @example
|
||||
* cy.apiRevokeAccessToken('token-id')
|
||||
*/
|
||||
apiRevokeAccessToken(tokenId: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Update a user auth method.
|
||||
* See https://api.mattermost.com/#tag/users/paths/~1users~1{user_id}~1mfa/put
|
||||
* @param {String} userId - ID of user to patch
|
||||
* @param {String} authData
|
||||
* @param {String} password
|
||||
* @param {String} authService
|
||||
* @example
|
||||
* cy.apiUpdateUserAuth('user-id', 'auth-data', 'password', 'auth-service');
|
||||
*/
|
||||
apiUpdateUserAuth(userId: string, authData: string, password: string, authService: string): Chainable<Response>;
|
||||
|
||||
/**
|
||||
* Get total count of users in the system
|
||||
* See https://api.mattermost.com/#operation/GetTotalUsersStats
|
||||
*
|
||||
* @returns {number} - total count of all users
|
||||
*
|
||||
* @example
|
||||
* cy.apiGetTotalUsers().then(() => {
|
||||
* // do something with total users
|
||||
* });
|
||||
*/
|
||||
apiGetTotalUsers(): Chainable<number>;
|
||||
}
|
||||
}
|
||||
506
e2e-tests/cypress/tests/support/api/user.js
Обычный файл
506
e2e-tests/cypress/tests/support/api/user.js
Обычный файл
@@ -0,0 +1,506 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import authenticator from 'authenticator';
|
||||
|
||||
import {getRandomId} from '../../utils';
|
||||
import {getAdminAccount} from '../env';
|
||||
|
||||
import {buildQueryString} from './helpers';
|
||||
|
||||
// *****************************************************************************
|
||||
// Users
|
||||
// https://api.mattermost.com/#tag/users
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiLogin', (user, requestOptions = {}) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/login',
|
||||
method: 'POST',
|
||||
body: {login_id: user.username || user.email, password: user.password},
|
||||
...requestOptions,
|
||||
}).then((response) => {
|
||||
if (requestOptions.failOnStatusCode) {
|
||||
expect(response.status).to.equal(200);
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
return cy.wrap({
|
||||
user: {
|
||||
...response.body,
|
||||
password: user.password,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return cy.wrap({error: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiLoginWithMFA', (user, token) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/login',
|
||||
method: 'POST',
|
||||
body: {login_id: user.username, password: user.password, token},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({
|
||||
user: {
|
||||
...response.body,
|
||||
password: user.password,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiAdminLogin', (requestOptions = {}) => {
|
||||
const admin = getAdminAccount();
|
||||
|
||||
// First, login with username
|
||||
cy.apiLogin(admin, requestOptions).then((resp) => {
|
||||
if (resp.error) {
|
||||
if (resp.error.id === 'mfa.validate_token.authenticate.app_error') {
|
||||
// On fail, try to login via MFA
|
||||
return cy.dbGetUser({username: admin.username}).then(({user: {mfasecret}}) => {
|
||||
const token = authenticator.generateToken(mfasecret);
|
||||
return cy.apiLoginWithMFA(admin, token);
|
||||
});
|
||||
}
|
||||
|
||||
// Or, try to login via email
|
||||
delete admin.username;
|
||||
return cy.apiLogin(admin, requestOptions);
|
||||
}
|
||||
|
||||
return resp;
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiAdminLoginWithMFA', (token) => {
|
||||
const admin = getAdminAccount();
|
||||
|
||||
return cy.apiLoginWithMFA(admin, token);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiLogout', () => {
|
||||
cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/logout',
|
||||
method: 'POST',
|
||||
log: false,
|
||||
});
|
||||
|
||||
// * Verify logged out
|
||||
cy.visit('/login?extra=expired').url().should('include', '/login');
|
||||
|
||||
// # Ensure we clear out these specific cookies
|
||||
['MMAUTHTOKEN', 'MMUSERID', 'MMCSRF'].forEach((cookie) => {
|
||||
cy.clearCookie(cookie);
|
||||
});
|
||||
|
||||
// # Clear remainder of cookies
|
||||
cy.clearCookies();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetMe', () => {
|
||||
return cy.apiGetUserById('me');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetUserById', (userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/' + userId,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({user: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetUserByEmail', (email, failOnStatusCode = true) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/email/' + email,
|
||||
failOnStatusCode,
|
||||
}).then((response) => {
|
||||
const {body, status} = response;
|
||||
|
||||
if (failOnStatusCode) {
|
||||
expect(status).to.equal(200);
|
||||
return cy.wrap({user: body});
|
||||
}
|
||||
return cy.wrap({user: status === 200 ? body : null});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetUsersByUsernames', (usernames = []) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/usernames',
|
||||
method: 'POST',
|
||||
body: usernames,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({users: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiPatchUser', (userId, userData) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'PUT',
|
||||
url: `/api/v4/users/${userId}/patch`,
|
||||
body: userData,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({user: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiPatchMe', (data) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/me/patch',
|
||||
method: 'PUT',
|
||||
body: data,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({user: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateCustomAdmin', ({loginAfter = false, hideAdminTrialModal = true} = {}) => {
|
||||
const sysadminUser = generateRandomUser('other-admin');
|
||||
|
||||
return cy.apiCreateUser({user: sysadminUser}).then(({user}) => {
|
||||
return cy.apiPatchUserRoles(user.id, ['system_admin', 'system_user']).then(() => {
|
||||
const data = {sysadmin: user};
|
||||
|
||||
cy.apiSaveStartTrialModal(user.id, hideAdminTrialModal.toString());
|
||||
|
||||
if (loginAfter) {
|
||||
return cy.apiLogin(user).then(() => {
|
||||
return cy.wrap(data);
|
||||
});
|
||||
}
|
||||
|
||||
return cy.wrap(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateAdmin', () => {
|
||||
const {username, password} = getAdminAccount();
|
||||
|
||||
const sysadminUser = {
|
||||
username,
|
||||
password,
|
||||
first_name: 'Kenneth',
|
||||
last_name: 'Moreno',
|
||||
email: 'sysadmin@sample.mattermost.com',
|
||||
};
|
||||
|
||||
const options = {
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'POST',
|
||||
url: '/api/v4/users',
|
||||
body: sysadminUser,
|
||||
};
|
||||
|
||||
// # Create a new user
|
||||
return cy.request(options).then((res) => {
|
||||
expect(res.status).to.equal(201);
|
||||
|
||||
return cy.wrap({sysadmin: {...res.body, password}});
|
||||
});
|
||||
});
|
||||
|
||||
function generateRandomUser(prefix = 'user') {
|
||||
const randomId = getRandomId();
|
||||
|
||||
return {
|
||||
email: `${prefix}${randomId}@sample.mattermost.com`,
|
||||
username: `${prefix}${randomId}`,
|
||||
password: 'passwd',
|
||||
first_name: `First${randomId}`,
|
||||
last_name: `Last${randomId}`,
|
||||
nickname: `Nickname${randomId}`,
|
||||
};
|
||||
}
|
||||
|
||||
Cypress.Commands.add('apiCreateUser', ({
|
||||
prefix = 'user',
|
||||
bypassTutorial = true,
|
||||
hideActionsMenu = true,
|
||||
hideOnboarding = true,
|
||||
bypassWhatsNewModal = true,
|
||||
user = null,
|
||||
} = {}) => {
|
||||
const newUser = user || generateRandomUser(prefix);
|
||||
|
||||
const createUserOption = {
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'POST',
|
||||
url: '/api/v4/users',
|
||||
body: newUser,
|
||||
};
|
||||
|
||||
return cy.request(createUserOption).then((userRes) => {
|
||||
expect(userRes.status).to.equal(201);
|
||||
|
||||
const createdUser = userRes.body;
|
||||
|
||||
// hide the onboarding task list by default so it doesn't block the execution of subsequent tests
|
||||
cy.apiSaveSkipStepsPreference(createdUser.id, 'true');
|
||||
cy.apiSaveOnboardingTaskListPreference(createdUser.id, 'onboarding_task_list_open', 'false');
|
||||
cy.apiSaveOnboardingTaskListPreference(createdUser.id, 'onboarding_task_list_show', 'false');
|
||||
|
||||
// hide drafts tour tip so it doesn't block the execution of subsequent tests
|
||||
cy.apiSaveDraftsTourTipPreference(createdUser.id, true);
|
||||
|
||||
if (bypassTutorial) {
|
||||
cy.apiSaveTutorialStep(createdUser.id, '999');
|
||||
}
|
||||
|
||||
if (hideActionsMenu) {
|
||||
cy.apiSaveActionsMenuPreference(createdUser.id, true);
|
||||
}
|
||||
|
||||
if (hideOnboarding) {
|
||||
cy.apiSaveOnboardingPreference(createdUser.id, 'hide', 'true');
|
||||
cy.apiSaveOnboardingPreference(createdUser.id, 'skip', 'true');
|
||||
}
|
||||
|
||||
if (bypassWhatsNewModal) {
|
||||
cy.apiHideSidebarWhatsNewModalPreference(createdUser.id, 'false');
|
||||
}
|
||||
|
||||
return cy.wrap({user: {...createdUser, password: newUser.password}});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiCreateGuestUser', ({
|
||||
prefix = 'guest',
|
||||
bypassTutorial = true,
|
||||
} = {}) => {
|
||||
return cy.apiCreateUser({prefix, bypassTutorial}).then(({user}) => {
|
||||
cy.apiDemoteUserToGuest(user.id);
|
||||
|
||||
return cy.wrap({guest: user});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Revoke all active sessions for a user
|
||||
* @param {String} userId - ID of user to revoke sessions
|
||||
*/
|
||||
Cypress.Commands.add('apiRevokeUserSessions', (userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/sessions/revoke/all`,
|
||||
method: 'POST',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({data: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetUsers', (queryParams = {}) => {
|
||||
const queryString = buildQueryString(queryParams);
|
||||
|
||||
return cy.request({
|
||||
method: 'GET',
|
||||
url: `/api/v4/users?${queryString}`,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({users: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetUsersNotInTeam', ({teamId, page = 0, perPage = 60} = {}) => {
|
||||
return cy.apiGetUsers({not_in_team: teamId, page, per_page: perPage});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiPatchUserRoles', (userId, roleNames = ['system_user']) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/roles`,
|
||||
method: 'PUT',
|
||||
body: {roles: roleNames.join(' ')},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({user: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDeactivateUser', (userId) => {
|
||||
const options = {
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'DELETE',
|
||||
url: `/api/v4/users/${userId}`,
|
||||
};
|
||||
|
||||
// # Deactivate a user account
|
||||
return cy.request(options).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiActivateUser', (userId) => {
|
||||
const options = {
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'PUT',
|
||||
url: `/api/v4/users/${userId}/active`,
|
||||
body: {
|
||||
active: true,
|
||||
},
|
||||
};
|
||||
|
||||
// # Activate a user account
|
||||
return cy.request(options).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiDemoteUserToGuest', (userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/demote`,
|
||||
method: 'POST',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.apiGetUserById(userId).then(({user}) => {
|
||||
return cy.wrap({guest: user});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiPromoteGuestToUser', (userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/promote`,
|
||||
method: 'POST',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.apiGetUserById(userId);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify a user email via API
|
||||
* @param {String} userId - ID of user of email to verify
|
||||
*/
|
||||
Cypress.Commands.add('apiVerifyUserEmailById', (userId) => {
|
||||
const options = {
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'POST',
|
||||
url: `/api/v4/users/${userId}/email/verify/member`,
|
||||
};
|
||||
|
||||
return cy.request(options).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({user: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiActivateUserMFA', (userId, activate, token) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: `/api/v4/users/${userId}/mfa`,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
activate,
|
||||
code: token,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiResetPassword', (userId, currentPass, newPass) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'PUT',
|
||||
url: `/api/v4/users/${userId}/password`,
|
||||
body: {
|
||||
current_password: currentPass,
|
||||
new_password: newPass,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({user: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGenerateMfaSecret', (userId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'POST',
|
||||
url: `/api/v4/users/${userId}/mfa/generate`,
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap({code: response.body});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiAccessToken', (userId, description) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/' + userId + '/tokens',
|
||||
method: 'POST',
|
||||
body: {
|
||||
description,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiRevokeAccessToken', (tokenId) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
url: '/api/v4/users/tokens/revoke',
|
||||
method: 'POST',
|
||||
body: {
|
||||
token_id: tokenId,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiUpdateUserAuth', (userId, authData, password, authService) => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'PUT',
|
||||
url: `/api/v4/users/${userId}/auth`,
|
||||
body: {
|
||||
auth_data: authData,
|
||||
password,
|
||||
auth_service: authService,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response);
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetTotalUsers', () => {
|
||||
return cy.request({
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'GET',
|
||||
url: '/api/v4/users/stats',
|
||||
}).then((response) => {
|
||||
expect(response.status).to.equal(200);
|
||||
return cy.wrap(response.body.total_users_count);
|
||||
});
|
||||
});
|
||||
|
||||
export {generateRandomUser};
|
||||
40
e2e-tests/cypress/tests/support/api/webhooks.d.ts
поставляемый
Обычный файл
40
e2e-tests/cypress/tests/support/api/webhooks.d.ts
поставляемый
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***************************************************************
|
||||
// Each command should be properly documented using JSDoc.
|
||||
// See https://jsdoc.app/index.html for reference.
|
||||
// Basic requirements for documentation are the following:
|
||||
// - Meaningful description
|
||||
// - Each parameter with `@params`
|
||||
// - Return value with `@returns`
|
||||
// - Example usage with `@example`
|
||||
// Custom command should follow naming convention of having `api` prefix, e.g. `apiLogin`.
|
||||
// ***************************************************************
|
||||
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* Get an incoming webhook given the hook id.
|
||||
* @param {string} hookId - Incoming Webhook GUID
|
||||
* @returns {IncomingWebhook} `out.webhook` as `IncomingWebhook`
|
||||
* @returns {string} `out.status`
|
||||
* @example
|
||||
* cy.apiGetIncomingWebhook('hook-id')
|
||||
*/
|
||||
apiGetIncomingWebhook(hookId: string): Chainable<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Get an outgoing webhook given the hook id.
|
||||
* @param {string} hookId - Outgoing Webhook GUID
|
||||
* @returns {OutgoingWebhook} `out.webhook` as `OutgoingWebhook`
|
||||
* @returns {string} `out.status`
|
||||
* @example
|
||||
* cy.apiGetOutgoingWebhook('hook-id')
|
||||
*/
|
||||
apiGetOutgoingWebhook(hookId: string): Chainable<Record<string, any>>;
|
||||
}
|
||||
}
|
||||
35
e2e-tests/cypress/tests/support/api/webhooks.js
Обычный файл
35
e2e-tests/cypress/tests/support/api/webhooks.js
Обычный файл
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// *****************************************************************************
|
||||
// Webhooks
|
||||
// https://api.mattermost.com/#tag/webhooks
|
||||
// *****************************************************************************
|
||||
|
||||
Cypress.Commands.add('apiGetIncomingWebhook', (hookId) => {
|
||||
const options = {
|
||||
url: `api/v4/hooks/incoming/${hookId}`,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'GET',
|
||||
failOnStatusCode: false,
|
||||
};
|
||||
|
||||
return cy.request(options).then((response) => {
|
||||
const {body, status} = response;
|
||||
return cy.wrap({webhook: body, status});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiGetOutgoingWebhook', (hookId) => {
|
||||
const options = {
|
||||
url: `api/v4/hooks/outgoing/${hookId}`,
|
||||
headers: {'X-Requested-With': 'XMLHttpRequest'},
|
||||
method: 'GET',
|
||||
failOnStatusCode: false,
|
||||
};
|
||||
|
||||
return cy.request(options).then((response) => {
|
||||
const {body, status} = response;
|
||||
return cy.wrap({webhook: body, status});
|
||||
});
|
||||
});
|
||||
Ссылка в новой задаче
Block a user