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

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

@@ -0,0 +1,36 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({data = {}, headers, method = 'get', url}) => {
let response;
try {
response = await axios({
data,
headers,
method,
url,
});
} catch (error) {
// If we have a response for the error, pull out the relevant parts
if (error.response) {
response = {
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data,
};
} else {
// If we get here something else went wrong, so throw
throw error;
}
}
return {
data: response.data,
headers: response.headers,
status: response.status,
statusText: response.statusText,
};
};

135
e2e-tests/cypress/tests/plugins/db_request.js Обычный файл
Просмотреть файл

@@ -0,0 +1,135 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/**
* Functions here are expected to work with MySQL and PostgreSQL (known as dialect).
* When updating this file, make sure to test in both dialect.
* You'll find table and columns names are being converted to lowercase. Reason being is that
* in MySQL, first letter is capitalized.
*/
const mapKeys = require('lodash.mapkeys');
function convertKeysToLowercase(obj) {
return mapKeys(obj, (_, k) => {
return k.toLowerCase();
});
}
function getKnexClient({client, connection}) {
return require('knex')({client, connection}); // eslint-disable-line global-require
}
// Reuse DB client connection
let knexClient;
const dbGetActiveUserSessions = async ({dbConfig, params: {username, userId, limit}}) => {
if (!knexClient) {
knexClient = getKnexClient(dbConfig);
}
const maxLimit = 50;
try {
let user;
if (username) {
user = await knexClient(toLowerCase(dbConfig, 'Users')).where('username', username).first();
user = convertKeysToLowercase(user);
}
const now = Date.now();
const sessions = await knexClient(toLowerCase(dbConfig, 'Sessions')).
where('userid', user ? user.id : userId).
where('expiresat', '>', now).
orderBy('lastactivityat', 'desc').
limit(limit && limit <= maxLimit ? limit : maxLimit);
return {
user,
sessions: sessions.map((session) => convertKeysToLowercase(session)),
};
} catch (error) {
const errorMessage = 'Failed to get active user sessions from the database.';
return {error, errorMessage};
}
};
const dbGetUser = async ({dbConfig, params: {username}}) => {
if (!knexClient) {
knexClient = getKnexClient(dbConfig);
}
try {
const user = await knexClient(toLowerCase(dbConfig, 'Users')).where('username', username).first();
return {user: convertKeysToLowercase(user)};
} catch (error) {
const errorMessage = 'Failed to get a user from the database.';
return {error, errorMessage};
}
};
const dbGetUserSession = async ({dbConfig, params: {sessionId}}) => {
if (!knexClient) {
knexClient = getKnexClient(dbConfig);
}
try {
const session = await knexClient(toLowerCase(dbConfig, 'Sessions')).
where('id', '=', sessionId).
first();
return {session: convertKeysToLowercase(session)};
} catch (error) {
const errorMessage = 'Failed to get a user session from the database.';
return {error, errorMessage};
}
};
const dbUpdateUserSession = async ({dbConfig, params: {sessionId, userId, fieldsToUpdate = {}}}) => {
if (!knexClient) {
knexClient = getKnexClient(dbConfig);
}
try {
let user = await knexClient(toLowerCase(dbConfig, 'Users')).where('id', userId).first();
if (!user) {
return {errorMessage: `No user found with id: ${userId}.`};
}
delete fieldsToUpdate.id;
delete fieldsToUpdate.userid;
user = convertKeysToLowercase(user);
await knexClient(toLowerCase(dbConfig, 'Sessions')).
where('id', '=', sessionId).
where('userid', '=', user.id).
update(fieldsToUpdate);
const session = await knexClient(toLowerCase(dbConfig, 'Sessions')).
where('id', '=', sessionId).
where('userid', '=', user.id).
first();
return {session: convertKeysToLowercase(session)};
} catch (error) {
const errorMessage = 'Failed to update a user session from the database.';
return {error, errorMessage};
}
};
function toLowerCase(config, name) {
if (config.client === 'mysql') {
return name;
}
return name.toLowerCase();
}
module.exports = {
dbGetActiveUserSessions,
dbGetUser,
dbGetUserSession,
dbUpdateUserSession,
};

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

@@ -0,0 +1,82 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import axios, {AxiosError, Method} from 'axios';
import * as timeouts from '../fixtures/timeouts';
export interface ExternalRequestUser{
username: string;
password: string;
}
interface ExternalRequestArg {
baseUrl: string;
user: ExternalRequestUser;
method: Method;
path: string;
data: any;
}
type ExternalRequestResult = { status: number; statusText: string; data: any; isError?: boolean } | { data: { id: string; isTimeout: boolean }; status?: undefined; statusText?: undefined; isError?: undefined };
export default async function externalRequest(arg: ExternalRequestArg): Promise<ExternalRequestResult> {
const {baseUrl, user, method = 'get', path, data = {}} = arg;
const loginUrl = `${baseUrl}/api/v4/users/login`;
// First we need to login with our external user to get cookies/tokens
let cookieString = '';
try {
const response = await axios({
url: loginUrl,
headers: {'X-Requested-With': 'XMLHttpRequest'},
method: 'post',
timeout: timeouts.TEN_SEC,
data: {login_id: user.username, password: user.password},
});
const setCookie = response.headers['set-cookie'];
(setCookie as any).forEach((cookie: string) => {
const nameAndValue = cookie.split(';')[0];
cookieString += nameAndValue + ';';
});
} catch (error) {
return getErrorResponse(error);
}
try {
const response = await axios({
method,
url: `${baseUrl}/api/v4/${path}`,
headers: {
'Content-Type': 'text/plain',
Cookie: cookieString,
'X-Requested-With': 'XMLHttpRequest',
},
timeout: timeouts.TEN_SEC,
data,
});
return {
status: response.status,
statusText: response.statusText,
data: response.data,
};
} catch (error) {
// If we have a response for the error, pull out the relevant parts
return getErrorResponse(error);
}
}
function getErrorResponse(error: AxiosError) {
if (error.response) {
return {
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data,
isError: true,
};
} else if (error.code === 'ECONNABORTED') {
return {data: {id: error.code, isTimeout: true}};
}
// If we get here something else went wrong, so throw
throw error;
}

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

@@ -0,0 +1,39 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const fs = require('fs');
const path = require('path');
/**
* Checks whether a file exist in the fixtures folder
* @param {string} filename - filename to check if it exists
*/
const fileExist = (filename) => {
const filePath = path.resolve(__dirname, `../fixtures/${filename}`);
return fs.existsSync(filePath);
};
/**
* Write data to a file in the fixtures folder
* @param {string} filename - filename where to write data into
* @param {string} fixturesFolder - folder at tests/fixtures
* @param {string} data - The data to write
*/
const writeToFile = ({filename, fixturesFolder, data = ''}) => {
const folder = path.resolve(__dirname, `../fixtures/${fixturesFolder}`);
if (!fs.existsSync(folder)) {
fs.mkdirSync(folder, {recursive: true});
}
const filePath = `${folder}/${filename}`;
fs.writeFileSync(filePath, data);
return null;
};
module.exports = {
fileExist,
writeToFile,
};

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

@@ -0,0 +1,16 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const fs = require('fs');
const pdf = require('pdf-parse');
/**
* Checks whether a file exist in the tests/downloads folder and return the content of it.
* @param {string} filePath - pdf file path
*/
module.exports = async (filePath) => {
const dataBuffer = fs.readFileSync(filePath);
const data = await pdf(dataBuffer);
return data;
};

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

