From a1ed780a861b47df70d26f0fae417a3795a90606 Mon Sep 17 00:00:00 2001 From: Umar Abid <75561750+umrkhn@users.noreply.github.com> Date: Mon, 6 May 2024 15:06:24 +0500 Subject: [PATCH] [MM-55327]: Convert /components/permissions_gates/team_permission_gate/team_permission_gate.tsx from Class Component to Function Component (#25395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: convert to func component * fix: update failing test snapshots * Handle tests better * Standarize all gates --------- Co-authored-by: Mattermost Build Co-authored-by: Daniel Espino GarcĂ­a --- .../__snapshots__/actions_menu.test.tsx.snap | 4 +- .../browse_channels.test.tsx.snap | 8 +- .../channel_header_dropdown.test.tsx.snap | 56 +-- .../product_menu_list.test.tsx.snap | 36 +- .../abstract_oauth_app.test.tsx.snap | 8 +- .../__snapshots__/main_menu.test.tsx.snap | 246 +++++------ .../any_team_permission_gate.test.tsx | 142 ++++++ .../any_team_permission_gate.tsx | 56 +++ .../any_team_permission_gate.test.tsx.snap | 299 ------------- .../any_team_permission_gate.test.tsx | 154 ------- .../any_team_permission_gate.tsx | 37 -- .../any_team_permission_gate/index.ts | 30 -- .../channel_permission_gate.test.tsx | 192 +++++++++ .../channel_permission_gate.tsx | 64 +++ .../channel_permission_gate.test.tsx.snap | 408 ------------------ .../channel_permission_gate.test.tsx | 204 --------- .../channel_permission_gate.tsx | 46 -- .../channel_permission_gate/index.ts | 32 -- .../mattermost.code-workspace | 20 - .../permissions_gates/gate.test.tsx | 34 ++ .../src/components/permissions_gates/gate.tsx | 23 + .../system_permission_gate.test.tsx | 101 +++++ .../system_permission_gate.tsx | 52 +++ .../system_permission_gate.test.tsx.snap | 184 -------- .../system_permission_gate/index.ts | 25 -- .../system_permission_gate.test.tsx | 108 ----- .../system_permission_gate.tsx | 39 -- .../team_permission_gate.test.tsx | 138 ++++++ .../team_permission_gate.tsx | 66 +++ .../team_permission_gate.test.tsx.snap | 273 ------------ .../team_permission_gate/index.ts | 31 -- .../team_permission_gate.test.tsx | 147 ------- .../team_permission_gate.tsx | 38 -- .../add_members_button.test.tsx.snap | 188 -------- .../add_members_button.test.tsx | 51 +-- .../add_members_button.tsx | 3 +- .../__snapshots__/post_reaction.test.tsx.snap | 4 +- .../reactions_list.test.tsx.snap | 4 +- .../__snapshots__/select_team.test.tsx.snap | 60 +-- .../invite_members_button.test.tsx.snap | 16 +- .../team_members_modal.test.tsx.snap | 4 +- .../main_menu_action.test.tsx.snap | 64 +-- 42 files changed, 1142 insertions(+), 2553 deletions(-) create mode 100644 webapp/channels/src/components/permissions_gates/any_team_permission_gate.test.tsx create mode 100644 webapp/channels/src/components/permissions_gates/any_team_permission_gate.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/any_team_permission_gate/__snapshots__/any_team_permission_gate.test.tsx.snap delete mode 100644 webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.test.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/any_team_permission_gate/index.ts create mode 100644 webapp/channels/src/components/permissions_gates/channel_permission_gate.test.tsx create mode 100644 webapp/channels/src/components/permissions_gates/channel_permission_gate.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/channel_permission_gate/__snapshots__/channel_permission_gate.test.tsx.snap delete mode 100644 webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.test.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/channel_permission_gate/index.ts delete mode 100644 webapp/channels/src/components/permissions_gates/channel_permission_gate/mattermost.code-workspace create mode 100644 webapp/channels/src/components/permissions_gates/gate.test.tsx create mode 100644 webapp/channels/src/components/permissions_gates/gate.tsx create mode 100644 webapp/channels/src/components/permissions_gates/system_permission_gate.test.tsx create mode 100644 webapp/channels/src/components/permissions_gates/system_permission_gate.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/system_permission_gate/__snapshots__/system_permission_gate.test.tsx.snap delete mode 100644 webapp/channels/src/components/permissions_gates/system_permission_gate/index.ts delete mode 100644 webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.test.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.tsx create mode 100644 webapp/channels/src/components/permissions_gates/team_permission_gate.test.tsx create mode 100644 webapp/channels/src/components/permissions_gates/team_permission_gate.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/team_permission_gate/__snapshots__/team_permission_gate.test.tsx.snap delete mode 100644 webapp/channels/src/components/permissions_gates/team_permission_gate/index.ts delete mode 100644 webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.test.tsx delete mode 100644 webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.tsx delete mode 100644 webapp/channels/src/components/post_view/channel_intro_message/__snapshots__/add_members_button.test.tsx.snap diff --git a/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu.test.tsx.snap b/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu.test.tsx.snap index 3f54495e47..ebc4b62266 100644 --- a/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu.test.tsx.snap +++ b/webapp/channels/src/components/actions_menu/__snapshots__/actions_menu.test.tsx.snap @@ -206,7 +206,7 @@ exports[`components/actions_menu/ActionsMenu no actions - sysadmin - menu should openLeft={true} openUp={false} > - - + `; diff --git a/webapp/channels/src/components/browse_channels/__snapshots__/browse_channels.test.tsx.snap b/webapp/channels/src/components/browse_channels/__snapshots__/browse_channels.test.tsx.snap index 5c64f04fe7..6138df0970 100644 --- a/webapp/channels/src/components/browse_channels/__snapshots__/browse_channels.test.tsx.snap +++ b/webapp/channels/src/components/browse_channels/__snapshots__/browse_channels.test.tsx.snap @@ -10,7 +10,7 @@ exports[`components/BrowseChannels should match snapshot and state 1`] = ` compassDesign={true} enforceFocus={false} headerButton={ - - + } id="browseChannelsModal" keyboardEscape={true} @@ -128,7 +128,7 @@ exports[`components/BrowseChannels should match snapshot and state 1`] = ` id="more_channels.searchError" />

- - + } rememberHideJoinedChannelsChecked={false} diff --git a/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap b/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap index 65a59bdc6a..78238d26dd 100644 --- a/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap +++ b/webapp/channels/src/components/channel_header_dropdown/__snapshots__/channel_header_dropdown.test.tsx.snap @@ -233,7 +233,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with no plugin i /> - - + - - - + - + - - - + - + - - + - - + `; @@ -1065,7 +1065,7 @@ exports[`components/ChannelHeaderDropdown should match snapshot with plugins 1`] /> - - + - - - + - + - - - + - + - - + - - + `; diff --git a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap index dec2d91f64..bf0ee289ee 100644 --- a/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap +++ b/webapp/channels/src/components/global_header/left_controls/product_menu/product_menu_list/__snapshots__/product_menu_list.test.tsx.snap @@ -11,7 +11,7 @@ exports[`components/global/product_switcher_menu should match snapshot with id 1 - - - + - + - - + - - - + - + - - + - - - + - + - - + - - +
@@ -337,7 +337,7 @@ exports[`components/integrations/AbstractOAuthApp should match snapshot, display
-
- +
diff --git a/webapp/channels/src/components/main_menu/__snapshots__/main_menu.test.tsx.snap b/webapp/channels/src/components/main_menu/__snapshots__/main_menu.test.tsx.snap index 87b9a61c44..b76dd22b68 100644 --- a/webapp/channels/src/components/main_menu/__snapshots__/main_menu.test.tsx.snap +++ b/webapp/channels/src/components/main_menu/__snapshots__/main_menu.test.tsx.snap @@ -55,7 +55,7 @@ exports[`components/Menu should match snapshot with guest access disabled and no ariaLabel="team menu" > - - - + - - - + - - + - - + - + - - +
- - - + - - + - - + - - + - - + - + - - +
- - - + - - + - - + - - + - - + - + - - +
- - + - - + - - - + - + - - - + - - + - - + - + - - + - - - + - - + - - + - - + - - + - + - - +
- - + - - + - - - + - + - - - + - - + - - + - + - - + - - - + - - + - - + - - + - - + - + - - +
- - + - - + - - - + - + - - - + - - + - - + - + - - + { + const state = { + entities: { + channels: { + myMembers: { + channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, + }, + }, + teams: { + teams: { + team_id: {id: 'team_id', delete_at: 0}, + team_id2: {id: 'team_id2', delete_at: 0}, + }, + myMembers: { + team_id: {team_id: 'team_id', roles: 'team_role'}, + team_id2: {team_id: 'team_id2', roles: 'team_role2'}, + }, + }, + users: { + currentUserId: 'user_id', + profiles: { + user_id: { + id: 'user_id', + roles: 'system_role', + }, + }, + }, + roles: { + roles: { + system_role: {permissions: ['test_system_permission']}, + team_role: {permissions: ['test_team_permission']}, + team_role2: {permissions: ['other_permission']}, + channel_role: {permissions: ['test_channel_permission']}, + }, + }, + }, + }; + const CONTENT = 'The content inside the permission gate'; + + describe('TeamPermissionGate', () => { + test('should show content when user have permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should show content when user have the permission in other team', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should show content when user have at least one of the permissions', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + }); + test('should NOT show content when user have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should show content when user not have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should NOT show content when user have the permission in other team and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should NOT show content when user doesn\'t have permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should show content when user have permission system wide', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + }); +}); diff --git a/webapp/channels/src/components/permissions_gates/any_team_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/any_team_permission_gate.tsx new file mode 100644 index 0000000000..87c780ad78 --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/any_team_permission_gate.tsx @@ -0,0 +1,56 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {useSelector} from 'react-redux'; + +import type {GlobalState} from '@mattermost/types/store'; + +import {haveITeamPermission} from 'mattermost-redux/selectors/entities/roles'; +import {getMyTeams} from 'mattermost-redux/selectors/entities/teams'; + +import Gate from './gate'; + +export type Props = { + + /** + * Permissions enough to pass the gate (binary OR) + */ + permissions: string[]; + + /** + * Invert the permission (used for else) + */ + invert?: boolean; + + /** + * Content protected by the permissions gate + */ + children: React.ReactNode; +}; + +const AnyTeamPermissionGate = ({permissions, children, invert = false}: Props) => { + const hasPermission = useSelector((state: GlobalState) => { + const teams = getMyTeams(state); + for (const team of teams) { + for (const permission of permissions) { + if (haveITeamPermission(state, team.id, permission)) { + return true; + } + } + } + + return false; + }); + + return ( + + {children} + + ); +}; + +export default React.memo(AnyTeamPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/__snapshots__/any_team_permission_gate.test.tsx.snap b/webapp/channels/src/components/permissions_gates/any_team_permission_gate/__snapshots__/any_team_permission_gate.test.tsx.snap deleted file mode 100644 index ca7f6a145d..0000000000 --- a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/__snapshots__/any_team_permission_gate.test.tsx.snap +++ /dev/null @@ -1,299 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user doesn't have permission 1`] = ` - - - -

- - - -`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have at least one of the permissions 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have permission 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have permission and use invert 1`] = ` - - - -

- - - -`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have permission system wide 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have the permission in other team 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have the permission in other team and use invert 1`] = ` - - - -

- - - -`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user not have permission and use invert 1`] = ` - - - -

