Merge branch 'master' into MM-51692-delete-reason-feedback-is-sending-notifications-with-translated-text

Этот коммит содержится в:
Mattermost Build
2023-04-12 17:33:47 +03:00
коммит произвёл GitHub
родитель aebc8e97f3 b00ca20abe
Коммит 56d76dbe28
693 изменённых файлов: 10297 добавлений и 5962 удалений

12
.github/PULL_REQUEST_TEMPLATE.md поставляемый
Просмотреть файл

@@ -19,6 +19,18 @@ If this pull request addresses a Help Wanted ticket, please link the relevant Gi
Otherwise, link the JIRA ticket.
-->
#### Screenshots
<!--
If the PR includes UI changes, include screenshots/GIFs.
For an easier comparison of UI changes a table (template below) can be used.
| before | after |
|----|----|
| <insert before screenshot here> | <insert after screenshot here> |
-->
#### Release Note
<!--
Add a release note for each of the following conditions:

124
.github/workflows/artifacts.yml поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,124 @@
name: Artifacts generation and upload
on:
workflow_run:
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'
steps:
- name: cd/Configure AWS
uses: aws-actions/configure-aws-credentials@07c2f971bac433df982ccc261983ae443861db49 # v1-node16
with:
aws-region: us-east-1
aws-access-key-id: ${{ secrets.PR_BUILDS_BUCKET_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.PR_BUILDS_BUCKET_AWS_SECRET_ACCESS_KEY }}
- 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-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
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
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'
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} .
sentry:
name: Send build info to sentry
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-22.04
env:
SENTRY_AUTH_TOKEN: ${{ secrets.MM_SERVER_SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.MM_SERVER_SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.MM_SERVER_SENTRY_PROJECT }}
steps:
- name: cd/Checkout mattermost-server
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: cd/Create Sentry release
uses: getsentry/action-release@85e0095193a153d57c458995f99d0afd81b9e5ea # v1.3.0

77
.github/workflows/ci.yml поставляемый
Просмотреть файл

@@ -1,4 +1,4 @@
name: mattermost-build
name: Mattermost Build
on:
pull_request:
push:
@@ -232,77 +232,4 @@ jobs:
with:
name: server-build-artifact
path: server/build/
retention-days: 14
upload-s3:
name: Upload to S3 bucket
runs-on: ubuntu-22.04
needs: build-mattermost-server
env:
REPO_NAME: ${{ github.event.repository.name }}
steps:
- name: Download dist artifacts
uses: actions/download-artifact@e9ef242655d12993efdcda9058dee2db83a2cb9b # v3.0.2
with:
name: server-dist-artifact
path: server/dist/
- name: Configure AWS
uses: aws-actions/configure-aws-credentials@07c2f971bac433df982ccc261983ae443861db49 # v1-node16
with:
aws-region: us-east-1
aws-access-key-id: ${{ secrets.MM_SERVER_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.MM_SERVER_AWS_SECRET_ACCESS_KEY }}
# We need to sanitize the branch name before using it
- name: ci/sanitize-branch-name
id: branch
uses: transferwise/sanitize-branch-name@b10b4d524ac5a7b645b43a3527db3a6cca017b9d # v1
# Search for the string "pull" and replace it with "PR" in branch-name
- name: ci/sanitize-branch-name-replace-pull-with-PR-
run: echo "BRANCH_NAME=$(echo ${{ steps.branch.outputs.sanitized-branch-name }} | sed 's/^pull\//PR-/g')" >> $GITHUB_ENV
- name: ci/artifact-upload
run: |
aws s3 cp server/dist/ s3://pr-builds.mattermost.com/$REPO_NAME/$BRANCH_NAME/ --acl public-read --cache-control "no-cache" --recursive
aws s3 cp server/dist/ s3://pr-builds.mattermost.com/$REPO_NAME/commit/${{ github.sha }}/ --acl public-read --cache-control "no-cache" --recursive
build-docker:
name: Build docker image
runs-on: ubuntu-22.04
needs: upload-s3
steps:
- name: Download build artifacts
uses: actions/download-artifact@e9ef242655d12993efdcda9058dee2db83a2cb9b # v3.0.2
with:
name: server-build-artifact
path: server/build/
- name: Login to Docker Hub
uses: docker/login-action@3da7dc6e2b31f99ef2cb9fb4c50fb0971e0d0139 # v2.1.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@11e8a2e2910826a92412015c515187a2d6750279 # v2.4
- name: 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/mattermost-server/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} .
sentry:
name: Send build info to sentry
runs-on: ubuntu-22.04
needs:
- test-postgres-binary
- test-postgres-normal
- test-mysql
- build-mattermost-server
if: ${{ github.event_name == 'push' }}
env:
SENTRY_AUTH_TOKEN: ${{ secrets.MM_SERVER_SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.MM_SERVER_SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.MM_SERVER_SENTRY_PROJECT }}
steps:
- name: Checkout mattermost-server
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Create Sentry release
uses: getsentry/action-release@85e0095193a153d57c458995f99d0afd81b9e5ea # v1.3.0
retention-days: 14

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