@@ -0,0 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({username, mailUrl}) => {
const mailboxUrl = `${mailUrl}/${username}`;
let response;
let recentEmail;
try {
response = await axios({url: mailboxUrl, method: 'get'});
recentEmail = response.data[response.data.length - 1];
} catch (error) {
return {status: error.status, data: null};
}
if (!recentEmail || !recentEmail.id) {
return {status: 501, data: null};
}
let recentEmailMessage;
const mailMessageUrl = `${mailboxUrl}/${recentEmail.id}`;
try {
response = await axios({url: mailMessageUrl, method: 'get'});
recentEmailMessage = response.data;
} catch (error) {
return {status: error.status, data: null};
}
return {status: response.status, data: recentEmailMessage};
};

76
e2e-tests/cypress/tests/plugins/index.js Обычный файл
Просмотреть файл

@@ -0,0 +1,76 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
/* eslint-disable no-console */
const clientRequest = require('./client_request');
const {
dbGetActiveUserSessions,
dbGetUser,
dbGetUserSession,
dbUpdateUserSession,
} = require('./db_request');
const externalRequest = require('./external_request').default;
const {fileExist, writeToFile} = require('./file_util');
const getPdfContent = require('./get_pdf_content');
const getRecentEmail = require('./get_recent_email');
const keycloakRequest = require('./keycloak_request');
const oktaRequest = require('./okta_request');
const postBotMessage = require('./post_bot_message');
const postIncomingWebhook = require('./post_incoming_webhook');
const postMessageAs = require('./post_message_as');
const postListOfMessages = require('./post_list_of_messages');
const reactToMessageAs = require('./react_to_message_as');
const {
shellFind,
shellRm,
shellUnzip,
} = require('./shell');
const urlHealthCheck = require('./url_health_check');
const log = (message) => {
console.log(message);
return null;
};
module.exports = (on, config) => {
on('task', {
clientRequest,
dbGetActiveUserSessions,
dbGetUser,
dbGetUserSession,
dbUpdateUserSession,
externalRequest,
fileExist,
writeToFile,
getPdfContent,
getRecentEmail,
keycloakRequest,
log,
oktaRequest,
postBotMessage,
postIncomingWebhook,
postMessageAs,
postListOfMessages,
urlHealthCheck,
reactToMessageAs,
shellFind,
shellRm,
shellUnzip,
});
on('before:browser:launch', (browser = {}, launchOptions) => {
if (browser.name === 'chrome' && !config.chromeWebSecurity) {
launchOptions.args.push('--disable-features=CrossSiteDocumentBlockingIfIsolating,CrossSiteDocumentBlockingAlways,IsolateOrigins,site-per-process');
launchOptions.args.push('--load-extension=tests/extensions/Ignore-X-Frame-headers');
}
if (browser.family === 'chromium' && browser.name !== 'electron') {
launchOptions.args.push('--disable-dev-shm-usage');
}
return launchOptions;
});
return config;
};

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

@@ -0,0 +1,36 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({baseUrl, headers = [], method = 'get', path = '', data = {}}) => {
let response;
try {
response = await axios({
method,
url: `${baseUrl}/${path}`,
headers,
data,
});
return {
status: response.status,
statusText: response.statusText,
data: response.data,
};
} catch (error) {
// If we have a response for the error, pull out the relevant parts
if (error.response) {
response = {
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data,
};
} else {
// If we get here something else went wrong, so throw
throw error;
}
}
return response;
};

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

@@ -0,0 +1,40 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({baseUrl, urlSuffix, method = 'get', token, data = {}}) => {
let response;
try {
response = await axios({
url: baseUrl + urlSuffix,
headers: {
'X-Requested-With': 'XMLHttpRequest',
Authorization: token,
},
method,
data,
});
return {
status: response.status,
statusText: response.statusText,
data: response.data,
};
} catch (error) {
// If we have a response for the error, pull out the relevant parts
if (error.response) {
response = {
status: error.response.status,
statusText: error.response.statusText,
data: error.response.data,
};
} else {
// If we get here something else went wrong, so throw
throw error;
}
}
return response;
};

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