- Invalid permission but inverted (shown) -

-
-
-
-`; diff --git a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.test.tsx b/webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.test.tsx deleted file mode 100644 index 65261da847..0000000000 --- a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.test.tsx +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {mount} from 'enzyme'; -import React from 'react'; -import {Provider} from 'react-redux'; - -import AnyTeamPermissionGate from 'components/permissions_gates/any_team_permission_gate'; - -import mockStore from 'tests/test_store'; - -describe('components/permissions_gates', () => { - const state = { - entities: { - channels: { - myMembers: { - channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, - }, - }, - teams: { - teams: { - team_id: {id: 'team_id', delete_at: 0}, - team_id2: {id: 'team_id2', delete_at: 0}, - }, - myMembers: { - team_id: {team_id: 'team_id', roles: 'team_role'}, - team_id2: {team_id: 'team_id2', roles: 'team_role2'}, - }, - }, - users: { - currentUserId: 'user_id', - profiles: { - user_id: { - id: 'user_id', - roles: 'system_role', - }, - }, - }, - roles: { - roles: { - system_role: {permissions: ['test_system_permission']}, - team_role: {permissions: ['test_team_permission']}, - team_role2: {permissions: ['other_permission']}, - channel_role: {permissions: ['test_channel_permission']}, - }, - }, - }, - }; - const store = mockStore(state); - - describe('TeamPermissionGate', () => { - test('should match snapshot when user have permission', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have the permission in other team', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have at least one of the permissions', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission and use invert', () => { - const wrapper = mount( - - -

{'Valid permission but inverted (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user not have permission and use invert', () => { - const wrapper = mount( - - -

{'Invalid permission but inverted (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have the permission in other team and use invert', () => { - const wrapper = mount( - - -

{'Valid permission but inverted (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user doesn\'t have permission', () => { - const wrapper = mount( - - -

{'Invalid permission (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission system wide', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - }); -}); diff --git a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.tsx deleted file mode 100644 index 43095bd953..0000000000 --- a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/any_team_permission_gate.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; - -export type Props = { - - /** - * Permissions enough to pass the gate (binary OR) - */ - permissions: string[]; - - /** - * Has permission - */ - hasPermission: boolean; - - /** - * Invert the permission (used for else) - */ - invert?: boolean; - - /** - * Content protected by the permissions gate - */ - children: React.ReactNode; -}; - -const AnyTeamPermissionGate = ({hasPermission, children, invert = false}: Props) => { - return ( - <> - {hasPermission === invert ? null : children} - - ); -}; - -export default React.memo(AnyTeamPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/index.ts b/webapp/channels/src/components/permissions_gates/any_team_permission_gate/index.ts deleted file mode 100644 index e4d0a8076f..0000000000 --- a/webapp/channels/src/components/permissions_gates/any_team_permission_gate/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {connect} from 'react-redux'; - -import type {GlobalState} from '@mattermost/types/store'; - -import {haveITeamPermission} from 'mattermost-redux/selectors/entities/roles'; -import {getMyTeams} from 'mattermost-redux/selectors/entities/teams'; - -import AnyTeamPermissionGate from './any_team_permission_gate'; - -type Props = { - permissions: string[]; -} - -function mapStateToProps(state: GlobalState, ownProps: Props) { - const teams = getMyTeams(state); - for (const team of teams) { - for (const permission of ownProps.permissions) { - if (haveITeamPermission(state, team.id, permission)) { - return {hasPermission: true}; - } - } - } - - return {hasPermission: false}; -} - -export default connect(mapStateToProps)(AnyTeamPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/channel_permission_gate.test.tsx b/webapp/channels/src/components/permissions_gates/channel_permission_gate.test.tsx new file mode 100644 index 0000000000..1e4836675d --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/channel_permission_gate.test.tsx @@ -0,0 +1,192 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {renderWithContext, screen} from 'tests/react_testing_utils'; + +import ChannelPermissionGate from './channel_permission_gate'; + +describe('components/permissions_gates', () => { + const state = { + entities: { + channels: { + myMembers: { + channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, + }, + roles: { + channel_id: new Set(['channel_role']), + }, + }, + teams: { + myMembers: { + team_id: {team_id: 'team_id', roles: 'team_role'}, + }, + }, + users: { + currentUserId: 'user_id', + profiles: { + user_id: { + id: 'user_id', + roles: 'system_role', + }, + }, + }, + roles: { + roles: { + system_role: {permissions: ['test_system_permission']}, + team_role: {permissions: ['test_team_permission']}, + channel_role: {permissions: ['test_channel_permission']}, + }, + }, + }, + }; + const CONTENT = 'The content inside the permission gate'; + + describe('ChannelPermissionGate', () => { + test('should show content when user have permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should show content when user have at least on of the permissions', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should NOT show content when user have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should show content when user not have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should NOT show content when user haven\'t permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should NOT show content when the channel doesn\'t exists', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should show content when user have permission team wide', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should show content when user have permission system wide', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + + test('should show content when user have permissions in DM and GM', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + + test('should NOT show content when user does not have permissions in DM and GM', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/webapp/channels/src/components/permissions_gates/channel_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/channel_permission_gate.tsx new file mode 100644 index 0000000000..f0bf4fd32f --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/channel_permission_gate.tsx @@ -0,0 +1,64 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {useSelector} from 'react-redux'; + +import type {GlobalState} from '@mattermost/types/store'; + +import {haveIChannelPermission} from 'mattermost-redux/selectors/entities/roles'; + +import Gate from './gate'; + +type Props = { + + /** + * Channel to check the permission + */ + channelId?: string; + + /** + * Team to check the permission + */ + teamId?: string; + + /** + * Permissions enough to pass the gate (binary OR) + */ + permissions: string[]; + + /** + * Invert the permission (used for else) + */ + invert?: boolean; + + /** + * Content protected by the permissions gate + */ + children: React.ReactNode; +} + +const ChannelPermissionGate = ({channelId, teamId, permissions, children, invert = false}: Props) => { + const hasPermission = useSelector((state: GlobalState) => { + if (!channelId || teamId === null || typeof teamId === 'undefined') { + return false; + } + for (const permission of permissions) { + if (haveIChannelPermission(state, teamId, channelId, permission)) { + return true; + } + } + return false; + }); + + return ( + + {children} + + ); +}; + +export default React.memo(ChannelPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/channel_permission_gate/__snapshots__/channel_permission_gate.test.tsx.snap b/webapp/channels/src/components/permissions_gates/channel_permission_gate/__snapshots__/channel_permission_gate.test.tsx.snap deleted file mode 100644 index 12e4fe151a..0000000000 --- a/webapp/channels/src/components/permissions_gates/channel_permission_gate/__snapshots__/channel_permission_gate.test.tsx.snap +++ /dev/null @@ -1,408 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when the channel doesn't exists 1`] = ` - - - -

- - - -`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user does not have permissions in DM and GM 1`] = ` - - - -

