Merge remote-tracking branch 'origin/master' into remote-users-not-count-for-license

This commit is contained in:
Jesús Espino 2023-05-05 16:59:40 +02:00
commit d02cfca92a
2499 changed files with 34779 additions and 16140 deletions

View File

@ -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 }}

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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
View 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
View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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');

View File

@ -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');

View File

@ -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();
});
});
});

View File

@ -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();

View File

@ -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');
}

View File

@ -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

View File

@ -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');
});
});

View File

@ -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');

View File

@ -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');
});

View File

@ -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'},

View File

@ -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);
});
});
});

View File

@ -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`);

View File

@ -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`);

View File

@ -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');

View File

@ -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();

View File

@ -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}`);

View File

@ -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', () => {

View File

@ -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);

View File

@ -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').

File diff suppressed because one or more lines are too long

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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'),

View File

@ -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.

View File

@ -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) {

View File

@ -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;
}

View File

@ -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> {

View File

@ -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',

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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};

View File

@ -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};

View File

@ -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();

View File

@ -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};

View File

@ -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) {

View File

@ -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,
};

View File

@ -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();

View File

@ -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

View File

@ -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();
}
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -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();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 KiB

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 266 KiB

View File

@ -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")[: ]+'

View File

@ -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 {

View File

@ -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 (

View File

@ -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) {

View File

@ -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 (

View File

@ -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.

View File

@ -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) {

View File

@ -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
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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 (

View File

@ -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) {

View File

@ -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) {

View File

@ -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 (

View File

@ -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) {

View File

@ -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

View File

@ -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) {

Some files were not shown because too many files have changed in this diff Show More