@@ -1,9 +1,7 @@
/plugin/ @mattermost/toolkit
/.github/workflows/channels-ci.yml @mattermost/web-platform
/webapp/package.json @mattermost/web-platform
/webapp/channels/package.json @mattermost/web-platform
/webapp/Makefile @mattermost/web-platform
/webapp/package-lock.json @mattermost/web-platform
/webapp/platform/*/package.json @mattermost/web-platform
/webapp/scripts @mattermost/web-platform
/webapp/scripts @mattermost/web-platform

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

@@ -22,8 +22,6 @@ describe('New Channel modal with Boards enabled', () => {
cy.apiLogin(sysadmin);
cy.visit(`/${testTeam.name}/channels/town-square`);
});
cy.shouldHaveFeatureFlag('BoardsProduct', true);
});
it('MM-T5141 New Channel is created with an associated Board', () => {

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

@@ -10,6 +10,8 @@
// Stage: @prod
// Group: @channels @collapsed_reply_threads
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('Collapsed Reply Threads', () => {
let testTeam;
let testUser;
@@ -174,4 +176,45 @@ describe('Collapsed Reply Threads', () => {
cy.uiCloseRHS();
});
});
it('MM-T5413 should auto-scroll to bottom upon pasting long text in reply', () => {
// # Post a root post as current user
cy.postMessageAs({
sender: testUser,
message: 'Another interesting post,',
channelId: testChannel.id,
}).then(({id: rootId}) => {
// # Post multiple replies as other user so that the new messages line is pushed up
Cypress._.times(20, (i) => {
cy.postMessageAs({
sender: otherUser,
message: 'Reply ' + i,
channelId: testChannel.id,
rootId,
});
});
// # Click root post
cy.get(`#post_${rootId}`).click();
// # Wait for RHS to open and scroll to position
cy.wait(TIMEOUTS.ONE_SEC);
// * RHS should open and the editor's actions should not be visible.
cy.get('#rhsContainer').findByTestId('SendMessageButton').should('not.be.visible');
// # Close RHS
cy.uiCloseRHS();
// # Click root post
cy.get(`#post_${rootId}`).click();
// # Paste a multiline string in the RHS textbox.
const text = 'word '.repeat(2000);
cy.get('#rhsContainer').findByTestId('reply_textbox').clear().invoke('val', text).trigger('input');
// * RHS should open and the editor should be visible and focused
cy.get('#rhsContainer').findByTestId('SendMessageButton').should('be.visible');
});
});
});

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('admin console', () => {
describe('admin console', {testIsolation: true}, () => {
let testUser;
let testTeam;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('api > runs', () => {
describe('api > runs', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -11,7 +11,7 @@
import {onlyOn} from '@cypress/skip-test';
describe('channels > App Bar', () => {
describe('channels > App Bar', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > broadcast', () => {
describe('channels > broadcast', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testAdmin;

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

@@ -11,7 +11,7 @@
import {onlyOn} from '@cypress/skip-test';
describe('channels > channel header', () => {
describe('channels > channel header', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -10,7 +10,7 @@
// Group: @playbooks
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('channels > general actions', () => {
describe('channels > general actions', {testIsolation: true}, () => {
let testTeam;
let testSysadmin;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > actions', () => {
describe('channels > actions', {testIsolation: true}, () => {
let testTeam;
let testSysadmin;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > post type components', () => {
describe('channels > post type components', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testChannel;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > retrospective', () => {
describe('runs > retrospective', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybookWithMetrics;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > header', () => {
describe('channels > rhs > header', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -11,7 +11,7 @@
import {HALF_SEC, ONE_SEC} from '../../../../fixtures/timeouts';
describe('channels > rhs > checklist', () => {
describe('channels > rhs > checklist', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;
@@ -164,7 +164,7 @@ describe('channels > rhs > checklist', () => {
});
// * Verify the expected error message.
cy.verifyEphemeralMessage('Failed to execute slash command /invalid');
cy.verifyEphemeralMessage('Failed to find slash command /invalid');
});
it('successfully runs a valid slash command', () => {

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > header', () => {
describe('channels > rhs > header', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > home', () => {
describe('channels > rhs > home', {testIsolation: true}, () => {
let testSysadmin;
let testTeam;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > runlist', () => {
describe('channels > rhs > runlist', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels rhs > start a run', () => {
describe('channels rhs > start a run', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testChannel;

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

@@ -13,7 +13,7 @@
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('channels > rhs > status update', () => {
describe('channels > rhs > status update', {testIsolation: true}, () => {
const defaultReminderMessage = '# Default reminder message';
let testTeam;
let testChannel;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > template', () => {
describe('channels > rhs > template', {testIsolation: true}, () => {
let team1;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > title', () => {
describe('channels > rhs > title', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -11,7 +11,7 @@
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('channels > rhs', () => {
describe('channels > rhs', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > run dialog', () => {
describe('channels > run dialog', {testIsolation: true}, () => {
let testTeam;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > run', () => {
describe('channels > run', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPrivateChannel;

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

@@ -11,7 +11,7 @@
import {switchToChannel} from '../../../channels/mark_as_unread/helpers';
describe('channels > slash command > owner', () => {
describe('channels > slash command > owner', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > info', () => {
describe('channels > slash command > info', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > owner', () => {
describe('channels > slash command > owner', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > test', () => {
describe('channels > slash command > test', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > todo', () => {
describe('channels > slash command > todo', {testIsolation: true}, () => {
let team1;
let team2;
let testUser;

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

@@ -11,7 +11,7 @@
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('channels > update request post', () => {
describe('channels > update request post', {testIsolation: true}, () => {
let testTeam;
let testParticipant;
let testChannelMemberOnly;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('digest messages', () => {
describe('digest messages', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -14,7 +14,7 @@
import {HALF_SEC} from '../../fixtures/timeouts';
import {stubClipboard} from '../../utils';
describe('lhs', () => {
describe('lhs', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPublicPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('navigation', () => {
describe('navigation', {testIsolation: true}, () => {
let testTeam;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('playbooks > edit', () => {
describe('playbooks > edit', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('playbooks > creation button', () => {
describe('playbooks > creation button', {testIsolation: true}, () => {
let testSysadmin;
let testTeam;
let testUser;

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

@@ -11,7 +11,7 @@
//
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('playbooks > edit > task actions', () => {
describe('playbooks > edit > task actions', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;

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

@@ -11,7 +11,7 @@
/* eslint-disable no-only-tests/no-only-tests */
describe('playbooks > edit_metrics', () => {
describe('playbooks > edit_metrics', {testIsolation: true}, () => {
let testTeam;
let testUser;

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

@@ -13,7 +13,7 @@
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('playbooks > edit', () => {
describe('playbooks > edit', {testIsolation: true}, () => {
let testTeam;
let testSysadmin;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('playbooks > feedback', () => {
describe('playbooks > feedback', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('playbooks > list', () => {
describe('playbooks > list', {testIsolation: true}, () => {
const playbookTitle = 'The Playbook Name';
let testTeam;
let testUser;

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

@@ -11,7 +11,7 @@
import {stubClipboard} from '../../../utils';
describe('playbooks > overview', () => {
describe('playbooks > overview', {testIsolation: true}, () => {
let testTeam;
let testOtherTeam;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('playbooks > list pagination', () => {
describe('playbooks > list pagination', {testIsolation: true}, () => {
let testTeam;
let testUser;
const ExtraPlaybooks = 20;

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

@@ -11,7 +11,7 @@
const RUN_NAME_MAX_LENGTH = 64;
describe('playbooks > start a run', () => {
describe('playbooks > start a run', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('playbooks > edit status update', () => {
describe('playbooks > edit status update', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > list', () => {
describe('runs > list', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testAnotherUser;

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

@@ -13,7 +13,7 @@
import {getRandomId} from '../../../utils';
describe('runs > permissions', () => {
describe('runs > permissions', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testOtherTeam;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page', () => {
describe('runs > run details page', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPublicPlaybook;

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

@@ -12,7 +12,7 @@
// Note that this test checks the basic behavior in Run details page as participant / viewer
// It relies on the Channel RHS Checklist test to cover the full behavior of the checklists
describe('runs > run details page > checklist', () => {
describe('runs > run details page > checklist', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page > finish', () => {
describe('runs > run details page > finish', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -13,7 +13,7 @@
import {stubClipboard} from '../../../utils';
describe('runs > run details page > header', () => {
describe('runs > run details page > header', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page > restart run', () => {
describe('runs > run details page > restart run', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -62,7 +62,7 @@ const verifyMetricInput = (index, title, target, description, placeholder) => {
const getRetro = () => cy.findByTestId('run-retrospective-section');
describe('runs > run details page', () => {
describe('runs > run details page', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -11,7 +11,7 @@
/* eslint-disable no-only-tests/no-only-tests */
describe('runs > run details page > status update', () => {
describe('runs > run details page > status update', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page > summary', () => {
describe('runs > run details page > summary', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testRun;

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

@@ -11,7 +11,7 @@
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('runs > task actions', () => {
describe('runs > task actions', {testIsolation: true}, () => {
let testPlaybook;
let testTeam;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page > rhs > participants', () => {
describe('runs > run details page > rhs > participants', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page > run info', () => {
describe('runs > run details page > run info', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page > RHS', () => {
describe('runs > run details page > RHS', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('runs > run details page > status update', () => {
describe('runs > run details page > status update', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('Task Inbox >', () => {
describe('Task Inbox >', {testIsolation: true}, () => {
let testTeam;
let testUser;

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

@@ -9,7 +9,7 @@
// Stage: @prod
// Group: @playbooks
describe('playbook tour points', () => {
describe('playbook tour points', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testSysadmin;

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

@@ -176,7 +176,7 @@ Cypress.Commands.add('updateStatus', (message, reminderQuery) => {
if (reminderQuery) {
cy.get('#reminder_timer_datetime').within(() => {
cy.get('input').type(reminderQuery, {delay: TIMEOUTS.TWO_HUNDRED_MILLIS, force: true}).type('{enter}', {force: true});
cy.get('input').type(reminderQuery, {delay: TIMEOUTS.TWO_HUNDRED_MILLIS, force: true}).wait(TIMEOUTS.ONE_SEC).type('{enter}');
});
}

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

@@ -5,7 +5,6 @@ import {expect} from '@playwright/test';
import {UserProfile} from '@mattermost/types/users';
import {Client, createRandomTeam, getAdminClient, getDefaultAdminUser, makeClient} from './support/server';
import {boardsPluginId, callsPluginId} from './support/constant';
import {defaultTeam} from './support/util';
import testConfig from './test.config';
@@ -97,26 +96,15 @@ async function printClientInfo(client: Client) {
- BuildHashEnterprise = ${config.BuildHashEnterprise}
- BuildEnterpriseReady = ${config.BuildEnterpriseReady}
- FeatureFlagAppsEnabled = ${config.FeatureFlagAppsEnabled}
- FeatureFlagBoardsProduct = ${config.FeatureFlagBoardsProduct}
- FeatureFlagCallsEnabled = ${config.FeatureFlagCallsEnabled}
- TelemetryId = ${config.TelemetryId}`);
}
function getProductsAsPlugin() {
const productsAsPlugin = [callsPluginId];
if (!testConfig.boardsProductEnabled) {
productsAsPlugin.push(boardsPluginId);
}
return productsAsPlugin;
}
async function ensurePluginsLoaded(client: Client) {
const pluginStatus = await client.getPluginStatuses();
const plugins = await client.getPlugins();
getProductsAsPlugin().forEach(async (pluginId) => {
testConfig.ensurePluginsInstalled.forEach(async (pluginId) => {
const isInstalled = pluginStatus.some((plugin) => plugin.plugin_id === pluginId);
if (!isInstalled) {
// eslint-disable-next-line no-console

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

@@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const appsPluginId = 'com.mattermost.apps';
export const boardsPluginId = 'focalboard';
export const boardsProductId = 'boards';
export const callsPluginId = 'com.mattermost.calls';

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

@@ -5,22 +5,10 @@ import os from 'node:os';
import {expect, test} from '@playwright/test';
import {boardsPluginId, callsPluginId} from './constant';
import {callsPluginId} from './constant';
import {getAdminClient} from './server/init';
import {isSmallScreen} from './util';
export async function shouldHaveBoardsEnabled(enabled = true) {
const {adminClient} = await getAdminClient();
const config = await adminClient.getConfig();
const boardsEnabled =
(typeof config.FeatureFlags.BoardsProduct === 'boolean' && config.FeatureFlags.BoardsProduct) ||
config.PluginSettings.PluginStates[boardsPluginId].Enable;
const matched = boardsEnabled === enabled;
expect(matched, matched ? '' : `Boards expect "${enabled}" but actual "${boardsEnabled}"`).toBeTruthy();
}
export async function shouldHaveCallsEnabled(enabled = true) {
const {adminClient} = await getAdminClient();
const config = await adminClient.getConfig();

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

@@ -167,8 +167,9 @@ async function makeClient(userRequest?: UserRequest, useCache = true): Promise<C
const userProfile = await client.login(userRequest.username, userRequest.password);
const user = {...userProfile, password: userRequest.password};
const config = await client.getClientConfigOld();
client.setUseBoardsProduct(config.FeatureFlagBoardsProduct === 'true');
// Manually do until boards as product is consistent in all the codebase.
client.setUseBoardsProduct(true);
if (useCache) {
clients[cacheKey] = {client, user};

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

@@ -6,7 +6,6 @@ import merge from 'deepmerge';
import {
AdminConfig,
ExperimentalSettings,
FeatureFlags,
PasswordSettings,
ServiceSettings,
TeamSettings,
@@ -23,7 +22,6 @@ export function getOnPremServerConfig(): AdminConfig {
type TestAdminConfig = {
ClusterSettings: Partial<ClusterSettings>;
ExperimentalSettings: Partial<ExperimentalSettings>;
FeatureFlags: Partial<FeatureFlags>;
PasswordSettings: Partial<PasswordSettings>;
PluginSettings: Partial<PluginSettings>;
ServiceSettings: Partial<ServiceSettings>;
@@ -40,9 +38,6 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
ExperimentalSettings: {
EnableAppBar: true,
},
FeatureFlags: {
BoardsProduct: testConfig.boardsProductEnabled,
},
PasswordSettings: {
MinimumLength: 5,
Lowercase: false,
@@ -57,11 +52,6 @@ const onPremServerConfig = (): Partial<TestAdminConfig> => {
defaultenabled: true,
},
},
PluginStates: {
focalboard: {
Enable: !testConfig.boardsProductEnabled,
},
},
},
ServiceSettings: {
SiteURL: testConfig.baseURL,
@@ -686,7 +676,6 @@ const defaultServerConfig: AdminConfig = {
GraphQL: false,
InsightsEnabled: true,
CommandPalette: false,
BoardsProduct: false,
SendWelcomePost: true,
WorkTemplate: false,
PostPriority: true,

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

@@ -1,13 +1,7 @@
import {test as base, Browser} from '@playwright/test';
import {TestBrowser} from './browser_context';
import {
shouldHaveBoardsEnabled,
shouldHaveCallsEnabled,
shouldHaveFeatureFlag,
shouldSkipInSmallScreen,
shouldRunInLinux,
} from './flag';
import {shouldHaveCallsEnabled, shouldHaveFeatureFlag, shouldSkipInSmallScreen, shouldRunInLinux} from './flag';
import {initSetup, getAdminClient} from './server';
import {hideDynamicChannelsContent, waitForAnimationEnd, waitUntil} from './test_action';
import {pages} from './ui/pages';
@@ -36,7 +30,6 @@ class PlaywrightExtended {
readonly testBrowser: TestBrowser;
// ./flag
readonly shouldHaveBoardsEnabled;
readonly shouldHaveCallsEnabled;
readonly shouldHaveFeatureFlag;
readonly shouldSkipInSmallScreen;
@@ -62,7 +55,6 @@ class PlaywrightExtended {
this.testBrowser = new TestBrowser(browser);
// ./flag
this.shouldHaveBoardsEnabled = shouldHaveBoardsEnabled;
this.shouldHaveCallsEnabled = shouldHaveCallsEnabled;
this.shouldHaveFeatureFlag = shouldHaveFeatureFlag;
this.shouldSkipInSmallScreen = shouldSkipInSmallScreen;

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

@@ -3,6 +3,9 @@
import {Page, ViewportSize} from '@playwright/test';
import * as dotenv from 'dotenv';
import {appsPluginId, callsPluginId} from '@e2e-support/constant';
dotenv.config();
export type TestArgs = {
@@ -17,7 +20,7 @@ export type TestConfig = {
adminUsername: string;
adminPassword: string;
adminEmail: string;
boardsProductEnabled: boolean;
ensurePluginsInstalled: string[];
resetBeforeTest: boolean;
haClusterEnabled: boolean;
haClusterNodeCount: number;
@@ -41,7 +44,10 @@ const config: TestConfig = {
adminUsername: process.env.PW_ADMIN_USERNAME || 'sysadmin',
adminPassword: process.env.PW_ADMIN_PASSWORD || 'Sys@dmin-sample1',
adminEmail: process.env.PW_ADMIN_EMAIL || 'sysadmin@sample.mattermost.com',
boardsProductEnabled: parseBool(process.env.PW_BOARDS_PRODUCT_ENABLED, true),
ensurePluginsInstalled:
typeof process.env?.PW_ENSURE_PLUGINS_INSTALLED === 'string'
? process.env.PW_ENSURE_PLUGINS_INSTALLED.split(',')
: [appsPluginId, callsPluginId],
haClusterEnabled: parseBool(process.env.PW_HA_CLUSTER_ENABLED, false),
haClusterNodeCount: parseNumber(process.env.PW_HA_CLUSTER_NODE_COUNT, 2),
haClusterName: process.env.PW_HA_CLUSTER_NAME || 'mm_dev_cluster',

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

@@ -7,8 +7,6 @@ import {shouldSkipInSmallScreen} from '@e2e-support/flag';
shouldSkipInSmallScreen();
test('MM-T4274 Create an Empty Board', async ({pw, pages}) => {
await pw.shouldHaveBoardsEnabled();
// Create and sign in a new user
const {user} = await pw.initSetup();

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

@@ -7,8 +7,6 @@ import {shouldSkipInSmallScreen} from '@e2e-support/flag';
shouldSkipInSmallScreen();
test('Board template', async ({pw, pages, browserName, viewport}, testInfo) => {
await pw.shouldHaveBoardsEnabled();
// Create and sign in a new user
const {user} = await pw.initSetup();

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

@@ -7,8 +7,6 @@ import {shouldSkipInSmallScreen} from '@e2e-support/flag';
shouldSkipInSmallScreen();
test('View untitled board', async ({pw, pages, browserName, viewport}, testInfo) => {
await pw.shouldHaveBoardsEnabled();
// Create and sign in a new user
const {user} = await pw.initSetup();

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

@@ -19,9 +19,6 @@ type CommandArgs struct {
T i18n.TranslateFunc `json:"-"`
UserMentions UserMentionMap `json:"-"`
ChannelMentions ChannelMentionMap `json:"-"`
// DO NOT USE Session field is deprecated. MM-26398
Session Session `json:"-"`
}
func (o *CommandArgs) Auditable() map[string]interface{} {

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

@@ -239,10 +239,10 @@ const (
Office365SettingsDefaultTokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
Office365SettingsDefaultUserAPIEndpoint = "https://graph.microsoft.com/v1.0/me"
CloudSettingsDefaultCwsURL = "https://customers.mattermost.com"
CloudSettingsDefaultCwsURL = "https://customers.cloud.mattermost.com"
CloudSettingsDefaultCwsAPIURL = "https://portal.internal.prod.cloud.mattermost.com"
// TODO: update to "https://portal.test.cloud.mattermost.com" when ready to use test license key
CloudSettingsDefaultCwsURLTest = "https://customers.mattermost.com"
CloudSettingsDefaultCwsURLTest = "https://customers.cloud.mattermost.com"
// TODO: update to // "https://api.internal.test.cloud.mattermost.com" when ready to use test license key
CloudSettingsDefaultCwsAPIURLTest = "https://portal.internal.prod.cloud.mattermost.com"

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@@ -1,4 +1,4 @@
// Code generated by mockery v2.10.4. DO NOT EDIT.
// Code generated by mockery v2.23.2. DO NOT EDIT.
// Regenerate this file using `make plugin-mocks`.
@@ -22,13 +22,16 @@ func (_m *Driver) Conn(isMaster bool) (string, error) {
ret := _m.Called(isMaster)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(bool) (string, error)); ok {
return rf(isMaster)
}
if rf, ok := ret.Get(0).(func(bool) string); ok {
r0 = rf(isMaster)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(bool) error); ok {
r1 = rf(isMaster)
} else {
@@ -57,13 +60,16 @@ func (_m *Driver) ConnExec(connID string, q string, args []driver.NamedValue) (p
ret := _m.Called(connID, q, args)
var r0 plugin.ResultContainer
var r1 error
if rf, ok := ret.Get(0).(func(string, string, []driver.NamedValue) (plugin.ResultContainer, error)); ok {
return rf(connID, q, args)
}
if rf, ok := ret.Get(0).(func(string, string, []driver.NamedValue) plugin.ResultContainer); ok {
r0 = rf(connID, q, args)
} else {
r0 = ret.Get(0).(plugin.ResultContainer)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, []driver.NamedValue) error); ok {
r1 = rf(connID, q, args)
} else {
@@ -92,13 +98,16 @@ func (_m *Driver) ConnQuery(connID string, q string, args []driver.NamedValue) (
ret := _m.Called(connID, q, args)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(string, string, []driver.NamedValue) (string, error)); ok {
return rf(connID, q, args)
}
if rf, ok := ret.Get(0).(func(string, string, []driver.NamedValue) string); ok {
r0 = rf(connID, q, args)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, []driver.NamedValue) error); ok {
r1 = rf(connID, q, args)
} else {
@@ -141,20 +150,23 @@ func (_m *Driver) RowsColumnTypePrecisionScale(rowsID string, index int) (int64,
ret := _m.Called(rowsID, index)
var r0 int64
var r1 int64
var r2 bool
if rf, ok := ret.Get(0).(func(string, int) (int64, int64, bool)); ok {
return rf(rowsID, index)
}
if rf, ok := ret.Get(0).(func(string, int) int64); ok {
r0 = rf(rowsID, index)
} else {
r0 = ret.Get(0).(int64)
}
var r1 int64
if rf, ok := ret.Get(1).(func(string, int) int64); ok {
r1 = rf(rowsID, index)
} else {
r1 = ret.Get(1).(int64)
}
var r2 bool
if rf, ok := ret.Get(2).(func(string, int) bool); ok {
r2 = rf(rowsID, index)
} else {
@@ -227,13 +239,16 @@ func (_m *Driver) Stmt(connID string, q string) (string, error) {
ret := _m.Called(connID, q)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok {
return rf(connID, q)
}
if rf, ok := ret.Get(0).(func(string, string) string); ok {
r0 = rf(connID, q)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(connID, q)
} else {
@@ -262,13 +277,16 @@ func (_m *Driver) StmtExec(stID string, args []driver.NamedValue) (plugin.Result
ret := _m.Called(stID, args)
var r0 plugin.ResultContainer
var r1 error
if rf, ok := ret.Get(0).(func(string, []driver.NamedValue) (plugin.ResultContainer, error)); ok {
return rf(stID, args)
}
if rf, ok := ret.Get(0).(func(string, []driver.NamedValue) plugin.ResultContainer); ok {
r0 = rf(stID, args)
} else {
r0 = ret.Get(0).(plugin.ResultContainer)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, []driver.NamedValue) error); ok {
r1 = rf(stID, args)
} else {
@@ -297,13 +315,16 @@ func (_m *Driver) StmtQuery(stID string, args []driver.NamedValue) (string, erro
ret := _m.Called(stID, args)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(string, []driver.NamedValue) (string, error)); ok {
return rf(stID, args)
}
if rf, ok := ret.Get(0).(func(string, []driver.NamedValue) string); ok {
r0 = rf(stID, args)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, []driver.NamedValue) error); ok {
r1 = rf(stID, args)
} else {
@@ -318,13 +339,16 @@ func (_m *Driver) Tx(connID string, opts driver.TxOptions) (string, error) {
ret := _m.Called(connID, opts)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(string, driver.TxOptions) (string, error)); ok {
return rf(connID, opts)
}
if rf, ok := ret.Get(0).(func(string, driver.TxOptions) string); ok {
r0 = rf(connID, opts)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, driver.TxOptions) error); ok {
r1 = rf(connID, opts)
} else {
@@ -361,3 +385,18 @@ func (_m *Driver) TxRollback(txID string) error {
return r0
}
type mockConstructorTestingTNewDriver interface {
mock.TestingT
Cleanup(func())
}
// NewDriver creates a new instance of Driver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewDriver(t mockConstructorTestingTNewDriver) *Driver {
mock := &Driver{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

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

@@ -1,4 +1,4 @@
// Code generated by mockery v2.10.4. DO NOT EDIT.
// Code generated by mockery v2.23.2. DO NOT EDIT.
// Regenerate this file using `make plugin-mocks`.
@@ -30,6 +30,10 @@ func (_m *Hooks) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo
ret := _m.Called(c, args)
var r0 *model.CommandResponse
var r1 *model.AppError
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.CommandArgs) (*model.CommandResponse, *model.AppError)); ok {
return rf(c, args)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.CommandArgs) *model.CommandResponse); ok {
r0 = rf(c, args)
} else {
@@ -38,7 +42,6 @@ func (_m *Hooks) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(*plugin.Context, *model.CommandArgs) *model.AppError); ok {
r1 = rf(c, args)
} else {
@@ -55,6 +58,10 @@ func (_m *Hooks) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, fil
ret := _m.Called(c, info, file, output)
var r0 *model.FileInfo
var r1 string
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.FileInfo, io.Reader, io.Writer) (*model.FileInfo, string)); ok {
return rf(c, info, file, output)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.FileInfo, io.Reader, io.Writer) *model.FileInfo); ok {
r0 = rf(c, info, file, output)
} else {
@@ -63,7 +70,6 @@ func (_m *Hooks) FileWillBeUploaded(c *plugin.Context, info *model.FileInfo, fil
}
}
var r1 string
if rf, ok := ret.Get(1).(func(*plugin.Context, *model.FileInfo, io.Reader, io.Writer) string); ok {
r1 = rf(c, info, file, output)
} else {
@@ -78,6 +84,10 @@ func (_m *Hooks) GetAllCollectionIDsForUser(c *plugin.Context, userID string, co
ret := _m.Called(c, userID, collectionType)
var r0 []string
var r1 error
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string) ([]string, error)); ok {
return rf(c, userID, collectionType)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string) []string); ok {
r0 = rf(c, userID, collectionType)
} else {
@@ -86,7 +96,6 @@ func (_m *Hooks) GetAllCollectionIDsForUser(c *plugin.Context, userID string, co
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*plugin.Context, string, string) error); ok {
r1 = rf(c, userID, collectionType)
} else {
@@ -101,6 +110,10 @@ func (_m *Hooks) GetAllUserIdsForCollection(c *plugin.Context, collectionType st
ret := _m.Called(c, collectionType, collectionID)
var r0 []string
var r1 error
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string) ([]string, error)); ok {
return rf(c, collectionType, collectionID)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string) []string); ok {
r0 = rf(c, collectionType, collectionID)
} else {
@@ -109,7 +122,6 @@ func (_m *Hooks) GetAllUserIdsForCollection(c *plugin.Context, collectionType st
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*plugin.Context, string, string) error); ok {
r1 = rf(c, collectionType, collectionID)
} else {
@@ -124,6 +136,10 @@ func (_m *Hooks) GetCollectionMetadataByIds(c *plugin.Context, collectionType st
ret := _m.Called(c, collectionType, collectionIds)
var r0 map[string]*model.CollectionMetadata
var r1 error
if rf, ok := ret.Get(0).(func(*plugin.Context, string, []string) (map[string]*model.CollectionMetadata, error)); ok {
return rf(c, collectionType, collectionIds)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, string, []string) map[string]*model.CollectionMetadata); ok {
r0 = rf(c, collectionType, collectionIds)
} else {
@@ -132,7 +148,6 @@ func (_m *Hooks) GetCollectionMetadataByIds(c *plugin.Context, collectionType st
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*plugin.Context, string, []string) error); ok {
r1 = rf(c, collectionType, collectionIds)
} else {
@@ -147,6 +162,10 @@ func (_m *Hooks) GetTopicMetadataByIds(c *plugin.Context, topicType string, topi
ret := _m.Called(c, topicType, topicIds)
var r0 map[string]*model.TopicMetadata
var r1 error
if rf, ok := ret.Get(0).(func(*plugin.Context, string, []string) (map[string]*model.TopicMetadata, error)); ok {
return rf(c, topicType, topicIds)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, string, []string) map[string]*model.TopicMetadata); ok {
r0 = rf(c, topicType, topicIds)
} else {
@@ -155,7 +174,6 @@ func (_m *Hooks) GetTopicMetadataByIds(c *plugin.Context, topicType string, topi
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*plugin.Context, string, []string) error); ok {
r1 = rf(c, topicType, topicIds)
} else {
@@ -170,13 +188,16 @@ func (_m *Hooks) GetTopicRedirect(c *plugin.Context, topicType string, topicID s
ret := _m.Called(c, topicType, topicID)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string) (string, error)); ok {
return rf(c, topicType, topicID)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string) string); ok {
r0 = rf(c, topicType, topicID)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*plugin.Context, string, string) error); ok {
r1 = rf(c, topicType, topicID)
} else {
@@ -191,6 +212,10 @@ func (_m *Hooks) Implemented() ([]string, error) {
ret := _m.Called()
var r0 []string
var r1 error
if rf, ok := ret.Get(0).(func() ([]string, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() []string); ok {
r0 = rf()
} else {
@@ -199,7 +224,6 @@ func (_m *Hooks) Implemented() ([]string, error) {
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
@@ -224,6 +248,10 @@ func (_m *Hooks) MessageWillBePosted(c *plugin.Context, post *model.Post) (*mode
ret := _m.Called(c, post)
var r0 *model.Post
var r1 string
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.Post) (*model.Post, string)); ok {
return rf(c, post)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.Post) *model.Post); ok {
r0 = rf(c, post)
} else {
@@ -232,7 +260,6 @@ func (_m *Hooks) MessageWillBePosted(c *plugin.Context, post *model.Post) (*mode
}
}
var r1 string
if rf, ok := ret.Get(1).(func(*plugin.Context, *model.Post) string); ok {
r1 = rf(c, post)
} else {
@@ -247,6 +274,10 @@ func (_m *Hooks) MessageWillBeUpdated(c *plugin.Context, newPost *model.Post, ol
ret := _m.Called(c, newPost, oldPost)
var r0 *model.Post
var r1 string
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.Post, *model.Post) (*model.Post, string)); ok {
return rf(c, newPost, oldPost)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, *model.Post, *model.Post) *model.Post); ok {
r0 = rf(c, newPost, oldPost)
} else {
@@ -255,7 +286,6 @@ func (_m *Hooks) MessageWillBeUpdated(c *plugin.Context, newPost *model.Post, ol
}
}
var r1 string
if rf, ok := ret.Get(1).(func(*plugin.Context, *model.Post, *model.Post) string); ok {
r1 = rf(c, newPost, oldPost)
} else {
@@ -361,13 +391,16 @@ func (_m *Hooks) RunDataRetention(nowTime int64, batchSize int64) (int64, error)
ret := _m.Called(nowTime, batchSize)
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(int64, int64) (int64, error)); ok {
return rf(nowTime, batchSize)
}
if rf, ok := ret.Get(0).(func(int64, int64) int64); ok {
r0 = rf(nowTime, batchSize)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(int64, int64) error); ok {
r1 = rf(nowTime, batchSize)
} else {
@@ -417,13 +450,16 @@ func (_m *Hooks) UserHasPermissionToCollection(c *plugin.Context, userID string,
ret := _m.Called(c, userID, collectionType, collectionId, permission)
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string, string, *model.Permission) (bool, error)); ok {
return rf(c, userID, collectionType, collectionId, permission)
}
if rf, ok := ret.Get(0).(func(*plugin.Context, string, string, string, *model.Permission) bool); ok {
r0 = rf(c, userID, collectionType, collectionId, permission)
} else {
r0 = ret.Get(0).(bool)
}
var r1 error
if rf, ok := ret.Get(1).(func(*plugin.Context, string, string, string, *model.Permission) error); ok {
r1 = rf(c, userID, collectionType, collectionId, permission)
} else {
@@ -451,3 +487,18 @@ func (_m *Hooks) UserWillLogIn(c *plugin.Context, user *model.User) string {
func (_m *Hooks) WebSocketMessageHasBeenPosted(webConnID string, userID string, req *model.WebSocketRequest) {
_m.Called(webConnID, userID, req)
}
type mockConstructorTestingTNewHooks interface {
mock.TestingT
Cleanup(func())
}
// NewHooks creates a new instance of Hooks. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewHooks(t mockConstructorTestingTNewHooks) *Hooks {
mock := &Hooks{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

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

@@ -142,7 +142,7 @@ TEMPLATES_DIR=templates
PLUGIN_PACKAGES ?= mattermost-plugin-antivirus-v0.1.2
PLUGIN_PACKAGES += mattermost-plugin-autolink-v1.2.2
PLUGIN_PACKAGES += mattermost-plugin-aws-SNS-v1.2.0
PLUGIN_PACKAGES += mattermost-plugin-calls-v0.14.0
PLUGIN_PACKAGES += mattermost-plugin-calls-v0.15.0
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
@@ -318,11 +318,11 @@ i18n-check: ## Exit on empty translation strings and translation source strings
$(GOBIN)/mmgotool i18n check-empty-src --portal-dir=""
store-mocks: ## Creates mock files.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir channels/store --name ".*Store" --output channels/store/storetest/mocks --note 'Regenerate this file using `make store-mocks`.'
telemetry-mocks: ## Creates mock files.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir platform/services/telemetry --all --output platform/services/telemetry/mocks --note 'Regenerate this file using `make telemetry-mocks`.'
store-layers: ## Generate layers for the store
@@ -340,39 +340,39 @@ new-migration: ## Creates a new migration. Run with make new-migration name=<>
$(GOBIN)/morph generate $(name) --driver postgres --dir channels/db/migrations --sequence
filestore-mocks: ## Creates mock files.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir platform/shared/filestore --all --output platform/shared/filestore/mocks --note 'Regenerate this file using `make filestore-mocks`.'
ldap-mocks: ## Creates mock files for ldap.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir $(BUILD_ENTERPRISE_DIR)/ldap --all --output $(BUILD_ENTERPRISE_DIR)/ldap/mocks --note 'Regenerate this file using `make ldap-mocks`.'
plugin-mocks: ## Creates mock files for plugins.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(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`.'
einterfaces-mocks: ## Creates mock files for einterfaces.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir channels/einterfaces --all --output channels/einterfaces/mocks --note 'Regenerate this file using `make einterfaces-mocks`.'
searchengine-mocks: ## Creates mock files for searchengines.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir platform/services/searchengine --all --output platform/services/searchengine/mocks --note 'Regenerate this file using `make searchengine-mocks`.'
sharedchannel-mocks: ## Creates mock files for shared channels.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir=./platform/services/sharedchannel --name=ServerIface --output=./platform/services/sharedchannel --inpackage --outpkg=sharedchannel --testonly --note 'Regenerate this file using `make sharedchannel-mocks`.'
$(GOBIN)/mockery --dir=./platform/services/sharedchannel --name=AppIface --output=./platform/services/sharedchannel --inpackage --outpkg=sharedchannel --testonly --note 'Regenerate this file using `make sharedchannel-mocks`.'
misc-mocks: ## Creates mocks for misc interfaces.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir channels/utils --name LicenseValidatorIface --output channels/utils/mocks --note 'Regenerate this file using `make misc-mocks`.'
$(GOBIN)/mockery --dir channels/app --name WorkTemplateExecutor --output channels/app/mocks --note 'Regenerate this file using `make misc-mocks`.'
email-mocks: ## Creates mocks for misc interfaces.
$(GO) install github.com/vektra/mockery/v2/...@v2.10.4
$(GO) install github.com/vektra/mockery/v2/...@v2.23.2
$(GOBIN)/mockery --dir channels/app/email --name ServiceInterface --output channels/app/email/mocks --note 'Regenerate this file using `make email-mocks`.'
platform-mocks: ## Creates mocks for platform interfaces.

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

@@ -77,10 +77,13 @@ func TestSetConfiguration(t *testing.T) {
})
t.Run("test enable shared boards", func(t *testing.T) {
baseProductSettings := &model.ProductSettings{
EnablePublicSharedBoards: &falseRef,
}
mmConfig := baseConfig
mmConfig.PluginSettings.Plugins = make(map[string]map[string]interface{})
mmConfig.PluginSettings.Plugins[server.PluginName] = make(map[string]interface{})
mmConfig.PluginSettings.Plugins[server.PluginName][server.SharedBoardsName] = true
mmConfig.ProductSettings = *baseProductSettings
mmConfig.ProductSettings.EnablePublicSharedBoards = &boolTrue
config := server.CreateBoardsConfig(*mmConfig, "", "")
assert.Equal(t, true, config.EnablePublicSharedBoards)
})

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

@@ -388,6 +388,11 @@ func (th *TestHelper) TearDown() {
panic(err)
}
err = th.Server.Store().Shutdown()
if err != nil {
panic(err)
}
os.RemoveAll(th.Server.Config().FilesPath)
if err := os.Remove(th.Server.Config().DBConfigString); err == nil {

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

@@ -59,8 +59,8 @@ func CreateBoardsConfig(mmconfig mm_model.Config, baseURL string, serverID strin
}
enablePublicSharedBoards := false
if mmconfig.PluginSettings.Plugins[PluginName][SharedBoardsName] == true {
enablePublicSharedBoards = true
if mmconfig.ProductSettings.EnablePublicSharedBoards != nil {
enablePublicSharedBoards = *mmconfig.ProductSettings.EnablePublicSharedBoards
}
enableBoardsDeletion := false

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

@@ -355,7 +355,7 @@ func (s *Server) Shutdown() error {
defer s.logger.Info("Server.Shutdown")
return s.store.Shutdown()
return nil
}
func (s *Server) Config() *config.Configuration {

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

@@ -177,7 +177,7 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent
*cfg.PasswordSettings.Symbol = false
*cfg.PasswordSettings.Number = false
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
})
if err := th.Server.Start(); err != nil {
panic(err)

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

@@ -353,7 +353,6 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
commandArgs.UserId = c.AppContext.Session().UserId
commandArgs.T = c.AppContext.T
commandArgs.SiteURL = c.GetSiteURLHeader()
commandArgs.Session = *c.AppContext.Session()
response, err := c.App.ExecuteCommand(c.AppContext, &commandArgs)
if err != nil {
@@ -424,7 +423,6 @@ func listCommandAutocompleteSuggestions(c *Context, w http.ResponseWriter, r *ht
RootId: query.Get("root_id"),
UserId: c.AppContext.Session().UserId,
T: c.AppContext.T,
Session: *c.AppContext.Session(),
SiteURL: c.GetSiteURLHeader(),
Command: userInput,
}

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

@@ -22,7 +22,7 @@ func TestAppRace(t *testing.T) {
for i := 0; i < 10; i++ {
a, err := New()
require.NoError(t, err)
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
a.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = "localhost:0" })
serverErr := a.StartServer()
require.NoError(t, serverErr)
a.Srv().Shutdown()

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

@@ -278,6 +278,7 @@ func TestGetDraftsForUser(t *testing.T) {
assert.Nil(t, createDraftErr2)
t.Run("get drafts", func(t *testing.T) {
t.Skip("MM-52088")
draftResp, err := th.App.GetDraftsForUser(user.Id, th.BasicTeam.Id)
assert.Nil(t, err)

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

@@ -1,4 +1,4 @@
// Code generated by mockery v2.10.4. DO NOT EDIT.
// Code generated by mockery v2.23.2. DO NOT EDIT.
// Regenerate this file using `make email-mocks`.
@@ -44,6 +44,10 @@ func (_m *ServiceInterface) CreateVerifyEmailToken(userID string, newEmail strin
ret := _m.Called(userID, newEmail)
var r0 *model.Token
var r1 error
if rf, ok := ret.Get(0).(func(string, string) (*model.Token, error)); ok {
return rf(userID, newEmail)
}
if rf, ok := ret.Get(0).(func(string, string) *model.Token); ok {
r0 = rf(userID, newEmail)
} else {
@@ -52,7 +56,6 @@ func (_m *ServiceInterface) CreateVerifyEmailToken(userID string, newEmail strin
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(userID, newEmail)
} else {
@@ -326,6 +329,10 @@ func (_m *ServiceInterface) SendInviteEmailsToTeamAndChannels(team *model.Team,
ret := _m.Called(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
var r0 []*model.EmailInviteWithError
var r1 error
if rf, ok := ret.Get(0).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool, bool, bool) ([]*model.EmailInviteWithError, error)); ok {
return rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
}
if rf, ok := ret.Get(0).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool, bool, bool) []*model.EmailInviteWithError); ok {
r0 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
} else {
@@ -334,7 +341,6 @@ func (_m *ServiceInterface) SendInviteEmailsToTeamAndChannels(team *model.Team,
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool, bool, bool) error); ok {
r1 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent, isSystemAdmin, isFirstAdmin)
} else {
@@ -433,13 +439,16 @@ func (_m *ServiceInterface) SendPasswordResetEmail(_a0 string, token *model.Toke
ret := _m.Called(_a0, token, locale, siteURL)
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(string, *model.Token, string, string) (bool, error)); ok {
return rf(_a0, token, locale, siteURL)
}
if rf, ok := ret.Get(0).(func(string, *model.Token, string, string) bool); ok {
r0 = rf(_a0, token, locale, siteURL)
} else {
r0 = ret.Get(0).(bool)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, *model.Token, string, string) error); ok {
r1 = rf(_a0, token, locale, siteURL)
} else {
@@ -454,13 +463,16 @@ func (_m *ServiceInterface) SendPaymentFailedEmail(_a0 string, locale string, fa
ret := _m.Called(_a0, locale, failedPayment, planName, siteURL)
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(string, string, *model.FailedPayment, string, string) (bool, error)); ok {
return rf(_a0, locale, failedPayment, planName, siteURL)
}
if rf, ok := ret.Get(0).(func(string, string, *model.FailedPayment, string, string) bool); ok {
r0 = rf(_a0, locale, failedPayment, planName, siteURL)
} else {
r0 = ret.Get(0).(bool)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, *model.FailedPayment, string, string) error); ok {
r1 = rf(_a0, locale, failedPayment, planName, siteURL)
} else {
@@ -544,3 +556,18 @@ func (_m *ServiceInterface) SendWelcomeEmail(userID string, _a1 string, verified
func (_m *ServiceInterface) Stop() {
_m.Called()
}
type mockConstructorTestingTNewServiceInterface interface {
mock.TestingT
Cleanup(func())
}
// NewServiceInterface creates a new instance of ServiceInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewServiceInterface(t mockConstructorTestingTNewServiceInterface) *ServiceInterface {
mock := &ServiceInterface{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

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

@@ -102,7 +102,7 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxUsersPerTeam = 50 })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.RateLimitSettings.Enable = false })
prevListenAddress := *th.App.Config().ServiceSettings.ListenAddress
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = ":0" })
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = "localhost:0" })
serverErr := th.Server.Start()
if serverErr != nil {
panic(serverErr)

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

@@ -1,4 +1,4 @@
// Code generated by mockery v2.10.4. DO NOT EDIT.
// Code generated by mockery v2.23.2. DO NOT EDIT.
// Regenerate this file using `make misc-mocks`.
@@ -22,13 +22,16 @@ func (_m *WorkTemplateExecutor) CreateBoard(c *request.Context, wtcr *worktempla
ret := _m.Called(c, wtcr, cBoard, linkToChannelID)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplateBoard, string) (string, error)); ok {
return rf(c, wtcr, cBoard, linkToChannelID)
}
if rf, ok := ret.Get(0).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplateBoard, string) string); ok {
r0 = rf(c, wtcr, cBoard, linkToChannelID)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplateBoard, string) error); ok {
r1 = rf(c, wtcr, cBoard, linkToChannelID)
} else {
@@ -43,13 +46,16 @@ func (_m *WorkTemplateExecutor) CreateChannel(c *request.Context, wtcr *worktemp
ret := _m.Called(c, wtcr, cChannel)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplateChannel) (string, error)); ok {
return rf(c, wtcr, cChannel)
}
if rf, ok := ret.Get(0).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplateChannel) string); ok {
r0 = rf(c, wtcr, cChannel)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplateChannel) error); ok {
r1 = rf(c, wtcr, cChannel)
} else {
@@ -64,13 +70,16 @@ func (_m *WorkTemplateExecutor) CreatePlaybook(c *request.Context, wtcr *worktem
ret := _m.Called(c, wtcr, playbook, channel)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplatePlaybook, model.WorkTemplateChannel) (string, error)); ok {
return rf(c, wtcr, playbook, channel)
}
if rf, ok := ret.Get(0).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplatePlaybook, model.WorkTemplateChannel) string); ok {
r0 = rf(c, wtcr, playbook, channel)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*request.Context, *worktemplates.ExecutionRequest, *model.WorkTemplatePlaybook, model.WorkTemplateChannel) error); ok {
r1 = rf(c, wtcr, playbook, channel)
} else {
@@ -93,3 +102,18 @@ func (_m *WorkTemplateExecutor) InstallPlugin(c *request.Context, wtcr *worktemp
return r0
}
type mockConstructorTestingTNewWorkTemplateExecutor interface {
mock.TestingT
Cleanup(func())
}
// NewWorkTemplateExecutor creates a new instance of WorkTemplateExecutor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewWorkTemplateExecutor(t mockConstructorTestingTNewWorkTemplateExecutor) *WorkTemplateExecutor {
mock := &WorkTemplateExecutor{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

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

@@ -309,6 +309,10 @@ func (a *App) GetOAuthAccessTokenForCodeFlow(clientId, grantType, redirectURI, c
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal_user.app_error", nil, "", http.StatusNotFound)
}
if user.DeleteAt != 0 {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.expired_code.app_error", nil, "", http.StatusForbidden)
}
accessData, nErr = a.Srv().Store().OAuth().GetPreviousAccessData(user.Id, clientId)
if nErr != nil {
return nil, model.NewAppError("GetOAuthAccessToken", "api.oauth.get_access_token.internal.app_error", nil, "", http.StatusBadRequest)

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

@@ -633,3 +633,47 @@ func TestDeauthorizeOAuthApp(t *testing.T) {
require.Equal(t, store.NewErrNotFound("AuthData", fmt.Sprintf("code=%s", code)), nErr)
assert.Nil(t, data)
}
func TestDeactivatedUserOAuthApp(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableOAuthServiceProvider = true })
oapp := &model.OAuthApp{
Name: "fakeoauthapp" + model.NewRandomString(10),
CreatorId: th.BasicUser2.Id,
Homepage: "https://nowhere.com",
Description: "test",
CallbackUrls: []string{"https://nowhere.com"},
}
oapp, err := th.App.CreateOAuthApp(oapp)
require.Nil(t, err)
authRequest := &model.AuthorizeRequest{
ResponseType: model.ImplicitResponseType,
ClientId: oapp.Id,
RedirectURI: oapp.CallbackUrls[0],
Scope: "",
State: "123",
}
redirectUrl, err := th.App.GetOAuthCodeRedirect(th.BasicUser.Id, authRequest)
assert.Nil(t, err)
uri, uErr := url.Parse(redirectUrl)
require.NoError(t, uErr)
queryParams := uri.Query()
code := queryParams.Get("code")
_, appErr := th.App.UpdateActive(th.Context, th.BasicUser, false)
require.Nil(t, appErr)
resp, accErr := th.App.GetOAuthAccessTokenForCodeFlow(oapp.Id, model.AccessTokenGrantType, oapp.CallbackUrls[0], code, oapp.ClientSecret, "")
assert.Nil(t, resp)
require.NotNil(t, accErr, "Should not get access token")
require.Equal(t, http.StatusBadRequest, accErr.StatusCode)
assert.Equal(t, "api.oauth.get_access_token.expired_code.app_error", accErr.Id)
}

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

@@ -143,8 +143,8 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
*memoryConfig.AnnouncementSettings.AdminNoticesEnabled = false
*memoryConfig.AnnouncementSettings.UserNoticesEnabled = false
*memoryConfig.MetricsSettings.Enable = true
*memoryConfig.ServiceSettings.ListenAddress = ":0"
*memoryConfig.MetricsSettings.ListenAddress = ":0"
*memoryConfig.ServiceSettings.ListenAddress = "localhost:0"
*memoryConfig.MetricsSettings.ListenAddress = "localhost:0"
configStore.Set(memoryConfig)
ps, err := New(ServiceConfig{

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

@@ -112,6 +112,7 @@ func TestMetrics(t *testing.T) {
require.NotNil(t, th.Service.metrics)
metricsAddr := strings.Replace(th.Service.metrics.listenAddr, "[::]", "http://localhost", 1)
metricsAddr = strings.Replace(metricsAddr, "127.0.0.1", "http://localhost", 1)
resp, err := http.Get(metricsAddr)
require.NoError(t, err)

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

@@ -547,6 +547,16 @@ func (a *App) SendEphemeralPost(c request.CTX, userID string, post *model.Post)
post = a.PreparePostForClientWithEmbedsAndImages(c, post, true, false, true)
post = model.AddPostActionCookies(post, a.PostActionCookieSecret())
sanitizedPost, appErr := a.SanitizePostMetadataForUser(c, post, userID)
if appErr != nil {
mlog.Error("Failed to sanitize post metadata for user", mlog.String("user_id", userID), mlog.Err(appErr))
// If we failed to sanitize the post, we still want to remove the metadata.
sanitizedPost = post.Clone()
sanitizedPost.Metadata = nil
}
post = sanitizedPost
postJSON, jsonErr := post.ToJSON()
if jsonErr != nil {
mlog.Warn("Failed to encode post to JSON", mlog.Err(jsonErr))

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

@@ -4,6 +4,7 @@
package app
import (
"bufio"
"bytes"
"fmt"
"image"
@@ -764,7 +765,24 @@ func cacheLinkMetadata(requestURL string, timestamp int64, og *opengraph.OpenGra
platform.LinkCache().SetWithExpiry(strconv.FormatInt(model.GenerateLinkMetadataHash(requestURL, timestamp), 16), metadata, platform.LinkCacheDuration)
}
// peekContentType peeks at the first 512 bytes of p, and attempts to detect
// the content type. Returns empty string if error occurs.
func peekContentType(p *bufio.Reader) string {
byt, err := p.Peek(512)
if err != nil && err != bufio.ErrBufferFull && err != io.EOF {
return ""
}
return http.DetectContentType(byt)
}
func (a *App) parseLinkMetadata(requestURL string, body io.Reader, contentType string) (*opengraph.OpenGraph, *model.PostImage, error) {
if contentType == "" {
bufRd := bufio.NewReader(body)
// If the content-type is missing we try to detect it from the actual data.
contentType = peekContentType(bufRd)
body = bufRd
}
if contentType == "image/svg+xml" {
image := &model.PostImage{
Format: "svg",

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

@@ -2595,6 +2595,18 @@ func TestParseLinkMetadata(t *testing.T) {
}, dimensions)
})
t.Run("image with no content-type given", func(t *testing.T) {
og, dimensions, err := th.App.parseLinkMetadata(imageURL, makeImageReader(), "")
assert.NoError(t, err)
assert.Nil(t, og)
assert.Equal(t, &model.PostImage{
Format: "png",
Width: 408,
Height: 336,
}, dimensions)
})
t.Run("malformed image", func(t *testing.T) {
og, dimensions, err := th.App.parseLinkMetadata(imageURL, makeOpenGraphReader(), "image/png")
assert.Error(t, err)

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

@@ -47,7 +47,7 @@ func newServerWithConfig(t *testing.T, f func(cfg *model.Config)) (*Server, erro
func TestStartServerSuccess(t *testing.T) {
s, err := newServerWithConfig(t, func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
})
require.NoError(t, err)
@@ -65,7 +65,7 @@ func TestStartServerPortUnavailable(t *testing.T) {
require.NoError(t, err)
// Listen on the next available port
listener, err := net.Listen("tcp", ":0")
listener, err := net.Listen("tcp", "localhost:0")
require.NoError(t, err)
// Attempt to listen on the port used above.
@@ -104,7 +104,7 @@ func TestStartServerNoS3Bucket(t *testing.T) {
AmazonS3PathPrefix: model.NewString(""),
AmazonS3SSL: model.NewBool(false),
}
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
_, _, err := store.Set(cfg)
require.NoError(t, err)
@@ -131,7 +131,7 @@ func TestStartServerTLSSuccess(t *testing.T) {
s, err := newServerWithConfig(t, func(cfg *model.Config) {
testDir, _ := fileutils.FindDir("tests")
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
@@ -185,7 +185,7 @@ func TestStartServerTLSVersion(t *testing.T) {
cfg := store.Get()
testDir, _ := fileutils.FindDir("tests")
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSMinVer = "1.2"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
@@ -229,7 +229,7 @@ func TestStartServerTLSOverwriteCipher(t *testing.T) {
s, err := newServerWithConfig(t, func(cfg *model.Config) {
testDir, _ := fileutils.FindDir("tests")
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
cfg.ServiceSettings.TLSOverwriteCiphers = []string{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
@@ -328,7 +328,7 @@ func TestPanicLog(t *testing.T) {
testDir, _ := fileutils.FindDir("tests")
s.platform.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")
@@ -404,7 +404,7 @@ func TestSentry(t *testing.T) {
SentryDSN = dsn.String()
s, err := newServerWithConfig(t, func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
*cfg.LogSettings.EnableSentry = false
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
@@ -448,7 +448,7 @@ func TestSentry(t *testing.T) {
SentryDSN = dsn.String()
s, err := newServerWithConfig(t, func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ListenAddress = "localhost:0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")
*cfg.ServiceSettings.TLSCertFile = path.Join(testDir, "tls_test_cert.pem")

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше