snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
317
e2e-tests/cypress/tests/support/task_commands.ts
Normal file
317
e2e-tests/cypress/tests/support/task_commands.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {type AxiosResponse} from 'axios';
|
||||
|
||||
import type {ChainableT} from '../types';
|
||||
|
||||
/**
|
||||
* postMessageAs is a task which is wrapped as command with post-verification
|
||||
* that a message is successfully posted by the user/sender
|
||||
* @param {Object} sender - a user object who will post a message
|
||||
* @param {String} message - message in a post
|
||||
* @param {Object} channelId - where a post will be posted
|
||||
*/
|
||||
|
||||
export interface PostMessageResp {
|
||||
id: string;
|
||||
status: number;
|
||||
// Post data from the API response
|
||||
data: Record<string, any>;
|
||||
}
|
||||
|
||||
interface PostMessageArg {
|
||||
sender: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
message: string;
|
||||
channelId: string;
|
||||
rootId?: string;
|
||||
createAt?: number;
|
||||
}
|
||||
|
||||
interface PostMessageRequest {
|
||||
token: string;
|
||||
message: string;
|
||||
props?: Record<string, unknown>;
|
||||
channelId: string;
|
||||
rootId?: string;
|
||||
createAt?: number;
|
||||
failOnStatus?: boolean;
|
||||
}
|
||||
|
||||
function postMessageAs(arg: PostMessageArg): ChainableT<PostMessageResp> {
|
||||
const {sender, message, channelId, rootId, createAt} = arg;
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
|
||||
// cy.task() returns untyped data
|
||||
return cy.task('postMessageAs', {sender, message, channelId, rootId, createAt, baseUrl}).then((response: any) => {
|
||||
const {status, data} = response;
|
||||
expect(status).to.equal(201);
|
||||
|
||||
// # Return the data so it can be interacted in a test
|
||||
return cy.wrap({id: data.id, status, data});
|
||||
});
|
||||
}
|
||||
Cypress.Commands.add('postMessageAs', postMessageAs);
|
||||
|
||||
/**
|
||||
* @param {string} [numberOfMessages = 30] - Number of messages
|
||||
* @param {Object} sender - a user object who will post a message
|
||||
* @param {String} message - message in a post
|
||||
* @param {Object} channelId - where a post will be posted
|
||||
*/
|
||||
|
||||
function postListOfMessages({numberOfMessages = 30, ...rest}): ChainableT<unknown> {
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
|
||||
// Cast needed: Cypress.Commands.add requires cast for overloaded commands
|
||||
return (cy as any).
|
||||
task('postListOfMessages', {numberOfMessages, baseUrl, ...rest}, {timeout: numberOfMessages * 200}).
|
||||
each((message: {status: number}) => expect(message.status).to.equal(201));
|
||||
}
|
||||
|
||||
Cypress.Commands.add('postListOfMessages', postListOfMessages);
|
||||
|
||||
/**
|
||||
* reactToMessageAs is a task wrapped as command with post-verification
|
||||
* that a reaction is added successfully to a message by a user/sender
|
||||
* @param {Object} sender - a user object who will post a message
|
||||
* @param {String} postId - post on which reaction is intended
|
||||
* @param {String} reaction - emoji text eg. smile
|
||||
*/
|
||||
Cypress.Commands.add('reactToMessageAs', ({sender, postId, reaction}) => {
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
|
||||
// cy.task() returns untyped data
|
||||
return cy.task('reactToMessageAs', {sender, postId, reaction, baseUrl}).then(({status, data}: any) => {
|
||||
expect(status).to.equal(200);
|
||||
|
||||
// # Return the response after reaction is added
|
||||
return cy.wrap({status, data});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* postIncomingWebhook is a task which is wrapped as command with post-verification
|
||||
* that the incoming webhook is successfully posted
|
||||
* @param {String} url - incoming webhook URL
|
||||
* @param {Object} data - payload on incoming webhook
|
||||
*/
|
||||
|
||||
interface WebhookData {
|
||||
text?: string;
|
||||
attachments?: Array<Record<string, unknown> & {pretext?: string}>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
function postIncomingWebhook({url, data, waitFor}: {
|
||||
url: string;
|
||||
data: WebhookData;
|
||||
waitFor?: string;
|
||||
}): ChainableT {
|
||||
cy.task('postIncomingWebhook', {url, data}).its('status').should('be.equal', 200);
|
||||
|
||||
if (!waitFor) {
|
||||
return cy.wrap(undefined) as ChainableT;
|
||||
}
|
||||
|
||||
return cy.waitUntil(() => cy.getLastPost().then((el) => {
|
||||
switch (waitFor) {
|
||||
case 'text': {
|
||||
const textEl = el.find('.post-message__text > p')[0];
|
||||
return Boolean(textEl && data.text && textEl.textContent.includes(data.text));
|
||||
}
|
||||
case 'attachment-pretext': {
|
||||
const pretext = data.attachments?.[0]?.pretext;
|
||||
if (!pretext) {
|
||||
return false;
|
||||
}
|
||||
const mmBlocksText = el.find('.mm-blocks-text');
|
||||
if (mmBlocksText.length) {
|
||||
return Array.from(mmBlocksText).some((node) => node.textContent?.includes(pretext));
|
||||
}
|
||||
const attachmentPretextEl = el.find('.attachment__thumb-pretext > p')[0];
|
||||
return Boolean(attachmentPretextEl && attachmentPretextEl.textContent.includes(pretext));
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Cypress.Commands.add('postIncomingWebhook', postIncomingWebhook);
|
||||
|
||||
interface ExternalRequestArg<T> {
|
||||
user: Record<string, unknown>;
|
||||
method: string;
|
||||
path: string;
|
||||
data?: T;
|
||||
failOnStatusCode?: boolean;
|
||||
}
|
||||
function externalRequest<T=unknown, U=unknown>(arg: ExternalRequestArg<U>): ChainableT<Pick<AxiosResponse<T>, 'data' | 'status'>> {
|
||||
const {user, method, path, data, failOnStatusCode = true} = arg;
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
|
||||
// cy.task() returns untyped data
|
||||
return cy.task('externalRequest', {baseUrl, user, method, path, data}).then((response: any) => {
|
||||
// Temporarily ignore error related to Cloud
|
||||
const cloudErrorId = [
|
||||
'ent.cloud.request_error',
|
||||
'api.cloud.get_subscription.error',
|
||||
];
|
||||
|
||||
if (response.data && !cloudErrorId.includes(response.data.id) && failOnStatusCode) {
|
||||
expect(response.status).to.be.oneOf([200, 201, 204]);
|
||||
}
|
||||
|
||||
return cy.wrap(response as Pick<AxiosResponse<T>, 'data' | 'status'>);
|
||||
}) as ChainableT<Pick<AxiosResponse<T>, 'data' | 'status'>>;
|
||||
}
|
||||
// Cypress.Commands.add requires cast for overloaded commands
|
||||
Cypress.Commands.add('externalRequest', externalRequest as any);
|
||||
|
||||
/**
|
||||
* postMessageAs is a task which is wrapped as command with post-verification
|
||||
* that a message is successfully posted by the bot
|
||||
* @param {String} message - message in a post
|
||||
* @param {Object} channelId - where a post will be posted
|
||||
*/
|
||||
|
||||
function postBotMessage({token, message, props, channelId, rootId, createAt, failOnStatus = true}: PostMessageRequest): ChainableT<PostMessageResp> {
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
|
||||
// cy.task() returns untyped data
|
||||
return cy.task('postBotMessage', {token, message, props, channelId, rootId, createAt, baseUrl}).then(({status, data}: any) => {
|
||||
if (failOnStatus) {
|
||||
expect(status).to.equal(201);
|
||||
}
|
||||
|
||||
// # Return the data so it can be interacted in a test
|
||||
return cy.wrap({id: data.id, status, data});
|
||||
});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('postBotMessage', postBotMessage);
|
||||
|
||||
/**
|
||||
* urlHealthCheck is a task wrapped as command that checks whether
|
||||
* a URL is healthy and reachable.
|
||||
* @param {String} name - name of service to check
|
||||
* @param {String} url - URL to check
|
||||
* @param {String} helperMessage - a message to display on error to help resolve the issue
|
||||
* @param {String} method - a request using a specific method
|
||||
* @param {String} httpStatus - expected HTTP status
|
||||
*/
|
||||
|
||||
function urlHealthCheck({name, url, helperMessage, method, httpStatus}: {name: string; url: string; helperMessage: string; method: string; httpStatus: number}): ChainableT {
|
||||
Cypress.log({name, message: `Checking URL health at ${url}`});
|
||||
|
||||
// cy.task() returns untyped data
|
||||
return cy.task('urlHealthCheck', {url, method}).then(({data, errorCode, status, success}: any) => {
|
||||
const urlService = `__${name}__ at ${url}`;
|
||||
|
||||
const successMessage = success ? `${urlService}: reachable` : `${errorCode}: The test you're running requires ${urlService} to be reachable. \n${helperMessage}`;
|
||||
expect(success, successMessage).to.equal(true);
|
||||
|
||||
const statusMessage = status === httpStatus ? `${urlService}: responded with ${status} HTTP status` : `${urlService}: expected to respond with ${httpStatus} but got ${status} HTTP status`;
|
||||
expect(status, statusMessage).to.equal(httpStatus);
|
||||
|
||||
return cy.wrap({data, status});
|
||||
});
|
||||
}
|
||||
|
||||
Cypress.Commands.add('urlHealthCheck', urlHealthCheck);
|
||||
|
||||
Cypress.Commands.add('requireWebhookServer', () => {
|
||||
const baseUrl = Cypress.config('baseUrl');
|
||||
const webhookBaseUrl = Cypress.expose('webhookBaseUrl');
|
||||
const adminUsername = Cypress.expose('adminUsername');
|
||||
const adminPassword = Cypress.expose('adminPassword');
|
||||
const helperMessage = `
|
||||
__Tips:__
|
||||
1. In local development, you may run "__npm run start:webhook__" at "/e2e" folder.
|
||||
2. If reachable from remote host, you may export it as env variable, like "__CYPRESS_webhookBaseUrl=[url] npm run cypress:open__".
|
||||
`;
|
||||
|
||||
cy.urlHealthCheck({
|
||||
name: 'Webhook Server',
|
||||
url: webhookBaseUrl,
|
||||
helperMessage,
|
||||
method: 'get',
|
||||
httpStatus: 200,
|
||||
});
|
||||
|
||||
cy.task('postIncomingWebhook', {
|
||||
url: `${webhookBaseUrl}/setup`,
|
||||
data: {
|
||||
baseUrl,
|
||||
webhookBaseUrl,
|
||||
adminUsername,
|
||||
adminPassword,
|
||||
}}).
|
||||
its('status').should('be.equal', 201);
|
||||
});
|
||||
|
||||
Cypress.Commands.overwrite('log', (subject, message) => cy.task('log', message));
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
|
||||
/**
|
||||
* externalRequest is a task which is wrapped as command with post-verification
|
||||
* that the external request is successfully completed
|
||||
* @param {Object} options
|
||||
* @param {<UserProfile, 'username' | 'password'>} options.user - a user initiating external request
|
||||
* @param {String} options.method - an HTTP method (e.g. get, post, etc)
|
||||
* @param {String} options.path - API path that is relative to Cypress.config().baseUrl
|
||||
* @param {Object} options.data - payload
|
||||
* @param {Boolean} options.failOnStatusCode - whether to fail on status code, default is true
|
||||
*
|
||||
* @example
|
||||
* cy.externalRequest({user: sysadmin, method: 'POST', path: 'config', data});
|
||||
*/
|
||||
externalRequest(options?: {
|
||||
user: Pick<UserProfile, 'username' | 'password'>;
|
||||
method: string;
|
||||
path: string;
|
||||
data?: Record<string, unknown> | unknown[];
|
||||
failOnStatusCode?: boolean;
|
||||
}): Chainable<Pick<AxiosResponse, 'data' | 'status'>>;
|
||||
|
||||
/**
|
||||
* Adds a given reaction to a specific post from a user
|
||||
* @param {Object} reactToMessageObject - Information on person and post to which a reaction needs to be added
|
||||
* @param {Object} reactToMessageObject.sender - a user object who will post a message
|
||||
* @param {string} reactToMessageObject.postId - post on which reaction is intended
|
||||
* @param {string} reactToMessageObject.reaction - emoji text eg. smile
|
||||
* @returns {Response} response: Cypress-chainable response
|
||||
*
|
||||
* @example
|
||||
* cy.reactToMessageAs({sender:user2, postId:"ABC123", reaction: 'smile'});
|
||||
*/
|
||||
reactToMessageAs({sender, postId, reaction}: {sender: Record<string, unknown>; postId: string; reaction: string}): Chainable<{status: number; data: unknown}>;
|
||||
|
||||
/**
|
||||
* Verify that the webhook server is accessible, and then sets up base URLs and credential.
|
||||
*
|
||||
* @example
|
||||
* cy.requireWebhookServer();
|
||||
*/
|
||||
requireWebhookServer(): Chainable;
|
||||
|
||||
postMessageAs: typeof postMessageAs;
|
||||
|
||||
postListOfMessages: typeof postListOfMessages;
|
||||
|
||||
postIncomingWebhook: typeof postIncomingWebhook;
|
||||
|
||||
postBotMessage: typeof postBotMessage;
|
||||
|
||||
urlHealthCheck: typeof urlHealthCheck;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user