MM-52642 CLD-6352 MM-54007 Prepare setup for Playwright pipeline (#24647)
* pipeline(playwright): prepare setup for playwright * feedback address * clean up readme * Fix docker-compose command and definition for dashboard * fix the dashboard * ignore dashboard when formatting --------- Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Mario Vitale <mvitale1989@hotmail.com>
@@ -1,7 +1,7 @@
|
|||||||
|
# shellcheck disable=SC2148,SC2155
|
||||||
|
|
||||||
# Utility variables
|
# Utility variables
|
||||||
# NB: these assume you `source` them from the directory this file is in
|
# NB: these assume you `source` them from the directory this file is in
|
||||||
export MME2E_DC_SERVER="docker compose -p mmserver -f $(readlink -f ../../server/build/gitlab-dc.postgres.yml) -f $(readlink -f ./server.override.yml)"
|
|
||||||
export MME2E_DC_DASHBOARD="docker compose -p mmdashboard -f $(readlink -f ./dashboard/docker/docker-compose.yml) -f $(readlink -f ./dashboard.override.yml)"
|
|
||||||
export MME2E_UID=$(id -u)
|
export MME2E_UID=$(id -u)
|
||||||
export MME2E_OSTYPE=$(docker version -f '{{ .Client.Os }}')
|
export MME2E_OSTYPE=$(docker version -f '{{ .Client.Os }}')
|
||||||
export MME2E_ARCHTYPE=$(docker version -f '{{ .Client.Arch }}')
|
export MME2E_ARCHTYPE=$(docker version -f '{{ .Client.Arch }}')
|
||||||
@@ -34,17 +34,18 @@ mme2e_get_current_shopt_arg () {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
mme2e_load_env_file () {
|
mme2e_load_env_file () {
|
||||||
# This loads the ./env file. Variables are automatically exported
|
# This loads the ./.env file. Variables are automatically exported
|
||||||
[ -f ./env ] || return 0
|
[ -f ./.env ] || return 0
|
||||||
MME2E_PREVIOUS_ALLEXPORT=$(mme2e_get_current_shopt_arg allexport)
|
MME2E_PREVIOUS_ALLEXPORT=$(mme2e_get_current_shopt_arg allexport)
|
||||||
set -o allexport
|
set -o allexport
|
||||||
mme2e_log "Loading env file"
|
mme2e_log "Loading .env file"
|
||||||
. ./env
|
. ./.env
|
||||||
|
# shellcheck disable=SC2086
|
||||||
set ${MME2E_PREVIOUS_ALLEXPORT}
|
set ${MME2E_PREVIOUS_ALLEXPORT}
|
||||||
}
|
}
|
||||||
mme2e_generate_envfile_from_var_names () {
|
mme2e_generate_envfile_from_var_names () {
|
||||||
# Read var names from stdin, one per line
|
# Read var names from stdin, one per line
|
||||||
while read VARIABLE; do
|
while read -r VARIABLE; do
|
||||||
[ -z "${!VARIABLE:-}" ] || echo "${VARIABLE}=${!VARIABLE}";
|
[ -z "${!VARIABLE:-}" ] || echo "${VARIABLE}=${!VARIABLE}";
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
@@ -59,8 +60,9 @@ mme2e_wait_command_success () {
|
|||||||
RETRIES_LEFT=$((RETRIES_LEFT - 1))
|
RETRIES_LEFT=$((RETRIES_LEFT - 1))
|
||||||
[ "$RETRIES_LEFT" -le "0" ] && break
|
[ "$RETRIES_LEFT" -le "0" ] && break
|
||||||
mme2e_log "${RETRY_MESSAGE} ($RETRIES_LEFT retries left, sleeping $RETRIES_INTERVAL seconds)"
|
mme2e_log "${RETRY_MESSAGE} ($RETRIES_LEFT retries left, sleeping $RETRIES_INTERVAL seconds)"
|
||||||
sleep $RETRIES_INTERVAL
|
sleep "$RETRIES_INTERVAL"
|
||||||
done
|
done
|
||||||
|
# shellcheck disable=SC2086
|
||||||
set ${MME2E_PREVIOUS_PIPEFAIL}
|
set ${MME2E_PREVIOUS_PIPEFAIL}
|
||||||
if [ "$RETRIES_LEFT" = "0" ]; then
|
if [ "$RETRIES_LEFT" = "0" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
@@ -92,11 +94,6 @@ mme2e_legacy_setup () {
|
|||||||
docker network inspect ${COMPOSE_PROJECT_NAME} >/dev/null 2>&1 || docker network create --driver=${DOCKER_NETWORK_DRIVER} ${COMPOSE_PROJECT_NAME}
|
docker network inspect ${COMPOSE_PROJECT_NAME} >/dev/null 2>&1 || docker network create --driver=${DOCKER_NETWORK_DRIVER} ${COMPOSE_PROJECT_NAME}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Utility alias, for interactive shell usage. Can be reversed with 'unalias docker-compose' in your shell
|
|
||||||
# NB: this won't work in the script
|
|
||||||
alias docker-compose-mmserver="${MME2E_DC_SERVER}"
|
|
||||||
alias docker-compose-mmdashboard="${MME2E_DC_DASHBOARD}"
|
|
||||||
|
|
||||||
# Call prerequisite utility functions
|
# Call prerequisite utility functions
|
||||||
mme2e_load_env_file
|
mme2e_load_env_file
|
||||||
mme2e_legacy_setup # Temporary
|
mme2e_legacy_setup # Temporary
|
||||||
|
|||||||
22
e2e-tests/.ci/.e2erc_setup
Обычный файл
@@ -0,0 +1,22 @@
|
|||||||
|
# shellcheck disable=SC2148
|
||||||
|
|
||||||
|
# Set up base docker compose file and export
|
||||||
|
DC_FILE="docker-compose.server.override.yml"
|
||||||
|
if [ ! -f docker-compose.server.override.yml ]; then
|
||||||
|
echo "No './.ci/docker-compose.server.override.yml' found. You may need to run 'make generate-docker-compose'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$TEST" = "cypress" ]; then
|
||||||
|
MME2E_DC_SERVER="docker compose -p mmserver -f ./$DC_FILE -f ./docker-compose.cypress.yml"
|
||||||
|
elif [ "$TEST" = "playwright" ]; then
|
||||||
|
MME2E_DC_SERVER="docker compose -p mmserver -f ./$DC_FILE -f ./docker-compose.playwright.yml"
|
||||||
|
else
|
||||||
|
MME2E_DC_SERVER="docker compose -p mmserver -f ./$DC_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export MME2E_DC_SERVER=$MME2E_DC_SERVER
|
||||||
|
alias docker-compose-mmserver='${MME2E_DC_SERVER}'
|
||||||
|
|
||||||
|
export MME2E_DC_DASHBOARD="docker compose -p mmdashboard -f ./dashboard/docker/docker-compose.yml -f ./dashboard.override.yml"
|
||||||
|
alias docker-compose-mmdashboard='${MME2E_DC_DASHBOARD}'
|
||||||
1
e2e-tests/.ci/.env.server.cloud
Обычный файл
@@ -0,0 +1 @@
|
|||||||
|
# NB: this file is just a placeholder required by docker-compose. It's supposed to be modified by the CI pipeline and should remain empty in git
|
||||||
3
e2e-tests/.ci/.gitignore
поставляемый
@@ -1,5 +1,6 @@
|
|||||||
dashboard
|
dashboard
|
||||||
env
|
.env
|
||||||
!.env.cypress
|
!.env.cypress
|
||||||
!.env.server
|
!.env.server
|
||||||
|
!.env.server.cloud
|
||||||
!.env.dashboard
|
!.env.dashboard
|
||||||
|
|||||||
4
e2e-tests/.ci/.shellcheckrc
Обычный файл
@@ -0,0 +1,4 @@
|
|||||||
|
# SC1091: Not following: <file>
|
||||||
|
# https://www.shellcheck.net/wiki/SC1091
|
||||||
|
disable=SC1091
|
||||||
|
# Used to ignore for sourcing ".e2erc*"
|
||||||
@@ -7,14 +7,14 @@ npm install
|
|||||||
MIGRATION_ATTEMPTS_LEFT=10
|
MIGRATION_ATTEMPTS_LEFT=10
|
||||||
MIGRATION_ATTEMPTS_INTERVAL=10
|
MIGRATION_ATTEMPTS_INTERVAL=10
|
||||||
until npm run migrate:latest; do
|
until npm run migrate:latest; do
|
||||||
MIGRATION_ATTEMPTS_LEFT=$((MIGRATION_ATTEMPTS_LEFT - 1))
|
MIGRATION_ATTEMPTS_LEFT=$((MIGRATION_ATTEMPTS_LEFT - 1))
|
||||||
[ "$MIGRATION_ATTEMPTS_LEFT" -gt 0 ] || break
|
[ "$MIGRATION_ATTEMPTS_LEFT" -gt 0 ] || break
|
||||||
echo "Migration script failed, sleeping $MIGRATION_ATTEMPTS_INTERVAL"
|
echo "Migration script failed, sleeping $MIGRATION_ATTEMPTS_INTERVAL"
|
||||||
sleep $MIGRATION_ATTEMPTS_INTERVAL
|
sleep $MIGRATION_ATTEMPTS_INTERVAL
|
||||||
done
|
done
|
||||||
[ "$MIGRATION_ATTEMPTS_LEFT" -gt 0 ] || {
|
[ "$MIGRATION_ATTEMPTS_LEFT" -gt 0 ] || {
|
||||||
echo "Migration script failed, exhausted attempts. Giving up."
|
echo "Migration script failed, exhausted attempts. Giving up."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Launch the dashboard
|
# Launch the dashboard
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e -u -o pipefail
|
set -e -u -o pipefail
|
||||||
cd $(dirname $0)
|
cd "$(dirname "$0")"
|
||||||
. .e2erc
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
|
|
||||||
MME2E_DASHBOARD_REF_DEFAULT="origin/main"
|
MME2E_DASHBOARD_REF_DEFAULT="origin/main"
|
||||||
MME2E_DASHBOARD_REF=${MME2E_DASHBOARD_REF:-$MME2E_DASHBOARD_REF_DEFAULT}
|
MME2E_DASHBOARD_REF=${MME2E_DASHBOARD_REF:-$MME2E_DASHBOARD_REF_DEFAULT}
|
||||||
@@ -9,26 +10,34 @@ MME2E_DASHBOARD_REF=${MME2E_DASHBOARD_REF:-$MME2E_DASHBOARD_REF_DEFAULT}
|
|||||||
mme2e_log "Cloning the automation-dashboard project"
|
mme2e_log "Cloning the automation-dashboard project"
|
||||||
if [ ! -d dashboard ]; then
|
if [ ! -d dashboard ]; then
|
||||||
git clone https://github.com/saturninoabril/automation-dashboard.git dashboard
|
git clone https://github.com/saturninoabril/automation-dashboard.git dashboard
|
||||||
. .e2erc # Must reinitialize some variables that depend on the dashboard repo being checked out
|
# Must reinitialize some variables that depend on the dashboard repo being checked out
|
||||||
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
fi
|
fi
|
||||||
git -C dashboard fetch
|
git -C dashboard fetch
|
||||||
git -C dashboard checkout $MME2E_DASHBOARD_REF
|
git -C dashboard checkout "$MME2E_DASHBOARD_REF"
|
||||||
|
|
||||||
mme2e_log "Starting the dashboard"
|
mme2e_log "Starting the dashboard"
|
||||||
${MME2E_DC_DASHBOARD} up -d db dashboard
|
${MME2E_DC_DASHBOARD} up -d db dashboard
|
||||||
|
|
||||||
mme2e_log "Generating the dashboard's local URL"
|
mme2e_log "Generating the dashboard's local URL"
|
||||||
case $MME2E_OSTYPE in
|
case $MME2E_OSTYPE in
|
||||||
darwin )
|
darwin)
|
||||||
AUTOMATION_DASHBOARD_IP=$(ifconfig $(route get 1.1.1.1 | grep interface | awk '{print $2}') | grep -w inet | awk '{print $2}') ;;
|
# shellcheck disable=SC2046
|
||||||
* )
|
AUTOMATION_DASHBOARD_IP=$(ifconfig $(route get 1.1.1.1 | grep interface | awk '{print $2}') | grep -w inet | awk '{print $2}')
|
||||||
MME2E_DC_DASHBOARD_NETWORK=$(docker inspect $(${MME2E_DC_DASHBOARD} ps -q dashboard) | jq -r '.[0].NetworkSettings.Networks | (keys|.[0])')
|
;;
|
||||||
AUTOMATION_DASHBOARD_IP=$(docker network inspect $MME2E_DC_DASHBOARD_NETWORK | jq -r '.[0].IPAM.Config[0].Gateway')
|
*)
|
||||||
|
# shellcheck disable=SC2046
|
||||||
|
MME2E_DC_DASHBOARD_NETWORK=$(docker inspect $(${MME2E_DC_DASHBOARD} ps -q dashboard) | jq -r '.[0].NetworkSettings.Networks | (keys|.[0])')
|
||||||
|
AUTOMATION_DASHBOARD_IP=$(docker network inspect "$MME2E_DC_DASHBOARD_NETWORK" | jq -r '.[0].IPAM.Config[0].Gateway')
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
# shellcheck disable=SC2034
|
||||||
AUTOMATION_DASHBOARD_URL="http://${AUTOMATION_DASHBOARD_IP}:4000/api"
|
AUTOMATION_DASHBOARD_URL="http://${AUTOMATION_DASHBOARD_IP}:4000/api"
|
||||||
|
|
||||||
mme2e_log "Generating a signed JWT token for accessing the dashboard"
|
mme2e_log "Generating a signed JWT token for accessing the dashboard"
|
||||||
${MME2E_DC_DASHBOARD} exec -T dashboard npm i
|
${MME2E_DC_DASHBOARD} exec -T dashboard bash -c "rm -rf node_modules && npm install --cache /tmp/empty-cache"
|
||||||
|
# shellcheck disable=SC2034
|
||||||
AUTOMATION_DASHBOARD_TOKEN=$(${MME2E_DC_DASHBOARD} exec -T dashboard node script/sign.js | awk '{ print $2; }') # The token secret is specified in the dashboard.override.yml file
|
AUTOMATION_DASHBOARD_TOKEN=$(${MME2E_DC_DASHBOARD} exec -T dashboard node script/sign.js | awk '{ print $2; }') # The token secret is specified in the dashboard.override.yml file
|
||||||
|
|
||||||
mme2e_log "Generating the .env.dashboard file, to point Cypress to the dashboard URL"
|
mme2e_log "Generating the .env.dashboard file, to point Cypress to the dashboard URL"
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e -u -o pipefail
|
set -e -u -o pipefail
|
||||||
cd $(dirname $0)
|
cd "$(dirname "$0")"
|
||||||
. .e2erc
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
|
|
||||||
if [ -d dashboard ]; then
|
if [ -d dashboard ]; then
|
||||||
mme2e_log "Stopping the dashboard containers"
|
mme2e_log "Stopping the dashboard containers"
|
||||||
|
|||||||
61
e2e-tests/.ci/docker-compose.cypress.yml
Обычный файл
@@ -0,0 +1,61 @@
|
|||||||
|
# Image hashes in this file are for amd64 systems
|
||||||
|
|
||||||
|
version: "2.4"
|
||||||
|
services:
|
||||||
|
cypress:
|
||||||
|
image: "cypress/browsers:node-18.16.1-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1"
|
||||||
|
### Temporarily disabling this image, until both the amd64 and arm64 version are mirrored
|
||||||
|
# image: "mattermostdevelopment/mirrored-cypress-browsers-public:node-18.16.1-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1"
|
||||||
|
entrypoint: ["/bin/bash", "-c"]
|
||||||
|
command: ["until [ -f /var/run/mm_terminate ]; do sleep 5; done"]
|
||||||
|
env_file:
|
||||||
|
- "../../e2e-tests/.ci/.env.dashboard"
|
||||||
|
- "../../e2e-tests/.ci/.env.cypress"
|
||||||
|
environment:
|
||||||
|
REPO: "mattermost"
|
||||||
|
# Cypress configuration
|
||||||
|
HEADLESS: "true"
|
||||||
|
CYPRESS_baseUrl: "http://server:8065"
|
||||||
|
CYPRESS_dbConnection: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10"
|
||||||
|
CYPRESS_smtpUrl: "http://inbucket:9001"
|
||||||
|
CYPRESS_webhookBaseUrl: "http://webhook-interactions:3000"
|
||||||
|
CYPRESS_chromeWebSecurity: "false"
|
||||||
|
CYPRESS_firstTest: "true"
|
||||||
|
CYPRESS_resetBeforeTest: "true"
|
||||||
|
CYPRESS_allowedUntrustedInternalConnections: "localhost webhook-interactions"
|
||||||
|
TM4J_ENABLE: "false"
|
||||||
|
# disable shared memory X11 affecting Cypress v4 and Chrome
|
||||||
|
# https://github.com/cypress-io/cypress-docker-images/issues/270
|
||||||
|
QT_X11_NO_MITSHM: "1"
|
||||||
|
_X11_NO_MITSHM: "1"
|
||||||
|
_MITSHM: "0"
|
||||||
|
# avoid too many progress messages
|
||||||
|
# https://github.com/cypress-io/cypress/issues/1243
|
||||||
|
CI: "1"
|
||||||
|
# Ensure we're independent from the global node environment
|
||||||
|
PATH: /cypress/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
ulimits:
|
||||||
|
nofile:
|
||||||
|
soft: 8096
|
||||||
|
hard: 1048576
|
||||||
|
working_dir: /cypress
|
||||||
|
volumes:
|
||||||
|
- "../../e2e-tests/cypress/:/cypress"
|
||||||
|
|
||||||
|
webhook-interactions:
|
||||||
|
image: mattermostdevelopment/mirrored-node:${NODE_VERSION_REQUIRED}
|
||||||
|
command: sh -c "npm install --legacy-peer-deps && exec node webhook_serve.js"
|
||||||
|
environment:
|
||||||
|
NODE_PATH: /usr/local/lib/node_modules/
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-s", "-o/dev/null", "127.0.0.1:3000"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 15s
|
||||||
|
retries: 12
|
||||||
|
working_dir: /cypress
|
||||||
|
volumes:
|
||||||
|
- "../../e2e-tests/cypress/:/cypress:ro"
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases:
|
||||||
|
- webhook-interactions
|
||||||
32
e2e-tests/.ci/docker-compose.playwright.yml
Обычный файл
@@ -0,0 +1,32 @@
|
|||||||
|
# Image hashes in this file are for amd64 systems
|
||||||
|
|
||||||
|
version: "2.4"
|
||||||
|
services:
|
||||||
|
playwright:
|
||||||
|
image: mcr.microsoft.com/playwright:v1.38.1-jammy
|
||||||
|
entrypoint: ["/bin/bash", "-c"]
|
||||||
|
command: ["until [ -f /var/run/mm_terminate ]; do sleep 5; done"]
|
||||||
|
env_file:
|
||||||
|
- "./.env.playwright"
|
||||||
|
environment:
|
||||||
|
CI: "true"
|
||||||
|
NODE_OPTIONS: --no-experimental-fetch
|
||||||
|
PW_BASE_URL: http://server:8065
|
||||||
|
PW_ADMIN_USERNAME: sysadmin
|
||||||
|
PW_ADMIN_PASSWORD: Sys@dmin-sample1
|
||||||
|
PW_ADMIN_EMAIL: sysadmin@sample.mattermost.com
|
||||||
|
PW_ENSURE_PLUGINS_INSTALLED: ""
|
||||||
|
PW_HA_CLUSTER_ENABLED: "false"
|
||||||
|
PW_RESET_BEFORE_TEST: "false"
|
||||||
|
PW_HEADLESS: "true"
|
||||||
|
PW_SLOWMO: 0
|
||||||
|
PW_WORKERS: 2
|
||||||
|
PW_SNAPSHOT_ENABLE: "false"
|
||||||
|
PW_PERCY_ENABLE: "false"
|
||||||
|
ulimits:
|
||||||
|
nofile:
|
||||||
|
soft: 8096
|
||||||
|
hard: 1048576
|
||||||
|
working_dir: /mattermost
|
||||||
|
volumes:
|
||||||
|
- "../../:/mattermost"
|
||||||
242
e2e-tests/.ci/docker_compose_generator.sh
Исполняемый файл
@@ -0,0 +1,242 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# SC2034: <variable> appears unused.
|
||||||
|
# https://www.shellcheck.net/wiki/SC2034
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
# Note: Variables are dynamically used depending on usage input (ENABLED_DOCKER_SERVICES)
|
||||||
|
|
||||||
|
set -e -u -o pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
. .e2erc
|
||||||
|
|
||||||
|
# File to be used for overriding docker compose
|
||||||
|
CONFIG_FILE="docker-compose.server.override.yml"
|
||||||
|
|
||||||
|
# Check if ENABLED_DOCKER_SERVICES is set or not
|
||||||
|
if [ -n "${ENABLED_DOCKER_SERVICES-}" ] && [ -n "$ENABLED_DOCKER_SERVICES" ]; then
|
||||||
|
mme2e_log "ENABLED_DOCKER_SERVICES: $ENABLED_DOCKER_SERVICES"
|
||||||
|
else
|
||||||
|
# If not set, then remove the override file if it exists and exit
|
||||||
|
mme2e_log "ENABLED_DOCKER_SERVICES is empty or unset."
|
||||||
|
if [ -f $CONFIG_FILE ]; then
|
||||||
|
rm -f $CONFIG_FILE
|
||||||
|
mme2e_log "$CONFIG_FILE removed."
|
||||||
|
else
|
||||||
|
mme2e_log "No override to docker compose."
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Define list of valid services
|
||||||
|
validServices=("postgres:5432" "minio:9000" "inbucket:9001" "openldap:389" "elasticsearch:9200" "keycloak:8080")
|
||||||
|
|
||||||
|
# Read the enabled docker services and split them into an array
|
||||||
|
enabled_docker_services="$ENABLED_DOCKER_SERVICES"
|
||||||
|
read -ra docker_services <<<"$enabled_docker_services"
|
||||||
|
|
||||||
|
# Initialize variables for required services
|
||||||
|
postgres_found=false
|
||||||
|
inbucket_found=false
|
||||||
|
|
||||||
|
# Get service and post of valid services
|
||||||
|
services=()
|
||||||
|
for service in "${docker_services[@]}"; do
|
||||||
|
port=""
|
||||||
|
for svcPort in "${validServices[@]}"; do
|
||||||
|
svc=${svcPort%%:*}
|
||||||
|
if [ "$service" == "$svc" ]; then
|
||||||
|
port=${svcPort#*:}
|
||||||
|
|
||||||
|
# Find the required services
|
||||||
|
if [ "$service" == "$svc" ]; then
|
||||||
|
if [ "$service" == "postgres" ]; then
|
||||||
|
postgres_found=true
|
||||||
|
elif [ "$service" == "inbucket" ]; then
|
||||||
|
inbucket_found=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$port" ]; then
|
||||||
|
mme2e_log "Unknown service $svc"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
services+=("$service:$port")
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if the required services such as postgres and inbucket are found
|
||||||
|
# Do not continue if any of the required services are not found.
|
||||||
|
if [ "$postgres_found" != true ] || [ "$inbucket_found" != true ]; then
|
||||||
|
mme2e_log "When overriding docker compose via ENABLED_DOCKER_SERVICES, postgres and inbucket are both required."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Define each service values
|
||||||
|
postgres='
|
||||||
|
postgres:
|
||||||
|
image: mattermostdevelopment/mirrored-postgres:12
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: mmuser
|
||||||
|
POSTGRES_PASSWORD: mostest
|
||||||
|
POSTGRES_DB: mattermost_test
|
||||||
|
command: postgres -c "config_file=/etc/postgresql/postgresql.conf"
|
||||||
|
volumes:
|
||||||
|
- ../../server/build/docker/postgres.conf:/etc/postgresql/postgresql.conf
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-h", "localhost"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 15s
|
||||||
|
retries: 12
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases:
|
||||||
|
- postgres'
|
||||||
|
|
||||||
|
inbucket='
|
||||||
|
inbucket:
|
||||||
|
restart: "no"
|
||||||
|
container_name: mattermost-inbucket
|
||||||
|
ports:
|
||||||
|
- "9001:9001"
|
||||||
|
- "10025:10025"
|
||||||
|
- "10110:10110"
|
||||||
|
extends:
|
||||||
|
file: ../../server/build/gitlab-dc.common.yml
|
||||||
|
service: inbucket'
|
||||||
|
|
||||||
|
minio='
|
||||||
|
minio:
|
||||||
|
restart: "no"
|
||||||
|
container_name: mattermost-minio
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
extends:
|
||||||
|
file: ../../server/build/gitlab-dc.common.yml
|
||||||
|
service: minio'
|
||||||
|
|
||||||
|
openldap='
|
||||||
|
openldap:
|
||||||
|
restart: "no"
|
||||||
|
container_name: mattermost-openldap
|
||||||
|
ports:
|
||||||
|
- "389:389"
|
||||||
|
- "636:636"
|
||||||
|
extends:
|
||||||
|
file: ../../server/build/gitlab-dc.common.yml
|
||||||
|
service: openldap'
|
||||||
|
|
||||||
|
elasticsearch='
|
||||||
|
elasticsearch:
|
||||||
|
restart: "no"
|
||||||
|
container_name: mattermost-elasticsearch
|
||||||
|
ports:
|
||||||
|
- "9200:9200"
|
||||||
|
- "9300:9300"
|
||||||
|
extends:
|
||||||
|
file: ../../server/build/gitlab-dc.common.yml
|
||||||
|
service: elasticsearch'
|
||||||
|
|
||||||
|
elasticsearch_arm64='
|
||||||
|
elasticsearch:
|
||||||
|
image: mattermostdevelopment/mattermost-elasticsearch:7.17.10
|
||||||
|
platform: linux/arm64/v8
|
||||||
|
restart: "no"
|
||||||
|
container_name: mattermost-elasticsearch
|
||||||
|
ports:
|
||||||
|
- "9200:9200"
|
||||||
|
- "9300:9300"
|
||||||
|
extends:
|
||||||
|
file: ../../server/build/gitlab-dc.common.yml
|
||||||
|
service: elasticsearch'
|
||||||
|
|
||||||
|
keycloak='
|
||||||
|
keycloak:
|
||||||
|
restart: "no"
|
||||||
|
container_name: mattermost-keycloak
|
||||||
|
ports:
|
||||||
|
- "8484:8080"
|
||||||
|
extends:
|
||||||
|
file: ../../server/build/gitlab-dc.common.yml
|
||||||
|
service: keycloak'
|
||||||
|
|
||||||
|
# Function to get the service value based on the key
|
||||||
|
get_service_val_by_key() {
|
||||||
|
local key="$1"
|
||||||
|
|
||||||
|
if [ "$MME2E_ARCHTYPE" = "arm64" ] && [ "$key" = "elasticsearch" ]; then
|
||||||
|
# Use arm64 version of elasticsearch
|
||||||
|
key="elasticsearch_arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use variable indirection to retrieve the value
|
||||||
|
local service_val="${!key}"
|
||||||
|
|
||||||
|
echo "$service_val"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate the docker compose override file
|
||||||
|
cat <<EOL >"$CONFIG_FILE"
|
||||||
|
# Image hashes in this file are for amd64 systems
|
||||||
|
# NB: May include paths relative to the "server/build" directory, which contains the original compose file that this yaml is overriding
|
||||||
|
|
||||||
|
version: "2.4"
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: \${SERVER_IMAGE}
|
||||||
|
restart: always
|
||||||
|
env_file:
|
||||||
|
- "./.env.server"
|
||||||
|
- "./.env.server.cloud"
|
||||||
|
environment:
|
||||||
|
MM_LICENSE: \${MM_LICENSE}
|
||||||
|
MM_SERVICESETTINGS_SITEURL: http://server:8065
|
||||||
|
MM_SERVICESETTINGS_ENABLELOCALMODE: "true"
|
||||||
|
MM_PLUGINSETTINGS_ENABLED: "true"
|
||||||
|
MM_PLUGINSETTINGS_ENABLEUPLOADS: "true"
|
||||||
|
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: "true"
|
||||||
|
MM_TEAMSETTINGS_ENABLEOPENSERVER: "true"
|
||||||
|
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10&binary_parameters=yes"
|
||||||
|
MM_SQLSETTINGS_DRIVERNAME: "postgres"
|
||||||
|
MM_EMAILSETTINGS_SMTPSERVER: "inbucket"
|
||||||
|
MM_CLUSTERSETTINGS_READONLYCONFIG: "false"
|
||||||
|
MM_SERVICESETTINGS_ENABLEONBOARDINGFLOW: "false"
|
||||||
|
MM_FEATUREFLAGS_ONBOARDINGTOURTIPS: "false"
|
||||||
|
MM_SERVICEENVIRONMENT: "test"
|
||||||
|
ports:
|
||||||
|
- "8065:8065"
|
||||||
|
depends_on:
|
||||||
|
$(for service in "${docker_services[@]}"; do
|
||||||
|
echo " $service:"
|
||||||
|
echo " condition: service_healthy"
|
||||||
|
done)
|
||||||
|
$(for service in "${docker_services[@]}"; do
|
||||||
|
service_val=$(get_service_val_by_key "$service")
|
||||||
|
|
||||||
|
if [ -n "$service_val" ]; then
|
||||||
|
echo "$service_val"
|
||||||
|
fi
|
||||||
|
done)
|
||||||
|
|
||||||
|
start_dependencies:
|
||||||
|
image: mattermost/mattermost-wait-for-dep:latest
|
||||||
|
depends_on:
|
||||||
|
$(for service in "${docker_services[@]}"; do
|
||||||
|
echo " - $service"
|
||||||
|
done)
|
||||||
|
command: $(
|
||||||
|
IFS=' '
|
||||||
|
echo "${services[*]}"
|
||||||
|
)
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: \${COMPOSE_PROJECT_NAME}
|
||||||
|
external: true
|
||||||
|
EOL
|
||||||
|
|
||||||
|
mme2e_log "Configuration generated in $CONFIG_FILE"
|
||||||
38
e2e-tests/.ci/server.cloud_customer_create.sh
Исполняемый файл
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e -u -o pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
. .e2erc
|
||||||
|
|
||||||
|
if [ "$SERVER" != "cloud" ]; then
|
||||||
|
mme2e_log "Not applicable to SERVER='$SERVER'. For cloud only."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if CWS_URL is set or not
|
||||||
|
if [ -n "${CWS_URL-}" ] && [ -n "$CWS_URL" ]; then
|
||||||
|
mme2e_log "CWS_URL is set."
|
||||||
|
else
|
||||||
|
mme2e_log "Environment variable CWS_URL is empty or unset. It must be set to create test cloud customer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
response=$(curl -X POST "${CWS_URL}/api/v1/internal/tests/create-customer?sku=cloud-enterprise&is_paid=true")
|
||||||
|
MM_CUSTOMER_ID=$(echo "$response" | jq -r .customer_id)
|
||||||
|
MM_CLOUD_API_KEY=$(echo "$response" | jq -r .api_key)
|
||||||
|
MM_CLOUD_INSTALLATION_ID=$(echo "$response" | jq -r .installation_id)
|
||||||
|
|
||||||
|
export MM_CUSTOMER_ID=$MM_CUSTOMER_ID
|
||||||
|
export MM_CLOUD_API_KEY=$MM_CLOUD_API_KEY
|
||||||
|
export MM_CLOUD_INSTALLATION_ID=$MM_CLOUD_INSTALLATION_ID
|
||||||
|
export MM_CLOUDSETTINGS_CWSURL=$CWS_URL
|
||||||
|
export MM_CLOUDSETTINGS_CWSAPIURL=$CWS_URL
|
||||||
|
|
||||||
|
mme2e_generate_envfile_from_var_names >.env.server.cloud <<EOF
|
||||||
|
MM_CLOUDSETTINGS_CWSURL
|
||||||
|
MM_CLOUDSETTINGS_CWSAPIURL
|
||||||
|
MM_CLOUD_API_KEY
|
||||||
|
MM_CUSTOMER_ID
|
||||||
|
MM_CLOUD_INSTALLATION_ID
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mme2e_log "Test cloud customer created, MM_CUSTOMER_ID: $MM_CUSTOMER_ID."
|
||||||
25
e2e-tests/.ci/server.cloud_customer_delete.sh
Исполняемый файл
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e -u -o pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
. .e2erc
|
||||||
|
|
||||||
|
if [ "$SERVER" != "cloud" ]; then
|
||||||
|
mme2e_log "Not applicable to SERVER='$SERVER'. For cloud only."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mme2e_log "Loading .env.server.cloud"
|
||||||
|
. .env.server.cloud
|
||||||
|
|
||||||
|
# Check if CWS_URL is set or not
|
||||||
|
if [ -n "${CWS_URL-}" ] && [ -n "$CWS_URL" ]; then
|
||||||
|
mme2e_log "CWS_URL is set."
|
||||||
|
else
|
||||||
|
mme2e_log "Environment variable CWS_URL is empty or unset. It must be set to delete test cloud customer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mme2e_log "Deleting customer $MM_CUSTOMER_ID."
|
||||||
|
curl -X DELETE "${CWS_URL}/api/v1/internal/tests/customers/$MM_CUSTOMER_ID/payment-customer"
|
||||||
|
|
||||||
|
mme2e_log "Test cloud customer deleted, MM_CUSTOMER_ID: $MM_CUSTOMER_ID."
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
---
|
|
||||||
# Image hashes in this file are for amd64 systems
|
|
||||||
# NB: paths relative to the `server/build` directory, which contains the original compose file that this yaml is overriding
|
|
||||||
version: "2.4"
|
|
||||||
services:
|
|
||||||
server:
|
|
||||||
image: "${SERVER_IMAGE}"
|
|
||||||
restart: always
|
|
||||||
env_file:
|
|
||||||
- "../../e2e-tests/.ci/.env.server"
|
|
||||||
environment:
|
|
||||||
MM_SERVICESETTINGS_SITEURL: "http://server:8065"
|
|
||||||
MM_SERVICESETTINGS_ENABLELOCALMODE: "true"
|
|
||||||
MM_SERVICESETTINGS_ALLOWCORSFROM: "http://localhost:8065"
|
|
||||||
MM_PLUGINSETTINGS_ENABLED: "true"
|
|
||||||
MM_PLUGINSETTINGS_ENABLEUPLOADS: "true"
|
|
||||||
MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS: "true"
|
|
||||||
MM_TEAMSETTINGS_ENABLEOPENSERVER: "true"
|
|
||||||
MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: "true"
|
|
||||||
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10&binary_parameters=yes"
|
|
||||||
MM_SQLSETTINGS_DRIVERNAME: "postgres"
|
|
||||||
MM_LDAPSETTINGS_LDAPSERVER: "openldap"
|
|
||||||
MM_EMAILSETTINGS_SMTPSERVER: "inbucket"
|
|
||||||
MM_CLUSTERSETTINGS_READONLYCONFIG: "false"
|
|
||||||
MM_FEATUREFLAGS_USECASEONBOARDING: "false"
|
|
||||||
MM_SERVICESETTINGS_ENABLEONBOARDINGFLOW: "false"
|
|
||||||
MM_FEATUREFLAGS_ONBOARDINGTOURTIPS: "false"
|
|
||||||
MM_SERVICEENVIRONMENT: "test"
|
|
||||||
volumes:
|
|
||||||
- "server-config:/mattermost/config"
|
|
||||||
ports:
|
|
||||||
- "8065:8065"
|
|
||||||
depends_on:
|
|
||||||
postgres:
|
|
||||||
condition: service_healthy
|
|
||||||
minio:
|
|
||||||
condition: service_healthy
|
|
||||||
inbucket:
|
|
||||||
condition: service_healthy
|
|
||||||
openldap:
|
|
||||||
condition: service_healthy
|
|
||||||
elasticsearch:
|
|
||||||
condition: service_healthy
|
|
||||||
keycloak:
|
|
||||||
condition: service_healthy
|
|
||||||
dejavu:
|
|
||||||
condition: service_healthy
|
|
||||||
prometheus:
|
|
||||||
condition: service_healthy
|
|
||||||
grafana:
|
|
||||||
condition: service_healthy
|
|
||||||
webhook-interactions:
|
|
||||||
condition: service_healthy
|
|
||||||
|
|
||||||
webhook-interactions:
|
|
||||||
image: mattermostdevelopment/mirrored-node:${NODE_VERSION_REQUIRED}
|
|
||||||
command: sh -c "npm install -g axios express client-oauth2@larkox/js-client-oauth2#e24e2eb5dfcbbbb3a59d095e831dbe0012b0ac49 && exec node webhook_serve.js"
|
|
||||||
environment:
|
|
||||||
NODE_PATH: /usr/local/lib/node_modules/
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "curl", "-s", "-o/dev/null", "127.0.0.1:3000"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 15s
|
|
||||||
retries: 12
|
|
||||||
working_dir: /cypress
|
|
||||||
volumes:
|
|
||||||
- "../../e2e-tests/cypress/:/cypress:ro"
|
|
||||||
networks:
|
|
||||||
default:
|
|
||||||
aliases:
|
|
||||||
- webhook-interactions
|
|
||||||
|
|
||||||
cypress:
|
|
||||||
image: "cypress/browsers:node-18.16.1-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1"
|
|
||||||
### Temorarily disabling this image, until both the amd64 and arm64 version are mirrored
|
|
||||||
# image: "mattermostdevelopment/mirrored-cypress-browsers-public:node-18.16.1-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1"
|
|
||||||
entrypoint: ["/bin/bash", "-c"]
|
|
||||||
command: ["until [ -f /var/run/mm_terminate ]; do sleep 5; done"]
|
|
||||||
env_file:
|
|
||||||
- "../../e2e-tests/.ci/.env.dashboard"
|
|
||||||
- "../../e2e-tests/.ci/.env.cypress"
|
|
||||||
environment:
|
|
||||||
REPO: "mattermost-server"
|
|
||||||
# Cypress configuration
|
|
||||||
HEADLESS: "true"
|
|
||||||
CYPRESS_baseUrl: "http://server:8065"
|
|
||||||
CYPRESS_dbConnection: "postgres://mmuser:mostest@postgres:5432/mattermost_test?sslmode=disable&connect_timeout=10"
|
|
||||||
CYPRESS_elasticsearchConnectionURL: "http://elasticsearch:9200"
|
|
||||||
CYPRESS_keycloakBaseUrl: "http://keycloak:8484"
|
|
||||||
CYPRESS_ldapServer: "openldap"
|
|
||||||
CYPRESS_minioS3Endpoint: "minio:9000"
|
|
||||||
CYPRESS_smtpUrl: "http://inbucket:9001"
|
|
||||||
CYPRESS_webhookBaseUrl: "http://webhook-interactions:3000"
|
|
||||||
CYPRESS_chromeWebSecurity: "false"
|
|
||||||
CYPRESS_firstTest: "true"
|
|
||||||
CYPRESS_resetBeforeTest: "true"
|
|
||||||
CYPRESS_runLDAPSync: "true"
|
|
||||||
CYPRESS_allowedUntrustedInternalConnections: "localhost webhook-interactions"
|
|
||||||
CYPRESS_serverEdition: E20
|
|
||||||
TM4J_ENABLE: "false"
|
|
||||||
# disable shared memory X11 affecting Cypress v4 and Chrome
|
|
||||||
# https://github.com/cypress-io/cypress-docker-images/issues/270
|
|
||||||
QT_X11_NO_MITSHM: "1"
|
|
||||||
_X11_NO_MITSHM: "1"
|
|
||||||
_MITSHM: "0"
|
|
||||||
# avoid too many progress messages
|
|
||||||
# https://github.com/cypress-io/cypress/issues/1243
|
|
||||||
CI: "1"
|
|
||||||
# Ensure we're independent from the global node environment
|
|
||||||
PATH: /cypress/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
||||||
ulimits:
|
|
||||||
nofile:
|
|
||||||
soft: 8096
|
|
||||||
hard: 1048576
|
|
||||||
working_dir: /cypress
|
|
||||||
volumes:
|
|
||||||
- "../../e2e-tests/cypress/:/cypress"
|
|
||||||
|
|
||||||
utils:
|
|
||||||
image: "mattermost/mattermost-build-server:20230904_golang-1.20.7"
|
|
||||||
entrypoint: ["/bin/bash", "-c"]
|
|
||||||
command: ["until [ -f /var/run/mm_terminate ]; do sleep 5; done"]
|
|
||||||
environment:
|
|
||||||
MM_SERVICEENVIRONMENT: "test" # Currently required for running the config_generator with the right environment
|
|
||||||
working_dir: "/opt/mattermost-server"
|
|
||||||
volumes:
|
|
||||||
- "../../:/opt/mattermost-server"
|
|
||||||
- "server-config:/opt/server-config"
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
server-config:
|
|
||||||
@@ -1,27 +1,34 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e -u -o pipefail
|
set -e -u -o pipefail
|
||||||
cd $(dirname $0)
|
cd "$(dirname "$0")"
|
||||||
. .e2erc
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
|
|
||||||
|
if [ "$TEST" != "cypress" ]; then
|
||||||
|
mme2e_log "Not applicable to TEST='$TEST'. For cypress only."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
# Install cypress dependencies
|
# Install cypress dependencies
|
||||||
mme2e_log "Prepare Cypress: install dependencies"
|
mme2e_log "Prepare Cypress: install dependencies"
|
||||||
${MME2E_DC_SERVER} exec -T -u 0 -- cypress bash -c "id $MME2E_UID || useradd -u $MME2E_UID -m nodeci" # Works around the node image's assumption that the app files are owned by user 1000
|
${MME2E_DC_SERVER} exec -T -u 0 -- cypress bash -c "id $MME2E_UID || useradd -u $MME2E_UID -m nodeci" # Works around the node image's assumption that the app files are owned by user 1000
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress npm i
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress npm i
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress cypress install
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress cypress install
|
||||||
|
|
||||||
# Populate cypress fixtures
|
# Populate cypress fixtures
|
||||||
mme2e_log "Prepare Cypress: populating fixtures"
|
mme2e_log "Prepare Cypress: populating fixtures"
|
||||||
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-gitlab/releases/download/v1.3.0/com.github.manland.mattermost-plugin-gitlab-1.3.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress tee tests/fixtures/com.github.manland.mattermost-plugin-gitlab-1.3.0.tar.gz >/dev/null
|
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-gitlab/releases/download/v1.3.0/com.github.manland.mattermost-plugin-gitlab-1.3.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress tee tests/fixtures/com.github.manland.mattermost-plugin-gitlab-1.3.0.tar.gz >/dev/null
|
||||||
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.9.0/com.mattermost.demo-plugin-0.9.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress tee tests/fixtures/com.mattermost.demo-plugin-0.9.0.tar.gz >/dev/null
|
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.9.0/com.mattermost.demo-plugin-0.9.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress tee tests/fixtures/com.mattermost.demo-plugin-0.9.0.tar.gz >/dev/null
|
||||||
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.8.0/com.mattermost.demo-plugin-0.8.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress tee tests/fixtures/com.mattermost.demo-plugin-0.8.0.tar.gz >/dev/null
|
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.8.0/com.mattermost.demo-plugin-0.8.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress tee tests/fixtures/com.mattermost.demo-plugin-0.8.0.tar.gz >/dev/null
|
||||||
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.8.0/com.mattermost.demo-plugin-0.8.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress tee tests/fixtures/com.mattermost.demo-plugin-0.8.0.tar.gz >/dev/null
|
${MME2E_DC_SERVER} exec -T -- server curl -L --silent https://github.com/mattermost/mattermost-plugin-demo/releases/download/v0.8.0/com.mattermost.demo-plugin-0.8.0.tar.gz | ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress tee tests/fixtures/com.mattermost.demo-plugin-0.8.0.tar.gz >/dev/null
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress tee tests/fixtures/keycloak.crt >/dev/null <../../server/build/docker/keycloak/keycloak.crt
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress tee tests/fixtures/keycloak.crt >/dev/null <../../server/build/docker/keycloak/keycloak.crt
|
||||||
|
|
||||||
|
if [[ $ENABLED_DOCKER_SERVICES == *"openldap"* ]]; then
|
||||||
|
${MME2E_DC_SERVER} exec -T -- openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest' <../../server/tests/test-data.ldif
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ENABLED_DOCKER_SERVICES == *"minio"* ]]; then
|
||||||
|
${MME2E_DC_SERVER} exec -T -- minio sh -c 'mkdir -p /data/mattermost-test'
|
||||||
|
fi
|
||||||
|
|
||||||
# Inject test data, prepare for E2E tests
|
|
||||||
mme2e_log "Prepare Server: injecting E2E test data"
|
|
||||||
${MME2E_DC_SERVER} exec -T -- server mmctl config set TeamSettings.MaxUsersPerTeam 100 --local
|
|
||||||
${MME2E_DC_SERVER} exec -T -- server mmctl sampledata -u 60 --local
|
|
||||||
${MME2E_DC_SERVER} exec -T -- openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest' <../../server/tests/test-data.ldif
|
|
||||||
${MME2E_DC_SERVER} exec -T -- minio sh -c 'mkdir -p /data/mattermost-test'
|
|
||||||
mme2e_log "Mattermost is running and ready for E2E testing"
|
mme2e_log "Mattermost is running and ready for E2E testing"
|
||||||
mme2e_log "You can use the test data credentials for logging in (username=sysadmin password=Sys@dmin-sample1)"
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e -u -o pipefail
|
set -e -u -o pipefail
|
||||||
cd $(dirname $0)
|
cd "$(dirname "$0")"
|
||||||
. .e2erc
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
|
|
||||||
# Set required variables
|
# Set required variables
|
||||||
TEST_FILTER_DEFAULT='--stage=@prod --group=@smoke'
|
TEST_FILTER_DEFAULT='--stage=@prod --group=@smoke'
|
||||||
@@ -9,8 +10,8 @@ TEST_FILTER=${TEST_FILTER:-$TEST_FILTER_DEFAULT}
|
|||||||
|
|
||||||
# Print run information
|
# Print run information
|
||||||
mme2e_log "Printing Cypress container informations"
|
mme2e_log "Printing Cypress container informations"
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress node -p 'module.paths'
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress node -p 'module.paths'
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress bash <<"EOF"
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<"EOF"
|
||||||
cat <<INNEREOF
|
cat <<INNEREOF
|
||||||
node version: $(node -v)
|
node version: $(node -v)
|
||||||
npm version: $(npm -v)
|
npm version: $(npm -v)
|
||||||
@@ -23,7 +24,7 @@ EOF
|
|||||||
|
|
||||||
# Initialize cypress report directory
|
# Initialize cypress report directory
|
||||||
mme2e_log "Prepare Cypress: clean and initialize report and logs directory"
|
mme2e_log "Prepare Cypress: clean and initialize report and logs directory"
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress bash <<EOF
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash <<EOF
|
||||||
rm -rf logs results
|
rm -rf logs results
|
||||||
mkdir -p logs
|
mkdir -p logs
|
||||||
mkdir -p results/junit
|
mkdir -p results/junit
|
||||||
@@ -33,13 +34,16 @@ EOF
|
|||||||
|
|
||||||
# Run cypress test
|
# Run cypress test
|
||||||
# No need to collect its exit status: if it's nonzero, this script will terminate since we use '-e'
|
# No need to collect its exit status: if it's nonzero, this script will terminate since we use '-e'
|
||||||
if ${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress bash -c '[ -n "${AUTOMATION_DASHBOARD_URL}" ]'; then
|
# shellcheck disable=SC2016
|
||||||
mme2e_log "AUTOMATION_DASHBOARD_URL is set. Using run_test_cycle.js for the cypress run"
|
if ${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress bash -c '[ -n "${AUTOMATION_DASHBOARD_URL}" ]'; then
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress node --trace-warnings generate_test_cycle.js ${TEST_FILTER}
|
mme2e_log "AUTOMATION_DASHBOARD_URL is set. Using run_test_cycle.js for the cypress run"
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress node run_test_cycle.js | tee ../cypress/logs/cypress.log
|
# shellcheck disable=SC2086
|
||||||
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress node --trace-warnings generate_test_cycle.js $TEST_FILTER
|
||||||
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress node run_test_cycle.js | tee ../cypress/logs/cypress.log
|
||||||
else
|
else
|
||||||
mme2e_log "AUTOMATION_DASHBOARD_URL is unset. Using run_tests.js for the cypress run"
|
mme2e_log "AUTOMATION_DASHBOARD_URL is unset. Using run_tests.js for the cypress run"
|
||||||
${MME2E_DC_SERVER} exec -T -u $MME2E_UID -- cypress node run_tests.js ${TEST_FILTER} | tee ../cypress/logs/cypress.log
|
# shellcheck disable=SC2086
|
||||||
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- cypress node run_tests.js $TEST_FILTER | tee ../cypress/logs/cypress.log
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Collect server logs
|
# Collect server logs
|
||||||
|
|||||||
28
e2e-tests/.ci/server.run_playwright.sh
Исполняемый файл
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e -u -o pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
|
|
||||||
|
# Initialize Playwright report directory
|
||||||
|
mme2e_log "Prepare Playwright: clean and initialize report and logs directory"
|
||||||
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash <<EOF
|
||||||
|
cd e2e-tests/playwright
|
||||||
|
rm -rf logs playwright-report test-results storage_state
|
||||||
|
mkdir -p logs
|
||||||
|
touch logs/mattermost.log
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Install Playwright dependencies
|
||||||
|
mme2e_log "Prepare Playwright: install dependencies"
|
||||||
|
${MME2E_DC_SERVER} exec -T -u "$MME2E_UID" -- playwright bash -c "cd e2e-tests/playwright && rm -rf node_modules && npm install --cache /tmp/empty-cache"
|
||||||
|
|
||||||
|
# Enable next line to debug Playwright
|
||||||
|
# export DEBUG=pw:protocol,pw:browser,pw:api
|
||||||
|
|
||||||
|
# Run Playwright test
|
||||||
|
# Note: Run on chrome but eventually will enable to all projects which include firefox and ipad.
|
||||||
|
${MME2E_DC_SERVER} exec -i -u "$MME2E_UID" -- playwright bash -c "cd e2e-tests/playwright && npm run test -- --project=chrome" | tee ../playwright/logs/playwright.log
|
||||||
|
|
||||||
|
# Collect server logs
|
||||||
|
${MME2E_DC_SERVER} logs --no-log-prefix -- server >../playwright/logs/mattermost.log 2>&1
|
||||||
@@ -1,31 +1,48 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e -u -o pipefail
|
set -e -u -o pipefail
|
||||||
cd $(dirname $0)
|
cd "$(dirname "$0")"
|
||||||
. .e2erc
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
|
|
||||||
export BRANCH_DEFAULT=$(git branch --show-current)
|
if [ "$TEST" != "cypress" ] && [ "$TEST" != "playwright" ] && [ "$TEST" != "server" ]; then
|
||||||
|
mme2e_log "Invalid TEST='$TEST', expected: 'cypress', 'playwright' or 'server'"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRANCH_DEFAULT=$(git branch --show-current)
|
||||||
export BRANCH=${BRANCH:-$BRANCH_DEFAULT}
|
export BRANCH=${BRANCH:-$BRANCH_DEFAULT}
|
||||||
export BUILD_ID_DEFAULT=$(date +%s)
|
BUILD_ID_DEFAULT=$(date +%s)
|
||||||
export BUILD_ID=${BUILD_ID:-$BUILD_ID_DEFAULT}
|
export BUILD_ID=${BUILD_ID:-$BUILD_ID_DEFAULT}
|
||||||
export CI_BASE_URL="${CI_BASE_URL:-localhost}"
|
export CI_BASE_URL="${CI_BASE_URL:-localhost}"
|
||||||
|
export SITE_URL="${SITE_URL:-http://server:8065}"
|
||||||
|
|
||||||
# Cleanup old containers, if any
|
# Cleanup old containers, if any
|
||||||
mme2e_log "Stopping leftover E2E containers, if any are running"
|
mme2e_log "Stopping leftover E2E containers, if any are running"
|
||||||
${MME2E_DC_SERVER} down -v
|
${MME2E_DC_SERVER} down -v --remove-orphans
|
||||||
|
|
||||||
# Generate .env.server
|
# Generate .env.server
|
||||||
mme2e_log "Generating .env.server"
|
mme2e_log "Generating .env.server"
|
||||||
|
if [ "$SERVER" = "cloud" ]; then
|
||||||
|
export "MM_NOTIFY_ADMIN_COOL_OFF_DAYS=0.00000001"
|
||||||
|
export 'MM_FEATUREFLAGS_ANNUALSUBSCRIPTION="true"'
|
||||||
|
fi
|
||||||
mme2e_generate_envfile_from_var_names >.env.server <<EOF
|
mme2e_generate_envfile_from_var_names >.env.server <<EOF
|
||||||
MM_LICENSE
|
MM_ELASTICSEARCHSETTINGS_CONNECTIONURL
|
||||||
|
MM_LDAPSETTINGS_LDAPSERVER
|
||||||
|
MM_NOTIFY_ADMIN_COOL_OFF_DAYS
|
||||||
|
MM_FEATUREFLAGS_ANNUALSUBSCRIPTION
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
envarr=$(echo ${MM_ENV:-} | tr "," "\n")
|
envarr=$(echo ${MM_ENV:-} | tr "," "\n")
|
||||||
for env in $envarr; do
|
for env in $envarr; do
|
||||||
echo "$env" >> .env.server
|
echo "$env" >>.env.server
|
||||||
done
|
done
|
||||||
|
|
||||||
# Generate .env.cypress
|
if [ "$TEST" = "cypress" ]; then
|
||||||
mme2e_log "Generating .env.cypress"
|
# Generate .env.cypress
|
||||||
mme2e_generate_envfile_from_var_names >.env.cypress <<EOF
|
mme2e_log "Cypress: Generating .env.cypress"
|
||||||
|
mme2e_generate_envfile_from_var_names >.env.cypress <<EOF
|
||||||
BRANCH
|
BRANCH
|
||||||
BUILD_ID
|
BUILD_ID
|
||||||
CI_BASE_URL
|
CI_BASE_URL
|
||||||
@@ -34,30 +51,47 @@ AUTOMATION_DASHBOARD_URL
|
|||||||
AUTOMATION_DASHBOARD_TOKEN
|
AUTOMATION_DASHBOARD_TOKEN
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# Additional variables to .env.cypress
|
||||||
|
if [[ $ENABLED_DOCKER_SERVICES == *"openldap"* ]]; then
|
||||||
|
echo "CYPRESS_ldapServer=openldap" >>.env.cypress
|
||||||
|
echo "CYPRESS_runLDAPSync=true" >>.env.cypress
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ENABLED_DOCKER_SERVICES == *"minio"* ]]; then
|
||||||
|
echo "CYPRESS_minioS3Endpoint=minio:9000" >>.env.cypress
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ENABLED_DOCKER_SERVICES == *"keycloak"* ]]; then
|
||||||
|
echo "CYPRESS_keycloakBaseUrl=http://keycloak:8484" >>.env.cypress
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ENABLED_DOCKER_SERVICES == *"elasticsearch"* ]]; then
|
||||||
|
echo "CYPRESS_elasticsearchConnectionURL=http://elasticsearch:9200" >>.env.cypress
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$SERVER" = "cloud" ]; then
|
||||||
|
echo "CYPRESS_serverEdition=Cloud" >>.env.cypress
|
||||||
|
else
|
||||||
|
echo "CYPRESS_serverEdition=E20" >>.env.cypress
|
||||||
|
fi
|
||||||
|
elif [ "$TEST" = "playwright" ]; then
|
||||||
|
# Generate .env.playwright
|
||||||
|
mme2e_log "Playwright: Generating .env.playwright"
|
||||||
|
mme2e_generate_envfile_from_var_names >.env.playwright <<EOF
|
||||||
|
BRANCH
|
||||||
|
BUILD_ID
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
mme2e_log "Preparing server setup only"
|
||||||
|
fi
|
||||||
|
|
||||||
# Wait for the required server image
|
# Wait for the required server image
|
||||||
mme2e_log "Waiting for server image to be available"
|
mme2e_log "Waiting for server image to be available"
|
||||||
mme2e_wait_image ${SERVER_IMAGE} 30 60
|
mme2e_wait_image "$SERVER_IMAGE" 30 60
|
||||||
|
|
||||||
# Create the containers and generate the server config
|
|
||||||
mme2e_log "Creating E2E containers and generating server config"
|
|
||||||
${MME2E_DC_SERVER} create
|
|
||||||
${MME2E_DC_SERVER} up -d -- utils
|
|
||||||
${MME2E_DC_SERVER} exec -T -w /opt/mattermost-server/server -- utils bash <<EOF
|
|
||||||
OUTPUT_CONFIG=/tmp/config_generated.json go run scripts/config_generator/main.go
|
|
||||||
jq "
|
|
||||||
.ServiceSettings.SiteURL=\"http://server:8065\"
|
|
||||||
| .PluginSettings.Enable=true
|
|
||||||
| .PluginSettings.EnableUploads=true
|
|
||||||
| .PluginSettings.AutomaticPrepackagedPlugins=true
|
|
||||||
| .TeamSettings.EnableOpenServer=true
|
|
||||||
| .ElasticsearchSettings.ConnectionURL=\"http://elasticsearch:9200\"
|
|
||||||
" </tmp/config_generated.json >/opt/server-config/config.json
|
|
||||||
echo "### Generated server config:"
|
|
||||||
cat /opt/server-config/config.json
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Launch mattermost-server, and wait for it to be healthy
|
# Launch mattermost-server, and wait for it to be healthy
|
||||||
mme2e_log "Starting E2E containers"
|
mme2e_log "Starting E2E containers"
|
||||||
|
${MME2E_DC_SERVER} create
|
||||||
${MME2E_DC_SERVER} up -d
|
${MME2E_DC_SERVER} up -d
|
||||||
if ! mme2e_wait_service_healthy server 60 10; then
|
if ! mme2e_wait_service_healthy server 60 10; then
|
||||||
mme2e_log "Mattermost container not healthy, retry attempts exhausted. Giving up." >&2
|
mme2e_log "Mattermost container not healthy, retry attempts exhausted. Giving up." >&2
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e -u -o pipefail
|
set -e -u -o pipefail
|
||||||
cd $(dirname $0)
|
cd "$(dirname "$0")"
|
||||||
. .e2erc
|
. .e2erc
|
||||||
|
. .e2erc_setup
|
||||||
|
|
||||||
mme2e_log "Stopping E2E containers"
|
mme2e_log "Stopping E2E containers"
|
||||||
${MME2E_DC_SERVER} down -v
|
${MME2E_DC_SERVER} down -v --remove-orphans
|
||||||
|
|||||||
3
e2e-tests/.gitignore
поставляемый
@@ -9,3 +9,6 @@
|
|||||||
|
|
||||||
# node
|
# node
|
||||||
*.lock
|
*.lock
|
||||||
|
|
||||||
|
# dev/ci
|
||||||
|
.ci/docker-compose.server.override.yml
|
||||||
|
|||||||
@@ -1,17 +1,39 @@
|
|||||||
|
# Load environment variables from .env file
|
||||||
|
-include .ci/.env
|
||||||
|
export
|
||||||
|
|
||||||
SHELL := /bin/bash
|
SHELL := /bin/bash
|
||||||
|
TEST ?= cypress
|
||||||
|
SERVER ?= self-hosted
|
||||||
|
ENABLED_DOCKER_SERVICES ?= postgres inbucket
|
||||||
|
|
||||||
|
define log_message
|
||||||
|
@echo "[$(shell date '+%Y-%m-%dT%H:%M:%S%Z')]$1"
|
||||||
|
endef
|
||||||
|
|
||||||
.PHONY: all run stop
|
.PHONY: all run stop
|
||||||
all: run
|
all: run
|
||||||
run: start-server prepare-server run-cypress
|
run: info generate-docker-compose create-cloud-customer start-server-test prepare-server run-test delete-cloud-customer
|
||||||
stop: stop-server stop-dashboard clean-env-placeholders
|
stop: stop-server stop-dashboard clean-env-placeholders
|
||||||
|
|
||||||
.PHONY: start-server prepare-server run-cypress stop-server restart-server
|
.PHONY: info generate-docker-compose start-server-test run-test start-server prepare-server stop-server restart-server
|
||||||
start-server:
|
generate-docker-compose:
|
||||||
|
bash ./.ci/docker_compose_generator.sh
|
||||||
|
start-server-test:
|
||||||
bash ./.ci/server.start.sh
|
bash ./.ci/server.start.sh
|
||||||
prepare-server:
|
prepare-server:
|
||||||
bash ./.ci/server.prepare.sh
|
bash ./.ci/server.prepare.sh
|
||||||
run-cypress:
|
run-test:
|
||||||
bash ./.ci/server.run_cypress.sh
|
@if [ "$(TEST)" = "cypress" ]; then bash ./.ci/server.run_cypress.sh; \
|
||||||
|
elif [ "$(TEST)" = "playwright" ]; then bash ./.ci/server.run_playwright.sh; \
|
||||||
|
else $(call log_message, "TEST: $(TEST)"); fi
|
||||||
|
info:
|
||||||
|
$(call log_message, "TEST: $(TEST)")
|
||||||
|
$(call log_message, "SERVER: $(SERVER)")
|
||||||
|
$(call log_message, "ENABLED_DOCKER_SERVICES: $(ENABLED_DOCKER_SERVICES)")
|
||||||
|
|
||||||
|
start-server: generate-docker-compose
|
||||||
|
TEST=server bash ./.ci/server.start.sh
|
||||||
stop-server:
|
stop-server:
|
||||||
bash ./.ci/server.stop.sh
|
bash ./.ci/server.stop.sh
|
||||||
restart-server: stop-server start-server
|
restart-server: stop-server start-server
|
||||||
@@ -26,5 +48,29 @@ stop-dashboard:
|
|||||||
print-env-placeholders:
|
print-env-placeholders:
|
||||||
find .ci -maxdepth 1 -name '.env.*' | xargs --verbose -l cat
|
find .ci -maxdepth 1 -name '.env.*' | xargs --verbose -l cat
|
||||||
clean-env-placeholders:
|
clean-env-placeholders:
|
||||||
git reset .ci/.env.{cypress,server,dashboard}
|
git reset .ci/.env.{cypress,server,server.cloud,dashboard}
|
||||||
git checkout .ci/.env.{cypress,server,dashboard}
|
git checkout .ci/.env.{cypress,server,server.cloud,dashboard}
|
||||||
|
|
||||||
|
.PHONY: create-cloud-customer delete-cloud-customer
|
||||||
|
create-cloud-customer:
|
||||||
|
@if [ "$(SERVER)" = "cloud" ]; then bash ./.ci/server.cloud_customer_create.sh; fi
|
||||||
|
delete-cloud-customer:
|
||||||
|
@if [ "$(SERVER)" = "cloud" ]; then bash ./.ci/server.cloud_customer_delete.sh; fi
|
||||||
|
|
||||||
|
.PHONY: prettier fmt-ci
|
||||||
|
# Install with https://webinstall.dev/shellcheck/
|
||||||
|
HAVE_SHELLCHECK ?= $(shell command -v shellcheck >/dev/null 2>&1 && echo yes)
|
||||||
|
|
||||||
|
# Install with https://webinstall.dev/shfmt/
|
||||||
|
HAVE_SHFMT ?= $(shell command -v shfmt >/dev/null 2>&1 && echo yes)
|
||||||
|
|
||||||
|
prettier:
|
||||||
|
# formats yaml files
|
||||||
|
npx prettier ./.ci "!./.ci/dashboard" --write --cache
|
||||||
|
fmt-ci: prettier
|
||||||
|
ifeq ($(HAVE_SHFMT),yes)
|
||||||
|
shfmt -w -s -i 2 ./.ci/*.sh
|
||||||
|
endif
|
||||||
|
ifeq ($(HAVE_SHELLCHECK),yes)
|
||||||
|
shellcheck ./.ci/*.sh ./.ci/.e2erc*
|
||||||
|
endif
|
||||||
|
|||||||
@@ -17,15 +17,34 @@ Instructions, tl;dr: create a local branch with your E2E test changes, then open
|
|||||||
Instructions, detailed:
|
Instructions, detailed:
|
||||||
1. (optional, undefined variables are set to sane defaults) Create the `.ci/env` file, and populate it with the variables you need out of the following list:
|
1. (optional, undefined variables are set to sane defaults) Create the `.ci/env` file, and populate it with the variables you need out of the following list:
|
||||||
* The following variables will be passed over to the server container: `MM_LICENSE` (no enterprise features will be available if this is unset), and the exploded `MM_ENV` (a comma-separated list of env var specifications)
|
* The following variables will be passed over to the server container: `MM_LICENSE` (no enterprise features will be available if this is unset), and the exploded `MM_ENV` (a comma-separated list of env var specifications)
|
||||||
|
* `TEST` either `cypress` (default) or `playwright`.
|
||||||
|
* `SERVER` either `self-hosted` (default) or `cloud`.
|
||||||
|
* The `ENABLED_DOCKER_SERVICES` is by default set to `postgres` and `inbucket` for smoke tests purpose, and for lightweight and faster start up time. Depending on the test requirement being worked on, you may want to override as needed, as such:
|
||||||
|
* Cypress full tests require full services with `postgres`, `inbucket`, `minio`, `openldap`, `elasticsearch` and `keycloak`.
|
||||||
|
* Cypress smoke tests require `postgres` and `inbucket` only.
|
||||||
|
* Playwright full tests require `postgres` and `inbucket` only.
|
||||||
* The following variables will be passed over to the cypress container: `BRANCH`, `BUILD_ID`, `CI_BASE_URL`, `BROWSER`, `AUTOMATION_DASHBOARD_URL` and `AUTOMATION_DASHBOARD_TOKEN`
|
* The following variables will be passed over to the cypress container: `BRANCH`, `BUILD_ID`, `CI_BASE_URL`, `BROWSER`, `AUTOMATION_DASHBOARD_URL` and `AUTOMATION_DASHBOARD_TOKEN`
|
||||||
* The `SERVER_IMAGE` variable can also be set, if you want to select a custom mattermost-server image. If not specified, the value of the `SERVER_IMAGE_DEFAULT` variable defined in file `.ci/.e2erc` is used.
|
* The `SERVER_IMAGE` variable can also be set, if you want to select a custom mattermost-server image. If not specified, the value of the `SERVER_IMAGE_DEFAULT` variable defined in file `.ci/.e2erc` is used.
|
||||||
* The `TEST_FILTER` variable can also be set, to customize which tests you want cypress to run. If not specified, only the smoke tests will run. Please check the `e2e-tests/cypress/run_tests.js` file for details about its format.
|
* The `TEST_FILTER` variable can also be set, to customize which tests you want cypress to run. If not specified, only the smoke tests will run. Please check the `e2e-tests/cypress/run_tests.js` file for details about its format.
|
||||||
|
* Set `CWS_URL` when spinning up a cloud-like test server that communicates with a test instance of customer web server.
|
||||||
2. (optional) `make start-dashboard`: start the automation-dashboard in the background
|
2. (optional) `make start-dashboard`: start the automation-dashboard in the background
|
||||||
* This also sets the `AUTOMATION_DASHBOARD_URL` and `AUTOMATION_DASHBOARD_TOKEN` variables for the cypress container
|
* This also sets the `AUTOMATION_DASHBOARD_URL` and `AUTOMATION_DASHBOARD_TOKEN` variables for the cypress container
|
||||||
* Note that if you run the dashboard locally, but also specify other `AUTOMATION_DASHBOARD_*` variables in your `.ci/env` file, the latter variables will take precedence
|
* Note that if you run the dashboard locally, but also specify other `AUTOMATION_DASHBOARD_*` variables in your `.ci/env` file, the latter variables will take precedence.
|
||||||
3. `make`: start and prepare the server, then run the cypress tests
|
* The dashboard is used for orchestrating specs with parallel test run and is typically used in CI.
|
||||||
* You can track the progress of the run in the `http://localhost:4000/cycles` dashboard, if you launched it locally
|
* Only Cypress is currently using the dashboard; Playwright is not.
|
||||||
4. `make stop`: tears down the server (and the dashboard, if running), then cleans up the env placeholder files
|
3. Run the following commands to run with cypress:
|
||||||
|
* `TEST=cypress make`: start and prepare the server, then run the cypress tests against self-hosted test server.
|
||||||
|
* `TEST=cypress SERVER=cloud make`: create test customer for cloud, start and prepare the server, run the cypress tests against cloud-like test server, then delete the test customer. When anticipating a licensed test server, make sure the loaded license via `MM_LICENSE` is for cloud.
|
||||||
|
* When anticipating a licensed test server, make sure the loaded license via `MM_LICENSE` does match either for self-hosted or cloud.
|
||||||
|
* `CWS_URL` is required to be set for cloud test.
|
||||||
|
* You can track the progress of the run in the `http://localhost:4000/cycles` dashboard, if you launched it locally.
|
||||||
|
4. Run the following commands to run with playwright:
|
||||||
|
* `TEST=playwright make`: start and prepare the server, then run the playwright tests against self-hosted test server.
|
||||||
|
* `TEST=playwright SERVER=cloud `: generate test customer for cloud, start and prepare the server, run the playwright tests against cloud-like test server, then delete the initially generated test customer.
|
||||||
|
* When anticipating a licensed test server, make sure the loaded license via `MM_LICENSE` does match either for self-hosted or cloud.
|
||||||
|
* `CWS_URL` is required to be set for cloud test.
|
||||||
|
5. `TEST=server make`: starts local server.
|
||||||
|
6. `make stop`: tears down the server (and the dashboard, if running), then cleans up the env placeholder files.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Setting a variable in `.ci/env` is functionally equivalent to exporting variables in your current shell's environment, before invoking the makefile.
|
- Setting a variable in `.ci/env` is functionally equivalent to exporting variables in your current shell's environment, before invoking the makefile.
|
||||||
@@ -34,5 +53,8 @@ Notes:
|
|||||||
- If you restart the dashboard at any point, you must also restart the server containers, so that it picks up the new IP of the dashboard from the newly generated `.env.dashboard` file
|
- If you restart the dashboard at any point, you must also restart the server containers, so that it picks up the new IP of the dashboard from the newly generated `.env.dashboard` file
|
||||||
- If you started the dashboard locally in the past, but want to point to another dashboard later, you can run `make clean-env-placeholders` to remove references to the local dashboard (you'll likely need to restart the server)
|
- If you started the dashboard locally in the past, but want to point to another dashboard later, you can run `make clean-env-placeholders` to remove references to the local dashboard (you'll likely need to restart the server)
|
||||||
- If new variables need to be passed to any of the containers:
|
- If new variables need to be passed to any of the containers:
|
||||||
* If their value is fixed (e.g. a static server configuration): these may be simply added to the `.ci/server.override.yml` file, to the appropriate container.
|
* If their value is fixed (e.g. a static server configuration), these may be simply added to the `docker_compose_generator.sh` file, to the appropriate container.
|
||||||
* If you need to introduce variables that you want to control from `.ci/env`: you need to update the scripts under the `.ci/` dir, and configure them to write the new variables' values over to the appropriate `.env.*` file. In particular, avoid defining variables that depend on other variables within the docker-compose override files: this is to ensure uniformity in their availability, and simplifies the question of what container has access to which variable considerably.
|
* If you need to introduce variables that you want to control from `.ci/env`: you need to update the scripts under the `.ci/` dir, and configure them to write the new variables' values over to the appropriate `.env.*` file. In particular, avoid defining variables that depend on other variables within the docker-compose override files: this is to ensure uniformity in their availability, and simplifies the question of what container has access to which variable considerably.
|
||||||
|
|
||||||
|
##### For code changes:
|
||||||
|
* `make fmt-ci` to format and check yaml files and shell scripts.
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ npm run test
|
|||||||
Change to root directory, run docker container
|
Change to root directory, run docker container
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.38.0-jammy /bin/bash
|
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.38.1-jammy /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 2. Inside the docker container
|
#### 2. Inside the docker container
|
||||||
|
|||||||
46
e2e-tests/playwright/package-lock.json
сгенерированный
@@ -10,7 +10,7 @@
|
|||||||
"@axe-core/playwright": "4.7.3",
|
"@axe-core/playwright": "4.7.3",
|
||||||
"@percy/cli": "1.27.1",
|
"@percy/cli": "1.27.1",
|
||||||
"@percy/playwright": "1.0.4",
|
"@percy/playwright": "1.0.4",
|
||||||
"@playwright/test": "1.38.0",
|
"@playwright/test": "1.38.1",
|
||||||
"async-wait-until": "2.0.12",
|
"async-wait-until": "2.0.12",
|
||||||
"axe-core": "4.8.1",
|
"axe-core": "4.8.1",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
@@ -488,11 +488,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.38.0",
|
"version": "1.38.1",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
|
||||||
"integrity": "sha512-xis/RXXsLxwThKnlIXouxmIvvT3zvQj1JE39GsNieMUrMpb3/GySHDh2j8itCG22qKVD4MYLBp7xB73cUW/UUw==",
|
"integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.38.0"
|
"playwright": "1.38.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@@ -2015,11 +2015,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.38.0",
|
"version": "1.38.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
|
||||||
"integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==",
|
"integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.38.0"
|
"playwright-core": "1.38.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@@ -2032,9 +2032,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.38.0",
|
"version": "1.38.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
|
||||||
"integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g==",
|
"integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
},
|
},
|
||||||
@@ -2781,11 +2781,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@playwright/test": {
|
"@playwright/test": {
|
||||||
"version": "1.38.0",
|
"version": "1.38.1",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.38.1.tgz",
|
||||||
"integrity": "sha512-xis/RXXsLxwThKnlIXouxmIvvT3zvQj1JE39GsNieMUrMpb3/GySHDh2j8itCG22qKVD4MYLBp7xB73cUW/UUw==",
|
"integrity": "sha512-NqRp8XMwj3AK+zKLbZShl0r/9wKgzqI/527bkptKXomtuo+dOjU9NdMASQ8DNC9z9zLOMbG53T4eihYr3XR+BQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"playwright": "1.38.0"
|
"playwright": "1.38.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/json-schema": {
|
"@types/json-schema": {
|
||||||
@@ -3846,18 +3846,18 @@
|
|||||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||||
},
|
},
|
||||||
"playwright": {
|
"playwright": {
|
||||||
"version": "1.38.0",
|
"version": "1.38.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.38.1.tgz",
|
||||||
"integrity": "sha512-fJGw+HO0YY+fU/F1N57DMO+TmXHTrmr905J05zwAQE9xkuwP/QLDk63rVhmyxh03dYnEhnRbsdbH9B0UVVRB3A==",
|
"integrity": "sha512-oRMSJmZrOu1FP5iu3UrCx8JEFRIMxLDM0c/3o4bpzU5Tz97BypefWf7TuTNPWeCe279TPal5RtPPZ+9lW/Qkow==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "2.3.2",
|
"fsevents": "2.3.2",
|
||||||
"playwright-core": "1.38.0"
|
"playwright-core": "1.38.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"playwright-core": {
|
"playwright-core": {
|
||||||
"version": "1.38.0",
|
"version": "1.38.1",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.0.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.38.1.tgz",
|
||||||
"integrity": "sha512-f8z1y8J9zvmHoEhKgspmCvOExF2XdcxMW8jNRuX4vkQFrzV4MlZ55iwb5QeyiFQgOFCUolXiRHgpjSEnqvO48g=="
|
"integrity": "sha512-tQqNFUKa3OfMf4b2jQ7aGLB8o9bS3bOY0yMEtldtC2+spf8QXG9zvXLTXUeRsoNuxEYMgLYR+NXfAa1rjKRcrg=="
|
||||||
},
|
},
|
||||||
"prelude-ls": {
|
"prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "cross-env PW_SNAPSHOT_ENABLE=true playwright test",
|
"test": "cross-env NODE_OPTIONS='--no-experimental-fetch' PW_SNAPSHOT_ENABLE=true playwright test",
|
||||||
"percy": "cross-env PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=iphone --project=ipad",
|
"test:update-snapshots": "cross-env NODE_OPTIONS='--no-experimental-fetch' PW_SNAPSHOT_ENABLE=true playwright test --update-snapshots",
|
||||||
|
"percy": "cross-env NODE_OPTIONS='--no-experimental-fetch' PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=ipad",
|
||||||
"tsc": "tsc -b",
|
"tsc": "tsc -b",
|
||||||
"lint": "eslint . --ext .js,.ts",
|
"lint": "eslint . --ext .js,.ts",
|
||||||
"prettier": "prettier . --check",
|
"prettier": "prettier . --check",
|
||||||
"prettier:fix": "prettier --write .",
|
"prettier:fix": "prettier --write .",
|
||||||
"check": "npm run tsc && npm run lint && npm run prettier",
|
"check": "npm run tsc && npm run lint && npm run prettier",
|
||||||
"codegen": "cross-env playwright codegen $PW_BASE_URL",
|
"codegen": "cross-env playwright codegen $PW_BASE_URL",
|
||||||
"playwright-ui": "playwright test --ui",
|
"playwright-ui": "cross-env NODE_OPTIONS='--no-experimental-fetch' playwright test --ui",
|
||||||
"test-slomo": "cross-env PW_SNAPSHOT_ENABLE=true PW_SLOWMO=1000 playwright test",
|
"test-slomo": "cross-env NODE_OPTIONS='--no-experimental-fetch' PW_SNAPSHOT_ENABLE=true PW_SLOWMO=1000 playwright test",
|
||||||
"show-report": "npx playwright show-report",
|
"show-report": "npx playwright show-report",
|
||||||
"postinstall": "npx playwright install"
|
"postinstall": "npx playwright install"
|
||||||
},
|
},
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
"@axe-core/playwright": "4.7.3",
|
"@axe-core/playwright": "4.7.3",
|
||||||
"@percy/cli": "1.27.1",
|
"@percy/cli": "1.27.1",
|
||||||
"@percy/playwright": "1.0.4",
|
"@percy/playwright": "1.0.4",
|
||||||
"@playwright/test": "1.38.0",
|
"@playwright/test": "1.38.1",
|
||||||
"async-wait-until": "2.0.12",
|
"async-wait-until": "2.0.12",
|
||||||
"axe-core": "4.8.1",
|
"axe-core": "4.8.1",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ export default defineConfig({
|
|||||||
workers: testConfig.workers,
|
workers: testConfig.workers,
|
||||||
expect: {
|
expect: {
|
||||||
timeout: duration.ten_sec,
|
timeout: duration.ten_sec,
|
||||||
toMatchSnapshot: {
|
toHaveScreenshot: {
|
||||||
threshold: 0.4,
|
threshold: 0.4,
|
||||||
maxDiffPixelRatio: 0.0001,
|
maxDiffPixelRatio: 0.0001,
|
||||||
|
animations: 'disabled',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
use: {
|
use: {
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
|
|||||||
},
|
},
|
||||||
TeamSettings: {
|
TeamSettings: {
|
||||||
EnableOpenServer: true,
|
EnableOpenServer: true,
|
||||||
|
MaxUsersPerTeam: 2000,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,14 +11,12 @@ export default class ChannelsCenterView {
|
|||||||
readonly container: Locator;
|
readonly container: Locator;
|
||||||
|
|
||||||
readonly header;
|
readonly header;
|
||||||
readonly headerMobile;
|
|
||||||
readonly postCreate;
|
readonly postCreate;
|
||||||
|
|
||||||
constructor(container: Locator) {
|
constructor(container: Locator) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
|
|
||||||
this.header = new components.ChannelsHeader(this.container.locator('.channel-header'));
|
this.header = new components.ChannelsHeader(this.container.locator('.channel-header'));
|
||||||
this.headerMobile = new components.ChannelsHeaderMobile(this.container.locator('.navbar'));
|
|
||||||
this.postCreate = new components.ChannelsPostCreate(container.getByTestId('post-create'));
|
this.postCreate = new components.ChannelsPostCreate(container.getByTestId('post-create'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See LICENSE.txt for license information.
|
|
||||||
|
|
||||||
import {expect, Locator} from '@playwright/test';
|
|
||||||
|
|
||||||
export default class ChannelsHeaderMobile {
|
|
||||||
readonly container: Locator;
|
|
||||||
|
|
||||||
constructor(container: Locator) {
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
async toBeVisible() {
|
|
||||||
await expect(this.container).toBeVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
async toggleSidebar() {
|
|
||||||
await this.container.getByRole('button', {name: 'Toggle sidebar Menu Icon'}).click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {ChannelsHeaderMobile};
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {ChannelsHeader} from './channels/header';
|
import {ChannelsHeader} from './channels/header';
|
||||||
import {ChannelsHeaderMobile} from './channels/header_mobile';
|
|
||||||
import {ChannelsAppBar} from './channels/app_bar';
|
import {ChannelsAppBar} from './channels/app_bar';
|
||||||
import {ChannelsPostCreate} from './channels/post_create';
|
import {ChannelsPostCreate} from './channels/post_create';
|
||||||
import {ChannelsPost} from './channels/post';
|
import {ChannelsPost} from './channels/post';
|
||||||
@@ -27,7 +26,6 @@ const components = {
|
|||||||
ChannelsSidebarRight,
|
ChannelsSidebarRight,
|
||||||
ChannelsAppBar,
|
ChannelsAppBar,
|
||||||
ChannelsHeader,
|
ChannelsHeader,
|
||||||
ChannelsHeaderMobile,
|
|
||||||
ChannelsPostCreate,
|
ChannelsPostCreate,
|
||||||
ChannelsPost,
|
ChannelsPost,
|
||||||
FindChannelsModal,
|
FindChannelsModal,
|
||||||
@@ -49,7 +47,6 @@ export {
|
|||||||
ChannelsSidebarRight,
|
ChannelsSidebarRight,
|
||||||
ChannelsAppBar,
|
ChannelsAppBar,
|
||||||
ChannelsHeader,
|
ChannelsHeader,
|
||||||
ChannelsHeaderMobile,
|
|
||||||
ChannelsPostCreate,
|
ChannelsPostCreate,
|
||||||
ChannelsPost,
|
ChannelsPost,
|
||||||
FindChannelsModal,
|
FindChannelsModal,
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ import chalk from 'chalk';
|
|||||||
import {expect, TestInfo} from '@playwright/test';
|
import {expect, TestInfo} from '@playwright/test';
|
||||||
|
|
||||||
import {illegalRe} from '@e2e-support/util';
|
import {illegalRe} from '@e2e-support/util';
|
||||||
import testConfig, {TestArgs} from '@e2e-test.config';
|
import testConfig from '@e2e-test.config';
|
||||||
|
import {ScreenshotOptions, TestArgs} from '@e2e-types';
|
||||||
|
|
||||||
import snapshotWithPercy from './percy';
|
import snapshotWithPercy from './percy';
|
||||||
|
|
||||||
export async function matchSnapshot(testInfo: TestInfo, testArgs: TestArgs) {
|
export async function matchSnapshot(testInfo: TestInfo, testArgs: TestArgs, options: ScreenshotOptions = {}) {
|
||||||
if (os.platform() !== 'linux') {
|
if (os.platform() !== 'linux') {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(
|
console.log(
|
||||||
@@ -25,7 +26,7 @@ export async function matchSnapshot(testInfo: TestInfo, testArgs: TestArgs) {
|
|||||||
if (testConfig.snapshotEnabled) {
|
if (testConfig.snapshotEnabled) {
|
||||||
// Visual test with built-in snapshot
|
// Visual test with built-in snapshot
|
||||||
const filename = testInfo.title.replace(illegalRe, '').replace(/\s/g, '-').trim().toLowerCase();
|
const filename = testInfo.title.replace(illegalRe, '').replace(/\s/g, '-').trim().toLowerCase();
|
||||||
expect(await testArgs.page.screenshot({fullPage: true})).toMatchSnapshot(`${filename}.png`);
|
await expect(testArgs.page).toHaveScreenshot(`${filename}.png`, {fullPage: true, ...options});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testConfig.percyEnabled) {
|
if (testConfig.percyEnabled) {
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
import percySnapshot from '@percy/playwright';
|
import percySnapshot from '@percy/playwright';
|
||||||
|
|
||||||
import testConfig, {TestArgs} from '@e2e-test.config';
|
import testConfig from '@e2e-test.config';
|
||||||
|
import {TestArgs} from '@e2e-types';
|
||||||
|
|
||||||
export default async function snapshotWithPercy(name: string, testArgs: TestArgs) {
|
export default async function snapshotWithPercy(name: string, testArgs: TestArgs) {
|
||||||
if (testArgs.browserName === 'chromium' && testConfig.percyEnabled && testArgs.viewport) {
|
if (testArgs.browserName === 'chromium' && testConfig.percyEnabled && testArgs.viewport) {
|
||||||
|
|||||||
@@ -1,40 +1,11 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import {Page, ViewportSize} from '@playwright/test';
|
|
||||||
import * as dotenv from 'dotenv';
|
import * as dotenv from 'dotenv';
|
||||||
|
|
||||||
|
import {TestConfig} from '@e2e-types';
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
export type TestArgs = {
|
|
||||||
page: Page;
|
|
||||||
browserName: string;
|
|
||||||
viewport?: ViewportSize | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TestConfig = {
|
|
||||||
// Server
|
|
||||||
baseURL: string;
|
|
||||||
adminUsername: string;
|
|
||||||
adminPassword: string;
|
|
||||||
adminEmail: string;
|
|
||||||
ensurePluginsInstalled: string[];
|
|
||||||
resetBeforeTest: boolean;
|
|
||||||
haClusterEnabled: boolean;
|
|
||||||
haClusterNodeCount: number;
|
|
||||||
haClusterName: string;
|
|
||||||
// CI
|
|
||||||
isCI: boolean;
|
|
||||||
// Playwright
|
|
||||||
headless: boolean;
|
|
||||||
slowMo: number;
|
|
||||||
workers: number;
|
|
||||||
// Visual tests
|
|
||||||
snapshotEnabled: boolean;
|
|
||||||
percyEnabled: boolean;
|
|
||||||
percyToken?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// All process.env should be defined here
|
// All process.env should be defined here
|
||||||
const config: TestConfig = {
|
const config: TestConfig = {
|
||||||
// Server
|
// Server
|
||||||
@@ -44,7 +15,7 @@ const config: TestConfig = {
|
|||||||
adminEmail: process.env.PW_ADMIN_EMAIL || 'sysadmin@sample.mattermost.com',
|
adminEmail: process.env.PW_ADMIN_EMAIL || 'sysadmin@sample.mattermost.com',
|
||||||
ensurePluginsInstalled:
|
ensurePluginsInstalled:
|
||||||
typeof process.env?.PW_ENSURE_PLUGINS_INSTALLED === 'string'
|
typeof process.env?.PW_ENSURE_PLUGINS_INSTALLED === 'string'
|
||||||
? process.env.PW_ENSURE_PLUGINS_INSTALLED.split(',')
|
? process.env.PW_ENSURE_PLUGINS_INSTALLED.split(',').filter((plugin) => Boolean(plugin))
|
||||||
: [],
|
: [],
|
||||||
haClusterEnabled: parseBool(process.env.PW_HA_CLUSTER_ENABLED, false),
|
haClusterEnabled: parseBool(process.env.PW_HA_CLUSTER_ENABLED, false),
|
||||||
haClusterNodeCount: parseNumber(process.env.PW_HA_CLUSTER_NODE_COUNT, 2),
|
haClusterNodeCount: parseNumber(process.env.PW_HA_CLUSTER_NODE_COUNT, 2),
|
||||||
|
|||||||
@@ -61,19 +61,19 @@ test('/login accessibility tab support', async ({pw, pages, page}) => {
|
|||||||
await loginPage.footer.termsLink.press('Tab');
|
await loginPage.footer.termsLink.press('Tab');
|
||||||
expect(await loginPage.footer.helpLink).toBeFocused();
|
expect(await loginPage.footer.helpLink).toBeFocused();
|
||||||
|
|
||||||
// * Should move focus to header logo after tab
|
// # Move focus to login input
|
||||||
await loginPage.footer.helpLink.press('Tab');
|
await loginPage.loginInput.focus();
|
||||||
expect(await loginPage.header.logo).toBeFocused();
|
expect(await loginPage.loginInput).toBeFocused();
|
||||||
|
|
||||||
// * Should move focus to create account link after tab
|
// * Should move focus to login body after shift+tab
|
||||||
await loginPage.header.logo.press('Tab');
|
await loginPage.loginInput.press('Shift+Tab');
|
||||||
expect(await loginPage.createAccountLink).toBeFocused();
|
|
||||||
|
|
||||||
// * Should move focus to create account link after tab
|
|
||||||
await loginPage.createAccountLink.press('Tab');
|
|
||||||
expect(await loginPage.bodyCard).toBeFocused();
|
expect(await loginPage.bodyCard).toBeFocused();
|
||||||
|
|
||||||
// * Then, should move focus to login body after tab
|
// * Should move focus to create account link after shift+tab
|
||||||
await loginPage.bodyCard.press('Tab');
|
await loginPage.bodyCard.press('Shift+Tab');
|
||||||
expect(await loginPage.loginInput).toBeFocused();
|
expect(await loginPage.createAccountLink).toBeFocused();
|
||||||
|
|
||||||
|
// * Should move focus to login body after tab
|
||||||
|
await loginPage.createAccountLink.press('Shift+Tab');
|
||||||
|
expect(await loginPage.header.logo).toBeFocused();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ test('/reset_password accessibility tab support', async ({pages, page}) => {
|
|||||||
await resetPasswordPage.footer.termsLink.press('Tab');
|
await resetPasswordPage.footer.termsLink.press('Tab');
|
||||||
expect(await resetPasswordPage.footer.helpLink).toBeFocused();
|
expect(await resetPasswordPage.footer.helpLink).toBeFocused();
|
||||||
|
|
||||||
// * Should move focus to header logo after tab
|
// # Move focus to email input
|
||||||
await resetPasswordPage.footer.helpLink.press('Tab');
|
await resetPasswordPage.emailInput.focus();
|
||||||
expect(await resetPasswordPage.header.backButton).toBeFocused();
|
|
||||||
|
|
||||||
// * Then, should move focus to email input after tab
|
|
||||||
await resetPasswordPage.header.backButton.press('Tab');
|
|
||||||
expect(await resetPasswordPage.emailInput).toBeFocused();
|
expect(await resetPasswordPage.emailInput).toBeFocused();
|
||||||
|
|
||||||
|
// * Should move focus to back button after shift+tab
|
||||||
|
await resetPasswordPage.emailInput.press('Shift+Tab');
|
||||||
|
expect(await resetPasswordPage.header.backButton).toBeFocused();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ test('/signup_user_complete accessibility quick check', async ({pages, page, axe
|
|||||||
expect(accessibilityScanResults.violations).toHaveLength(0);
|
expect(accessibilityScanResults.violations).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('/signup_user_complete accessibility tab support', async ({pages, page}) => {
|
test('/signup_user_complete accessibility tab support', async ({pages, page}, testInfo) => {
|
||||||
// # Go to reset password page
|
// # Go to reset password page
|
||||||
const signupPage = new pages.SignupPage(page);
|
const signupPage = new pages.SignupPage(page);
|
||||||
await signupPage.goto();
|
await signupPage.goto();
|
||||||
@@ -71,23 +71,41 @@ test('/signup_user_complete accessibility tab support', async ({pages, page}) =>
|
|||||||
await signupPage.footer.termsLink.press('Tab');
|
await signupPage.footer.termsLink.press('Tab');
|
||||||
expect(await signupPage.footer.helpLink).toBeFocused();
|
expect(await signupPage.footer.helpLink).toBeFocused();
|
||||||
|
|
||||||
// * Should move focus to header logo after tab
|
// # Move focus to email input
|
||||||
await signupPage.footer.helpLink.press('Tab');
|
await signupPage.emailInput.focus();
|
||||||
expect(await signupPage.header.logo).toBeFocused();
|
expect(await signupPage.emailInput).toBeFocused();
|
||||||
|
|
||||||
// * Should move focus to header back button after tab
|
// * Should move focus to sign up body after shift+tab
|
||||||
await signupPage.header.logo.press('Tab');
|
await signupPage.emailInput.press('Shift+Tab');
|
||||||
expect(await signupPage.header.backButton).toBeFocused();
|
|
||||||
|
|
||||||
// * Should move focus to log in link after tab
|
|
||||||
await signupPage.header.backButton.press('Tab');
|
|
||||||
expect(await signupPage.loginLink).toBeFocused();
|
|
||||||
|
|
||||||
// * Should move focus to sign up body after tab
|
|
||||||
await signupPage.loginLink.press('Tab');
|
|
||||||
expect(await signupPage.bodyCard).toBeFocused();
|
expect(await signupPage.bodyCard).toBeFocused();
|
||||||
|
|
||||||
// * Then, should move focus to email input after tab
|
// * Should move focus to sign up body after shift+tab
|
||||||
await signupPage.bodyCard.press('Tab');
|
await signupPage.emailInput.press('Shift+Tab');
|
||||||
expect(await signupPage.emailInput).toBeFocused();
|
expect(await signupPage.bodyCard).toBeFocused();
|
||||||
|
|
||||||
|
if (testInfo.project.name === 'ipad') {
|
||||||
|
// * Should move focus to header back button after shift+tab
|
||||||
|
await signupPage.bodyCard.press('Shift+Tab');
|
||||||
|
expect(await signupPage.header.backButton).toBeFocused();
|
||||||
|
|
||||||
|
// * Should move focus to log in link after shift+tab
|
||||||
|
await signupPage.header.backButton.press('Shift+Tab');
|
||||||
|
expect(await signupPage.loginLink).toBeFocused();
|
||||||
|
|
||||||
|
// * Should move focus to header logo after shift+tab
|
||||||
|
await signupPage.loginLink.press('Shift+Tab');
|
||||||
|
expect(await signupPage.header.logo).toBeFocused();
|
||||||
|
} else {
|
||||||
|
// * Should move focus to log in link after shift+tab
|
||||||
|
await signupPage.bodyCard.press('Shift+Tab');
|
||||||
|
expect(await signupPage.loginLink).toBeFocused();
|
||||||
|
|
||||||
|
// * Should move focus to header back button after shift+tab
|
||||||
|
await signupPage.loginLink.press('Shift+Tab');
|
||||||
|
expect(await signupPage.header.backButton).toBeFocused();
|
||||||
|
|
||||||
|
// * Should move focus to header logo after shift+tab
|
||||||
|
await signupPage.header.backButton.press('Shift+Tab');
|
||||||
|
expect(await signupPage.header.logo).toBeFocused();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ test('MM-T5424 Find channel search returns only 50 results when there are more t
|
|||||||
await channelsPage.toBeVisible();
|
await channelsPage.toBeVisible();
|
||||||
|
|
||||||
// # Click on "Find channel" and type "test_channel"
|
// # Click on "Find channel" and type "test_channel"
|
||||||
await channelsPage.centerView.headerMobile.toggleSidebar();
|
|
||||||
await channelsPage.sidebarLeft.findChannelButton.click();
|
await channelsPage.sidebarLeft.findChannelButton.click();
|
||||||
|
|
||||||
await channelsPage.findChannelsModal.toBeVisible();
|
await channelsPage.findChannelsModal.toBeVisible();
|
||||||
|
|||||||
|
До Ширина: | Высота: | Размер: 161 KiB После Ширина: | Высота: | Размер: 68 KiB |
|
До Ширина: | Высота: | Размер: 107 KiB После Ширина: | Высота: | Размер: 107 KiB |
|
До Ширина: | Высота: | Размер: 170 KiB После Ширина: | Высота: | Размер: 170 KiB |
|
До Ширина: | Высота: | Размер: 220 KiB После Ширина: | Высота: | Размер: 93 KiB |
|
До Ширина: | Высота: | Размер: 153 KiB После Ширина: | Высота: | Размер: 150 KiB |
|
До Ширина: | Высота: | Размер: 271 KiB После Ширина: | Высота: | Размер: 271 KiB |
|
До Ширина: | Высота: | Размер: 319 KiB После Ширина: | Высота: | Размер: 136 KiB |
|
До Ширина: | Высота: | Размер: 302 KiB После Ширина: | Высота: | Размер: 131 KiB |
|
До Ширина: | Высота: | Размер: 167 KiB После Ширина: | Высота: | Размер: 167 KiB |
|
До Ширина: | Высота: | Размер: 172 KiB После Ширина: | Высота: | Размер: 172 KiB |
|
До Ширина: | Высота: | Размер: 297 KiB После Ширина: | Высота: | Размер: 296 KiB |
|
До Ширина: | Высота: | Размер: 429 KiB После Ширина: | Высота: | Размер: 175 KiB |
|
До Ширина: | Высота: | Размер: 397 KiB После Ширина: | Высота: | Размер: 168 KiB |
@@ -10,7 +10,8 @@
|
|||||||
"@mattermost/client/*": ["../../webapp/platform/client/lib/*"],
|
"@mattermost/client/*": ["../../webapp/platform/client/lib/*"],
|
||||||
"@mattermost/types/*": ["../../webapp/platform/types/lib/*"],
|
"@mattermost/types/*": ["../../webapp/platform/types/lib/*"],
|
||||||
"@e2e-support/*": ["support/*"],
|
"@e2e-support/*": ["support/*"],
|
||||||
"@e2e-test.config": ["test.config.ts"]
|
"@e2e-test.config": ["test.config.ts"],
|
||||||
|
"@e2e-types": ["types.ts"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["./**/*"],
|
"include": ["./**/*"],
|
||||||
|
|||||||
134
e2e-tests/playwright/types.ts
Обычный файл
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
import {Locator, Page, ViewportSize} from '@playwright/test';
|
||||||
|
|
||||||
|
export type TestArgs = {
|
||||||
|
page: Page;
|
||||||
|
browserName: string;
|
||||||
|
viewport?: ViewportSize | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TestConfig = {
|
||||||
|
// Server
|
||||||
|
baseURL: string;
|
||||||
|
adminUsername: string;
|
||||||
|
adminPassword: string;
|
||||||
|
adminEmail: string;
|
||||||
|
ensurePluginsInstalled: string[];
|
||||||
|
resetBeforeTest: boolean;
|
||||||
|
haClusterEnabled: boolean;
|
||||||
|
haClusterNodeCount: number;
|
||||||
|
haClusterName: string;
|
||||||
|
// CI
|
||||||
|
isCI: boolean;
|
||||||
|
// Playwright
|
||||||
|
headless: boolean;
|
||||||
|
slowMo: number;
|
||||||
|
workers: number;
|
||||||
|
// Visual tests
|
||||||
|
snapshotEnabled: boolean;
|
||||||
|
percyEnabled: boolean;
|
||||||
|
percyToken?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Based on https://github.com/microsoft/playwright/blob/d6ec1ae3994f127e38b866a231a34efc6a4cac0d/packages/playwright/types/test.d.ts#L5692-L5759
|
||||||
|
export type ScreenshotOptions = {
|
||||||
|
/**
|
||||||
|
* When set to `"disabled"`, stops CSS animations, CSS transitions and Web Animations. Animations get different
|
||||||
|
* treatment depending on their duration:
|
||||||
|
* - finite animations are fast-forwarded to completion, so they'll fire `transitionend` event.
|
||||||
|
* - infinite animations are canceled to initial state, and then played over after the screenshot.
|
||||||
|
*
|
||||||
|
* Defaults to `"disabled"` that disables animations.
|
||||||
|
*/
|
||||||
|
animations?: 'disabled' | 'allow';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`, text caret behavior will not be
|
||||||
|
* changed. Defaults to `"hide"`.
|
||||||
|
*/
|
||||||
|
caret?: 'hide' | 'initial';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which specifies clipping of the resulting image.
|
||||||
|
*/
|
||||||
|
clip?: {
|
||||||
|
/**
|
||||||
|
* x-coordinate of top-left corner of clip area
|
||||||
|
*/
|
||||||
|
x: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* y-coordinate of top-left corner of clip area
|
||||||
|
*/
|
||||||
|
y: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* width of clipping area
|
||||||
|
*/
|
||||||
|
width: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* height of clipping area
|
||||||
|
*/
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When true, takes a screenshot of the full scrollable page, instead of the currently visible viewport. Defaults to
|
||||||
|
* `false`.
|
||||||
|
*/
|
||||||
|
fullPage?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify locators that should be masked when the screenshot is taken. Masked elements will be overlaid with a pink
|
||||||
|
* box `#FF00FF` (customized by `maskColor`) that completely covers its bounding box.
|
||||||
|
*/
|
||||||
|
mask?: Array<Locator>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the color of the overlay box for masked elements, in
|
||||||
|
* [CSS color format](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value). Default color is pink `#FF00FF`.
|
||||||
|
*/
|
||||||
|
maskColor?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An acceptable ratio of pixels that are different to the total amount of pixels, between `0` and `1`. Default is
|
||||||
|
* configurable with `TestConfig.expect`. Unset by default.
|
||||||
|
*/
|
||||||
|
maxDiffPixelRatio?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An acceptable amount of pixels that could be different. Default is configurable with `TestConfig.expect`. Unset by
|
||||||
|
* default.
|
||||||
|
*/
|
||||||
|
maxDiffPixels?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides default white background and allows capturing screenshots with transparency. Not applicable to `jpeg` images.
|
||||||
|
* Defaults to `false`.
|
||||||
|
*/
|
||||||
|
omitBackground?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When set to `"css"`, screenshot will have a single pixel per each css pixel on the page. For high-dpi devices, this
|
||||||
|
* will keep screenshots small. Using `"device"` option will produce a single pixel per each device pixel, so
|
||||||
|
* screenshots of high-dpi devices will be twice as large or even larger.
|
||||||
|
*
|
||||||
|
* Defaults to `"css"`.
|
||||||
|
*/
|
||||||
|
scale?: 'css' | 'device';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An acceptable perceived color difference in the [YIQ color space](https://en.wikipedia.org/wiki/YIQ) between the
|
||||||
|
* same pixel in compared images, between zero (strict) and one (lax), default is configurable with
|
||||||
|
* `TestConfig.expect`. Defaults to `0.2`.
|
||||||
|
*/
|
||||||
|
threshold?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to retry the assertion for in milliseconds. Defaults to `timeout` in `TestConfig.expect`.
|
||||||
|
*/
|
||||||
|
timeout?: number;
|
||||||
|
};
|
||||||
@@ -71,6 +71,7 @@ const AppBarPluginComponent = (props: PluginComponentProps) => {
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={iconUrl}
|
src={iconUrl}
|
||||||
|
alt={component.pluginId}
|
||||||
onLoad={onImageLoadComplete}
|
onLoad={onImageLoadComplete}
|
||||||
onError={onImageLoadError}
|
onError={onImageLoadError}
|
||||||
/>
|
/>
|
||||||
|
|||||||