Merge remote-tracking branch 'origin/master' into remote-users-not-count-for-license
114
.github/workflows/artifacts.yml
vendored
@ -4,15 +4,12 @@ on:
|
||||
workflows: ["Mattermost Build"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
upload-s3:
|
||||
name: cd/Upload artifacts to S3
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
REPO_NAME: ${{ github.event.repository.name }}
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: cd/Configure AWS
|
||||
uses: aws-actions/configure-aws-credentials@07c2f971bac433df982ccc261983ae443861db49 # v1-node16
|
||||
@ -28,89 +25,50 @@ jobs:
|
||||
workflow_conclusion: success
|
||||
name: server-dist-artifact
|
||||
path: server/dist
|
||||
# Get Branch name from calling workflow
|
||||
# Search for the string "pull" and replace it with "PR" in branch-name
|
||||
- name: cd/Get branch name
|
||||
run: echo "BRANCH_NAME=$(echo ${{ github.event.workflow_run.head_branch }} | sed 's/^pull\//PR-/g')" >> $GITHUB_ENV
|
||||
- name: cd/Upload artifacts to S3
|
||||
env:
|
||||
REPO_NAME: ${{ github.event.repository.name }}
|
||||
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||
run: |
|
||||
aws s3 cp server/dist/ s3://pr-builds.mattermost.com/$REPO_NAME/$BRANCH_NAME/ --acl public-read --cache-control "no-cache" --recursive --no-progress
|
||||
aws s3 cp server/dist/ s3://pr-builds.mattermost.com/$REPO_NAME/commit/${{ github.sha }}/ --acl public-read --cache-control "no-cache" --recursive --no-progress
|
||||
aws s3 cp server/dist/ s3://pr-builds.mattermost.com/$REPO_NAME/commit/$COMMIT_SHA/ --acl public-read --cache-control "no-cache" --recursive --no-progress
|
||||
|
||||
build-docker:
|
||||
name: cd/Build and push docker image
|
||||
needs: upload-s3
|
||||
env:
|
||||
REPO_NAME: ${{ github.event.repository.name }}
|
||||
runs-on: ubuntu-22.04
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: cd/Login to Docker Hub
|
||||
uses: docker/login-action@3da7dc6e2b31f99ef2cb9fb4c50fb0971e0d0139 # v2.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_DEV_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_DEV_TOKEN }}
|
||||
- name: cd/Download artifacts
|
||||
uses: dawidd6/action-download-artifact@0c49384d39ceb023b8040f480a25596fd6cf441b # v2.26.0
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
workflow_conclusion: success
|
||||
name: server-build-artifact
|
||||
path: server/build/
|
||||
- name: cd/Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@11e8a2e2910826a92412015c515187a2d6750279 # v2.4
|
||||
- name: cd/Docker build and push
|
||||
env:
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
run: |
|
||||
export TAG=$(echo "${{ github.event.pull_request.head.sha || github.sha }}" | cut -c1-7)
|
||||
cd server/build
|
||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
export MM_PACKAGE=https://pr-builds.mattermost.com/$REPO_NAME/commit/${{ github.sha }}/mattermost-team-linux-amd64.tar.gz
|
||||
docker buildx build --push --build-arg MM_PACKAGE=$MM_PACKAGE -t mattermostdevelopment/mm-te-test:${TAG} .
|
||||
# Temporary uploading also to mattermost/mm-te-test:${TAG} except mattermostdevelopment/mm-te-test:${TAG}
|
||||
# Context: https://community.mattermost.com/private-core/pl/3jzzxzfiji8hx833ewyuthzkjh
|
||||
build-docker-temp:
|
||||
name: cd/Build and push docker image
|
||||
needs: upload-s3
|
||||
env:
|
||||
REPO_NAME: ${{ github.event.repository.name }}
|
||||
runs-on: ubuntu-22.04
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: cd/Login to Docker Hub
|
||||
uses: docker/login-action@3da7dc6e2b31f99ef2cb9fb4c50fb0971e0d0139 # v2.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: cd/Download artifacts
|
||||
uses: dawidd6/action-download-artifact@0c49384d39ceb023b8040f480a25596fd6cf441b # v2.26.0
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
workflow_conclusion: success
|
||||
name: server-build-artifact
|
||||
path: server/build/
|
||||
- name: cd/Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@11e8a2e2910826a92412015c515187a2d6750279 # v2.4
|
||||
- name: cd/Docker build and push
|
||||
env:
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
run: |
|
||||
export TAG=$(echo "${{ github.event.pull_request.head.sha || github.sha }}" | cut -c1-7)
|
||||
cd server/build
|
||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
export MM_PACKAGE=https://pr-builds.mattermost.com/$REPO_NAME/commit/${{ github.sha }}/mattermost-team-linux-amd64.tar.gz
|
||||
docker buildx build --push --build-arg MM_PACKAGE=$MM_PACKAGE -t mattermost/mm-te-test:${TAG} .
|
||||
- name: cd/Login to Docker Hub
|
||||
uses: docker/login-action@3da7dc6e2b31f99ef2cb9fb4c50fb0971e0d0139 # v2.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_DEV_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_DEV_TOKEN }}
|
||||
- name: cd/Download artifacts
|
||||
uses: dawidd6/action-download-artifact@0c49384d39ceb023b8040f480a25596fd6cf441b # v2.26.0
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
workflow_conclusion: success
|
||||
name: server-build-artifact
|
||||
path: server/build/
|
||||
- name: cd/Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@11e8a2e2910826a92412015c515187a2d6750279 # v2.4
|
||||
- name: cd/Docker build and push
|
||||
env:
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
REPO_NAME: ${{ github.event.repository.name }}
|
||||
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||
run: |
|
||||
export TAG=$(echo "${{ github.event.pull_request.head.sha || github.event.workflow_run.head_sha }}" | cut -c1-7)
|
||||
cd server/build
|
||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
export MM_PACKAGE=https://pr-builds.mattermost.com/$REPO_NAME/commit/$COMMIT_SHA/mattermost-team-linux-amd64.tar.gz
|
||||
docker buildx build --push --build-arg MM_PACKAGE=$MM_PACKAGE -t mattermostdevelopment/mm-te-test:${TAG} .
|
||||
|
||||
sentry:
|
||||
name: Send build info to sentry
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.event.workflow_run.conclusion == 'success'
|
||||
github.event.workflow_run.event == 'push'
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.MM_SERVER_SENTRY_AUTH_TOKEN }}
|
||||
|
12
.github/workflows/channels-ci.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
- mono-repo*
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ !contains( github.ref , 'heads/ref/master') }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@ -83,6 +83,16 @@ jobs:
|
||||
npm run mmjstool -- i18n clean-empty --webapp-dir ./src --mobile-dir /tmp/fake-mobile-dir --check
|
||||
npm run mmjstool -- i18n check-empty-src --webapp-dir ./src --mobile-dir /tmp/fake-mobile-dir
|
||||
rm -rf tmp
|
||||
- name: ci/lint-boards
|
||||
working-directory: webapp/boards
|
||||
run: |
|
||||
npm run i18n-extract
|
||||
git --no-pager diff --exit-code i18n/en.json || (echo "Please run \"cd webapp/boards && npm run i18n-extract\" and commit the changes in webapp/boards/i18n/en.json." && exit 1)
|
||||
- name: ci/lint-playbooks
|
||||
working-directory: webapp/playbooks
|
||||
run: |
|
||||
npm run i18n-extract
|
||||
git --no-pager diff --exit-code i18n/en.json || (echo "Please run \"cd webapp/playbooks && npm run i18n-extract\" and commit the changes in webapp/playbooks/i18n/en.json." && exit 1)
|
||||
check-types:
|
||||
runs-on: ubuntu-22.04
|
||||
defaults:
|
||||
|
13
.github/workflows/ci.yml
vendored
@ -11,7 +11,7 @@ env:
|
||||
go-version: "1.19.5"
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ !contains( github.ref , 'heads/ref/master') }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
jobs:
|
||||
check-mocks:
|
||||
name: Check mocks
|
||||
@ -26,6 +26,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Generate mocks
|
||||
run: make mocks
|
||||
- name: Check mocks
|
||||
@ -43,6 +44,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Run go mod tidy
|
||||
run: make modules-tidy
|
||||
- name: Check modules
|
||||
@ -60,13 +62,14 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Run make-gen-serialized
|
||||
run: make gen-serialized
|
||||
- name: Check serialized
|
||||
run: if [[ -n $(git status --porcelain) ]]; then echo "Please update the serialized files using 'make gen-serialized'"; exit 1; fi
|
||||
check-mattermost-vet:
|
||||
name: Check style
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
runs-on: ubuntu-22.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: server
|
||||
@ -77,6 +80,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Reset config
|
||||
run: make config-reset
|
||||
- name: Run plugin-checker
|
||||
@ -106,6 +110,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Checkout mattermost-api-reference
|
||||
run: |
|
||||
cd ..
|
||||
@ -128,6 +133,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Generate work templates
|
||||
run: make generate-worktemplates
|
||||
- name: Check generated work templates
|
||||
@ -160,6 +166,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Generate store layers
|
||||
run: make store-layers
|
||||
- name: Check generated code
|
||||
@ -177,6 +184,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Generate app layers
|
||||
run: make app-layers
|
||||
- name: Check generated code
|
||||
@ -216,6 +224,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Build
|
||||
run: |
|
||||
make config-reset
|
||||
|
2
.github/workflows/codeql-analysis.yml
vendored
@ -2,7 +2,7 @@ name: "CodeQL"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ !contains( github.ref , 'heads/ref/master') }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
2
.github/workflows/e2e-tests-ci.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
- mono-repo*
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ !contains( github.ref , 'heads/ref/master') }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
159
.github/workflows/esrupgrade-common.yml
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
name: ESR Upgrade
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
db-dump-url:
|
||||
required: true
|
||||
type: string
|
||||
initial-version:
|
||||
required: true
|
||||
type: string
|
||||
final-version:
|
||||
required: true
|
||||
type: string
|
||||
env:
|
||||
COMPOSE_PROJECT_NAME: ghactions
|
||||
BUILD_IMAGE: mattermost/mattermost-enterprise-edition:${{ inputs.final-version }}
|
||||
MYSQL_CONN_ARGS: -h localhost -P 3306 --protocol=tcp -ummuser -pmostest mattermost_test
|
||||
DUMP_SERVER_NAME: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.dump.server.sql
|
||||
DUMP_SCRIPT_NAME: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.dump.script.sql
|
||||
MIGRATION_SCRIPT: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.mysql.up.sql
|
||||
CLEANUP_SCRIPT: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.mysql.cleanup.sql
|
||||
PREPROCESS_SCRIPT: esr.common.mysql.preprocess.sql
|
||||
DIFF_NAME: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.diff
|
||||
jobs:
|
||||
esr-upgrade-server:
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout mattermost-server
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
- name: Run docker compose
|
||||
run: |
|
||||
cd server/build
|
||||
docker-compose --no-ansi run --rm start_dependencies
|
||||
cat ../tests/test-data.ldif | docker-compose --no-ansi exec -T openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';
|
||||
docker-compose --no-ansi exec -T minio sh -c 'mkdir -p /data/mattermost-test';
|
||||
docker-compose --no-ansi ps
|
||||
- name: Wait for docker compose
|
||||
run: |
|
||||
until docker network inspect ghactions_mm-test; do echo "Waiting for Docker Compose Network..."; sleep 1; done;
|
||||
docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://mysql:3306; do echo waiting for mysql; sleep 5; done;"
|
||||
docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://elasticsearch:9200; do echo waiting for elasticsearch; sleep 5; done;"
|
||||
- name: Initialize the database with the source DB dump
|
||||
run: |
|
||||
curl ${{ inputs.db-dump-url }} | zcat | docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS
|
||||
- name: Common preprocessing of the DB dump
|
||||
run: |
|
||||
cd server/scripts/esrupgrades
|
||||
docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $PREPROCESS_SCRIPT
|
||||
- name: Pull EE image
|
||||
run: |
|
||||
docker pull $BUILD_IMAGE
|
||||
- name: Run migration through server
|
||||
run: |
|
||||
mkdir -p client/plugins
|
||||
cd server/build
|
||||
# Run the server in the background to trigger the migrations
|
||||
docker run --name mmserver \
|
||||
--net ghactions_mm-test \
|
||||
--ulimit nofile=8096:8096 \
|
||||
--env-file=dotenv/test.env \
|
||||
--env MM_SQLSETTINGS_DRIVERNAME="mysql" \
|
||||
--env MM_SQLSETTINGS_DATASOURCE="mmuser:mostest@tcp(mysql:3306)/mattermost_test?charset=utf8mb4,utf8&multiStatements=true" \
|
||||
-v ~/work/mattermost-server:/mattermost-server \
|
||||
-w /mattermost-server/mattermost-server \
|
||||
$BUILD_IMAGE &
|
||||
# In parallel, wait for the migrations to finish.
|
||||
# To verify this, we check that the server has finished the startup job through the log line "Server is listening on"
|
||||
until docker logs mmserver | grep "Server is listening on"; do\
|
||||
echo "Waiting for migrations to finish..."; \
|
||||
sleep 1; \
|
||||
done;
|
||||
# Make sure to stop the server. Also, redirect output to null;
|
||||
# otherwise, the name of the container gets written to the console, which is weird
|
||||
docker stop mmserver > /dev/null
|
||||
- name: Cleanup DB
|
||||
run : |
|
||||
cd server/scripts/esrupgrades
|
||||
docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $CLEANUP_SCRIPT
|
||||
- name: Dump upgraded database
|
||||
run: |
|
||||
# Use --skip-opt to have each INSERT into one line.
|
||||
# Use --set-gtid-purged=OFF to suppress GTID-related statements.
|
||||
docker exec -i ghactions_mysql_1 mysqldump \
|
||||
--skip-opt --set-gtid-purged=OFF \
|
||||
$MYSQL_CONN_ARGS > $DUMP_SERVER_NAME
|
||||
- name: Cleanup dump and compress
|
||||
run: |
|
||||
# We skip the very last line, which simply contains the date of the dump
|
||||
head -n -1 ${DUMP_SERVER_NAME} | gzip > ${DUMP_SERVER_NAME}.gz
|
||||
- name: Upload dump
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: upgraded-dump-server
|
||||
path: ${{ env.DUMP_SERVER_NAME }}.gz
|
||||
esr-upgrade-script:
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout mattermost-server
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
- name: Run docker compose
|
||||
run: |
|
||||
cd server/build
|
||||
docker-compose --no-ansi run --rm start_dependencies
|
||||
cat ../tests/test-data.ldif | docker-compose --no-ansi exec -T openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest';
|
||||
docker-compose --no-ansi exec -T minio sh -c 'mkdir -p /data/mattermost-test';
|
||||
docker-compose --no-ansi ps
|
||||
- name: Wait for docker compose
|
||||
run: |
|
||||
until docker network inspect ghactions_mm-test; do echo "Waiting for Docker Compose Network..."; sleep 1; done;
|
||||
docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://mysql:3306; do echo waiting for mysql; sleep 5; done;"
|
||||
docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://elasticsearch:9200; do echo waiting for elasticsearch; sleep 5; done;"
|
||||
- name: Initialize the database with the source DB dump
|
||||
run: |
|
||||
curl ${{ inputs.db-dump-url }} | zcat | docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS
|
||||
- name: Preprocess the DB dump
|
||||
run: |
|
||||
cd server/scripts/esrupgrades
|
||||
docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $PREPROCESS_SCRIPT
|
||||
- name: Run migration through script
|
||||
run : |
|
||||
cd server/scripts/esrupgrades
|
||||
docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $MIGRATION_SCRIPT
|
||||
- name: Cleanup DB
|
||||
run : |
|
||||
cd server/scripts/esrupgrades
|
||||
docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $CLEANUP_SCRIPT
|
||||
- name: Dump upgraded database
|
||||
run: |
|
||||
docker exec -i ghactions_mysql_1 mysqldump --skip-opt --set-gtid-purged=OFF $MYSQL_CONN_ARGS > $DUMP_SCRIPT_NAME
|
||||
- name: Cleanup dump and compress
|
||||
run: |
|
||||
# We skip the very last line, which simply contains the date of the dump
|
||||
head -n -1 ${DUMP_SCRIPT_NAME} | gzip > ${DUMP_SCRIPT_NAME}.gz
|
||||
- name: Upload dump
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: upgraded-dump-script
|
||||
path: ${{ env.DUMP_SCRIPT_NAME }}.gz
|
||||
esr-upgrade-diff:
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
needs:
|
||||
- esr-upgrade-server
|
||||
- esr-upgrade-script
|
||||
steps:
|
||||
- name: Retrieve dumps
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Diff dumps
|
||||
run: |
|
||||
gzip -d upgraded-dump-server/${DUMP_SERVER_NAME}.gz
|
||||
gzip -d upgraded-dump-script/${DUMP_SCRIPT_NAME}.gz
|
||||
diff upgraded-dump-server/$DUMP_SERVER_NAME upgraded-dump-script/$DUMP_SCRIPT_NAME > $DIFF_NAME
|
||||
- name: Upload diff
|
||||
if: failure() # Upload the diff only if the previous step failed; i.e., if the diff is non-empty
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dumps-diff
|
||||
path: ${{ env.DIFF_NAME }}
|
33
.github/workflows/esrupgrade.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: ESR Upgrade
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'server/scripts/esrupgrades/*'
|
||||
- '.github/workflows/esr*'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- cloud
|
||||
- release-*
|
||||
jobs:
|
||||
esr-upgrade-5_37-7_8:
|
||||
name: Run ESR upgrade script from 5.37 to 7.8
|
||||
uses: ./.github/workflows/esrupgrade-common.yml
|
||||
with:
|
||||
db-dump-url: https://lt-public-data.s3.amazonaws.com/47K_537_mysql_collationfixed.sql.gz
|
||||
initial-version: 5.37
|
||||
final-version: 7.8
|
||||
esr-upgrade-5_37-6_3:
|
||||
name: Run ESR upgrade script from 5.37 to 6.3
|
||||
uses: ./.github/workflows/esrupgrade-common.yml
|
||||
with:
|
||||
db-dump-url: https://lt-public-data.s3.amazonaws.com/47K_537_mysql_collationfixed.sql.gz
|
||||
initial-version: 5.37
|
||||
final-version: 6.3
|
||||
esr-upgrade-6_3-7_8:
|
||||
name: Run ESR upgrade script from 6.3 to 7.8
|
||||
uses: ./.github/workflows/esrupgrade-common.yml
|
||||
with:
|
||||
db-dump-url: https://lt-public-data.s3.amazonaws.com/47K_63_mysql.sql.gz
|
||||
initial-version: 6.3
|
||||
final-version: 7.8
|
2
.github/workflows/scorecards-analysis.yml
vendored
@ -7,7 +7,7 @@ on:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ !contains( github.ref , 'heads/ref/master') }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
1
.github/workflows/test.yml
vendored
@ -23,6 +23,7 @@ jobs:
|
||||
uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0
|
||||
with:
|
||||
go-version: ${{ env.go-version }}
|
||||
cache-dependency-path: server/go.sum
|
||||
- name: Run docker compose
|
||||
run: |
|
||||
cd server/build
|
||||
|
@ -5,3 +5,6 @@
|
||||
/webapp/package-lock.json @mattermost/web-platform
|
||||
/webapp/platform/*/package.json @mattermost/web-platform
|
||||
/webapp/scripts @mattermost/web-platform
|
||||
/server/channels/db/migrations @mattermost/server-platform
|
||||
/server/boards/services/store/sqlstore/migrations @mattermost/server-platform
|
||||
/server/playbooks/server/sqlstore/migrations @mattermost/server-platform
|
||||
|
13
e2e-tests/cypress/package-lock.json
generated
@ -12,7 +12,6 @@
|
||||
"@babel/eslint-parser": "7.19.1",
|
||||
"@babel/eslint-plugin": "7.19.1",
|
||||
"@cypress/request": "2.88.11",
|
||||
"@cypress/skip-test": "2.6.1",
|
||||
"@mattermost/types": "7.4.0",
|
||||
"@testing-library/cypress": "9.0.0",
|
||||
"@types/async": "3.2.16",
|
||||
@ -2250,12 +2249,6 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/skip-test": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/skip-test/-/skip-test-2.6.1.tgz",
|
||||
"integrity": "sha512-X+ibefBiuOmC5gKG91wRIT0/OqXeETYvu7zXktjZ3yLeO186Y8ia0K7/gQUpAwuUi28DuqMd1+7tBQVtPkzbPA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@cypress/xvfb": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
|
||||
@ -19062,12 +19055,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@cypress/skip-test": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/skip-test/-/skip-test-2.6.1.tgz",
|
||||
"integrity": "sha512-X+ibefBiuOmC5gKG91wRIT0/OqXeETYvu7zXktjZ3yLeO186Y8ia0K7/gQUpAwuUi28DuqMd1+7tBQVtPkzbPA==",
|
||||
"dev": true
|
||||
},
|
||||
"@cypress/xvfb": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz",
|
||||
|
@ -3,7 +3,6 @@
|
||||
"@babel/eslint-parser": "7.19.1",
|
||||
"@babel/eslint-plugin": "7.19.1",
|
||||
"@cypress/request": "2.88.11",
|
||||
"@cypress/skip-test": "2.6.1",
|
||||
"@mattermost/types": "7.4.0",
|
||||
"@testing-library/cypress": "9.0.0",
|
||||
"@types/async": "3.2.16",
|
||||
|
@ -98,6 +98,72 @@ describe('Create and delete board / card', () => {
|
||||
cy.findByText('for testing purposes only').should('be.visible');
|
||||
});
|
||||
|
||||
it('MM-T4276 Set up Board emoji', () => {
|
||||
cy.visit('/boards');
|
||||
|
||||
// # Create an empty board and change tile to Testing
|
||||
cy.findByText('Create an empty board').should('exist').click({force: true});
|
||||
cy.get('.BoardComponent').should('exist');
|
||||
|
||||
// # Change Title
|
||||
cy.findByPlaceholderText('Untitled board').should('be.visible').wait(timeouts.HALF_SEC);
|
||||
|
||||
// * Assert that the title is changed to "testing"
|
||||
cy.findByPlaceholderText('Untitled board').
|
||||
clear().
|
||||
type('Testing').
|
||||
type('{enter}').
|
||||
should('have.value', 'Testing');
|
||||
|
||||
// # "Add icon" and "Show description" options appear
|
||||
cy.findByText('Add icon').should('exist');
|
||||
cy.findByText('show description').should('exist');
|
||||
|
||||
// # Click on "Add icon"
|
||||
cy.findByText('Add icon').should('exist').click({force: true});
|
||||
|
||||
// * Assert that a random emoji is selected and added at the beginning of the board title
|
||||
cy.get('.IconSelector').should('exist');
|
||||
|
||||
// # Click on the emoji next to the board title
|
||||
cy.get('.IconSelector .MenuWrapper').should('exist').click({force: true});
|
||||
|
||||
// * Assert that Dropdown menu with 3 options appears
|
||||
cy.findByText('Random').should('exist');
|
||||
cy.findByText('Pick icon').should('exist');
|
||||
cy.findByText('Remove icon').should('exist');
|
||||
|
||||
// # Hover your mouse over the "Pick Icon" option
|
||||
cy.findByText('Pick icon').trigger('mouseover');
|
||||
|
||||
// * Assert that emoji picker menu appears
|
||||
cy.get('.IconSelector .menu-contents').should('exist');
|
||||
|
||||
// # Click on the emoji from the picker
|
||||
cy.get('.EmojiPicker').should('exist').and('be.visible').within(() => {
|
||||
// # Click on the emoji
|
||||
cy.get("[aria-label='😀, grinning']").should('exist');
|
||||
cy.get("[aria-label='😀, grinning']").eq(0).click({force: true});
|
||||
});
|
||||
|
||||
// * Assert that Selected emoji is now displayed next to the board title
|
||||
cy.get('.IconSelector span').contains('😀');
|
||||
|
||||
// # Click on the emoji next to the board title
|
||||
cy.get('.IconSelector .MenuWrapper').should('exist').click({force: true});
|
||||
|
||||
// * Assert that Dropdown menu with 3 options appears
|
||||
cy.findByText('Random').should('exist');
|
||||
cy.findByText('Pick icon').should('exist');
|
||||
cy.findByText('Remove icon').should('exist');
|
||||
|
||||
// # Click "Remove icon"
|
||||
cy.findByText('Remove icon').click({force: true});
|
||||
|
||||
// * Assert that Icon next to the board title is removed
|
||||
cy.get('.IconSelector').should('not.exist');
|
||||
});
|
||||
|
||||
it('MM-T5397 Can create and delete a board and a card', () => {
|
||||
// Visit a page and create new empty board
|
||||
cy.visit('/boards');
|
||||
|
@ -18,7 +18,6 @@ describe('Verify Accessibility Support in Post', () => {
|
||||
let otherUser;
|
||||
let testTeam;
|
||||
let testChannel;
|
||||
let emojiPickerEnabled;
|
||||
|
||||
before(() => {
|
||||
cy.apiInitSetup().then(({team, channel, user}) => {
|
||||
@ -33,10 +32,6 @@ describe('Verify Accessibility Support in Post', () => {
|
||||
cy.apiAddUserToChannel(testChannel.id, otherUser.id);
|
||||
});
|
||||
});
|
||||
|
||||
cy.apiGetConfig().then(({config}) => {
|
||||
emojiPickerEnabled = config.ServiceSettings.EnableEmojiPicker;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -179,17 +174,10 @@ describe('Verify Accessibility Support in Post', () => {
|
||||
cy.get(`#CENTER_time_${postId}`).should('be.focused');
|
||||
cy.focused().tab();
|
||||
|
||||
// eslint-disable-next-line no-negated-condition
|
||||
if (!emojiPickerEnabled) {
|
||||
// * Verify focus is on the actions button
|
||||
cy.get(`#CENTER_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
|
||||
for (let i = 0; i < 3; i++) {
|
||||
// * Verify focus is on the reactions button
|
||||
cy.get(`#recent_reaction_${i}`).should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
|
||||
cy.focused().tab();
|
||||
} else {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
// * Verify focus is on the reactions button
|
||||
cy.get(`#recent_reaction_${i}`).should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
|
||||
cy.focused().tab();
|
||||
}
|
||||
}
|
||||
|
||||
// * Verify focus is on the reactions button
|
||||
@ -200,15 +188,17 @@ describe('Verify Accessibility Support in Post', () => {
|
||||
cy.get(`#CENTER_flagIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'save');
|
||||
cy.focused().tab();
|
||||
|
||||
// * Verify focus is on message actions button
|
||||
cy.get(`#CENTER_actions_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'actions');
|
||||
cy.focused().tab();
|
||||
|
||||
// * Verify focus is on the comment button
|
||||
cy.get(`#CENTER_commentIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'reply');
|
||||
cy.focused().tab();
|
||||
|
||||
if (emojiPickerEnabled) {
|
||||
// * Verify focus is on the more button
|
||||
cy.get(`#CENTER_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'More');
|
||||
cy.focused().tab();
|
||||
}
|
||||
// * Verify focus is on the more button
|
||||
cy.get(`#CENTER_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
|
||||
cy.focused().tab();
|
||||
|
||||
// * Verify focus is on the post text
|
||||
cy.get(`#postMessageText_${postId}`).should('be.focused').and('have.attr', 'aria-readonly', 'true');
|
||||
@ -244,11 +234,13 @@ describe('Verify Accessibility Support in Post', () => {
|
||||
cy.get(`#rhsPostMessageText_${postId}`).should('be.focused').and('have.attr', 'aria-readonly', 'true');
|
||||
cy.focused().tab({shift: true});
|
||||
|
||||
if (emojiPickerEnabled) {
|
||||
// * Verify focus is on the actions button
|
||||
cy.get(`#RHS_COMMENT_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'More');
|
||||
cy.focused().tab({shift: true});
|
||||
}
|
||||
// * Verify focus is on the more button
|
||||
cy.get(`#RHS_COMMENT_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
|
||||
cy.focused().tab({shift: true});
|
||||
|
||||
// * Verify focus is on message actions button
|
||||
cy.get(`#RHS_COMMENT_actions_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'actions');
|
||||
cy.focused().tab({shift: true});
|
||||
|
||||
// * Verify focus is on the save icon
|
||||
cy.get(`#RHS_COMMENT_flagIcon_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'save');
|
||||
@ -258,15 +250,9 @@ describe('Verify Accessibility Support in Post', () => {
|
||||
cy.get(`#RHS_COMMENT_reaction_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'add reaction');
|
||||
cy.focused().tab({shift: true});
|
||||
|
||||
// eslint-disable-next-line no-negated-condition
|
||||
if (!emojiPickerEnabled) {
|
||||
// * Verify focus is on the actions button
|
||||
cy.get(`#RHS_COMMENT_button_${postId}`).should('be.focused').and('have.attr', 'aria-label', 'more');
|
||||
cy.focused().tab({shift: true});
|
||||
} else {
|
||||
cy.get('#recent_reaction_0').should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
|
||||
cy.focused().tab({shift: true});
|
||||
}
|
||||
// * Verify focus is on most recent action
|
||||
cy.get('#recent_reaction_0').should('have.class', 'emoticon--post-menu').and('have.attr', 'aria-label');
|
||||
cy.focused().tab({shift: true});
|
||||
|
||||
// * Verify focus is on the time
|
||||
cy.get(`#RHS_COMMENT_time_${postId}`).should('be.focused');
|
||||
|
@ -11,6 +11,7 @@
|
||||
// Group: @channels @bot_accounts
|
||||
|
||||
import {createBotPatch} from '../../../support/api/bots';
|
||||
import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
describe('Bot tags', () => {
|
||||
let me;
|
||||
@ -48,8 +49,8 @@ describe('Bot tags', () => {
|
||||
await client.pinPost(postId);
|
||||
|
||||
cy.visit(`/${team.name}/channels/${channel.name}`);
|
||||
cy.clickPostDotMenu(postId);
|
||||
cy.get(`#CENTER_flagIcon_${postId}`).click();
|
||||
cy.get(`#post_${postId}`).trigger('mouseover', {force: true});
|
||||
cy.wait(TIMEOUTS.HALF_SEC).get(`#CENTER_flagIcon_${postId}`).click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -15,7 +15,10 @@ import {getRandomId} from '../../../utils';
|
||||
describe('Leave an archived channel', () => {
|
||||
let testTeam;
|
||||
let offTopicUrl;
|
||||
|
||||
const channelType = {
|
||||
public: 'Channel Type: Public',
|
||||
archived: 'Channel Type: Archived',
|
||||
};
|
||||
before(() => {
|
||||
cy.apiUpdateConfig({
|
||||
TeamSettings: {
|
||||
@ -97,7 +100,7 @@ describe('Leave an archived channel', () => {
|
||||
// # More channels modal opens
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
// # Click on dropdown
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
cy.findByText(channelType.public).should('be.visible').click();
|
||||
|
||||
// # Click archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@ -145,7 +148,7 @@ describe('Leave an archived channel', () => {
|
||||
// # More channels modal opens
|
||||
cy.get('.more-modal').should('be.visible').within(() => {
|
||||
// # Public channel list opens by default
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
cy.findByText(channelType.public).should('be.visible').click();
|
||||
|
||||
// # Click on archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@ -198,7 +201,7 @@ describe('Leave an archived channel', () => {
|
||||
// # More channels modal opens
|
||||
cy.get('.more-modal').should('be.visible').within(() => {
|
||||
// # Public channels are shown by default
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
cy.findByText(channelType.public).should('be.visible').click();
|
||||
|
||||
// # Go to archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@ -252,7 +255,7 @@ describe('Leave an archived channel', () => {
|
||||
// # More channels modal opens
|
||||
cy.get('.more-modal').should('be.visible').within(() => {
|
||||
// # Show public channels is visible by default
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
cy.findByText(channelType.public).should('be.visible').click();
|
||||
|
||||
// # Go to archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
@ -286,7 +289,7 @@ describe('Leave an archived channel', () => {
|
||||
|
||||
// # More channels modal opens and lands on public channels
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
cy.findByText('Show: Public Channels').should('be.visible').click();
|
||||
cy.findByText(channelType.public).should('be.visible').click();
|
||||
|
||||
// # Go to archived channels
|
||||
cy.findByText('Archived Channels').click();
|
||||
|
@ -14,6 +14,11 @@ import * as TIMEOUTS from '../../../fixtures/timeouts';
|
||||
|
||||
import {createPrivateChannel} from '../enterprise/elasticsearch_autocomplete/helpers';
|
||||
|
||||
const channelType = {
|
||||
public: 'Channel Type: Public',
|
||||
archived: 'Channel Type: Archived',
|
||||
};
|
||||
|
||||
describe('Channels', () => {
|
||||
let testUser;
|
||||
let otherUser;
|
||||
@ -65,7 +70,7 @@ describe('Channels', () => {
|
||||
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
// * Dropdown should be visible, defaulting to "Public Channels"
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Show: Public Channels').wait(TIMEOUTS.HALF_SEC);
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', channelType.public).wait(TIMEOUTS.HALF_SEC);
|
||||
|
||||
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
|
||||
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 1).within(() => {
|
||||
@ -113,7 +118,7 @@ describe('Channels', () => {
|
||||
cy.findByText('Archived Channels').should('be.visible').click();
|
||||
|
||||
// * Channel test should be visible as an archived channel in the list
|
||||
cy.wrap(el).should('contain', 'Show: Archived Channels');
|
||||
cy.wrap(el).should('contain', channelType.archived);
|
||||
});
|
||||
|
||||
cy.get('#searchChannelsTextbox').should('be.visible').type(testChannel.display_name).wait(TIMEOUTS.HALF_SEC);
|
||||
@ -196,7 +201,7 @@ describe('Channels', () => {
|
||||
|
||||
// * Dropdown should be visible, defaulting to "Public Channels"
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').within((el) => {
|
||||
cy.wrap(el).should('contain', 'Show: Public Channels');
|
||||
cy.wrap(el).should('contain', channelType.public);
|
||||
});
|
||||
|
||||
// * Users should be able to type and search
|
||||
@ -207,12 +212,12 @@ describe('Channels', () => {
|
||||
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
// * Users should be able to switch to "Archived Channels" list
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', 'Show: Public Channels').click().within((el) => {
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('contain', channelType.public).click().within((el) => {
|
||||
// # Click on archived channels item
|
||||
cy.findByText('Archived Channels').should('be.visible').click();
|
||||
|
||||
// * Modal should show the archived channels list
|
||||
cy.wrap(el).should('contain', 'Show: Archived Channels');
|
||||
cy.wrap(el).should('contain', channelType.archived);
|
||||
}).wait(TIMEOUTS.HALF_SEC);
|
||||
cy.get('#searchChannelsTextbox').clear();
|
||||
cy.get('#moreChannelsList').should('be.visible').children().should('have.length', 2);
|
||||
@ -250,7 +255,7 @@ function verifyMoreChannelsModal(isEnabled) {
|
||||
// * Verify that the more channels modal is open and with or without option to view archived channels
|
||||
cy.get('#moreChannelsModal').should('be.visible').within(() => {
|
||||
if (isEnabled) {
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('have.text', 'Show: Public Channels');
|
||||
cy.get('#channelsMoreDropdown').should('be.visible').and('have.text', channelType.public);
|
||||
} else {
|
||||
cy.get('#channelsMoreDropdown').should('not.exist');
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ describe('System Console - Subscriptions section', () => {
|
||||
cy.get('.RHS').find('button').should('be.enabled');
|
||||
|
||||
// # Change the user seats field to a value smaller than the current number of users
|
||||
const lessThanUserCount = count - 5;
|
||||
const lessThanUserCount = 1;
|
||||
cy.get('#input_UserSeats').clear().type(lessThanUserCount);
|
||||
|
||||
// * Ensure that the yearly, monthly, and yearly saving prices match the new user seats value entered
|
||||
|
@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// - Use element ID when selecting an element. Create one if none.
|
||||
// ***************************************************************
|
||||
// Stage: @prod
|
||||
|
||||
describe('Insights', () => {
|
||||
let teamA;
|
||||
|
||||
before(() => {
|
||||
cy.shouldHaveFeatureFlag('InsightsEnabled', true);
|
||||
|
||||
cy.apiInitSetup().then(({team}) => {
|
||||
teamA = team;
|
||||
});
|
||||
});
|
||||
it('Check all the cards exist', () => {
|
||||
cy.apiAdminLogin();
|
||||
|
||||
// # Go to the Insights view
|
||||
cy.visit(`/${teamA.name}/activity-and-insights`);
|
||||
|
||||
// * Check top channels exists
|
||||
cy.get('.top-channels-card').should('exist');
|
||||
|
||||
// * Check top threads exists
|
||||
cy.get('.top-threads-card').should('exist');
|
||||
|
||||
// * Check top boards exists because product mode is enabled
|
||||
cy.get('.top-boards-card').should('exist');
|
||||
|
||||
// * Check top reactions exists
|
||||
cy.get('.top-reactions-card').should('exist');
|
||||
|
||||
// * Check top dms exists
|
||||
cy.get('.top-dms-card').should('exist');
|
||||
|
||||
// * Check least active channels exists
|
||||
cy.get('.least-active-channels-card').should('exist');
|
||||
|
||||
// * Check top playbooks exists because product mode is enabled
|
||||
cy.get('.top-playbooks-card').should('exist');
|
||||
});
|
||||
});
|
@ -69,6 +69,12 @@ describe('Incoming webhook', () => {
|
||||
|
||||
cy.getLastPost().within(() => {
|
||||
cy.findByRole('link', {name: 'Testing Integration Attachments', hidden: true});
|
||||
});
|
||||
|
||||
// # Scroll to the bottom of the posts
|
||||
cy.get('.post-list__dynamic').scrollTo('bottom');
|
||||
|
||||
cy.getLastPost().within(() => {
|
||||
cy.get('.attachment__image').should('be.visible');
|
||||
cy.get(':nth-child(2) > thead > tr > .attachment-field__caption').should('have.text', 'Area');
|
||||
cy.get(':nth-child(3) > thead > tr > :nth-child(1)').should('have.text', 'Iteration');
|
||||
|
@ -191,7 +191,9 @@ describe('Keyboard shortcut CTRL/CMD+Shift+\\ for adding reaction to last messag
|
||||
Cypress._.times(3, () => {
|
||||
doReactToLastMessageShortcut('CENTER');
|
||||
cy.get('#emojiPicker').should('exist');
|
||||
cy.get('body').click();
|
||||
|
||||
// # Click anywhere to close emoji picker
|
||||
cy.get('#channelHeaderInfo').click();
|
||||
cy.get('#emojiPicker').should('not.exist');
|
||||
});
|
||||
|
||||
|
@ -15,7 +15,7 @@ import theme from '../../../fixtures/theme.json';
|
||||
|
||||
describe('Status dropdown menu', () => {
|
||||
const statusTestCases = [
|
||||
{text: 'Online', className: 'icon-check', profileClassName: 'icon-check-circle'},
|
||||
{text: 'Online', className: 'icon-check-circle', profileClassName: 'icon-check-circle'},
|
||||
{text: 'Away', className: 'icon-clock'},
|
||||
{text: 'Do Not Disturb', className: 'icon-minus-circle'},
|
||||
{text: 'Offline', className: 'icon-circle-outline'},
|
||||
|
@ -145,4 +145,42 @@ describe('Edit Message', () => {
|
||||
cy.get(postText).should('have.text', `${secondMessage} Another new message Edited`);
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T5416 should discard any changes made after cancelling the edit and opening the edit textbox again should display the original message', () => {
|
||||
const message = 'World!';
|
||||
cy.postMessage(message);
|
||||
|
||||
// * Verify message is sent and not pending
|
||||
cy.getLastPostId().then((postId) => {
|
||||
const postText = `#postMessageText_${postId}`;
|
||||
cy.get(postText).should('have.text', message);
|
||||
|
||||
// # Open edit textbox
|
||||
cy.uiGetPostTextBox().type('{uparrow}');
|
||||
|
||||
// * Edit Post Input should appear, and edit the post
|
||||
cy.get('#edit_textbox').should('be.visible');
|
||||
|
||||
// * Press the escape key to cancel
|
||||
cy.get('#edit_textbox').should('have.text', message).type(' Another new message{esc}');
|
||||
cy.get('#edit_textbox').should('not.exist');
|
||||
|
||||
// * Check that the message wasn't edited
|
||||
cy.get(postText).should('have.text', message);
|
||||
});
|
||||
|
||||
cy.getLastPostId().then((postId) => {
|
||||
const postText = `#postMessageText_${postId}`;
|
||||
cy.get(postText).should('have.text', message);
|
||||
|
||||
// # Open edit textbox again
|
||||
cy.uiGetPostTextBox().type('{uparrow}');
|
||||
|
||||
// * Edit Post Input should appear, and edit the post
|
||||
cy.get('#edit_textbox').should('be.visible');
|
||||
|
||||
// * Opening the edit textbox again after previously cancelling the edit should contain the original message.
|
||||
cy.get('#edit_textbox').should('have.text', message);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -9,71 +9,45 @@
|
||||
// Stage: @prod
|
||||
// Group: @playbooks
|
||||
|
||||
import {onlyOn} from '@cypress/skip-test';
|
||||
|
||||
describe('channels > App Bar', {testIsolation: true}, () => {
|
||||
let testTeam;
|
||||
let testUser;
|
||||
let testPlaybook;
|
||||
let appBarEnabled;
|
||||
|
||||
before(() => {
|
||||
cy.apiInitSetup().then(({team, user}) => {
|
||||
testTeam = team;
|
||||
testUser = user;
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
|
||||
// # Create a playbook
|
||||
cy.apiCreateTestPlaybook({
|
||||
teamId: testTeam.id,
|
||||
title: 'Playbook',
|
||||
userId: testUser.id,
|
||||
}).then((playbook) => {
|
||||
testPlaybook = playbook;
|
||||
|
||||
// # Start a playbook run
|
||||
cy.apiRunPlaybook({
|
||||
teamId: testTeam.id,
|
||||
playbookId: testPlaybook.id,
|
||||
playbookRunName: 'Playbook Run',
|
||||
ownerUserId: testUser.id,
|
||||
});
|
||||
});
|
||||
|
||||
cy.apiGetConfig(true).then(({config}) => {
|
||||
appBarEnabled = config.EnableAppBar === 'true';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Size the viewport to show the RHS without covering posts.
|
||||
cy.viewport('macbook-13');
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
cy.apiAdminLogin();
|
||||
});
|
||||
|
||||
describe('App Bar disabled', () => {
|
||||
it('should not show the Playbook App Bar icon', () => {
|
||||
onlyOn(!appBarEnabled);
|
||||
cy.apiUpdateConfig({ExperimentalSettings: {EnableAppBar: false}});
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
|
||||
// # Navigate directly to a non-playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/town-square`);
|
||||
|
||||
// * Verify App Bar icon is not showing
|
||||
cy.get('#channel_view').within(() => {
|
||||
cy.getPlaybooksAppBarIcon().should('not.exist');
|
||||
});
|
||||
cy.get('.app-bar').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('App Bar enabled', () => {
|
||||
it('should show the Playbook App Bar icon', () => {
|
||||
onlyOn(appBarEnabled);
|
||||
beforeEach(() => {
|
||||
cy.apiUpdateConfig({ExperimentalSettings: {EnableAppBar: true}});
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
});
|
||||
|
||||
it('should show the Playbook App Bar icon', () => {
|
||||
// # Navigate directly to a non-playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/town-square`);
|
||||
|
||||
@ -82,8 +56,6 @@ describe('channels > App Bar', {testIsolation: true}, () => {
|
||||
});
|
||||
|
||||
it('should show "Playbooks" tooltip for Playbook App Bar icon', () => {
|
||||
onlyOn(appBarEnabled);
|
||||
|
||||
// # Navigate directly to a non-playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/town-square`);
|
||||
|
||||
|
@ -9,14 +9,11 @@
|
||||
// Stage: @prod
|
||||
// Group: @playbooks
|
||||
|
||||
import {onlyOn} from '@cypress/skip-test';
|
||||
|
||||
describe('channels > channel header', {testIsolation: true}, () => {
|
||||
let testTeam;
|
||||
let testUser;
|
||||
let testPlaybook;
|
||||
let testPlaybookRun;
|
||||
let appBarEnabled;
|
||||
|
||||
before(() => {
|
||||
cy.apiInitSetup().then(({team, user}) => {
|
||||
@ -44,24 +41,16 @@ describe('channels > channel header', {testIsolation: true}, () => {
|
||||
testPlaybookRun = run;
|
||||
});
|
||||
});
|
||||
|
||||
cy.apiGetConfig(true).then(({config}) => {
|
||||
appBarEnabled = config.EnableAppBar === 'true';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
// # Size the viewport to show the RHS without covering posts.
|
||||
cy.viewport('macbook-13');
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
});
|
||||
|
||||
describe('App Bar enabled', () => {
|
||||
it('webapp should hide the Playbook channel header button', () => {
|
||||
onlyOn(appBarEnabled);
|
||||
cy.apiAdminLogin();
|
||||
cy.apiUpdateConfig({ExperimentalSettings: {EnableAppBar: true}});
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
|
||||
// # Navigate directly to a non-playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/town-square`);
|
||||
@ -74,9 +63,15 @@ describe('channels > channel header', {testIsolation: true}, () => {
|
||||
});
|
||||
|
||||
describe('App Bar disabled', () => {
|
||||
it('webapp should show the Playbook channel header button', () => {
|
||||
onlyOn(!appBarEnabled);
|
||||
beforeEach(() => {
|
||||
cy.apiAdminLogin();
|
||||
cy.apiUpdateConfig({ExperimentalSettings: {EnableAppBar: false}});
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
});
|
||||
|
||||
it('webapp should show the Playbook channel header button', () => {
|
||||
// # Navigate directly to a non-playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/town-square`);
|
||||
|
||||
@ -87,8 +82,6 @@ describe('channels > channel header', {testIsolation: true}, () => {
|
||||
});
|
||||
|
||||
it('tooltip text should show "Playbooks" for Playbook channel header button', () => {
|
||||
onlyOn(!appBarEnabled);
|
||||
|
||||
// # Navigate directly to a non-playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/town-square`);
|
||||
|
||||
@ -103,6 +96,11 @@ describe('channels > channel header', {testIsolation: true}, () => {
|
||||
});
|
||||
|
||||
describe('description text', () => {
|
||||
beforeEach(() => {
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
});
|
||||
|
||||
it('should contain a link to the playbook', () => {
|
||||
// # Navigate directly to a playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/playbook-run`);
|
||||
@ -112,6 +110,7 @@ describe('channels > channel header', {testIsolation: true}, () => {
|
||||
expect(href).to.equals(`/playbooks/playbooks/${testPlaybook.id}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should contain a link to the overview page', () => {
|
||||
// # Navigate directly to a playbook run channel
|
||||
cy.visit(`/${testTeam.name}/channels/playbook-run`);
|
||||
|
@ -82,7 +82,7 @@ describe('channels > rhs > status update', {testIsolation: true}, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('description link navigates to run overview', () => {
|
||||
it('description link navigates to run overview', () => {
|
||||
// # Run the `/playbook update` slash command.
|
||||
cy.uiPostMessageQuickly('/playbook update');
|
||||
|
||||
|
@ -153,7 +153,7 @@ describe('lhs', {testIsolation: true}, () => {
|
||||
cy.findByTestId('dropdownmenu').should('be.visible');
|
||||
});
|
||||
|
||||
it.skip('can copy link', () => {
|
||||
it('can copy link', () => {
|
||||
// # Visit the playbook run
|
||||
cy.visit(`/playbooks/runs/${playbookRun.id}`);
|
||||
stubClipboard().as('clipboard');
|
||||
@ -295,7 +295,7 @@ describe('lhs', {testIsolation: true}, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('leave run, when on rdp of the same run', () => {
|
||||
it('leave run, when on rdp of the same run', () => {
|
||||
// # Click on leave menu item
|
||||
getRunDropdownItemByText('Runs', playbookRun.name, 'Leave and unfollow run').click();
|
||||
|
||||
|
@ -290,7 +290,7 @@ describe('playbooks > edit_metrics', {testIsolation: true}, () => {
|
||||
});
|
||||
|
||||
describe('delete metric', () => {
|
||||
it.skip('verifies when clicking delete button; saved metrics have different confirmation text; deleted metrics are deleted', () => {
|
||||
it('verifies when clicking delete button; saved metrics have different confirmation text; deleted metrics are deleted', () => {
|
||||
// # Visit the selected playbook
|
||||
cy.visit(`/playbooks/playbooks/${testPlaybook.id}`);
|
||||
|
||||
|
@ -160,9 +160,7 @@ describe('runs > permissions', {testIsolation: true}, () => {
|
||||
});
|
||||
|
||||
describe('should be visible', () => {
|
||||
// XXX: Skipping this test, since public playbooks currently have no members. This will
|
||||
// likely change in the future, so keeping the skeleton.
|
||||
it.skip('to playbook members', () => {
|
||||
it('to playbook members', () => {
|
||||
assertRunIsVisible(run, playbookMember);
|
||||
});
|
||||
|
||||
@ -242,9 +240,7 @@ describe('runs > permissions', {testIsolation: true}, () => {
|
||||
});
|
||||
|
||||
describe('should be visible', () => {
|
||||
// XXX: Skipping this test, since public playbooks currently have no members. This will
|
||||
// likely change in the future.
|
||||
it.skip('to playbook members', () => {
|
||||
it('to playbook members', () => {
|
||||
assertRunIsVisible(run, playbookMember);
|
||||
});
|
||||
|
||||
@ -332,10 +328,9 @@ describe('runs > permissions', {testIsolation: true}, () => {
|
||||
assertRunIsVisible(run, runParticipant);
|
||||
});
|
||||
|
||||
// Skipping this test, since followers cannot follow a run with a private channel from
|
||||
// a private playbook. (But leaving it for clarity in the code.)
|
||||
it.skip('to run followers', () => {
|
||||
assertRunIsVisible(run, runFollower);
|
||||
// Followers cannot follow a run with a private channel from a private playbook
|
||||
it('to run followers', () => {
|
||||
assertRunIsNotVisible(run, runFollower);
|
||||
});
|
||||
|
||||
it('to admins in the team', () => {
|
||||
@ -414,10 +409,9 @@ describe('runs > permissions', {testIsolation: true}, () => {
|
||||
assertRunIsVisible(run, runParticipant);
|
||||
});
|
||||
|
||||
// Skipping this test, since followers cannot follow a run with a private channel from
|
||||
// a private playbook. (But leaving it for clarity in the code.)
|
||||
it.skip('to run followers', () => {
|
||||
assertRunIsVisible(run, runFollower);
|
||||
// Followers cannot follow a run with a private channel from a private playbook
|
||||
it('to run followers', () => {
|
||||
assertRunIsNotVisible(run, runFollower);
|
||||
});
|
||||
|
||||
it('to admins in the team', () => {
|
||||
|
@ -692,7 +692,7 @@ describe('runs > run details page > header', {testIsolation: true}, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('Join action disabled', () => {
|
||||
describe('Join action disabled', () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin(testUser);
|
||||
|
||||
|
@ -267,7 +267,7 @@ describe('runs > run details page > status update', {testIsolation: true}, () =>
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('requests an update and confirm', () => {
|
||||
it('requests an update and confirm', () => {
|
||||
// # Click on request update
|
||||
cy.findByTestId('run-statusupdate-section').
|
||||
should('be.visible').
|
||||
@ -281,11 +281,11 @@ describe('runs > run details page > status update', {testIsolation: true}, () =>
|
||||
cy.visit(`${testTeam.name}/channels/${playbookRunChannelName}`);
|
||||
|
||||
// * Assert that message has been sent
|
||||
cy.getLastPost().contains(`${testUser.username} requested a status update for ${testPublicPlaybook.name}.`);
|
||||
cy.getLastPost().contains(`${testViewerUser.username} requested a status update for ${testRun.name}.`);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('requests an update and cancel', () => {
|
||||
it('requests an update and cancel', () => {
|
||||
// # Click request update
|
||||
cy.findByTestId('run-statusupdate-section').
|
||||
should('be.visible').
|
||||
|
@ -2,8 +2,21 @@
|
||||
|
||||
#### 1. Start local server in a separate terminal.
|
||||
|
||||
```
|
||||
# Typically run the local server with:
|
||||
cd server && make run
|
||||
|
||||
# Or build and distribute webapp including channels, boards and playbooks
|
||||
# so that their product URLs do not rely on Webpack dev server.
|
||||
# Especially important when running test inside the Playwright's docker container.
|
||||
cd webapp && make dist
|
||||
cd server && make run-server
|
||||
```
|
||||
|
||||
#### 2. Install dependencies and run the test.
|
||||
|
||||
Note: If you're using Node.js version 18 and above, you may need to set `NODE_OPTIONS='--no-experimental-fetch'`.
|
||||
|
||||
```
|
||||
# Install npm packages
|
||||
npm i
|
||||
@ -32,14 +45,16 @@ npm run test
|
||||
Change to root directory, run docker container
|
||||
|
||||
```
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.30.0-focal /bin/bash
|
||||
docker run -it --rm -v "$(pwd):/mattermost/" --ipc=host mcr.microsoft.com/playwright:v1.32.0-focal /bin/bash
|
||||
```
|
||||
|
||||
#### 2. Inside the docker container
|
||||
|
||||
```
|
||||
export NODE_OPTIONS='--no-experimental-fetch'
|
||||
export PW_BASE_URL=http://host.docker.internal:8065
|
||||
cd mattermost/e2e/playwright
|
||||
export PW_HEADLESS=true
|
||||
cd mattermost/e2e-tests/playwright
|
||||
|
||||
# Install npm packages. Use "npm ci" to match the automated environment
|
||||
npm ci
|
||||
|
1044
e2e-tests/playwright/package-lock.json
generated
@ -1,33 +1,35 @@
|
||||
{
|
||||
"scripts": {
|
||||
"test": "PW_SNAPSHOT_ENABLE=true playwright test",
|
||||
"percy": "PERCY_TOKEN=$PERCY_TOKEN PW_PERCY_ENABLE=true percy exec -- playwright test --project=chrome --project=iphone --project=ipad",
|
||||
"test": "cross-env 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",
|
||||
"tsc": "tsc -b",
|
||||
"lint": "eslint . --ext .js,.ts",
|
||||
"prettier": "prettier --write .",
|
||||
"check": "npm run tsc && npm run lint && npm run prettier",
|
||||
"codegen": "playwright codegen $PW_BASE_URL",
|
||||
"test-slomo": "PW_SNAPSHOT_ENABLE=true PW_HEADLESS=false PW_SLOWMO=1000 playwright test",
|
||||
"codegen": "cross-env playwright codegen $PW_BASE_URL",
|
||||
"playwright-ui": "playwright test --ui",
|
||||
"test-slomo": "cross-env PW_SNAPSHOT_ENABLE=true PW_SLOWMO=1000 playwright test",
|
||||
"show-report": "npx playwright show-report"
|
||||
},
|
||||
"dependencies": {
|
||||
"@percy/cli": "1.18.0",
|
||||
"@percy/cli": "1.23.0",
|
||||
"@percy/playwright": "1.0.4",
|
||||
"@playwright/test": "1.30.0",
|
||||
"@playwright/test": "1.32.3",
|
||||
"async-wait-until": "2.0.12",
|
||||
"chalk": "4.1.2",
|
||||
"deepmerge": "4.3.0",
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv": "16.0.3",
|
||||
"form-data": "4.0.0",
|
||||
"isomorphic-unfetch": "4.0.2",
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/uuid": "9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.51.0",
|
||||
"@typescript-eslint/parser": "5.51.0",
|
||||
"eslint": "8.34.0",
|
||||
"prettier": "2.8.4",
|
||||
"typescript": "4.9.5"
|
||||
"@types/uuid": "9.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "5.59.0",
|
||||
"@typescript-eslint/parser": "5.59.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.38.0",
|
||||
"prettier": "2.8.7",
|
||||
"typescript": "5.0.4"
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {defineConfig, devices} from '@playwright/test';
|
||||
import {duration} from '@e2e-support/util';
|
||||
import testConfig from '@e2e-test.config';
|
||||
|
||||
const defaultOutputFolder = 'playwright-report';
|
||||
const defaultOutputFolder = './playwright-report';
|
||||
|
||||
export default defineConfig({
|
||||
globalSetup: require.resolve('./global_setup'),
|
||||
|
@ -35,7 +35,7 @@
|
||||
# - Default to "false" if not set.
|
||||
|
||||
# 12. PW_HEADLESS
|
||||
# - Default to "true" if not set. Set to false to run test in head mode.
|
||||
# - Default to "false" or headless mode if not set. Set to true to run test in headed mode.
|
||||
|
||||
# 13. PW_SLOWMO
|
||||
# - Default to "0" if not set which means normal test speed run. Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
|
@ -3,16 +3,18 @@
|
||||
|
||||
import {writeFile} from 'node:fs/promises';
|
||||
|
||||
import {request, Browser} from '@playwright/test';
|
||||
import {request, Browser, BrowserContext} from '@playwright/test';
|
||||
|
||||
import {UserProfile} from '@mattermost/types/users';
|
||||
import testConfig from '@e2e-test.config';
|
||||
|
||||
export class TestBrowser {
|
||||
readonly browser: Browser;
|
||||
context: BrowserContext | null;
|
||||
|
||||
constructor(browser: Browser) {
|
||||
this.browser = browser;
|
||||
this.context = null;
|
||||
}
|
||||
|
||||
async login(user: UserProfile | null) {
|
||||
@ -27,8 +29,16 @@ export class TestBrowser {
|
||||
const context = await this.browser.newContext(options);
|
||||
const page = await context.newPage();
|
||||
|
||||
this.context = context;
|
||||
|
||||
return {context, page};
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this.context) {
|
||||
await this.context.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function loginByAPI(loginId: string, password: string, token = '', ldapOnly = false) {
|
||||
|
@ -4,25 +4,32 @@
|
||||
import {getRandomId} from '@e2e-support/util';
|
||||
import {Channel, ChannelType} from '@mattermost/types/channels';
|
||||
|
||||
export function createRandomChannel(
|
||||
teamId: string,
|
||||
name: string,
|
||||
displayName: string,
|
||||
type: ChannelType = 'O',
|
||||
purpose = '',
|
||||
header = '',
|
||||
unique = true
|
||||
): Channel {
|
||||
const randomSuffix = getRandomId();
|
||||
type ChannelInput = {
|
||||
teamId: string;
|
||||
name: string;
|
||||
displayName: string;
|
||||
type?: ChannelType;
|
||||
purpose?: string;
|
||||
header?: string;
|
||||
unique?: boolean;
|
||||
};
|
||||
|
||||
export function createRandomChannel(channelInput: ChannelInput): Channel {
|
||||
const channel = {
|
||||
team_id: teamId,
|
||||
name: unique ? `${name}-${randomSuffix}` : name,
|
||||
display_name: unique ? `${displayName} ${randomSuffix}` : displayName,
|
||||
type,
|
||||
purpose,
|
||||
header,
|
||||
team_id: channelInput.teamId,
|
||||
name: channelInput.name,
|
||||
display_name: channelInput.displayName,
|
||||
type: channelInput.type || 'O',
|
||||
purpose: channelInput.type || '',
|
||||
header: channelInput.type || '',
|
||||
};
|
||||
|
||||
if (channelInput.unique) {
|
||||
const randomSuffix = getRandomId();
|
||||
|
||||
channel.name = `${channelInput.name}-${randomSuffix}`;
|
||||
channel.display_name = `${channelInput.displayName} ${randomSuffix}`;
|
||||
}
|
||||
|
||||
return channel as Channel;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// This is based on "packages/client/src/client4.ts". Modified for node client.
|
||||
// This is based on "webapp/platform/client/src/client4.ts". Modified for node client.
|
||||
// Update should be made in comparison with the base Client4.
|
||||
|
||||
import fs from 'node:fs';
|
||||
@ -134,7 +134,7 @@ export default class Client extends Client4 {
|
||||
|
||||
// *****************************************************************************
|
||||
// Boards client
|
||||
// based on https://github.com/mattermost/focalboard/blob/main/webapp/src/octoClient.ts
|
||||
// based on "webapp/boards/src/octoClient.ts"
|
||||
// *****************************************************************************
|
||||
|
||||
async patchUserConfig(userID: string, patch: UserConfigPatch): Promise<UserPreference[] | undefined> {
|
||||
|
@ -170,7 +170,6 @@ const defaultServerConfig: AdminConfig = {
|
||||
EnableCustomGroups: true,
|
||||
SelfHostedPurchase: true,
|
||||
AllowSyncedDrafts: true,
|
||||
SelfHostedExpansion: false,
|
||||
},
|
||||
TeamSettings: {
|
||||
SiteName: 'Mattermost',
|
||||
@ -319,7 +318,6 @@ const defaultServerConfig: AdminConfig = {
|
||||
LoginButtonColor: '#0000',
|
||||
LoginButtonBorderColor: '#2389D7',
|
||||
LoginButtonTextColor: '#2389D7',
|
||||
EnableInactivityEmail: true,
|
||||
},
|
||||
RateLimitSettings: {
|
||||
Enable: false,
|
||||
@ -532,7 +530,8 @@ const defaultServerConfig: AdminConfig = {
|
||||
EnableSharedChannels: false,
|
||||
EnableRemoteClusterService: false,
|
||||
EnableAppBar: false,
|
||||
PatchPluginsReactDOM: false,
|
||||
DisableRefetchingOnBrowserFocus: false,
|
||||
DelayChannelAutocomplete: false,
|
||||
},
|
||||
AnalyticsSettings: {
|
||||
MaxUsersForStatistics: 2500,
|
||||
@ -622,12 +621,6 @@ const defaultServerConfig: AdminConfig = {
|
||||
'com.mattermost.nps': {
|
||||
Enable: true,
|
||||
},
|
||||
focalboard: {
|
||||
Enable: true,
|
||||
},
|
||||
playbooks: {
|
||||
Enable: true,
|
||||
},
|
||||
},
|
||||
EnableMarketplace: true,
|
||||
EnableRemoteMarketplace: true,
|
||||
@ -671,13 +664,11 @@ const defaultServerConfig: AdminConfig = {
|
||||
BoardsFeatureFlags: '',
|
||||
BoardsDataRetention: false,
|
||||
NormalizeLdapDNs: false,
|
||||
EnableInactivityCheckJob: true,
|
||||
UseCaseOnboarding: true,
|
||||
GraphQL: false,
|
||||
InsightsEnabled: true,
|
||||
CommandPalette: false,
|
||||
SendWelcomePost: true,
|
||||
WorkTemplate: false,
|
||||
WorkTemplate: true,
|
||||
PostPriority: true,
|
||||
WysiwygEditor: false,
|
||||
PeopleProduct: false,
|
||||
@ -686,7 +677,9 @@ const defaultServerConfig: AdminConfig = {
|
||||
ThreadsEverywhere: false,
|
||||
GlobalDrafts: true,
|
||||
OnboardingTourTips: true,
|
||||
DeprecateCloudFree: false,
|
||||
AppsSidebarCategory: false,
|
||||
CloudReverseTrial: false,
|
||||
},
|
||||
ImportSettings: {
|
||||
Directory: './import',
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
import path from 'node:path';
|
||||
import {expect} from '@playwright/test';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import {ClientError} from '@mattermost/client/client4';
|
||||
import {PreferenceType} from '@mattermost/types/preferences';
|
||||
import testConfig from '@e2e-test.config';
|
||||
|
||||
@ -77,10 +79,21 @@ export async function initSetup({
|
||||
offTopicUrl: getUrl(team.name, 'off-topic'),
|
||||
townSquareUrl: getUrl(team.name, 'town-square'),
|
||||
};
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
// log an error for debugging
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err);
|
||||
const err = error as ClientError;
|
||||
if (err.message === 'Could not parse multipart form.') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(chalk.yellow(`node version: ${process.version}\nNODE_OPTIONS: ${process.env.NODE_OPTIONS}`));
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
chalk.green(
|
||||
`This failed due to the experimental fetch support in Node.js starting v18.0.0.\nYou may set environment variable: "export NODE_OPTIONS='--no-experimental-fetch'", then try again.'`
|
||||
)
|
||||
);
|
||||
}
|
||||
expect(err, 'Should not throw an error').toBeFalsy();
|
||||
throw err;
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import {test as base, Browser} from '@playwright/test';
|
||||
import {test as base, Browser, ViewportSize} from '@playwright/test';
|
||||
|
||||
import {TestBrowser} from './browser_context';
|
||||
import {shouldHaveCallsEnabled, shouldHaveFeatureFlag, shouldSkipInSmallScreen, shouldRunInLinux} from './flag';
|
||||
import {initSetup, getAdminClient} from './server';
|
||||
import {isSmallScreen} from './util';
|
||||
import {hideDynamicChannelsContent, waitForAnimationEnd, waitUntil} from './test_action';
|
||||
import {pages} from './ui/pages';
|
||||
import {matchSnapshot} from './visual';
|
||||
@ -15,9 +16,10 @@ type ExtendedFixtures = {
|
||||
};
|
||||
|
||||
export const test = base.extend<ExtendedFixtures>({
|
||||
pw: async ({browser}, use) => {
|
||||
const pw = new PlaywrightExtended(browser);
|
||||
pw: async ({browser, viewport}, use) => {
|
||||
const pw = new PlaywrightExtended(browser, viewport);
|
||||
await use(pw);
|
||||
await pw.testBrowser.close();
|
||||
},
|
||||
// eslint-disable-next-line no-empty-pattern
|
||||
pages: async ({}, use) => {
|
||||
@ -47,10 +49,13 @@ class PlaywrightExtended {
|
||||
// ./ui/pages
|
||||
readonly pages;
|
||||
|
||||
// ./util
|
||||
readonly isSmallScreen;
|
||||
|
||||
// ./visual
|
||||
readonly matchSnapshot;
|
||||
|
||||
constructor(browser: Browser) {
|
||||
constructor(browser: Browser, viewport: ViewportSize | null) {
|
||||
// ./browser_context
|
||||
this.testBrowser = new TestBrowser(browser);
|
||||
|
||||
@ -72,6 +77,9 @@ class PlaywrightExtended {
|
||||
// ./ui/pages
|
||||
this.pages = pages;
|
||||
|
||||
// ./util
|
||||
this.isSmallScreen = () => isSmallScreen(viewport);
|
||||
|
||||
// ./visual
|
||||
this.matchSnapshot = matchSnapshot;
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
export default class FindChannelsModal {
|
||||
readonly container: Locator;
|
||||
readonly input;
|
||||
readonly searchList;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
|
||||
this.input = container.getByRole('textbox', {name: 'quick switch input'});
|
||||
this.searchList = container.locator('.suggestion-list__item');
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
export {FindChannelsModal};
|
@ -0,0 +1,22 @@
|
||||
// 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 toggleSidebar() {
|
||||
await this.container.getByRole('button', {name: 'Toggle sidebar Menu Icon'}).click();
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
export {ChannelsHeaderMobile};
|
@ -9,6 +9,7 @@ export default class ChannelsPostCreate {
|
||||
readonly input;
|
||||
readonly attachmentButton;
|
||||
readonly emojiButton;
|
||||
readonly sendButton: Locator;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
@ -16,12 +17,17 @@ export default class ChannelsPostCreate {
|
||||
this.input = container.getByTestId('post_textbox');
|
||||
this.attachmentButton = container.getByLabel('attachment');
|
||||
this.emojiButton = container.getByLabel('select an emoji');
|
||||
this.sendButton = container.getByTestId('SendMessageButton');
|
||||
}
|
||||
|
||||
async postMessage(message: string) {
|
||||
await this.input.fill(message);
|
||||
}
|
||||
|
||||
async sendMessage() {
|
||||
await this.sendButton.click();
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
await expect(this.input).toBeVisible();
|
||||
|
@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, Locator} from '@playwright/test';
|
||||
|
||||
export default class ChannelsSidebarLeft {
|
||||
readonly container: Locator;
|
||||
readonly findChannelButton;
|
||||
|
||||
constructor(container: Locator) {
|
||||
this.container = container;
|
||||
|
||||
this.findChannelButton = container.getByRole('button', {name: 'Find Channels'});
|
||||
}
|
||||
|
||||
async toBeVisible() {
|
||||
await expect(this.container).toBeVisible();
|
||||
}
|
||||
}
|
||||
|
||||
export {ChannelsSidebarLeft};
|
@ -16,7 +16,7 @@ export default class GlobalHeader {
|
||||
|
||||
async switchProduct(name: string) {
|
||||
await this.productSwitchMenu.click();
|
||||
await this.container.getByRole('link', {name: ` ${name}`}).click();
|
||||
await this.container.getByRole('link', {name}).click();
|
||||
}
|
||||
|
||||
async toBeVisible(name: string) {
|
||||
|
@ -3,19 +3,25 @@
|
||||
|
||||
import {BoardsSidebar} from './boards/sidebar';
|
||||
import {ChannelsHeader} from './channels/header';
|
||||
import {ChannelsHeaderMobile} from './channels/header_mobile';
|
||||
import {ChannelsAppBar} from './channels/app_bar';
|
||||
import {ChannelsPostCreate} from './channels/post_create';
|
||||
import {ChannelsPost} from './channels/post';
|
||||
import {ChannelsSidebarLeft} from './channels/sidebar_left';
|
||||
import {ChannelsSidebarRight} from './channels/sidebar_right';
|
||||
import {FindChannelsModal} from './channels/find_channels_modal';
|
||||
import {GlobalHeader} from './global_header';
|
||||
|
||||
const components = {
|
||||
BoardsSidebar,
|
||||
ChannelsAppBar,
|
||||
ChannelsHeader,
|
||||
ChannelsHeaderMobile,
|
||||
ChannelsPostCreate,
|
||||
ChannelsPost,
|
||||
ChannelsSidebarLeft,
|
||||
ChannelsSidebarRight,
|
||||
FindChannelsModal,
|
||||
GlobalHeader,
|
||||
};
|
||||
|
||||
@ -24,8 +30,11 @@ export {
|
||||
BoardsSidebar,
|
||||
ChannelsAppBar,
|
||||
ChannelsHeader,
|
||||
ChannelsHeaderMobile,
|
||||
ChannelsPostCreate,
|
||||
ChannelsPost,
|
||||
ChannelsSidebarLeft,
|
||||
ChannelsSidebarRight,
|
||||
FindChannelsModal,
|
||||
GlobalHeader,
|
||||
};
|
||||
|
@ -11,17 +11,23 @@ export default class ChannelsPage {
|
||||
readonly channels = 'Channels';
|
||||
readonly page: Page;
|
||||
readonly postCreate;
|
||||
readonly findChannelsModal;
|
||||
readonly globalHeader;
|
||||
readonly header;
|
||||
readonly headerMobile;
|
||||
readonly appBar;
|
||||
readonly sidebarLeft;
|
||||
readonly sidebarRight;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.postCreate = new components.ChannelsPostCreate(page.locator('#post-create'));
|
||||
this.findChannelsModal = new components.FindChannelsModal(page.getByRole('dialog', {name: 'Find Channels'}));
|
||||
this.globalHeader = new components.GlobalHeader(page.locator('#global-header'));
|
||||
this.header = new components.ChannelsHeader(page.locator('.channel-header'));
|
||||
this.headerMobile = new components.ChannelsHeaderMobile(page.locator('.navbar'));
|
||||
this.appBar = new components.ChannelsAppBar(page.locator('.app-bar'));
|
||||
this.sidebarLeft = new components.ChannelsSidebarLeft(page.locator('#SidebarContainer'));
|
||||
this.sidebarRight = new components.ChannelsSidebarRight(page.locator('#sidebar-right'));
|
||||
}
|
||||
|
||||
@ -49,6 +55,10 @@ export default class ChannelsPage {
|
||||
await this.postCreate.postMessage(message);
|
||||
}
|
||||
|
||||
async sendMessage() {
|
||||
await this.postCreate.sendMessage();
|
||||
}
|
||||
|
||||
async getFirstPost() {
|
||||
await this.page.getByTestId('postView').first().waitFor();
|
||||
const post = await this.page.getByTestId('postView').first();
|
||||
|
@ -55,7 +55,7 @@ const config: TestConfig = {
|
||||
// CI
|
||||
isCI: !!process.env.CI,
|
||||
// Playwright
|
||||
headless: parseBool(process.env.PW_HEADLESS, false),
|
||||
headless: parseBool(process.env.PW_HEADLESS, true),
|
||||
slowMo: parseNumber(process.env.PW_SLOWMO, 0),
|
||||
workers: parseNumber(process.env.PW_WORKERS, 1),
|
||||
// Visual tests
|
||||
|
@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@e2e-support/test_fixture';
|
||||
import {createRandomChannel} from '@e2e-support/server';
|
||||
|
||||
test('MM-T5424 Find channel search returns only 50 results when there are more than 50 channels with similar names', async ({
|
||||
pw,
|
||||
pages,
|
||||
}) => {
|
||||
const {adminClient, user, team} = await pw.initSetup();
|
||||
|
||||
const commonName = 'test_channel';
|
||||
|
||||
// # Create more than 50 channels with similar names
|
||||
const channelsRes = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
let suffix = i.toString();
|
||||
if (i < 10) {
|
||||
suffix = `0${i}`;
|
||||
}
|
||||
const channel = createRandomChannel({
|
||||
teamId: team.id,
|
||||
name: `${commonName}_${suffix}`,
|
||||
displayName: `Test Channel ${suffix}`,
|
||||
});
|
||||
channelsRes.push(adminClient.createChannel(channel));
|
||||
}
|
||||
await Promise.all(channelsRes);
|
||||
|
||||
// # Log in a user in new browser context
|
||||
const {page} = await pw.testBrowser.login(user);
|
||||
|
||||
// # Visit a default channel page
|
||||
const channelsPage = new pages.ChannelsPage(page);
|
||||
await channelsPage.goto();
|
||||
await channelsPage.toBeVisible();
|
||||
|
||||
// # Click on "Find channel" and type "test_channel"
|
||||
if (pw.isSmallScreen()) {
|
||||
await channelsPage.headerMobile.toggleSidebar();
|
||||
}
|
||||
await channelsPage.sidebarLeft.findChannelButton.click();
|
||||
|
||||
await channelsPage.findChannelsModal.toBeVisible();
|
||||
await channelsPage.findChannelsModal.input.fill(commonName);
|
||||
|
||||
const limitCount = 50;
|
||||
|
||||
// # Only 50 results for similar name should be displayed.
|
||||
await expect(channelsPage.findChannelsModal.searchList).toHaveCount(limitCount);
|
||||
|
||||
for (let i = 0; i < limitCount; i++) {
|
||||
let suffix = i.toString();
|
||||
if (i < 10) {
|
||||
suffix = `0${i}`;
|
||||
}
|
||||
|
||||
await expect(channelsPage.findChannelsModal.container.getByTestId(`${commonName}_${suffix}`)).toBeVisible();
|
||||
}
|
||||
});
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 238 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 84 KiB |
@ -2,7 +2,6 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {expect, test} from '@e2e-support/test_fixture';
|
||||
import {isSmallScreen} from '@e2e-support/util';
|
||||
|
||||
test('Intro to channel as regular user', async ({pw, pages, browserName, viewport}, testInfo) => {
|
||||
// Create and sign in a new user
|
||||
@ -23,7 +22,7 @@ test('Intro to channel as regular user', async ({pw, pages, browserName, viewpor
|
||||
// await wait(duration.one_sec);
|
||||
|
||||
// Wait for Playbooks icon to be loaded in App bar, except in iphone
|
||||
if (!isSmallScreen(viewport)) {
|
||||
if (!pw.isSmallScreen()) {
|
||||
await expect(channelsPage.appBar.playbooksIcon).toBeVisible();
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 167 KiB |
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 213 KiB |
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 150 KiB |
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 147 KiB |
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 271 KiB |
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 312 KiB |
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 241 KiB |
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 277 KiB |
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 297 KiB |
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 230 KiB |
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 165 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 285 KiB After Width: | Height: | Size: 296 KiB |
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 411 KiB |
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 355 KiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 291 KiB |
Before Width: | Height: | Size: 348 KiB After Width: | Height: | Size: 393 KiB |
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 266 KiB |
104
server/Makefile
@ -1,4 +1,4 @@
|
||||
.PHONY: build package run stop run-client run-server run-haserver stop-haserver stop-client stop-server restart restart-server restart-client restart-haserver start-docker clean-dist clean nuke check-style check-client-style check-server-style check-unit-tests test dist run-client-tests setup-run-client-tests cleanup-run-client-tests test-client build-linux build-osx build-windows package-prep package-linux package-osx package-windows internal-test-web-client vet run-server-for-web-client-tests diff-config prepackaged-plugins prepackaged-binaries test-server test-server-ee test-server-quick test-server-race new-migration migrations-extract
|
||||
.PHONY: build package run stop run-client run-server run-haserver stop-haserver stop-client stop-server restart restart-server restart-client restart-haserver start-docker update-docker clean-dist clean nuke check-style check-client-style check-server-style check-unit-tests test dist run-client-tests setup-run-client-tests cleanup-run-client-tests test-client build-linux build-osx build-windows package-prep package-linux package-osx package-windows internal-test-web-client vet run-server-for-web-client-tests diff-config prepackaged-plugins prepackaged-binaries test-server test-server-ee test-server-quick test-server-race new-migration migrations-extract
|
||||
|
||||
ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
@ -102,11 +102,11 @@ GOFLAGS ?= $(GOFLAGS:)
|
||||
export GOBIN ?= $(PWD)/bin
|
||||
GO=go
|
||||
DELVE=dlv
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildNumber=$(BUILD_NUMBER)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildDate=$(BUILD_DATE)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHash=$(BUILD_HASH)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildHashEnterprise=$(BUILD_HASH_ENTERPRISE)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/server/v8/model.BuildNumber=$(BUILD_NUMBER)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/server/v8/model.BuildDate=$(BUILD_DATE)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/server/v8/model.BuildHash=$(BUILD_HASH)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/server/v8/model.BuildHashEnterprise=$(BUILD_HASH_ENTERPRISE)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/server/v8/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)"
|
||||
|
||||
GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1)
|
||||
GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
|
||||
@ -132,21 +132,22 @@ DIST_PATH_WIN=$(DIST_ROOT)/windows/mattermost
|
||||
TESTS=.
|
||||
|
||||
# Packages lists
|
||||
TE_PACKAGES=$(shell $(GO) list ./... | grep -vE 'v6/server/playbooks|v6/server/boards')
|
||||
BOARDS_PACKAGES=$(shell $(GO) list ./... | grep -E 'v6/server/boards')
|
||||
PLAYBOOKS_PACKAGES=$(shell $(GO) list ./... | grep -E 'v6/server/playbooks')
|
||||
TE_PACKAGES=$(shell $(GO) list ./... | grep -vE 'server/v8/playbooks|server/v8/boards')
|
||||
BOARDS_PACKAGES=$(shell $(GO) list ./... | grep -E 'server/v8/boards')
|
||||
PLAYBOOKS_PACKAGES=$(shell $(GO) list ./... | grep -E 'server/v8/playbooks')
|
||||
SUITE_PACKAGES=$(shell $(GO) list ./...)
|
||||
|
||||
TEMPLATES_DIR=templates
|
||||
|
||||
# Plugins Packages
|
||||
PLUGIN_PACKAGES ?= mattermost-plugin-antivirus-v0.1.2
|
||||
PLUGIN_PACKAGES += mattermost-plugin-autolink-v1.2.2
|
||||
PLUGIN_PACKAGES += mattermost-plugin-autolink-v1.4.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-aws-SNS-v1.2.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-calls-v0.15.1
|
||||
PLUGIN_PACKAGES += mattermost-plugin-channel-export-v1.0.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-confluence-v1.3.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-custom-attributes-v1.3.1
|
||||
PLUGIN_PACKAGES += mattermost-plugin-github-v2.1.4
|
||||
PLUGIN_PACKAGES += mattermost-plugin-github-v2.1.5
|
||||
PLUGIN_PACKAGES += mattermost-plugin-gitlab-v1.6.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-jenkins-v1.1.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-jira-v3.2.2
|
||||
@ -155,7 +156,7 @@ PLUGIN_PACKAGES += mattermost-plugin-nps-v1.3.1
|
||||
PLUGIN_PACKAGES += mattermost-plugin-todo-v0.6.1
|
||||
PLUGIN_PACKAGES += mattermost-plugin-welcomebot-v1.2.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-zoom-v1.6.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-apps-v1.2.0
|
||||
PLUGIN_PACKAGES += mattermost-plugin-apps-v1.2.1
|
||||
|
||||
# Prepares the enterprise build if exists. The IGNORE stuff is a hack to get the Makefile to execute the commands outside a target
|
||||
ifeq ($(BUILD_ENTERPRISE_READY),true)
|
||||
@ -189,7 +190,7 @@ endif
|
||||
include config.mk
|
||||
include build/*.mk
|
||||
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/v6/model.MockCWS=$(MM_ENABLE_CWS_MOCK)"
|
||||
LDFLAGS += -X "github.com/mattermost/mattermost-server/server/v8/model.MockCWS=$(MM_ENABLE_CWS_MOCK)"
|
||||
|
||||
RUN_IN_BACKGROUND ?=
|
||||
ifeq ($(RUN_SERVER_IN_BACKGROUND),true)
|
||||
@ -239,6 +240,11 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
update-docker: stop-docker ## Updates the docker containers for local development.
|
||||
@echo Updating docker containers
|
||||
|
||||
$(GO) run ./build/docker-compose-generator/main.go $(ENABLED_DOCKER_SERVICES) | docker-compose -f docker-compose.makefile.yml -f /dev/stdin $(DOCKER_COMPOSE_OVERRIDE) up --no-start
|
||||
|
||||
run-haserver:
|
||||
ifeq ($(BUILD_ENTERPRISE_READY),true)
|
||||
@echo Starting mattermost in an HA topology '(3 node cluster)'
|
||||
@ -270,7 +276,7 @@ else
|
||||
endif
|
||||
|
||||
plugin-checker:
|
||||
$(GO) run $(GOFLAGS) ../plugin/checker
|
||||
$(GO) run $(GOFLAGS) ./plugin/checker
|
||||
|
||||
prepackaged-plugins: ## Populate the prepackaged-plugins directory
|
||||
@echo Downloading prepackaged plugins
|
||||
@ -289,7 +295,7 @@ endif
|
||||
|
||||
golangci-lint: ## Run golangci-lint on codebase
|
||||
@# Keep the version in sync with the command in .circleci/config.yml
|
||||
$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.1
|
||||
$(GO) install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
|
||||
|
||||
@echo Running golangci-lint
|
||||
$(GOBIN)/golangci-lint run ./...
|
||||
@ -349,9 +355,9 @@ ldap-mocks: ## Creates mock files for ldap.
|
||||
|
||||
plugin-mocks: ## Creates mock files for plugins.
|
||||
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
|
||||
$(GOBIN)/mockery --dir ../plugin --name API --output ../plugin/plugintest --outpkg plugintest --case underscore --note 'Regenerate this file using `make plugin-mocks`.'
|
||||
$(GOBIN)/mockery --dir ../plugin --name Hooks --output ../plugin/plugintest --outpkg plugintest --case underscore --note 'Regenerate this file using `make plugin-mocks`.'
|
||||
$(GOBIN)/mockery --dir ../plugin --name Driver --output ../plugin/plugintest --outpkg plugintest --case underscore --note 'Regenerate this file using `make plugin-mocks`.'
|
||||
$(GOBIN)/mockery --dir ./plugin --name API --output ./plugin/plugintest --outpkg plugintest --case underscore --note 'Regenerate this file using `make plugin-mocks`.'
|
||||
$(GOBIN)/mockery --dir ./plugin --name Hooks --output ./plugin/plugintest --outpkg plugintest --case underscore --note 'Regenerate this file using `make plugin-mocks`.'
|
||||
$(GOBIN)/mockery --dir ./plugin --name Driver --output ./plugin/plugintest --outpkg plugintest --case underscore --note 'Regenerate this file using `make plugin-mocks`.'
|
||||
|
||||
einterfaces-mocks: ## Creates mock files for einterfaces.
|
||||
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
|
||||
@ -380,7 +386,7 @@ platform-mocks: ## Creates mocks for platform interfaces.
|
||||
$(GOBIN)/mockery --dir channels/app/platform --name SuiteIFace --output channels/app/platform/mocks --note 'Regenerate this file using `make platform-mocks`.'
|
||||
|
||||
pluginapi: ## Generates api and hooks glue code for plugins
|
||||
$(GO) generate $(GOFLAGS) ../plugin
|
||||
$(GO) generate $(GOFLAGS) ./plugin
|
||||
|
||||
mocks: store-mocks telemetry-mocks filestore-mocks ldap-mocks plugin-mocks einterfaces-mocks searchengine-mocks sharedchannel-mocks misc-mocks email-mocks platform-mocks
|
||||
|
||||
@ -412,7 +418,7 @@ go-junit-report:
|
||||
test-compile: ## Compile tests.
|
||||
@echo COMPILE TESTS
|
||||
|
||||
for package in $(TE_PACKAGES) $(BOARDS_PACKAGES) $(PLAYBOOKS_PACKAGES) $(EE_PACKAGES); do \
|
||||
for package in $(SUITE_PACKAGES) $(EE_PACKAGES); do \
|
||||
$(GO) test $(GOFLAGS) -c $$package; \
|
||||
done
|
||||
|
||||
@ -450,9 +456,9 @@ else
|
||||
endif
|
||||
|
||||
test-server-race: test-server-pre
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=true ./scripts/test.sh "$(GO)" "-race $(GOFLAGS)" "$(TE_PACKAGES) $(EE_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "90m" "atomic"
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=false ./scripts/test.sh "$(GO)" "-race $(GOFLAGS)" "$(BOARDS_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "90m" "atomic"
|
||||
MM_DISABLE_PLAYBOOKS=false MM_DISABLE_BOARDS=true ./scripts/test.sh "$(GO)" "-race $(GOFLAGS)" "$(PLAYBOOKS_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "90m" "atomic"
|
||||
./scripts/test.sh "$(GO)" "-race $(GOFLAGS)" "$(TE_PACKAGES) $(EE_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "90m" "atomic"
|
||||
./scripts/test.sh "$(GO)" "-race $(GOFLAGS)" "$(BOARDS_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "90m" "atomic"
|
||||
./scripts/test.sh "$(GO)" "-race $(GOFLAGS)" "$(PLAYBOOKS_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "90m" "atomic"
|
||||
ifneq ($(IS_CI),true)
|
||||
ifneq ($(MM_NO_DOCKER),true)
|
||||
ifneq ($(TEMP_DOCKER_SERVICES),)
|
||||
@ -463,9 +469,7 @@ ifneq ($(IS_CI),true)
|
||||
endif
|
||||
|
||||
test-server: test-server-pre
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=true ./scripts/test.sh "$(GO)" "$(GOFLAGS)" "$(TE_PACKAGES) $(EE_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "45m" "count"
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=false ./scripts/test.sh "$(GO)" "$(GOFLAGS)" "$(BOARDS_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "45m" "count"
|
||||
MM_DISABLE_PLAYBOOKS=false MM_DISABLE_BOARDS=true ./scripts/test.sh "$(GO)" "$(GOFLAGS)" "$(PLAYBOOKS_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "45m" "count"
|
||||
./scripts/test.sh "$(GO)" "$(GOFLAGS)" "$(SUITE_PACKAGES) $(EE_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "90m" "count"
|
||||
ifneq ($(IS_CI),true)
|
||||
ifneq ($(MM_NO_DOCKER),true)
|
||||
ifneq ($(TEMP_DOCKER_SERVICES),)
|
||||
@ -477,19 +481,15 @@ endif
|
||||
|
||||
test-server-ee: check-prereqs-enterprise start-docker go-junit-report do-cover-file ## Runs EE tests.
|
||||
@echo Running only EE tests
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=true ./scripts/test.sh "$(GO)" "$(GOFLAGS)" "$(EE_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "20m" "count"
|
||||
./scripts/test.sh "$(GO)" "$(GOFLAGS)" "$(EE_PACKAGES)" "$(TESTS)" "$(TESTFLAGS)" "$(GOBIN)" "20m" "count"
|
||||
|
||||
test-server-quick: check-prereqs-enterprise ## Runs only quick tests.
|
||||
ifeq ($(BUILD_ENTERPRISE_READY),true)
|
||||
@echo Running all tests
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=true $(GO) test $(GOFLAGS) -short $(TE_PACKAGES) $(EE_PACKAGES)
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=false $(GO) test $(GOFLAGS) -short $(BOARDS_PACKAGES)
|
||||
MM_DISABLE_PLAYBOOKS=false MM_DISABLE_BOARDS=true $(GO) test $(GOFLAGS) -short $(PLAYBOOKS_PACKAGES)
|
||||
$(GO) test $(GOFLAGS) -short $(SUITE_PACKAGES) $(EE_PACKAGES)
|
||||
else
|
||||
@echo Running only TE tests
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=true $(GO) test $(GOFLAGS) -short $(TE_PACKAGES)
|
||||
MM_DISABLE_PLAYBOOKS=true MM_DISABLE_BOARDS=false $(GO) test $(GOFLAGS) -short $(BOARDS_PACKAGES)
|
||||
MM_DISABLE_PLAYBOOKS=false MM_DISABLE_BOARDS=true $(GO) test $(GOFLAGS) -short $(PLAYBOOKS_PACKAGES)
|
||||
$(GO) test $(GOFLAGS) -short $(SUITE_PACKAGES)
|
||||
endif
|
||||
|
||||
internal-test-web-client: ## Runs web client tests.
|
||||
@ -552,20 +552,20 @@ run-server: setup-go-work prepackaged-binaries validate-go-version start-docker
|
||||
debug-server: start-docker ## Compile and start server using delve.
|
||||
mkdir -p $(BUILD_WEBAPP_DIR)/channels/dist/files
|
||||
$(DELVE) debug $(PLATFORM_FILES) --build-flags="-ldflags '\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildNumber=$(BUILD_NUMBER)\
|
||||
-X \"github.com/mattermost/mattermost-server/v6/model.BuildDate=$(BUILD_DATE)\"\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildHash=$(BUILD_HASH)\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildHashEnterprise=$(BUILD_HASH_ENTERPRISE)\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)'"
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildNumber=$(BUILD_NUMBER)\
|
||||
-X \"github.com/mattermost/mattermost-server/server/v8/model.BuildDate=$(BUILD_DATE)\"\
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildHash=$(BUILD_HASH)\
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildHashEnterprise=$(BUILD_HASH_ENTERPRISE)\
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)'"
|
||||
|
||||
debug-server-headless: start-docker ## Debug server from within an IDE like VSCode or IntelliJ.
|
||||
mkdir -p $(BUILD_WEBAPP_DIR)/channels/dist/files
|
||||
$(DELVE) debug --headless --listen=:2345 --api-version=2 --accept-multiclient $(PLATFORM_FILES) --build-flags="-ldflags '\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildNumber=$(BUILD_NUMBER)\
|
||||
-X \"github.com/mattermost/mattermost-server/v6/model.BuildDate=$(BUILD_DATE)\"\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildHash=$(BUILD_HASH)\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildHashEnterprise=$(BUILD_HASH_ENTERPRISE)\
|
||||
-X github.com/mattermost/mattermost-server/v6/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)'"
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildNumber=$(BUILD_NUMBER)\
|
||||
-X \"github.com/mattermost/mattermost-server/server/v8/model.BuildDate=$(BUILD_DATE)\"\
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildHash=$(BUILD_HASH)\
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildHashEnterprise=$(BUILD_HASH_ENTERPRISE)\
|
||||
-X github.com/mattermost/mattermost-server/server/v8/model.BuildEnterpriseReady=$(BUILD_ENTERPRISE_READY)'"
|
||||
|
||||
run-cli: start-docker ## Runs CLI.
|
||||
@echo Running mattermost for development
|
||||
@ -733,18 +733,18 @@ gen-serialized: ## Generates serialization methods for hot structs
|
||||
# would be to temporarily move all the structs to the same file,
|
||||
# but that involves a lot of manual work.
|
||||
$(GO) install github.com/tinylib/msgp@v1.1.6
|
||||
$(GOBIN)/msgp -file=../model/session.go -tests=false -o=../model/session_serial_gen.go
|
||||
$(GOBIN)/msgp -file=./model/session.go -tests=false -o=./model/session_serial_gen.go
|
||||
@echo "$$LICENSE_HEADER" > tmp.go
|
||||
@cat ../model/session_serial_gen.go >> tmp.go
|
||||
@mv tmp.go ../model/session_serial_gen.go
|
||||
$(GOBIN)/msgp -file=../model/user.go -tests=false -o=../model/user_serial_gen.go
|
||||
@cat ./model/session_serial_gen.go >> tmp.go
|
||||
@mv tmp.go ./model/session_serial_gen.go
|
||||
$(GOBIN)/msgp -file=./model/user.go -tests=false -o=./model/user_serial_gen.go
|
||||
@echo "$$LICENSE_HEADER" > tmp.go
|
||||
@cat ../model/user_serial_gen.go >> tmp.go
|
||||
@mv tmp.go ../model/user_serial_gen.go
|
||||
$(GOBIN)/msgp -file=../model/team_member.go -tests=false -o=../model/team_member_serial_gen.go
|
||||
@cat ./model/user_serial_gen.go >> tmp.go
|
||||
@mv tmp.go ./model/user_serial_gen.go
|
||||
$(GOBIN)/msgp -file=./model/team_member.go -tests=false -o=./model/team_member_serial_gen.go
|
||||
@echo "$$LICENSE_HEADER" > tmp.go
|
||||
@cat ../model/team_member_serial_gen.go >> tmp.go
|
||||
@mv tmp.go ../model/team_member_serial_gen.go
|
||||
@cat ./model/team_member_serial_gen.go >> tmp.go
|
||||
@mv tmp.go ./model/team_member_serial_gen.go
|
||||
|
||||
todo: ## Display TODO and FIXME items in the source code.
|
||||
@! ag --ignore Makefile --ignore-dir runtime '(TODO|XXX|FIXME|"FIX ME")[: ]+'
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
type AdminSetPasswordData struct {
|
||||
|
@ -12,12 +12,12 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/app"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/permissions"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/app"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/permissions"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
func TestErrorResponse(t *testing.T) {
|
||||
|
@ -10,11 +10,11 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
mm_model "github.com/mattermost/mattermost-server/server/v8/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -6,8 +6,8 @@ package api
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
)
|
||||
|
||||
// makeAuditRecord creates an audit record pre-populated with data from the request.
|
||||
|
@ -13,12 +13,12 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/auth"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/utils"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/auth"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/utils"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
func (a *API) registerAuthRoutes(r *mux.Router) {
|
||||
|
@ -12,10 +12,10 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
func (a *API) registerBlocksRoutes(r *mux.Router) {
|
||||
@ -72,7 +72,6 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
|
||||
query := r.URL.Query()
|
||||
parentID := query.Get("parent_id")
|
||||
blockType := query.Get("type")
|
||||
all := query.Get("all")
|
||||
blockID := query.Get("block_id")
|
||||
boardID := mux.Vars(r)["boardID"]
|
||||
|
||||
@ -122,18 +121,11 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.AddMeta("boardID", boardID)
|
||||
auditRec.AddMeta("parentID", parentID)
|
||||
auditRec.AddMeta("blockType", blockType)
|
||||
auditRec.AddMeta("all", all)
|
||||
auditRec.AddMeta("blockID", blockID)
|
||||
|
||||
var blocks []*model.Block
|
||||
var block *model.Block
|
||||
switch {
|
||||
case all != "":
|
||||
blocks, err = a.app.GetBlocksForBoard(boardID)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
case blockID != "":
|
||||
block, err = a.app.GetBlockByID(blockID)
|
||||
if err != nil {
|
||||
@ -148,7 +140,12 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
blocks = append(blocks, block)
|
||||
default:
|
||||
blocks, err = a.app.GetBlocks(boardID, parentID, blockType)
|
||||
opts := model.QueryBlocksOptions{
|
||||
BoardID: boardID,
|
||||
ParentID: parentID,
|
||||
BlockType: model.BlockType(blockType),
|
||||
}
|
||||
blocks, err = a.app.GetBlocks(opts)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
@ -307,7 +304,7 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
|
||||
// this query param exists when creating template from board, or board from template
|
||||
sourceBoardID := r.URL.Query().Get("sourceBoardID")
|
||||
if sourceBoardID != "" {
|
||||
if updateFileIDsErr := a.app.CopyCardFiles(sourceBoardID, blocks); updateFileIDsErr != nil {
|
||||
if updateFileIDsErr := a.app.CopyAndUpdateCardFiles(sourceBoardID, userID, blocks, false); updateFileIDsErr != nil {
|
||||
a.errorResponse(w, r, updateFileIDsErr)
|
||||
return
|
||||
}
|
||||
|
@ -10,10 +10,10 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
func (a *API) registerBoardsRoutes(r *mux.Router) {
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
func (a *API) registerBoardsAndBlocksRoutes(r *mux.Router) {
|
||||
|
@ -12,10 +12,10 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
)
|
||||
|
||||
func (a *API) registerCategoriesRoutes(r *mux.Router) {
|
||||
|
@ -10,11 +10,11 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
mm_model "github.com/mattermost/mattermost-server/server/v8/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
func (a *API) registerChannelsRoutes(r *mux.Router) {
|
||||
|
@ -11,10 +11,10 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
mm_model "github.com/mattermost/mattermost-server/server/v8/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
)
|
||||
|
||||
func (a *API) registerContentBlocksRoutes(r *mux.Router) {
|
||||
|
@ -11,17 +11,17 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/app"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/app"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
mm_model "github.com/mattermost/mattermost-server/server/v8/model"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v6/server/platform/shared/web"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/server/v8/platform/shared/web"
|
||||
)
|
||||
|
||||
// FileUploadResponse is the response to a file upload
|
||||
@ -312,7 +312,7 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.AddMeta("teamID", board.TeamID)
|
||||
auditRec.AddMeta("filename", handle.Filename)
|
||||
|
||||
fileID, err := a.app.SaveFile(file, board.TeamID, boardID, handle.Filename)
|
||||
fileID, err := a.app.SaveFile(file, board.TeamID, boardID, handle.Filename, board.IsTemplate)
|
||||
if err != nil {
|
||||
a.errorResponse(w, r, err)
|
||||
return
|
||||
|
@ -12,10 +12,10 @@ import (
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/model"
|
||||
"github.com/mattermost/mattermost-server/v6/server/boards/services/audit"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/boards/services/audit"
|
||||
|
||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
||||
mm_model "github.com/mattermost/mattermost-server/server/v8/model"
|
||||
)
|
||||
|
||||
func (a *API) registerInsightsRoutes(r *mux.Router) {
|
||||
|