@@ -0,0 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({token, message, props = {}, channelId, rootId, createAt = 0, baseUrl}) => {
let response;
try {
response = await axios({
url: `${baseUrl}/api/v4/posts`,
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
Authorization: `Bearer ${token}`,
},
method: 'post',
data: {
channel_id: channelId,
message,
props,
type: '',
create_at: createAt,
root_id: rootId,
},
});
} catch (err) {
if (err.response) {
response = err.response;
}
}
return {status: response.status, data: response.data};
};

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

@@ -0,0 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({url, data}) => {
let response;
try {
response = await axios({method: 'post', url, data});
} catch (err) {
if (err.response) {
response = err.response;
}
}
return {status: response.status, data: response.data};
};

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

@@ -0,0 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const postMessageAs = require('./post_message_as');
module.exports = async ({numberOfMessages, ...rest}) => {
const results = [];
for (let i = 0; i < numberOfMessages; i++) {
// Parallel posting of the messages (Promise.all) is not handled well by the server
// resulting in random failed posts
// so we use serial posting
// eslint-disable-next-line no-await-in-loop
results.push(await postMessageAs({message: `Message ${i}`, ...rest}));
}
return results;
};

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

@@ -0,0 +1,44 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({sender, message, channelId, rootId, createAt = 0, baseUrl}) => {
const loginResponse = await axios({
url: `${baseUrl}/api/v4/users/login`,
headers: {'X-Requested-With': 'XMLHttpRequest'},
method: 'post',
data: {login_id: sender.username, password: sender.password},
});
const setCookie = loginResponse.headers['set-cookie'];
let cookieString = '';
setCookie.forEach((cookie) => {
const nameAndValue = cookie.split(';')[0];
cookieString += nameAndValue + ';';
});
let response;
try {
response = await axios({
url: `${baseUrl}/api/v4/posts`,
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
Cookie: cookieString,
},
method: 'post',
data: {
channel_id: channelId,
message,
type: '',
create_at: createAt,
root_id: rootId,
},
});
} catch (err) {
expect(Boolean(err)).to.equal(false);
}
return {status: response.status, data: response.data};
};

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

@@ -0,0 +1,44 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({sender, postId, reaction, baseUrl}) => {
const loginResponse = await axios({
url: `${baseUrl}/api/v4/users/login`,
headers: {'X-Requested-With': 'XMLHttpRequest'},
method: 'post',
data: {login_id: sender.username, password: sender.password},
});
const setCookie = loginResponse.headers['set-cookie'];
let cookieString = '';
setCookie.forEach((cookie) => {
const nameAndValue = cookie.split(';')[0];
cookieString += nameAndValue + ';';
});
let response;
try {
response = await axios({
url: `${baseUrl}/api/v4/reactions`,
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
Cookie: cookieString,
},
method: 'post',
data: {
user_id: sender.id,
post_id: postId,
emoji_name: reaction,
},
});
} catch (err) {
if (err.response) {
response = err.response;
}
}
return {status: response.status, data: response.data};
};

30
e2e-tests/cypress/tests/plugins/shell.js Обычный файл
Просмотреть файл

@@ -0,0 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const extractZip = require('extract-zip');
const shell = require('shelljs');
const shellFind = ({path, pattern}) => {
return shell.find(path).filter((file) => {
return file.match(pattern);
});
};
const shellRm = ({option, file}) => {
return shell.rm(option, file);
};
const shellUnzip = async ({source, target}) => {
try {
await extractZip(source, {dir: target});
return null;
} catch (err) {
return err;
}
};
module.exports = {
shellFind,
shellRm,
shellUnzip,
};

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

@@ -0,0 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const axios = require('axios');
module.exports = async ({url, method}) => {
let response;
try {
response = await axios({url, method});
return {data: response.data, status: response.status, success: true};
} catch (err) {
return {success: false, errorCode: err.code};
}
};