- - - -`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user have at least on of the permissions 1`] = ` - - - -

- Valid permission (shown) -

- - -
-`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user have permission 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user have permission and use invert 1`] = ` - - - -

- - - -`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user have permission system wide 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user have permission team wide 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user have permissions in DM and GM 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user haven't permission 1`] = ` - - - -

- - - -`; - -exports[`components/permissions_gates ChannelPermissionGate should match snapshot when user not have permission and use invert 1`] = ` - - - -

- Invalid permission but inverted (shown) -

-
-
-
-`; diff --git a/webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.test.tsx b/webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.test.tsx deleted file mode 100644 index 41a4162b7c..0000000000 --- a/webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.test.tsx +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {mount} from 'enzyme'; -import React from 'react'; -import {Provider} from 'react-redux'; - -import ChannelPermissionGate from 'components/permissions_gates/channel_permission_gate'; -import TeamPermissionGate from 'components/permissions_gates/team_permission_gate'; - -import mockStore from 'tests/test_store'; - -describe('components/permissions_gates', () => { - const state = { - entities: { - channels: { - myMembers: { - channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, - }, - roles: { - channel_id: new Set(['channel_role']), - }, - }, - teams: { - myMembers: { - team_id: {team_id: 'team_id', roles: 'team_role'}, - }, - }, - users: { - currentUserId: 'user_id', - profiles: { - user_id: { - id: 'user_id', - roles: 'system_role', - }, - }, - }, - roles: { - roles: { - system_role: {permissions: ['test_system_permission']}, - team_role: {permissions: ['test_team_permission']}, - channel_role: {permissions: ['test_channel_permission']}, - }, - }, - }, - }; - const store = mockStore(state); - - describe('ChannelPermissionGate', () => { - test('should match snapshot when user have permission', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have at least on of the permissions', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission and use invert', () => { - const wrapper = mount( - - -

{'Valid permission but inverted (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user not have permission and use invert', () => { - const wrapper = mount( - - -

{'Invalid permission but inverted (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user haven\'t permission', () => { - const wrapper = mount( - - -

{'Invalid permission (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when the channel doesn\'t exists', () => { - const wrapper = mount( - - -

{'Valid permission invalid channel (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission team wide', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission system wide', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot when user have permissions in DM and GM', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - - test('should match snapshot when user does not have permissions in DM and GM', () => { - const wrapper = mount( - - -

{'Invalid permission (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - }); -}); diff --git a/webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.tsx deleted file mode 100644 index 27d612a041..0000000000 --- a/webapp/channels/src/components/permissions_gates/channel_permission_gate/channel_permission_gate.tsx +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; - -type Props = { - - /** - * Channel to check the permission - */ - channelId?: string; - - /** - * Team to check the permission - */ - teamId?: string; - - /** - * Permissions enough to pass the gate (binary OR) - */ - permissions: string[]; - - /** - * Has permission - */ - hasPermission: boolean; - - /** - * Invert the permission (used for else) - */ - invert?: boolean; - - /** - * Content protected by the permissions gate - */ - children: React.ReactNode; -} - -const ChannelPermissionGate = ({hasPermission, children, invert = false}: Props) => { - if (hasPermission !== invert) { - return <>{children}; - } - return null; -}; - -export default React.memo(ChannelPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/channel_permission_gate/index.ts b/webapp/channels/src/components/permissions_gates/channel_permission_gate/index.ts deleted file mode 100644 index 7b3f92a518..0000000000 --- a/webapp/channels/src/components/permissions_gates/channel_permission_gate/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {connect} from 'react-redux'; - -import type {GlobalState} from '@mattermost/types/store'; - -import {haveIChannelPermission} from 'mattermost-redux/selectors/entities/roles'; - -import ChannelPermissionGate from './channel_permission_gate'; - -type Props = { - channelId?: string; - teamId?: string; - permissions: string[]; -} - -function mapStateToProps(state: GlobalState, ownProps: Props) { - if (!ownProps.channelId || ownProps.teamId === null || typeof ownProps.teamId === 'undefined') { - return {hasPermission: false}; - } - - for (const permission of ownProps.permissions) { - if (haveIChannelPermission(state, ownProps.teamId, ownProps.channelId, permission)) { - return {hasPermission: true}; - } - } - - return {hasPermission: false}; -} - -export default connect(mapStateToProps)(ChannelPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/channel_permission_gate/mattermost.code-workspace b/webapp/channels/src/components/permissions_gates/channel_permission_gate/mattermost.code-workspace deleted file mode 100644 index 0d72d4b6a5..0000000000 --- a/webapp/channels/src/components/permissions_gates/channel_permission_gate/mattermost.code-workspace +++ /dev/null @@ -1,20 +0,0 @@ -{ - "folders": [ - { - "path": "../../../../wp-calypso" - }, - { - "path": "../../../../wk9" - }, - { - "path": "../../.." - }, - { - "path": "../../../../mattermost-server" - }, - { - "path": "../../../../../Desktop/Coursera/confusion-master" - } - ], - "settings": {} -} diff --git a/webapp/channels/src/components/permissions_gates/gate.test.tsx b/webapp/channels/src/components/permissions_gates/gate.test.tsx new file mode 100644 index 0000000000..0f9fa469da --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/gate.test.tsx @@ -0,0 +1,34 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {render, screen} from 'tests/react_testing_utils'; + +import Gate from './gate'; + +describe('components/permissions_gates', () => { + const CONTENT = 'The content inside the permission gate'; + + describe('Gate', () => { + for (const hasPermission of [true, false]) { + for (const invert of [true, false]) { + test(`hasPermission=${hasPermission}; invert=${invert}; expected=${invert !== hasPermission}`, () => { + render( + +

{CONTENT}

+
, + ); + if (invert === hasPermission) { + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + } else { + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + } + }); + } + } + }); +}); diff --git a/webapp/channels/src/components/permissions_gates/gate.tsx b/webapp/channels/src/components/permissions_gates/gate.tsx new file mode 100644 index 0000000000..e6b764e697 --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/gate.tsx @@ -0,0 +1,23 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +type Props = { + hasPermission: boolean; + invert?: boolean; + children: React.ReactNode; +} + +const Gate = ({ + hasPermission, + invert, + children, +}: Props) => { + if (hasPermission !== invert) { + return <>{children}; + } + return null; +}; + +export default Gate; diff --git a/webapp/channels/src/components/permissions_gates/system_permission_gate.test.tsx b/webapp/channels/src/components/permissions_gates/system_permission_gate.test.tsx new file mode 100644 index 0000000000..22d210257f --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/system_permission_gate.test.tsx @@ -0,0 +1,101 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {renderWithContext, screen} from 'tests/react_testing_utils'; + +import SystemPermissionGate from './system_permission_gate'; + +describe('components/permissions_gates', () => { + const state = { + entities: { + channels: { + myMembers: { + channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, + }, + }, + teams: { + myMembers: { + team_id: {team_id: 'team_id', roles: 'team_role'}, + }, + }, + users: { + currentUserId: 'user_id', + profiles: { + user_id: { + id: 'user_id', + roles: 'system_role', + }, + }, + }, + roles: { + roles: { + system_role: {permissions: ['test_system_permission']}, + team_role: {permissions: ['test_team_permission']}, + channel_role: {permissions: ['test_channel_permission']}, + }, + }, + }, + }; + const CONTENT = 'The content inside the permission gate'; + + describe('SystemPermissionGate', () => { + test('should show content when user have permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should show content when user have at least on of the permissions', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should NOT show content when user have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should show content when user not have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should NOT show content when user haven\'t permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/webapp/channels/src/components/permissions_gates/system_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/system_permission_gate.tsx new file mode 100644 index 0000000000..ce9519bb44 --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/system_permission_gate.tsx @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {useSelector} from 'react-redux'; + +import type {GlobalState} from '@mattermost/types/store'; + +import {haveISystemPermission} from 'mattermost-redux/selectors/entities/roles_helpers'; + +import Gate from './gate'; + +type Props = { + permissions: string[]; + + /** + * Invert the permission (used for else) + */ + invert?: boolean; + + /** + * Content protected by the permissions gate + */ + children: React.ReactNode; +}; + +const SystemPermissionGate = ({ + invert = false, + permissions, + children, +}: Props) => { + const hasPermission = useSelector((state: GlobalState) => { + for (const permission of permissions) { + if (haveISystemPermission(state, {permission})) { + return true; + } + } + + return false; + }); + + return ( + + {children} + + ); +}; + +export default React.memo(SystemPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/system_permission_gate/__snapshots__/system_permission_gate.test.tsx.snap b/webapp/channels/src/components/permissions_gates/system_permission_gate/__snapshots__/system_permission_gate.test.tsx.snap deleted file mode 100644 index 679188b6be..0000000000 --- a/webapp/channels/src/components/permissions_gates/system_permission_gate/__snapshots__/system_permission_gate.test.tsx.snap +++ /dev/null @@ -1,184 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/permissions_gates SystemPermissionGate should match snapshot when user have at least on of the permissions 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates SystemPermissionGate should match snapshot when user have permission 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates SystemPermissionGate should match snapshot when user have permission and use invert 1`] = ` - - - - - -`; - -exports[`components/permissions_gates SystemPermissionGate should match snapshot when user haven't permission 1`] = ` - - - - - -`; - -exports[`components/permissions_gates SystemPermissionGate should match snapshot when user not have permission and use invert 1`] = ` - - - -

- Invalid permission but inverted (shown) -

-
-
-
-`; diff --git a/webapp/channels/src/components/permissions_gates/system_permission_gate/index.ts b/webapp/channels/src/components/permissions_gates/system_permission_gate/index.ts deleted file mode 100644 index ba3acb80d5..0000000000 --- a/webapp/channels/src/components/permissions_gates/system_permission_gate/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {connect} from 'react-redux'; - -import {haveISystemPermission} from 'mattermost-redux/selectors/entities/roles'; - -import type {GlobalState} from 'types/store'; - -import SystemPermissionGate from './system_permission_gate'; - -type Props = { - permissions: string[]; -} -function mapStateToProps(state: GlobalState, ownProps: Props) { - for (const permission of ownProps.permissions) { - if (haveISystemPermission(state, {permission})) { - return {hasPermission: true}; - } - } - - return {hasPermission: false}; -} - -export default connect(mapStateToProps)(SystemPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.test.tsx b/webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.test.tsx deleted file mode 100644 index 048298f2a8..0000000000 --- a/webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.test.tsx +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {mount} from 'enzyme'; -import React from 'react'; -import {Provider} from 'react-redux'; - -import SystemPermissionGate from 'components/permissions_gates/system_permission_gate'; - -import mockStore from 'tests/test_store'; - -describe('components/permissions_gates', () => { - const state = { - entities: { - channels: { - myMembers: { - channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, - }, - }, - teams: { - myMembers: { - team_id: {team_id: 'team_id', roles: 'team_role'}, - }, - }, - users: { - currentUserId: 'user_id', - profiles: { - user_id: { - id: 'user_id', - roles: 'system_role', - }, - }, - }, - roles: { - roles: { - system_role: {permissions: ['test_system_permission']}, - team_role: {permissions: ['test_team_permission']}, - channel_role: {permissions: ['test_channel_permission']}, - }, - }, - }, - }; - const store = mockStore(state); - - describe('SystemPermissionGate', () => { - test('should match snapshot when user have permission', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have at least on of the permissions', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission and use invert', () => { - const wrapper = mount( - - -

{'Valid permission but inverted (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user not have permission and use invert', () => { - const wrapper = mount( - - -

{'Invalid permission but inverted (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user haven\'t permission', () => { - const wrapper = mount( - - -

{'Invalid permission (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - }); -}); diff --git a/webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.tsx deleted file mode 100644 index a8d45d0009..0000000000 --- a/webapp/channels/src/components/permissions_gates/system_permission_gate/system_permission_gate.tsx +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; - -type Props = { - permissions: string[]; - - /** - * Has permission - * This prop is will always be passed by the mapStateToProps function - * it should be required when this component is converted to TS, for now its optional to make the TS compiler quite. - * about this prop not being passed from where this component is used - */ - hasPermission: boolean; - - /** - * Invert the permission (used for else) - */ - invert?: boolean; - - /** - * Content protected by the permissions gate - */ - children: React.ReactNode; -}; - -const SystemPermissionGate = ({ - invert = false, - hasPermission, - children, -}: Props) => { - if (hasPermission !== invert) { - return <>{children}; - } - return null; -}; - -export default SystemPermissionGate; diff --git a/webapp/channels/src/components/permissions_gates/team_permission_gate.test.tsx b/webapp/channels/src/components/permissions_gates/team_permission_gate.test.tsx new file mode 100644 index 0000000000..357702b652 --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/team_permission_gate.test.tsx @@ -0,0 +1,138 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {renderWithContext, screen} from 'tests/react_testing_utils'; + +import TeamPermissionGate from './team_permission_gate'; + +describe('components/permissions_gates', () => { + const state = { + entities: { + channels: { + myMembers: { + channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, + }, + }, + teams: { + myMembers: { + team_id: {team_id: 'team_id', roles: 'team_role'}, + }, + }, + users: { + currentUserId: 'user_id', + profiles: { + user_id: { + id: 'user_id', + roles: 'system_role', + }, + }, + }, + roles: { + roles: { + system_role: {permissions: ['test_system_permission']}, + team_role: {permissions: ['test_team_permission']}, + channel_role: {permissions: ['test_channel_permission']}, + }, + }, + }, + }; + const CONTENT = 'The content inside the permission gate'; + + describe('TeamPermissionGate', () => { + test('should show content when user have permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should show content when user have at least on of the permissions', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should NOT show content when user have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should show content when user not have permission and use invert', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + test('should NOT show content when user haven\'t permission', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should NOT show content when the team doesn\'t exists', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).not.toBeInTheDocument(); + }); + test('should show content when user have permission system wide', () => { + renderWithContext( + +

{CONTENT}

+
, + state, + ); + + expect(screen.queryByText(CONTENT)).toBeInTheDocument(); + }); + }); +}); diff --git a/webapp/channels/src/components/permissions_gates/team_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/team_permission_gate.tsx new file mode 100644 index 0000000000..ec1f234ee2 --- /dev/null +++ b/webapp/channels/src/components/permissions_gates/team_permission_gate.tsx @@ -0,0 +1,66 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {useSelector} from 'react-redux'; + +import {haveITeamPermission} from 'mattermost-redux/selectors/entities/roles'; + +import type {GlobalState} from 'types/store'; + +import Gate from './gate'; + +type Props = { + + /** + * Team to check the permission + */ + teamId?: string; + + /** + * Permissions enough to pass the gate (binary OR) + */ + permissions: string[]; + + /** + * Invert the permission (used for else) + */ + invert?: boolean; + + /** + * Content protected by the permissions gate + */ + children: React.ReactNode; +}; + +const TeamPermissionGate = ({ + teamId, + permissions, + invert = false, + children, +}: Props) => { + const hasPermission = useSelector((state: GlobalState) => { + if (!teamId) { + return false; + } + + for (const permission of permissions) { + if (haveITeamPermission(state, teamId, permission)) { + return true; + } + } + + return false; + }); + + return ( + + {children} + + ); +}; + +export default React.memo(TeamPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/team_permission_gate/__snapshots__/team_permission_gate.test.tsx.snap b/webapp/channels/src/components/permissions_gates/team_permission_gate/__snapshots__/team_permission_gate.test.tsx.snap deleted file mode 100644 index d32b4e5826..0000000000 --- a/webapp/channels/src/components/permissions_gates/team_permission_gate/__snapshots__/team_permission_gate.test.tsx.snap +++ /dev/null @@ -1,273 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when the team doesn't exists 1`] = ` - - - - - -`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have at least on of the permissions 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have permission 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have permission and use invert 1`] = ` - - - - - -`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user have permission system wide 1`] = ` - - - -

- Valid permission (shown) -

-
-
-
-`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user haven't permission 1`] = ` - - - - - -`; - -exports[`components/permissions_gates TeamPermissionGate should match snapshot when user not have permission and use invert 1`] = ` - - - -

- Invalid permission but inverted (shown) -

-
-
-
-`; diff --git a/webapp/channels/src/components/permissions_gates/team_permission_gate/index.ts b/webapp/channels/src/components/permissions_gates/team_permission_gate/index.ts deleted file mode 100644 index 9be96e929b..0000000000 --- a/webapp/channels/src/components/permissions_gates/team_permission_gate/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {connect} from 'react-redux'; - -import {haveITeamPermission} from 'mattermost-redux/selectors/entities/roles'; - -import type {GlobalState} from 'types/store'; - -import TeamPermissionGate from './team_permission_gate'; - -type Props = { - teamId?: string; - permissions: string[]; -} - -function mapStateToProps(state: GlobalState, ownProps: Props) { - if (!ownProps.teamId) { - return {hasPermission: false}; - } - - for (const permission of ownProps.permissions) { - if (haveITeamPermission(state, ownProps.teamId, permission)) { - return {hasPermission: true}; - } - } - - return {hasPermission: false}; -} - -export default connect(mapStateToProps)(TeamPermissionGate); diff --git a/webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.test.tsx b/webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.test.tsx deleted file mode 100644 index 02d1c97c54..0000000000 --- a/webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.test.tsx +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {mount} from 'enzyme'; -import React from 'react'; -import {Provider} from 'react-redux'; - -import TeamPermissionGate from 'components/permissions_gates/team_permission_gate'; - -import mockStore from 'tests/test_store'; - -describe('components/permissions_gates', () => { - const state = { - entities: { - channels: { - myMembers: { - channel_id: {channel_id: 'channel_id', roles: 'channel_role'}, - }, - }, - teams: { - myMembers: { - team_id: {team_id: 'team_id', roles: 'team_role'}, - }, - }, - users: { - currentUserId: 'user_id', - profiles: { - user_id: { - id: 'user_id', - roles: 'system_role', - }, - }, - }, - roles: { - roles: { - system_role: {permissions: ['test_system_permission']}, - team_role: {permissions: ['test_team_permission']}, - channel_role: {permissions: ['test_channel_permission']}, - }, - }, - }, - }; - const store = mockStore(state); - - describe('TeamPermissionGate', () => { - test('should match snapshot when user have permission', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have at least on of the permissions', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission and use invert', () => { - const wrapper = mount( - - -

{'Valid permission but inverted (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user not have permission and use invert', () => { - const wrapper = mount( - - -

{'Invalid permission but inverted (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user haven\'t permission', () => { - const wrapper = mount( - - -

{'Invalid permission (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when the team doesn\'t exists', () => { - const wrapper = mount( - - -

{'Valid permission invalid team (not shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - test('should match snapshot when user have permission system wide', () => { - const wrapper = mount( - - -

{'Valid permission (shown)'}

-
-
, - ); - - expect(wrapper).toMatchSnapshot(); - }); - }); -}); diff --git a/webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.tsx b/webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.tsx deleted file mode 100644 index 02f5c213fc..0000000000 --- a/webapp/channels/src/components/permissions_gates/team_permission_gate/team_permission_gate.tsx +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import React from 'react'; - -type Props = { - - /** - * Has permission - */ - hasPermission: boolean; - - /** - * Invert the permission (used for else) - */ - invert: boolean; - - /** - * Content protected by the permissions gate - */ - children: React.ReactNode; -}; - -export default class TeamPermissionGate extends React.PureComponent { - public static defaultProps = { - invert: false, - }; - - render() { - if (this.props.hasPermission && !this.props.invert) { - return this.props.children; - } - if (!this.props.hasPermission && this.props.invert) { - return this.props.children; - } - return null; - } -} diff --git a/webapp/channels/src/components/post_view/channel_intro_message/__snapshots__/add_members_button.test.tsx.snap b/webapp/channels/src/components/post_view/channel_intro_message/__snapshots__/add_members_button.test.tsx.snap deleted file mode 100644 index f2abdb064e..0000000000 --- a/webapp/channels/src/components/post_view/channel_intro_message/__snapshots__/add_members_button.test.tsx.snap +++ /dev/null @@ -1,188 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`components/post_view/AddMembersButton should match snapshot, less than limit 1`] = ` - - - - - - - -`; - -exports[`components/post_view/AddMembersButton should match snapshot, more than limit 1`] = ` - - - - - - - -`; - -exports[`components/post_view/AddMembersButton should match snapshot, setHeader and pluginButtons 1`] = ` - - - Create a board - - } - setHeader={ - - } - totalUsers={100} - usersLimit={10} - > - - - - - -`; diff --git a/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.test.tsx b/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.test.tsx index c838ce386b..c1a8f5f3b7 100644 --- a/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.test.tsx +++ b/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.test.tsx @@ -1,15 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {mount} from 'enzyme'; import React from 'react'; -import {Provider} from 'react-redux'; import type {Channel} from '@mattermost/types/channels'; +import {Permissions} from 'mattermost-redux/constants'; import * as teams from 'mattermost-redux/selectors/entities/teams'; -import mockStore from 'tests/test_store'; +import {renderWithContext, screen} from 'tests/react_testing_utils'; import AddMembersButton from './add_members_button'; @@ -35,6 +34,7 @@ describe('components/post_view/AddMembersButton', () => { currentTeamId: 'team-id', }, users: { + currentUserId: 'test-user-id', profiles: { 'test-user-id': { id: 'test-user-id', @@ -44,15 +44,15 @@ describe('components/post_view/AddMembersButton', () => { }, roles: { roles: { - system_role: {permissions: ['test_system_permission']}, - team_role: {permissions: ['test_team_permission']}, - channel_role: {permissions: ['test_channel_permission']}, + system_role: {permissions: [ + Permissions.ADD_USER_TO_TEAM, + Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS, + ]}, }, }, }, }; - const store = mockStore(initialState); jest.spyOn(teams, 'getCurrentTeamId').mockReturnValue('team-id'); test('should match snapshot, less than limit', () => { @@ -61,13 +61,12 @@ describe('components/post_view/AddMembersButton', () => { usersLimit: 100, channel, }; - const wrapper = mount( - - - , + renderWithContext( + , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(screen.queryByText('Invite others to the workspace')).toBeInTheDocument(); }); test('should match snapshot, more than limit', () => { @@ -76,39 +75,33 @@ describe('components/post_view/AddMembersButton', () => { usersLimit: 10, channel, }; - const wrapper = mount( - - - , + renderWithContext( + , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(screen.queryByText('Add people')).toBeInTheDocument(); }); test('should match snapshot, setHeader and pluginButtons', () => { + const PLUGIN_TEXT = 'Create a board plugin'; const pluginButtons = ( - ); - const setHeader = ( - ); + const props = { totalUsers: 100, usersLimit: 10, channel, - setHeader, pluginButtons, }; - const wrapper = mount( - - - , + renderWithContext( + , + initialState, ); - expect(wrapper).toMatchSnapshot(); + expect(screen.queryByText(PLUGIN_TEXT)).toBeInTheDocument(); }); }); diff --git a/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.tsx b/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.tsx index bcd4e15583..e8154fc8af 100644 --- a/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.tsx +++ b/webapp/channels/src/components/post_view/channel_intro_message/add_members_button.tsx @@ -21,7 +21,6 @@ import ToggleModalButton from 'components/toggle_modal_button'; import LoadingSpinner from 'components/widgets/loading/loading_spinner'; import {Constants, ModalIdentifiers} from 'utils/constants'; -import {localizeMessage} from 'utils/utils'; import './add_members_button.scss'; @@ -68,7 +67,7 @@ const LessThanMaxFreeUsers = ({pluginButtons}: {pluginButtons: React.ReactNode}) {pluginButtons}
- + `; diff --git a/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap b/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap index 640168e29e..d6c70a2495 100644 --- a/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap +++ b/webapp/channels/src/components/post_view/reaction_list/__snapshots__/reactions_list.test.tsx.snap @@ -62,7 +62,7 @@ exports[`components/ReactionList should render when there are reactions 1`] = ` target={[Function]} topOffset={-5} /> - - +
diff --git a/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap b/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap index 388bf219df..16ba753fd8 100644 --- a/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap +++ b/webapp/channels/src/components/select_team/__snapshots__/select_team.test.tsx.snap @@ -62,7 +62,7 @@ exports[`components/select_team/SelectTeam should match snapshot 1`] = `
-
- - +
- +
@@ -179,7 +179,7 @@ exports[`components/select_team/SelectTeam should match snapshot, on create team - - + @@ -240,7 +240,7 @@ exports[`components/select_team/SelectTeam should match snapshot, on error 1`] = - - - + - + @@ -316,7 +316,7 @@ exports[`components/select_team/SelectTeam should match snapshot, on loading 1`] siteName="Mattermost" /> - - - + - + @@ -425,7 +425,7 @@ exports[`components/select_team/SelectTeam should match snapshot, on no joinable className="signup-team-dir-err" >
- - - + - +
- - - + - + @@ -545,7 +545,7 @@ exports[`components/select_team/SelectTeam should match snapshot, on no joinable - - - + - + @@ -662,7 +662,7 @@ exports[`components/select_team/SelectTeam should match snapshot, on no joinable - - - + - + diff --git a/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap b/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap index acdc673be5..45e3bfcab5 100644 --- a/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap +++ b/webapp/channels/src/components/sidebar/__snapshots__/invite_members_button.test.tsx.snap @@ -16,7 +16,7 @@ exports[`components/sidebar/invite_members_button should match snapshot 1`] = ` - - - - + + `; diff --git a/webapp/channels/src/components/team_members_modal/__snapshots__/team_members_modal.test.tsx.snap b/webapp/channels/src/components/team_members_modal/__snapshots__/team_members_modal.test.tsx.snap index add69ec982..6a83511637 100644 --- a/webapp/channels/src/components/team_members_modal/__snapshots__/team_members_modal.test.tsx.snap +++ b/webapp/channels/src/components/team_members_modal/__snapshots__/team_members_modal.test.tsx.snap @@ -51,7 +51,7 @@ exports[`components/TeamMembersModal should match snapshot 1`] = ` } /> - - + - - + - - + - - - + - + - - - + - - + - - + - + - - + - - - + - - + - - + - - + - - + - + - - +